1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "DocAccessible.h"
8 #include "mozilla/a11y/DocAccessibleParent.h"
9 #include "mozilla/a11y/DocManager.h"
10 #include "mozilla/a11y/Platform.h"
11 #include "mozilla/a11y/ProxyAccessibleBase.h"
12 #include "mozilla/a11y/ProxyAccessible.h"
13 #include "mozilla/a11y/Role.h"
14 #include "mozilla/dom/Element.h"
15 #include "mozilla/dom/TabParent.h"
16 #include "mozilla/Unused.h"
17 #include "RelationType.h"
18 #include "xpcAccessibleDocument.h"
19 
20 namespace mozilla {
21 namespace a11y {
22 
23 template <class Derived>
Shutdown()24 void ProxyAccessibleBase<Derived>::Shutdown() {
25   MOZ_DIAGNOSTIC_ASSERT(!IsDoc());
26   xpcAccessibleDocument* xpcDoc =
27       GetAccService()->GetCachedXPCDocument(Document());
28   if (xpcDoc) {
29     xpcDoc->NotifyOfShutdown(static_cast<Derived*>(this));
30   }
31 
32   // XXX Ideally  this wouldn't be necessary, but it seems OuterDoc accessibles
33   // can be destroyed before the doc they own.
34   uint32_t childCount = mChildren.Length();
35   if (!mOuterDoc) {
36     for (uint32_t idx = 0; idx < childCount; idx++) mChildren[idx]->Shutdown();
37   } else {
38     if (childCount > 1) {
39       MOZ_CRASH("outer doc has too many documents!");
40     } else if (childCount == 1) {
41       mChildren[0]->AsDoc()->Unbind();
42     }
43   }
44 
45   mChildren.Clear();
46   ProxyDestroyed(static_cast<Derived*>(this));
47   mDoc->RemoveAccessible(static_cast<Derived*>(this));
48 }
49 
50 template <class Derived>
SetChildDoc(DocAccessibleParent * aChildDoc)51 void ProxyAccessibleBase<Derived>::SetChildDoc(DocAccessibleParent* aChildDoc) {
52   MOZ_ASSERT(aChildDoc);
53   MOZ_ASSERT(mChildren.Length() == 0);
54   mChildren.AppendElement(aChildDoc);
55   mOuterDoc = true;
56 }
57 
58 template <class Derived>
ClearChildDoc(DocAccessibleParent * aChildDoc)59 void ProxyAccessibleBase<Derived>::ClearChildDoc(
60     DocAccessibleParent* aChildDoc) {
61   MOZ_ASSERT(aChildDoc);
62   // This is possible if we're replacing one document with another: Doc 1
63   // has not had a chance to remove itself, but was already replaced by Doc 2
64   // in SetChildDoc(). This could result in two subsequent calls to
65   // ClearChildDoc() even though mChildren.Length() == 1.
66   MOZ_ASSERT(mChildren.Length() <= 1);
67   mChildren.RemoveElement(aChildDoc);
68 }
69 
70 template <class Derived>
MustPruneChildren() const71 bool ProxyAccessibleBase<Derived>::MustPruneChildren() const {
72   // this is the equivalent to nsAccUtils::MustPrune for proxies and should be
73   // kept in sync with that.
74   if (mRole != roles::MENUITEM && mRole != roles::COMBOBOX_OPTION &&
75       mRole != roles::OPTION && mRole != roles::ENTRY &&
76       mRole != roles::FLAT_EQUATION && mRole != roles::PASSWORD_TEXT &&
77       mRole != roles::PUSHBUTTON && mRole != roles::TOGGLE_BUTTON &&
78       mRole != roles::GRAPHIC && mRole != roles::SLIDER &&
79       mRole != roles::PROGRESSBAR && mRole != roles::SEPARATOR)
80     return false;
81 
82   if (mChildren.Length() != 1) return false;
83 
84   return mChildren[0]->Role() == roles::TEXT_LEAF ||
85          mChildren[0]->Role() == roles::STATICTEXT;
86 }
87 
88 template <class Derived>
EmbeddedChildCount() const89 uint32_t ProxyAccessibleBase<Derived>::EmbeddedChildCount() const {
90   size_t count = 0, kids = mChildren.Length();
91   for (size_t i = 0; i < kids; i++) {
92     if (mChildren[i]->IsEmbeddedObject()) {
93       count++;
94     }
95   }
96 
97   return count;
98 }
99 
100 template <class Derived>
IndexOfEmbeddedChild(const Derived * aChild)101 int32_t ProxyAccessibleBase<Derived>::IndexOfEmbeddedChild(
102     const Derived* aChild) {
103   size_t index = 0, kids = mChildren.Length();
104   for (size_t i = 0; i < kids; i++) {
105     if (mChildren[i]->IsEmbeddedObject()) {
106       if (mChildren[i] == aChild) {
107         return index;
108       }
109 
110       index++;
111     }
112   }
113 
114   return -1;
115 }
116 
117 template <class Derived>
EmbeddedChildAt(size_t aChildIdx)118 Derived* ProxyAccessibleBase<Derived>::EmbeddedChildAt(size_t aChildIdx) {
119   size_t index = 0, kids = mChildren.Length();
120   for (size_t i = 0; i < kids; i++) {
121     if (!mChildren[i]->IsEmbeddedObject()) {
122       continue;
123     }
124 
125     if (index == aChildIdx) {
126       return mChildren[i];
127     }
128 
129     index++;
130   }
131 
132   return nullptr;
133 }
134 
135 template <class Derived>
OuterDocOfRemoteBrowser() const136 Accessible* ProxyAccessibleBase<Derived>::OuterDocOfRemoteBrowser() const {
137   auto tab = static_cast<dom::TabParent*>(mDoc->Manager());
138   dom::Element* frame = tab->GetOwnerElement();
139   NS_ASSERTION(frame, "why isn't the tab in a frame!");
140   if (!frame) return nullptr;
141 
142   DocAccessible* chromeDoc = GetExistingDocAccessible(frame->OwnerDoc());
143 
144   return chromeDoc ? chromeDoc->GetAccessible(frame) : nullptr;
145 }
146 
147 template <class Derived>
SetParent(Derived * aParent)148 void ProxyAccessibleBase<Derived>::SetParent(Derived* aParent) {
149   MOZ_ASSERT(IsDoc(), "we should only reparent documents");
150   if (!aParent) {
151     mParent = kNoParent;
152   } else {
153     MOZ_ASSERT(!aParent->IsDoc());
154     mParent = aParent->ID();
155   }
156 }
157 
158 template <class Derived>
Parent() const159 Derived* ProxyAccessibleBase<Derived>::Parent() const {
160   if (mParent == kNoParent) {
161     return nullptr;
162   }
163 
164   // if we are not a document then are parent is another proxy in the same
165   // document.  That means we can just ask our document for the proxy with our
166   // parent id.
167   if (!IsDoc()) {
168     return Document()->GetAccessible(mParent);
169   }
170 
171   // If we are a top level document then our parent is not a proxy.
172   if (AsDoc()->IsTopLevel()) {
173     return nullptr;
174   }
175 
176   // Finally if we are a non top level document then our parent id is for a
177   // proxy in our parent document so get the proxy from there.
178   DocAccessibleParent* parentDoc = AsDoc()->ParentDoc();
179   MOZ_ASSERT(parentDoc);
180   MOZ_ASSERT(mParent);
181   return parentDoc->GetAccessible(mParent);
182 }
183 
184 template class ProxyAccessibleBase<ProxyAccessible>;
185 
186 }  // namespace a11y
187 }  // namespace mozilla
188