Bug 1416986 part 2: Include interfaces the client is likely to request in the accessible handler payload. r=aklotz

Now that virtual buffers have to render across processes, we want to eliminate as many cross-process calls as possible.
This includes QueryInterface calls, since buffers query for several interfaces on every node they visit.
To avoid these cross-process QI calls, we include interfaces clients are likely to request in the handler payload.
This way, they get marshaled in the single call used to retrieve the object.

This patch does the following:

1. Passes the interceptor when building the payload.
We need this so we can get interceptors for other interfaces.

2. Splits the payload into two main parts: a static part and a dynamic part.
The (new) static part contains the interface pointers. The dynamic part contains the rest.
This is necessary because the refresh call cannot pass the interceptor, but the interceptor is needed to build the static part.
Also, re-building the static part is pointless when refreshing.

3. Includes the interface pointers in the payload (BuildStaticIA2Data).
The pointers also have to be cleaned up after marshaling.

4. Releases the interface pointers in the handler after the payload is received.
We do this because they're aggregated by the proxy manager as they're unmarshaled.

MozReview-Commit-ID: 6VRLMNScgwW

--HG--
extra : rebase_source : 249589643b7a69e870962ea55a44849bf03a2693
This commit is contained in:
James Teh 2017-11-15 12:18:18 +10:00
Родитель 980e6321f7
Коммит aebc388fb6
4 изменённых файлов: 199 добавлений и 30 удалений

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

