зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
56e5364aaf
Коммит
570cbae5c4
|
@ -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);
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче