AX: Implement paragraph related text marker functions using TextIterator
[WebKit-https.git] / Source / WebCore / accessibility / AXObjectCache.h
index bfa9277..bb95868 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2003, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2003, 2006, 2007, 2008, 2009, 2010, 2011, 2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 #ifndef AXObjectCache_h
 #define AXObjectCache_h
 
+#include "AXTextStateChangeIntent.h"
 #include "AccessibilityObject.h"
+#include "Range.h"
 #include "Timer.h"
+#include "VisibleUnits.h"
 #include <limits.h>
 #include <wtf/Forward.h>
 #include <wtf/HashMap.h>
 #include <wtf/HashSet.h>
+#include <wtf/ListHashSet.h>
 #include <wtf/RefPtr.h>
 
 namespace WebCore {
@@ -49,9 +53,35 @@ struct TextMarkerData {
     AXID axID;
     Node* node;
     int offset;
+    int characterStartIndex;
+    int characterOffset;
+    bool ignored;
     EAffinity affinity;
 };
 
+struct CharacterOffset {
+    Node* node;
+    int startIndex;
+    int offset;
+    int remainingOffset;
+    
+    CharacterOffset(Node* n = nullptr, int startIndex = 0, int offset = 0, int remaining = 0)
+        : node(n)
+        , startIndex(startIndex)
+        , offset(offset)
+        , remainingOffset(remaining)
+    { }
+    
+    int remaining() const { return remainingOffset; }
+    bool isNull() const { return !node; }
+    bool isEqual(CharacterOffset& other) const
+    {
+        if (isNull() || other.isNull())
+            return false;
+        return node == other.node && startIndex == other.startIndex && offset == other.offset;
+    }
+};
+
 class AXComputedObjectAttributeCache {
 public:
     AccessibilityObjectInclusion getIgnored(AXID) const;
@@ -67,6 +97,10 @@ private:
     HashMap<AXID, CachedAXObjectAttributes> m_idMapping;
 };
 
+#if !PLATFORM(COCOA)
+enum AXTextChange { AXTextInserted, AXTextDeleted, AXTextAttributesChanged };
+#endif
+
 enum PostTarget { TargetElement, TargetObservableParent };
 
 enum PostType { PostSynchronously, PostAsynchronously };
@@ -122,6 +156,9 @@ public:
     void handleScrolledToAnchor(const Node* anchorNode);
     void handleAriaExpandedChange(Node*);
     void handleScrollbarUpdate(ScrollView*);
+    
+    void handleAriaModalChange(Node*);
+    Node* ariaModalNode();
 
     void handleAttributeChanged(const QualifiedName& attrName, Element*);
     void recomputeIsIgnored(RenderObject* renderer);
@@ -154,7 +191,29 @@ public:
 
     // Text marker utilities.
     void textMarkerDataForVisiblePosition(TextMarkerData&, const VisiblePosition&);
+    void textMarkerDataForCharacterOffset(TextMarkerData&, const CharacterOffset&);
+    void textMarkerDataForNextCharacterOffset(TextMarkerData&, const CharacterOffset&);
+    void textMarkerDataForPreviousCharacterOffset(TextMarkerData&, const CharacterOffset&);
     VisiblePosition visiblePositionForTextMarkerData(TextMarkerData&);
+    CharacterOffset characterOffsetForTextMarkerData(TextMarkerData&);
+    CharacterOffset nextCharacterOffset(const CharacterOffset&, bool ignoreStart = true);
+    CharacterOffset previousCharacterOffset(const CharacterOffset&, bool ignoreStart = true);
+    void startOrEndTextMarkerDataForRange(TextMarkerData&, RefPtr<Range>, bool);
+    AccessibilityObject* accessibilityObjectForTextMarkerData(TextMarkerData&);
+    RefPtr<Range> rangeForUnorderedCharacterOffsets(const CharacterOffset&, const CharacterOffset&);
+    static RefPtr<Range> rangeForNodeContents(Node*);
+    static int lengthForRange(Range*);
+    
+    // Word boundary
+    CharacterOffset nextWordEndCharacterOffset(const CharacterOffset&);
+    CharacterOffset previousWordStartCharacterOffset(const CharacterOffset&);
+    RefPtr<Range> leftWordRange(const CharacterOffset&);
+    RefPtr<Range> rightWordRange(const CharacterOffset&);
+    
+    // Paragraph
+    RefPtr<Range> paragraphForCharacterOffset(const CharacterOffset&);
+    CharacterOffset nextParagraphEndCharacterOffset(const CharacterOffset&);
+    CharacterOffset previousParagraphStartCharacterOffset(const CharacterOffset&);
 
     enum AXNotification {
         AXActiveDescendantChanged,
@@ -189,12 +248,18 @@ public:
     void postNotification(Node*, AXNotification, PostTarget = TargetElement, PostType = PostAsynchronously);
     void postNotification(AccessibilityObject*, Document*, AXNotification, PostTarget = TargetElement, PostType = PostAsynchronously);
 
-    enum AXTextChange {
-        AXTextInserted,
-        AXTextDeleted,
-    };
+#ifndef NDEBUG
+    void showIntent(const AXTextStateChangeIntent&);
+#endif
+
+    void setTextSelectionIntent(const AXTextStateChangeIntent&);
+    void setIsSynchronizingSelection(bool);
 
-    void nodeTextChangeNotification(Node*, AXTextChange, unsigned offset, const String&);
+    void postTextStateChangeNotification(Node*, AXTextEditType, const String&, const VisiblePosition&);
+    void postTextReplacementNotification(Node*, AXTextEditType deletionType, const String& deletedText, AXTextEditType insertionType, const String& insertedText, const VisiblePosition&);
+    void postTextStateChangeNotification(Node*, const AXTextStateChangeIntent&, const VisibleSelection&);
+    void postTextStateChangeNotification(const Position&, const AXTextStateChangeIntent&, const VisibleSelection&);
+    void postLiveRegionChangeNotification(AccessibilityObject*);
 
     enum AXLoadingEvent {
         AXLoadingStarted,
@@ -213,12 +278,24 @@ public:
     AXComputedObjectAttributeCache* computedObjectAttributeCache() { return m_computedObjectAttributeCache.get(); }
 
     Document& document() const { return m_document; }
-    
+
+#if PLATFORM(MAC)
+    static void setShouldRepostNotificationsForTests(bool value);
+#endif
+
 protected:
     void postPlatformNotification(AccessibilityObject*, AXNotification);
     void platformHandleFocusedUIElementChanged(Node* oldFocusedNode, Node* newFocusedNode);
 
-    void nodeTextChangePlatformNotification(AccessibilityObject*, AXTextChange, unsigned offset, const String&);
+#if PLATFORM(COCOA)
+    void postTextStateChangePlatformNotification(AccessibilityObject*, const AXTextStateChangeIntent&, const VisibleSelection&);
+    void postTextStateChangePlatformNotification(AccessibilityObject*, AXTextEditType, const String&, const VisiblePosition&);
+    void postTextReplacementPlatformNotification(AccessibilityObject*, AXTextEditType, const String&, AXTextEditType, const String&, const VisiblePosition&);
+#else
+    static AXTextChange textChangeForEditType(AXTextEditType);
+    void nodeTextChangePlatformNotification(AccessibilityObject*, AXTextChange, unsigned, const String&);
+#endif
+
     void frameLoadingEventPlatformNotification(AccessibilityObject*, AXLoadingEvent);
     void textChanged(AccessibilityObject*);
     void labelChanged(Element*);
@@ -227,8 +304,51 @@ protected:
     void setNodeInUse(Node* n) { m_textMarkerNodes.add(n); }
     void removeNodeForUse(Node* n) { m_textMarkerNodes.remove(n); }
     bool isNodeInUse(Node* n) { return m_textMarkerNodes.contains(n); }
+    
+    // CharacterOffset functions.
+    enum TraverseOption { TraverseOptionDefault = 1 << 0, TraverseOptionToNodeEnd = 1 << 1, TraverseOptionIncludeStart = 1 << 2 };
+    Node* nextNode(Node*) const;
+    Node* previousNode(Node*) const;
+    CharacterOffset traverseToOffsetInRange(RefPtr<Range>, int, bool, bool stayWithinRange = false);
+    VisiblePosition visiblePositionFromCharacterOffset(const CharacterOffset&);
+    CharacterOffset characterOffsetFromVisiblePosition(const VisiblePosition&);
+    void setTextMarkerDataWithCharacterOffset(TextMarkerData&, const CharacterOffset&);
+    UChar32 characterAfter(const CharacterOffset&);
+    UChar32 characterBefore(const CharacterOffset&);
+    CharacterOffset startOrEndCharacterOffsetForRange(RefPtr<Range>, bool);
+    CharacterOffset characterOffsetForNodeAndOffset(Node&, int, TraverseOption = TraverseOptionDefault);
+    CharacterOffset previousWordBoundary(CharacterOffset&, BoundarySearchFunction);
+    CharacterOffset nextWordBoundary(CharacterOffset&, BoundarySearchFunction);
+    CharacterOffset startCharacterOffsetOfWord(const CharacterOffset&, EWordSide = RightWordIfOnBoundary);
+    CharacterOffset endCharacterOffsetOfWord(const CharacterOffset&, EWordSide = RightWordIfOnBoundary);
+    CharacterOffset startCharacterOffsetOfParagraph(const CharacterOffset&, EditingBoundaryCrossingRule = CannotCrossEditingBoundary);
+    CharacterOffset endCharacterOffsetOfParagraph(const CharacterOffset&, EditingBoundaryCrossingRule = CannotCrossEditingBoundary);
 
 private:
+    AccessibilityObject* rootWebArea();
+
+    static AccessibilityObject* focusedImageMapUIElement(HTMLAreaElement*);
+
+    AXID getAXID(AccessibilityObject*);
+
+    void notificationPostTimerFired();
+
+    void liveRegionChangedNotificationPostTimerFired();
+
+    void postTextStateChangeNotification(AccessibilityObject*, const AXTextStateChangeIntent&, const VisibleSelection&);
+
+    bool enqueuePasswordValueChangeNotification(AccessibilityObject*);
+    void passwordNotificationPostTimerFired();
+
+    void handleMenuOpened(Node*);
+    void handleLiveRegionCreated(Node*);
+    void handleMenuItemSelected(Node*);
+    
+    // aria-modal related
+    void findAriaModalNodes();
+    void updateCurrentAriaModalNode();
+    bool isNodeVisible(Node*) const;
+
     Document& m_document;
     HashMap<AXID, RefPtr<AccessibilityObject>> m_objects;
     HashMap<RenderObject*, AXID> m_renderObjectMapping;
@@ -238,19 +358,24 @@ private:
     std::unique_ptr<AXComputedObjectAttributeCache> m_computedObjectAttributeCache;
     WEBCORE_EXPORT static bool gAccessibilityEnabled;
     WEBCORE_EXPORT static bool gAccessibilityEnhancedUserInterfaceEnabled;
-    
+
     HashSet<AXID> m_idsInUse;
-    
+
     Timer m_notificationPostTimer;
     Vector<std::pair<RefPtr<AccessibilityObject>, AXNotification>> m_notificationsToPost;
-    void notificationPostTimerFired();
-    void handleMenuOpened(Node*);
-    void handleLiveRegionCreated(Node*);
-    void handleMenuItemSelected(Node*);
+
+    Timer m_passwordNotificationPostTimer;
+
+    ListHashSet<RefPtr<AccessibilityObject>> m_passwordNotificationsToPost;
     
-    static AccessibilityObject* focusedImageMapUIElement(HTMLAreaElement*);
+    Timer m_liveRegionChangedPostTimer;
+    ListHashSet<RefPtr<AccessibilityObject>> m_liveRegionObjectsSet;
     
-    AXID getAXID(AccessibilityObject*);
+    Node* m_currentAriaModalNode;
+    ListHashSet<Node*> m_ariaModalNodesSet;
+
+    AXTextStateChangeIntent m_textSelectionIntent;
+    bool m_isSynchronizingSelection { false };
 };
 
 class AXAttributeCacheEnabler
@@ -258,11 +383,13 @@ class AXAttributeCacheEnabler
 public:
     explicit AXAttributeCacheEnabler(AXObjectCache *cache);
     ~AXAttributeCacheEnabler();
-    
+
+#if HAVE(ACCESSIBILITY)
 private:
     AXObjectCache* m_cache;
+#endif
 };
-    
+
 bool nodeHasRole(Node*, const String& role);
 // This will let you know if aria-hidden was explicitly set to false.
 bool isNodeAriaVisible(Node*);
@@ -270,7 +397,7 @@ bool isNodeAriaVisible(Node*);
 #if !HAVE(ACCESSIBILITY)
 inline AccessibilityObjectInclusion AXComputedObjectAttributeCache::getIgnored(AXID) const { return DefaultBehavior; }
 inline void AXComputedObjectAttributeCache::setIgnored(AXID, AccessibilityObjectInclusion) { }
-inline AXObjectCache::AXObjectCache(Document& document) : m_document(document), m_notificationPostTimer(this, (Timer::TimerFiredFunction) nullptr) { }
+inline AXObjectCache::AXObjectCache(Document& document) : m_document(document), m_notificationPostTimer(nullptr), m_passwordNotificationPostTimer(nullptr), m_liveRegionChangedPostTimer(nullptr) { }
 inline AXObjectCache::~AXObjectCache() { }
 inline AccessibilityObject* AXObjectCache::focusedUIElementForPage(const Page*) { return nullptr; }
 inline AccessibilityObject* AXObjectCache::get(RenderObject*) { return nullptr; }
@@ -282,12 +409,12 @@ inline AccessibilityObject* AXObjectCache::getOrCreate(Node*) { return nullptr;
 inline AccessibilityObject* AXObjectCache::getOrCreate(Widget*) { return nullptr; }
 inline AccessibilityObject* AXObjectCache::rootObject() { return nullptr; }
 inline AccessibilityObject* AXObjectCache::rootObjectForFrame(Frame*) { return nullptr; }
-inline Element* AXObjectCache::rootAXEditableElement(Node*) { return nullptr; }
 inline bool nodeHasRole(Node*, const String&) { return false; }
 inline void AXObjectCache::startCachingComputedObjectAttributesUntilTreeMutates() { }
 inline void AXObjectCache::stopCachingComputedObjectAttributes() { }
 inline bool isNodeAriaVisible(Node*) { return true; }
 inline const Element* AXObjectCache::rootAXEditableElement(const Node*) { return nullptr; }
+inline Node* AXObjectCache::ariaModalNode() { return nullptr; }
 inline void AXObjectCache::attachWrapper(AccessibilityObject*) { }
 inline void AXObjectCache::checkedStateChanged(Node*) { }
 inline void AXObjectCache::childrenChanged(RenderObject*, RenderObject*) { }
@@ -303,23 +430,41 @@ inline void AXObjectCache::frameLoadingEventPlatformNotification(AccessibilityOb
 inline void AXObjectCache::handleActiveDescendantChanged(Node*) { }
 inline void AXObjectCache::handleAriaExpandedChange(Node*) { }
 inline void AXObjectCache::handleAriaRoleChanged(Node*) { }
+inline void AXObjectCache::handleAriaModalChange(Node*) { }
 inline void AXObjectCache::handleFocusedUIElementChanged(Node*, Node*) { }
 inline void AXObjectCache::handleScrollbarUpdate(ScrollView*) { }
 inline void AXObjectCache::handleAttributeChanged(const QualifiedName&, Element*) { }
 inline void AXObjectCache::recomputeIsIgnored(RenderObject*) { }
 inline void AXObjectCache::handleScrolledToAnchor(const Node*) { }
-inline void AXObjectCache::nodeTextChangeNotification(Node*, AXTextChange, unsigned, const String&) { }
-inline void AXObjectCache::nodeTextChangePlatformNotification(AccessibilityObject*, AXTextChange, unsigned, const String&) { }
+inline void AXObjectCache::postTextStateChangeNotification(Node*, const AXTextStateChangeIntent&, const VisibleSelection&) { }
+inline void AXObjectCache::postTextStateChangeNotification(Node*, AXTextEditType, const String&, const VisiblePosition&) { }
+inline void AXObjectCache::postTextReplacementNotification(Node*, AXTextEditType, const String&, AXTextEditType, const String&, const VisiblePosition&) { }
 inline void AXObjectCache::postNotification(AccessibilityObject*, Document*, AXNotification, PostTarget, PostType) { }
 inline void AXObjectCache::postNotification(RenderObject*, AXNotification, PostTarget, PostType) { }
 inline void AXObjectCache::postNotification(Node*, AXNotification, PostTarget, PostType) { }
 inline void AXObjectCache::postPlatformNotification(AccessibilityObject*, AXNotification) { }
+inline void AXObjectCache::postLiveRegionChangeNotification(AccessibilityObject*) { }
+inline RefPtr<Range> AXObjectCache::rangeForNodeContents(Node*) { return nullptr; }
 inline void AXObjectCache::remove(AXID) { }
 inline void AXObjectCache::remove(RenderObject*) { }
 inline void AXObjectCache::remove(Node*) { }
 inline void AXObjectCache::remove(Widget*) { }
 inline void AXObjectCache::selectedChildrenChanged(RenderObject*) { }
 inline void AXObjectCache::selectedChildrenChanged(Node*) { }
+inline void AXObjectCache::setIsSynchronizingSelection(bool) { }
+inline void AXObjectCache::setTextSelectionIntent(const AXTextStateChangeIntent&) { }
+#if PLATFORM(COCOA)
+inline void AXObjectCache::postTextStateChangePlatformNotification(AccessibilityObject*, const AXTextStateChangeIntent&, const VisibleSelection&) { }
+inline void AXObjectCache::postTextStateChangePlatformNotification(AccessibilityObject*, AXTextEditType, const String&, const VisiblePosition&) { }
+inline void AXObjectCache::postTextReplacementPlatformNotification(AccessibilityObject*, AXTextEditType, const String&, AXTextEditType, const String&, const VisiblePosition&) { }
+#else
+inline AXTextChange AXObjectCache::textChangeForEditType(AXTextEditType) { return AXTextInserted; }
+inline void AXObjectCache::nodeTextChangePlatformNotification(AccessibilityObject*, AXTextChange, unsigned, const String&) { }
+#endif
+
+inline AXAttributeCacheEnabler::AXAttributeCacheEnabler(AXObjectCache*) { }
+inline AXAttributeCacheEnabler::~AXAttributeCacheEnabler() { }
+
 #endif
 
 }