DocAccessible.h (29806B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #ifndef mozilla_a11y_DocAccessible_h__ 7 #define mozilla_a11y_DocAccessible_h__ 8 9 #include "HyperTextAccessible.h" 10 #include "AccEvent.h" 11 #include "nsAccessibilityService.h" 12 13 #include "nsClassHashtable.h" 14 #include "nsTHashMap.h" 15 #include "mozilla/UniquePtr.h" 16 #include "nsIDocumentObserver.h" 17 #include "nsITimer.h" 18 #include "nsTHashSet.h" 19 #include "nsWeakReference.h" 20 21 const uint32_t kDefaultCacheLength = 128; 22 23 namespace mozilla { 24 25 class EditorBase; 26 class PresShell; 27 28 namespace dom { 29 class Document; 30 } 31 32 namespace a11y { 33 34 class DocManager; 35 class NotificationController; 36 class DocAccessibleChild; 37 class RelatedAccIterator; 38 template <class Class, class... Args> 39 class TNotification; 40 41 /** 42 * An accessibility tree node that originated in a content process and 43 * represents a document. Tabs, in-process iframes, and out-of-process iframes 44 * all use this class to represent the doc they contain. 45 */ 46 class DocAccessible : public HyperTextAccessible, 47 public nsIDocumentObserver, 48 public nsSupportsWeakReference { 49 NS_DECL_ISUPPORTS_INHERITED 50 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(DocAccessible, LocalAccessible) 51 52 protected: 53 typedef mozilla::dom::Document Document; 54 55 public: 56 DocAccessible(Document* aDocument, PresShell* aPresShell); 57 58 // nsIDocumentObserver 59 NS_DECL_NSIDOCUMENTOBSERVER 60 61 // LocalAccessible 62 virtual void Init(); 63 virtual void Shutdown() override; 64 virtual nsIFrame* GetFrame() const override; 65 virtual nsINode* GetNode() const override; 66 Document* DocumentNode() const { return mDocumentNode; } 67 68 virtual mozilla::a11y::ENameValueFlag DirectName( 69 nsString& aName) const override; 70 virtual EDescriptionValueFlag Description( 71 nsString& aDescription) const override; 72 virtual Accessible* FocusedChild() override; 73 virtual mozilla::a11y::role NativeRole() const override; 74 virtual uint64_t NativeState() const override; 75 virtual uint64_t NativeInteractiveState() const override; 76 virtual bool NativelyUnavailable() const override; 77 virtual void ApplyARIAState(uint64_t* aState) const override; 78 79 virtual void TakeFocus() const override; 80 81 #ifdef A11Y_LOG 82 virtual nsresult HandleAccEvent(AccEvent* aEvent) override; 83 #endif 84 85 virtual nsRect RelativeBounds(nsIFrame** aRelativeFrame) const override; 86 87 // ActionAccessible 88 virtual bool HasPrimaryAction() const override; 89 virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) override; 90 91 // HyperTextAccessible 92 virtual already_AddRefed<EditorBase> GetEditor() const override; 93 94 // DocAccessible 95 96 /** 97 * Return document URL. 98 */ 99 void URL(nsAString& aURL) const; 100 101 /** 102 * Return DOM document title. 103 */ 104 void Title(nsString& aTitle) const; 105 106 /** 107 * Return DOM document mime type. 108 */ 109 void MimeType(nsAString& aType) const; 110 /** 111 * Return DOM document type. 112 */ 113 void DocType(nsAString& aType) const; 114 115 /** 116 * Adds an entry to queued cache updates indicating aAcc requires 117 * a cache update on domain aNewDomain. If we've already queued an update 118 * for aAcc, aNewDomain is or'd with the existing domain(s) 119 * and the map is updated. Otherwise, the entry is simply inserted. 120 * This function also schedules processing on the controller. 121 * Note that this CANNOT be used for anything which fires events, since events 122 * must be fired after their associated cache update. 123 */ 124 void QueueCacheUpdate(LocalAccessible* aAcc, uint64_t aNewDomain, 125 bool aBypassActiveDomains = false); 126 127 /** 128 * Walks the dependent ids and elements maps for the given accessible and 129 * queues a CacheDomain::Relations cache update fore each related acc. 130 * We call this when we observe an ID mutation or when an acc is bound 131 * to its document. 132 */ 133 void QueueCacheUpdateForDependentRelations( 134 LocalAccessible* aAcc, const nsAttrValue* aOldId = nullptr); 135 136 /** 137 * Returns true if the instance has shutdown. 138 */ 139 bool HasShutdown() const { return !mPresShell; } 140 141 /** 142 * Return presentation shell for this document accessible. 143 */ 144 PresShell* PresShellPtr() const { 145 MOZ_DIAGNOSTIC_ASSERT(!HasShutdown()); 146 return mPresShell; 147 } 148 149 /** 150 * Return the presentation shell's context. 151 */ 152 nsPresContext* PresContext() const; 153 154 /** 155 * Return true if associated DOM document was loaded and isn't unloading. 156 */ 157 bool IsContentLoaded() const; 158 159 bool IsHidden() const; 160 161 void SetViewportCacheDirty(bool aDirty) { mViewportCacheDirty = aDirty; } 162 163 /** 164 * Document load states. 165 */ 166 enum LoadState { 167 // initial tree construction is pending 168 eTreeConstructionPending = 0, 169 // initial tree construction done 170 eTreeConstructed = 1, 171 // DOM document is loaded. 172 eDOMLoaded = 1 << 1, 173 // document is ready 174 eReady = eTreeConstructed | eDOMLoaded, 175 // document and all its subdocuments are ready 176 eCompletelyLoaded = eReady | 1 << 2 177 }; 178 179 /** 180 * Return true if the document has given document state. 181 */ 182 bool HasLoadState(LoadState aState) const { 183 return (mLoadState & static_cast<uint32_t>(aState)) == 184 static_cast<uint32_t>(aState); 185 } 186 187 /** 188 * Return a native window handler or pointer depending on platform. 189 */ 190 virtual void* GetNativeWindow() const; 191 192 /** 193 * Return the parent document. 194 */ 195 DocAccessible* ParentDocument() const { 196 return mParent ? mParent->Document() : nullptr; 197 } 198 199 /** 200 * Return the child document count. 201 */ 202 uint32_t ChildDocumentCount() const { return mChildDocuments.Length(); } 203 204 /** 205 * Return the child document at the given index. 206 */ 207 DocAccessible* GetChildDocumentAt(uint32_t aIndex) const { 208 return mChildDocuments.SafeElementAt(aIndex, nullptr); 209 } 210 211 /** 212 * Fire accessible event asynchronously. 213 */ 214 void FireDelayedEvent(AccEvent* aEvent); 215 void FireDelayedEvent(uint32_t aEventType, LocalAccessible* aTarget); 216 void FireEventsOnInsertion(LocalAccessible* aContainer); 217 218 /** 219 * Fire value change event on the given accessible if applicable. 220 */ 221 void MaybeNotifyOfValueChange(LocalAccessible* aAccessible); 222 223 void SetAnchorJump(nsIContent* aTargetNode) { mAnchorJumpElm = aTargetNode; } 224 225 /** 226 * Process an anchor jump, if any. Returns false if the current focus caused 227 * us to ignore the anchor jump, true otherwise. 228 */ 229 bool ProcessAnchorJump(); 230 231 /** 232 * Bind the child document to the tree. 233 */ 234 void BindChildDocument(DocAccessible* aDocument); 235 236 /** 237 * Process the generic notification. 238 * 239 * @note The caller must guarantee that the given instance still exists when 240 * notification is processed. 241 * @see NotificationController::HandleNotification 242 */ 243 template <class Class, class... Args> 244 void HandleNotification( 245 Class* aInstance, 246 typename TNotification<Class, Args...>::Callback aMethod, Args*... aArgs); 247 248 /** 249 * Return the cached accessible by the given DOM node if it's in subtree of 250 * this document accessible or the document accessible itself, otherwise null. 251 * 252 * @return the accessible object 253 */ 254 LocalAccessible* GetAccessible(nsINode* aNode) const; 255 256 /** 257 * Return an accessible for the given node even if the node is not in 258 * document's node map cache (like HTML area element). 259 * 260 * XXX: it should be really merged with GetAccessible(). 261 */ 262 LocalAccessible* GetAccessibleEvenIfNotInMap(nsINode* aNode) const; 263 LocalAccessible* GetAccessibleEvenIfNotInMapOrContainer(nsINode* aNode) const; 264 265 /** 266 * Return whether the given DOM node has an accessible or not. 267 */ 268 bool HasAccessible(nsINode* aNode) const { return GetAccessible(aNode); } 269 270 /** 271 * Return the cached accessible by the given unique ID within this document. 272 * 273 * @note the unique ID matches with the uniqueID() of Accessible 274 * 275 * @param aUniqueID [in] the unique ID used to cache the node. 276 */ 277 LocalAccessible* GetAccessibleByUniqueID(void* aUniqueID) { 278 return UniqueID() == aUniqueID ? this : mAccessibleCache.GetWeak(aUniqueID); 279 } 280 281 /** 282 * Return the cached accessible by the given unique ID looking through 283 * this and nested documents. 284 */ 285 LocalAccessible* GetAccessibleByUniqueIDInSubtree(void* aUniqueID); 286 287 /** 288 * Return an accessible for the given DOM node or container accessible if 289 * the node is not accessible. If aNoContainerIfPruned is true it will return 290 * null if the node is in a pruned subtree (eg. aria-hidden or unselected deck 291 * panel) 292 */ 293 LocalAccessible* GetAccessibleOrContainer( 294 nsINode* aNode, bool aNoContainerIfPruned = false) const; 295 296 /** 297 * Return a container accessible for the given DOM node. 298 */ 299 LocalAccessible* GetContainerAccessible(nsINode* aNode) const; 300 301 /** 302 * Return an accessible for the given node if any, or an immediate accessible 303 * container for it. 304 */ 305 LocalAccessible* AccessibleOrTrueContainer( 306 nsINode* aNode, bool aNoContainerIfPruned = false) const; 307 308 /** 309 * Return an accessible for the given node or its first accessible descendant. 310 */ 311 LocalAccessible* GetAccessibleOrDescendant(nsINode* aNode) const; 312 313 /** 314 * Returns aria-owns seized child at the given index. 315 */ 316 LocalAccessible* ARIAOwnedAt(LocalAccessible* aParent, 317 uint32_t aIndex) const { 318 nsTArray<RefPtr<LocalAccessible>>* children = mARIAOwnsHash.Get(aParent); 319 if (children) { 320 return children->SafeElementAt(aIndex); 321 } 322 return nullptr; 323 } 324 uint32_t ARIAOwnedCount(LocalAccessible* aParent) const { 325 nsTArray<RefPtr<LocalAccessible>>* children = mARIAOwnsHash.Get(aParent); 326 return children ? children->Length() : 0; 327 } 328 329 /** 330 * Return true if the given ID is referred by relation attribute. 331 */ 332 bool IsDependentID(dom::Element* aElement, const nsAString& aID) const { 333 return GetRelProviders(aElement, aID); 334 } 335 336 /** 337 * Initialize the newly created accessible and put it into document caches. 338 * 339 * @param aAccessible [in] created accessible 340 * @param aRoleMapEntry [in] the role map entry role the ARIA role or 341 * nullptr if none 342 */ 343 void BindToDocument(LocalAccessible* aAccessible, 344 const nsRoleMapEntry* aRoleMapEntry); 345 346 /** 347 * Remove from document and shutdown the given accessible. 348 */ 349 void UnbindFromDocument(LocalAccessible* aAccessible); 350 351 /** 352 * Notify the document accessible that content was inserted. 353 */ 354 void ContentInserted(nsIContent* aStartChildNode, nsIContent* aEndChildNode); 355 356 /** 357 * @see nsAccessibilityService::ScheduleAccessibilitySubtreeUpdate 358 */ 359 void ScheduleTreeUpdate(nsIContent* aContent); 360 361 /** 362 * Update the tree on content removal. 363 */ 364 void ContentRemoved(LocalAccessible* aAccessible); 365 void ContentRemoved(nsIContent* aContentNode); 366 367 /** 368 * Updates accessible tree when rendered text is changed. 369 */ 370 void UpdateText(nsIContent* aTextNode); 371 372 /** 373 * Recreate an accessible, results in hide/show events pair. 374 */ 375 void RecreateAccessible(nsIContent* aContent); 376 377 /** 378 * Schedule ARIA owned element relocation if needed. Return true if relocation 379 * was scheduled. 380 */ 381 bool RelocateARIAOwnedIfNeeded(nsIContent* aEl); 382 383 /** 384 * Return a notification controller associated with the document. 385 */ 386 NotificationController* Controller() const { return mNotificationController; } 387 388 /** 389 * If this document is in a content process return the object responsible for 390 * communicating with the main process for it. 391 */ 392 DocAccessibleChild* IPCDoc() const { return mIPCDoc; } 393 394 /** 395 * Notify the document that a DOM node has been scrolled. document will 396 * dispatch throttled accessibility events for scrolling, and a scroll-end 397 * event. This function also queues a cache update for ScrollPosition. 398 */ 399 void HandleScroll(nsINode* aTarget); 400 401 /** 402 * Retrieves the scroll frame (if it exists) for the given accessible 403 * and returns its scroll position and scroll range. If the given 404 * accessible is `this`, return the scroll position and range of 405 * the root scroll frame. Return values have been scaled by the 406 * PresShell's resolution when aShouldScaleByResolution is explicitly 407 * true or unspecified. 408 */ 409 std::pair<nsPoint, nsRect> ComputeScrollData( 410 const LocalAccessible* aAcc, bool aShouldScaleByResolution = true); 411 412 /** 413 * Only works in content process documents. 414 */ 415 bool IsAccessibleBeingMoved(LocalAccessible* aAcc) { 416 return mMovedAccessibles.Contains(aAcc); 417 } 418 419 void AttrElementWillChange(dom::Element* aElement, nsAtom* aAttr); 420 void AttrElementChanged(dom::Element* aElement, nsAtom* aAttr); 421 422 /** 423 * Given an accessible, check if it is anchored to other frames, and 424 * refresh the cache on each of those frames' accessibles. 425 */ 426 void RefreshAnchorRelationCacheForTarget(LocalAccessible* aTarget); 427 428 protected: 429 virtual ~DocAccessible(); 430 431 void LastRelease(); 432 433 // DocAccessible 434 virtual nsresult AddEventListeners(); 435 virtual nsresult RemoveEventListeners(); 436 437 /** 438 * Marks this document as loaded or loading. 439 */ 440 void NotifyOfLoad(uint32_t aLoadEventType); 441 void NotifyOfLoading(bool aIsReloading); 442 443 friend class DocManager; 444 445 /** 446 * Perform initial update (create accessible tree). 447 * Can be overridden by wrappers to prepare initialization work. 448 */ 449 virtual void DoInitialUpdate(); 450 451 /** 452 * Updates root element and picks up ARIA role on it if any. 453 */ 454 void UpdateRootElIfNeeded(); 455 456 /** 457 * Process document load notification, fire document load and state busy 458 * events if applicable. 459 */ 460 void ProcessLoad(); 461 462 /** 463 * Append the given document accessible to this document's child document 464 * accessibles. 465 */ 466 bool AppendChildDocument(DocAccessible* aChildDocument) { 467 // XXX(Bug 1631371) Check if this should use a fallible operation as it 468 // pretended earlier, or change the return type to void. 469 mChildDocuments.AppendElement(aChildDocument); 470 return true; 471 } 472 473 /** 474 * Remove the given document accessible from this document's child document 475 * accessibles. 476 */ 477 void RemoveChildDocument(DocAccessible* aChildDocument) { 478 mChildDocuments.RemoveElement(aChildDocument); 479 } 480 481 /** 482 * Add dependent IDs pointed by accessible element by relation attribute to 483 * cache. If the relation attribute is missed then all relation attributes 484 * are checked. 485 * 486 * @param aRelProvider [in] accessible that element has relation attribute 487 * @param aRelAttr [in, optional] relation attribute 488 */ 489 void AddDependentIDsFor(LocalAccessible* aRelProvider, 490 nsAtom* aRelAttr = nullptr); 491 492 /** 493 * Remove dependent IDs pointed by accessible element by relation attribute 494 * from cache. If the relation attribute is absent then all relation 495 * attributes are checked. 496 * 497 * @param aRelProvider [in] accessible that element has relation attribute 498 * @param aRelAttr [in, optional] relation attribute 499 */ 500 void RemoveDependentIDsFor(LocalAccessible* aRelProvider, 501 nsAtom* aRelAttr = nullptr); 502 503 /** 504 * Add dependent elements targeted by a relation attribute on an accessible 505 * element to the dependent elements cache. This is used for reflected IDL 506 * attributes which return DOM elements and reflect a content attribute, where 507 * the IDL attribute has been set to an element. For example, if the 508 * .popoverTargetElement IDL attribute is set to an element using JS, the 509 * target element will be added to the dependent elements cache. If the 510 * relation attribute is not specified, then all relation attributes are 511 * checked. 512 * 513 * @param aRelProvider [in] the accessible with the relation IDL attribute. 514 * @param aRelAttr [in, optional] the name of the reflected content attribute. 515 * For example, for the popoverTargetElement IDL attribute, this would be 516 * "popovertarget". 517 */ 518 void AddDependentElementsFor(LocalAccessible* aRelProvider, 519 nsAtom* aRelAttr = nullptr); 520 521 /** 522 * Remove dependent elements targeted by a relation attribute on an accessible 523 * element from the dependent elements cache. If the relation attribute is 524 * not specified, then all relation attributes are checked. 525 * 526 * @param aRelProvider [in] the accessible with the relation IDL attribute. 527 * @param aRelAttr [in, optional] the name of the reflected content attribute. 528 */ 529 void RemoveDependentElementsFor(LocalAccessible* aRelProvider, 530 nsAtom* aRelAttr = nullptr); 531 532 /** 533 * Update or recreate an accessible depending on a changed attribute. 534 * 535 * @param aElement [in] the element the attribute was changed on 536 * @param aAttribute [in] the changed attribute 537 * @return true if an action was taken on the attribute change 538 */ 539 bool UpdateAccessibleOnAttrChange(mozilla::dom::Element* aElement, 540 nsAtom* aAttribute); 541 542 /** 543 * Process ARIA active-descendant attribute change. 544 */ 545 void ARIAActiveDescendantChanged(LocalAccessible* aAccessible); 546 547 /** 548 * Update the accessible tree for inserted content. 549 */ 550 void ProcessContentInserted( 551 LocalAccessible* aContainer, 552 const nsTArray<nsCOMPtr<nsIContent>>* aInsertedContent); 553 void ProcessContentInserted(LocalAccessible* aContainer, 554 nsIContent* aInsertedContent); 555 556 /** 557 * Used to notify the document to make it process the invalidation list. 558 * 559 * While children are cached we may encounter the case there's no accessible 560 * for referred content by related accessible. Store these related nodes to 561 * invalidate their containers later. 562 */ 563 void ProcessInvalidationList(); 564 565 /** 566 * Process mPendingUpdates 567 */ 568 void ProcessPendingUpdates(); 569 570 /** 571 * Called from NotificationController to process this doc's 572 * queued cache updates. For each acc in the map, this function 573 * sends a cache update with its corresponding CacheDomain. 574 * Each domain bit in aInitialDomains indicates that this is the first push 575 * for that cache domain. 576 */ 577 void ProcessQueuedCacheUpdates(uint64_t aInitialDomains = 0); 578 579 /** 580 * Called from NotificationController before mutation events are processed to 581 * notify the parent process which Accessibles are being moved (if any). 582 */ 583 void SendAccessiblesWillMove(); 584 585 /** 586 * Called from NotificationController after all mutation events have been 587 * processed to clear our data about mutations during this tick. 588 */ 589 void ClearMutationData() { 590 mMovedAccessibles.Clear(); 591 mInsertedAccessibles.Clear(); 592 mRemovedNodes.Clear(); 593 } 594 595 /** 596 * Steals or puts back accessible subtrees. 597 */ 598 void DoARIAOwnsRelocation(LocalAccessible* aOwner); 599 600 /** 601 * Moves children back under their original parents. 602 */ 603 void PutChildrenBack(nsTArray<RefPtr<LocalAccessible>>* aChildren, 604 uint32_t aStartIdx); 605 606 bool MoveChild(LocalAccessible* aChild, LocalAccessible* aNewParent, 607 int32_t aIdxInParent); 608 609 /** 610 * Create accessible tree. 611 * 612 * @param aRoot [in] a root of subtree to create 613 * @param aFocusedAcc [in, optional] a focused accessible under created 614 * subtree if any 615 */ 616 void CacheChildrenInSubtree(LocalAccessible* aRoot, 617 LocalAccessible** aFocusedAcc = nullptr); 618 void CreateSubtree(LocalAccessible* aRoot); 619 620 /** 621 * Remove accessibles in subtree from node to accessible map. 622 */ 623 void UncacheChildrenInSubtree(LocalAccessible* aRoot); 624 625 /** 626 * Shutdown any cached accessible in the subtree. 627 * 628 * @param aAccessible [in] the root of the subrtee to invalidate accessible 629 * child/parent refs in 630 */ 631 void ShutdownChildrenInSubtree(LocalAccessible* aAccessible); 632 633 /** 634 * Return true if the document is a target of document loading events 635 * (for example, state busy change or document reload events). 636 * 637 * Rule: The root chrome document accessible is never an event target 638 * (for example, Firefox UI window). 639 */ 640 bool IsLoadEventTarget() const; 641 642 /* 643 * Set the object responsible for communicating with the main process on 644 * behalf of this document. 645 */ 646 void SetIPCDoc(DocAccessibleChild* aIPCDoc); 647 648 friend class DocAccessibleChild; 649 650 /** 651 * Used to fire scrolling end event after page scroll. 652 * 653 * @param aTimer [in] the timer object 654 * @param aClosure [in] the document accessible where scrolling happens 655 */ 656 static void ScrollTimerCallback(nsITimer* aTimer, void* aClosure); 657 658 void DispatchScrollingEvent(nsINode* aTarget, uint32_t aEventType); 659 660 /** 661 * Check if an id attribute change affects aria-activedescendant and handle 662 * the aria-activedescendant change if appropriate. 663 * If the currently focused element has aria-activedescendant and an 664 * element's id changes to match this, the id was probably moved from the 665 * previous active descendant, thus making this element the new active 666 * descendant. In that case, accessible focus must be changed accordingly. 667 */ 668 void ARIAActiveDescendantIDMaybeMoved(LocalAccessible* aAccessible); 669 670 /** 671 * Traverse content subtree and for each node do one of 3 things: 672 * 1. Check if content node has an accessible that should be removed and 673 * remove it. 674 * 2. Check if content node has an accessible that needs to be recreated. 675 * Remove it and schedule it for reinsertion. 676 * 3. Check if content node has no accessible but needs one. Schedule one for 677 * insertion. 678 * 679 * Returns true if the root node should be reinserted. 680 */ 681 bool PruneOrInsertSubtree(nsIContent* aRoot); 682 683 protected: 684 /** 685 * State and property flags, kept by mDocFlags. 686 */ 687 enum { 688 // Whether the document is a top level content document in this process. 689 eTopLevelContentDocInProcess = 1 << 0 690 }; 691 692 /** 693 * Cache of accessibles within this document accessible. 694 */ 695 AccessibleHashtable mAccessibleCache; 696 nsTHashMap<nsPtrHashKey<const nsINode>, LocalAccessible*> 697 mNodeToAccessibleMap; 698 699 Document* mDocumentNode; 700 nsCOMPtr<nsITimer> mScrollWatchTimer; 701 nsTHashMap<nsPtrHashKey<nsINode>, TimeStamp> mLastScrollingDispatch; 702 703 /** 704 * Bit mask of document load states (@see LoadState). 705 */ 706 uint32_t mLoadState : 3; 707 708 /** 709 * Bit mask of other states and props. 710 */ 711 uint32_t mDocFlags : 27; 712 713 /** 714 * Tracks whether we have seen changes to this document's content that 715 * indicate we should re-send the viewport cache we use for hittesting. 716 * This value is set in `BundleFieldsForCache` and processed in 717 * `ProcessQueuedCacheUpdates`. 718 */ 719 bool mViewportCacheDirty : 1; 720 721 /** 722 * Type of document load event fired after the document is loaded completely. 723 */ 724 uint32_t mLoadEventType; 725 726 /** 727 * Reference to anchor jump element. 728 */ 729 nsCOMPtr<nsIContent> mAnchorJumpElm; 730 731 /** 732 * A generic state (see items below) before the attribute value was changed. 733 * @see AttributeWillChange and AttributeChanged notifications. 734 */ 735 736 // Previous state bits before attribute change 737 uint64_t mPrevStateBits; 738 739 nsTArray<RefPtr<DocAccessible>> mChildDocuments; 740 741 /** 742 * A storage class for pairing content with one of its relation attributes. 743 */ 744 class AttrRelProvider { 745 public: 746 AttrRelProvider(nsAtom* aRelAttr, nsIContent* aContent) 747 : mRelAttr(aRelAttr), mContent(aContent) {} 748 749 nsAtom* mRelAttr; 750 nsCOMPtr<nsIContent> mContent; 751 752 private: 753 AttrRelProvider(); 754 AttrRelProvider(const AttrRelProvider&); 755 AttrRelProvider& operator=(const AttrRelProvider&); 756 }; 757 758 typedef nsTArray<mozilla::UniquePtr<AttrRelProvider>> AttrRelProviders; 759 typedef nsClassHashtable<nsStringHashKey, AttrRelProviders> 760 DependentIDsHashtable; 761 762 /** 763 * Returns/creates/removes attribute relation providers associated with 764 * a DOM document if the element is in uncomposed document or associated 765 * with shadow DOM the element is in. 766 */ 767 AttrRelProviders* GetRelProviders(dom::Element* aElement, 768 const nsAString& aID) const; 769 AttrRelProviders* GetOrCreateRelProviders(dom::Element* aElement, 770 const nsAString& aID); 771 void RemoveRelProvidersIfEmpty(dom::Element* aElement, const nsAString& aID); 772 773 /** 774 * A map used to look up the target node for an implicit reverse relation 775 * where the target of the explicit relation is specified as an id. 776 * For example: 777 * <div id="label">Name:</div><input aria-labelledby="label"> 778 * The div should get a LABEL_FOR relation targeting the input. To facilitate 779 * that, mDependentIDsHashes maps from "label" to an AttrRelProvider 780 * specifying aria-labelledby and the input. Because ids are scoped to the 781 * nearest ancestor document or shadow root, mDependentIDsHashes maps from the 782 * DocumentOrShadowRoot first. 783 */ 784 nsClassHashtable<nsPtrHashKey<dom::DocumentOrShadowRoot>, 785 DependentIDsHashtable> 786 mDependentIDsHashes; 787 788 /** 789 * A map used to look up the target element for an implicit reverse relation 790 * where the target of the explicit relation is also specified as an element. 791 * This is similar to mDependentIDsHashes, except that this is used when a 792 * DOM property is used to set the relation target element directly, rather 793 * than using an id. For example: 794 * <button>More info</button><div popover>Some info</div> 795 * The button's .popoverTargetElement property is set to the div so that the 796 * button invokes the popover. 797 * To facilitate finding the invoker given the popover, mDependentElementsMap 798 * maps from the div to an AttrRelProvider specifying popovertarget and the 799 * button. 800 */ 801 nsTHashMap<nsIContent*, AttrRelProviders> mDependentElementsMap; 802 803 friend class RelatedAccIterator; 804 805 /** 806 * Used for our caching algorithm. We store the list of nodes that should be 807 * invalidated. 808 * 809 * @see ProcessInvalidationList 810 */ 811 nsTArray<RefPtr<nsIContent>> mInvalidationList; 812 813 /** 814 * Holds a list of aria-owns relocations. 815 */ 816 nsClassHashtable<nsPtrHashKey<LocalAccessible>, 817 nsTArray<RefPtr<LocalAccessible>>> 818 mARIAOwnsHash; 819 820 /** 821 * Keeps a list of pending subtrees to update post-refresh. 822 */ 823 nsTArray<RefPtr<nsIContent>> mPendingUpdates; 824 825 /** 826 * Used to process notification from core and accessible events. 827 */ 828 RefPtr<NotificationController> mNotificationController; 829 friend class EventTree; 830 friend class NotificationController; 831 832 /* 833 * The accessibility service may need to process queued cache updates outside 834 * of the regular NotificationController flow. 835 */ 836 friend class ::nsAccessibilityService; 837 838 private: 839 void SetRoleMapEntryForDoc(dom::Element* aElement); 840 841 /** 842 * This must be called whenever an Accessible is moved in a content process. 843 * It keeps track of Accessibles moved during this tick. 844 */ 845 void TrackMovedAccessible(LocalAccessible* aAcc); 846 847 /** 848 * For hidden subtrees, fire a name/description change event if the subtree 849 * is a target of aria-labelledby/describedby. 850 * This does nothing if it is called on a node which is not part of a hidden 851 * aria-labelledby/describedby target. 852 */ 853 void MaybeHandleChangeToHiddenNameOrDescription(nsIContent* aChild); 854 855 void MaybeHandleChangeToAriaActions(LocalAccessible* aAcc, 856 const nsAtom* aAttribute); 857 858 void MaybeFireEventsForChangedPopover(LocalAccessible* aAcc); 859 860 PresShell* mPresShell; 861 862 // Exclusively owned by IPDL so don't manually delete it! 863 // Cleared in ActorDestroy 864 DocAccessibleChild* mIPCDoc; 865 866 // These data structures map between LocalAccessibles and CacheDomains, 867 // tracking cache updates that have been queued during the current tick but 868 // not yet sent. If there are a lot of nearby text cache updates (e.g. during 869 // a reflow), it is much more performant to process them in order because we 870 // then benefit from the layout line cursor. However, we still only want to 871 // process each LocalAccessible only once. Therefore, we use an array for 872 // ordering and a hash map to avoid duplicates, since Gecko has no ordered 873 // set data structure. The array contains pairs of LocalAccessible and cache 874 // domain. The hash map maps from LocalAccessible to the corresponding index 875 // in the array. These data structures must be kept in sync. It is possible 876 // for these to contain a reference to the document they live on. We clear 877 // them in Shutdown() to avoid cyclical references. 878 nsTArray<std::pair<RefPtr<LocalAccessible>, uint64_t>> 879 mQueuedCacheUpdatesArray; 880 nsTHashMap<LocalAccessible*, size_t> mQueuedCacheUpdatesHash; 881 882 // A set of Accessibles moved during this tick. Only used in content 883 // processes. 884 nsTHashSet<RefPtr<LocalAccessible>> mMovedAccessibles; 885 // A set of Accessibles inserted during this tick. Only used in content 886 // processes. This is needed to prevent insertions + moves of the same 887 // Accessible in the same tick from being tracked as moves. 888 nsTHashSet<RefPtr<LocalAccessible>> mInsertedAccessibles; 889 // A set of DOM nodes removed during this tick. This avoids a lot of pointless 890 // recursive DOM traversals. 891 nsTHashSet<nsIContent*> mRemovedNodes; 892 }; 893 894 inline DocAccessible* LocalAccessible::AsDoc() { 895 return IsDoc() ? static_cast<DocAccessible*>(this) : nullptr; 896 } 897 898 } // namespace a11y 899 } // namespace mozilla 900 901 #endif