/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_a11y_DocAccessibleChild_h #define mozilla_a11y_DocAccessibleChild_h #include "mozilla/a11y/COMPtrTypes.h" #include "mozilla/a11y/DocAccessibleChildBase.h" #include "mozilla/dom/BrowserBridgeChild.h" #include "mozilla/dom/BrowserChild.h" #include "mozilla/mscom/Ptr.h" namespace mozilla { namespace a11y { /* * These objects handle content side communication for an accessible document, * and their lifetime is the same as the document they represent. */ class DocAccessibleChild : public DocAccessibleChildBase { public: DocAccessibleChild(DocAccessible* aDoc, IProtocol* aManager); ~DocAccessibleChild(); virtual void Shutdown() override; virtual ipc::IPCResult RecvParentCOMProxy( const IDispatchHolder& aParentCOMProxy) override; virtual ipc::IPCResult RecvEmulatedWindow( const WindowsHandle& aEmulatedWindowHandle, const IDispatchHolder& aEmulatedWindowCOMProxy) override; virtual ipc::IPCResult RecvTopLevelDocCOMProxy( const IAccessibleHolder& aCOMProxy) override; virtual ipc::IPCResult RecvRestoreFocus() override; HWND GetNativeWindowHandle() const; IDispatch* GetEmulatedWindowIAccessible() const { return mEmulatedWindowProxy.get(); } IDispatch* GetParentIAccessible() const { return mParentProxy.get(); } IAccessible* GetTopLevelDocIAccessible() const { return mTopLevelDocProxy.get(); } bool SendEvent(const uint64_t& aID, const uint32_t& type); bool SendHideEvent(const uint64_t& aRootID, const bool& aFromUser); bool SendStateChangeEvent(const uint64_t& aID, const uint64_t& aState, const bool& aEnabled); bool SendCaretMoveEvent(const uint64_t& aID, const int32_t& aOffset, const bool& aIsSelectionCollapsed, const bool& aIsAtEndOfLine, const int32_t& aGranularity); bool SendCaretMoveEvent(const uint64_t& aID, const LayoutDeviceIntRect& aCaretRect, const int32_t& aOffset, const bool& aIsSelectionCollapsed, const bool& aIsAtEndOfLine, const int32_t& aGranularity); 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, const bool aDoSync = false); bool SendSelectionEvent(const uint64_t& aID, const uint64_t& aWidgetID, const uint32_t& aType); bool SendRoleChangedEvent(const a11y::role& aRole, uint8_t aRoleMapEntryIndex); bool SendScrollingEvent(const uint64_t& aID, const uint64_t& aType, const uint32_t& aScrollX, const uint32_t& aScrollY, const uint32_t& aMaxScrollX, const uint32_t& aMaxScrollY); bool ConstructChildDocInParentProcess(DocAccessibleChild* aNewChildDoc, uint64_t aUniqueID, uint32_t aMsaaID); bool SendBindChildDoc(DocAccessibleChild* aChildDoc, const uint64_t& aNewParentID); /** * Set the embedder accessible on a BrowserBridgeChild to an accessible in * this document. * Sending this will be deferred if this DocAccessibleChild hasn't been * constructed in the parent process yet. */ void SetEmbedderOnBridge(dom::BrowserBridgeChild* aBridge, uint64_t aID); protected: virtual void MaybeSendShowEvent(ShowEventData& aData, bool aFromUser) override; private: void RemoveDeferredConstructor(); 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 * attempting to fire events during this window of time. If this object does * not yet have its parent proxy, instead of immediately sending the events to * our parent, we enqueue them to mDeferredEvents. As soon as * RecvParentCOMProxy is called, we play back mDeferredEvents. */ struct DeferredEvent { void Dispatch() { Dispatch(mTarget); } virtual ~DeferredEvent() {} protected: explicit DeferredEvent(DocAccessibleChild* aTarget) : mTarget(aTarget) {} virtual void Dispatch(DocAccessibleChild* aIPCDoc) = 0; private: DocAccessibleChild* mTarget; }; void PushDeferredEvent(UniquePtr aEvent); struct SerializedShow final : public DeferredEvent { SerializedShow(DocAccessibleChild* aTarget, ShowEventData& aEventData, bool aFromUser) : DeferredEvent(aTarget), mEventData(aEventData.ID(), aEventData.Idx(), nsTArray(), aEventData.EventSuppressed()), mFromUser(aFromUser) { // Since IPDL doesn't generate a move constructor for ShowEventData, // we move NewTree manually (ugh). We still construct with an empty // NewTree above so that the compiler catches any changes made to the // ShowEventData structure in IPDL. mEventData.NewTree() = std::move(aEventData.NewTree()); } void Dispatch(DocAccessibleChild* aIPCDoc) override { Unused << aIPCDoc->SendShowEvent(mEventData, mFromUser); } ShowEventData mEventData; bool mFromUser; }; struct SerializedHide final : public DeferredEvent { SerializedHide(DocAccessibleChild* aTarget, uint64_t aRootID, bool aFromUser) : DeferredEvent(aTarget), mRootID(aRootID), mFromUser(aFromUser) {} void Dispatch(DocAccessibleChild* aIPCDoc) override { Unused << aIPCDoc->SendHideEvent(mRootID, mFromUser); } uint64_t mRootID; bool mFromUser; }; struct SerializedStateChange final : public DeferredEvent { SerializedStateChange(DocAccessibleChild* aTarget, uint64_t aID, uint64_t aState, bool aEnabled) : DeferredEvent(aTarget), mID(aID), mState(aState), mEnabled(aEnabled) {} void Dispatch(DocAccessibleChild* aIPCDoc) override { Unused << aIPCDoc->SendStateChangeEvent(mID, mState, mEnabled); } uint64_t mID; uint64_t mState; bool mEnabled; }; struct SerializedCaretMove final : public DeferredEvent { SerializedCaretMove(DocAccessibleChild* aTarget, uint64_t aID, const LayoutDeviceIntRect& aCaretRect, int32_t aOffset, bool aIsSelectionCollapsed, bool aIsAtEndOfLine, int32_t aGranularity) : DeferredEvent(aTarget), mID(aID), mCaretRect(aCaretRect), mOffset(aOffset), mIsSelectionCollapsed(aIsSelectionCollapsed), mIsAtEndOfLine(aIsAtEndOfLine), mGranularity(aGranularity) {} void Dispatch(DocAccessibleChild* aIPCDoc) override { Unused << aIPCDoc->SendCaretMoveEvent(mID, mCaretRect, mOffset, mIsSelectionCollapsed, mIsAtEndOfLine, mGranularity); } uint64_t mID; LayoutDeviceIntRect mCaretRect; int32_t mOffset; bool mIsSelectionCollapsed; bool mIsAtEndOfLine; int32_t mGranularity; }; 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 { SerializedTextChange(DocAccessibleChild* aTarget, uint64_t aID, const nsString& aStr, int32_t aStart, uint32_t aLen, bool aIsInsert, bool aFromUser) : DeferredEvent(aTarget), mID(aID), mStr(aStr), mStart(aStart), mLen(aLen), mIsInsert(aIsInsert), mFromUser(aFromUser) {} void Dispatch(DocAccessibleChild* aIPCDoc) override { Unused << aIPCDoc->SendTextChangeEvent(mID, mStr, mStart, mLen, mIsInsert, mFromUser, false); } uint64_t mID; nsString mStr; int32_t mStart; uint32_t mLen; bool mIsInsert; bool mFromUser; }; struct SerializedSelection final : public DeferredEvent { SerializedSelection(DocAccessibleChild* aTarget, uint64_t aID, uint64_t aWidgetID, uint32_t aType) : DeferredEvent(aTarget), mID(aID), mWidgetID(aWidgetID), mType(aType) {} void Dispatch(DocAccessibleChild* aIPCDoc) override { Unused << aIPCDoc->SendSelectionEvent(mID, mWidgetID, mType); } uint64_t mID; uint64_t mWidgetID; uint32_t mType; }; struct SerializedRoleChanged final : public DeferredEvent { explicit SerializedRoleChanged(DocAccessibleChild* aTarget, a11y::role aRole, uint8_t aRoleMapEntryIndex) : DeferredEvent(aTarget), mRole(aRole), mRoleMapEntryIndex(aRoleMapEntryIndex) {} void Dispatch(DocAccessibleChild* aIPCDoc) override { Unused << aIPCDoc->SendRoleChangedEvent(mRole, mRoleMapEntryIndex); } a11y::role mRole; uint8_t mRoleMapEntryIndex; }; struct SerializedScrolling final : public DeferredEvent { explicit SerializedScrolling(DocAccessibleChild* aTarget, uint64_t aID, uint64_t aType, uint32_t aScrollX, uint32_t aScrollY, uint32_t aMaxScrollX, uint32_t aMaxScrollY) : DeferredEvent(aTarget), mID(aID), mType(aType), mScrollX(aScrollX), mScrollY(aScrollY), mMaxScrollX(aMaxScrollX), mMaxScrollY(aMaxScrollY) {} void Dispatch(DocAccessibleChild* aIPCDoc) override { Unused << aIPCDoc->SendScrollingEvent(mID, mType, mScrollX, mScrollY, mMaxScrollX, mMaxScrollY); } uint64_t mID; uint64_t mType; uint32_t mScrollX; uint32_t mScrollY; uint32_t mMaxScrollX; uint32_t mMaxScrollY; }; struct SerializedEvent final : public DeferredEvent { SerializedEvent(DocAccessibleChild* aTarget, uint64_t aID, uint32_t aType) : DeferredEvent(aTarget), mID(aID), mType(aType) {} void Dispatch(DocAccessibleChild* aIPCDoc) override { Unused << aIPCDoc->SendEvent(mID, mType); } uint64_t mID; uint32_t mType; }; struct SerializedChildDocConstructor final : public DeferredEvent { SerializedChildDocConstructor(DocAccessibleChild* aIPCDoc, DocAccessibleChild* aParentIPCDoc, uint64_t aUniqueID, dom::BrowsingContext* aBrowsingContext, uint32_t aMsaaID) : DeferredEvent(aParentIPCDoc), mIPCDoc(aIPCDoc), mUniqueID(aUniqueID), mBrowsingContext(aBrowsingContext), mMsaaID(aMsaaID) {} void Dispatch(DocAccessibleChild* aParentIPCDoc) override { auto browserChild = static_cast(aParentIPCDoc->Manager()); MOZ_ASSERT(browserChild); Unused << browserChild->SendPDocAccessibleConstructor( mIPCDoc, aParentIPCDoc, mUniqueID, mBrowsingContext, mMsaaID, IAccessibleHolder()); mIPCDoc->SetConstructedInParentProcess(); } DocAccessibleChild* mIPCDoc; uint64_t mUniqueID; // By the time we replay this, the document and its BrowsingContext might // be dead, so we use MaybeDiscardedBrowsingContext here. Ideally, we just // wouldn't replay this, but this is tricky because IPDL should manage the // lifetime of DocAccessibleChild, which means we must send the constructor. dom::MaybeDiscardedBrowsingContext mBrowsingContext; uint32_t mMsaaID; }; friend struct SerializedChildDocConstructor; struct SerializedBindChildDoc final : public DeferredEvent { SerializedBindChildDoc(DocAccessibleChild* aParentDoc, DocAccessibleChild* aChildDoc, uint64_t aNewParentID) : DeferredEvent(aParentDoc), mChildDoc(aChildDoc), mNewParentID(aNewParentID) {} void Dispatch(DocAccessibleChild* aParentIPCDoc) override { Unused << aParentIPCDoc->SendBindChildDoc(mChildDoc, mNewParentID); } DocAccessibleChild* mChildDoc; uint64_t mNewParentID; }; struct SerializedShutdown final : public DeferredEvent { explicit SerializedShutdown(DocAccessibleChild* aTarget) : DeferredEvent(aTarget) {} void Dispatch(DocAccessibleChild* aIPCDoc) override { aIPCDoc->Shutdown(); } }; struct SerializedSetEmbedder final : public DeferredEvent { SerializedSetEmbedder(dom::BrowserBridgeChild* aBridge, DocAccessibleChild* aDoc, uint64_t aID) : DeferredEvent(aDoc), mBridge(aBridge), mID(aID) {} void Dispatch(DocAccessibleChild* aDoc) override { mBridge->SetEmbedderAccessible(aDoc, mID); } RefPtr mBridge; uint64_t mID; }; mscom::ProxyUniquePtr mParentProxy; mscom::ProxyUniquePtr mEmulatedWindowProxy; mscom::ProxyUniquePtr mTopLevelDocProxy; nsTArray> mDeferredEvents; HWND mEmulatedWindowHandle; }; } // namespace a11y } // namespace mozilla #endif // mozilla_a11y_DocAccessibleChild_h