Bug 1619506 part 1: Implement QueryService to SID_IAccessibleContentDocument for OOP iframes. r=yzen

For OOP iframes, the top level document lives in a different process.
Previously, we incorrectly returned the top level document in the same process.
This was causing JAWS to incorrectly identify OOP iframe documents as separate tab documents.
To fix this, we must send the real top level document down from the parent process and return that when requested in the content process.

Differential Revision: https://phabricator.services.mozilla.com/D70162

--HG--
extra : moz-landing-system : lando
This commit is contained in:
James Teh 2020-04-08 18:28:08 +00:00
parent c2cfba805b
commit fa30324c84
6 changed files with 68 additions and 0 deletions

View File

@ -25,6 +25,20 @@
#endif
namespace mozilla {
#if defined(XP_WIN)
namespace mscom {
namespace detail {
// Needed by mscom::PassthruProxy::Wrap<IAccessible>.
template <>
struct VTableSizer<IAccessible> {
// 3 methods in IUnknown + 4 in IDispatch + 21 in IAccessible = 28 total
enum { Size = 28 };
};
} // namespace detail
} // namespace mscom
#endif // defined (XP_WIN)
namespace a11y {
uint64_t DocAccessibleParent::sMaxDocID = 0;
@ -606,6 +620,27 @@ ipc::IPCResult DocAccessibleParent::AddChildDoc(DocAccessibleParent* aChildDoc,
Unused << aChildDoc->SendEmulatedWindow(
reinterpret_cast<uintptr_t>(mEmulatedWindowHandle), nullptr);
}
// Send a COM proxy for the top level document to the embedded document
// process. This will be returned when the client calls QueryService
// with SID_IAccessibleContentDocument on an accessible in the embedded
// document.
DocAccessibleParent* topDoc = this;
while (DocAccessibleParent* parentDoc = topDoc->ParentDoc()) {
topDoc = parentDoc;
}
MOZ_ASSERT(topDoc && topDoc->IsTopLevel());
RefPtr<IAccessible> topDocAcc;
topDoc->GetCOMInterface((void**)getter_AddRefs(topDocAcc));
RefPtr<IAccessible> topDocWrapped(
mscom::PassthruProxy::Wrap<IAccessible>(WrapNotNull(topDocAcc)));
IAccessibleHolder::COMPtrType topDocPtr(
mscom::ToProxyUniquePtr(std::move(topDocWrapped)));
IAccessibleHolder topDocHolder(std::move(topDocPtr));
if (aChildDoc->SendTopLevelDocCOMProxy(topDocHolder)) {
# if defined(MOZ_SANDBOX)
aChildDoc->mTopLevelDocProxyStream = topDocHolder.GetPreservedStream();
# endif // defined(MOZ_SANDBOX)
}
#endif // defined(XP_WIN)
// We need to fire a reorder event on the outer doc accessible.
// For same-process documents, this is fired by the content process, but

View File

@ -309,6 +309,7 @@ class DocAccessibleParent : public ProxyAccessible,
# if defined(MOZ_SANDBOX)
mscom::PreservedStreamPtr mParentProxyStream;
mscom::PreservedStreamPtr mDocProxyStream;
mscom::PreservedStreamPtr mTopLevelDocProxyStream;
# endif // defined(MOZ_SANDBOX)
#endif // defined(XP_WIN)

View File

@ -72,6 +72,13 @@ ipc::IPCResult DocAccessibleChild::RecvEmulatedWindow(
return IPC_OK();
}
ipc::IPCResult DocAccessibleChild::RecvTopLevelDocCOMProxy(
const IAccessibleHolder& aCOMProxy) {
MOZ_ASSERT(!aCOMProxy.IsNull());
mTopLevelDocProxy.reset(const_cast<IAccessibleHolder&>(aCOMProxy).Release());
return IPC_OK();
}
HWND DocAccessibleChild::GetNativeWindowHandle() const {
if (mEmulatedWindowHandle) {
return mEmulatedWindowHandle;

View File

@ -32,6 +32,8 @@ class DocAccessibleChild : public DocAccessibleChildBase {
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;
@ -40,6 +42,9 @@ class DocAccessibleChild : public DocAccessibleChildBase {
}
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);
@ -351,6 +356,7 @@ class DocAccessibleChild : public DocAccessibleChildBase {
bool mIsRemoteConstructed;
mscom::ProxyUniquePtr<IDispatch> mParentProxy;
mscom::ProxyUniquePtr<IDispatch> mEmulatedWindowProxy;
mscom::ProxyUniquePtr<IAccessible> mTopLevelDocProxy;
nsTArray<UniquePtr<DeferredEvent>> mDeferredEvents;
HWND mEmulatedWindowHandle;
};

View File

@ -96,6 +96,7 @@ child:
async ParentCOMProxy(IDispatchHolder aParentCOMProxy);
async EmulatedWindow(WindowsHandle aEmulatedWindowHandle,
IDispatchHolder aEmulatedWindowCOMProxy);
async TopLevelDocCOMProxy(IAccessibleHolder aCOMProxy);
/*
* Called as a result of focus shifting from chrome to content
* elements through keyboard navigation.

View File

@ -11,8 +11,10 @@
#include "nsAccUtils.h"
#include "nsCoreUtils.h"
#include "Relation.h"
#include "RootAccessible.h"
#include "uiaRawElmProvider.h"
#include "mozilla/a11y/DocAccessibleChild.h"
#include "mozilla/Preferences.h"
#include "ISimpleDOM.h"
@ -57,6 +59,22 @@ ServiceProvider::QueryService(REFGUID aGuidService, REFIID aIID,
if (aGuidService == SID_IAccessibleContentDocument) {
if (aIID != IID_IAccessible) return E_NOINTERFACE;
// If mAccessible is within an OOP iframe document, the top level document
// lives in a different process.
if (XRE_IsContentProcess()) {
RootAccessible* root = mAccessible->RootAccessible();
MOZ_ASSERT(root);
DocAccessibleChild* ipcDoc = root->IPCDoc();
if (ipcDoc) {
RefPtr<IAccessible> topDoc = ipcDoc->GetTopLevelDocIAccessible();
// topDoc will be null if this isn't an OOP iframe document.
if (topDoc) {
topDoc.forget(aInstancePtr);
return S_OK;
}
}
}
Relation rel =
mAccessible->RelationByType(RelationType::CONTAINING_TAB_PANE);
AccessibleWrap* tabDoc = static_cast<AccessibleWrap*>(rel.Next());