diff --git a/accessible/base/Platform.h b/accessible/base/Platform.h index 25204565b897..a6fe22ae7399 100644 --- a/accessible/base/Platform.h +++ b/accessible/base/Platform.h @@ -74,7 +74,15 @@ void ProxyDestroyed(ProxyAccessible*); void ProxyEvent(ProxyAccessible* aTarget, uint32_t aEventType); void ProxyStateChangeEvent(ProxyAccessible* aTarget, uint64_t aState, bool aEnabled); + +#if defined(XP_WIN) +void ProxyFocusEvent(ProxyAccessible* aTarget, + const LayoutDeviceIntRect& aCaretRect); +void ProxyCaretMoveEvent(ProxyAccessible* aTarget, + const LayoutDeviceIntRect& aCaretRect); +#else void ProxyCaretMoveEvent(ProxyAccessible* aTarget, int32_t aOffset); +#endif void ProxyTextChangeEvent(ProxyAccessible* aTarget, const nsString& aStr, int32_t aStart, uint32_t aLen, bool aIsInsert, bool aFromUser); diff --git a/accessible/generic/Accessible.cpp b/accessible/generic/Accessible.cpp index 329d7b2ee530..868b672e1793 100644 --- a/accessible/generic/Accessible.cpp +++ b/accessible/generic/Accessible.cpp @@ -891,6 +891,12 @@ Accessible::HandleAccEvent(AccEvent* aEvent) ipcDoc->SendSelectionEvent(id, widgetID, aEvent->GetEventType()); break; } +#if defined(XP_WIN) + case nsIAccessibleEvent::EVENT_FOCUS: { + ipcDoc->SendFocusEvent(id); + break; + } +#endif default: ipcDoc->SendEvent(id, aEvent->GetEventType()); } diff --git a/accessible/ipc/DocAccessibleParent.cpp b/accessible/ipc/DocAccessibleParent.cpp index c433d9866d1c..5e9a38c46b21 100644 --- a/accessible/ipc/DocAccessibleParent.cpp +++ b/accessible/ipc/DocAccessibleParent.cpp @@ -271,7 +271,11 @@ DocAccessibleParent::RecvStateChangeEvent(const uint64_t& aID, } mozilla::ipc::IPCResult -DocAccessibleParent::RecvCaretMoveEvent(const uint64_t& aID, const int32_t& aOffset) +DocAccessibleParent::RecvCaretMoveEvent(const uint64_t& aID, +#if defined(XP_WIN) + const LayoutDeviceIntRect& aCaretRect, +#endif // defined (XP_WIN) + const int32_t& aOffset) { if (mShutdown) { return IPC_OK(); @@ -283,7 +287,11 @@ DocAccessibleParent::RecvCaretMoveEvent(const uint64_t& aID, const int32_t& aOff return IPC_OK(); } +#if defined(XP_WIN) + ProxyCaretMoveEvent(proxy, aCaretRect); +#else ProxyCaretMoveEvent(proxy, aOffset); +#endif if (!nsCoreUtils::AccEventObserversExist()) { return IPC_OK(); @@ -708,6 +716,37 @@ DocAccessibleParent::RecvGetWindowedPluginIAccessible( #endif } +mozilla::ipc::IPCResult +DocAccessibleParent::RecvFocusEvent(const uint64_t& aID, + const LayoutDeviceIntRect& aCaretRect) +{ + if (mShutdown) { + return IPC_OK(); + } + + ProxyAccessible* proxy = GetAccessible(aID); + if (!proxy) { + NS_ERROR("no proxy for event!"); + return IPC_OK(); + } + + ProxyFocusEvent(proxy, aCaretRect); + + if (!nsCoreUtils::AccEventObserversExist()) { + return IPC_OK(); + } + + xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(proxy); + xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this); + nsIDOMNode* node = nullptr; + bool fromUser = true; // XXX fix me + RefPtr event = new xpcAccEvent(nsIAccessibleEvent::EVENT_FOCUS, + xpcAcc, doc, node, fromUser); + nsCoreUtils::DispatchAccEvent(Move(event)); + + return IPC_OK(); +} + #endif // defined(XP_WIN) } // a11y diff --git a/accessible/ipc/DocAccessibleParent.h b/accessible/ipc/DocAccessibleParent.h index c23d96d5b5ed..b5be1fbc2d9f 100644 --- a/accessible/ipc/DocAccessibleParent.h +++ b/accessible/ipc/DocAccessibleParent.h @@ -80,8 +80,11 @@ public: const uint64_t& aState, const bool& aEnabled) override final; - virtual mozilla::ipc::IPCResult RecvCaretMoveEvent(const uint64_t& aID, const int32_t& aOffset) - override final; + virtual mozilla::ipc::IPCResult RecvCaretMoveEvent(const uint64_t& aID, +#if defined(XP_WIN) + const LayoutDeviceIntRect& aCaretRect, +#endif + const int32_t& aOffset) override final; virtual mozilla::ipc::IPCResult RecvTextChangeEvent(const uint64_t& aID, const nsString& aStr, const int32_t& aStart, const uint32_t& aLen, @@ -93,6 +96,9 @@ public: const int32_t& aStart, const uint32_t& aLen, const bool& aIsInsert, const bool& aFromUser) override; + + virtual mozilla::ipc::IPCResult RecvFocusEvent(const uint64_t& aID, + const LayoutDeviceIntRect& aCaretRect) override; #endif // defined(XP_WIN) virtual mozilla::ipc::IPCResult RecvSelectionEvent(const uint64_t& aID, diff --git a/accessible/ipc/win/DocAccessibleChild.cpp b/accessible/ipc/win/DocAccessibleChild.cpp index c8170c1af95f..4987131da0a3 100644 --- a/accessible/ipc/win/DocAccessibleChild.cpp +++ b/accessible/ipc/win/DocAccessibleChild.cpp @@ -159,15 +159,64 @@ DocAccessibleChild::SendStateChangeEvent(const uint64_t& aID, return true; } +LayoutDeviceIntRect +DocAccessibleChild::GetCaretRectFor(const uint64_t& aID) +{ + Accessible* target; + + if (aID) { + target = reinterpret_cast(aID); + } else { + target = mDoc; + } + + MOZ_ASSERT(target); + + HyperTextAccessible* text = target->AsHyperText(); + if (!text) { + return LayoutDeviceIntRect(); + } + + nsIWidget* widget = nullptr; + return text->GetCaretRect(&widget); +} + +bool +DocAccessibleChild::SendFocusEvent(const uint64_t& aID) +{ + return SendFocusEvent(aID, GetCaretRectFor(aID)); +} + +bool +DocAccessibleChild::SendFocusEvent(const uint64_t& aID, + const LayoutDeviceIntRect& aCaretRect) +{ + if (IsConstructedInParentProcess()) { + return PDocAccessibleChild::SendFocusEvent(aID, aCaretRect); + } + + PushDeferredEvent(MakeUnique(this, aID, aCaretRect)); + return true; +} + bool DocAccessibleChild::SendCaretMoveEvent(const uint64_t& aID, const int32_t& aOffset) +{ + return SendCaretMoveEvent(aID, GetCaretRectFor(aID), aOffset); +} + +bool +DocAccessibleChild::SendCaretMoveEvent(const uint64_t& aID, + const LayoutDeviceIntRect& aCaretRect, + const int32_t& aOffset) { if (IsConstructedInParentProcess()) { - return PDocAccessibleChild::SendCaretMoveEvent(aID, aOffset); + return PDocAccessibleChild::SendCaretMoveEvent(aID, aCaretRect, aOffset); } - PushDeferredEvent(MakeUnique(this, aID, aOffset)); + PushDeferredEvent(MakeUnique(this, aID, aCaretRect, + aOffset)); return true; } diff --git a/accessible/ipc/win/DocAccessibleChild.h b/accessible/ipc/win/DocAccessibleChild.h index c99a04542aac..383c6823a123 100644 --- a/accessible/ipc/win/DocAccessibleChild.h +++ b/accessible/ipc/win/DocAccessibleChild.h @@ -43,6 +43,12 @@ public: bool SendStateChangeEvent(const uint64_t& aID, const uint64_t& aState, const bool& aEnabled); bool SendCaretMoveEvent(const uint64_t& aID, const int32_t& aOffset); + bool SendCaretMoveEvent(const uint64_t& aID, + const LayoutDeviceIntRect& aCaretRect, + const int32_t& aOffset); + bool SendFocusEvent(const uint64_t& aID); + bool SendFocusEvent(const uint64_t& aID, + const LayoutDeviceIntRect& aCaretRect); bool SendTextChangeEvent(const uint64_t& aID, const nsString& aStr, const int32_t& aStart, const uint32_t& aLen, const bool& aIsInsert, const bool& aFromUser); @@ -65,6 +71,8 @@ private: bool IsConstructedInParentProcess() const { return mIsRemoteConstructed; } void SetConstructedInParentProcess() { mIsRemoteConstructed = true; } + LayoutDeviceIntRect GetCaretRectFor(const uint64_t& aID); + /** * DocAccessibleChild should not fire events until it has asynchronously * received the COM proxy for its parent. OTOH, content a11y may still be @@ -159,19 +167,39 @@ private: struct SerializedCaretMove final : public DeferredEvent { SerializedCaretMove(DocAccessibleChild* aTarget, uint64_t aID, - int32_t aOffset) + const LayoutDeviceIntRect& aCaretRect, int32_t aOffset) : DeferredEvent(aTarget) , mID(aID) + , mCaretRect(aCaretRect) , mOffset(aOffset) {} void Dispatch(DocAccessibleChild* aIPCDoc) override { - Unused << aIPCDoc->SendCaretMoveEvent(mID, mOffset); + Unused << aIPCDoc->SendCaretMoveEvent(mID, mCaretRect, mOffset); } - uint64_t mID; - int32_t mOffset; + uint64_t mID; + LayoutDeviceIntRect mCaretRect; + int32_t mOffset; + }; + + struct SerializedFocus final : public DeferredEvent + { + SerializedFocus(DocAccessibleChild* aTarget, uint64_t aID, + const LayoutDeviceIntRect& aCaretRect) + : DeferredEvent(aTarget) + , mID(aID) + , mCaretRect(aCaretRect) + {} + + void Dispatch(DocAccessibleChild* aIPCDoc) override + { + Unused << aIPCDoc->SendFocusEvent(mID, mCaretRect); + } + + uint64_t mID; + LayoutDeviceIntRect mCaretRect; }; struct SerializedTextChange final : public DeferredEvent diff --git a/accessible/ipc/win/PDocAccessible.ipdl b/accessible/ipc/win/PDocAccessible.ipdl index 08145cd494bb..50594eabe003 100644 --- a/accessible/ipc/win/PDocAccessible.ipdl +++ b/accessible/ipc/win/PDocAccessible.ipdl @@ -9,6 +9,7 @@ include protocol PBrowser; using mozilla::a11y::IAccessibleHolder from "mozilla/a11y/IPCTypes.h"; using mozilla::WindowsHandle from "ipc/IPCMessageUtils.h"; +using mozilla::LayoutDeviceIntRect from "Units.h"; namespace mozilla { namespace a11y { @@ -50,13 +51,15 @@ parent: async ShowEvent(ShowEventData data, bool aFromUser); async HideEvent(uint64_t aRootID, bool aFromUser); async StateChangeEvent(uint64_t aID, uint64_t aState, bool aEnabled); - async CaretMoveEvent(uint64_t aID, int32_t aOffset); + async CaretMoveEvent(uint64_t aID, LayoutDeviceIntRect aCaretRect, + int32_t aOffset); async TextChangeEvent(uint64_t aID, nsString aStr, int32_t aStart, uint32_t aLen, bool aIsInsert, bool aFromUser); sync SyncTextChangeEvent(uint64_t aID, nsString aStr, int32_t aStart, uint32_t aLen, bool aIsInsert, bool aFromUser); async SelectionEvent(uint64_t aID, uint64_t aWidgetID, uint32_t aType); async RoleChangedEvent(uint32_t aRole); + async FocusEvent(uint64_t aID, LayoutDeviceIntRect aCaretRect); /* * Tell the parent document to bind the existing document as a new child diff --git a/accessible/windows/msaa/AccessibleWrap.cpp b/accessible/windows/msaa/AccessibleWrap.cpp index 30035981598b..92c0a3e820c4 100644 --- a/accessible/windows/msaa/AccessibleWrap.cpp +++ b/accessible/windows/msaa/AccessibleWrap.cpp @@ -1559,19 +1559,43 @@ AccessibleWrap::UpdateSystemCaretFor(Accessible* aAccessible) nsIWidget* widget = nullptr; LayoutDeviceIntRect caretRect = text->GetCaretRect(&widget); - HWND caretWnd; - if (caretRect.IsEmpty() || !(caretWnd = (HWND)widget->GetNativeData(NS_NATIVE_WINDOW))) { + + if (!widget) { + return; + } + + HWND caretWnd = reinterpret_cast(widget->GetNativeData(NS_NATIVE_WINDOW)); + UpdateSystemCaretFor(caretWnd, caretRect); +} + +/* static */ void +AccessibleWrap::UpdateSystemCaretFor(ProxyAccessible* aProxy, + const LayoutDeviceIntRect& aCaretRect) +{ + ::DestroyCaret(); + + // The HWND should be the real widget HWND, not an emulated HWND. + // We get the HWND from the proxy's outer doc to bypass window emulation. + Accessible* outerDoc = aProxy->OuterDocOfRemoteBrowser(); + UpdateSystemCaretFor(GetHWNDFor(outerDoc), aCaretRect); +} + +/* static */ void +AccessibleWrap::UpdateSystemCaretFor(HWND aCaretWnd, + const LayoutDeviceIntRect& aCaretRect) +{ + if (!aCaretWnd || aCaretRect.IsEmpty()) { return; } // Create invisible bitmap for caret, otherwise its appearance interferes // with Gecko caret - nsAutoBitmap caretBitMap(CreateBitmap(1, caretRect.height, 1, 1, nullptr)); - if (::CreateCaret(caretWnd, caretBitMap, 1, caretRect.height)) { // Also destroys the last caret - ::ShowCaret(caretWnd); + nsAutoBitmap caretBitMap(CreateBitmap(1, aCaretRect.height, 1, 1, nullptr)); + if (::CreateCaret(aCaretWnd, caretBitMap, 1, aCaretRect.height)) { // Also destroys the last caret + ::ShowCaret(aCaretWnd); RECT windowRect; - ::GetWindowRect(caretWnd, &windowRect); - ::SetCaretPos(caretRect.x - windowRect.left, caretRect.y - windowRect.top); + ::GetWindowRect(aCaretWnd, &windowRect); + ::SetCaretPos(aCaretRect.x - windowRect.left, aCaretRect.y - windowRect.top); } } diff --git a/accessible/windows/msaa/AccessibleWrap.h b/accessible/windows/msaa/AccessibleWrap.h index 602a074cc7cb..9ac34cfb4a2b 100644 --- a/accessible/windows/msaa/AccessibleWrap.h +++ b/accessible/windows/msaa/AccessibleWrap.h @@ -172,7 +172,14 @@ public: // construction, destruction * Gecko is still responsible for drawing its own caret */ void UpdateSystemCaretFor(Accessible* aAccessible); + static void UpdateSystemCaretFor(ProxyAccessible* aProxy, + const LayoutDeviceIntRect& aCaretRect); +private: + static void UpdateSystemCaretFor(HWND aCaretWnd, + const LayoutDeviceIntRect& aCaretRect); + +public: /** * Find an accessible by the given child ID in cached documents. */ diff --git a/accessible/windows/msaa/Platform.cpp b/accessible/windows/msaa/Platform.cpp index 63f6111c1f3f..d4b9728cdf52 100644 --- a/accessible/windows/msaa/Platform.cpp +++ b/accessible/windows/msaa/Platform.cpp @@ -116,8 +116,19 @@ a11y::ProxyStateChangeEvent(ProxyAccessible* aTarget, uint64_t, bool) } void -a11y::ProxyCaretMoveEvent(ProxyAccessible* aTarget, int32_t aOffset) +a11y::ProxyFocusEvent(ProxyAccessible* aTarget, + const LayoutDeviceIntRect& aCaretRect) { + AccessibleWrap::UpdateSystemCaretFor(aTarget, aCaretRect); + AccessibleWrap::FireWinEvent(WrapperFor(aTarget), + nsIAccessibleEvent::EVENT_FOCUS); +} + +void +a11y::ProxyCaretMoveEvent(ProxyAccessible* aTarget, + const LayoutDeviceIntRect& aCaretRect) +{ + AccessibleWrap::UpdateSystemCaretFor(aTarget, aCaretRect); AccessibleWrap::FireWinEvent(WrapperFor(aTarget), nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED); }