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"
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 ////////////////////////////////////////////////////////////////////////////////
36 ////////////////////////////////////////////////////////////////////////////////
37 AccessibleWrap::AccessibleWrap(nsIContent
* aContent
, DocAccessible
* aDoc
)
38 : LocalAccessible(aContent
, aDoc
) {}
40 NS_IMPL_ISUPPORTS_INHERITED0(AccessibleWrap
, LocalAccessible
)
42 void AccessibleWrap::Shutdown() {
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() {
58 mMsaa
= MsaaAccessible::Create(this);
63 void AccessibleWrap::GetNativeInterface(void** aOutAccessible
) {
64 RefPtr
<IAccessible
> result
= GetMsaa();
65 return result
.forget(aOutAccessible
);
68 ////////////////////////////////////////////////////////////////////////////////
71 nsresult
AccessibleWrap::HandleAccEvent(AccEvent
* aEvent
) {
72 nsresult rv
= LocalAccessible::HandleAccEvent(aEvent
);
73 NS_ENSURE_SUCCESS(rv
, rv
);
75 if (IPCAccessibilityActive()) {
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
);
97 ////////////////////////////////////////////////////////////////////////////////
100 //------- Helper methods ---------
102 bool AccessibleWrap::IsRootForHWND() {
106 HWND thisHwnd
= MsaaAccessible::GetHWNDFor(this);
107 AccessibleWrap
* parent
= static_cast<AccessibleWrap
*>(LocalParent());
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
118 HyperTextAccessible
* text
= aAccessible
->AsHyperText();
121 nsIWidget
* widget
= nullptr;
122 LayoutDeviceIntRect caretRect
= text
->GetCaretRect(&widget
);
129 reinterpret_cast<HWND
>(widget
->GetNativeData(NS_NATIVE_WINDOW
));
130 UpdateSystemCaretFor(caretWnd
, caretRect
);
134 void AccessibleWrap::UpdateSystemCaretFor(
135 RemoteAccessible
* aProxy
, const LayoutDeviceIntRect
& aCaretRect
) {
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
);
145 void AccessibleWrap::UpdateSystemCaretFor(
146 HWND aCaretWnd
, const LayoutDeviceIntRect
& aCaretRect
) {
147 if (!aCaretWnd
|| aCaretRect
.IsEmpty()) {
151 // Create invisible bitmap for caret, otherwise its appearance interferes
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
);
158 ::GetWindowRect(aCaretWnd
, &windowRect
);
159 ::SetCaretPos(aCaretRect
.X() - windowRect
.left
,
160 aCaretRect
.Y() - windowRect
.top
);
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
)) {
179 sHandlerControllers
->AppendElement(std::move(ctrlData
));
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()) {
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
);
208 Unused
<< NS_WARN_IF(FAILED(hr
));
214 bool AccessibleWrap::DispatchTextChangeToHandler(Accessible
* aAcc
,
216 const nsString
& aText
,
219 MOZ_ASSERT(XRE_IsParentProcess());
220 MOZ_ASSERT(NS_IsMainThread());
221 MOZ_ASSERT(!StaticPrefs::accessibility_cache_enabled_AtStartup());
223 if (!sHandlerControllers
|| sHandlerControllers
->IsEmpty()) {
227 HWND hwnd
= MsaaAccessible::GetHWNDFor(aAcc
);
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
) {
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
);