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 "MsaaDocAccessible.h"
8
9 #include "DocAccessibleChild.h"
10 #include "mozilla/StaticPrefs_accessibility.h"
11 #include "nsWinUtils.h"
12 #include "Role.h"
13
14 using namespace mozilla;
15 using namespace mozilla::a11y;
16
DocAcc()17 DocAccessible* MsaaDocAccessible::DocAcc() {
18 return static_cast<DocAccessible*>(LocalAcc());
19 }
20
21 /* static */
GetFrom(DocAccessible * aDoc)22 MsaaDocAccessible* MsaaDocAccessible::GetFrom(DocAccessible* aDoc) {
23 return static_cast<MsaaDocAccessible*>(MsaaAccessible::GetFrom(aDoc));
24 }
25
26 /* static */
GetFrom(DocAccessibleParent * aDoc)27 MsaaDocAccessible* MsaaDocAccessible::GetFrom(DocAccessibleParent* aDoc) {
28 MOZ_ASSERT(StaticPrefs::accessibility_cache_enabled_AtStartup());
29 return static_cast<MsaaDocAccessible*>(
30 reinterpret_cast<MsaaAccessible*>(aDoc->GetWrapper()));
31 }
32
33 /* static */
GetFromOwned(Accessible * aAcc)34 MsaaDocAccessible* MsaaDocAccessible::GetFromOwned(Accessible* aAcc) {
35 if (RemoteAccessible* remoteAcc = aAcc->AsRemote()) {
36 DocAccessibleParent* doc = remoteAcc->Document();
37 if (!doc) {
38 return nullptr;
39 }
40 return MsaaDocAccessible::GetFrom(doc);
41 }
42 DocAccessible* doc = aAcc->AsLocal()->Document();
43 if (!doc) {
44 return nullptr;
45 }
46 return MsaaDocAccessible::GetFrom(doc);
47 }
48
49 // IUnknown
50 IMPL_IUNKNOWN_QUERY_HEAD(MsaaDocAccessible)
51 if (aIID == IID_ISimpleDOMDocument && LocalAcc()) {
52 statistics::ISimpleDOMUsed();
53 *aInstancePtr = static_cast<ISimpleDOMDocument*>(new sdnDocAccessible(this));
54 static_cast<IUnknown*>(*aInstancePtr)->AddRef();
55 return S_OK;
56 }
IMPL_IUNKNOWN_QUERY_TAIL_INHERITED(ia2AccessibleHypertext)57 IMPL_IUNKNOWN_QUERY_TAIL_INHERITED(ia2AccessibleHypertext)
58
59 STDMETHODIMP
60 MsaaDocAccessible::get_accParent(
61 /* [retval][out] */ IDispatch __RPC_FAR* __RPC_FAR* ppdispParent) {
62 if (!mAcc) {
63 return CO_E_OBJNOTCONNECTED;
64 }
65
66 if (mAcc->IsRemote()) {
67 MOZ_ASSERT(StaticPrefs::accessibility_cache_enabled_AtStartup());
68 DocAccessibleParent* remoteDoc = mAcc->AsRemote()->AsDoc();
69 if (nsWinUtils::IsWindowEmulationStarted() && remoteDoc->IsTopLevel()) {
70 // Window emulation is enabled and this is a top level document. Return
71 // window system accessible object.
72 HWND hwnd = remoteDoc->GetEmulatedWindowHandle();
73 MOZ_ASSERT(hwnd);
74 if (hwnd &&
75 SUCCEEDED(::AccessibleObjectFromWindow(
76 hwnd, OBJID_WINDOW, IID_IAccessible, (void**)ppdispParent))) {
77 return S_OK;
78 }
79 }
80 return MsaaAccessible::get_accParent(ppdispParent);
81 }
82
83 DocAccessible* docAcc = DocAcc();
84 MOZ_ASSERT(docAcc);
85
86 // We might be a top-level document in a content process.
87 DocAccessibleChild* ipcDoc = docAcc->IPCDoc();
88 if (ipcDoc && static_cast<dom::BrowserChild*>(ipcDoc->Manager())
89 ->GetTopLevelDocAccessibleChild() == ipcDoc) {
90 MOZ_ASSERT(!StaticPrefs::accessibility_cache_enabled_AtStartup());
91 // Emulated window proxy is only set for the top level content document when
92 // emulation is enabled.
93 RefPtr<IDispatch> dispParent = ipcDoc->GetEmulatedWindowIAccessible();
94 if (!dispParent) {
95 dispParent = ipcDoc->GetParentIAccessible();
96 }
97
98 if (!dispParent) {
99 return S_FALSE;
100 }
101
102 dispParent.forget(ppdispParent);
103 return S_OK;
104 }
105
106 // In the parent process, return window system accessible object for root
107 // document accessibles, as well as tab document accessibles if window
108 // emulation is enabled.
109 if (XRE_IsParentProcess() &&
110 (!docAcc->ParentDocument() ||
111 (nsWinUtils::IsWindowEmulationStarted() &&
112 nsCoreUtils::IsTopLevelContentDocInProcess(docAcc->DocumentNode())))) {
113 HWND hwnd = static_cast<HWND>(docAcc->GetNativeWindow());
114 if (hwnd && !docAcc->ParentDocument()) {
115 nsIFrame* frame = docAcc->GetFrame();
116 if (frame) {
117 nsIWidget* widget = frame->GetNearestWidget();
118 if (widget->WindowType() == eWindowType_child && !widget->GetParent()) {
119 // Bug 1427304: Windows opened with popup=yes (such as the WebRTC
120 // sharing indicator) get two HWNDs. The root widget is associated
121 // with the inner HWND, but the outer HWND still answers to
122 // WM_GETOBJECT queries. This means that getting the parent of the
123 // oleacc window accessible for the inner HWND returns this
124 // root accessible. Thus, to avoid a loop, we must never return the
125 // oleacc window accessible for the inner HWND. Instead, we use the
126 // outer HWND here.
127 HWND parentHwnd = ::GetParent(hwnd);
128 if (parentHwnd) {
129 MOZ_ASSERT(::GetWindowLongW(parentHwnd, GWL_STYLE) & WS_POPUP,
130 "Parent HWND should be a popup!");
131 hwnd = parentHwnd;
132 }
133 }
134 }
135 }
136 if (hwnd &&
137 SUCCEEDED(::AccessibleObjectFromWindow(
138 hwnd, OBJID_WINDOW, IID_IAccessible, (void**)ppdispParent))) {
139 return S_OK;
140 }
141 }
142
143 return MsaaAccessible::get_accParent(ppdispParent);
144 }
145
146 STDMETHODIMP
get_accValue(VARIANT aVarChild,BSTR __RPC_FAR * aValue)147 MsaaDocAccessible::get_accValue(VARIANT aVarChild, BSTR __RPC_FAR* aValue) {
148 if (!aValue) return E_INVALIDARG;
149 *aValue = nullptr;
150
151 // For backwards-compat, we still support old MSAA hack to provide URL in
152 // accValue Check for real value first
153 HRESULT hr = MsaaAccessible::get_accValue(aVarChild, aValue);
154 if (FAILED(hr) || *aValue || aVarChild.lVal != CHILDID_SELF) return hr;
155
156 DocAccessible* docAcc = DocAcc();
157 // MsaaAccessible::get_accValue should have failed (and thus we should have
158 // returned early) if the Accessible is dead.
159 MOZ_ASSERT(docAcc);
160 // If document is being used to create a widget, don't use the URL hack
161 roles::Role role = docAcc->Role();
162 if (role != roles::DOCUMENT && role != roles::APPLICATION &&
163 role != roles::DIALOG && role != roles::ALERT &&
164 role != roles::NON_NATIVE_DOCUMENT)
165 return hr;
166
167 nsAutoString url;
168 docAcc->URL(url);
169 if (url.IsEmpty()) return S_FALSE;
170
171 *aValue = ::SysAllocStringLen(url.get(), url.Length());
172 return *aValue ? S_OK : E_OUTOFMEMORY;
173 }
174