@ -21,6 +21,7 @@
#include "mozilla/Move.h"
#include "mozilla/mscom/AgileReference.h"
#include "mozilla/mscom/FastMarshaler.h"
#include "mozilla/mscom/Interceptor.h"
#include "mozilla/mscom/MainThreadInvoker.h"
#include "mozilla/mscom/Ptr.h"
#include "mozilla/mscom/StructStream.h"
@ -97,7 +98,8 @@ HandlerProvider::GetHandler(NotNull<CLSID*> aHandlerClsid)
}
void
HandlerProvider::GetAndSerializePayload(const MutexAutoLock&)
HandlerProvider::GetAndSerializePayload(const MutexAutoLock&,
NotNull<mscom::IInterceptor*> aInterceptor)
{
MOZ_ASSERT(mscom::IsCurrentThreadMTA());
@ -107,10 +109,11 @@ HandlerProvider::GetAndSerializePayload(const MutexAutoLock&)
IA2Payload payload{};
if (!mscom::InvokeOnMainThread("HandlerProvider::BuildIA2Data",
this, &HandlerProvider::BuildIA2Data,
&payload.mData) ||
!payload.mData.mUniqueId) {
if (!mscom::InvokeOnMainThread("HandlerProvider::BuildInitialIA2Data",
this, &HandlerProvider::BuildInitialIA2Data,
aInterceptor,
&payload.mStaticData, &payload.mDynamicData) ||
!payload.mDynamicData.mUniqueId) {
return;
}
@ -124,9 +127,10 @@ HandlerProvider::GetAndSerializePayload(const MutexAutoLock&)
mSerializer = MakeUnique<mscom::StructToStream>(payload, &IA2Payload_Encode);
// Now that we have serialized payload, we should free any BSTRs that were
// allocated in BuildIA2Data.
ClearIA2Data(payload.mData);
// Now that we have serialized payload, we should clean up any
// BSTRs, interfaces, etc. fetched in BuildInitialIA2Data.
CleanupStaticIA2Data(payload.mStaticData);
CleanupDynamicIA2Data(payload.mDynamicData);
}
HRESULT
@ -142,7 +146,7 @@ HandlerProvider::GetHandlerPayloadSize(NotNull<mscom::IInterceptor*> aIntercepto
MutexAutoLock lock(mMutex);
GetAndSerializePayload(lock);
GetAndSerializePayload(lock, aInterceptor);
if (!mSerializer || !(*mSerializer)) {
// Failed payload serialization is non-fatal
@ -182,7 +186,72 @@ private:
};
void
HandlerProvider::BuildIA2Data(IA2Data* aOutIA2Data)
HandlerProvider::BuildStaticIA2Data(
NotNull<mscom::IInterceptor*> aInterceptor,
StaticIA2Data* aOutData)
{
MOZ_ASSERT(aOutData);
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mTargetUnk);
MOZ_ASSERT(IsTargetInterfaceCacheable());
// Include interfaces the client is likely to request.
// This is cheap here and saves multiple cross-process calls later.
// These interfaces must be released in CleanupStaticIA2Data!
// If the target is already an IAccessible2, this pointer is redundant.
// However, the target might be an IAccessibleHyperlink, etc., in which
// case the client will almost certainly QI for IAccessible2.
HRESULT hr = aInterceptor->GetInterceptorForIID(NEWEST_IA2_IID,
(void**)&aOutData->mIA2);
if (FAILED(hr)) {
// IA2 should always be present, so something has
// gone very wrong if this fails.
aOutData->mIA2 = nullptr;
return;
}
// Some of these interfaces aren't present on all accessibles,
// so it's not a failure if these interfaces can't be fetched.
hr = aInterceptor->GetInterceptorForIID(IID_IEnumVARIANT,
(void**)&aOutData->mIEnumVARIANT);
if (FAILED(hr)) {
aOutData->mIEnumVARIANT = nullptr;
}
hr = aInterceptor->GetInterceptorForIID(IID_IAccessibleHypertext2,
(void**)&aOutData->mIAHypertext);
if (FAILED(hr)) {
aOutData->mIAHypertext = nullptr;
}
hr = aInterceptor->GetInterceptorForIID(IID_IAccessibleHyperlink,
(void**)&aOutData->mIAHyperlink);
if (FAILED(hr)) {
aOutData->mIAHyperlink = nullptr;
}
hr = aInterceptor->GetInterceptorForIID(IID_IAccessibleTable,
(void**)&aOutData->mIATable);
if (FAILED(hr)) {
aOutData->mIATable = nullptr;
}
hr = aInterceptor->GetInterceptorForIID(IID_IAccessibleTable2,
(void**)&aOutData->mIATable2);
if (FAILED(hr)) {
aOutData->mIATable2 = nullptr;
}
hr = aInterceptor->GetInterceptorForIID(IID_IAccessibleTableCell,
(void**)&aOutData->mIATableCell);
if (FAILED(hr)) {
aOutData->mIATableCell = nullptr;
}
}
void
HandlerProvider::BuildDynamicIA2Data(DynamicIA2Data* aOutIA2Data)
{
MOZ_ASSERT(aOutIA2Data);
MOZ_ASSERT(NS_IsMainThread());
@ -203,7 +272,7 @@ HandlerProvider::BuildIA2Data(IA2Data* aOutIA2Data)
};
auto cleanup = [this, aOutIA2Data]() -> void {
ClearIA2Data(*aOutIA2Data);
CleanupDynamicIA2Data(*aOutIA2Data);
};
ExecuteWhen<decltype(hasFailed), decltype(cleanup)> onFail(hasFailed, cleanup);
@ -291,10 +360,57 @@ HandlerProvider::BuildIA2Data(IA2Data* aOutIA2Data)
}
void
HandlerProvider::ClearIA2Data(IA2Data& aData)
HandlerProvider::CleanupStaticIA2Data(StaticIA2Data& aData)
{
// When CoMarshalInterface writes interfaces out to a stream, it AddRefs.
// Therefore, we must release our references after this.
if (aData.mIA2) {
aData.mIA2->Release();
}
if (aData.mIEnumVARIANT) {
aData.mIEnumVARIANT->Release();
}
if (aData.mIAHypertext) {
aData.mIAHypertext->Release();
}
if (aData.mIAHyperlink) {
aData.mIAHyperlink->Release();
}
if (aData.mIATable) {
aData.mIATable->Release();
}
if (aData.mIATable2) {
aData.mIATable2->Release();
}
if (aData.mIATableCell) {
aData.mIATableCell->Release();
}
ZeroMemory(&aData, sizeof(StaticIA2Data));
}
void
HandlerProvider::CleanupDynamicIA2Data(DynamicIA2Data& aData)
{
::VariantClear(&aData.mRole);
ZeroMemory(&aData, sizeof(IA2Data));
ZeroMemory(&aData, sizeof(DynamicIA2Data));
}
void
HandlerProvider::BuildInitialIA2Data(
NotNull<mscom::IInterceptor*> aInterceptor,
StaticIA2Data* aOutStaticData,
DynamicIA2Data* aOutDynamicData)
{
BuildStaticIA2Data(aInterceptor, aOutStaticData);
if (!aOutStaticData->mIA2) {
return;
}
BuildDynamicIA2Data(aOutDynamicData);
if (!aOutDynamicData->mUniqueId) {
// Building dynamic data failed, which means building the payload failed.
// However, we've already built static data, so we must clean this up.
CleanupStaticIA2Data(*aOutStaticData);
}
}
bool
@ -407,12 +523,12 @@ HandlerProvider::put_HandlerControl(long aPid, IHandlerControl* aCtrl)
}
HRESULT
HandlerProvider::Refresh(IA2Data* aOutData)
HandlerProvider::Refresh(DynamicIA2Data* aOutData)
{
MOZ_ASSERT(mscom::IsCurrentThreadMTA());
if (!mscom::InvokeOnMainThread("HandlerProvider::BuildIA2Data",
this, &HandlerProvider::BuildIA2Data,
if (!mscom::InvokeOnMainThread("HandlerProvider::BuildDynamicIA2Data",
this, &HandlerProvider::BuildDynamicIA2Data,
aOutData)) {
return E_FAIL;
}

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

@ -54,16 +54,23 @@ public:
// IGeckoBackChannel
STDMETHODIMP put_HandlerControl(long aPid, IHandlerControl* aCtrl) override;
STDMETHODIMP Refresh(IA2Data* aOutData) override;
STDMETHODIMP Refresh(DynamicIA2Data* aOutData) override;
private:
~HandlerProvider() = default;
void SetHandlerControlOnMainThread(DWORD aPid,
mscom::ProxyUniquePtr<IHandlerControl> aCtrl);
void GetAndSerializePayload(const MutexAutoLock&);
void BuildIA2Data(IA2Data* aOutIA2Data);
static void ClearIA2Data(IA2Data& aData);
void GetAndSerializePayload(const MutexAutoLock&,
NotNull<mscom::IInterceptor*> aInterceptor);
void BuildStaticIA2Data(NotNull<mscom::IInterceptor*> aInterceptor,
StaticIA2Data* aOutData);
void BuildDynamicIA2Data(DynamicIA2Data* aOutIA2Data);
void BuildInitialIA2Data(NotNull<mscom::IInterceptor*> aInterceptor,
StaticIA2Data* aOutStaticData,
DynamicIA2Data* aOutDynamicData);
static void CleanupStaticIA2Data(StaticIA2Data& aData);
static void CleanupDynamicIA2Data(DynamicIA2Data& aData);
bool IsTargetInterfaceCacheable();
Atomic<uint32_t> mRefCnt;

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

@ -147,7 +147,7 @@ AccessibleHandler::MaybeUpdateCachedData()
return E_POINTER;
}
return mCachedData.mGeckoBackChannel->Refresh(&mCachedData.mData);
return mCachedData.mGeckoBackChannel->Refresh(&mCachedData.mDynamicData);
}
HRESULT
@ -245,6 +245,34 @@ AccessibleHandler::ReadHandlerPayload(IStream* aStream, REFIID aIid)
return E_FAIL;
}
// These interfaces have been aggregated into the proxy manager.
// The proxy manager will resolve these interfaces now on QI,
// so we can release these pointers.
// Note that if pointers to other objects (in contrast to
// interfaces of *this* object) are added in future, we should not release
// those pointers.
if (mCachedData.mStaticData.mIA2) {
mCachedData.mStaticData.mIA2->Release();
}
if (mCachedData.mStaticData.mIEnumVARIANT) {
mCachedData.mStaticData.mIEnumVARIANT->Release();
}
if (mCachedData.mStaticData.mIAHypertext) {
mCachedData.mStaticData.mIAHypertext->Release();
}
if (mCachedData.mStaticData.mIAHyperlink) {
mCachedData.mStaticData.mIAHyperlink->Release();
}
if (mCachedData.mStaticData.mIATable) {
mCachedData.mStaticData.mIATable->Release();
}
if (mCachedData.mStaticData.mIATable2) {
mCachedData.mStaticData.mIATable2->Release();
}
if (mCachedData.mStaticData.mIATableCell) {
mCachedData.mStaticData.mIATableCell->Release();
}
if (!mCachedData.mGeckoBackChannel) {
return S_OK;
}
@ -411,12 +439,12 @@ CopyBSTR(BSTR aSrc)
#define GET_FIELD(member, assignTo) \
{ \
assignTo = mCachedData.mData.member; \
assignTo = mCachedData.mDynamicData.member; \
}
#define GET_BSTR(member, assignTo) \
{ \
assignTo = CopyBSTR(mCachedData.mData.member); \
assignTo = CopyBSTR(mCachedData.mDynamicData.member); \
}
/*** IAccessible ***/
@ -547,7 +575,7 @@ AccessibleHandler::get_accRole(VARIANT varChild, VARIANT *pvarRole)
}
BEGIN_CACHE_ACCESS;
return ::VariantCopy(pvarRole, &mCachedData.mData.mRole);
return ::VariantCopy(pvarRole, &mCachedData.mDynamicData.mRole);
}
@ -919,7 +947,7 @@ AccessibleHandler::get_uniqueID(long* uniqueID)
}
return mIA2PassThru->get_uniqueID(uniqueID);
}
*uniqueID = mCachedData.mData.mUniqueId;
*uniqueID = mCachedData.mDynamicData.mUniqueId;
return S_OK;
}

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

