Index: Source/testing/runner/WebAXObjectProxy.cpp |
diff --git a/Source/testing/runner/WebAXObjectProxy.cpp b/Source/testing/runner/WebAXObjectProxy.cpp |
index aa866300c20a10c027d5ad1071df1d368de30655..b154e0f16ad416fa2a874f0e8030853cc92c45d5 100644 |
--- a/Source/testing/runner/WebAXObjectProxy.cpp |
+++ b/Source/testing/runner/WebAXObjectProxy.cpp |
@@ -132,6 +132,8 @@ string roleToString(WebAXRole role) |
return result.append("Image"); |
case WebAXRoleIncrementor: |
return result.append("Incrementor"); |
+ case WebAXRoleInlineTextBox: |
+ return result.append("InlineTextBox"); |
case WebAXRoleLabel: |
return result.append("Label"); |
case WebAXRoleLegend: |
@@ -346,6 +348,108 @@ string getAttributes(const WebAXObject& object) |
return attributes; |
} |
+WebRect boundsForCharacter(const WebAXObject& object, int characterIndex) |
+{ |
+ BLINK_ASSERT(object.role() == WebAXRoleStaticText); |
+ int end = 0; |
+ for (unsigned i = 0; i < object.childCount(); i++) { |
+ WebAXObject inlineTextBox = object.childAt(i); |
+ BLINK_ASSERT(inlineTextBox.role() == WebAXRoleInlineTextBox); |
+ int start = end; |
+ end += inlineTextBox.stringValue().length(); |
+ if (end <= characterIndex) |
+ continue; |
+ WebRect inlineTextBoxRect = inlineTextBox.boundingBoxRect(); |
+ int localIndex = characterIndex - start; |
+ WebVector<int> characterOffsets; |
+ inlineTextBox.characterOffsets(characterOffsets); |
+ BLINK_ASSERT(characterOffsets.size() > 0 && characterOffsets.size() == inlineTextBox.stringValue().length()); |
+ switch (inlineTextBox.textDirection()) { |
+ case WebAXTextDirectionLR: { |
+ if (localIndex) { |
+ int left = inlineTextBoxRect.x + characterOffsets[localIndex - 1]; |
+ int width = characterOffsets[localIndex] - characterOffsets[localIndex - 1]; |
+ return WebRect(left, inlineTextBoxRect.y, width, inlineTextBoxRect.height); |
+ } |
+ return WebRect(inlineTextBoxRect.x, inlineTextBoxRect.y, characterOffsets[0], inlineTextBoxRect.height); |
+ } |
+ case WebAXTextDirectionRL: { |
+ int right = inlineTextBoxRect.x + inlineTextBoxRect.width; |
+ |
+ if (localIndex) { |
+ int left = right - characterOffsets[localIndex]; |
+ int width = characterOffsets[localIndex] - characterOffsets[localIndex - 1]; |
+ return WebRect(left, inlineTextBoxRect.y, width, inlineTextBoxRect.height); |
+ } |
+ int left = right - characterOffsets[0]; |
+ return WebRect(left, inlineTextBoxRect.y, characterOffsets[0], inlineTextBoxRect.height); |
+ } |
+ case WebAXTextDirectionTB: { |
+ if (localIndex) { |
+ int top = inlineTextBoxRect.y + characterOffsets[localIndex - 1]; |
+ int height = characterOffsets[localIndex] - characterOffsets[localIndex - 1]; |
+ return WebRect(inlineTextBoxRect.x, top, inlineTextBoxRect.width, height); |
+ } |
+ return WebRect(inlineTextBoxRect.x, inlineTextBoxRect.y, inlineTextBoxRect.width, characterOffsets[0]); |
+ } |
+ case WebAXTextDirectionBT: { |
+ int bottom = inlineTextBoxRect.y + inlineTextBoxRect.height; |
+ |
+ if (localIndex) { |
+ int top = bottom - characterOffsets[localIndex]; |
+ int height = characterOffsets[localIndex] - characterOffsets[localIndex - 1]; |
+ return WebRect(inlineTextBoxRect.x, top, inlineTextBoxRect.width, height); |
+ } |
+ int top = bottom - characterOffsets[0]; |
+ return WebRect(inlineTextBoxRect.x, top, inlineTextBoxRect.width, characterOffsets[0]); |
+ } |
+ } |
+ } |
+ |
+ BLINK_ASSERT(false); |
+ return WebRect(); |
+} |
+ |
+void getBoundariesForOneWord(const WebAXObject& object, int characterIndex, int& wordStart, int& wordEnd) |
+{ |
+ int end = 0; |
+ for (unsigned i = 0; i < object.childCount(); i++) { |
+ WebAXObject inlineTextBox = object.childAt(i); |
+ BLINK_ASSERT(inlineTextBox.role() == WebAXRoleInlineTextBox); |
+ int start = end; |
+ end += inlineTextBox.stringValue().length(); |
+ if (end <= characterIndex) |
+ continue; |
+ int localIndex = characterIndex - start; |
+ |
+ WebVector<int> starts; |
+ WebVector<int> ends; |
+ inlineTextBox.wordBoundaries(starts, ends); |
+ size_t wordCount = starts.size(); |
+ BLINK_ASSERT(ends.size() == wordCount); |
+ |
+ // If there are no words, use the InlineTextBox boundaries. |
+ if (!wordCount) { |
+ wordStart = start; |
+ wordEnd = end; |
+ return; |
+ } |
+ |
+ // Look for a character within any word other than the last. |
+ for (size_t j = 0; j < wordCount - 1; j++) { |
+ if (localIndex <= ends[j]) { |
+ wordStart = start + starts[j]; |
+ wordEnd = start + ends[j]; |
+ return; |
+ } |
+ } |
+ |
+ // Return the last word by default. |
+ wordStart = start + starts[wordCount - 1]; |
+ wordEnd = start + ends[wordCount - 1]; |
+ return; |
+ } |
+} |
// Collects attributes into a string, delimited by dashes. Used by all methods |
// that output lists of attributes: attributesOfLinkedUIElementsCallback, |
@@ -459,6 +563,8 @@ WebAXObjectProxy::WebAXObjectProxy(const WebAXObject& object, Factory* factory) |
bindMethod("scrollToMakeVisible", &WebAXObjectProxy::scrollToMakeVisibleCallback); |
bindMethod("scrollToMakeVisibleWithSubFocus", &WebAXObjectProxy::scrollToMakeVisibleWithSubFocusCallback); |
bindMethod("scrollToGlobalPoint", &WebAXObjectProxy::scrollToGlobalPointCallback); |
+ bindMethod("wordStart", &WebAXObjectProxy::wordStartCallback); |
+ bindMethod("wordEnd", &WebAXObjectProxy::wordEndCallback); |
bindFallbackMethod(&WebAXObjectProxy::fallbackCallback); |
} |
@@ -748,9 +854,36 @@ void WebAXObjectProxy::lineForIndexCallback(const CppArgumentList& arguments, Cp |
result->set(line); |
} |
-void WebAXObjectProxy::boundsForRangeCallback(const CppArgumentList&, CppVariant* result) |
+void WebAXObjectProxy::boundsForRangeCallback(const CppArgumentList& arguments, CppVariant* result) |
{ |
result->setNull(); |
+ |
+ if (arguments.size() != 2 || !arguments[0].isNumber() || !arguments[1].isNumber()) |
+ return; |
+ |
+ if (accessibilityObject().role() != WebAXRoleStaticText) |
+ return; |
+ |
+ int start = arguments[0].toInt32(); |
+ int end = arguments[1].toInt32(); |
+ int len = end - start; |
+ |
+ // Get the bounds for each character and union them into one large rectangle. |
+ // This is just for testing so it doesn't need to be efficient. |
+ WebRect bounds = boundsForCharacter(accessibilityObject(), start); |
+ for (int i = 1; i < len; i++) { |
+ WebRect next = boundsForCharacter(accessibilityObject(), start + i); |
+ int right = std::max(bounds.x + bounds.width, next.x + next.width); |
+ int bottom = std::max(bounds.y + bounds.height, next.y + next.height); |
+ bounds.x = std::min(bounds.x, next.x); |
+ bounds.y = std::min(bounds.y, next.y); |
+ bounds.width = right - bounds.x; |
+ bounds.height = bottom - bounds.y; |
+ } |
+ |
+ char buffer[100]; |
+ snprintf(buffer, sizeof(buffer), "{x: %d, y: %d, width: %d, height: %d}", bounds.x, bounds.y, bounds.width, bounds.height); |
+ result->set(string(buffer)); |
} |
void WebAXObjectProxy::stringForRangeCallback(const CppArgumentList&, CppVariant* result) |
@@ -1036,6 +1169,38 @@ void WebAXObjectProxy::scrollToGlobalPointCallback(const CppArgumentList& argume |
result->setNull(); |
} |
+void WebAXObjectProxy::wordStartCallback(const CppArgumentList& arguments, CppVariant* result) |
+{ |
+ result->setNull(); |
+ |
+ if (arguments.size() != 1 || !arguments[0].isNumber()) |
+ return; |
+ |
+ if (accessibilityObject().role() != WebAXRoleStaticText) |
+ return; |
+ |
+ int characterIndex = arguments[0].toInt32(); |
+ int wordStart, wordEnd; |
+ getBoundariesForOneWord(accessibilityObject(), characterIndex, wordStart, wordEnd); |
+ result->set(wordStart); |
+} |
+ |
+void WebAXObjectProxy::wordEndCallback(const CppArgumentList& arguments, CppVariant* result) |
+{ |
+ result->setNull(); |
+ |
+ if (arguments.size() != 1 || !arguments[0].isNumber()) |
+ return; |
+ |
+ if (accessibilityObject().role() != WebAXRoleStaticText) |
+ return; |
+ |
+ int characterIndex = arguments[0].toInt32(); |
+ int wordStart, wordEnd; |
+ getBoundariesForOneWord(accessibilityObject(), characterIndex, wordStart, wordEnd); |
+ result->set(wordEnd); |
+} |
+ |
void WebAXObjectProxy::fallbackCallback(const CppArgumentList &, CppVariant* result) |
{ |
// FIXME: Implement this. |