gecko-dev/accessible/ipc/DocAccessibleParent.h
Aaron Klotz 570cbae5c4 Bug 1364544: Ensure that proxied CARET_MOVED and FOCUS events update the Win32 system caret before firing their WinEvents; r=eeejay
MozReview-Commit-ID: LVML7EZaSYD

In non-e10s AccessibleWrap::HandleAccEvent, we special case our handling of
CARET_MOVED and FOCUS events with a call to UpdateSystemCaretFor. In e10s mode
we were not doing the same thing for proxied events sent from content. This
threw JAWS for a loop and presumably messes up other ATs as well.

This patch modifies the IPDL messages for these two events so that we may
send the caret rect along with the event, thus allowing us to update the
system caret for proxied events as well.

--HG--
extra : rebase_source : e1502c12b038739520afd5c7078d011e25ea669e
2017-05-15 14:11:46 -06:00

263 lines
8.5 KiB
C++

/* -*- 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_DocAccessibleParent_h
#define mozilla_a11y_DocAccessibleParent_h
#include "nsAccessibilityService.h"
#include "mozilla/a11y/PDocAccessibleParent.h"
#include "mozilla/a11y/ProxyAccessible.h"
#include "nsClassHashtable.h"
#include "nsHashKeys.h"
#include "nsISupportsImpl.h"
namespace mozilla {
namespace a11y {
class xpcAccessibleGeneric;
/*
* These objects live in the main process and comunicate with and represent
* an accessible document in a content process.
*/
class DocAccessibleParent : public ProxyAccessible,
public PDocAccessibleParent
{
public:
DocAccessibleParent() :
ProxyAccessible(this), mParentDoc(kNoParentDoc),
mTopLevel(false), mShutdown(false)
#if defined(XP_WIN)
, mEmulatedWindowHandle(nullptr)
#endif // defined(XP_WIN)
{
MOZ_COUNT_CTOR_INHERITED(DocAccessibleParent, ProxyAccessible);
sMaxDocID++;
mActorID = sMaxDocID;
MOZ_ASSERT(!LiveDocs().Get(mActorID));
LiveDocs().Put(mActorID, this);
}
~DocAccessibleParent()
{
LiveDocs().Remove(mActorID);
MOZ_COUNT_DTOR_INHERITED(DocAccessibleParent, ProxyAccessible);
MOZ_ASSERT(mChildDocs.Length() == 0);
MOZ_ASSERT(!ParentDoc());
}
void SetTopLevel() { mTopLevel = true; }
bool IsTopLevel() const { return mTopLevel; }
bool IsShutdown() const { return mShutdown; }
/**
* Mark this actor as shutdown without doing any cleanup. This should only
* be called on actors that have just been initialized, so probably only from
* RecvPDocAccessibleConstructor.
*/
void MarkAsShutdown()
{
MOZ_ASSERT(mChildDocs.IsEmpty());
MOZ_ASSERT(mAccessibles.Count() == 0);
mShutdown = true;
}
/*
* Called when a message from a document in a child process notifies the main
* process it is firing an event.
*/
virtual mozilla::ipc::IPCResult RecvEvent(const uint64_t& aID, const uint32_t& aType)
override;
virtual mozilla::ipc::IPCResult RecvShowEvent(const ShowEventData& aData, const bool& aFromUser)
override;
virtual mozilla::ipc::IPCResult RecvHideEvent(const uint64_t& aRootID, const bool& aFromUser)
override;
virtual mozilla::ipc::IPCResult RecvStateChangeEvent(const uint64_t& aID,
const uint64_t& aState,
const bool& aEnabled) 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,
const bool& aIsInsert,
const bool& aFromUser) override;
#if defined(XP_WIN)
virtual mozilla::ipc::IPCResult RecvSyncTextChangeEvent(const uint64_t& aID, const nsString& aStr,
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,
const uint64_t& aWidgetID,
const uint32_t& aType) override;
virtual mozilla::ipc::IPCResult RecvRoleChangedEvent(const uint32_t& aRole) override final;
virtual mozilla::ipc::IPCResult RecvBindChildDoc(PDocAccessibleParent* aChildDoc, const uint64_t& aID) override;
void Unbind()
{
if (DocAccessibleParent* parent = ParentDoc()) {
parent->RemoveChildDoc(this);
}
SetParent(nullptr);
}
virtual mozilla::ipc::IPCResult RecvShutdown() override;
void Destroy();
virtual void ActorDestroy(ActorDestroyReason aWhy) override
{
MOZ_ASSERT(CheckDocTree());
if (!mShutdown)
Destroy();
}
/*
* Return the main processes representation of the parent document (if any)
* of the document this object represents.
*/
DocAccessibleParent* ParentDoc() const;
static const uint64_t kNoParentDoc = UINT64_MAX;
/*
* Called when a document in a content process notifies the main process of a
* new child document.
*/
ipc::IPCResult AddChildDoc(DocAccessibleParent* aChildDoc,
uint64_t aParentID, bool aCreating = true);
/*
* Called when the document in the content process this object represents
* notifies the main process a child document has been removed.
*/
void RemoveChildDoc(DocAccessibleParent* aChildDoc)
{
ProxyAccessible* parent = aChildDoc->Parent();
MOZ_ASSERT(parent);
if (parent) {
aChildDoc->Parent()->ClearChildDoc(aChildDoc);
}
DebugOnly<bool> result = mChildDocs.RemoveElement(aChildDoc->mActorID);
aChildDoc->mParentDoc = kNoParentDoc;
MOZ_ASSERT(result);
}
void RemoveAccessible(ProxyAccessible* aAccessible)
{
MOZ_DIAGNOSTIC_ASSERT(mAccessibles.GetEntry(aAccessible->ID()));
mAccessibles.RemoveEntry(aAccessible->ID());
}
/**
* Return the accessible for given id.
*/
ProxyAccessible* GetAccessible(uintptr_t aID)
{
if (!aID)
return this;
ProxyEntry* e = mAccessibles.GetEntry(aID);
return e ? e->mProxy : nullptr;
}
const ProxyAccessible* GetAccessible(uintptr_t aID) const
{ return const_cast<DocAccessibleParent*>(this)->GetAccessible(aID); }
size_t ChildDocCount() const { return mChildDocs.Length(); }
const DocAccessibleParent* ChildDocAt(size_t aIdx) const
{ return const_cast<DocAccessibleParent*>(this)->ChildDocAt(aIdx); }
DocAccessibleParent* ChildDocAt(size_t aIdx)
{ return LiveDocs().Get(mChildDocs[aIdx]); }
#if defined(XP_WIN)
void MaybeInitWindowEmulation();
void SendParentCOMProxy();
virtual mozilla::ipc::IPCResult RecvGetWindowedPluginIAccessible(
const WindowsHandle& aHwnd, IAccessibleHolder* aPluginCOMProxy) override;
/**
* Set emulated native window handle for a document.
* @param aWindowHandle emulated native window handle
*/
void SetEmulatedWindowHandle(HWND aWindowHandle);
HWND GetEmulatedWindowHandle() const { return mEmulatedWindowHandle; }
#endif
private:
class ProxyEntry : public PLDHashEntryHdr
{
public:
explicit ProxyEntry(const void*) : mProxy(nullptr) {}
ProxyEntry(ProxyEntry&& aOther) :
mProxy(aOther.mProxy) { aOther.mProxy = nullptr; }
~ProxyEntry() { delete mProxy; }
typedef uint64_t KeyType;
typedef const void* KeyTypePointer;
bool KeyEquals(const void* aKey) const
{ return mProxy->ID() == (uint64_t)aKey; }
static const void* KeyToPointer(uint64_t aKey) { return (void*)aKey; }
static PLDHashNumber HashKey(const void* aKey) { return (uint64_t)aKey; }
enum { ALLOW_MEMMOVE = true };
ProxyAccessible* mProxy;
};
uint32_t AddSubtree(ProxyAccessible* aParent,
const nsTArray<AccessibleData>& aNewTree, uint32_t aIdx,
uint32_t aIdxInParent);
MOZ_MUST_USE bool CheckDocTree() const;
xpcAccessibleGeneric* GetXPCAccessible(ProxyAccessible* aProxy);
nsTArray<uint64_t> mChildDocs;
uint64_t mParentDoc;
#if defined(XP_WIN)
// The handle associated with the emulated window that contains this document
HWND mEmulatedWindowHandle;
#endif
/*
* Conceptually this is a map from IDs to proxies, but we store the ID in the
* proxy object so we can't use a real map.
*/
nsTHashtable<ProxyEntry> mAccessibles;
uint64_t mActorID;
bool mTopLevel;
bool mShutdown;
static uint64_t sMaxDocID;
static nsDataHashtable<nsUint64HashKey, DocAccessibleParent*>&
LiveDocs()
{
static nsDataHashtable<nsUint64HashKey, DocAccessibleParent*> sLiveDocs;
return sLiveDocs;
}
};
}
}
#endif