| /* |
| * Copyright (C) 2009 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "public/web/WebAXObject.h" |
| |
| #include "SkMatrix44.h" |
| #include "core/HTMLNames.h" |
| #include "core/css/CSSPrimitiveValueMappings.h" |
| #include "core/dom/Document.h" |
| #include "core/dom/Node.h" |
| #include "core/editing/markers/DocumentMarker.h" |
| #include "core/frame/FrameHost.h" |
| #include "core/frame/FrameView.h" |
| #include "core/frame/VisualViewport.h" |
| #include "core/input/KeyboardEventManager.h" |
| #include "core/style/ComputedStyle.h" |
| #include "core/page/Page.h" |
| #include "modules/accessibility/AXObject.h" |
| #include "modules/accessibility/AXObjectCacheImpl.h" |
| #include "modules/accessibility/AXTable.h" |
| #include "modules/accessibility/AXTableCell.h" |
| #include "modules/accessibility/AXTableColumn.h" |
| #include "modules/accessibility/AXTableRow.h" |
| #include "public/platform/WebFloatRect.h" |
| #include "public/platform/WebPoint.h" |
| #include "public/platform/WebRect.h" |
| #include "public/platform/WebString.h" |
| #include "public/platform/WebURL.h" |
| #include "public/web/WebDocument.h" |
| #include "public/web/WebElement.h" |
| #include "public/web/WebNode.h" |
| #include "web/WebLocalFrameImpl.h" |
| #include "web/WebViewImpl.h" |
| #include "wtf/text/StringBuilder.h" |
| |
| namespace blink { |
| |
| class WebAXSparseAttributeClientAdapter : public AXSparseAttributeClient { |
| public: |
| WebAXSparseAttributeClientAdapter(WebAXSparseAttributeClient& attributeMap) |
| : m_attributeMap(attributeMap) {} |
| virtual ~WebAXSparseAttributeClientAdapter() {} |
| |
| private: |
| WebAXSparseAttributeClient& m_attributeMap; |
| |
| void addBoolAttribute(AXBoolAttribute attribute, bool value) override { |
| m_attributeMap.addBoolAttribute(static_cast<WebAXBoolAttribute>(attribute), |
| value); |
| } |
| |
| void addStringAttribute(AXStringAttribute attribute, |
| const String& value) override { |
| m_attributeMap.addStringAttribute( |
| static_cast<WebAXStringAttribute>(attribute), value); |
| } |
| |
| void addObjectAttribute(AXObjectAttribute attribute, |
| AXObject& value) override { |
| m_attributeMap.addObjectAttribute( |
| static_cast<WebAXObjectAttribute>(attribute), WebAXObject(&value)); |
| } |
| |
| void addObjectVectorAttribute(AXObjectVectorAttribute attribute, |
| HeapVector<Member<AXObject>>& value) override { |
| WebVector<WebAXObject> result(value.size()); |
| for (size_t i = 0; i < value.size(); i++) |
| result[i] = WebAXObject(value[i]); |
| m_attributeMap.addObjectVectorAttribute( |
| static_cast<WebAXObjectVectorAttribute>(attribute), result); |
| } |
| }; |
| |
| #if DCHECK_IS_ON() |
| // It's not safe to call some WebAXObject APIs if a layout is pending. |
| // Clients should call updateLayoutAndCheckValidity first. |
| static bool isLayoutClean(Document* document) { |
| if (!document || !document->view()) |
| return false; |
| return document->lifecycle().state() >= DocumentLifecycle::LayoutClean || |
| ((document->lifecycle().state() == DocumentLifecycle::StyleClean || |
| document->lifecycle().state() == |
| DocumentLifecycle::LayoutSubtreeChangeClean) && |
| !document->view()->needsLayout()); |
| } |
| #endif |
| |
| WebScopedAXContext::WebScopedAXContext(WebDocument& rootDocument) |
| : m_private(ScopedAXObjectCache::create(*rootDocument.unwrap<Document>())) { |
| } |
| |
| WebScopedAXContext::~WebScopedAXContext() { |
| m_private.reset(0); |
| } |
| |
| WebAXObject WebScopedAXContext::root() const { |
| return WebAXObject(static_cast<AXObjectCacheImpl*>(m_private->get())->root()); |
| } |
| |
| void WebAXObject::reset() { |
| m_private.reset(); |
| } |
| |
| void WebAXObject::assign(const WebAXObject& other) { |
| m_private = other.m_private; |
| } |
| |
| bool WebAXObject::equals(const WebAXObject& n) const { |
| return m_private.get() == n.m_private.get(); |
| } |
| |
| bool WebAXObject::isDetached() const { |
| if (m_private.isNull()) |
| return true; |
| |
| return m_private->isDetached(); |
| } |
| |
| int WebAXObject::axID() const { |
| if (isDetached()) |
| return -1; |
| |
| return m_private->axObjectID(); |
| } |
| |
| int WebAXObject::generateAXID() const { |
| if (isDetached()) |
| return -1; |
| |
| return m_private->axObjectCache().platformGenerateAXID(); |
| } |
| |
| bool WebAXObject::updateLayoutAndCheckValidity() { |
| if (!isDetached()) { |
| Document* document = m_private->getDocument(); |
| if (!document || !document->view()) |
| return false; |
| document->view()->updateLifecycleToCompositingCleanPlusScrolling(); |
| } |
| |
| // Doing a layout can cause this object to be invalid, so check again. |
| return !isDetached(); |
| } |
| |
| WebAXSupportedAction WebAXObject::action() const { |
| if (isDetached()) |
| return WebAXSupportedAction::None; |
| |
| return static_cast<WebAXSupportedAction>(m_private->action()); |
| } |
| |
| bool WebAXObject::canDecrement() const { |
| if (isDetached()) |
| return false; |
| |
| return m_private->isSlider(); |
| } |
| |
| bool WebAXObject::canIncrement() const { |
| if (isDetached()) |
| return false; |
| |
| return m_private->isSlider(); |
| } |
| |
| bool WebAXObject::canPress() const { |
| if (isDetached()) |
| return false; |
| |
| return m_private->actionElement() || m_private->isButton() || |
| m_private->isMenuRelated(); |
| } |
| |
| bool WebAXObject::canSetFocusAttribute() const { |
| if (isDetached()) |
| return false; |
| |
| return m_private->canSetFocusAttribute(); |
| } |
| |
| bool WebAXObject::canSetValueAttribute() const { |
| if (isDetached()) |
| return false; |
| |
| return m_private->canSetValueAttribute(); |
| } |
| |
| unsigned WebAXObject::childCount() const { |
| if (isDetached()) |
| return 0; |
| |
| return m_private->children().size(); |
| } |
| |
| WebAXObject WebAXObject::childAt(unsigned index) const { |
| if (isDetached()) |
| return WebAXObject(); |
| |
| if (m_private->children().size() <= index) |
| return WebAXObject(); |
| |
| return WebAXObject(m_private->children()[index]); |
| } |
| |
| WebAXObject WebAXObject::parentObject() const { |
| if (isDetached()) |
| return WebAXObject(); |
| |
| return WebAXObject(m_private->parentObject()); |
| } |
| |
| void WebAXObject::getSparseAXAttributes( |
| WebAXSparseAttributeClient& client) const { |
| if (isDetached()) |
| return; |
| |
| WebAXSparseAttributeClientAdapter adapter(client); |
| m_private->getSparseAXAttributes(adapter); |
| } |
| |
| bool WebAXObject::canSetSelectedAttribute() const { |
| if (isDetached()) |
| return false; |
| |
| return m_private->canSetSelectedAttribute(); |
| } |
| |
| bool WebAXObject::isAnchor() const { |
| if (isDetached()) |
| return false; |
| |
| return m_private->isAnchor(); |
| } |
| |
| bool WebAXObject::isAriaReadOnly() const { |
| if (isDetached()) |
| return false; |
| |
| return equalIgnoringASCIICase( |
| m_private->getAttribute(HTMLNames::aria_readonlyAttr), "true"); |
| } |
| |
| WebString WebAXObject::ariaAutoComplete() const { |
| if (isDetached()) |
| return WebString(); |
| |
| return m_private->ariaAutoComplete(); |
| } |
| |
| WebAXAriaCurrentState WebAXObject::ariaCurrentState() const { |
| if (isDetached()) |
| return WebAXAriaCurrentStateUndefined; |
| |
| return static_cast<WebAXAriaCurrentState>(m_private->ariaCurrentState()); |
| } |
| |
| bool WebAXObject::isButtonStateMixed() const { |
| if (isDetached()) |
| return false; |
| |
| return m_private->checkboxOrRadioValue() == ButtonStateMixed; |
| } |
| |
| bool WebAXObject::isChecked() const { |
| if (isDetached()) |
| return false; |
| |
| return m_private->isChecked(); |
| } |
| |
| bool WebAXObject::isClickable() const { |
| if (isDetached()) |
| return false; |
| |
| return m_private->isClickable(); |
| } |
| |
| bool WebAXObject::isCollapsed() const { |
| if (isDetached()) |
| return false; |
| |
| return m_private->isCollapsed(); |
| } |
| |
| bool WebAXObject::isControl() const { |
| if (isDetached()) |
| return false; |
| |
| return m_private->isControl(); |
| } |
| |
| bool WebAXObject::isEnabled() const { |
| if (isDetached()) |
| return false; |
| |
| return m_private->isEnabled(); |
| } |
| |
| WebAXExpanded WebAXObject::isExpanded() const { |
| if (isDetached()) |
| return WebAXExpandedUndefined; |
| |
| return static_cast<WebAXExpanded>(m_private->isExpanded()); |
| } |
| |
| bool WebAXObject::isFocused() const { |
| if (isDetached()) |
| return false; |
| |
| return m_private->isFocused(); |
| } |
| |
| bool WebAXObject::isHovered() const { |
| if (isDetached()) |
| return false; |
| |
| return m_private->isHovered(); |
| } |
| |
| bool WebAXObject::isLinked() const { |
| if (isDetached()) |
| return false; |
| |
| return m_private->isLinked(); |
| } |
| |
| bool WebAXObject::isLoaded() const { |
| if (isDetached()) |
| return false; |
| |
| return m_private->isLoaded(); |
| } |
| |
| bool WebAXObject::isModal() const { |
| if (isDetached()) |
| return false; |
| |
| return m_private->isModal(); |
| } |
| |
| bool WebAXObject::isMultiSelectable() const { |
| if (isDetached()) |
| return false; |
| |
| return m_private->isMultiSelectable(); |
| } |
| |
| bool WebAXObject::isOffScreen() const { |
| if (isDetached()) |
| return false; |
| |
| return m_private->isOffScreen(); |
| } |
| |
| bool WebAXObject::isPasswordField() const { |
| if (isDetached()) |
| return false; |
| |
| return m_private->isPasswordField(); |
| } |
| |
| bool WebAXObject::isPressed() const { |
| if (isDetached()) |
| return false; |
| |
| return m_private->isPressed(); |
| } |
| |
| bool WebAXObject::isReadOnly() const { |
| if (isDetached()) |
| return false; |
| |
| return m_private->isReadOnly(); |
| } |
| |
| bool WebAXObject::isRequired() const { |
| if (isDetached()) |
| return false; |
| |
| return m_private->isRequired(); |
| } |
| |
| bool WebAXObject::isSelected() const { |
| if (isDetached()) |
| return false; |
| |
| return m_private->isSelected(); |
| } |
| |
| bool WebAXObject::isSelectedOptionActive() const { |
| if (isDetached()) |
| return false; |
| |
| return m_private->isSelectedOptionActive(); |
| } |
| |
| bool WebAXObject::isVisible() const { |
| if (isDetached()) |
| return false; |
| |
| return m_private->isVisible(); |
| } |
| |
| bool WebAXObject::isVisited() const { |
| if (isDetached()) |
| return false; |
| |
| return m_private->isVisited(); |
| } |
| |
| WebString WebAXObject::accessKey() const { |
| if (isDetached()) |
| return WebString(); |
| |
| return WebString(m_private->accessKey()); |
| } |
| |
| unsigned WebAXObject::backgroundColor() const { |
| if (isDetached()) |
| return 0; |
| |
| // RGBA32 is an alias for unsigned int. |
| return m_private->backgroundColor(); |
| } |
| |
| unsigned WebAXObject::color() const { |
| if (isDetached()) |
| return 0; |
| |
| // RGBA32 is an alias for unsigned int. |
| return m_private->color(); |
| } |
| |
| // Deprecated. |
| void WebAXObject::colorValue(int& r, int& g, int& b) const { |
| if (isDetached()) |
| return; |
| |
| unsigned color = m_private->colorValue(); |
| r = (color >> 16) & 0xFF; |
| g = (color >> 8) & 0xFF; |
| b = color & 0xFF; |
| } |
| |
| unsigned WebAXObject::colorValue() const { |
| if (isDetached()) |
| return 0; |
| |
| // RGBA32 is an alias for unsigned int. |
| return m_private->colorValue(); |
| } |
| |
| WebAXObject WebAXObject::ariaActiveDescendant() const { |
| if (isDetached()) |
| return WebAXObject(); |
| |
| return WebAXObject(m_private->activeDescendant()); |
| } |
| |
| bool WebAXObject::ariaHasPopup() const { |
| if (isDetached()) |
| return false; |
| |
| return m_private->ariaHasPopup(); |
| } |
| |
| bool WebAXObject::isEditable() const { |
| if (isDetached()) |
| return false; |
| |
| return m_private->isEditable(); |
| } |
| |
| bool WebAXObject::isMultiline() const { |
| if (isDetached()) |
| return false; |
| |
| return m_private->isMultiline(); |
| } |
| |
| bool WebAXObject::isRichlyEditable() const { |
| if (isDetached()) |
| return false; |
| |
| return m_private->isRichlyEditable(); |
| } |
| |
| int WebAXObject::posInSet() const { |
| if (isDetached()) |
| return 0; |
| |
| return m_private->posInSet(); |
| } |
| |
| int WebAXObject::setSize() const { |
| if (isDetached()) |
| return 0; |
| |
| return m_private->setSize(); |
| } |
| |
| bool WebAXObject::isInLiveRegion() const { |
| if (isDetached()) |
| return false; |
| |
| return 0 != m_private->liveRegionRoot(); |
| } |
| |
| bool WebAXObject::liveRegionAtomic() const { |
| if (isDetached()) |
| return false; |
| |
| return m_private->liveRegionAtomic(); |
| } |
| |
| bool WebAXObject::liveRegionBusy() const { |
| if (isDetached()) |
| return false; |
| |
| return m_private->liveRegionBusy(); |
| } |
| |
| WebString WebAXObject::liveRegionRelevant() const { |
| if (isDetached()) |
| return WebString(); |
| |
| return m_private->liveRegionRelevant(); |
| } |
| |
| WebString WebAXObject::liveRegionStatus() const { |
| if (isDetached()) |
| return WebString(); |
| |
| return m_private->liveRegionStatus(); |
| } |
| |
| WebAXObject WebAXObject::liveRegionRoot() const { |
| if (isDetached()) |
| return WebAXObject(); |
| |
| AXObject* liveRegionRoot = m_private->liveRegionRoot(); |
| if (liveRegionRoot) |
| return WebAXObject(liveRegionRoot); |
| return WebAXObject(); |
| } |
| |
| bool WebAXObject::containerLiveRegionAtomic() const { |
| if (isDetached()) |
| return false; |
| |
| return m_private->containerLiveRegionAtomic(); |
| } |
| |
| bool WebAXObject::containerLiveRegionBusy() const { |
| if (isDetached()) |
| return false; |
| |
| return m_private->containerLiveRegionBusy(); |
| } |
| |
| WebString WebAXObject::containerLiveRegionRelevant() const { |
| if (isDetached()) |
| return WebString(); |
| |
| return m_private->containerLiveRegionRelevant(); |
| } |
| |
| WebString WebAXObject::containerLiveRegionStatus() const { |
| if (isDetached()) |
| return WebString(); |
| |
| return m_private->containerLiveRegionStatus(); |
| } |
| |
| bool WebAXObject::ariaOwns(WebVector<WebAXObject>& ownsElements) const { |
| // aria-owns rearranges the accessibility tree rather than just |
| // exposing an attribute. |
| |
| // FIXME(dmazzoni): remove this function after we stop calling it |
| // from Chromium. http://crbug.com/489590 |
| |
| return false; |
| } |
| |
| WebString WebAXObject::fontFamily() const { |
| if (isDetached()) |
| return WebString(); |
| |
| return m_private->fontFamily(); |
| } |
| |
| float WebAXObject::fontSize() const { |
| if (isDetached()) |
| return 0.0f; |
| |
| return m_private->fontSize(); |
| } |
| |
| bool WebAXObject::canvasHasFallbackContent() const { |
| if (isDetached()) |
| return false; |
| |
| return m_private->canvasHasFallbackContent(); |
| } |
| |
| WebString WebAXObject::imageDataUrl(const WebSize& maxSize) const { |
| if (isDetached()) |
| return WebString(); |
| |
| return m_private->imageDataUrl(maxSize); |
| } |
| |
| WebAXInvalidState WebAXObject::invalidState() const { |
| if (isDetached()) |
| return WebAXInvalidStateUndefined; |
| |
| return static_cast<WebAXInvalidState>(m_private->getInvalidState()); |
| } |
| |
| // Only used when invalidState() returns WebAXInvalidStateOther. |
| WebString WebAXObject::ariaInvalidValue() const { |
| if (isDetached()) |
| return WebString(); |
| |
| return m_private->ariaInvalidValue(); |
| } |
| |
| double WebAXObject::estimatedLoadingProgress() const { |
| if (isDetached()) |
| return 0.0; |
| |
| return m_private->estimatedLoadingProgress(); |
| } |
| |
| int WebAXObject::headingLevel() const { |
| if (isDetached()) |
| return 0; |
| |
| return m_private->headingLevel(); |
| } |
| |
| int WebAXObject::hierarchicalLevel() const { |
| if (isDetached()) |
| return 0; |
| |
| return m_private->hierarchicalLevel(); |
| } |
| |
| // FIXME: This method passes in a point that has page scale applied but assumes |
| // that (0, 0) is the top left of the visual viewport. In other words, the |
| // point has the VisualViewport scale applied, but not the VisualViewport |
| // offset. crbug.com/459591. |
| WebAXObject WebAXObject::hitTest(const WebPoint& point) const { |
| if (isDetached()) |
| return WebAXObject(); |
| |
| IntPoint contentsPoint = |
| m_private->documentFrameView()->soonToBeRemovedUnscaledViewportToContents( |
| point); |
| AXObject* hit = m_private->accessibilityHitTest(contentsPoint); |
| |
| if (hit) |
| return WebAXObject(hit); |
| |
| if (m_private->getBoundsInFrameCoordinates().contains(contentsPoint)) |
| return *this; |
| |
| return WebAXObject(); |
| } |
| |
| WebString WebAXObject::keyboardShortcut() const { |
| if (isDetached()) |
| return WebString(); |
| |
| String accessKey = m_private->accessKey(); |
| if (accessKey.isNull()) |
| return WebString(); |
| |
| DEFINE_STATIC_LOCAL(String, modifierString, ()); |
| if (modifierString.isNull()) { |
| unsigned modifiers = KeyboardEventManager::kAccessKeyModifiers; |
| // Follow the same order as Mozilla MSAA implementation: |
| // Ctrl+Alt+Shift+Meta+key. MSDN states that keyboard shortcut strings |
| // should not be localized and defines the separator as "+". |
| StringBuilder modifierStringBuilder; |
| if (modifiers & WebInputEvent::ControlKey) |
| modifierStringBuilder.append("Ctrl+"); |
| if (modifiers & WebInputEvent::AltKey) |
| modifierStringBuilder.append("Alt+"); |
| if (modifiers & WebInputEvent::ShiftKey) |
| modifierStringBuilder.append("Shift+"); |
| if (modifiers & WebInputEvent::MetaKey) |
| modifierStringBuilder.append("Win+"); |
| modifierString = modifierStringBuilder.toString(); |
| } |
| |
| return String(modifierString + accessKey); |
| } |
| |
| WebString WebAXObject::language() const { |
| if (isDetached()) |
| return WebString(); |
| |
| return m_private->language(); |
| } |
| |
| bool WebAXObject::performDefaultAction() const { |
| if (isDetached()) |
| return false; |
| |
| return m_private->performDefaultAction(); |
| } |
| |
| bool WebAXObject::increment() const { |
| if (isDetached()) |
| return false; |
| |
| if (canIncrement()) { |
| m_private->increment(); |
| return true; |
| } |
| return false; |
| } |
| |
| bool WebAXObject::decrement() const { |
| if (isDetached()) |
| return false; |
| |
| if (canDecrement()) { |
| m_private->decrement(); |
| return true; |
| } |
| return false; |
| } |
| |
| WebAXOrientation WebAXObject::orientation() const { |
| if (isDetached()) |
| return WebAXOrientationUndefined; |
| |
| return static_cast<WebAXOrientation>(m_private->orientation()); |
| } |
| |
| bool WebAXObject::press() const { |
| if (isDetached()) |
| return false; |
| |
| return m_private->press(); |
| } |
| |
| WebAXRole WebAXObject::role() const { |
| if (isDetached()) |
| return WebAXRoleUnknown; |
| |
| return static_cast<WebAXRole>(m_private->roleValue()); |
| } |
| |
| void WebAXObject::selection(WebAXObject& anchorObject, |
| int& anchorOffset, |
| WebAXTextAffinity& anchorAffinity, |
| WebAXObject& focusObject, |
| int& focusOffset, |
| WebAXTextAffinity& focusAffinity) const { |
| if (isDetached()) { |
| anchorObject = WebAXObject(); |
| anchorOffset = -1; |
| anchorAffinity = WebAXTextAffinityDownstream; |
| focusObject = WebAXObject(); |
| focusOffset = -1; |
| focusAffinity = WebAXTextAffinityDownstream; |
| return; |
| } |
| |
| AXObject::AXRange axSelection = m_private->selection(); |
| anchorObject = WebAXObject(axSelection.anchorObject); |
| anchorOffset = axSelection.anchorOffset; |
| anchorAffinity = static_cast<WebAXTextAffinity>(axSelection.anchorAffinity); |
| focusObject = WebAXObject(axSelection.focusObject); |
| focusOffset = axSelection.focusOffset; |
| focusAffinity = static_cast<WebAXTextAffinity>(axSelection.focusAffinity); |
| return; |
| } |
| |
| void WebAXObject::setSelection(const WebAXObject& anchorObject, |
| int anchorOffset, |
| const WebAXObject& focusObject, |
| int focusOffset) const { |
| if (isDetached()) |
| return; |
| |
| AXObject::AXRange axSelection(anchorObject, anchorOffset, |
| TextAffinity::Upstream, focusObject, |
| focusOffset, TextAffinity::Downstream); |
| m_private->setSelection(axSelection); |
| return; |
| } |
| |
| unsigned WebAXObject::selectionEnd() const { |
| if (isDetached()) |
| return 0; |
| |
| AXObject::AXRange axSelection = m_private->selectionUnderObject(); |
| if (axSelection.focusOffset < 0) |
| return 0; |
| |
| return axSelection.focusOffset; |
| } |
| |
| unsigned WebAXObject::selectionStart() const { |
| if (isDetached()) |
| return 0; |
| |
| AXObject::AXRange axSelection = m_private->selectionUnderObject(); |
| if (axSelection.anchorOffset < 0) |
| return 0; |
| |
| return axSelection.anchorOffset; |
| } |
| |
| unsigned WebAXObject::selectionEndLineNumber() const { |
| if (isDetached()) |
| return 0; |
| |
| VisiblePosition position = m_private->visiblePositionForIndex(selectionEnd()); |
| int lineNumber = m_private->lineForPosition(position); |
| if (lineNumber < 0) |
| return 0; |
| |
| return lineNumber; |
| } |
| |
| unsigned WebAXObject::selectionStartLineNumber() const { |
| if (isDetached()) |
| return 0; |
| |
| VisiblePosition position = |
| m_private->visiblePositionForIndex(selectionStart()); |
| int lineNumber = m_private->lineForPosition(position); |
| if (lineNumber < 0) |
| return 0; |
| |
| return lineNumber; |
| } |
| |
| void WebAXObject::setFocused(bool on) const { |
| if (!isDetached()) |
| m_private->setFocused(on); |
| } |
| |
| void WebAXObject::setSelectedTextRange(int selectionStart, |
| int selectionEnd) const { |
| if (isDetached()) |
| return; |
| |
| m_private->setSelection(AXObject::AXRange(selectionStart, selectionEnd)); |
| } |
| |
| void WebAXObject::setSequentialFocusNavigationStartingPoint() const { |
| if (isDetached()) |
| return; |
| |
| m_private->setSequentialFocusNavigationStartingPoint(); |
| } |
| |
| void WebAXObject::setValue(WebString value) const { |
| if (isDetached()) |
| return; |
| |
| m_private->setValue(value); |
| } |
| |
| void WebAXObject::showContextMenu() const { |
| if (isDetached()) |
| return; |
| |
| Node* node = m_private->getNode(); |
| if (!node) |
| return; |
| |
| Element* element = nullptr; |
| if (node->isElementNode()) { |
| element = toElement(node); |
| } else if (node->isDocumentNode()) { |
| element = node->document().documentElement(); |
| } else { |
| node->updateDistribution(); |
| ContainerNode* parent = FlatTreeTraversal::parent(*node); |
| if (!parent) |
| return; |
| SECURITY_DCHECK(parent->isElementNode()); |
| element = toElement(parent); |
| } |
| |
| if (!element) |
| return; |
| |
| LocalFrame* frame = element->document().frame(); |
| if (!frame) |
| return; |
| |
| WebViewImpl* view = WebLocalFrameImpl::fromFrame(frame)->viewImpl(); |
| if (!view) |
| return; |
| |
| view->showContextMenuForElement(WebElement(element)); |
| } |
| |
| WebString WebAXObject::stringValue() const { |
| if (isDetached()) |
| return WebString(); |
| |
| return m_private->stringValue(); |
| } |
| |
| WebAXTextDirection WebAXObject::textDirection() const { |
| if (isDetached()) |
| return WebAXTextDirectionLR; |
| |
| return static_cast<WebAXTextDirection>(m_private->textDirection()); |
| } |
| |
| WebAXTextStyle WebAXObject::textStyle() const { |
| if (isDetached()) |
| return WebAXTextStyleNone; |
| |
| return static_cast<WebAXTextStyle>(m_private->getTextStyle()); |
| } |
| |
| WebURL WebAXObject::url() const { |
| if (isDetached()) |
| return WebURL(); |
| |
| return m_private->url(); |
| } |
| |
| WebString WebAXObject::name(WebAXNameFrom& outNameFrom, |
| WebVector<WebAXObject>& outNameObjects) const { |
| if (isDetached()) |
| return WebString(); |
| |
| AXNameFrom nameFrom = AXNameFromUninitialized; |
| HeapVector<Member<AXObject>> nameObjects; |
| WebString result = m_private->name(nameFrom, &nameObjects); |
| outNameFrom = static_cast<WebAXNameFrom>(nameFrom); |
| |
| WebVector<WebAXObject> webNameObjects(nameObjects.size()); |
| for (size_t i = 0; i < nameObjects.size(); i++) |
| webNameObjects[i] = WebAXObject(nameObjects[i]); |
| outNameObjects.swap(webNameObjects); |
| |
| return result; |
| } |
| |
| WebString WebAXObject::name() const { |
| if (isDetached()) |
| return WebString(); |
| |
| AXNameFrom nameFrom; |
| HeapVector<Member<AXObject>> nameObjects; |
| return m_private->name(nameFrom, &nameObjects); |
| } |
| |
| WebString WebAXObject::description( |
| WebAXNameFrom nameFrom, |
| WebAXDescriptionFrom& outDescriptionFrom, |
| WebVector<WebAXObject>& outDescriptionObjects) const { |
| if (isDetached()) |
| return WebString(); |
| |
| AXDescriptionFrom descriptionFrom = AXDescriptionFromUninitialized; |
| HeapVector<Member<AXObject>> descriptionObjects; |
| String result = m_private->description(static_cast<AXNameFrom>(nameFrom), |
| descriptionFrom, &descriptionObjects); |
| outDescriptionFrom = static_cast<WebAXDescriptionFrom>(descriptionFrom); |
| |
| WebVector<WebAXObject> webDescriptionObjects(descriptionObjects.size()); |
| for (size_t i = 0; i < descriptionObjects.size(); i++) |
| webDescriptionObjects[i] = WebAXObject(descriptionObjects[i]); |
| outDescriptionObjects.swap(webDescriptionObjects); |
| |
| return result; |
| } |
| |
| WebString WebAXObject::placeholder(WebAXNameFrom nameFrom) const { |
| if (isDetached()) |
| return WebString(); |
| |
| return m_private->placeholder(static_cast<AXNameFrom>(nameFrom)); |
| } |
| |
| bool WebAXObject::supportsRangeValue() const { |
| if (isDetached()) |
| return false; |
| |
| return m_private->supportsRangeValue(); |
| } |
| |
| WebString WebAXObject::valueDescription() const { |
| if (isDetached()) |
| return WebString(); |
| |
| return m_private->valueDescription(); |
| } |
| |
| float WebAXObject::valueForRange() const { |
| if (isDetached()) |
| return 0.0; |
| |
| return m_private->valueForRange(); |
| } |
| |
| float WebAXObject::maxValueForRange() const { |
| if (isDetached()) |
| return 0.0; |
| |
| return m_private->maxValueForRange(); |
| } |
| |
| float WebAXObject::minValueForRange() const { |
| if (isDetached()) |
| return 0.0; |
| |
| return m_private->minValueForRange(); |
| } |
| |
| WebNode WebAXObject::node() const { |
| if (isDetached()) |
| return WebNode(); |
| |
| Node* node = m_private->getNode(); |
| if (!node) |
| return WebNode(); |
| |
| return WebNode(node); |
| } |
| |
| WebDocument WebAXObject::document() const { |
| if (isDetached()) |
| return WebDocument(); |
| |
| Document* document = m_private->getDocument(); |
| if (!document) |
| return WebDocument(); |
| |
| return WebDocument(document); |
| } |
| |
| bool WebAXObject::hasComputedStyle() const { |
| if (isDetached()) |
| return false; |
| |
| Document* document = m_private->getDocument(); |
| if (document) |
| document->updateStyleAndLayoutTree(); |
| |
| Node* node = m_private->getNode(); |
| if (!node) |
| return false; |
| |
| return node->ensureComputedStyle(); |
| } |
| |
| WebString WebAXObject::computedStyleDisplay() const { |
| if (isDetached()) |
| return WebString(); |
| |
| Document* document = m_private->getDocument(); |
| if (document) |
| document->updateStyleAndLayoutTree(); |
| |
| Node* node = m_private->getNode(); |
| if (!node) |
| return WebString(); |
| |
| const ComputedStyle* computedStyle = node->ensureComputedStyle(); |
| if (!computedStyle) |
| return WebString(); |
| |
| return WebString( |
| CSSIdentifierValue::create(computedStyle->display())->cssText()); |
| } |
| |
| bool WebAXObject::accessibilityIsIgnored() const { |
| if (isDetached()) |
| return false; |
| |
| return m_private->accessibilityIsIgnored(); |
| } |
| |
| bool WebAXObject::lineBreaks(WebVector<int>& result) const { |
| if (isDetached()) |
| return false; |
| |
| Vector<int> lineBreaksVector; |
| m_private->lineBreaks(lineBreaksVector); |
| |
| size_t vectorSize = lineBreaksVector.size(); |
| WebVector<int> lineBreaksWebVector(vectorSize); |
| for (size_t i = 0; i < vectorSize; i++) |
| lineBreaksWebVector[i] = lineBreaksVector[i]; |
| result.swap(lineBreaksWebVector); |
| |
| return true; |
| } |
| |
| int WebAXObject::ariaColumnCount() const { |
| if (isDetached()) |
| return 0; |
| |
| if (!m_private->isAXTable()) |
| return 0; |
| |
| return toAXTable(m_private.get())->ariaColumnCount(); |
| } |
| |
| unsigned WebAXObject::ariaColumnIndex() const { |
| if (isDetached()) |
| return 0; |
| |
| if (!m_private->isTableCell()) |
| return 0; |
| |
| return toAXTableCell(m_private.get())->ariaColumnIndex(); |
| } |
| |
| int WebAXObject::ariaRowCount() const { |
| if (isDetached()) |
| return 0; |
| |
| if (!m_private->isAXTable()) |
| return 0; |
| |
| return toAXTable(m_private.get())->ariaRowCount(); |
| } |
| |
| unsigned WebAXObject::ariaRowIndex() const { |
| if (isDetached()) |
| return 0; |
| |
| if (m_private->isTableCell()) |
| return toAXTableCell(m_private.get())->ariaRowIndex(); |
| |
| if (m_private->isTableRow()) |
| return toAXTableRow(m_private.get())->ariaRowIndex(); |
| |
| return 0; |
| } |
| |
| unsigned WebAXObject::columnCount() const { |
| if (isDetached()) |
| return false; |
| |
| if (!m_private->isAXTable()) |
| return 0; |
| |
| return toAXTable(m_private.get())->columnCount(); |
| } |
| |
| unsigned WebAXObject::rowCount() const { |
| if (isDetached()) |
| return 0; |
| |
| if (!m_private->isAXTable()) |
| return 0; |
| |
| return toAXTable(m_private.get())->rowCount(); |
| } |
| |
| WebAXObject WebAXObject::cellForColumnAndRow(unsigned column, |
| unsigned row) const { |
| if (isDetached()) |
| return WebAXObject(); |
| |
| if (!m_private->isAXTable()) |
| return WebAXObject(); |
| |
| AXTableCell* cell = |
| toAXTable(m_private.get())->cellForColumnAndRow(column, row); |
| return WebAXObject(static_cast<AXObject*>(cell)); |
| } |
| |
| WebAXObject WebAXObject::headerContainerObject() const { |
| if (isDetached()) |
| return WebAXObject(); |
| |
| if (!m_private->isAXTable()) |
| return WebAXObject(); |
| |
| return WebAXObject(toAXTable(m_private.get())->headerContainer()); |
| } |
| |
| WebAXObject WebAXObject::rowAtIndex(unsigned rowIndex) const { |
| if (isDetached()) |
| return WebAXObject(); |
| |
| if (!m_private->isAXTable()) |
| return WebAXObject(); |
| |
| const AXObject::AXObjectVector& rows = toAXTable(m_private.get())->rows(); |
| if (rowIndex < rows.size()) |
| return WebAXObject(rows[rowIndex]); |
| |
| return WebAXObject(); |
| } |
| |
| WebAXObject WebAXObject::columnAtIndex(unsigned columnIndex) const { |
| if (isDetached()) |
| return WebAXObject(); |
| |
| if (!m_private->isAXTable()) |
| return WebAXObject(); |
| |
| const AXObject::AXObjectVector& columns = |
| toAXTable(m_private.get())->columns(); |
| if (columnIndex < columns.size()) |
| return WebAXObject(columns[columnIndex]); |
| |
| return WebAXObject(); |
| } |
| |
| unsigned WebAXObject::rowIndex() const { |
| if (isDetached()) |
| return 0; |
| |
| if (!m_private->isTableRow()) |
| return 0; |
| |
| return toAXTableRow(m_private.get())->rowIndex(); |
| } |
| |
| WebAXObject WebAXObject::rowHeader() const { |
| if (isDetached()) |
| return WebAXObject(); |
| |
| if (!m_private->isTableRow()) |
| return WebAXObject(); |
| |
| return WebAXObject(toAXTableRow(m_private.get())->headerObject()); |
| } |
| |
| void WebAXObject::rowHeaders(WebVector<WebAXObject>& rowHeaderElements) const { |
| if (isDetached()) |
| return; |
| |
| if (!m_private->isAXTable()) |
| return; |
| |
| AXObject::AXObjectVector headers; |
| toAXTable(m_private.get())->rowHeaders(headers); |
| |
| size_t headerCount = headers.size(); |
| WebVector<WebAXObject> result(headerCount); |
| |
| for (size_t i = 0; i < headerCount; i++) |
| result[i] = WebAXObject(headers[i]); |
| |
| rowHeaderElements.swap(result); |
| } |
| |
| unsigned WebAXObject::columnIndex() const { |
| if (isDetached()) |
| return 0; |
| |
| if (m_private->roleValue() != ColumnRole) |
| return 0; |
| |
| return toAXTableColumn(m_private.get())->columnIndex(); |
| } |
| |
| WebAXObject WebAXObject::columnHeader() const { |
| if (isDetached()) |
| return WebAXObject(); |
| |
| if (m_private->roleValue() != ColumnRole) |
| return WebAXObject(); |
| |
| return WebAXObject(toAXTableColumn(m_private.get())->headerObject()); |
| } |
| |
| void WebAXObject::columnHeaders( |
| WebVector<WebAXObject>& columnHeaderElements) const { |
| if (isDetached()) |
| return; |
| |
| if (!m_private->isAXTable()) |
| return; |
| |
| AXObject::AXObjectVector headers; |
| toAXTable(m_private.get())->columnHeaders(headers); |
| |
| size_t headerCount = headers.size(); |
| WebVector<WebAXObject> result(headerCount); |
| |
| for (size_t i = 0; i < headerCount; i++) |
| result[i] = WebAXObject(headers[i]); |
| |
| columnHeaderElements.swap(result); |
| } |
| |
| unsigned WebAXObject::cellColumnIndex() const { |
| if (isDetached()) |
| return 0; |
| |
| if (!m_private->isTableCell()) |
| return 0; |
| |
| std::pair<unsigned, unsigned> columnRange; |
| toAXTableCell(m_private.get())->columnIndexRange(columnRange); |
| return columnRange.first; |
| } |
| |
| unsigned WebAXObject::cellColumnSpan() const { |
| if (isDetached()) |
| return 0; |
| |
| if (!m_private->isTableCell()) |
| return 0; |
| |
| std::pair<unsigned, unsigned> columnRange; |
| toAXTableCell(m_private.get())->columnIndexRange(columnRange); |
| return columnRange.second; |
| } |
| |
| unsigned WebAXObject::cellRowIndex() const { |
| if (isDetached()) |
| return 0; |
| |
| if (!m_private->isTableCell()) |
| return 0; |
| |
| std::pair<unsigned, unsigned> rowRange; |
| toAXTableCell(m_private.get())->rowIndexRange(rowRange); |
| return rowRange.first; |
| } |
| |
| unsigned WebAXObject::cellRowSpan() const { |
| if (isDetached()) |
| return 0; |
| |
| if (!m_private->isTableCell()) |
| return 0; |
| |
| std::pair<unsigned, unsigned> rowRange; |
| toAXTableCell(m_private.get())->rowIndexRange(rowRange); |
| return rowRange.second; |
| } |
| |
| WebAXSortDirection WebAXObject::sortDirection() const { |
| if (isDetached()) |
| return WebAXSortDirectionUndefined; |
| |
| return static_cast<WebAXSortDirection>(m_private->getSortDirection()); |
| } |
| |
| void WebAXObject::loadInlineTextBoxes() const { |
| if (isDetached()) |
| return; |
| |
| m_private->loadInlineTextBoxes(); |
| } |
| |
| WebAXObject WebAXObject::nextOnLine() const { |
| if (isDetached()) |
| return WebAXObject(); |
| |
| return WebAXObject(m_private.get()->nextOnLine()); |
| } |
| |
| WebAXObject WebAXObject::previousOnLine() const { |
| if (isDetached()) |
| return WebAXObject(); |
| |
| return WebAXObject(m_private.get()->previousOnLine()); |
| } |
| |
| void WebAXObject::markers(WebVector<WebAXMarkerType>& types, |
| WebVector<int>& starts, |
| WebVector<int>& ends) const { |
| if (isDetached()) |
| return; |
| |
| Vector<DocumentMarker::MarkerType> markerTypes; |
| Vector<AXObject::AXRange> markerRanges; |
| m_private->markers(markerTypes, markerRanges); |
| DCHECK_EQ(markerTypes.size(), markerRanges.size()); |
| |
| WebVector<WebAXMarkerType> webMarkerTypes(markerTypes.size()); |
| WebVector<int> startOffsets(markerRanges.size()); |
| WebVector<int> endOffsets(markerRanges.size()); |
| for (size_t i = 0; i < markerTypes.size(); ++i) { |
| webMarkerTypes[i] = static_cast<WebAXMarkerType>(markerTypes[i]); |
| DCHECK(markerRanges[i].isSimple()); |
| startOffsets[i] = markerRanges[i].anchorOffset; |
| endOffsets[i] = markerRanges[i].focusOffset; |
| } |
| |
| types.swap(webMarkerTypes); |
| starts.swap(startOffsets); |
| ends.swap(endOffsets); |
| } |
| |
| void WebAXObject::characterOffsets(WebVector<int>& offsets) const { |
| if (isDetached()) |
| return; |
| |
| Vector<int> offsetsVector; |
| m_private->textCharacterOffsets(offsetsVector); |
| |
| size_t vectorSize = offsetsVector.size(); |
| WebVector<int> offsetsWebVector(vectorSize); |
| for (size_t i = 0; i < vectorSize; i++) |
| offsetsWebVector[i] = offsetsVector[i]; |
| offsets.swap(offsetsWebVector); |
| } |
| |
| void WebAXObject::wordBoundaries(WebVector<int>& starts, |
| WebVector<int>& ends) const { |
| if (isDetached()) |
| return; |
| |
| Vector<AXObject::AXRange> wordBoundaries; |
| m_private->wordBoundaries(wordBoundaries); |
| |
| WebVector<int> wordStartOffsets(wordBoundaries.size()); |
| WebVector<int> wordEndOffsets(wordBoundaries.size()); |
| for (size_t i = 0; i < wordBoundaries.size(); ++i) { |
| DCHECK(wordBoundaries[i].isSimple()); |
| wordStartOffsets[i] = wordBoundaries[i].anchorOffset; |
| wordEndOffsets[i] = wordBoundaries[i].focusOffset; |
| } |
| |
| starts.swap(wordStartOffsets); |
| ends.swap(wordEndOffsets); |
| } |
| |
| bool WebAXObject::isScrollableContainer() const { |
| if (isDetached()) |
| return false; |
| |
| return m_private->isScrollableContainer(); |
| } |
| |
| WebPoint WebAXObject::getScrollOffset() const { |
| if (isDetached()) |
| return WebPoint(); |
| |
| return m_private->getScrollOffset(); |
| } |
| |
| WebPoint WebAXObject::minimumScrollOffset() const { |
| if (isDetached()) |
| return WebPoint(); |
| |
| return m_private->minimumScrollOffset(); |
| } |
| |
| WebPoint WebAXObject::maximumScrollOffset() const { |
| if (isDetached()) |
| return WebPoint(); |
| |
| return m_private->maximumScrollOffset(); |
| } |
| |
| void WebAXObject::setScrollOffset(const WebPoint& offset) const { |
| if (isDetached()) |
| return; |
| |
| m_private->setScrollOffset(offset); |
| } |
| |
| void WebAXObject::getRelativeBounds(WebAXObject& offsetContainer, |
| WebFloatRect& boundsInContainer, |
| SkMatrix44& containerTransform) const { |
| if (isDetached()) |
| return; |
| |
| #if DCHECK_IS_ON() |
| DCHECK(isLayoutClean(m_private->getDocument())); |
| #endif |
| |
| AXObject* container = nullptr; |
| FloatRect bounds; |
| m_private->getRelativeBounds(&container, bounds, containerTransform); |
| offsetContainer = WebAXObject(container); |
| boundsInContainer = WebFloatRect(bounds); |
| } |
| |
| void WebAXObject::scrollToMakeVisible() const { |
| if (!isDetached()) |
| m_private->scrollToMakeVisible(); |
| } |
| |
| void WebAXObject::scrollToMakeVisibleWithSubFocus( |
| const WebRect& subfocus) const { |
| if (!isDetached()) |
| m_private->scrollToMakeVisibleWithSubFocus(subfocus); |
| } |
| |
| void WebAXObject::scrollToGlobalPoint(const WebPoint& point) const { |
| if (!isDetached()) |
| m_private->scrollToGlobalPoint(point); |
| } |
| |
| WebAXObject::WebAXObject(AXObject* object) : m_private(object) {} |
| |
| WebAXObject& WebAXObject::operator=(AXObject* object) { |
| m_private = object; |
| return *this; |
| } |
| |
| WebAXObject::operator AXObject*() const { |
| return m_private.get(); |
| } |
| |
| } // namespace blink |