[GTK] ATK text-caret-moved and text-selection-changed events not being emitted
https://bugs.webkit.org/show_bug.cgi?id=76069
Reviewed by Martin Robinson.
Source/WebCore:
Fix bug introduced with patch for Bug 72830.
* accessibility/AccessibilityObject.cpp:
(WebCore::AccessibilityObject::isDescendantOfObject): New function,
to check if an accessibility object is a descendant of other object.
(WebCore::AccessibilityObject::isAncestorOfObject): New function,
to check if an accessibility object is an ancestor of other object.
* accessibility/AccessibilityObject.h:
* accessibility/gtk/AccessibilityObjectWrapperAtk.cpp:
(webkit_accessible_text_get_caret_offset): Make sure to pass the
right reference object to objectFocusedAndCaretOffsetUnignored.
(objectFocusedAndCaretOffsetUnignored): Use positionBeforeNode
instead of firstPositionInNode for calculating the begining of the
range used to calculate the offsets. Ensure that the reference
object is never a descendant of the actual object being returned.
* editing/gtk/FrameSelectionGtk.cpp:
(WebCore::FrameSelection::notifyAccessibilityForSelectionChange):
Pass the right accessibility object associated with the current
selection to objectFocusedAndCaretOffsetUnignored.
Source/WebKit/gtk:
Update caret browsing related unit tests to check emissions of
'text-caret-moved' and 'text-selection-changed' signals.
* tests/testatk.c:
(textCaretMovedCallback): New callback for 'text-caret-moved'.
(testWebkitAtkCaretOffsets): Check emissions of 'text-caret-moved'.
(textSelectionChangedCallback): New callback for 'text-selection-changed'.
(testWebkitAtkTextSelections): Check emissions of 'text-selection-changed'.
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@105590 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 7c89fe8..dfb424d 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,32 @@
+2012-01-22 Mario Sanchez Prada <msanchez@igalia.com>
+
+ [GTK] ATK text-caret-moved and text-selection-changed events not being emitted
+ https://bugs.webkit.org/show_bug.cgi?id=76069
+
+ Reviewed by Martin Robinson.
+
+ Fix bug introduced with patch for Bug 72830.
+
+ * accessibility/AccessibilityObject.cpp:
+ (WebCore::AccessibilityObject::isDescendantOfObject): New function,
+ to check if an accessibility object is a descendant of other object.
+ (WebCore::AccessibilityObject::isAncestorOfObject): New function,
+ to check if an accessibility object is an ancestor of other object.
+ * accessibility/AccessibilityObject.h:
+
+ * accessibility/gtk/AccessibilityObjectWrapperAtk.cpp:
+ (webkit_accessible_text_get_caret_offset): Make sure to pass the
+ right reference object to objectFocusedAndCaretOffsetUnignored.
+ (objectFocusedAndCaretOffsetUnignored): Use positionBeforeNode
+ instead of firstPositionInNode for calculating the begining of the
+ range used to calculate the offsets. Ensure that the reference
+ object is never a descendant of the actual object being returned.
+
+ * editing/gtk/FrameSelectionGtk.cpp:
+ (WebCore::FrameSelection::notifyAccessibilityForSelectionChange):
+ Pass the right accessibility object associated with the current
+ selection to objectFocusedAndCaretOffsetUnignored.
+
2012-01-21 David Reveman <reveman@chromium.org>
[Chromium] Incremental texture updates are not atomic.
diff --git a/Source/WebCore/accessibility/AccessibilityObject.cpp b/Source/WebCore/accessibility/AccessibilityObject.cpp
index 1b8cd98..bbbbff2 100644
--- a/Source/WebCore/accessibility/AccessibilityObject.cpp
+++ b/Source/WebCore/accessibility/AccessibilityObject.cpp
@@ -1294,6 +1294,26 @@
return AccessibilityOrientationHorizontal;
}
+bool AccessibilityObject::isDescendantOfObject(const AccessibilityObject* axObject) const
+{
+ if (!axObject || !axObject->hasChildren())
+ return false;
+
+ for (const AccessibilityObject* parent = parentObject(); parent; parent = parent->parentObject()) {
+ if (parent == axObject)
+ return true;
+ }
+ return false;
+}
+
+bool AccessibilityObject::isAncestorOfObject(const AccessibilityObject* axObject) const
+{
+ if (!axObject)
+ return false;
+
+ return this == axObject || axObject->isDescendantOfObject(this);
+}
+
typedef HashMap<String, AccessibilityRole, CaseFoldingHash> ARIARoleMap;
struct RoleEntry {
diff --git a/Source/WebCore/accessibility/AccessibilityObject.h b/Source/WebCore/accessibility/AccessibilityObject.h
index bd5741c..c417cc9 100644
--- a/Source/WebCore/accessibility/AccessibilityObject.h
+++ b/Source/WebCore/accessibility/AccessibilityObject.h
@@ -564,6 +564,8 @@
virtual AccessibilityObject* activeDescendant() const { return 0; }
virtual void handleActiveDescendantChanged() { }
virtual void handleAriaExpandedChanged() { }
+ bool isDescendantOfObject(const AccessibilityObject*) const;
+ bool isAncestorOfObject(const AccessibilityObject*) const;
static AccessibilityRole ariaRoleToWebCoreRole(const String&);
const AtomicString& getAttribute(const QualifiedName&) const;
diff --git a/Source/WebCore/accessibility/gtk/AccessibilityObjectWrapperAtk.cpp b/Source/WebCore/accessibility/gtk/AccessibilityObjectWrapperAtk.cpp
index eadacaf..fe9f007 100644
--- a/Source/WebCore/accessibility/gtk/AccessibilityObjectWrapperAtk.cpp
+++ b/Source/WebCore/accessibility/gtk/AccessibilityObjectWrapperAtk.cpp
@@ -1345,6 +1345,12 @@
if (!coreObject->isAccessibilityRenderObject())
return 0;
+ // We need to make sure we pass a valid object as reference.
+ if (coreObject->accessibilityIsIgnored())
+ coreObject = coreObject->parentObjectUnignored();
+ if (!coreObject)
+ return 0;
+
int offset;
if (!objectFocusedAndCaretOffsetUnignored(coreObject, offset))
return 0;
@@ -2736,19 +2742,25 @@
return 0;
// Look for the actual (not ignoring accessibility) selected object.
- if (focusedObject->accessibilityIsIgnored())
- focusedObject = focusedObject->parentObjectUnignored();
- if (!focusedObject)
+ AccessibilityObject* firstUnignoredParent = focusedObject;
+ if (firstUnignoredParent->accessibilityIsIgnored())
+ firstUnignoredParent = firstUnignoredParent->parentObjectUnignored();
+ if (!firstUnignoredParent)
return 0;
// Don't ignore links if the offset is being requested for a link.
- if (!referenceObject->isLink() && focusedObject->isLink())
- focusedObject = focusedObject->parentObjectUnignored();
- if (!focusedObject)
+ if (!referenceObject->isLink() && firstUnignoredParent->isLink())
+ firstUnignoredParent = firstUnignoredParent->parentObjectUnignored();
+ if (!firstUnignoredParent)
return 0;
+ // The reference object must either coincide with the focused
+ // object being considered, or be a descendant of it.
+ if (referenceObject->isDescendantOfObject(firstUnignoredParent))
+ referenceObject = firstUnignoredParent;
+
Node* startNode = 0;
- if (focusedObject != referenceObject || focusedObject->isTextControl()) {
+ if (firstUnignoredParent != referenceObject || firstUnignoredParent->isTextControl()) {
// We need to use the first child's node of the reference
// object as the start point to calculate the caret offset
// because we want it to be relative to the object of
@@ -2759,10 +2771,10 @@
startNode = axFirstChild->node();
}
if (!startNode)
- startNode = focusedObject->node();
+ startNode = firstUnignoredParent->node();
- VisiblePosition startPosition = VisiblePosition(firstPositionInNode(startNode), DOWNSTREAM);
- VisiblePosition endPosition = focusedObject->selection().visibleEnd();
+ VisiblePosition startPosition = VisiblePosition(positionBeforeNode(startNode), DOWNSTREAM);
+ VisiblePosition endPosition = firstUnignoredParent->selection().visibleEnd();
if (startPosition == endPosition)
offset = 0;
@@ -2774,7 +2786,7 @@
offset = TextIterator::rangeLength(range.get(), true);
}
- return focusedObject;
+ return firstUnignoredParent;
}
#endif // HAVE(ACCESSIBILITY)
diff --git a/Source/WebCore/editing/gtk/FrameSelectionGtk.cpp b/Source/WebCore/editing/gtk/FrameSelectionGtk.cpp
index a637870..af14d39 100644
--- a/Source/WebCore/editing/gtk/FrameSelectionGtk.cpp
+++ b/Source/WebCore/editing/gtk/FrameSelectionGtk.cpp
@@ -84,8 +84,10 @@
if (!m_selection.start().isNotNull() || !m_selection.end().isNotNull())
return;
- // Look for the accessibility object for the Frame.
- AccessibilityObject* accessibilityObject = m_frame->document()->axObjectCache()->rootObjectForFrame(m_frame);
+ RenderObject* focusedNode = m_selection.end().containerNode()->renderer();
+ AccessibilityObject* accessibilityObject = m_frame->document()->axObjectCache()->getOrCreate(focusedNode);
+
+ // Need to check this as getOrCreate could return 0.
if (!accessibilityObject)
return;
diff --git a/Source/WebKit/gtk/ChangeLog b/Source/WebKit/gtk/ChangeLog
index 08bb849..c70e10a 100644
--- a/Source/WebKit/gtk/ChangeLog
+++ b/Source/WebKit/gtk/ChangeLog
@@ -1,3 +1,19 @@
+2012-01-22 Mario Sanchez Prada <msanchez@igalia.com>
+
+ [GTK] ATK text-caret-moved and text-selection-changed events not being emitted
+ https://bugs.webkit.org/show_bug.cgi?id=76069
+
+ Reviewed by Martin Robinson.
+
+ Update caret browsing related unit tests to check emissions of
+ 'text-caret-moved' and 'text-selection-changed' signals.
+
+ * tests/testatk.c:
+ (textCaretMovedCallback): New callback for 'text-caret-moved'.
+ (testWebkitAtkCaretOffsets): Check emissions of 'text-caret-moved'.
+ (textSelectionChangedCallback): New callback for 'text-selection-changed'.
+ (testWebkitAtkTextSelections): Check emissions of 'text-selection-changed'.
+
2012-01-18 Evan Nemerson <evan@coeus-group.com>
[GTK] WebKit-3.0.gir does not include information about C includes or exported packages
diff --git a/Source/WebKit/gtk/tests/testatk.c b/Source/WebKit/gtk/tests/testatk.c
index a11552f..b48b557 100644
--- a/Source/WebKit/gtk/tests/testatk.c
+++ b/Source/WebKit/gtk/tests/testatk.c
@@ -254,6 +254,17 @@
}
}
+static gchar* textCaretMovedResult = 0;
+
+static void textCaretMovedCallback(AtkText* text, gint pos, gpointer data)
+{
+ g_assert(ATK_IS_TEXT(text));
+
+ g_free(textCaretMovedResult);
+ AtkRole role = atk_object_get_role(ATK_OBJECT(text));
+ textCaretMovedResult = g_strdup_printf("|%s|%d|", atk_role_get_name(role), pos);
+}
+
static void testWebkitAtkCaretOffsets()
{
WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
@@ -267,6 +278,8 @@
AtkObject* header = atk_object_ref_accessible_child(object, 0);
g_assert(ATK_IS_TEXT(header));
+ g_signal_connect(header, "text-caret-moved", G_CALLBACK(textCaretMovedCallback), 0);
+
gchar* text = atk_text_get_text(ATK_TEXT(header), 0, -1);
g_assert_cmpstr(text, ==, "A text header");
g_free (text);
@@ -276,9 +289,12 @@
g_assert_cmpint(result, ==, TRUE);
gint offset = atk_text_get_caret_offset(ATK_TEXT(header));
g_assert_cmpint(offset, ==, 5);
+ g_assert_cmpstr(textCaretMovedResult, ==, "|heading|5|");
AtkObject* paragraph = atk_object_ref_accessible_child(object, 1);
g_assert(ATK_IS_TEXT(paragraph));
+ g_signal_connect(paragraph, "text-caret-moved", G_CALLBACK(textCaretMovedCallback), 0);
+
text = atk_text_get_text(ATK_TEXT(paragraph), 0, -1);
g_assert_cmpstr(text, ==, "A paragraph with a link in the middle");
g_free (text);
@@ -288,16 +304,32 @@
g_assert_cmpint(result, ==, TRUE);
offset = atk_text_get_caret_offset(ATK_TEXT(paragraph));
g_assert_cmpint(offset, ==, 5);
+ g_assert_cmpstr(textCaretMovedResult, ==, "|paragraph|5|");
result = atk_text_set_caret_offset(ATK_TEXT(paragraph), 20);
g_assert_cmpint(result, ==, TRUE);
offset = atk_text_get_caret_offset(ATK_TEXT(paragraph));
g_assert_cmpint(offset, ==, 20);
+ g_assert_cmpstr(textCaretMovedResult, ==, "|paragraph|20|");
result = atk_text_set_caret_offset(ATK_TEXT(paragraph), 30);
g_assert_cmpint(result, ==, TRUE);
offset = atk_text_get_caret_offset(ATK_TEXT(paragraph));
g_assert_cmpint(offset, ==, 30);
+ g_assert_cmpstr(textCaretMovedResult, ==, "|paragraph|30|");
+
+ AtkObject* link = atk_object_ref_accessible_child(paragraph, 0);
+ g_assert(ATK_IS_TEXT(link));
+ text = atk_text_get_text(ATK_TEXT(link), 0, -1);
+ g_assert_cmpstr(text, ==, "with a link");
+ g_free (text);
+
+ result = atk_text_set_caret_offset(ATK_TEXT(link), 5);
+ g_assert_cmpint(result, ==, TRUE);
+ offset = atk_text_get_caret_offset(ATK_TEXT(link));
+ g_assert_cmpint(offset, ==, 5);
+ /* Positions inside links are reported relative to the paragraph. */
+ g_assert_cmpstr(textCaretMovedResult, ==, "|paragraph|17|");
AtkObject* list = atk_object_ref_accessible_child(object, 2);
g_assert(ATK_OBJECT(list));
@@ -355,8 +387,11 @@
offset = atk_text_get_caret_offset(ATK_TEXT(textEntry));
g_assert_cmpint(offset, ==, 5);
+ g_free(textCaretMovedResult);
+
g_object_unref(header);
g_object_unref(paragraph);
+ g_object_unref(link);
g_object_unref(list);
g_object_unref(listItem);
g_object_unref(panel);
@@ -1087,6 +1122,20 @@
atk_attribute_set_free(set3);
}
+static gchar* textSelectionChangedResult = 0;
+
+static void textSelectionChangedCallback(AtkText* text, gpointer data)
+{
+ g_assert(ATK_IS_TEXT(text));
+
+ g_free(textSelectionChangedResult);
+ AtkRole role = atk_object_get_role(ATK_OBJECT(text));
+ int startOffset = 0;
+ int endOffset = 0;
+ atk_text_get_selection(ATK_TEXT(text), 0, &startOffset, &endOffset);
+ textSelectionChangedResult = g_strdup_printf("|%s|%d|%d|", atk_role_get_name(role), startOffset, endOffset);
+}
+
static void testWebkitAtkTextSelections()
{
WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
@@ -1100,9 +1149,11 @@
AtkText* paragraph1 = ATK_TEXT(atk_object_ref_accessible_child(object, 0));
g_assert(ATK_IS_TEXT(paragraph1));
+ g_signal_connect(paragraph1, "text-selection-changed", G_CALLBACK(textSelectionChangedCallback), 0);
AtkText* paragraph2 = ATK_TEXT(atk_object_ref_accessible_child(object, 1));
g_assert(ATK_IS_TEXT(paragraph2));
+ g_signal_connect(paragraph2, "text-selection-changed", G_CALLBACK(textSelectionChangedCallback), 0);
AtkText* link = ATK_TEXT(atk_object_ref_accessible_child(ATK_OBJECT(paragraph2), 0));
g_assert(ATK_IS_TEXT(link));
@@ -1138,11 +1189,13 @@
result = atk_text_set_selection(paragraph1, 0, 5, 25);
g_assert(result);
g_assert_cmpint(atk_text_get_n_selections(paragraph1), ==, 1);
+ g_assert_cmpstr(textSelectionChangedResult, ==, "|paragraph|5|25|");
selectedText = atk_text_get_selection(paragraph1, 0, &startOffset, &endOffset);
g_assert_cmpint(startOffset, ==, 5);
g_assert_cmpint(endOffset, ==, 25);
g_assert_cmpstr(selectedText, ==, "agraph with plain te");
g_free (selectedText);
+
/* Try removing the selection from other AtkText object (should fail). */
result = atk_text_remove_selection(paragraph2, 0);
g_assert(!result);
@@ -1185,6 +1238,7 @@
result = atk_text_set_selection(paragraph2, 0, 27, 37);
g_assert(result);
g_assert_cmpint(atk_text_get_n_selections(paragraph2), ==, 1);
+ g_assert_cmpstr(textSelectionChangedResult, ==, "|paragraph|27|37|");
selectedText = atk_text_get_selection(paragraph2, 0, &startOffset, &endOffset);
g_assert_cmpint(startOffset, ==, 27);
g_assert_cmpint(endOffset, ==, 37);
@@ -1234,6 +1288,8 @@
g_assert_cmpstr(selectedText, ==, "A list");
g_free (selectedText);
+ g_free(textSelectionChangedResult);
+
g_object_unref(paragraph1);
g_object_unref(paragraph2);
g_object_unref(list);