@ -7,12 +7,29 @@
#include "mozilla-config.h"
#include "AccessibleHandler.h"
import "oaidl.idl";
import "ocidl.idl";
import "ServProv.idl";
import "AccessibleText.idl";
import "Accessible2_3.idl";
import "AccessibleHypertext2.idl";
import "AccessibleHyperlink.idl";
import "AccessibleTable.idl";
import "AccessibleTable2.idl";
import "AccessibleTableCell.idl";
typedef struct _IA2Data
typedef struct _StaticIA2Data
{
NEWEST_IA2_INTERFACE* mIA2;
IEnumVARIANT* mIEnumVARIANT;
IAccessibleHypertext2* mIAHypertext;
IAccessibleHyperlink* mIAHyperlink;
IAccessibleTable* mIATable;
IAccessibleTable2* mIATable2;
IAccessibleTableCell* mIATableCell;
} StaticIA2Data;
typedef struct _DynamicIA2Data
{
VARIANT mRole;
long mState;
@ -32,7 +49,7 @@ typedef struct _IA2Data
BSTR mAttributes;
IA2Locale mIA2Locale;
long mUniqueId;
} IA2Data;
} DynamicIA2Data;
interface IGeckoBackChannel;
@ -100,7 +117,8 @@ interface HandlerData
{
typedef struct _IA2Payload
{
IA2Data mData;
StaticIA2Data mStaticData;
DynamicIA2Data mDynamicData;
IGeckoBackChannel* mGeckoBackChannel;
} IA2Payload;
}
@ -123,7 +141,7 @@ interface IHandlerControl : IUnknown
interface IGeckoBackChannel : IUnknown
{
[propput] HRESULT HandlerControl([in] long aPid, [in] IHandlerControl* aCtrl);
HRESULT Refresh([out] IA2Data* aOutData);
HRESULT Refresh([out] DynamicIA2Data* aOutData);
}
[uuid(1e545f07-f108-4912-9471-546827a80983)]