| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/web_test/renderer/web_ax_object_proxy.h" |
| |
| #include <stddef.h> |
| |
| #include <algorithm> |
| #include <map> |
| #include <utility> |
| |
| #include "base/stl_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "gin/handle.h" |
| #include "third_party/blink/public/platform/web_string.h" |
| #include "third_party/blink/public/web/blink.h" |
| #include "third_party/blink/public/web/web_document.h" |
| #include "third_party/blink/public/web/web_local_frame.h" |
| #include "third_party/skia/include/core/SkMatrix44.h" |
| #include "ui/accessibility/ax_action_data.h" |
| #include "ui/accessibility/ax_enums.mojom-shared.h" |
| #include "ui/accessibility/ax_node_data.h" |
| #include "ui/gfx/geometry/rect_f.h" |
| #include "ui/gfx/transform.h" |
| |
| namespace content { |
| |
| namespace { |
| |
| // Map role value to string, matching Safari/Mac platform implementation to |
| // avoid rebaselining web tests. |
| std::string RoleToString(ax::mojom::Role role) { |
| std::string prefix = "k"; |
| std::ostringstream result; |
| result << role; |
| // Check that |result| starts with |prefix|. |
| DCHECK_EQ(result.str().find(prefix), 0ull); |
| return "AXRole: AX" + result.str().substr(prefix.size()); |
| } |
| |
| std::string GetStringValue(const blink::WebAXObject& object) { |
| std::string value; |
| if (object.Role() == ax::mojom::Role::kColorWell) { |
| unsigned int color = object.ColorValue(); |
| unsigned int red = (color >> 16) & 0xFF; |
| unsigned int green = (color >> 8) & 0xFF; |
| unsigned int blue = color & 0xFF; |
| value = base::StringPrintf("rgba(%d, %d, %d, 1)", red, green, blue); |
| } else { |
| value = object.GetValueForControl().Utf8(); |
| } |
| return value.insert(0, "AXValue: "); |
| } |
| |
| std::string GetRole(const blink::WebAXObject& object) { |
| std::string role_string = RoleToString(object.Role()); |
| |
| // Special-case canvas with fallback content because Chromium wants to treat |
| // this as essentially a separate role that it can map differently depending |
| // on the platform. |
| if (object.Role() == ax::mojom::Role::kCanvas && |
| object.CanvasHasFallbackContent()) { |
| role_string += "WithFallbackContent"; |
| } |
| |
| return role_string; |
| } |
| |
| std::string GetLanguage(const blink::WebAXObject& object) { |
| std::string language = object.Language().Utf8(); |
| return language.insert(0, "AXLanguage: "); |
| } |
| |
| std::string GetAttributes(const blink::WebAXObject& object) { |
| std::string attributes(object.GetName().Utf8()); |
| attributes.append("\n"); |
| attributes.append(GetRole(object)); |
| return attributes; |
| } |
| |
| // New bounds calculation algorithm. Retrieves the frame-relative bounds |
| // of an object by calling getRelativeBounds and then applying the offsets |
| // and transforms recursively on each container of this object. |
| gfx::RectF BoundsForObject(const blink::WebAXObject& object) { |
| blink::WebAXObject container; |
| gfx::RectF bounds; |
| SkMatrix44 matrix; |
| object.GetRelativeBounds(container, bounds, matrix); |
| gfx::RectF computed_bounds(0, 0, bounds.width(), bounds.height()); |
| while (!container.IsDetached()) { |
| computed_bounds.Offset(bounds.x(), bounds.y()); |
| computed_bounds.Offset(-container.GetScrollOffset().x(), |
| -container.GetScrollOffset().y()); |
| if (!matrix.isIdentity()) { |
| gfx::Transform transform(matrix); |
| transform.TransformRect(&computed_bounds); |
| } |
| container.GetRelativeBounds(container, bounds, matrix); |
| } |
| return computed_bounds; |
| } |
| |
| gfx::Rect BoundsForCharacter(const blink::WebAXObject& object, |
| int character_index) { |
| DCHECK_EQ(object.Role(), ax::mojom::Role::kStaticText); |
| int end = 0; |
| for (unsigned i = 0; i < object.ChildCount(); i++) { |
| blink::WebAXObject inline_text_box = object.ChildAt(i); |
| DCHECK_EQ(inline_text_box.Role(), ax::mojom::Role::kInlineTextBox); |
| int start = end; |
| blink::WebString name = inline_text_box.GetName(); |
| end += name.length(); |
| if (character_index < start || character_index >= end) |
| continue; |
| |
| gfx::RectF inline_text_box_rect = BoundsForObject(inline_text_box); |
| |
| int local_index = character_index - start; |
| blink::WebVector<int> character_offsets; |
| inline_text_box.CharacterOffsets(character_offsets); |
| if (character_offsets.size() != name.length()) |
| return gfx::Rect(); |
| |
| switch (inline_text_box.GetTextDirection()) { |
| case ax::mojom::WritingDirection::kLtr: { |
| if (local_index) { |
| int left = |
| inline_text_box_rect.x() + character_offsets[local_index - 1]; |
| int width = character_offsets[local_index] - |
| character_offsets[local_index - 1]; |
| return gfx::Rect(left, inline_text_box_rect.y(), width, |
| inline_text_box_rect.height()); |
| } |
| return gfx::Rect(inline_text_box_rect.x(), inline_text_box_rect.y(), |
| character_offsets[0], inline_text_box_rect.height()); |
| } |
| case ax::mojom::WritingDirection::kRtl: { |
| int right = inline_text_box_rect.x() + inline_text_box_rect.width(); |
| |
| if (local_index) { |
| int left = right - character_offsets[local_index]; |
| int width = character_offsets[local_index] - |
| character_offsets[local_index - 1]; |
| return gfx::Rect(left, inline_text_box_rect.y(), width, |
| inline_text_box_rect.height()); |
| } |
| int left = right - character_offsets[0]; |
| return gfx::Rect(left, inline_text_box_rect.y(), character_offsets[0], |
| inline_text_box_rect.height()); |
| } |
| case ax::mojom::WritingDirection::kTtb: { |
| if (local_index) { |
| int top = |
| inline_text_box_rect.y() + character_offsets[local_index - 1]; |
| int height = character_offsets[local_index] - |
| character_offsets[local_index - 1]; |
| return gfx::Rect(inline_text_box_rect.x(), top, |
| inline_text_box_rect.width(), height); |
| } |
| return gfx::Rect(inline_text_box_rect.x(), inline_text_box_rect.y(), |
| inline_text_box_rect.width(), character_offsets[0]); |
| } |
| case ax::mojom::WritingDirection::kBtt: { |
| int bottom = inline_text_box_rect.y() + inline_text_box_rect.height(); |
| |
| if (local_index) { |
| int top = bottom - character_offsets[local_index]; |
| int height = character_offsets[local_index] - |
| character_offsets[local_index - 1]; |
| return gfx::Rect(inline_text_box_rect.x(), top, |
| inline_text_box_rect.width(), height); |
| } |
| int top = bottom - character_offsets[0]; |
| return gfx::Rect(inline_text_box_rect.x(), top, |
| inline_text_box_rect.width(), character_offsets[0]); |
| } |
| default: |
| NOTREACHED(); |
| return gfx::Rect(); |
| } |
| } |
| |
| DCHECK(false); |
| return gfx::Rect(); |
| } |
| |
| void GetBoundariesForOneWord(const blink::WebAXObject& object, |
| int character_index, |
| int& word_start, |
| int& word_end) { |
| int end = 0; |
| for (size_t i = 0; i < object.ChildCount(); i++) { |
| blink::WebAXObject inline_text_box = object.ChildAt(i); |
| DCHECK_EQ(inline_text_box.Role(), ax::mojom::Role::kInlineTextBox); |
| int start = end; |
| blink::WebString name = inline_text_box.GetName(); |
| end += name.length(); |
| if (end <= character_index) |
| continue; |
| int local_index = character_index - start; |
| |
| blink::WebVector<int> starts; |
| blink::WebVector<int> ends; |
| inline_text_box.GetWordBoundaries(starts, ends); |
| size_t word_count = starts.size(); |
| DCHECK_EQ(ends.size(), word_count); |
| |
| // If there are no words, use the InlineTextBox boundaries. |
| if (!word_count) { |
| word_start = start; |
| word_end = end; |
| return; |
| } |
| |
| // Look for a character within any word other than the last. |
| for (size_t j = 0; j < word_count - 1; j++) { |
| if (local_index < ends[j]) { |
| word_start = start + starts[j]; |
| word_end = start + ends[j]; |
| return; |
| } |
| } |
| |
| // Return the last word by default. |
| word_start = start + starts[word_count - 1]; |
| word_end = start + ends[word_count - 1]; |
| return; |
| } |
| } |
| |
| // Collects attributes into a string, delimited by dashes. Used by all methods |
| // that output lists of attributes: attributesOfLinkedUIElementsCallback, |
| // AttributesOfChildrenCallback, etc. |
| class AttributesCollector { |
| public: |
| AttributesCollector() {} |
| ~AttributesCollector() {} |
| |
| void CollectAttributes(const blink::WebAXObject& object) { |
| attributes_.append("\n------------\n"); |
| attributes_.append(GetAttributes(object)); |
| } |
| |
| std::string attributes() const { return attributes_; } |
| |
| private: |
| std::string attributes_; |
| |
| DISALLOW_COPY_AND_ASSIGN(AttributesCollector); |
| }; |
| |
| } // namespace |
| |
| gin::WrapperInfo WebAXObjectProxy::kWrapperInfo = {gin::kEmbedderNativeGin}; |
| |
| WebAXObjectProxy::WebAXObjectProxy(const blink::WebAXObject& object, |
| WebAXObjectProxy::Factory* factory) |
| : accessibility_object_(object), factory_(factory) {} |
| |
| WebAXObjectProxy::~WebAXObjectProxy() { |
| // v8::Persistent will leak on destroy, due to the default |
| // NonCopyablePersistentTraits (it claims this may change in the future). |
| notification_callback_.Reset(); |
| } |
| |
| void WebAXObjectProxy::UpdateLayout() { |
| blink::WebAXObject::UpdateLayout(accessibility_object_.GetDocument()); |
| } |
| |
| ui::AXNodeData WebAXObjectProxy::GetAXNodeData() const { |
| ui::AXNodeData node_data; |
| accessibility_object_.Serialize(&node_data, ui::kAXModeComplete); |
| return node_data; |
| } |
| |
| gin::ObjectTemplateBuilder WebAXObjectProxy::GetObjectTemplateBuilder( |
| v8::Isolate* isolate) { |
| return gin::Wrappable<WebAXObjectProxy>::GetObjectTemplateBuilder(isolate) |
| .SetProperty("role", &WebAXObjectProxy::Role) |
| .SetProperty("stringValue", &WebAXObjectProxy::StringValue) |
| .SetProperty("language", &WebAXObjectProxy::Language) |
| .SetProperty("x", &WebAXObjectProxy::X) |
| .SetProperty("y", &WebAXObjectProxy::Y) |
| .SetProperty("width", &WebAXObjectProxy::Width) |
| .SetProperty("height", &WebAXObjectProxy::Height) |
| .SetProperty("inPageLinkTarget", &WebAXObjectProxy::InPageLinkTarget) |
| .SetProperty("intValue", &WebAXObjectProxy::IntValue) |
| .SetProperty("minValue", &WebAXObjectProxy::MinValue) |
| .SetProperty("maxValue", &WebAXObjectProxy::MaxValue) |
| .SetProperty("stepValue", &WebAXObjectProxy::StepValue) |
| .SetProperty("valueDescription", &WebAXObjectProxy::ValueDescription) |
| .SetProperty("childrenCount", &WebAXObjectProxy::ChildrenCount) |
| .SetProperty("selectionIsBackward", |
| &WebAXObjectProxy::SelectionIsBackward) |
| .SetProperty("selectionAnchorObject", |
| &WebAXObjectProxy::SelectionAnchorObject) |
| .SetProperty("selectionAnchorOffset", |
| &WebAXObjectProxy::SelectionAnchorOffset) |
| .SetProperty("selectionAnchorAffinity", |
| &WebAXObjectProxy::SelectionAnchorAffinity) |
| .SetProperty("selectionFocusObject", |
| &WebAXObjectProxy::SelectionFocusObject) |
| .SetProperty("selectionFocusOffset", |
| &WebAXObjectProxy::SelectionFocusOffset) |
| .SetProperty("selectionFocusAffinity", |
| &WebAXObjectProxy::SelectionFocusAffinity) |
| .SetProperty("isAtomic", &WebAXObjectProxy::IsAtomic) |
| .SetProperty("isAutofillAvailable", |
| &WebAXObjectProxy::IsAutofillAvailable) |
| .SetProperty("isBusy", &WebAXObjectProxy::IsBusy) |
| .SetProperty("isRequired", &WebAXObjectProxy::IsRequired) |
| .SetProperty("isEditable", &WebAXObjectProxy::IsEditable) |
| .SetProperty("isEditableRoot", &WebAXObjectProxy::IsEditableRoot) |
| .SetProperty("isRichlyEditable", &WebAXObjectProxy::IsRichlyEditable) |
| .SetProperty("isFocused", &WebAXObjectProxy::IsFocused) |
| .SetProperty("isFocusable", &WebAXObjectProxy::IsFocusable) |
| .SetProperty("isModal", &WebAXObjectProxy::IsModal) |
| .SetProperty("isSelected", &WebAXObjectProxy::IsSelected) |
| .SetProperty("isSelectable", &WebAXObjectProxy::IsSelectable) |
| .SetProperty("isMultiLine", &WebAXObjectProxy::IsMultiLine) |
| .SetProperty("isMultiSelectable", &WebAXObjectProxy::IsMultiSelectable) |
| .SetProperty("isSelectedOptionActive", |
| &WebAXObjectProxy::IsSelectedOptionActive) |
| .SetProperty("isExpanded", &WebAXObjectProxy::IsExpanded) |
| .SetProperty("checked", &WebAXObjectProxy::Checked) |
| .SetProperty("isVisible", &WebAXObjectProxy::IsVisible) |
| .SetProperty("isVisited", &WebAXObjectProxy::IsVisited) |
| .SetProperty("isOffScreen", &WebAXObjectProxy::IsOffScreen) |
| .SetProperty("isCollapsed", &WebAXObjectProxy::IsCollapsed) |
| .SetProperty("hasPopup", &WebAXObjectProxy::HasPopup) |
| .SetProperty("isValid", &WebAXObjectProxy::IsValid) |
| .SetProperty("isReadOnly", &WebAXObjectProxy::IsReadOnly) |
| .SetProperty("isIgnored", &WebAXObjectProxy::IsIgnored) |
| .SetProperty("restriction", &WebAXObjectProxy::Restriction) |
| .SetProperty("activeDescendant", &WebAXObjectProxy::ActiveDescendant) |
| .SetProperty("backgroundColor", &WebAXObjectProxy::BackgroundColor) |
| .SetProperty("color", &WebAXObjectProxy::Color) |
| .SetProperty("colorValue", &WebAXObjectProxy::ColorValue) |
| .SetProperty("fontFamily", &WebAXObjectProxy::FontFamily) |
| .SetProperty("fontSize", &WebAXObjectProxy::FontSize) |
| .SetProperty("autocomplete", &WebAXObjectProxy::Autocomplete) |
| .SetProperty("current", &WebAXObjectProxy::Current) |
| .SetProperty("invalid", &WebAXObjectProxy::Invalid) |
| .SetProperty("keyShortcuts", &WebAXObjectProxy::KeyShortcuts) |
| .SetProperty("ariaColumnCount", &WebAXObjectProxy::AriaColumnCount) |
| .SetProperty("ariaColumnIndex", &WebAXObjectProxy::AriaColumnIndex) |
| .SetProperty("ariaColumnSpan", &WebAXObjectProxy::AriaColumnSpan) |
| .SetProperty("ariaRowCount", &WebAXObjectProxy::AriaRowCount) |
| .SetProperty("ariaRowIndex", &WebAXObjectProxy::AriaRowIndex) |
| .SetProperty("ariaRowSpan", &WebAXObjectProxy::AriaRowSpan) |
| .SetProperty("live", &WebAXObjectProxy::Live) |
| .SetProperty("orientation", &WebAXObjectProxy::Orientation) |
| .SetProperty("relevant", &WebAXObjectProxy::Relevant) |
| .SetProperty("roleDescription", &WebAXObjectProxy::RoleDescription) |
| .SetProperty("sort", &WebAXObjectProxy::Sort) |
| .SetProperty("url", &WebAXObjectProxy::Url) |
| .SetProperty("hierarchicalLevel", &WebAXObjectProxy::HierarchicalLevel) |
| .SetProperty("posInSet", &WebAXObjectProxy::PosInSet) |
| .SetProperty("setSize", &WebAXObjectProxy::SetSize) |
| .SetProperty("clickPointX", &WebAXObjectProxy::ClickPointX) |
| .SetProperty("clickPointY", &WebAXObjectProxy::ClickPointY) |
| .SetProperty("rowCount", &WebAXObjectProxy::RowCount) |
| .SetProperty("rowHeadersCount", &WebAXObjectProxy::RowHeadersCount) |
| .SetProperty("columnCount", &WebAXObjectProxy::ColumnCount) |
| .SetProperty("columnHeadersCount", &WebAXObjectProxy::ColumnHeadersCount) |
| .SetProperty("isClickable", &WebAXObjectProxy::IsClickable) |
| // |
| // NEW bounding rect calculation - high-level interface |
| // |
| .SetProperty("boundsX", &WebAXObjectProxy::BoundsX) |
| .SetProperty("boundsY", &WebAXObjectProxy::BoundsY) |
| .SetProperty("boundsWidth", &WebAXObjectProxy::BoundsWidth) |
| .SetProperty("boundsHeight", &WebAXObjectProxy::BoundsHeight) |
| .SetMethod("allAttributes", &WebAXObjectProxy::AllAttributes) |
| .SetMethod("attributesOfChildren", |
| &WebAXObjectProxy::AttributesOfChildren) |
| .SetMethod("ariaActiveDescendantElement", |
| &WebAXObjectProxy::AriaActiveDescendantElement) |
| .SetMethod("ariaControlsElementAtIndex", |
| &WebAXObjectProxy::AriaControlsElementAtIndex) |
| .SetMethod("ariaDetailsElementAtIndex", |
| &WebAXObjectProxy::AriaDetailsElementAtIndex) |
| .SetMethod("ariaErrorMessageElement", |
| &WebAXObjectProxy::AriaErrorMessageElement) |
| .SetMethod("ariaFlowToElementAtIndex", |
| &WebAXObjectProxy::AriaFlowToElementAtIndex) |
| .SetMethod("ariaOwnsElementAtIndex", |
| &WebAXObjectProxy::AriaOwnsElementAtIndex) |
| .SetMethod("boundsForRange", &WebAXObjectProxy::BoundsForRange) |
| .SetMethod("childAtIndex", &WebAXObjectProxy::ChildAtIndex) |
| .SetMethod("elementAtPoint", &WebAXObjectProxy::ElementAtPoint) |
| .SetMethod("rowHeaderAtIndex", &WebAXObjectProxy::RowHeaderAtIndex) |
| .SetMethod("columnHeaderAtIndex", &WebAXObjectProxy::ColumnHeaderAtIndex) |
| .SetMethod("rowIndexRange", &WebAXObjectProxy::RowIndexRange) |
| .SetMethod("columnIndexRange", &WebAXObjectProxy::ColumnIndexRange) |
| .SetMethod("cellForColumnAndRow", &WebAXObjectProxy::CellForColumnAndRow) |
| .SetMethod("setSelectedTextRange", |
| &WebAXObjectProxy::SetSelectedTextRange) |
| .SetMethod("setSelection", &WebAXObjectProxy::SetSelection) |
| .SetMethod("isAttributeSettable", &WebAXObjectProxy::IsAttributeSettable) |
| .SetMethod("isPressActionSupported", |
| &WebAXObjectProxy::IsPressActionSupported) |
| .SetMethod("parentElement", &WebAXObjectProxy::ParentElement) |
| .SetMethod("increment", &WebAXObjectProxy::Increment) |
| .SetMethod("decrement", &WebAXObjectProxy::Decrement) |
| .SetMethod("showMenu", &WebAXObjectProxy::ShowMenu) |
| .SetMethod("press", &WebAXObjectProxy::Press) |
| .SetMethod("setValue", &WebAXObjectProxy::SetValue) |
| .SetMethod("isEqual", &WebAXObjectProxy::IsEqual) |
| .SetMethod("setNotificationListener", |
| &WebAXObjectProxy::SetNotificationListener) |
| .SetMethod("unsetNotificationListener", |
| &WebAXObjectProxy::UnsetNotificationListener) |
| .SetMethod("takeFocus", &WebAXObjectProxy::TakeFocus) |
| .SetMethod("scrollToMakeVisible", &WebAXObjectProxy::ScrollToMakeVisible) |
| .SetMethod("scrollToMakeVisibleWithSubFocus", |
| &WebAXObjectProxy::ScrollToMakeVisibleWithSubFocus) |
| .SetMethod("scrollToGlobalPoint", &WebAXObjectProxy::ScrollToGlobalPoint) |
| .SetMethod("scrollX", &WebAXObjectProxy::ScrollX) |
| .SetMethod("scrollY", &WebAXObjectProxy::ScrollY) |
| .SetMethod("toString", &WebAXObjectProxy::ToString) |
| .SetMethod("wordStart", &WebAXObjectProxy::WordStart) |
| .SetMethod("wordEnd", &WebAXObjectProxy::WordEnd) |
| .SetMethod("nextOnLine", &WebAXObjectProxy::NextOnLine) |
| .SetMethod("previousOnLine", &WebAXObjectProxy::PreviousOnLine) |
| .SetMethod("misspellingAtIndex", &WebAXObjectProxy::MisspellingAtIndex) |
| // TODO(hajimehoshi): This is for backward compatibility. Remove them. |
| .SetMethod("addNotificationListener", |
| &WebAXObjectProxy::SetNotificationListener) |
| .SetMethod("removeNotificationListener", |
| &WebAXObjectProxy::UnsetNotificationListener) |
| // |
| // NEW accessible name and description accessors |
| // |
| .SetProperty("name", &WebAXObjectProxy::Name) |
| .SetProperty("nameFrom", &WebAXObjectProxy::NameFrom) |
| .SetMethod("nameElementCount", &WebAXObjectProxy::NameElementCount) |
| .SetMethod("nameElementAtIndex", &WebAXObjectProxy::NameElementAtIndex) |
| .SetProperty("description", &WebAXObjectProxy::Description) |
| .SetProperty("descriptionFrom", &WebAXObjectProxy::DescriptionFrom) |
| .SetProperty("placeholder", &WebAXObjectProxy::Placeholder) |
| .SetProperty("misspellingsCount", &WebAXObjectProxy::MisspellingsCount) |
| .SetMethod("descriptionElementCount", |
| &WebAXObjectProxy::DescriptionElementCount) |
| .SetMethod("descriptionElementAtIndex", |
| &WebAXObjectProxy::DescriptionElementAtIndex) |
| // |
| // NEW bounding rect calculation - low-level interface |
| // |
| .SetMethod("offsetContainer", &WebAXObjectProxy::OffsetContainer) |
| .SetMethod("boundsInContainerX", &WebAXObjectProxy::BoundsInContainerX) |
| .SetMethod("boundsInContainerY", &WebAXObjectProxy::BoundsInContainerY) |
| .SetMethod("boundsInContainerWidth", |
| &WebAXObjectProxy::BoundsInContainerWidth) |
| .SetMethod("boundsInContainerHeight", |
| &WebAXObjectProxy::BoundsInContainerHeight) |
| .SetMethod("hasNonIdentityTransform", |
| &WebAXObjectProxy::HasNonIdentityTransform); |
| } |
| |
| v8::Local<v8::Object> WebAXObjectProxy::GetChildAtIndex(unsigned index) { |
| UpdateLayout(); |
| return factory_->GetOrCreate(accessibility_object_.ChildAt(index)); |
| } |
| |
| bool WebAXObjectProxy::IsRoot() const { |
| return false; |
| } |
| |
| bool WebAXObjectProxy::IsEqualToObject(const blink::WebAXObject& other) { |
| return accessibility_object_.Equals(other); |
| } |
| |
| void WebAXObjectProxy::NotificationReceived( |
| blink::WebLocalFrame* frame, |
| const std::string& notification_name, |
| const std::vector<ui::AXEventIntent>& event_intents) { |
| if (notification_callback_.IsEmpty()) |
| return; |
| |
| v8::Local<v8::Context> context = frame->MainWorldScriptContext(); |
| if (context.IsEmpty()) |
| return; |
| |
| v8::Isolate* isolate = blink::MainThreadIsolate(); |
| |
| v8::Local<v8::Array> intents_array( |
| v8::Array::New(isolate, event_intents.size())); |
| for (size_t i = 0; i < event_intents.size(); ++i) { |
| intents_array |
| ->CreateDataProperty(context, uint32_t{i}, |
| v8::String::NewFromUtf8( |
| isolate, event_intents[i].ToString().c_str()) |
| .ToLocalChecked()) |
| .Check(); |
| } |
| |
| v8::Local<v8::Value> argv[] = { |
| v8::String::NewFromUtf8(isolate, notification_name.c_str()) |
| .ToLocalChecked(), |
| intents_array}; |
| // TODO(aboxhall): Can we force this to run in a new task, to avoid |
| // dirtying layout during post-layout hooks? |
| frame->CallFunctionEvenIfScriptDisabled( |
| v8::Local<v8::Function>::New(isolate, notification_callback_), |
| context->Global(), base::size(argv), argv); |
| } |
| |
| void WebAXObjectProxy::Reset() { |
| notification_callback_.Reset(); |
| } |
| |
| std::string WebAXObjectProxy::Role() { |
| UpdateLayout(); |
| return GetRole(accessibility_object_); |
| } |
| |
| std::string WebAXObjectProxy::StringValue() { |
| UpdateLayout(); |
| return GetStringValue(accessibility_object_); |
| } |
| |
| std::string WebAXObjectProxy::Language() { |
| UpdateLayout(); |
| return GetLanguage(accessibility_object_); |
| } |
| |
| int WebAXObjectProxy::X() { |
| UpdateLayout(); |
| return BoundsForObject(accessibility_object_).x(); |
| } |
| |
| int WebAXObjectProxy::Y() { |
| UpdateLayout(); |
| return BoundsForObject(accessibility_object_).y(); |
| } |
| |
| int WebAXObjectProxy::Width() { |
| UpdateLayout(); |
| return BoundsForObject(accessibility_object_).width(); |
| } |
| |
| int WebAXObjectProxy::Height() { |
| UpdateLayout(); |
| return BoundsForObject(accessibility_object_).height(); |
| } |
| |
| v8::Local<v8::Value> WebAXObjectProxy::InPageLinkTarget() { |
| UpdateLayout(); |
| blink::WebAXObject target = accessibility_object_.InPageLinkTarget(); |
| if (target.IsNull()) |
| return v8::Null(blink::MainThreadIsolate()); |
| return factory_->GetOrCreate(target); |
| } |
| |
| int WebAXObjectProxy::IntValue() { |
| UpdateLayout(); |
| |
| if (accessibility_object_.SupportsRangeValue()) { |
| float value = 0.0f; |
| accessibility_object_.ValueForRange(&value); |
| return static_cast<int>(value); |
| } else if (accessibility_object_.Role() == ax::mojom::Role::kHeading) { |
| return accessibility_object_.HeadingLevel(); |
| } else { |
| return atoi(accessibility_object_.GetValueForControl().Utf8().data()); |
| } |
| } |
| |
| int WebAXObjectProxy::MinValue() { |
| UpdateLayout(); |
| float min_value = 0.0f; |
| accessibility_object_.MinValueForRange(&min_value); |
| return min_value; |
| } |
| |
| int WebAXObjectProxy::MaxValue() { |
| UpdateLayout(); |
| float max_value = 0.0f; |
| accessibility_object_.MaxValueForRange(&max_value); |
| return max_value; |
| } |
| |
| int WebAXObjectProxy::StepValue() { |
| UpdateLayout(); |
| float step_value = 0.0f; |
| accessibility_object_.StepValueForRange(&step_value); |
| return step_value; |
| } |
| |
| std::string WebAXObjectProxy::ValueDescription() { |
| UpdateLayout(); |
| std::string value_description = |
| GetAXNodeData().GetStringAttribute(ax::mojom::StringAttribute::kValue); |
| return value_description.insert(0, "AXValueDescription: "); |
| } |
| |
| int WebAXObjectProxy::ChildrenCount() { |
| UpdateLayout(); |
| int count = 1; // Root object always has only one child, the WebView. |
| if (!IsRoot()) |
| count = accessibility_object_.ChildCount(); |
| return count; |
| } |
| |
| bool WebAXObjectProxy::SelectionIsBackward() { |
| UpdateLayout(); |
| |
| bool is_selection_backward = false; |
| blink::WebAXObject anchor_object; |
| int anchor_offset = -1; |
| ax::mojom::TextAffinity anchor_affinity; |
| blink::WebAXObject focus_object; |
| int focus_offset = -1; |
| ax::mojom::TextAffinity focus_affinity; |
| accessibility_object_.Selection(is_selection_backward, anchor_object, |
| anchor_offset, anchor_affinity, focus_object, |
| focus_offset, focus_affinity); |
| return is_selection_backward; |
| } |
| |
| v8::Local<v8::Value> WebAXObjectProxy::SelectionAnchorObject() { |
| UpdateLayout(); |
| |
| bool is_selection_backward = false; |
| blink::WebAXObject anchor_object; |
| int anchor_offset = -1; |
| ax::mojom::TextAffinity anchor_affinity; |
| blink::WebAXObject focus_object; |
| int focus_offset = -1; |
| ax::mojom::TextAffinity focus_affinity; |
| accessibility_object_.Selection(is_selection_backward, anchor_object, |
| anchor_offset, anchor_affinity, focus_object, |
| focus_offset, focus_affinity); |
| if (anchor_object.IsNull()) |
| return v8::Null(blink::MainThreadIsolate()); |
| |
| return factory_->GetOrCreate(anchor_object); |
| } |
| |
| int WebAXObjectProxy::SelectionAnchorOffset() { |
| UpdateLayout(); |
| |
| bool is_selection_backward = false; |
| blink::WebAXObject anchor_object; |
| int anchor_offset = -1; |
| ax::mojom::TextAffinity anchor_affinity; |
| blink::WebAXObject focus_object; |
| int focus_offset = -1; |
| ax::mojom::TextAffinity focus_affinity; |
| accessibility_object_.Selection(is_selection_backward, anchor_object, |
| anchor_offset, anchor_affinity, focus_object, |
| focus_offset, focus_affinity); |
| if (anchor_offset < 0) |
| return -1; |
| |
| return anchor_offset; |
| } |
| |
| std::string WebAXObjectProxy::SelectionAnchorAffinity() { |
| UpdateLayout(); |
| |
| bool is_selection_backward = false; |
| blink::WebAXObject anchor_object; |
| int anchor_offset = -1; |
| ax::mojom::TextAffinity anchor_affinity; |
| blink::WebAXObject focus_object; |
| int focus_offset = -1; |
| ax::mojom::TextAffinity focus_affinity; |
| accessibility_object_.Selection(is_selection_backward, anchor_object, |
| anchor_offset, anchor_affinity, focus_object, |
| focus_offset, focus_affinity); |
| return anchor_affinity == ax::mojom::TextAffinity::kUpstream ? "upstream" |
| : "downstream"; |
| } |
| |
| v8::Local<v8::Value> WebAXObjectProxy::SelectionFocusObject() { |
| UpdateLayout(); |
| |
| bool is_selection_backward = false; |
| blink::WebAXObject anchor_object; |
| int anchor_offset = -1; |
| ax::mojom::TextAffinity anchor_affinity; |
| blink::WebAXObject focus_object; |
| int focus_offset = -1; |
| ax::mojom::TextAffinity focus_affinity; |
| accessibility_object_.Selection(is_selection_backward, anchor_object, |
| anchor_offset, anchor_affinity, focus_object, |
| focus_offset, focus_affinity); |
| if (focus_object.IsNull()) |
| return v8::Null(blink::MainThreadIsolate()); |
| |
| return factory_->GetOrCreate(focus_object); |
| } |
| |
| int WebAXObjectProxy::SelectionFocusOffset() { |
| UpdateLayout(); |
| |
| bool is_selection_backward = false; |
| blink::WebAXObject anchor_object; |
| int anchor_offset = -1; |
| ax::mojom::TextAffinity anchor_affinity; |
| blink::WebAXObject focus_object; |
| int focus_offset = -1; |
| ax::mojom::TextAffinity focus_affinity; |
| accessibility_object_.Selection(is_selection_backward, anchor_object, |
| anchor_offset, anchor_affinity, focus_object, |
| focus_offset, focus_affinity); |
| if (focus_offset < 0) |
| return -1; |
| |
| return focus_offset; |
| } |
| |
| std::string WebAXObjectProxy::SelectionFocusAffinity() { |
| UpdateLayout(); |
| |
| bool is_selection_backward = false; |
| blink::WebAXObject anchor_object; |
| int anchor_offset = -1; |
| ax::mojom::TextAffinity anchor_affinity; |
| blink::WebAXObject focus_object; |
| int focus_offset = -1; |
| ax::mojom::TextAffinity focus_affinity; |
| accessibility_object_.Selection(is_selection_backward, anchor_object, |
| anchor_offset, anchor_affinity, focus_object, |
| focus_offset, focus_affinity); |
| return focus_affinity == ax::mojom::TextAffinity::kUpstream ? "upstream" |
| : "downstream"; |
| } |
| |
| bool WebAXObjectProxy::IsAtomic() { |
| UpdateLayout(); |
| return accessibility_object_.LiveRegionAtomic(); |
| } |
| |
| bool WebAXObjectProxy::IsAutofillAvailable() { |
| UpdateLayout(); |
| return GetAXNodeData().HasState(ax::mojom::State::kAutofillAvailable); |
| } |
| |
| bool WebAXObjectProxy::IsBusy() { |
| UpdateLayout(); |
| return GetAXNodeData().GetBoolAttribute(ax::mojom::BoolAttribute::kBusy); |
| } |
| |
| std::string WebAXObjectProxy::Restriction() { |
| UpdateLayout(); |
| blink::WebAXRestriction web_ax_restriction = |
| static_cast<blink::WebAXRestriction>(GetAXNodeData().GetRestriction()); |
| switch (web_ax_restriction) { |
| case blink::kWebAXRestrictionReadOnly: |
| return "readOnly"; |
| case blink::kWebAXRestrictionDisabled: |
| return "disabled"; |
| case blink::kWebAXRestrictionNone: |
| break; |
| } |
| return "none"; |
| } |
| |
| bool WebAXObjectProxy::IsRequired() { |
| UpdateLayout(); |
| return GetAXNodeData().HasState(ax::mojom::State::kRequired); |
| } |
| |
| bool WebAXObjectProxy::IsEditableRoot() { |
| UpdateLayout(); |
| return GetAXNodeData().GetBoolAttribute( |
| ax::mojom::BoolAttribute::kEditableRoot); |
| } |
| |
| bool WebAXObjectProxy::IsEditable() { |
| UpdateLayout(); |
| return GetAXNodeData().HasState(ax::mojom::State::kEditable); |
| } |
| |
| bool WebAXObjectProxy::IsRichlyEditable() { |
| UpdateLayout(); |
| return GetAXNodeData().HasState(ax::mojom::State::kRichlyEditable); |
| } |
| |
| bool WebAXObjectProxy::IsFocused() { |
| UpdateLayout(); |
| return accessibility_object_.IsFocused(); |
| } |
| |
| bool WebAXObjectProxy::IsFocusable() { |
| UpdateLayout(); |
| return GetAXNodeData().HasState(ax::mojom::State::kFocusable); |
| } |
| |
| bool WebAXObjectProxy::IsModal() { |
| UpdateLayout(); |
| return accessibility_object_.IsModal(); |
| } |
| |
| bool WebAXObjectProxy::IsSelected() { |
| UpdateLayout(); |
| return GetAXNodeData().GetBoolAttribute(ax::mojom::BoolAttribute::kSelected); |
| } |
| |
| bool WebAXObjectProxy::IsSelectable() { |
| UpdateLayout(); |
| ui::AXNodeData node_data = GetAXNodeData(); |
| // It's selectable if it has the attribute, whether it's true or false. |
| return node_data.HasBoolAttribute(ax::mojom::BoolAttribute::kSelected) && |
| node_data.GetRestriction() != ax::mojom::Restriction::kDisabled; |
| } |
| |
| bool WebAXObjectProxy::IsMultiLine() { |
| UpdateLayout(); |
| return GetAXNodeData().HasState(ax::mojom::State::kMultiline); |
| } |
| |
| bool WebAXObjectProxy::IsMultiSelectable() { |
| UpdateLayout(); |
| return GetAXNodeData().HasState(ax::mojom::State::kMultiselectable); |
| } |
| |
| bool WebAXObjectProxy::IsSelectedOptionActive() { |
| UpdateLayout(); |
| return accessibility_object_.IsSelectedOptionActive(); |
| } |
| |
| bool WebAXObjectProxy::IsExpanded() { |
| UpdateLayout(); |
| return GetAXNodeData().HasState(ax::mojom::State::kExpanded); |
| } |
| |
| std::string WebAXObjectProxy::Checked() { |
| UpdateLayout(); |
| switch (accessibility_object_.CheckedState()) { |
| case ax::mojom::CheckedState::kTrue: |
| return "true"; |
| case ax::mojom::CheckedState::kMixed: |
| return "mixed"; |
| case ax::mojom::CheckedState::kFalse: |
| return "false"; |
| default: |
| return std::string(); |
| } |
| } |
| |
| bool WebAXObjectProxy::IsCollapsed() { |
| UpdateLayout(); |
| return GetAXNodeData().HasState(ax::mojom::State::kCollapsed); |
| } |
| |
| bool WebAXObjectProxy::IsVisible() { |
| UpdateLayout(); |
| return !GetAXNodeData().HasState(ax::mojom::State::kInvisible); |
| } |
| |
| bool WebAXObjectProxy::IsVisited() { |
| UpdateLayout(); |
| return accessibility_object_.IsVisited(); |
| } |
| |
| bool WebAXObjectProxy::IsOffScreen() { |
| UpdateLayout(); |
| return accessibility_object_.IsOffScreen(); |
| } |
| |
| bool WebAXObjectProxy::IsValid() { |
| UpdateLayout(); |
| return !accessibility_object_.IsDetached(); |
| } |
| |
| bool WebAXObjectProxy::IsReadOnly() { |
| UpdateLayout(); |
| return GetAXNodeData().GetRestriction() == ax::mojom::Restriction::kReadOnly; |
| } |
| |
| bool WebAXObjectProxy::IsIgnored() { |
| UpdateLayout(); |
| return accessibility_object_.AccessibilityIsIgnored(); |
| } |
| |
| v8::Local<v8::Object> WebAXObjectProxy::ActiveDescendant() { |
| UpdateLayout(); |
| blink::WebAXObject element = accessibility_object_.AriaActiveDescendant(); |
| return factory_->GetOrCreate(element); |
| } |
| |
| unsigned int WebAXObjectProxy::BackgroundColor() { |
| UpdateLayout(); |
| return GetAXNodeData().GetIntAttribute( |
| ax::mojom::IntAttribute::kBackgroundColor); |
| } |
| |
| unsigned int WebAXObjectProxy::Color() { |
| UpdateLayout(); |
| unsigned int color = |
| GetAXNodeData().GetIntAttribute(ax::mojom::IntAttribute::kColor); |
| // Remove the alpha because it's always 1 and thus not informative. |
| return color & 0xFFFFFF; |
| } |
| |
| // For input elements of type color. |
| unsigned int WebAXObjectProxy::ColorValue() { |
| UpdateLayout(); |
| return accessibility_object_.ColorValue(); |
| } |
| |
| std::string WebAXObjectProxy::FontFamily() { |
| UpdateLayout(); |
| std::string font_family = GetAXNodeData().GetStringAttribute( |
| ax::mojom::StringAttribute::kFontFamily); |
| return font_family.insert(0, "AXFontFamily: "); |
| } |
| |
| float WebAXObjectProxy::FontSize() { |
| UpdateLayout(); |
| return GetAXNodeData().GetFloatAttribute( |
| ax::mojom::FloatAttribute::kFontSize); |
| } |
| |
| std::string WebAXObjectProxy::Autocomplete() { |
| UpdateLayout(); |
| return accessibility_object_.AutoComplete().Utf8(); |
| } |
| |
| std::string WebAXObjectProxy::Current() { |
| UpdateLayout(); |
| switch (accessibility_object_.AriaCurrentState()) { |
| case ax::mojom::AriaCurrentState::kFalse: |
| return "false"; |
| case ax::mojom::AriaCurrentState::kTrue: |
| return "true"; |
| case ax::mojom::AriaCurrentState::kPage: |
| return "page"; |
| case ax::mojom::AriaCurrentState::kStep: |
| return "step"; |
| case ax::mojom::AriaCurrentState::kLocation: |
| return "location"; |
| case ax::mojom::AriaCurrentState::kDate: |
| return "date"; |
| case ax::mojom::AriaCurrentState::kTime: |
| return "time"; |
| default: |
| return std::string(); |
| } |
| } |
| |
| std::string WebAXObjectProxy::HasPopup() { |
| UpdateLayout(); |
| switch (GetAXNodeData().GetHasPopup()) { |
| case ax::mojom::HasPopup::kTrue: |
| return "true"; |
| case ax::mojom::HasPopup::kMenu: |
| return "menu"; |
| case ax::mojom::HasPopup::kListbox: |
| return "listbox"; |
| case ax::mojom::HasPopup::kTree: |
| return "tree"; |
| case ax::mojom::HasPopup::kGrid: |
| return "grid"; |
| case ax::mojom::HasPopup::kDialog: |
| return "dialog"; |
| default: |
| return std::string(); |
| } |
| } |
| |
| std::string WebAXObjectProxy::Invalid() { |
| UpdateLayout(); |
| switch (accessibility_object_.InvalidState()) { |
| case ax::mojom::InvalidState::kFalse: |
| return "false"; |
| case ax::mojom::InvalidState::kTrue: |
| return "true"; |
| case ax::mojom::InvalidState::kOther: |
| return "other"; |
| default: |
| return std::string(); |
| } |
| } |
| |
| std::string WebAXObjectProxy::KeyShortcuts() { |
| UpdateLayout(); |
| return GetAXNodeData().GetStringAttribute( |
| ax::mojom::StringAttribute::kKeyShortcuts); |
| } |
| |
| int32_t WebAXObjectProxy::AriaColumnCount() { |
| UpdateLayout(); |
| return GetAXNodeData().GetIntAttribute( |
| ax::mojom::IntAttribute::kAriaColumnCount); |
| } |
| |
| uint32_t WebAXObjectProxy::AriaColumnIndex() { |
| UpdateLayout(); |
| return GetAXNodeData().GetIntAttribute( |
| ax::mojom::IntAttribute::kAriaCellColumnIndex); |
| } |
| |
| uint32_t WebAXObjectProxy::AriaColumnSpan() { |
| UpdateLayout(); |
| return GetAXNodeData().GetIntAttribute( |
| ax::mojom::IntAttribute::kAriaCellColumnSpan); |
| } |
| |
| int32_t WebAXObjectProxy::AriaRowCount() { |
| UpdateLayout(); |
| return GetAXNodeData().GetIntAttribute( |
| ax::mojom::IntAttribute::kAriaRowCount); |
| } |
| |
| uint32_t WebAXObjectProxy::AriaRowIndex() { |
| UpdateLayout(); |
| return GetAXNodeData().GetIntAttribute( |
| ax::mojom::IntAttribute::kAriaCellRowIndex); |
| } |
| |
| uint32_t WebAXObjectProxy::AriaRowSpan() { |
| UpdateLayout(); |
| return GetAXNodeData().GetIntAttribute( |
| ax::mojom::IntAttribute::kAriaCellRowSpan); |
| } |
| |
| std::string WebAXObjectProxy::Live() { |
| UpdateLayout(); |
| return accessibility_object_.LiveRegionStatus().Utf8(); |
| } |
| |
| std::string WebAXObjectProxy::Orientation() { |
| UpdateLayout(); |
| ui::AXNodeData node_data = GetAXNodeData(); |
| if (node_data.HasState(ax::mojom::State::kVertical)) |
| return "AXOrientation: AXVerticalOrientation"; |
| else if (node_data.HasState(ax::mojom::State::kHorizontal)) |
| return "AXOrientation: AXHorizontalOrientation"; |
| return std::string(); |
| } |
| |
| std::string WebAXObjectProxy::Relevant() { |
| UpdateLayout(); |
| return accessibility_object_.LiveRegionRelevant().Utf8(); |
| } |
| |
| std::string WebAXObjectProxy::RoleDescription() { |
| UpdateLayout(); |
| return GetAXNodeData().GetStringAttribute( |
| ax::mojom::StringAttribute::kRoleDescription); |
| } |
| |
| std::string WebAXObjectProxy::Sort() { |
| UpdateLayout(); |
| switch (accessibility_object_.SortDirection()) { |
| case ax::mojom::SortDirection::kAscending: |
| return "ascending"; |
| case ax::mojom::SortDirection::kDescending: |
| return "descending"; |
| case ax::mojom::SortDirection::kOther: |
| return "other"; |
| default: |
| return std::string(); |
| } |
| } |
| |
| std::string WebAXObjectProxy::Url() { |
| UpdateLayout(); |
| return accessibility_object_.Url().GetString().Utf8(); |
| } |
| |
| int WebAXObjectProxy::HierarchicalLevel() { |
| UpdateLayout(); |
| return accessibility_object_.HierarchicalLevel(); |
| } |
| |
| int WebAXObjectProxy::PosInSet() { |
| UpdateLayout(); |
| return GetAXNodeData().GetIntAttribute(ax::mojom::IntAttribute::kPosInSet); |
| } |
| |
| int WebAXObjectProxy::SetSize() { |
| UpdateLayout(); |
| return GetAXNodeData().GetIntAttribute(ax::mojom::IntAttribute::kSetSize); |
| } |
| |
| int WebAXObjectProxy::ClickPointX() { |
| UpdateLayout(); |
| gfx::RectF bounds = BoundsForObject(accessibility_object_); |
| return bounds.x() + bounds.width() / 2; |
| } |
| |
| int WebAXObjectProxy::ClickPointY() { |
| UpdateLayout(); |
| gfx::RectF bounds = BoundsForObject(accessibility_object_); |
| return bounds.y() + bounds.height() / 2; |
| } |
| |
| int32_t WebAXObjectProxy::RowCount() { |
| UpdateLayout(); |
| return static_cast<int32_t>(accessibility_object_.RowCount()); |
| } |
| |
| int32_t WebAXObjectProxy::RowHeadersCount() { |
| UpdateLayout(); |
| blink::WebVector<blink::WebAXObject> headers; |
| accessibility_object_.RowHeaders(headers); |
| return static_cast<int32_t>(headers.size()); |
| } |
| |
| int32_t WebAXObjectProxy::ColumnCount() { |
| UpdateLayout(); |
| return static_cast<int32_t>(accessibility_object_.ColumnCount()); |
| } |
| |
| int32_t WebAXObjectProxy::ColumnHeadersCount() { |
| UpdateLayout(); |
| blink::WebVector<blink::WebAXObject> headers; |
| accessibility_object_.ColumnHeaders(headers); |
| return static_cast<int32_t>(headers.size()); |
| } |
| |
| bool WebAXObjectProxy::IsClickable() { |
| UpdateLayout(); |
| return accessibility_object_.IsClickable(); |
| } |
| |
| v8::Local<v8::Object> WebAXObjectProxy::AriaActiveDescendantElement() { |
| UpdateLayout(); |
| int ax_id = GetAXNodeData().GetIntAttribute( |
| ax::mojom::IntAttribute::kActivedescendantId); |
| |
| if (!ax_id) |
| return v8::Local<v8::Object>(); |
| |
| blink::WebAXObject web_ax_object = blink::WebAXObject::FromWebDocumentByID( |
| accessibility_object_.GetDocument(), ax_id); |
| return factory_->GetOrCreate(web_ax_object); |
| } |
| |
| v8::Local<v8::Object> WebAXObjectProxy::AriaControlsElementAtIndex( |
| unsigned index) { |
| UpdateLayout(); |
| auto ax_ids = GetAXNodeData().GetIntListAttribute( |
| ax::mojom::IntListAttribute::kControlsIds); |
| size_t element_count = ax_ids.size(); |
| |
| if (index >= element_count) |
| return v8::Local<v8::Object>(); |
| |
| blink::WebAXObject web_ax_object = blink::WebAXObject::FromWebDocumentByID( |
| accessibility_object_.GetDocument(), ax_ids[index]); |
| |
| return factory_->GetOrCreate(web_ax_object); |
| } |
| |
| v8::Local<v8::Object> WebAXObjectProxy::AriaDetailsElementAtIndex( |
| unsigned index) { |
| UpdateLayout(); |
| auto ax_ids = GetAXNodeData().GetIntListAttribute( |
| ax::mojom::IntListAttribute::kDetailsIds); |
| size_t element_count = ax_ids.size(); |
| |
| if (index >= element_count) |
| return v8::Local<v8::Object>(); |
| |
| blink::WebAXObject web_ax_object = blink::WebAXObject::FromWebDocumentByID( |
| accessibility_object_.GetDocument(), ax_ids[index]); |
| |
| return factory_->GetOrCreate(web_ax_object); |
| } |
| |
| v8::Local<v8::Object> WebAXObjectProxy::AriaErrorMessageElement() { |
| UpdateLayout(); |
| int ax_id = |
| GetAXNodeData().GetIntAttribute(ax::mojom::IntAttribute::kErrormessageId); |
| |
| if (!ax_id) |
| return v8::Local<v8::Object>(); |
| |
| blink::WebAXObject web_ax_object = blink::WebAXObject::FromWebDocumentByID( |
| accessibility_object_.GetDocument(), ax_id); |
| return factory_->GetOrCreate(web_ax_object); |
| } |
| |
| v8::Local<v8::Object> WebAXObjectProxy::AriaFlowToElementAtIndex( |
| unsigned index) { |
| UpdateLayout(); |
| auto ax_ids = GetAXNodeData().GetIntListAttribute( |
| ax::mojom::IntListAttribute::kFlowtoIds); |
| size_t element_count = ax_ids.size(); |
| |
| if (index >= element_count) |
| return v8::Local<v8::Object>(); |
| |
| blink::WebAXObject web_ax_object = blink::WebAXObject::FromWebDocumentByID( |
| accessibility_object_.GetDocument(), ax_ids[index]); |
| |
| return factory_->GetOrCreate(web_ax_object); |
| } |
| |
| v8::Local<v8::Object> WebAXObjectProxy::AriaOwnsElementAtIndex(unsigned index) { |
| UpdateLayout(); |
| blink::WebVector<blink::WebAXObject> elements; |
| accessibility_object_.AriaOwns(elements); |
| size_t element_count = elements.size(); |
| if (index >= element_count) |
| return v8::Local<v8::Object>(); |
| |
| return factory_->GetOrCreate(elements[index]); |
| } |
| |
| std::string WebAXObjectProxy::AllAttributes() { |
| UpdateLayout(); |
| return GetAttributes(accessibility_object_); |
| } |
| |
| std::string WebAXObjectProxy::AttributesOfChildren() { |
| UpdateLayout(); |
| AttributesCollector collector; |
| unsigned size = accessibility_object_.ChildCount(); |
| for (unsigned i = 0; i < size; ++i) |
| collector.CollectAttributes(accessibility_object_.ChildAt(i)); |
| return collector.attributes(); |
| } |
| |
| std::string WebAXObjectProxy::BoundsForRange(int start, int end) { |
| UpdateLayout(); |
| if (accessibility_object_.Role() != ax::mojom::Role::kStaticText) |
| return std::string(); |
| |
| if (!accessibility_object_.MaybeUpdateLayoutAndCheckValidity()) |
| return std::string(); |
| |
| 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. |
| gfx::Rect bounds = BoundsForCharacter(accessibility_object_, start); |
| for (int i = 1; i < len; i++) { |
| gfx::Rect next = BoundsForCharacter(accessibility_object_, 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.set_x(std::min(bounds.x(), next.x())); |
| bounds.set_y(std::min(bounds.y(), next.y())); |
| bounds.set_width(right - bounds.x()); |
| bounds.set_height(bottom - bounds.y()); |
| } |
| |
| return base::StringPrintf("{x: %d, y: %d, width: %d, height: %d}", bounds.x(), |
| bounds.y(), bounds.width(), bounds.height()); |
| } |
| |
| v8::Local<v8::Object> WebAXObjectProxy::ChildAtIndex(int index) { |
| // Scripts can sometimes provide bad input. |
| // Return undefined object in case of range error, rather than passing a bad |
| // index into the a11y core, where it would trigger a DCHECK. |
| int num_children = ChildrenCount(); |
| if (index < 0 || index >= num_children) |
| return v8::Local<v8::Object>(); |
| |
| return GetChildAtIndex(index); |
| } |
| |
| v8::Local<v8::Object> WebAXObjectProxy::ElementAtPoint(int x, int y) { |
| UpdateLayout(); |
| gfx::Point point(x, y); |
| blink::WebAXObject obj = accessibility_object_.HitTest(point); |
| if (obj.IsNull()) |
| return v8::Local<v8::Object>(); |
| |
| return factory_->GetOrCreate(obj); |
| } |
| |
| v8::Local<v8::Object> WebAXObjectProxy::RowHeaderAtIndex(unsigned index) { |
| UpdateLayout(); |
| blink::WebVector<blink::WebAXObject> headers; |
| accessibility_object_.RowHeaders(headers); |
| size_t header_count = headers.size(); |
| if (index >= header_count) |
| return {}; |
| |
| return factory_->GetOrCreate(headers[index]); |
| } |
| |
| v8::Local<v8::Object> WebAXObjectProxy::ColumnHeaderAtIndex(unsigned index) { |
| UpdateLayout(); |
| blink::WebVector<blink::WebAXObject> headers; |
| accessibility_object_.ColumnHeaders(headers); |
| size_t header_count = headers.size(); |
| if (index >= header_count) |
| return {}; |
| |
| return factory_->GetOrCreate(headers[index]); |
| } |
| |
| std::string WebAXObjectProxy::RowIndexRange() { |
| UpdateLayout(); |
| unsigned row_index = accessibility_object_.CellRowIndex(); |
| unsigned row_span = accessibility_object_.CellRowSpan(); |
| return base::StringPrintf("{%d, %d}", row_index, row_span); |
| } |
| |
| std::string WebAXObjectProxy::ColumnIndexRange() { |
| UpdateLayout(); |
| unsigned column_index = accessibility_object_.CellColumnIndex(); |
| unsigned column_span = accessibility_object_.CellColumnSpan(); |
| return base::StringPrintf("{%d, %d}", column_index, column_span); |
| } |
| |
| v8::Local<v8::Object> WebAXObjectProxy::CellForColumnAndRow(int column, |
| int row) { |
| UpdateLayout(); |
| blink::WebAXObject obj = |
| accessibility_object_.CellForColumnAndRow(column, row); |
| if (obj.IsNull()) |
| return v8::Local<v8::Object>(); |
| |
| return factory_->GetOrCreate(obj); |
| } |
| |
| void WebAXObjectProxy::SetSelectedTextRange(int selection_start, int length) { |
| UpdateLayout(); |
| accessibility_object_.SetSelection(accessibility_object_, selection_start, |
| accessibility_object_, |
| selection_start + length); |
| } |
| |
| bool WebAXObjectProxy::SetSelection(v8::Local<v8::Value> anchor_object, |
| int anchor_offset, |
| v8::Local<v8::Value> focus_object, |
| int focus_offset) { |
| if (anchor_object.IsEmpty() || focus_object.IsEmpty() || |
| !anchor_object->IsObject() || !focus_object->IsObject() || |
| anchor_offset < 0 || focus_offset < 0) { |
| return false; |
| } |
| |
| WebAXObjectProxy* web_ax_anchor = nullptr; |
| if (!gin::ConvertFromV8(blink::MainThreadIsolate(), anchor_object, |
| &web_ax_anchor)) { |
| return false; |
| } |
| DCHECK(web_ax_anchor); |
| |
| WebAXObjectProxy* web_ax_focus = nullptr; |
| if (!gin::ConvertFromV8(blink::MainThreadIsolate(), focus_object, |
| &web_ax_focus)) { |
| return false; |
| } |
| DCHECK(web_ax_focus); |
| |
| UpdateLayout(); |
| return accessibility_object_.SetSelection( |
| web_ax_anchor->accessibility_object_, anchor_offset, |
| web_ax_focus->accessibility_object_, focus_offset); |
| } |
| |
| bool WebAXObjectProxy::IsAttributeSettable(const std::string& attribute) { |
| UpdateLayout(); |
| bool settable = false; |
| if (attribute == "AXValue") |
| settable = accessibility_object_.CanSetValueAttribute(); |
| return settable; |
| } |
| |
| bool WebAXObjectProxy::IsPressActionSupported() { |
| UpdateLayout(); |
| return accessibility_object_.CanPress(); |
| } |
| |
| v8::Local<v8::Object> WebAXObjectProxy::ParentElement() { |
| UpdateLayout(); |
| blink::WebAXObject parent_object = accessibility_object_.ParentObject(); |
| return factory_->GetOrCreate(parent_object); |
| } |
| |
| void WebAXObjectProxy::Increment() { |
| UpdateLayout(); |
| ui::AXActionData action_data; |
| action_data.action = ax::mojom::Action::kIncrement; |
| accessibility_object_.PerformAction(action_data); |
| } |
| |
| void WebAXObjectProxy::Decrement() { |
| UpdateLayout(); |
| ui::AXActionData action_data; |
| action_data.action = ax::mojom::Action::kDecrement; |
| accessibility_object_.PerformAction(action_data); |
| } |
| |
| void WebAXObjectProxy::ShowMenu() { |
| UpdateLayout(); |
| ui::AXActionData action_data; |
| action_data.action = ax::mojom::Action::kShowContextMenu; |
| accessibility_object_.PerformAction(action_data); |
| } |
| |
| void WebAXObjectProxy::Press() { |
| UpdateLayout(); |
| ui::AXActionData action_data; |
| action_data.action = ax::mojom::Action::kDoDefault; |
| accessibility_object_.PerformAction(action_data); |
| } |
| |
| bool WebAXObjectProxy::SetValue(const std::string& value) { |
| UpdateLayout(); |
| if (GetAXNodeData().GetRestriction() != ax::mojom::Restriction::kNone || |
| accessibility_object_.GetValueForControl().IsEmpty()) { |
| return false; |
| } |
| |
| ui::AXActionData action_data; |
| action_data.action = ax::mojom::Action::kSetValue; |
| action_data.value = value; |
| return accessibility_object_.PerformAction(action_data); |
| } |
| |
| bool WebAXObjectProxy::IsEqual(v8::Local<v8::Object> proxy) { |
| WebAXObjectProxy* unwrapped_proxy = nullptr; |
| if (!gin::ConvertFromV8(blink::MainThreadIsolate(), proxy, &unwrapped_proxy)) |
| return false; |
| return unwrapped_proxy->IsEqualToObject(accessibility_object_); |
| } |
| |
| void WebAXObjectProxy::SetNotificationListener( |
| v8::Local<v8::Function> callback) { |
| v8::Isolate* isolate = blink::MainThreadIsolate(); |
| notification_callback_.Reset(isolate, callback); |
| } |
| |
| void WebAXObjectProxy::UnsetNotificationListener() { |
| notification_callback_.Reset(); |
| } |
| |
| void WebAXObjectProxy::TakeFocus() { |
| UpdateLayout(); |
| ui::AXActionData action_data; |
| action_data.action = ax::mojom::Action::kFocus; |
| accessibility_object_.PerformAction(action_data); |
| } |
| |
| void WebAXObjectProxy::ScrollToMakeVisible() { |
| UpdateLayout(); |
| accessibility_object_.ScrollToMakeVisible(); |
| } |
| |
| void WebAXObjectProxy::ScrollToMakeVisibleWithSubFocus(int x, |
| int y, |
| int width, |
| int height) { |
| UpdateLayout(); |
| accessibility_object_.ScrollToMakeVisibleWithSubFocus( |
| gfx::Rect(x, y, width, height)); |
| } |
| |
| void WebAXObjectProxy::ScrollToGlobalPoint(int x, int y) { |
| UpdateLayout(); |
| ui::AXActionData action_data; |
| action_data.action = ax::mojom::Action::kScrollToPoint; |
| action_data.target_point = gfx::Point(x, y); |
| accessibility_object_.PerformAction(action_data); |
| } |
| |
| int WebAXObjectProxy::ScrollX() { |
| UpdateLayout(); |
| return GetAXNodeData().GetIntAttribute(ax::mojom::IntAttribute::kScrollX); |
| } |
| |
| int WebAXObjectProxy::ScrollY() { |
| UpdateLayout(); |
| return GetAXNodeData().GetIntAttribute(ax::mojom::IntAttribute::kScrollY); |
| } |
| |
| std::string WebAXObjectProxy::ToString() { |
| UpdateLayout(); |
| return accessibility_object_.ToString().Utf8(); |
| } |
| |
| float WebAXObjectProxy::BoundsX() { |
| UpdateLayout(); |
| return BoundsForObject(accessibility_object_).x(); |
| } |
| |
| float WebAXObjectProxy::BoundsY() { |
| UpdateLayout(); |
| return BoundsForObject(accessibility_object_).y(); |
| } |
| |
| float WebAXObjectProxy::BoundsWidth() { |
| UpdateLayout(); |
| return BoundsForObject(accessibility_object_).width(); |
| } |
| |
| float WebAXObjectProxy::BoundsHeight() { |
| UpdateLayout(); |
| return BoundsForObject(accessibility_object_).height(); |
| } |
| |
| int WebAXObjectProxy::WordStart(int character_index) { |
| UpdateLayout(); |
| if (accessibility_object_.Role() != ax::mojom::Role::kStaticText) |
| return -1; |
| |
| int word_start = 0, word_end = 0; |
| GetBoundariesForOneWord(accessibility_object_, character_index, word_start, |
| word_end); |
| return word_start; |
| } |
| |
| int WebAXObjectProxy::WordEnd(int character_index) { |
| UpdateLayout(); |
| if (accessibility_object_.Role() != ax::mojom::Role::kStaticText) |
| return -1; |
| |
| int word_start = 0, word_end = 0; |
| GetBoundariesForOneWord(accessibility_object_, character_index, word_start, |
| word_end); |
| return word_end; |
| } |
| |
| v8::Local<v8::Object> WebAXObjectProxy::NextOnLine() { |
| UpdateLayout(); |
| blink::WebAXObject obj = accessibility_object_.NextOnLine(); |
| if (obj.IsNull()) |
| return v8::Local<v8::Object>(); |
| |
| return factory_->GetOrCreate(obj); |
| } |
| |
| v8::Local<v8::Object> WebAXObjectProxy::PreviousOnLine() { |
| UpdateLayout(); |
| blink::WebAXObject obj = accessibility_object_.PreviousOnLine(); |
| if (obj.IsNull()) |
| return v8::Local<v8::Object>(); |
| |
| return factory_->GetOrCreate(obj); |
| } |
| |
| std::vector<std::string> WebAXObjectProxy::GetMisspellings() const { |
| std::vector<std::string> misspellings; |
| std::string text(accessibility_object_.GetName().Utf8()); |
| if (text.empty()) |
| return {}; |
| |
| const ui::AXNodeData& node_data = GetAXNodeData(); |
| std::vector<int32_t> marker_types; |
| std::vector<int32_t> marker_starts; |
| std::vector<int32_t> marker_ends; |
| if (node_data.GetIntListAttribute(ax::mojom::IntListAttribute::kMarkerTypes, |
| &marker_types) && |
| node_data.GetIntListAttribute(ax::mojom::IntListAttribute::kMarkerStarts, |
| &marker_starts) && |
| node_data.GetIntListAttribute(ax::mojom::IntListAttribute::kMarkerEnds, |
| &marker_ends)) { |
| DCHECK_EQ(marker_types.size(), marker_starts.size()); |
| DCHECK_EQ(marker_types.size(), marker_ends.size()); |
| for (size_t i = 0; i < marker_types.size(); ++i) { |
| if (marker_types[i] == int32_t(ax::mojom::MarkerType::kSpelling)) { |
| DCHECK_LE(marker_starts[i], marker_ends[i]); |
| misspellings.push_back( |
| text.substr(marker_starts[i], marker_ends[i] - marker_starts[i])); |
| } |
| } |
| |
| return misspellings; |
| } |
| |
| return {}; |
| } |
| |
| std::string WebAXObjectProxy::MisspellingAtIndex(int index) { |
| UpdateLayout(); |
| |
| std::vector<std::string> misspellings = GetMisspellings(); |
| if (index < 0 || index >= int{misspellings.size()}) |
| return std::string(); |
| return misspellings[index]; |
| } |
| |
| std::string WebAXObjectProxy::Name() { |
| UpdateLayout(); |
| return accessibility_object_.GetName().Utf8(); |
| } |
| |
| std::string WebAXObjectProxy::NameFrom() { |
| UpdateLayout(); |
| ax::mojom::NameFrom name_from = ax::mojom::NameFrom::kUninitialized; |
| blink::WebVector<blink::WebAXObject> name_objects; |
| accessibility_object_.GetName(name_from, name_objects); |
| switch (name_from) { |
| case ax::mojom::NameFrom::kUninitialized: |
| case ax::mojom::NameFrom::kNone: |
| return ""; |
| case ax::mojom::NameFrom::kAttribute: |
| return "attribute"; |
| case ax::mojom::NameFrom::kAttributeExplicitlyEmpty: |
| return "attributeExplicitlyEmpty"; |
| case ax::mojom::NameFrom::kCaption: |
| return "caption"; |
| case ax::mojom::NameFrom::kContents: |
| return "contents"; |
| case ax::mojom::NameFrom::kPlaceholder: |
| return "placeholder"; |
| case ax::mojom::NameFrom::kRelatedElement: |
| return "relatedElement"; |
| case ax::mojom::NameFrom::kValue: |
| return "value"; |
| case ax::mojom::NameFrom::kTitle: |
| return "title"; |
| } |
| |
| NOTREACHED(); |
| return std::string(); |
| } |
| |
| int WebAXObjectProxy::NameElementCount() { |
| UpdateLayout(); |
| ax::mojom::NameFrom name_from; |
| blink::WebVector<blink::WebAXObject> name_objects; |
| accessibility_object_.GetName(name_from, name_objects); |
| return static_cast<int>(name_objects.size()); |
| } |
| |
| v8::Local<v8::Object> WebAXObjectProxy::NameElementAtIndex(unsigned index) { |
| UpdateLayout(); |
| ax::mojom::NameFrom name_from; |
| blink::WebVector<blink::WebAXObject> name_objects; |
| accessibility_object_.GetName(name_from, name_objects); |
| if (index >= name_objects.size()) |
| return {}; |
| return factory_->GetOrCreate(name_objects[index]); |
| } |
| |
| std::string WebAXObjectProxy::Description() { |
| UpdateLayout(); |
| ax::mojom::NameFrom name_from; |
| blink::WebVector<blink::WebAXObject> name_objects; |
| accessibility_object_.GetName(name_from, name_objects); |
| ax::mojom::DescriptionFrom description_from; |
| blink::WebVector<blink::WebAXObject> description_objects; |
| return accessibility_object_ |
| .Description(name_from, description_from, description_objects) |
| .Utf8(); |
| } |
| |
| std::string WebAXObjectProxy::DescriptionFrom() { |
| UpdateLayout(); |
| ax::mojom::NameFrom name_from; |
| blink::WebVector<blink::WebAXObject> name_objects; |
| accessibility_object_.GetName(name_from, name_objects); |
| ax::mojom::DescriptionFrom description_from = |
| ax::mojom::DescriptionFrom::kNone; |
| blink::WebVector<blink::WebAXObject> description_objects; |
| accessibility_object_.Description(name_from, description_from, |
| description_objects); |
| switch (description_from) { |
| case ax::mojom::DescriptionFrom::kNone: |
| return ""; |
| case ax::mojom::DescriptionFrom::kAriaDescription: |
| return "ariaDescription"; |
| case ax::mojom::DescriptionFrom::kButtonLabel: |
| return "buttonLabel"; |
| case ax::mojom::DescriptionFrom::kRelatedElement: |
| return "relatedElement"; |
| case ax::mojom::DescriptionFrom::kRubyAnnotation: |
| return "rubyAnnotation"; |
| case ax::mojom::DescriptionFrom::kSummary: |
| return "summary"; |
| case ax::mojom::DescriptionFrom::kTableCaption: |
| return "tableCaption"; |
| case ax::mojom::DescriptionFrom::kTitle: |
| return "title"; |
| } |
| |
| NOTREACHED(); |
| return std::string(); |
| } |
| |
| std::string WebAXObjectProxy::Placeholder() { |
| UpdateLayout(); |
| ax::mojom::NameFrom name_from; |
| blink::WebVector<blink::WebAXObject> name_objects; |
| accessibility_object_.GetName(name_from, name_objects); |
| return accessibility_object_.Placeholder(name_from).Utf8(); |
| } |
| |
| int WebAXObjectProxy::MisspellingsCount() { |
| UpdateLayout(); |
| return GetMisspellings().size(); |
| } |
| |
| int WebAXObjectProxy::DescriptionElementCount() { |
| UpdateLayout(); |
| ax::mojom::NameFrom name_from; |
| blink::WebVector<blink::WebAXObject> name_objects; |
| accessibility_object_.GetName(name_from, name_objects); |
| ax::mojom::DescriptionFrom description_from; |
| blink::WebVector<blink::WebAXObject> description_objects; |
| accessibility_object_.Description(name_from, description_from, |
| description_objects); |
| return static_cast<int>(description_objects.size()); |
| } |
| |
| v8::Local<v8::Object> WebAXObjectProxy::DescriptionElementAtIndex( |
| unsigned index) { |
| UpdateLayout(); |
| ax::mojom::NameFrom name_from; |
| blink::WebVector<blink::WebAXObject> name_objects; |
| accessibility_object_.GetName(name_from, name_objects); |
| ax::mojom::DescriptionFrom description_from; |
| blink::WebVector<blink::WebAXObject> description_objects; |
| accessibility_object_.Description(name_from, description_from, |
| description_objects); |
| if (index >= description_objects.size()) |
| return v8::Local<v8::Object>(); |
| return factory_->GetOrCreate(description_objects[index]); |
| } |
| |
| v8::Local<v8::Object> WebAXObjectProxy::OffsetContainer() { |
| UpdateLayout(); |
| blink::WebAXObject container; |
| gfx::RectF bounds; |
| SkMatrix44 matrix; |
| accessibility_object_.GetRelativeBounds(container, bounds, matrix); |
| return factory_->GetOrCreate(container); |
| } |
| |
| float WebAXObjectProxy::BoundsInContainerX() { |
| UpdateLayout(); |
| blink::WebAXObject container; |
| gfx::RectF bounds; |
| SkMatrix44 matrix; |
| accessibility_object_.GetRelativeBounds(container, bounds, matrix); |
| return bounds.x(); |
| } |
| |
| float WebAXObjectProxy::BoundsInContainerY() { |
| UpdateLayout(); |
| blink::WebAXObject container; |
| gfx::RectF bounds; |
| SkMatrix44 matrix; |
| accessibility_object_.GetRelativeBounds(container, bounds, matrix); |
| return bounds.y(); |
| } |
| |
| float WebAXObjectProxy::BoundsInContainerWidth() { |
| UpdateLayout(); |
| blink::WebAXObject container; |
| gfx::RectF bounds; |
| SkMatrix44 matrix; |
| accessibility_object_.GetRelativeBounds(container, bounds, matrix); |
| return bounds.width(); |
| } |
| |
| float WebAXObjectProxy::BoundsInContainerHeight() { |
| UpdateLayout(); |
| blink::WebAXObject container; |
| gfx::RectF bounds; |
| SkMatrix44 matrix; |
| accessibility_object_.GetRelativeBounds(container, bounds, matrix); |
| return bounds.height(); |
| } |
| |
| bool WebAXObjectProxy::HasNonIdentityTransform() { |
| UpdateLayout(); |
| blink::WebAXObject container; |
| gfx::RectF bounds; |
| SkMatrix44 matrix; |
| accessibility_object_.GetRelativeBounds(container, bounds, matrix); |
| return !matrix.isIdentity(); |
| } |
| |
| RootWebAXObjectProxy::RootWebAXObjectProxy(const blink::WebAXObject& object, |
| Factory* factory) |
| : WebAXObjectProxy(object, factory) {} |
| |
| v8::Local<v8::Object> RootWebAXObjectProxy::GetChildAtIndex(unsigned index) { |
| if (index) |
| return v8::Local<v8::Object>(); |
| |
| return factory()->GetOrCreate(accessibility_object()); |
| } |
| |
| bool RootWebAXObjectProxy::IsRoot() const { |
| return true; |
| } |
| |
| WebAXObjectProxyList::WebAXObjectProxyList() = default; |
| |
| WebAXObjectProxyList::~WebAXObjectProxyList() { |
| Clear(); |
| } |
| |
| void WebAXObjectProxyList::Clear() { |
| v8::Isolate* isolate = blink::MainThreadIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| for (auto& persistent : elements_) { |
| auto local = v8::Local<v8::Object>::New(isolate, persistent); |
| |
| WebAXObjectProxy* proxy = nullptr; |
| bool ok = gin::ConvertFromV8(isolate, local, &proxy); |
| DCHECK(ok); |
| |
| // Because the v8::Persistent in this container uses |
| // CopyablePersistentObject traits, it will not leak the Persistent objects |
| // on destruction. However, blink may be keeping a reference to the |proxy|. |
| // We Reset() it to drop the callback in the proxy object now that its not |
| // in the proxy list. |
| proxy->Reset(); |
| } |
| |
| elements_.clear(); |
| } |
| |
| v8::Local<v8::Object> WebAXObjectProxyList::GetOrCreate( |
| const blink::WebAXObject& object) { |
| if (object.IsNull()) |
| return v8::Local<v8::Object>(); |
| |
| v8::Isolate* isolate = blink::MainThreadIsolate(); |
| |
| for (const auto& persistent : elements_) { |
| auto local = v8::Local<v8::Object>::New(isolate, persistent); |
| |
| WebAXObjectProxy* proxy = nullptr; |
| bool ok = gin::ConvertFromV8(isolate, local, &proxy); |
| DCHECK(ok); |
| |
| if (proxy->IsEqualToObject(object)) |
| return local; |
| } |
| |
| v8::Local<v8::Value> value_handle = |
| gin::CreateHandle(isolate, new WebAXObjectProxy(object, this)).ToV8(); |
| v8::Local<v8::Object> handle; |
| if (value_handle.IsEmpty() || |
| !value_handle->ToObject(isolate->GetCurrentContext()).ToLocal(&handle)) { |
| return {}; |
| } |
| |
| elements_.emplace_back(isolate, handle); |
| return handle; |
| } |
| |
| } // namespace content |