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
This commit is contained in:
Aaron Klotz 2017-05-15 14:11:46 -06:00
Родитель 56e5364aaf
Коммит 570cbae5c4
10 изменённых файлов: 199 добавлений и 18 удалений

Просмотреть файл

@ -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);

Просмотреть файл

@ -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());
}

Просмотреть файл

@ -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<xpcAccEvent> event = new xpcAccEvent(nsIAccessibleEvent::EVENT_FOCUS,
xpcAcc, doc, node, fromUser);
nsCoreUtils::DispatchAccEvent(Move(event));
return IPC_OK();
}
#endif // defined(XP_WIN)
} // a11y

Просмотреть файл

@ -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,

Просмотреть файл

@ -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<Accessible*>(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<SerializedFocus>(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<SerializedCaretMove>(this, aID, aOffset));
PushDeferredEvent(MakeUnique<SerializedCaretMove>(this, aID, aCaretRect,
aOffset));
return true;
}

Просмотреть файл

@ -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

Просмотреть файл

@ -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

Просмотреть файл

@ -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<HWND>(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);
}
}

Просмотреть файл

@ -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.
*/

Просмотреть файл

@ -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);
}