зеркало из https://github.com/mozilla/gecko-dev.git
188 строки
6.0 KiB
C++
188 строки
6.0 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* 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/. */
|
|
|
|
#include "RootAccessibleWrap.h"
|
|
|
|
#include "Compatibility.h"
|
|
#include "mozilla/PresShell.h"
|
|
#include "mozilla/WindowsVersion.h"
|
|
#include "nsCoreUtils.h"
|
|
#include "nsWinUtils.h"
|
|
|
|
using namespace mozilla;
|
|
using namespace mozilla::a11y;
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Constructor/destructor
|
|
|
|
RootAccessibleWrap::RootAccessibleWrap(dom::Document* aDocument,
|
|
PresShell* aPresShell)
|
|
: RootAccessible(aDocument, aPresShell), mOuter(&mInternalUnknown) {}
|
|
|
|
RootAccessibleWrap::~RootAccessibleWrap() {}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Aggregated IUnknown
|
|
HRESULT
|
|
RootAccessibleWrap::InternalQueryInterface(REFIID aIid, void** aOutInterface) {
|
|
if (!aOutInterface) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
// InternalQueryInterface should always return its internal unknown
|
|
// when queried for IID_IUnknown...
|
|
if (aIid == IID_IUnknown) {
|
|
RefPtr<IUnknown> punk(&mInternalUnknown);
|
|
punk.forget(aOutInterface);
|
|
return S_OK;
|
|
}
|
|
|
|
// ...Otherwise we pass through to the base COM implementation of
|
|
// QueryInterface which is provided by DocAccessibleWrap.
|
|
return DocAccessibleWrap::QueryInterface(aIid, aOutInterface);
|
|
}
|
|
|
|
ULONG
|
|
RootAccessibleWrap::InternalAddRef() { return DocAccessible::AddRef(); }
|
|
|
|
ULONG
|
|
RootAccessibleWrap::InternalRelease() { return DocAccessible::Release(); }
|
|
|
|
already_AddRefed<IUnknown> RootAccessibleWrap::Aggregate(IUnknown* aOuter) {
|
|
MOZ_ASSERT(mOuter &&
|
|
(mOuter == &mInternalUnknown || mOuter == aOuter || !aOuter));
|
|
if (!aOuter) {
|
|
// If there is no aOuter then we should always set mOuter to
|
|
// mInternalUnknown. This is standard COM aggregation stuff.
|
|
mOuter = &mInternalUnknown;
|
|
return nullptr;
|
|
}
|
|
|
|
mOuter = aOuter;
|
|
return GetInternalUnknown();
|
|
}
|
|
|
|
already_AddRefed<IUnknown> RootAccessibleWrap::GetInternalUnknown() {
|
|
RefPtr<IUnknown> result(&mInternalUnknown);
|
|
return result.forget();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// RootAccessible
|
|
|
|
void RootAccessibleWrap::DocumentActivated(DocAccessible* aDocument) {
|
|
// This check will never work with e10s enabled, in other words, as of
|
|
// Firefox 57.
|
|
if (Compatibility::IsDolphin() &&
|
|
nsCoreUtils::IsTabDocument(aDocument->DocumentNode())) {
|
|
uint32_t count = mChildDocuments.Length();
|
|
for (uint32_t idx = 0; idx < count; idx++) {
|
|
DocAccessible* childDoc = mChildDocuments[idx];
|
|
HWND childDocHWND = static_cast<HWND>(childDoc->GetNativeWindow());
|
|
if (childDoc != aDocument)
|
|
nsWinUtils::HideNativeWindow(childDocHWND);
|
|
else
|
|
nsWinUtils::ShowNativeWindow(childDocHWND);
|
|
}
|
|
}
|
|
}
|
|
|
|
STDMETHODIMP
|
|
RootAccessibleWrap::accNavigate(
|
|
/* [in] */ long navDir,
|
|
/* [optional][in] */ VARIANT varStart,
|
|
/* [retval][out] */ VARIANT __RPC_FAR* pvarEndUpAt) {
|
|
// Special handling for NAVRELATION_EMBEDS.
|
|
// When we only have a single process, this can be handled the same way as
|
|
// any other relation.
|
|
// However, for multi process, the normal relation mechanism doesn't work
|
|
// because it can't handle remote objects.
|
|
if (navDir != NAVRELATION_EMBEDS || varStart.vt != VT_I4 ||
|
|
varStart.lVal != CHILDID_SELF) {
|
|
// We only handle EMBEDS on the root here.
|
|
// Forward to the base implementation.
|
|
return DocAccessibleWrap::accNavigate(navDir, varStart, pvarEndUpAt);
|
|
}
|
|
|
|
if (!pvarEndUpAt) {
|
|
return E_INVALIDARG;
|
|
}
|
|
if (IsDefunct()) {
|
|
return CO_E_OBJNOTCONNECTED;
|
|
}
|
|
|
|
Accessible* target = nullptr;
|
|
// Get the document in the active tab.
|
|
ProxyAccessible* docProxy = GetPrimaryRemoteTopLevelContentDoc();
|
|
if (docProxy) {
|
|
target = WrapperFor(docProxy);
|
|
} else {
|
|
// The base implementation could handle this, but we may as well
|
|
// just handle it here.
|
|
Relation rel = RelationByType(RelationType::EMBEDS);
|
|
target = rel.Next();
|
|
}
|
|
|
|
if (!target) {
|
|
return E_FAIL;
|
|
}
|
|
|
|
VariantInit(pvarEndUpAt);
|
|
pvarEndUpAt->pdispVal = NativeAccessible(target);
|
|
pvarEndUpAt->vt = VT_DISPATCH;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
RootAccessibleWrap::get_accFocus(
|
|
/* [retval][out] */ VARIANT __RPC_FAR* pvarChild) {
|
|
HRESULT hr = DocAccessibleWrap::get_accFocus(pvarChild);
|
|
if (FAILED(hr) || pvarChild->vt != VT_EMPTY || !IsWin8OrLater()) {
|
|
// 1. We got a definite result (either failure or an accessible); or
|
|
// 2. This is Windows 7, where we don't want to retrieve the focus from a
|
|
// remote document because this causes mysterious intermittent crashes
|
|
// when we're called by UIA clients; see bug 1424505.
|
|
return hr;
|
|
}
|
|
|
|
// The base implementation reported no focus.
|
|
// Focus might be in a remote document.
|
|
// (The base implementation can't handle this.)
|
|
// Get the document in the active tab.
|
|
ProxyAccessible* docProxy = GetPrimaryRemoteTopLevelContentDoc();
|
|
if (!docProxy) {
|
|
return hr;
|
|
}
|
|
Accessible* docAcc = WrapperFor(docProxy);
|
|
if (!docAcc) {
|
|
return E_FAIL;
|
|
}
|
|
RefPtr<IDispatch> docDisp = NativeAccessible(docAcc);
|
|
if (!docDisp) {
|
|
return E_FAIL;
|
|
}
|
|
RefPtr<IAccessible> docIa;
|
|
hr = docDisp->QueryInterface(IID_IAccessible, (void**)getter_AddRefs(docIa));
|
|
MOZ_ASSERT(SUCCEEDED(hr));
|
|
MOZ_ASSERT(docIa);
|
|
|
|
// Ask this document for its focused descendant.
|
|
// We return this as is to the client except for CHILDID_SELF (see below).
|
|
hr = docIa->get_accFocus(pvarChild);
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
|
|
if (pvarChild->vt == VT_I4 && pvarChild->lVal == CHILDID_SELF) {
|
|
// The document itself has focus.
|
|
// We're handling a call to accFocus on the root accessible,
|
|
// so replace CHILDID_SELF with the document accessible.
|
|
pvarChild->vt = VT_DISPATCH;
|
|
docDisp.forget(&pvarChild->pdispVal);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|