1/*
2 * Copyright (C) 2019 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27
28#if ENABLE(ACCESSIBILITY_ISOLATED_TREE)
29#include "AXIsolatedTree.h"
30
31#include "AXIsolatedTreeNode.h"
32#include "Page.h"
33#include <wtf/NeverDestroyed.h>
34
35namespace WebCore {
36
37static Lock s_cacheLock;
38
39static unsigned newTreeID()
40{
41 static unsigned s_currentTreeID = 0;
42 return ++s_currentTreeID;
43}
44
45HashMap<uint64_t, Ref<AXIsolatedTree>>& AXIsolatedTree::treePageCache()
46{
47 static NeverDestroyed<HashMap<uint64_t, Ref<AXIsolatedTree>>> map;
48 return map;
49}
50
51HashMap<AXIsolatedTreeID, Ref<AXIsolatedTree>>& AXIsolatedTree::treeIDCache()
52{
53 static NeverDestroyed<HashMap<AXIsolatedTreeID, Ref<AXIsolatedTree>>> map;
54 return map;
55}
56
57AXIsolatedTree::AXIsolatedTree()
58 : m_treeID(newTreeID())
59{
60}
61
62AXIsolatedTree::~AXIsolatedTree() = default;
63
64Ref<AXIsolatedTree> AXIsolatedTree::create()
65{
66 ASSERT(isMainThread());
67 return adoptRef(*new AXIsolatedTree());
68}
69
70Ref<AXIsolatedTree> AXIsolatedTree::initializePageTreeForID(uint64_t pageID, AXObjectCache& cache)
71{
72 RELEASE_ASSERT(isMainThread());
73 auto tree = cache->generateIsolatedAccessibilityTree();
74 tree->setInitialRequestInProgress(true);
75 tree->applyPendingChanges();
76 tree->setInitialRequestInProgress(false);
77 return tree;
78}
79
80RefPtr<AXIsolatedTreeNode> AXIsolatedTree::nodeInTreeForID(AXIsolatedTreeID treeID, AXID axID)
81{
82 return treeForID(treeID)->nodeForID(axID);
83}
84
85RefPtr<AXIsolatedTree> AXIsolatedTree::treeForID(AXIsolatedTreeID treeID)
86{
87 return treeIDCache().get(treeID);
88}
89
90Ref<AXIsolatedTree> AXIsolatedTree::createTreeForPageID(uint64_t pageID)
91{
92 LockHolder locker(s_cacheLock);
93
94 auto newTree = AXIsolatedTree::create();
95 treePageCache().set(pageID, newTree.copyRef());
96 treeIDCache().set(newTree->treeIdentifier(), newTree.copyRef());
97 return newTree;
98}
99
100RefPtr<AXIsolatedTree> AXIsolatedTree::treeForPageID(uint64_t pageID)
101{
102 LockHolder locker(s_cacheLock);
103
104 if (auto tree = treePageCache().get(pageID))
105 return makeRefPtr(tree);
106
107 return nullptr;
108}
109
110RefPtr<AXIsolatedTreeNode> AXIsolatedTree::nodeForID(AXID axID) const
111{
112 RELEASE_ASSERT(!isMainThread() || initialRequest);
113 if (!axID)
114 return nullptr;
115 return m_readerThreadNodeMap.get(axID);
116}
117
118RefPtr<AXIsolatedTreeNode> AXIsolatedTree::focusedUIElement()
119{
120 return nodeForID(m_focusedNodeID);
121}
122
123RefPtr<AXIsolatedTreeNode> AXIsolatedTree::rootNode()
124{
125 return nodeForID(m_rootNodeID);
126}
127
128void AXIsolatedTree::setRootNodeID(AXID axID)
129{
130 LockHolder locker { m_changeLogLock };
131 m_pendingRootNodeID = axID;
132}
133
134void AXIsolatedTree::setFocusedNodeID(AXID axID)
135{
136 LockHolder locker { m_changeLogLock };
137 m_pendingFocusedNodeID = axID;
138}
139
140void AXIsolatedTree::removeNode(AXID axID)
141{
142 LockHolder locker { m_changeLogLock };
143 m_pendingRemovals.append(axID);
144}
145
146void AXIsolatedTree::appendNodeChanges(Vector<Ref<AXIsolatedTreeNode>>& log)
147{
148 LockHolder locker { m_changeLogLock };
149 for (auto& node : log)
150 m_pendingAppends.append(node.copyRef());
151}
152
153void AXIsolatedTree::setInitialRequestInProgress(bool initialRequestInProgress)
154{
155 m_initialRequestInProgress = initialRequestInProgress;
156}
157
158void AXIsolatedTree::applyPendingChanges()
159{
160 RELEASE_ASSERT(!isMainThread() || initialRequest);
161 LockHolder locker { m_changeLogLock };
162 Vector<Ref<AXIsolatedTreeNode>> appendCopy;
163 std::swap(appendCopy, m_pendingAppends);
164 Vector<AXID> removeCopy({ WTFMove(m_pendingRemovals) });
165 locker.unlockEarly();
166
167 // We don't clear the pending IDs beacause if the next round of updates does not modify them, then they stay the same
168 // value without extra bookkeeping.
169 m_rootNodeID = m_pendingRootNodeID;
170 m_focusedNodeID = m_pendingFocusedNodeID;
171
172 for (auto& item : appendCopy) {
173 item->setTreeIdentifier(m_treeID);
174 m_readerThreadNodeMap.add(item->identifier(), WTFMove(item));
175 }
176
177 for (auto item : removeCopy)
178 m_readerThreadNodeMap.remove(item);
179}
180
181} // namespace WebCore
182
183#endif // ENABLE(ACCESSIBILITY_ISOLATED_TREE)
184