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 #include "OuterDocAccessible.h"
7
8 #include "LocalAccessible-inl.h"
9 #include "nsAccUtils.h"
10 #include "DocAccessible-inl.h"
11 #include "mozilla/a11y/DocAccessibleChild.h"
12 #include "mozilla/a11y/DocAccessibleParent.h"
13 #if defined(XP_WIN)
14 # include "mozilla/a11y/ProxyWrappers.h"
15 #endif
16 #include "mozilla/dom/BrowserBridgeChild.h"
17 #include "mozilla/dom/BrowserParent.h"
18 #include "Role.h"
19 #include "States.h"
20
21 #ifdef A11Y_LOG
22 # include "Logging.h"
23 #endif
24
25 using namespace mozilla;
26 using namespace mozilla::a11y;
27
28 ////////////////////////////////////////////////////////////////////////////////
29 // OuterDocAccessible
30 ////////////////////////////////////////////////////////////////////////////////
31
OuterDocAccessible(nsIContent * aContent,DocAccessible * aDoc)32 OuterDocAccessible::OuterDocAccessible(nsIContent* aContent,
33 DocAccessible* aDoc)
34 : AccessibleWrap(aContent, aDoc) {
35 mType = eOuterDocType;
36
37 #ifdef XP_WIN
38 if (DocAccessibleParent* remoteDoc = RemoteChildDoc()) {
39 remoteDoc->SendParentCOMProxy(this);
40 }
41 #endif
42
43 if (IPCAccessibilityActive()) {
44 auto bridge = dom::BrowserBridgeChild::GetFrom(aContent);
45 if (bridge) {
46 // This is an iframe which will be rendered in another process.
47 SendEmbedderAccessible(bridge);
48 }
49 }
50
51 // Request document accessible for the content document to make sure it's
52 // created. It will appended to outerdoc accessible children asynchronously.
53 dom::Document* outerDoc = mContent->GetUncomposedDoc();
54 if (outerDoc) {
55 dom::Document* innerDoc = outerDoc->GetSubDocumentFor(mContent);
56 if (innerDoc) GetAccService()->GetDocAccessible(innerDoc);
57 }
58 }
59
~OuterDocAccessible()60 OuterDocAccessible::~OuterDocAccessible() {}
61
SendEmbedderAccessible(dom::BrowserBridgeChild * aBridge)62 void OuterDocAccessible::SendEmbedderAccessible(
63 dom::BrowserBridgeChild* aBridge) {
64 MOZ_ASSERT(mDoc);
65 DocAccessibleChild* ipcDoc = mDoc->IPCDoc();
66 if (ipcDoc) {
67 uint64_t id = reinterpret_cast<uintptr_t>(UniqueID());
68 #if defined(XP_WIN)
69 ipcDoc->SetEmbedderOnBridge(aBridge, id);
70 #else
71 aBridge->SendSetEmbedderAccessible(ipcDoc, id);
72 #endif
73 }
74 }
75
76 ////////////////////////////////////////////////////////////////////////////////
77 // LocalAccessible public (DON'T add methods here)
78
NativeRole() const79 role OuterDocAccessible::NativeRole() const { return roles::INTERNAL_FRAME; }
80
LocalChildAtPoint(int32_t aX,int32_t aY,EWhichChildAtPoint aWhichChild)81 LocalAccessible* OuterDocAccessible::LocalChildAtPoint(
82 int32_t aX, int32_t aY, EWhichChildAtPoint aWhichChild) {
83 nsIntRect docRect = Bounds();
84 if (!docRect.Contains(aX, aY)) return nullptr;
85
86 // Always return the inner doc as direct child accessible unless bounds
87 // outside of it.
88 LocalAccessible* child = LocalChildAt(0);
89 NS_ENSURE_TRUE(child, nullptr);
90
91 if (aWhichChild == Accessible::EWhichChildAtPoint::DeepestChild) {
92 #if defined(XP_WIN)
93 // On Windows, OuterDocAccessible::GetChildAt can return a proxy wrapper
94 // for a remote document. These aren't real Accessibles and
95 // shouldn't be returned except to the Windows a11y code (which doesn't use
96 // eDeepestChild). Calling ChildAtPoint on these will crash!
97 return nullptr;
98 #else
99 return child->LocalChildAtPoint(
100 aX, aY, Accessible::EWhichChildAtPoint::DeepestChild);
101 #endif // defined(XP_WIN)
102 }
103 return child;
104 }
105
106 ////////////////////////////////////////////////////////////////////////////////
107 // LocalAccessible public
108
Shutdown()109 void OuterDocAccessible::Shutdown() {
110 #ifdef A11Y_LOG
111 if (logging::IsEnabled(logging::eDocDestroy)) logging::OuterDocDestroy(this);
112 #endif
113
114 LocalAccessible* child = mChildren.SafeElementAt(0, nullptr);
115 if (child) {
116 #ifdef A11Y_LOG
117 if (logging::IsEnabled(logging::eDocDestroy)) {
118 logging::DocDestroy("outerdoc's child document rebind is scheduled",
119 child->AsDoc()->DocumentNode());
120 }
121 #endif
122 RemoveChild(child);
123
124 // XXX: sometimes outerdoc accessible is shutdown because of layout style
125 // change however the presshell of underlying document isn't destroyed and
126 // the document doesn't get pagehide events. Schedule a document rebind
127 // to its parent document. Otherwise a document accessible may be lost if
128 // its outerdoc has being recreated (see bug 862863 for details).
129 if (!mDoc->IsDefunct()) {
130 MOZ_ASSERT(!child->IsDefunct(),
131 "Attempt to reattach shutdown document accessible");
132 if (!child->IsDefunct()) {
133 mDoc->BindChildDocument(child->AsDoc());
134 }
135 }
136 }
137
138 AccessibleWrap::Shutdown();
139 }
140
InsertChildAt(uint32_t aIdx,LocalAccessible * aAccessible)141 bool OuterDocAccessible::InsertChildAt(uint32_t aIdx,
142 LocalAccessible* aAccessible) {
143 MOZ_RELEASE_ASSERT(aAccessible->IsDoc(),
144 "OuterDocAccessible can have a document child only!");
145
146 // We keep showing the old document for a bit after creating the new one,
147 // and while building the new DOM and frame tree. That's done on purpose
148 // to avoid weird flashes of default background color.
149 // The old viewer will be destroyed after the new one is created.
150 // For a11y, it should be safe to shut down the old document now.
151 if (mChildren.Length()) mChildren[0]->Shutdown();
152
153 if (!AccessibleWrap::InsertChildAt(0, aAccessible)) return false;
154
155 #ifdef A11Y_LOG
156 if (logging::IsEnabled(logging::eDocCreate)) {
157 logging::DocCreate("append document to outerdoc",
158 aAccessible->AsDoc()->DocumentNode());
159 logging::Address("outerdoc", this);
160 }
161 #endif
162
163 return true;
164 }
165
RemoveChild(LocalAccessible * aAccessible)166 bool OuterDocAccessible::RemoveChild(LocalAccessible* aAccessible) {
167 LocalAccessible* child = mChildren.SafeElementAt(0, nullptr);
168 MOZ_ASSERT(child == aAccessible, "Wrong child to remove!");
169 if (child != aAccessible) {
170 return false;
171 }
172
173 #ifdef A11Y_LOG
174 if (logging::IsEnabled(logging::eDocDestroy)) {
175 logging::DocDestroy("remove document from outerdoc",
176 child->AsDoc()->DocumentNode(), child->AsDoc());
177 logging::Address("outerdoc", this);
178 }
179 #endif
180
181 bool wasRemoved = AccessibleWrap::RemoveChild(child);
182
183 NS_ASSERTION(!mChildren.Length(),
184 "This child document of outerdoc accessible wasn't removed!");
185
186 return wasRemoved;
187 }
188
IsAcceptableChild(nsIContent * aEl) const189 bool OuterDocAccessible::IsAcceptableChild(nsIContent* aEl) const {
190 // outer document accessible doesn't not participate in ordinal tree
191 // mutations.
192 return false;
193 }
194
195 #if defined(XP_WIN)
196
RemoteChildDocAccessible() const197 LocalAccessible* OuterDocAccessible::RemoteChildDocAccessible() const {
198 RemoteAccessible* docProxy = RemoteChildDoc();
199 if (docProxy) {
200 // We're in the parent process, but we're embedding a remote document.
201 return WrapperFor(docProxy);
202 }
203
204 if (IPCAccessibilityActive()) {
205 auto bridge = dom::BrowserBridgeChild::GetFrom(mContent);
206 if (bridge) {
207 // We're an iframe in a content process and we're embedding a remote
208 // document (in another content process). The COM proxy for the embedded
209 // document accessible was sent to us from the parent via PBrowserBridge.
210 return bridge->GetEmbeddedDocAccessible();
211 }
212 }
213
214 return nullptr;
215 }
216
217 // On Windows e10s, since we don't cache in the chrome process, LocalChildAt
218 // must be implemented so that we properly cross the chrome-to-content
219 // boundary when traversing.
220
LocalChildAt(uint32_t aIndex) const221 LocalAccessible* OuterDocAccessible::LocalChildAt(uint32_t aIndex) const {
222 LocalAccessible* result = AccessibleWrap::LocalChildAt(aIndex);
223 if (result || aIndex) {
224 return result;
225 }
226 // If we are asking for child 0 and GetChildAt doesn't return anything, try
227 // to get the remote child doc and return that instead.
228 return RemoteChildDocAccessible();
229 }
230
231 #endif // defined(XP_WIN)
232
233 // Accessible
234
ChildCount() const235 uint32_t OuterDocAccessible::ChildCount() const {
236 uint32_t result = mChildren.Length();
237 if (!result &&
238 #if defined(XP_WIN)
239 ((StaticPrefs::accessibility_cache_enabled_AtStartup() &&
240 RemoteChildDoc()) ||
241 // On Windows with the cache disabled, as well as returning 1 for a
242 // remote document in the parent process, we also need to return 1 in a
243 // content process for an OOP iframe.
244 RemoteChildDocAccessible())
245 #else
246 RemoteChildDoc()
247 #endif
248 ) {
249 result = 1;
250 }
251 return result;
252 }
253
ChildAt(uint32_t aIndex) const254 Accessible* OuterDocAccessible::ChildAt(uint32_t aIndex) const {
255 // We deliberately bypass OuterDocAccessible::LocalChildAt on Windows because
256 // it will return a RemoteAccessibleWrap for a remote document.
257 LocalAccessible* result = AccessibleWrap::LocalChildAt(aIndex);
258 if (result || aIndex) {
259 return result;
260 }
261
262 return RemoteChildDoc();
263 }
264
ChildAtPoint(int32_t aX,int32_t aY,EWhichChildAtPoint aWhichChild)265 Accessible* OuterDocAccessible::ChildAtPoint(int32_t aX, int32_t aY,
266 EWhichChildAtPoint aWhichChild) {
267 nsIntRect docRect = Bounds();
268 if (!docRect.Contains(aX, aY)) return nullptr;
269
270 // Always return the inner doc as direct child accessible unless bounds
271 // outside of it.
272 Accessible* child = ChildAt(0);
273 NS_ENSURE_TRUE(child, nullptr);
274
275 if (aWhichChild == EWhichChildAtPoint::DeepestChild) {
276 return child->ChildAtPoint(aX, aY, EWhichChildAtPoint::DeepestChild);
277 }
278 return child;
279 }
280
RemoteChildDoc() const281 DocAccessibleParent* OuterDocAccessible::RemoteChildDoc() const {
282 dom::BrowserParent* tab = dom::BrowserParent::GetFrom(GetContent());
283 if (!tab) {
284 return nullptr;
285 }
286
287 return tab->GetTopLevelDocAccessible();
288 }
289