OuterDocAccessible.cpp (7437B)
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 "DocAccessible-inl.h" 10 #include "mozilla/a11y/DocAccessibleChild.h" 11 #include "mozilla/a11y/DocAccessibleParent.h" 12 #include "mozilla/dom/BrowserBridgeChild.h" 13 #include "mozilla/dom/BrowserParent.h" 14 #include "mozilla/a11y/Role.h" 15 16 #ifdef A11Y_LOG 17 # include "Logging.h" 18 #endif 19 20 using namespace mozilla; 21 using namespace mozilla::a11y; 22 23 //////////////////////////////////////////////////////////////////////////////// 24 // OuterDocAccessible 25 //////////////////////////////////////////////////////////////////////////////// 26 27 OuterDocAccessible::OuterDocAccessible(nsIContent* aContent, 28 DocAccessible* aDoc) 29 : AccessibleWrap(aContent, aDoc) { 30 mType = eOuterDocType; 31 32 if (IPCAccessibilityActive()) { 33 auto bridge = dom::BrowserBridgeChild::GetFrom(aContent); 34 if (bridge) { 35 // This is an iframe which will be rendered in another process. 36 SendEmbedderAccessible(bridge); 37 } 38 } 39 40 // Request document accessible for the content document to make sure it's 41 // created. It will appended to outerdoc accessible children asynchronously. 42 dom::Document* outerDoc = mContent->GetUncomposedDoc(); 43 if (outerDoc) { 44 dom::Document* innerDoc = outerDoc->GetSubDocumentFor(mContent); 45 if (innerDoc) GetAccService()->GetDocAccessible(innerDoc); 46 } 47 } 48 49 OuterDocAccessible::~OuterDocAccessible() {} 50 51 void OuterDocAccessible::SendEmbedderAccessible( 52 dom::BrowserBridgeChild* aBridge) { 53 MOZ_ASSERT(mDoc); 54 DocAccessibleChild* ipcDoc = mDoc->IPCDoc(); 55 if (ipcDoc) { 56 uint64_t id = reinterpret_cast<uintptr_t>(UniqueID()); 57 aBridge->SetEmbedderAccessible(ipcDoc, id); 58 } 59 } 60 61 //////////////////////////////////////////////////////////////////////////////// 62 // LocalAccessible public (DON'T add methods here) 63 64 role OuterDocAccessible::NativeRole() const { return roles::INTERNAL_FRAME; } 65 66 LocalAccessible* OuterDocAccessible::LocalChildAtPoint( 67 int32_t aX, int32_t aY, EWhichChildAtPoint aWhichChild) { 68 LayoutDeviceIntRect docRect = Bounds(); 69 if (!docRect.Contains(aX, aY)) return nullptr; 70 71 // Always return the inner doc as direct child accessible unless bounds 72 // outside of it. 73 LocalAccessible* child = LocalChildAt(0); 74 NS_ENSURE_TRUE(child, nullptr); 75 76 if (aWhichChild == Accessible::EWhichChildAtPoint::DeepestChild) { 77 return child->LocalChildAtPoint( 78 aX, aY, Accessible::EWhichChildAtPoint::DeepestChild); 79 } 80 return child; 81 } 82 83 //////////////////////////////////////////////////////////////////////////////// 84 // LocalAccessible public 85 86 void OuterDocAccessible::Shutdown() { 87 #ifdef A11Y_LOG 88 if (logging::IsEnabled(logging::eDocDestroy)) logging::OuterDocDestroy(this); 89 #endif 90 91 if (auto* bridge = dom::BrowserBridgeChild::GetFrom(mContent)) { 92 uint64_t id = reinterpret_cast<uintptr_t>(UniqueID()); 93 if (bridge->GetEmbedderAccessibleID() == id) { 94 // We were the last embedder accessible sent via PBrowserBridge; i.e. a 95 // new embedder accessible hasn't been created yet for this iframe. Clear 96 // the embedder accessible on PBrowserBridge. 97 bridge->SetEmbedderAccessible(nullptr, 0); 98 } 99 } 100 101 LocalAccessible* child = mChildren.SafeElementAt(0, nullptr); 102 if (child) { 103 #ifdef A11Y_LOG 104 if (logging::IsEnabled(logging::eDocDestroy)) { 105 logging::DocDestroy("outerdoc's child document rebind is scheduled", 106 child->AsDoc()->DocumentNode()); 107 } 108 #endif 109 RemoveChild(child); 110 111 // XXX: sometimes outerdoc accessible is shutdown because of layout style 112 // change however the presshell of underlying document isn't destroyed and 113 // the document doesn't get pagehide events. Schedule a document rebind 114 // to its parent document. Otherwise a document accessible may be lost if 115 // its outerdoc has being recreated (see bug 862863 for details). 116 if (!mDoc->IsDefunct()) { 117 MOZ_ASSERT(!child->IsDefunct(), 118 "Attempt to reattach shutdown document accessible"); 119 if (!child->IsDefunct()) { 120 mDoc->BindChildDocument(child->AsDoc()); 121 } 122 } 123 } 124 125 AccessibleWrap::Shutdown(); 126 } 127 128 bool OuterDocAccessible::InsertChildAt(uint32_t aIdx, 129 LocalAccessible* aAccessible) { 130 MOZ_RELEASE_ASSERT(aAccessible->IsDoc(), 131 "OuterDocAccessible can have a document child only!"); 132 133 // We keep showing the old document for a bit after creating the new one, 134 // and while building the new DOM and frame tree. That's done on purpose 135 // to avoid weird flashes of default background color. 136 // The old viewer will be destroyed after the new one is created. 137 // For a11y, it should be safe to shut down the old document now. 138 if (mChildren.Length()) mChildren[0]->Shutdown(); 139 140 if (!AccessibleWrap::InsertChildAt(0, aAccessible)) return false; 141 142 #ifdef A11Y_LOG 143 if (logging::IsEnabled(logging::eDocCreate)) { 144 logging::DocCreate("append document to outerdoc", 145 aAccessible->AsDoc()->DocumentNode()); 146 logging::Address("outerdoc", this); 147 } 148 #endif 149 150 return true; 151 } 152 153 bool OuterDocAccessible::RemoveChild(LocalAccessible* aAccessible) { 154 LocalAccessible* child = mChildren.SafeElementAt(0, nullptr); 155 MOZ_ASSERT(child == aAccessible, "Wrong child to remove!"); 156 if (child != aAccessible) { 157 return false; 158 } 159 160 #ifdef A11Y_LOG 161 if (logging::IsEnabled(logging::eDocDestroy)) { 162 logging::DocDestroy("remove document from outerdoc", 163 child->AsDoc()->DocumentNode(), child->AsDoc()); 164 logging::Address("outerdoc", this); 165 } 166 #endif 167 168 bool wasRemoved = AccessibleWrap::RemoveChild(child); 169 170 NS_ASSERTION(!mChildren.Length(), 171 "This child document of outerdoc accessible wasn't removed!"); 172 173 return wasRemoved; 174 } 175 176 bool OuterDocAccessible::IsAcceptableChild(nsIContent* aEl) const { 177 // outer document accessible doesn't not participate in ordinal tree 178 // mutations. 179 return false; 180 } 181 182 // Accessible 183 184 uint32_t OuterDocAccessible::ChildCount() const { 185 uint32_t result = mChildren.Length(); 186 if (!result && RemoteChildDoc()) { 187 result = 1; 188 } 189 return result; 190 } 191 192 Accessible* OuterDocAccessible::ChildAt(uint32_t aIndex) const { 193 LocalAccessible* result = LocalChildAt(aIndex); 194 if (result || aIndex) { 195 return result; 196 } 197 198 return RemoteChildDoc(); 199 } 200 201 Accessible* OuterDocAccessible::ChildAtPoint(int32_t aX, int32_t aY, 202 EWhichChildAtPoint aWhichChild) { 203 LayoutDeviceIntRect docRect = Bounds(); 204 if (!docRect.Contains(aX, aY)) return nullptr; 205 206 // Always return the inner doc as direct child accessible unless bounds 207 // outside of it. 208 Accessible* child = ChildAt(0); 209 NS_ENSURE_TRUE(child, nullptr); 210 211 if (aWhichChild == EWhichChildAtPoint::DeepestChild) { 212 return child->ChildAtPoint(aX, aY, EWhichChildAtPoint::DeepestChild); 213 } 214 return child; 215 } 216 217 DocAccessibleParent* OuterDocAccessible::RemoteChildDoc() const { 218 dom::BrowserParent* tab = dom::BrowserParent::GetFrom(GetContent()); 219 if (!tab) { 220 return nullptr; 221 } 222 223 return tab->GetTopLevelDocAccessible(); 224 }