Bug 1769547 - Do not MOZ_CRASH() on missing process r=nika
[gecko.git] / accessible / windows / msaa / AccessibleWrap.cpp
blobe299f04b33d4604e72792a870c815099a7ee6208
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/. */
7 #include "AccessibleWrap.h"
9 #include "mozilla/a11y/DocAccessibleParent.h"
10 #include "AccEvent.h"
11 #include "GeckoCustom.h"
12 #include "nsAccUtils.h"
13 #include "nsIAccessibleEvent.h"
14 #include "nsIWidget.h"
15 #include "nsWindowsHelpers.h"
16 #include "mozilla/a11y/HyperTextAccessible.h"
17 #include "mozilla/a11y/RemoteAccessible.h"
18 #include "ServiceProvider.h"
19 #include "sdnAccessible.h"
21 #include "mozilla/mscom/AsyncInvoker.h"
22 #include "mozilla/StaticPrefs_accessibility.h"
24 using namespace mozilla;
25 using namespace mozilla::a11y;
27 /* For documentation of the accessibility architecture,
28 * see http://lxr.mozilla.org/seamonkey/source/accessible/accessible-docs.html
31 StaticAutoPtr<nsTArray<AccessibleWrap::HandlerControllerData>>
32 AccessibleWrap::sHandlerControllers;
34 ////////////////////////////////////////////////////////////////////////////////
35 // AccessibleWrap
36 ////////////////////////////////////////////////////////////////////////////////
37 AccessibleWrap::AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc)
38 : LocalAccessible(aContent, aDoc) {}
40 NS_IMPL_ISUPPORTS_INHERITED0(AccessibleWrap, LocalAccessible)
42 void AccessibleWrap::Shutdown() {
43 if (mMsaa) {
44 mMsaa->MsaaShutdown();
45 // Don't release mMsaa here because this will cause its id to be released
46 // immediately, which will result in immediate reuse, causing problems
47 // for clients. Instead, we release it in the destructor.
49 LocalAccessible::Shutdown();
52 //-----------------------------------------------------
53 // IUnknown interface methods - see iunknown.h for documentation
54 //-----------------------------------------------------
56 MsaaAccessible* AccessibleWrap::GetMsaa() {
57 if (!mMsaa) {
58 mMsaa = MsaaAccessible::Create(this);
60 return mMsaa;
63 void AccessibleWrap::GetNativeInterface(void** aOutAccessible) {
64 RefPtr<IAccessible> result = GetMsaa();
65 return result.forget(aOutAccessible);
68 ////////////////////////////////////////////////////////////////////////////////
69 // LocalAccessible
71 nsresult AccessibleWrap::HandleAccEvent(AccEvent* aEvent) {
72 nsresult rv = LocalAccessible::HandleAccEvent(aEvent);
73 NS_ENSURE_SUCCESS(rv, rv);
75 if (IPCAccessibilityActive()) {
76 return NS_OK;
79 uint32_t eventType = aEvent->GetEventType();
81 // Means we're not active.
82 NS_ENSURE_TRUE(!IsDefunct(), NS_ERROR_FAILURE);
84 LocalAccessible* accessible = aEvent->GetAccessible();
85 if (!accessible) return NS_OK;
87 if (eventType == nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED ||
88 eventType == nsIAccessibleEvent::EVENT_FOCUS) {
89 UpdateSystemCaretFor(accessible);
92 MsaaAccessible::FireWinEvent(accessible, eventType);
94 return NS_OK;
97 ////////////////////////////////////////////////////////////////////////////////
98 // AccessibleWrap
100 //------- Helper methods ---------
102 bool AccessibleWrap::IsRootForHWND() {
103 if (IsRoot()) {
104 return true;
106 HWND thisHwnd = MsaaAccessible::GetHWNDFor(this);
107 AccessibleWrap* parent = static_cast<AccessibleWrap*>(LocalParent());
108 MOZ_ASSERT(parent);
109 HWND parentHwnd = MsaaAccessible::GetHWNDFor(parent);
110 return thisHwnd != parentHwnd;
113 void AccessibleWrap::UpdateSystemCaretFor(LocalAccessible* aAccessible) {
114 // Move the system caret so that Windows Tablet Edition and tradional ATs with
115 // off-screen model can follow the caret
116 ::DestroyCaret();
118 HyperTextAccessible* text = aAccessible->AsHyperText();
119 if (!text) return;
121 nsIWidget* widget = nullptr;
122 LayoutDeviceIntRect caretRect = text->GetCaretRect(&widget);
124 if (!widget) {
125 return;
128 HWND caretWnd =
129 reinterpret_cast<HWND>(widget->GetNativeData(NS_NATIVE_WINDOW));
130 UpdateSystemCaretFor(caretWnd, caretRect);
133 /* static */
134 void AccessibleWrap::UpdateSystemCaretFor(
135 RemoteAccessible* aProxy, const LayoutDeviceIntRect& aCaretRect) {
136 ::DestroyCaret();
138 // The HWND should be the real widget HWND, not an emulated HWND.
139 // We get the HWND from the proxy's outer doc to bypass window emulation.
140 LocalAccessible* outerDoc = aProxy->OuterDocOfRemoteBrowser();
141 UpdateSystemCaretFor(MsaaAccessible::GetHWNDFor(outerDoc), aCaretRect);
144 /* static */
145 void AccessibleWrap::UpdateSystemCaretFor(
146 HWND aCaretWnd, const LayoutDeviceIntRect& aCaretRect) {
147 if (!aCaretWnd || aCaretRect.IsEmpty()) {
148 return;
151 // Create invisible bitmap for caret, otherwise its appearance interferes
152 // with Gecko caret
153 nsAutoBitmap caretBitMap(CreateBitmap(1, aCaretRect.Height(), 1, 1, nullptr));
154 if (::CreateCaret(aCaretWnd, caretBitMap, 1,
155 aCaretRect.Height())) { // Also destroys the last caret
156 ::ShowCaret(aCaretWnd);
157 RECT windowRect;
158 ::GetWindowRect(aCaretWnd, &windowRect);
159 ::SetCaretPos(aCaretRect.X() - windowRect.left,
160 aCaretRect.Y() - windowRect.top);
164 /* static */
165 void AccessibleWrap::SetHandlerControl(DWORD aPid,
166 RefPtr<IHandlerControl> aCtrl) {
167 MOZ_ASSERT(XRE_IsParentProcess() && NS_IsMainThread());
169 if (!sHandlerControllers) {
170 sHandlerControllers = new nsTArray<HandlerControllerData>();
171 ClearOnShutdown(&sHandlerControllers);
174 HandlerControllerData ctrlData(aPid, std::move(aCtrl));
175 if (sHandlerControllers->Contains(ctrlData)) {
176 return;
179 sHandlerControllers->AppendElement(std::move(ctrlData));
182 /* static */
183 void AccessibleWrap::InvalidateHandlers() {
184 static const HRESULT kErrorServerDied =
185 HRESULT_FROM_WIN32(RPC_S_SERVER_UNAVAILABLE);
187 MOZ_ASSERT(XRE_IsParentProcess());
188 MOZ_ASSERT(NS_IsMainThread());
190 if (!sHandlerControllers || sHandlerControllers->IsEmpty()) {
191 return;
194 // We iterate in reverse so that we may safely remove defunct elements while
195 // executing the loop.
196 for (auto& controller : Reversed(*sHandlerControllers)) {
197 MOZ_ASSERT(controller.mPid);
198 MOZ_ASSERT(controller.mCtrl);
200 ASYNC_INVOKER_FOR(IHandlerControl)
201 invoker(controller.mCtrl, Some(controller.mIsProxy));
203 HRESULT hr = ASYNC_INVOKE(invoker, Invalidate);
205 if (hr == CO_E_OBJNOTCONNECTED || hr == kErrorServerDied) {
206 sHandlerControllers->RemoveElement(controller);
207 } else {
208 Unused << NS_WARN_IF(FAILED(hr));
213 /* static */
214 bool AccessibleWrap::DispatchTextChangeToHandler(Accessible* aAcc,
215 bool aIsInsert,
216 const nsString& aText,
217 int32_t aStart,
218 uint32_t aLen) {
219 MOZ_ASSERT(XRE_IsParentProcess());
220 MOZ_ASSERT(NS_IsMainThread());
221 MOZ_ASSERT(!StaticPrefs::accessibility_cache_enabled_AtStartup());
223 if (!sHandlerControllers || sHandlerControllers->IsEmpty()) {
224 return false;
227 HWND hwnd = MsaaAccessible::GetHWNDFor(aAcc);
228 MOZ_ASSERT(hwnd);
229 if (!hwnd) {
230 return false;
233 long msaaId = MsaaAccessible::GetChildIDFor(aAcc);
235 DWORD ourPid = ::GetCurrentProcessId();
237 // The handler ends up calling NotifyWinEvent, which should only be done once
238 // since it broadcasts the same event to every process who is subscribed.
239 // OTOH, if our chrome process contains a handler, we should prefer to
240 // broadcast the event from that process, as we want any DLLs injected by ATs
241 // to receive the event synchronously. Otherwise we simply choose the first
242 // handler in the list, for the lack of a better heuristic.
244 nsTArray<HandlerControllerData>::index_type ctrlIndex =
245 sHandlerControllers->IndexOf(ourPid);
247 if (ctrlIndex == nsTArray<HandlerControllerData>::NoIndex) {
248 ctrlIndex = 0;
251 HandlerControllerData& controller = sHandlerControllers->ElementAt(ctrlIndex);
252 MOZ_ASSERT(controller.mPid);
253 MOZ_ASSERT(controller.mCtrl);
255 VARIANT_BOOL isInsert = aIsInsert ? VARIANT_TRUE : VARIANT_FALSE;
257 IA2TextSegment textSegment{::SysAllocStringLen(aText.get(), aText.Length()),
258 aStart, aStart + static_cast<long>(aLen)};
260 ASYNC_INVOKER_FOR(IHandlerControl)
261 invoker(controller.mCtrl, Some(controller.mIsProxy));
263 HRESULT hr = ASYNC_INVOKE(invoker, OnTextChange, PtrToLong(hwnd), msaaId,
264 isInsert, &textSegment);
266 ::SysFreeString(textSegment.text);
268 return SUCCEEDED(hr);