blob: 0d9bb159121862a2480da9a88e1c856c0740c4eb [file] [log] [blame]
// Copyright 2017 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.
#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_ACCESSIBILITY_AX_RELATION_CACHE_H_
#define THIRD_PARTY_BLINK_RENDERER_MODULES_ACCESSIBILITY_AX_RELATION_CACHE_H_
#include "base/macros.h"
#include "third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h"
#include "third_party/blink/renderer/modules/modules_export.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
#include "third_party/blink/renderer/platform/wtf/hash_map.h"
#include "third_party/blink/renderer/platform/wtf/hash_set.h"
namespace blink {
// This class should only be used from inside the accessibility directory.
class AXRelationCache {
USING_FAST_MALLOC(AXRelationCache);
public:
explicit AXRelationCache(AXObjectCacheImpl*);
virtual ~AXRelationCache();
//
// Safe to call at any time. Doesn't make any changes to the tree.
//
// Returns true if the given object's position in the tree was due to
// aria-owns.
bool IsAriaOwned(AXID) const;
bool IsAriaOwned(const AXObject*) const;
// Returns the parent of the given object due to aria-owns.
AXObject* GetAriaOwnedParent(const AXObject*) const;
// Returns the validated owned children of this element with aria-owns.
void GetAriaOwnedChildren(const AXObject* owner,
HeapVector<Member<AXObject>>& owned_children);
// Return true if any label ever pointed to the element via the for attribute.
bool MayHaveHTMLLabelViaForAttribute(const HTMLElement&);
// Given an element in the DOM tree that was either just added or whose id
// just changed, check to see if another object wants to be its parent due to
// aria-owns. If so, add it to a queue of ids to process later during
// ProcessUpdatesWithCleanLayout.
// |node| is not optional.
// |obj| is optional. If provided, it must match the AXObject for |node|.
void UpdateRelatedTree(Node* node, AXObject* obj);
// Remove given AXID from cache.
void RemoveAXID(AXID);
// Update map of ids to related objects.
// If one or more ids aren't found, they're added to a lookup table so that if
// an element with that id appears later, it can be added when you call
// UpdateRelatedTree.
void UpdateReverseRelations(const AXObject* relation_source,
const Vector<String>& target_ids);
// Called when the "for" attribute of a label element changes and the
// reverse mapping needs to be updated.
void LabelChanged(Node*);
//
// Only called when layout is clean, in the kInAccessibility lifecycle
// stage.
//
// Called once towards the end of the kInAccessibility lifecycle stage.
// Iterates over a list of ququed nodes that may require changes to their
// set of owned children and calls UpdateAriaOwnsWithCleanLayout on each
// of them.
void ProcessUpdatesWithCleanLayout();
// Determines the set of child nodes that this object owns due to aria-owns
// (fully validating that the ownership is legal and free of cycles).
// If that differs from the previously cached set of owned children,
// calls ChildrenChanged on all affected nodes (old and new parents).
// This affects the tree, which is why it should only be called at a
// specific time in the lifecycle.
void UpdateAriaOwnsWithCleanLayout(AXObject* owner);
static bool IsValidOwner(AXObject* owner);
static bool IsValidOwnedChild(AXObject* child);
private:
// Given an object that has explicitly set elements for aria-owns, update the
// internal state to reflect the new set of children owned by this object.
// Note that |owned_children| will be the AXObjects corresponding to the
// elements in |attr_associated_elements|. These elements are validated -
// exist in the DOM, and are a descendant of a shadow including ancestor.
void UpdateAriaOwnsFromAttrAssociatedElementsWithCleanLayout(
AXObject* owner,
const HeapVector<Member<Element>>& attr_associated_elements,
HeapVector<Member<AXObject>>& owned_children);
// If any object is related to this object via <label for>, aria-owns,
// aria-describedby or aria-labeledby, update the text for the related object.
void UpdateRelatedText(Node*);
bool IsValidOwnsRelation(AXObject* owner, AXObject* child) const;
void UnmapOwnedChildren(const AXObject* owner, Vector<AXID>);
void MapOwnedChildren(const AXObject* owner, Vector<AXID>);
void GetReverseRelated(Node*, HeapVector<Member<AXObject>>& sources);
// Updates |aria_owner_to_children_mapping_| after calling UpdateAriaOwns for
// either the content attribute or the attr associated elements.
void UpdateAriaOwnerToChildrenMappingWithCleanLayout(
AXObject* owner,
HeapVector<Member<AXObject>>& validated_owned_children_result);
// Whether the document has been scanned for initial relationships
// first or not.
bool initialized_ = false;
WeakPersistent<AXObjectCacheImpl> object_cache_;
// Map from the AXID of the owner to the AXIDs of the children.
// This is a validated map, it doesn't contain illegal, duplicate,
// or cyclical matches, or references to IDs that don't exist.
HashMap<AXID, Vector<AXID>> aria_owner_to_children_mapping_;
// Map from the AXID of a child to the AXID of the parent that owns it.
HashMap<AXID, AXID> aria_owned_child_to_owner_mapping_;
// Reverse relation map from an ID (the ID attribute of a DOM element) to the
// set of elements that at some time pointed to that ID via aria-owns,
// aria-labelledby, aria-desribedby. This is *unvalidated*, it includes
// possible extras and duplicates.
// This is used so that:
// - When an element with an ID is added to the tree or changes its ID, we can
// quickly determine if it affects an aria-owns relationship.
// - When text changes, we can recompute any label or description based on it
// and fire the appropriate change events.
HashMap<String, HashSet<AXID>> id_attr_to_related_mapping_;
// HTML id attributes that at one time havehad a <label for> pointing to it.
// IDs are not necessarily removed from this set. It is not necessary to
// remove IDs as false positives are ok. Being able to determine that a
// labelable element has never had an associated label allows the accessible
// name calculation to be optimized.
HashSet<AtomicString> all_previously_seen_label_target_ids_;
// A set of IDs that need to be updated during the kInAccessibility
// lifecycle phase. For each of these, the new set of owned children
// will be computed, and if it's different than before, ChildrenChanged
// will be fired on all affected nodes.
HashSet<AXID> owner_ids_to_update_;
// Helpers that call back into object cache
AXObject* ObjectFromAXID(AXID) const;
AXObject* GetOrCreate(Node*, const AXObject* owner);
AXObject* Get(Node*);
void ChildrenChanged(AXObject*);
// Do an initial scan of the document to find any relationships.
// We'll catch any subsequent ones when attributes change.
void DoInitialDocumentScan();
DISALLOW_COPY_AND_ASSIGN(AXRelationCache);
};
} // namespace blink
#endif