diff --git a/accessible/base/StyleInfo.cpp b/accessible/base/StyleInfo.cpp index 6720030d32c1..14402c857123 100644 --- a/accessible/base/StyleInfo.cpp +++ b/accessible/base/StyleInfo.cpp @@ -18,9 +18,7 @@ StyleInfo::StyleInfo(dom::Element* aElement, nsIPresShell* aPresShell) : mElement(aElement) { mStyleContext = - nsComputedDOMStyle::GetStyleContextForElementNoFlush(aElement, - nullptr, - aPresShell); + nsComputedDOMStyle::GetStyleContextNoFlush(aElement, nullptr, aPresShell); } void diff --git a/accessible/ipc/IPCTypes.h b/accessible/ipc/IPCTypes.h index 0e77c6348b33..df4e3939d20e 100644 --- a/accessible/ipc/IPCTypes.h +++ b/accessible/ipc/IPCTypes.h @@ -25,6 +25,8 @@ #define __XMLDocument_FWD_DEFINED__ #endif // !defined(__XMLDocument_FWD_DEFINED__) +#include + #include "mozilla/a11y/COMPtrTypes.h" // This define in rpcndr.h messes up our code, so we must undefine it after @@ -39,6 +41,7 @@ namespace mozilla { namespace a11y { typedef uint32_t IAccessibleHolder; +typedef uint32_t IHandlerControlHolder; } // namespace a11y } // namespace mozilla diff --git a/accessible/ipc/win/COMPtrTypes.cpp b/accessible/ipc/win/COMPtrTypes.cpp index 857f4235e471..08ae70f1aa37 100644 --- a/accessible/ipc/win/COMPtrTypes.cpp +++ b/accessible/ipc/win/COMPtrTypes.cpp @@ -6,13 +6,19 @@ #include "mozilla/a11y/COMPtrTypes.h" +#include "Accessible2_3.h" #include "MainThreadUtils.h" #include "mozilla/a11y/Accessible.h" +#include "mozilla/a11y/HandlerProvider.h" #include "mozilla/Move.h" #include "mozilla/mscom/MainThreadHandoff.h" +#include "mozilla/mscom/Utils.h" +#include "mozilla/Preferences.h" #include "mozilla/RefPtr.h" +#include "nsXULAppAPI.h" using mozilla::mscom::MainThreadHandoff; +using mozilla::mscom::ProxyUniquePtr; using mozilla::mscom::STAUniquePtr; namespace mozilla { @@ -26,24 +32,44 @@ CreateHolderFromAccessible(Accessible* aAccToWrap) return nullptr; } - IAccessible* rawNative = nullptr; - aAccToWrap->GetNativeInterface((void**)&rawNative); - MOZ_ASSERT(rawNative); - if (!rawNative) { + STAUniquePtr iaToProxy; + aAccToWrap->GetNativeInterface(mscom::getter_AddRefs(iaToProxy)); + MOZ_ASSERT(iaToProxy); + if (!iaToProxy) { return nullptr; } - STAUniquePtr iaToProxy(rawNative); + static const bool useHandler = + Preferences::GetBool("accessibility.handler.enabled", false); - IAccessible* rawIntercepted = nullptr; - HRESULT hr = MainThreadHandoff::WrapInterface(Move(iaToProxy), &rawIntercepted); + RefPtr payload; + if (useHandler) { + payload = new HandlerProvider(IID_IAccessible, + mscom::ToInterceptorTargetPtr(iaToProxy)); + } + + ProxyUniquePtr intercepted; + HRESULT hr = MainThreadHandoff::WrapInterface(Move(iaToProxy), payload, + (IAccessible**) mscom::getter_AddRefs(intercepted)); MOZ_ASSERT(SUCCEEDED(hr)); if (FAILED(hr)) { return nullptr; } - IAccessibleHolder::COMPtrType iaIntercepted(rawIntercepted); - return IAccessibleHolder(Move(iaIntercepted)); + return IAccessibleHolder(Move(intercepted)); +} + +IHandlerControlHolder +CreateHolderFromHandlerControl(mscom::ProxyUniquePtr aHandlerControl) +{ + MOZ_ASSERT(aHandlerControl); + MOZ_ASSERT(XRE_IsContentProcess()); + MOZ_ASSERT(NS_IsMainThread()); + if (!aHandlerControl) { + return nullptr; + } + + return IHandlerControlHolder(Move(aHandlerControl)); } } // namespace a11y diff --git a/accessible/ipc/win/COMPtrTypes.h b/accessible/ipc/win/COMPtrTypes.h index 122e1ea5ec40..9f8397413548 100644 --- a/accessible/ipc/win/COMPtrTypes.h +++ b/accessible/ipc/win/COMPtrTypes.h @@ -7,6 +7,7 @@ #ifndef mozilla_a11y_COMPtrTypes_h #define mozilla_a11y_COMPtrTypes_h +#include "mozilla/a11y/AccessibleHandler.h" #include "mozilla/mscom/COMPtrHolder.h" #include @@ -21,6 +22,11 @@ class Accessible; IAccessibleHolder CreateHolderFromAccessible(Accessible* aAccToWrap); +typedef mozilla::mscom::COMPtrHolder IHandlerControlHolder; + +IHandlerControlHolder +CreateHolderFromHandlerControl(mscom::ProxyUniquePtr aHandlerControl); + } // namespace a11y } // namespace mozilla diff --git a/accessible/ipc/win/HandlerProvider.cpp b/accessible/ipc/win/HandlerProvider.cpp new file mode 100644 index 000000000000..0c76d5bba1b8 --- /dev/null +++ b/accessible/ipc/win/HandlerProvider.cpp @@ -0,0 +1,263 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#define INITGUID + +#include "mozilla/a11y/HandlerProvider.h" + +#include "Accessible2_3.h" +#include "HandlerData.h" +#include "HandlerData_i.c" +#include "mozilla/Assertions.h" +#include "mozilla/a11y/AccessibleWrap.h" +#include "mozilla/dom/ContentChild.h" +#include "mozilla/Move.h" +#include "mozilla/mscom/AgileReference.h" +#include "mozilla/mscom/MainThreadInvoker.h" +#include "mozilla/mscom/Ptr.h" +#include "mozilla/mscom/StructStream.h" +#include "mozilla/mscom/Utils.h" +#include "nsThreadUtils.h" + +#include + +namespace mozilla { +namespace a11y { + +HandlerProvider::HandlerProvider(REFIID aIid, + mscom::InterceptorTargetPtr aTarget) + : mRefCnt(0) + , mMutex("mozilla::a11y::HandlerProvider::mMutex") + , mTargetUnkIid(aIid) + , mTargetUnk(Move(aTarget)) +{ +} + +HRESULT +HandlerProvider::QueryInterface(REFIID riid, void** ppv) +{ + if (!ppv) { + return E_INVALIDARG; + } + + RefPtr punk; + + if (riid == IID_IUnknown || riid == IID_IGeckoBackChannel) { + punk = static_cast(this); + } + + if (!punk) { + return E_NOINTERFACE; + } + + punk.forget(ppv); + return S_OK; +} + +ULONG +HandlerProvider::AddRef() +{ + return ++mRefCnt; +} + +ULONG +HandlerProvider::Release() +{ + ULONG result = --mRefCnt; + if (!result) { + delete this; + } + return result; +} + +HRESULT +HandlerProvider::GetHandler(NotNull aHandlerClsid) +{ + if (!IsTargetInterfaceCacheable()) { + return E_NOINTERFACE; + } + + *aHandlerClsid = CLSID_AccessibleHandler; + return S_OK; +} + +void +HandlerProvider::GetAndSerializePayload(const MutexAutoLock&) +{ + MOZ_ASSERT(mscom::IsCurrentThreadMTA()); + + if (mSerializer) { + return; + } + + IA2Payload payload{}; + + if (!mscom::InvokeOnMainThread(this, &HandlerProvider::BuildIA2Data, + &payload.mData) || + !payload.mData.mUniqueId) { + return; + } + + // But we set mGeckoBackChannel on the current thread which resides in the + // MTA. This is important to ensure that COM always invokes + // IGeckoBackChannel methods in an MTA background thread. + + RefPtr payloadRef(this); + // AddRef/Release pair for this reference is handled by payloadRef + payload.mGeckoBackChannel = this; + + mSerializer = MakeUnique(payload, &IA2Payload_Encode); + + // Now that we have serialized payload, we should free any BSTRs that were + // allocated in BuildIA2Data. + ClearIA2Data(payload.mData); +} + +HRESULT +HandlerProvider::GetHandlerPayloadSize(NotNull aOutPayloadSize) +{ + MOZ_ASSERT(mscom::IsCurrentThreadMTA()); + + if (!IsTargetInterfaceCacheable()) { + *aOutPayloadSize = mscom::StructToStream::GetEmptySize(); + return S_OK; + } + + MutexAutoLock lock(mMutex); + + GetAndSerializePayload(lock); + + if (!mSerializer) { + return E_FAIL; + } + + *aOutPayloadSize = mSerializer->GetSize(); + return S_OK; +} + +void +HandlerProvider::BuildIA2Data(IA2Data* aOutIA2Data) +{ + MOZ_ASSERT(aOutIA2Data); + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mTargetUnk); + MOZ_ASSERT(IsTargetInterfaceCacheable()); + + RefPtr + target(static_cast(mTargetUnk.get())); + + // NB: get_uniqueID should be the final property retrieved in this method, + // as its presence is used to determine whether the rest of this data + // retrieval was successful. + HRESULT hr = target->get_uniqueID(&aOutIA2Data->mUniqueId); + if (FAILED(hr)) { + ClearIA2Data(*aOutIA2Data); + } +} + +void +HandlerProvider::ClearIA2Data(IA2Data& aData) +{ + ZeroMemory(&aData, sizeof(IA2Data)); +} + +bool +HandlerProvider::IsTargetInterfaceCacheable() +{ + return MarshalAs(mTargetUnkIid) == NEWEST_IA2_IID; +} + +HRESULT +HandlerProvider::WriteHandlerPayload(NotNull aStream) +{ + MutexAutoLock lock(mMutex); + + if (!mSerializer) { + mscom::StructToStream emptyStruct; + return emptyStruct.Write(aStream); + } + + HRESULT hr = mSerializer->Write(aStream); + + mSerializer.reset(); + + return hr; +} + +REFIID +HandlerProvider::MarshalAs(REFIID aIid) +{ + static_assert(&NEWEST_IA2_IID == &IID_IAccessible2_3, + "You have modified NEWEST_IA2_IID. This code needs updating."); + if (aIid == IID_IDispatch || aIid == IID_IAccessible || + aIid == IID_IAccessible2 || aIid == IID_IAccessible2_2 || + aIid == IID_IAccessible2_3) { + // This should always be the newest IA2 interface ID + return NEWEST_IA2_IID; + } + // Otherwise we juse return the identity. + return aIid; +} + +HRESULT +HandlerProvider::NewInstance(REFIID aIid, + mscom::InterceptorTargetPtr aTarget, + NotNull aOutNewPayload) +{ + RefPtr newPayload(new HandlerProvider(aIid, Move(aTarget))); + newPayload.forget(aOutNewPayload.get()); + return S_OK; +} + +void +HandlerProvider::SetHandlerControlOnMainThread(DWORD aPid, + mscom::ProxyUniquePtr aCtrl) +{ + MOZ_ASSERT(NS_IsMainThread()); + + auto content = dom::ContentChild::GetSingleton(); + MOZ_ASSERT(content); + + IHandlerControlHolder holder(CreateHolderFromHandlerControl(Move(aCtrl))); + Unused << content->SendA11yHandlerControl(aPid, holder); +} + +HRESULT +HandlerProvider::put_HandlerControl(long aPid, IHandlerControl* aCtrl) +{ + MOZ_ASSERT(mscom::IsCurrentThreadMTA()); + + if (!aCtrl) { + return E_INVALIDARG; + } + + auto ptrProxy = mscom::ToProxyUniquePtr(aCtrl); + + if (!mscom::InvokeOnMainThread(this, + &HandlerProvider::SetHandlerControlOnMainThread, + static_cast(aPid), Move(ptrProxy))) { + return E_FAIL; + } + + return S_OK; +} + +HRESULT +HandlerProvider::Refresh(IA2Data* aOutData) +{ + MOZ_ASSERT(mscom::IsCurrentThreadMTA()); + + if (!mscom::InvokeOnMainThread(this, &HandlerProvider::BuildIA2Data, + aOutData)) { + return E_FAIL; + } + + return S_OK; +} + +} // namespace a11y +} // namespace mozilla + diff --git a/accessible/ipc/win/HandlerProvider.h b/accessible/ipc/win/HandlerProvider.h new file mode 100644 index 000000000000..0540938ce512 --- /dev/null +++ b/accessible/ipc/win/HandlerProvider.h @@ -0,0 +1,75 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_a11y_HandlerProvider_h +#define mozilla_a11y_HandlerProvider_h + +#include "handler/AccessibleHandler.h" +#include "mozilla/AlreadyAddRefed.h" +#include "mozilla/Atomics.h" +#include "mozilla/mscom/IHandlerProvider.h" +#include "mozilla/mscom/Ptr.h" +#include "mozilla/mscom/StructStream.h" +#include "mozilla/Mutex.h" +#include "mozilla/UniquePtr.h" + +struct NEWEST_IA2_INTERFACE; + +namespace mozilla { + +namespace mscom { + +class StructToStream; + +} // namespace mscom + +namespace a11y { + +class HandlerProvider final : public IGeckoBackChannel + , public mscom::IHandlerProvider +{ +public: + HandlerProvider(REFIID aIid, mscom::InterceptorTargetPtr aTarget); + + // IUnknown + STDMETHODIMP QueryInterface(REFIID riid, void** ppv) override; + STDMETHODIMP_(ULONG) AddRef() override; + STDMETHODIMP_(ULONG) Release() override; + + // IHandlerProvider + STDMETHODIMP GetHandler(NotNull aHandlerClsid) override; + STDMETHODIMP GetHandlerPayloadSize(NotNull aOutPayloadSize) override; + STDMETHODIMP WriteHandlerPayload(NotNull aStream) override; + STDMETHODIMP_(REFIID) MarshalAs(REFIID aIid) override; + STDMETHODIMP NewInstance(REFIID aIid, + mscom::InterceptorTargetPtr aTarget, + NotNull aOutNewPayload) override; + + // IGeckoBackChannel + STDMETHODIMP put_HandlerControl(long aPid, IHandlerControl* aCtrl) override; + STDMETHODIMP Refresh(IA2Data* aOutData) override; + +private: + ~HandlerProvider() = default; + + void SetHandlerControlOnMainThread(DWORD aPid, + mscom::ProxyUniquePtr aCtrl); + void GetAndSerializePayload(const MutexAutoLock&); + void BuildIA2Data(IA2Data* aOutIA2Data); + static void ClearIA2Data(IA2Data& aData); + bool IsTargetInterfaceCacheable(); + + Atomic mRefCnt; + Mutex mMutex; // Protects mSerializer + REFIID mTargetUnkIid; + mscom::InterceptorTargetPtr mTargetUnk; // Constant, main thread only + UniquePtr mSerializer; +}; + +} // namespace a11y +} // namespace mozilla + +#endif // mozilla_a11y_HandlerProvider_h diff --git a/accessible/ipc/win/handler/AccessibleHandler.cpp b/accessible/ipc/win/handler/AccessibleHandler.cpp new file mode 100644 index 000000000000..6de902f6bab2 --- /dev/null +++ b/accessible/ipc/win/handler/AccessibleHandler.cpp @@ -0,0 +1,1008 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#if defined(MOZILLA_INTERNAL_API) +#error This code is NOT for internal Gecko use! +#endif // defined(MOZILLA_INTERNAL_API) + +#define INITGUID + +#include "AccessibleHandler.h" +#include "AccessibleHandlerControl.h" + +#include "Factory.h" +#include "HandlerData.h" +#include "mozilla/ArrayUtils.h" +#include "mozilla/mscom/Registration.h" +#include "mozilla/UniquePtr.h" + +#include +#include +#include + +#include "AccessibleHypertext.h" +#include "Accessible2_i.c" +#include "Accessible2_2_i.c" +#include "Accessible2_3_i.c" + +namespace mozilla { +namespace a11y { + +static mscom::Factory sHandlerFactory; + +HRESULT +AccessibleHandler::Create(IUnknown* aOuter, REFIID aIid, void** aOutInterface) +{ + if (!aOutInterface || !aOuter || aIid != IID_IUnknown) { + return E_INVALIDARG; + } + + *aOutInterface = nullptr; + + HRESULT hr; + RefPtr handler(new AccessibleHandler(aOuter, &hr)); + if (!handler) { + return E_OUTOFMEMORY; + } + if (FAILED(hr)) { + return hr; + } + + return handler->InternalQueryInterface(aIid, aOutInterface); +} + +AccessibleHandler::AccessibleHandler(IUnknown* aOuter, HRESULT* aResult) + : mscom::Handler(aOuter, aResult) + , mDispatch(nullptr) + , mIA2PassThru(nullptr) + , mServProvPassThru(nullptr) + , mCachedData() + , mCacheGen(0) +{ + RefPtr ctl(gControlFactory.GetOrCreateSingleton()); + MOZ_ASSERT(ctl); + if (!ctl) { + if (aResult) { + *aResult = E_UNEXPECTED; + } + return; + } + + mCacheGen = ctl->GetCacheGen(); +} + +AccessibleHandler::~AccessibleHandler() +{ + if (mCachedData.mGeckoBackChannel) { + mCachedData.mGeckoBackChannel->Release(); + } +} + +HRESULT +AccessibleHandler::ResolveIA2() +{ + if (mIA2PassThru) { + return S_OK; + } + + RefPtr proxy(GetProxy()); + if (!proxy) { + return E_UNEXPECTED; + } + + HRESULT hr = proxy->QueryInterface(NEWEST_IA2_IID, + reinterpret_cast(&mIA2PassThru)); + if (SUCCEEDED(hr)) { + // mIA2PassThru is a weak reference (see comments in AccesssibleHandler.h) + mIA2PassThru->Release(); + } + + return hr; +} + +HRESULT +AccessibleHandler::MaybeUpdateCachedData() +{ + RefPtr ctl(gControlFactory.GetOrCreateSingleton()); + if (!ctl) { + return E_OUTOFMEMORY; + } + + uint32_t gen = ctl->GetCacheGen(); + if (gen == mCacheGen) { + return S_OK; + } + + if (!mCachedData.mGeckoBackChannel) { + return E_POINTER; + } + + return mCachedData.mGeckoBackChannel->Refresh(&mCachedData.mData); +} + +HRESULT +AccessibleHandler::ResolveIDispatch() +{ + if (mDispatch) { + return S_OK; + } + + HRESULT hr; + + if (!mDispatchUnk) { + RefPtr ctl(gControlFactory.GetOrCreateSingleton()); + if (!ctl) { + return E_OUTOFMEMORY; + } + + RefPtr typeinfo; + hr = ctl->GetHandlerTypeInfo(getter_AddRefs(typeinfo)); + if (FAILED(hr)) { + return hr; + } + + hr = ::CreateStdDispatch(GetOuter(), static_cast(this), + typeinfo, getter_AddRefs(mDispatchUnk)); + if (FAILED(hr)) { + return hr; + } + } + + hr = mDispatchUnk->QueryInterface(IID_IDispatch, + reinterpret_cast(&mDispatch)); + if (SUCCEEDED(hr)) { + // mDispatch is weak (see comments in AccessibleHandler.h) + mDispatch->Release(); + } + + return hr; +} + +HRESULT +AccessibleHandler::QueryHandlerInterface(IUnknown* aProxyUnknown, REFIID aIid, + void** aOutInterface) +{ + MOZ_ASSERT(aProxyUnknown); + + static_assert(&NEWEST_IA2_IID == &IID_IAccessible2_3, + "You have modified NEWEST_IA2_IID. This code needs updating."); + if (aIid == IID_IDispatch || aIid == IID_IAccessible2_3 || + aIid == IID_IAccessible2_2 || aIid == IID_IAccessible2 || + aIid == IID_IAccessible) { + RefPtr ia2(static_cast(this)); + ia2.forget(aOutInterface); + return S_OK; + } + + if (aIid == IID_IServiceProvider) { + RefPtr svcProv(static_cast(this)); + svcProv.forget(aOutInterface); + return S_OK; + } + + if (aIid == IID_IProvideClassInfo) { + RefPtr clsInfo(this); + clsInfo.forget(aOutInterface); + return S_OK; + } + + return E_NOINTERFACE; +} + +HRESULT +AccessibleHandler::ReadHandlerPayload(IStream* aStream, REFIID aIid) +{ + if (!aStream) { + return E_INVALIDARG; + } + + mscom::StructFromStream deserializer(aStream); + if (!deserializer) { + return E_FAIL; + } + if (deserializer.IsEmpty()) { + return S_FALSE; + } + + if (!deserializer.Read(&mCachedData, &IA2Payload_Decode)) { + return E_FAIL; + } + + if (!mCachedData.mGeckoBackChannel) { + return S_OK; + } + + long pid = static_cast(::GetCurrentProcessId()); + + RefPtr ctl; + HRESULT hr = gControlFactory.CreateInstance(nullptr, IID_IHandlerControl, + getter_AddRefs(ctl)); + if (SUCCEEDED(hr)) { + hr = mCachedData.mGeckoBackChannel->put_HandlerControl(pid, ctl); + MOZ_ASSERT(SUCCEEDED(hr)); + } + + return hr; +} + +REFIID +AccessibleHandler::MarshalAs(REFIID aIid) +{ + static_assert(&NEWEST_IA2_IID == &IID_IAccessible2_3, + "You have modified NEWEST_IA2_IID. This code needs updating."); + if (aIid == IID_IAccessible2_3 || aIid == IID_IAccessible2_2 || + aIid == IID_IAccessible2 || aIid == IID_IAccessible || + aIid == IID_IDispatch) { + return NEWEST_IA2_IID; + } + + return aIid; +} + +HRESULT +AccessibleHandler::GetHandlerPayloadSize(REFIID aIid, DWORD* aOutPayloadSize) +{ + if (!aOutPayloadSize) { + return E_INVALIDARG; + } + + // If we're sending the payload to somebody else, we'd better make sure that + // it is up to date. If the cache update fails then we'll return a 0 payload + // size so that we don't transfer obsolete data. + if (FAILED(MaybeUpdateCachedData())) { + *aOutPayloadSize = mscom::StructToStream::GetEmptySize(); + return S_OK; + } + + mSerializer = MakeUnique(mCachedData, &IA2Payload_Encode); + if (!mSerializer) { + return E_FAIL; + } + + *aOutPayloadSize = mSerializer->GetSize(); + return S_OK; +} + +HRESULT +AccessibleHandler::WriteHandlerPayload(IStream* aStream, REFIID aIid) +{ + if (!aStream) { + return E_INVALIDARG; + } + + if (!mSerializer) { + return E_UNEXPECTED; + } + + HRESULT hr = mSerializer->Write(aStream); + mSerializer.reset(); + return hr; +} + +HRESULT +AccessibleHandler::QueryInterface(REFIID riid, void** ppv) +{ + return Handler::QueryInterface(riid, ppv); +} + +ULONG +AccessibleHandler::AddRef() +{ + return Handler::AddRef(); +} + +ULONG +AccessibleHandler::Release() +{ + return Handler::Release(); +} + +HRESULT +AccessibleHandler::GetTypeInfoCount(UINT *pctinfo) +{ + HRESULT hr = ResolveIDispatch(); + if (FAILED(hr)) { + return hr; + } + + return mDispatch->GetTypeInfoCount(pctinfo); +} + +HRESULT +AccessibleHandler::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) +{ + HRESULT hr = ResolveIDispatch(); + if (FAILED(hr)) { + return hr; + } + + return mDispatch->GetTypeInfo(iTInfo, lcid, ppTInfo); +} + + +HRESULT +AccessibleHandler::GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, + LCID lcid, DISPID *rgDispId) +{ + HRESULT hr = ResolveIDispatch(); + if (FAILED(hr)) { + return hr; + } + + return mDispatch->GetIDsOfNames(riid, rgszNames, cNames, lcid, rgDispId); +} + +HRESULT +AccessibleHandler::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, + WORD wFlags, DISPPARAMS *pDispParams, + VARIANT *pVarResult, EXCEPINFO *pExcepInfo, + UINT *puArgErr) +{ + HRESULT hr = ResolveIDispatch(); + if (FAILED(hr)) { + return hr; + } + + return mDispatch->Invoke(dispIdMember, riid, lcid, wFlags, pDispParams, + pVarResult, pExcepInfo, puArgErr); +} + +#define BEGIN_CACHE_ACCESS \ + { \ + HRESULT hr; \ + if (FAILED(hr = MaybeUpdateCachedData())) { \ + return hr; \ + } \ + } + +static BSTR +CopyBSTR(BSTR aSrc) +{ + return SysAllocStringLen(aSrc, SysStringLen(aSrc)); +} + +HRESULT +AccessibleHandler::get_accParent(IDispatch **ppdispParent) +{ + HRESULT hr = ResolveIA2(); + if (FAILED(hr)) { + return hr; + } + return mIA2PassThru->get_accParent(ppdispParent); +} + +HRESULT +AccessibleHandler::get_accChildCount(long *pcountChildren) +{ + if (!pcountChildren) { + return E_INVALIDARG; + } + HRESULT hr = ResolveIA2(); + if (FAILED(hr)) { + return hr; + } + return mIA2PassThru->get_accChildCount(pcountChildren); +} + +HRESULT +AccessibleHandler::get_accChild(VARIANT varChild, IDispatch **ppdispChild) +{ + if (!ppdispChild) { + return E_INVALIDARG; + } + // Unlikely, but we might as well optimize for it + if (varChild.vt == VT_I4 && varChild.lVal == CHILDID_SELF) { + RefPtr disp(this); + disp.forget(ppdispChild); + return S_OK; + } + HRESULT hr = ResolveIA2(); + if (FAILED(hr)) { + return hr; + } + return mIA2PassThru->get_accChild(varChild, ppdispChild); +} + +HRESULT +AccessibleHandler::get_accName(VARIANT varChild, BSTR *pszName) +{ + if (!pszName) { + return E_INVALIDARG; + } + HRESULT hr = ResolveIA2(); + if (FAILED(hr)) { + return hr; + } + return mIA2PassThru->get_accName(varChild, pszName); +} + +HRESULT +AccessibleHandler::get_accValue(VARIANT varChild, BSTR *pszValue) +{ + if (!pszValue) { + return E_INVALIDARG; + } + HRESULT hr = ResolveIA2(); + if (FAILED(hr)) { + return hr; + } + return mIA2PassThru->get_accValue(varChild, pszValue); +} + +HRESULT +AccessibleHandler::get_accDescription(VARIANT varChild, BSTR *pszDescription) +{ + if (!pszDescription) { + return E_INVALIDARG; + } + HRESULT hr = ResolveIA2(); + if (FAILED(hr)) { + return hr; + } + return mIA2PassThru->get_accDescription(varChild, pszDescription); +} + + +HRESULT +AccessibleHandler::get_accRole(VARIANT varChild, VARIANT *pvarRole) +{ + if (!pvarRole) { + return E_INVALIDARG; + } + HRESULT hr = ResolveIA2(); + if (FAILED(hr)) { + return hr; + } + return mIA2PassThru->get_accRole(varChild, pvarRole); +} + + +HRESULT +AccessibleHandler::get_accState(VARIANT varChild, VARIANT *pvarState) +{ + if (!pvarState) { + return E_INVALIDARG; + } + HRESULT hr = ResolveIA2(); + if (FAILED(hr)) { + return hr; + } + return mIA2PassThru->get_accState(varChild, pvarState); +} + +HRESULT +AccessibleHandler::get_accHelp(VARIANT varChild, BSTR *pszHelp) +{ + // This matches what AccessibleWrap does + if (!pszHelp) { + return E_INVALIDARG; + } + *pszHelp = nullptr; + return S_FALSE; +} + +HRESULT +AccessibleHandler::get_accHelpTopic(BSTR *pszHelpFile, VARIANT varChild, + long *pidTopic) +{ + // This matches what AccessibleWrap does + if (!pszHelpFile || !pidTopic) { + return E_INVALIDARG; + } + *pszHelpFile = nullptr; + *pidTopic = 0; + return S_FALSE; +} + +HRESULT +AccessibleHandler::get_accKeyboardShortcut(VARIANT varChild, + BSTR *pszKeyboardShortcut) +{ + if (!pszKeyboardShortcut) { + return E_INVALIDARG; + } + HRESULT hr = ResolveIA2(); + if (FAILED(hr)) { + return hr; + } + return mIA2PassThru->get_accKeyboardShortcut(varChild, pszKeyboardShortcut); +} + +HRESULT +AccessibleHandler::get_accFocus(VARIANT *pvarChild) +{ + HRESULT hr = ResolveIA2(); + if (FAILED(hr)) { + return hr; + } + return mIA2PassThru->get_accFocus(pvarChild); +} + +HRESULT +AccessibleHandler::get_accSelection(VARIANT *pvarChildren) +{ + HRESULT hr = ResolveIA2(); + if (FAILED(hr)) { + return hr; + } + return mIA2PassThru->get_accSelection(pvarChildren); +} + +HRESULT +AccessibleHandler::get_accDefaultAction(VARIANT varChild, + BSTR *pszDefaultAction) +{ + if (!pszDefaultAction) { + return E_INVALIDARG; + } + HRESULT hr = ResolveIA2(); + if (FAILED(hr)) { + return hr; + } + return mIA2PassThru->get_accDefaultAction(varChild, pszDefaultAction); +} + +HRESULT +AccessibleHandler::accSelect(long flagsSelect, VARIANT varChild) +{ + HRESULT hr = ResolveIA2(); + if (FAILED(hr)) { + return hr; + } + return mIA2PassThru->accSelect(flagsSelect, varChild); +} + +HRESULT +AccessibleHandler::accLocation(long *pxLeft, long *pyTop, long *pcxWidth, + long *pcyHeight, VARIANT varChild) +{ + HRESULT hr = ResolveIA2(); + if (FAILED(hr)) { + return hr; + } + return mIA2PassThru->accLocation(pxLeft, pyTop, pcxWidth, pcyHeight, + varChild); +} + +HRESULT +AccessibleHandler::accNavigate(long navDir, VARIANT varStart, + VARIANT *pvarEndUpAt) +{ + HRESULT hr = ResolveIA2(); + if (FAILED(hr)) { + return hr; + } + return mIA2PassThru->accNavigate(navDir, varStart, pvarEndUpAt); +} + +HRESULT +AccessibleHandler::accHitTest(long xLeft, long yTop, VARIANT *pvarChild) +{ + HRESULT hr = ResolveIA2(); + if (FAILED(hr)) { + return hr; + } + return mIA2PassThru->accHitTest(xLeft, yTop, pvarChild); +} + +HRESULT +AccessibleHandler::accDoDefaultAction(VARIANT varChild) +{ + HRESULT hr = ResolveIA2(); + if (FAILED(hr)) { + return hr; + } + return mIA2PassThru->accDoDefaultAction(varChild); +} + +HRESULT +AccessibleHandler::put_accName(VARIANT varChild, BSTR szName) +{ + // This matches AccessibleWrap + return E_NOTIMPL; +} + +HRESULT +AccessibleHandler::put_accValue(VARIANT varChild, BSTR szValue) +{ + // This matches AccessibleWrap + return E_NOTIMPL; +} + +HRESULT +AccessibleHandler::get_nRelations(long* nRelations) +{ + HRESULT hr = ResolveIA2(); + if (FAILED(hr)) { + return hr; + } + return mIA2PassThru->get_nRelations(nRelations); +} + +HRESULT +AccessibleHandler::get_relation(long relationIndex, + IAccessibleRelation** relation) +{ + HRESULT hr = ResolveIA2(); + if (FAILED(hr)) { + return hr; + } + return mIA2PassThru->get_relation(relationIndex, relation); +} + +HRESULT +AccessibleHandler::get_relations(long maxRelations, + IAccessibleRelation** relations, + long* nRelations) +{ + HRESULT hr = ResolveIA2(); + if (FAILED(hr)) { + return hr; + } + return mIA2PassThru->get_relations(maxRelations, relations, nRelations); +} + +HRESULT +AccessibleHandler::role(long* role) +{ + if (!role) { + return E_INVALIDARG; + } + HRESULT hr = ResolveIA2(); + if (FAILED(hr)) { + return hr; + } + return mIA2PassThru->role(role); +} + +HRESULT +AccessibleHandler::scrollTo(IA2ScrollType scrollType) +{ + HRESULT hr = ResolveIA2(); + if (FAILED(hr)) { + return hr; + } + return mIA2PassThru->scrollTo(scrollType); +} + +HRESULT +AccessibleHandler::scrollToPoint(IA2CoordinateType coordinateType, long x, + long y) +{ + HRESULT hr = ResolveIA2(); + if (FAILED(hr)) { + return hr; + } + return mIA2PassThru->scrollToPoint(coordinateType, x, y); +} + +HRESULT +AccessibleHandler::get_groupPosition(long* groupLevel, long* similarItemsInGroup, + long* positionInGroup) +{ + HRESULT hr = ResolveIA2(); + if (FAILED(hr)) { + return hr; + } + return mIA2PassThru->get_groupPosition(groupLevel, similarItemsInGroup, + positionInGroup); +} + +HRESULT +AccessibleHandler::get_states(AccessibleStates* states) +{ + if (!states) { + return E_INVALIDARG; + } + HRESULT hr = ResolveIA2(); + if (FAILED(hr)) { + return hr; + } + return mIA2PassThru->get_states(states); +} + +HRESULT +AccessibleHandler::get_extendedRole(BSTR* extendedRole) +{ + // This matches ia2Accessible + if (!extendedRole) { + return E_INVALIDARG; + } + *extendedRole = nullptr; + return E_NOTIMPL; +} + +HRESULT +AccessibleHandler::get_localizedExtendedRole(BSTR* localizedExtendedRole) +{ + // This matches ia2Accessible + if (!localizedExtendedRole) { + return E_INVALIDARG; + } + *localizedExtendedRole = nullptr; + return E_NOTIMPL; +} + +HRESULT +AccessibleHandler::get_nExtendedStates(long* nExtendedStates) +{ + // This matches ia2Accessible + if (!nExtendedStates) { + return E_INVALIDARG; + } + *nExtendedStates = 0; + return E_NOTIMPL; +} + +HRESULT +AccessibleHandler::get_extendedStates(long maxExtendedStates, BSTR** extendedStates, + long* nExtendedStates) +{ + // This matches ia2Accessible + if (!extendedStates || !nExtendedStates) { + return E_INVALIDARG; + } + *extendedStates = nullptr; + *nExtendedStates = 0; + return E_NOTIMPL; +} + +HRESULT +AccessibleHandler::get_localizedExtendedStates(long maxLocalizedExtendedStates, + BSTR** localizedExtendedStates, + long* nLocalizedExtendedStates) +{ + // This matches ia2Accessible + if (!localizedExtendedStates || !nLocalizedExtendedStates) { + return E_INVALIDARG; + } + *localizedExtendedStates = nullptr; + *nLocalizedExtendedStates = 0; + return E_NOTIMPL; +} + +HRESULT +AccessibleHandler::get_uniqueID(long* uniqueID) +{ + if (!uniqueID) { + return E_INVALIDARG; + } + if (!HasPayload()) { + HRESULT hr = ResolveIA2(); + if (FAILED(hr)) { + return hr; + } + return mIA2PassThru->get_uniqueID(uniqueID); + } + *uniqueID = mCachedData.mData.mUniqueId; + return S_OK; +} + +HRESULT +AccessibleHandler::get_windowHandle(HWND* windowHandle) +{ + if (!windowHandle) { + return E_INVALIDARG; + } + HRESULT hr = ResolveIA2(); + if (FAILED(hr)) { + return hr; + } + return mIA2PassThru->get_windowHandle(windowHandle); +} + +HRESULT +AccessibleHandler::get_indexInParent(long* indexInParent) +{ + HRESULT hr = ResolveIA2(); + if (FAILED(hr)) { + return hr; + } + return mIA2PassThru->get_indexInParent(indexInParent); +} + +HRESULT +AccessibleHandler::get_locale(IA2Locale* locale) +{ + if (!locale) { + return E_INVALIDARG; + } + HRESULT hr = ResolveIA2(); + if (FAILED(hr)) { + return hr; + } + return mIA2PassThru->get_locale(locale); + return S_OK; +} + +HRESULT +AccessibleHandler::get_attributes(BSTR* attributes) +{ + if (!attributes) { + return E_INVALIDARG; + } + HRESULT hr = ResolveIA2(); + if (FAILED(hr)) { + return hr; + } + return mIA2PassThru->get_attributes(attributes); +} + +HRESULT +AccessibleHandler::get_attribute(BSTR name, VARIANT* attribute) +{ + // We could extract these individually from cached mAttributes. + // Consider it if traffic warrants it + HRESULT hr = ResolveIA2(); + if (FAILED(hr)) { + return hr; + } + return mIA2PassThru->get_attribute(name, attribute); +} + +HRESULT +AccessibleHandler::get_accessibleWithCaret(IUnknown** accessible, + long* caretOffset) +{ + HRESULT hr = ResolveIA2(); + if (FAILED(hr)) { + return hr; + } + return mIA2PassThru->get_accessibleWithCaret(accessible, caretOffset); +} + +HRESULT +AccessibleHandler::get_relationTargetsOfType(BSTR type, long maxTargets, + IUnknown*** targets, + long* nTargets) +{ + HRESULT hr = ResolveIA2(); + if (FAILED(hr)) { + return hr; + } + return mIA2PassThru->get_relationTargetsOfType(type, maxTargets, targets, + nTargets); +} + +HRESULT +AccessibleHandler::get_selectionRanges(IA2Range** ranges, long* nRanges) +{ + HRESULT hr = ResolveIA2(); + if (FAILED(hr)) { + return hr; + } + return mIA2PassThru->get_selectionRanges(ranges, nRanges); +} + +static const GUID kUnsupportedServices[] = { + // Unknown, queried by Windows + {0x33f139ee, 0xe509, 0x47f7, {0xbf, 0x39, 0x83, 0x76, 0x44, 0xf7, 0x45, 0x76}}, + // Unknown, queried by Windows + {0xFDA075CF, 0x7C8B, 0x498C, { 0xB5, 0x14, 0xA9, 0xCB, 0x52, 0x1B, 0xBF, 0xB4 }}, + // Unknown, queried by Windows + {0x8EDAA462, 0x21F4, 0x4C87, { 0xA0, 0x12, 0xB3, 0xCD, 0xA3, 0xAB, 0x01, 0xFC }}, + // Unknown, queried by Windows + {0xacd46652, 0x829d, 0x41cb, { 0xa5, 0xfc, 0x17, 0xac, 0xf4, 0x36, 0x61, 0xac }}, + // Unknown, queried by Windows + {0xb96fdb85, 0x7204, 0x4724, { 0x84, 0x2b, 0xc7, 0x05, 0x9d, 0xed, 0xb9, 0xd0 }} +}; + +HRESULT +AccessibleHandler::QueryService(REFGUID aServiceId, REFIID aIid, + void** aOutInterface) +{ + static_assert(&NEWEST_IA2_IID == &IID_IAccessible2_3, + "You have modified NEWEST_IA2_IID. This code needs updating."); + /* We're taking advantage of the fact that we are implementing IA2 as part + of our own object to implement this just like a QI. */ + if (aIid == IID_IAccessible2_3 || aIid == IID_IAccessible2_2 || + aIid == IID_IAccessible2) { + RefPtr ia2(this); + ia2.forget(aOutInterface); + return S_OK; + } + + for (uint32_t i = 0; i < ArrayLength(kUnsupportedServices); ++i) { + if (aServiceId == kUnsupportedServices[i]) { + return E_NOINTERFACE; + } + } + + if (!mServProvPassThru) { + RefPtr proxy(GetProxy()); + if (!proxy) { + return E_UNEXPECTED; + } + + HRESULT hr = proxy->QueryInterface(IID_IServiceProvider, + reinterpret_cast(&mServProvPassThru)); + if (FAILED(hr)) { + return hr; + } + + // mServProvPassThru is a weak reference (see comments in + // AccessibleHandler.h) + mServProvPassThru->Release(); + } + + return mServProvPassThru->QueryService(aServiceId, aIid, aOutInterface); +} + +HRESULT +AccessibleHandler::GetClassInfo(ITypeInfo** aOutTypeInfo) +{ + RefPtr ctl(gControlFactory.GetOrCreateSingleton()); + if (!ctl) { + return E_OUTOFMEMORY; + } + + return ctl->GetHandlerTypeInfo(aOutTypeInfo); +} + +} // namespace a11y +} // namespace mozilla + +extern "C" HRESULT __stdcall +ProxyDllCanUnloadNow(); + +extern "C" HRESULT __stdcall +DllCanUnloadNow() +{ + return mozilla::mscom::Module::CanUnload() && ProxyDllCanUnloadNow(); +} + +extern "C" HRESULT __stdcall +ProxyDllGetClassObject(REFCLSID aClsid, REFIID aIid, LPVOID* aOutInterface); + +extern "C" HRESULT __stdcall +DllGetClassObject(REFCLSID aClsid, REFIID aIid, LPVOID* aOutInterface) +{ + if (aClsid == CLSID_AccessibleHandler) { + return mozilla::a11y::sHandlerFactory.QueryInterface(aIid, aOutInterface); + } + return ProxyDllGetClassObject(aClsid, aIid, aOutInterface); +} + +extern "C" BOOL WINAPI +ProxyDllMain(HINSTANCE aInstDll, DWORD aReason, LPVOID aReserved); + +BOOL WINAPI +DllMain(HINSTANCE aInstDll, DWORD aReason, LPVOID aReserved) +{ + if (aReason == DLL_PROCESS_ATTACH) { + DisableThreadLibraryCalls((HMODULE)aInstDll); + } + // This is required for ProxyDllRegisterServer to work correctly + return ProxyDllMain(aInstDll, aReason, aReserved); +} + +extern "C" HRESULT __stdcall +ProxyDllRegisterServer(); + +extern "C" HRESULT __stdcall +DllRegisterServer() +{ + HRESULT hr = mozilla::mscom::Handler::Register(CLSID_AccessibleHandler); + if (FAILED(hr)) { + return hr; + } + + return ProxyDllRegisterServer(); +} + +extern "C" HRESULT __stdcall +ProxyDllUnregisterServer(); + +extern "C" HRESULT __stdcall +DllUnregisterServer() +{ + HRESULT hr = mozilla::mscom::Handler::Unregister(CLSID_AccessibleHandler); + if (FAILED(hr)) { + return hr; + } + + return ProxyDllUnregisterServer(); +} diff --git a/accessible/ipc/win/handler/AccessibleHandler.def b/accessible/ipc/win/handler/AccessibleHandler.def new file mode 100644 index 000000000000..3037880feef5 --- /dev/null +++ b/accessible/ipc/win/handler/AccessibleHandler.def @@ -0,0 +1,10 @@ +;+# 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/. + +LIBRARY AccessibleHandler.dll + +EXPORTS DllGetClassObject PRIVATE + DllCanUnloadNow PRIVATE + DllRegisterServer PRIVATE + DllUnregisterServer PRIVATE diff --git a/accessible/ipc/win/handler/AccessibleHandler.h b/accessible/ipc/win/handler/AccessibleHandler.h new file mode 100644 index 000000000000..9171f3191cf2 --- /dev/null +++ b/accessible/ipc/win/handler/AccessibleHandler.h @@ -0,0 +1,195 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_a11y_AccessibleHandler_h +#define mozilla_a11y_AccessibleHandler_h + +#define NEWEST_IA2_BASENAME Accessible2_3 + +#define __QUOTE(idl) #idl +#define __GENIDL(base) __QUOTE(base##.idl) +#define IDLFOR(base) __GENIDL(base) +#define NEWEST_IA2_IDL IDLFOR(NEWEST_IA2_BASENAME) + +#define __GENIFACE(base) I##base +#define INTERFACEFOR(base) __GENIFACE(base) +#define NEWEST_IA2_INTERFACE INTERFACEFOR(NEWEST_IA2_BASENAME) + +#define __GENIID(iface) IID_##iface +#define IIDFOR(iface) __GENIID(iface) +#define NEWEST_IA2_IID IIDFOR(NEWEST_IA2_INTERFACE) + +#if defined(__midl) + +import NEWEST_IA2_IDL; + +#else + +#include "HandlerData.h" + +#include + +#if !defined(MOZILLA_INTERNAL_API) + +#include "Accessible2_3.h" +#include "Handler.h" +#include "mozilla/mscom/StructStream.h" +#include "mozilla/UniquePtr.h" + +#include +#include + +namespace mozilla { +namespace a11y { + +class AccessibleHandler final : public mscom::Handler + , public NEWEST_IA2_INTERFACE + , public IServiceProvider + , public IProvideClassInfo +{ +public: + static HRESULT Create(IUnknown* aOuter, REFIID aIid, void** aOutInterface); + + // mscom::Handler + HRESULT QueryHandlerInterface(IUnknown* aProxyUnknown, REFIID aIid, + void** aOutInterface) override; + HRESULT ReadHandlerPayload(IStream* aStream, REFIID aIid) override; + + REFIID MarshalAs(REFIID aRequestedIid) override; + HRESULT GetHandlerPayloadSize(REFIID aIid, DWORD* aOutPayloadSize) override; + HRESULT WriteHandlerPayload(IStream* aStream, REFIID aIId) override; + + // IUnknown + STDMETHODIMP QueryInterface(REFIID riid, void** ppv) override; + STDMETHODIMP_(ULONG) AddRef() override; + STDMETHODIMP_(ULONG) Release() override; + + // IDispatch + STDMETHODIMP GetTypeInfoCount(UINT *pctinfo) override; + STDMETHODIMP GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) override; + STDMETHODIMP GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, + LCID lcid, DISPID *rgDispId) override; + STDMETHODIMP Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, + DISPPARAMS *pDispParams, VARIANT *pVarResult, + EXCEPINFO *pExcepInfo, UINT *puArgErr) override; + + // IAccessible + STDMETHODIMP get_accParent(IDispatch **ppdispParent) override; + STDMETHODIMP get_accChildCount(long *pcountChildren) override; + STDMETHODIMP get_accChild(VARIANT varChild, IDispatch **ppdispChild) override; + STDMETHODIMP get_accName(VARIANT varChild, BSTR *pszName) override; + STDMETHODIMP get_accValue(VARIANT varChild, BSTR *pszValue) override; + STDMETHODIMP get_accDescription(VARIANT varChild, BSTR *pszDescription) override; + STDMETHODIMP get_accRole(VARIANT varChild, VARIANT *pvarRole) override; + STDMETHODIMP get_accState(VARIANT varChild, VARIANT *pvarState) override; + STDMETHODIMP get_accHelp(VARIANT varChild, BSTR *pszHelp) override; + STDMETHODIMP get_accHelpTopic(BSTR *pszHelpFile, VARIANT varChild, + long *pidTopic) override; + STDMETHODIMP get_accKeyboardShortcut(VARIANT varChild, + BSTR *pszKeyboardShortcut) override; + STDMETHODIMP get_accFocus(VARIANT *pvarChild) override; + STDMETHODIMP get_accSelection(VARIANT *pvarChildren) override; + STDMETHODIMP get_accDefaultAction(VARIANT varChild, + BSTR *pszDefaultAction) override; + STDMETHODIMP accSelect(long flagsSelect, VARIANT varChild) override; + STDMETHODIMP accLocation(long *pxLeft, long *pyTop, long *pcxWidth, + long *pcyHeight, VARIANT varChild) override; + STDMETHODIMP accNavigate(long navDir, VARIANT varStart, + VARIANT *pvarEndUpAt) override; + STDMETHODIMP accHitTest( long xLeft, long yTop, VARIANT *pvarChild) override; + STDMETHODIMP accDoDefaultAction(VARIANT varChild) override; + STDMETHODIMP put_accName(VARIANT varChild, BSTR szName) override; + STDMETHODIMP put_accValue(VARIANT varChild, BSTR szValue) override; + + // IAccessible2 + STDMETHODIMP get_nRelations(long* nRelations) override; + STDMETHODIMP get_relation(long relationIndex, + IAccessibleRelation** relation) override; + STDMETHODIMP get_relations(long maxRelations, IAccessibleRelation** relations, + long* nRelations) override; + STDMETHODIMP role(long* role) override; + STDMETHODIMP scrollTo(IA2ScrollType scrollType) override; + STDMETHODIMP scrollToPoint(IA2CoordinateType coordinateType, long x, + long y) override; + STDMETHODIMP get_groupPosition(long* groupLevel, long* similarItemsInGroup, + long* positionInGroup) override; + STDMETHODIMP get_states(AccessibleStates* states) override; + STDMETHODIMP get_extendedRole(BSTR* extendedRole) override; + STDMETHODIMP get_localizedExtendedRole(BSTR* localizedExtendedRole) override; + STDMETHODIMP get_nExtendedStates(long* nExtendedStates) override; + STDMETHODIMP get_extendedStates(long maxExtendedStates, BSTR** extendedStates, + long* nExtendedStates) override; + STDMETHODIMP get_localizedExtendedStates(long maxLocalizedExtendedStates, + BSTR** localizedExtendedStates, + long* nLocalizedExtendedStates) override; + STDMETHODIMP get_uniqueID(long* uniqueID) override; + STDMETHODIMP get_windowHandle(HWND* windowHandle) override; + STDMETHODIMP get_indexInParent(long* indexInParent) override; + STDMETHODIMP get_locale(IA2Locale* locale) override; + STDMETHODIMP get_attributes(BSTR* attributes) override; + + // IAccessible2_2 + STDMETHODIMP get_attribute(BSTR name, VARIANT* attribute) override; + STDMETHODIMP get_accessibleWithCaret(IUnknown** accessible, + long* caretOffset) override; + STDMETHODIMP get_relationTargetsOfType(BSTR type, long maxTargets, + IUnknown*** targets, + long* nTargets) override; + + // IAccessible2_3 + STDMETHODIMP get_selectionRanges(IA2Range** ranges, long* nRanges) override; + + // IServiceProvider + STDMETHODIMP QueryService(REFGUID aServiceId, REFIID aIid, + void** aOutInterface) override; + + // IProvideClassInfo + STDMETHODIMP GetClassInfo(ITypeInfo** aOutTypeInfo) override; + +private: + AccessibleHandler(IUnknown* aOuter, HRESULT* aResult); + virtual ~AccessibleHandler(); + + HRESULT ResolveIA2(); + HRESULT ResolveIDispatch(); + HRESULT MaybeUpdateCachedData(); + + RefPtr mDispatchUnk; + /** + * Handlers aggregate their proxies. This means that their proxies delegate + * their IUnknown implementation to us. + * + * mDispatchUnk and the result of Handler::GetProxy() are both strong + * references to the aggregated objects. OTOH, any interfaces that are QI'd + * from those aggregated objects have delegated unknowns. + * + * AddRef'ing an interface with a delegated unknown ends up incrementing the + * refcount of the *aggregator*. Since we are the aggregator of mDispatchUnk + * and of the wrapped proxy, holding a strong reference to any interfaces + * QI'd off of those objects would create a reference cycle. + * + * We may hold onto pointers to those references, but when we query them we + * must immediately Release() them to prevent these cycles. + * + * It is safe for us to use these raw pointers because the aggregated + * objects's lifetimes are proper subsets of our own lifetime. + */ + IDispatch* mDispatch; // weak + NEWEST_IA2_INTERFACE* mIA2PassThru; // weak + IServiceProvider* mServProvPassThru; // weak + IA2Payload mCachedData; + UniquePtr mSerializer; + uint32_t mCacheGen; +}; + +} // namespace a11y +} // namespace mozilla + +#endif // !defined(MOZILLA_INTERNAL_API) + +#endif // defined(__midl) + +#endif // mozilla_a11y_AccessibleHandler_h diff --git a/accessible/ipc/win/handler/AccessibleHandler.rc b/accessible/ipc/win/handler/AccessibleHandler.rc new file mode 100644 index 000000000000..3dac2f89d5fb --- /dev/null +++ b/accessible/ipc/win/handler/AccessibleHandler.rc @@ -0,0 +1,5 @@ +/* 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/. */ + +1 typelib HandlerData.tlb diff --git a/accessible/ipc/win/handler/AccessibleHandlerControl.cpp b/accessible/ipc/win/handler/AccessibleHandlerControl.cpp new file mode 100644 index 000000000000..4f9e27fcfcab --- /dev/null +++ b/accessible/ipc/win/handler/AccessibleHandlerControl.cpp @@ -0,0 +1,98 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#if defined(MOZILLA_INTERNAL_API) +#error This code is NOT for internal Gecko use! +#endif // defined(MOZILLA_INTERNAL_API) + +#include "AccessibleHandlerControl.h" + +#include "AccessibleHandler.h" + +#include "AccessibleEventId.h" + +#include "mozilla/Move.h" +#include "mozilla/RefPtr.h" + +namespace mozilla { +namespace a11y { + +mscom::SingletonFactory gControlFactory; + +HRESULT +AccessibleHandlerControl::Create(AccessibleHandlerControl** aOutObject) +{ + if (!aOutObject) { + return E_INVALIDARG; + } + + RefPtr ctl(new AccessibleHandlerControl()); + ctl.forget(aOutObject); + return S_OK; +} + +AccessibleHandlerControl::AccessibleHandlerControl() + : mRefCnt(0) + , mCacheGen(0) + , mIA2Proxy(mscom::RegisterProxy(L"ia2marshal.dll")) + , mHandlerProxy(mscom::RegisterProxy()) +{ + MOZ_ASSERT(mIA2Proxy); +} + +HRESULT +AccessibleHandlerControl::QueryInterface(REFIID aIid, void** aOutInterface) +{ + if (!aOutInterface) { + return E_INVALIDARG; + } + + if (aIid == IID_IUnknown || aIid == IID_IHandlerControl) { + RefPtr ctl(this); + ctl.forget(aOutInterface); + return S_OK; + } + + *aOutInterface = nullptr; + return E_NOINTERFACE; +} + +ULONG +AccessibleHandlerControl::AddRef() +{ + return ++mRefCnt; +} + +ULONG +AccessibleHandlerControl::Release() +{ + ULONG result = --mRefCnt; + if (!result) { + delete this; + } + return result; +} + +HRESULT +AccessibleHandlerControl::Invalidate() +{ + ++mCacheGen; + return S_OK; +} + +HRESULT +AccessibleHandlerControl::GetHandlerTypeInfo(ITypeInfo** aOutTypeInfo) +{ + if (!mHandlerProxy) { + return E_UNEXPECTED; + } + + return mHandlerProxy->GetTypeInfoForGuid(CLSID_AccessibleHandler, + aOutTypeInfo); +} + +} // namespace a11y +} // namespace mozilla diff --git a/accessible/ipc/win/handler/AccessibleHandlerControl.h b/accessible/ipc/win/handler/AccessibleHandlerControl.h new file mode 100644 index 000000000000..3c4b26d80efd --- /dev/null +++ b/accessible/ipc/win/handler/AccessibleHandlerControl.h @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#if defined(MOZILLA_INTERNAL_API) +#error This code is NOT for internal Gecko use! +#endif // defined(MOZILLA_INTERNAL_API) + +#ifndef mozilla_a11y_AccessibleHandlerControl_h +#define mozilla_a11y_AccessibleHandlerControl_h + +#include "Factory.h" +#include "HandlerData.h" +#include "mozilla/mscom/Registration.h" + +namespace mozilla { +namespace a11y { + +class AccessibleHandlerControl final : public IHandlerControl +{ +public: + static HRESULT Create(AccessibleHandlerControl** aOutObject); + + // IUnknown + STDMETHODIMP QueryInterface(REFIID riid, void** ppv) override; + STDMETHODIMP_(ULONG) AddRef() override; + STDMETHODIMP_(ULONG) Release() override; + + // IHandlerControl + STDMETHODIMP Invalidate() override; + + uint32_t GetCacheGen() const + { + return mCacheGen; + } + + HRESULT GetHandlerTypeInfo(ITypeInfo** aOutTypeInfo); + +private: + AccessibleHandlerControl(); + ~AccessibleHandlerControl() = default; + + ULONG mRefCnt; + uint32_t mCacheGen; + UniquePtr mIA2Proxy; + UniquePtr mHandlerProxy; +}; + +extern mscom::SingletonFactory gControlFactory; + +} // namespace a11y +} // namespace mozilla + +#endif // mozilla_a11y_AccessibleHandlerControl_h diff --git a/accessible/ipc/win/handler/HandlerData.acf b/accessible/ipc/win/handler/HandlerData.acf new file mode 100644 index 000000000000..993240b06377 --- /dev/null +++ b/accessible/ipc/win/handler/HandlerData.acf @@ -0,0 +1,11 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +[explicit_handle] +interface HandlerData +{ + typedef [encode,decode] IA2Payload; +} diff --git a/accessible/ipc/win/handler/HandlerData.idl b/accessible/ipc/win/handler/HandlerData.idl new file mode 100644 index 000000000000..24279f14cc1e --- /dev/null +++ b/accessible/ipc/win/handler/HandlerData.idl @@ -0,0 +1,119 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "AccessibleHandler.h" + +import "ocidl.idl"; +import "ServProv.idl"; + +typedef struct _IA2Data +{ + long mUniqueId; +} IA2Data; + +interface IGeckoBackChannel; + +// We define different CLSIDs and IIDs depending on channel and officiality. +// This prevents handlers from installing overtop one another when multiple +// channels are present. Note that we do not do this for all UUIDs in this IDL, +// just the ones that are written to the registry (coclass and interfaces that +// have the [object] annotation) +#if !defined(MOZ_OFFICIAL_BRANDING) + +# if defined(DEBUG) + +// Local debug builds +# define HANDLER_CLSID 398ffd8d-5382-48f7-9e3b-19012762d39a +# define IHANDLERCONTROL_IID a218497e-8b10-460b-b668-a92b7ee39ff2 +# define ASYNCIHANDLERCONTROL_IID ca18b9ab-04b6-41be-87f7-d99913d6a2e8 +# define IGECKOBACKCHANNEL_IID 231c4946-4479-4c8e-aadc-8a0e48fc4c51 + +# else + +// Local non-debug builds +# define HANDLER_CLSID ce573faf-7815-4fc2-a031-b092268ace9e +# define IHANDLERCONTROL_IID 2b715cce-1790-4fe1-aef5-48bb5acdf3a1 +# define ASYNCIHANDLERCONTROL_IID 8e089670-4f57-41a7-89c0-37f17482fa6f +# define IGECKOBACKCHANNEL_IID 18e2488d-310f-400f-8339-0e50b513e801 + +# endif + +#elif defined(NIGHTLY_BUILD) + +// Official Nightly +# define IHANDLERCONTROL_IID c57343fc-e011-40c2-b748-da82eabf0f1f +# define ASYNCIHANDLERCONTROL_IID 648c92a1-ea35-46da-a806-6b55c6247373 +# define HANDLER_CLSID 4629216b-8753-41bf-9527-5bff51401671 +# define IGECKOBACKCHANNEL_IID e61e038d-40dd-464a-9aba-66b206b6911b + +#elif defined(RELEASE_OR_BETA) + +// Official Beta and Official Release +# define IHANDLERCONTROL_IID ce30f77e-8847-44f0-a648-a9656bd89c0d +# define ASYNCIHANDLERCONTROL_IID dca8d857-1a63-4045-8f36-8809eb093d04 +# define HANDLER_CLSID 1baa303d-b4b9-45e5-9ccb-e3fca3e274b6 +# define IGECKOBACKCHANNEL_IID b32983ff-ef84-4945-8f86-fb7491b4f57b + +#else + +// Official Aurora +# define IHANDLERCONTROL_IID 3316ce35-f892-4832-97c5-06c52c03cdba +# define ASYNCIHANDLERCONTROL_IID 15b48b76-ad38-4ad3-bd1a-d3c48a5a9947 +# define HANDLER_CLSID 4a195748-dca2-45fb-9295-0a139e76a9e7 +# define IGECKOBACKCHANNEL_IID dd2e4a89-999e-4d65-8b65-440c923ddb61 + +#endif + +[uuid(2b0e83b3-fd1a-443f-9ed6-c00d39055b58)] +interface HandlerData +{ + typedef struct _IA2Payload + { + IA2Data mData; + IGeckoBackChannel* mGeckoBackChannel; + } IA2Payload; +} + +[object, + uuid(IHANDLERCONTROL_IID), + async_uuid(ASYNCIHANDLERCONTROL_IID), + pointer_default(unique)] +interface IHandlerControl : IUnknown +{ + HRESULT Invalidate(); +} + +[object, + uuid(IGECKOBACKCHANNEL_IID), + pointer_default(unique)] +interface IGeckoBackChannel : IUnknown +{ + [propput] HRESULT HandlerControl([in] long aPid, [in] IHandlerControl* aCtrl); + HRESULT Refresh([out] IA2Data* aOutData); +} + +[uuid(1e545f07-f108-4912-9471-546827a80983)] +library AccessibleHandlerTypeLib +{ + /** + * This definition is required in order for the handler implementation to + * support IDispatch (aka Automation). This is used by interpreted language + * FFIs to discover which interfaces may be controlled via IDispatch. + * (In particular, the python FFI used by NVDA needs this). + * + * In reality, the only a11y interface that is Automation compliant is + * IAccessible; our remaining interfaces are not. + * + * Once the FFI knows that IAccessible is supported, the FFI queries for + * IAccessible and is then able to resolve non-automation interfaces from + * there. + */ + [uuid(HANDLER_CLSID)] + coclass AccessibleHandler + { + [default] interface IAccessible; + }; +}; diff --git a/accessible/ipc/win/handler/Makefile.in b/accessible/ipc/win/handler/Makefile.in new file mode 100644 index 000000000000..27e45e95666b --- /dev/null +++ b/accessible/ipc/win/handler/Makefile.in @@ -0,0 +1,38 @@ +# 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/. + +IA2DIR = $(topsrcdir)/other-licenses/ia2 +MSAADIR = $(topsrcdir)/accessible/interfaces/msaa +GARBAGE += $(MIDL_GENERATED_FILES) midl_done + +MIDL_GENERATED_FILES = \ + dlldata.c \ + HandlerData.h \ + HandlerData_c.c \ + HandlerData_i.c \ + HandlerData_p.c \ + HandlerData.tlb \ + $(NULL) + +export:: $(MIDL_GENERATED_FILES) + +$(MIDL_GENERATED_FILES): midl_done + +midl_done: HandlerData.acf HandlerData.idl + $(MIDL) $(MIDL_FLAGS) -I $(IA2DIR) -I $(MSAADIR) -Oicf -acf $(srcdir)/HandlerData.acf $(srcdir)/HandlerData.idl + touch $@ + +INSTALL_TARGETS += midl +midl_FILES := HandlerData.h \ + HandlerData_i.c \ + $(NULL) +midl_DEST := $(DIST)/include +midl_TARGET := midl + +export:: midl + +register:: + regsvr32 -s $(DIST)/bin/$(SHARED_LIBRARY) + +include $(topsrcdir)/config/rules.mk diff --git a/accessible/ipc/win/handler/moz.build b/accessible/ipc/win/handler/moz.build new file mode 100644 index 000000000000..b4fea56d725f --- /dev/null +++ b/accessible/ipc/win/handler/moz.build @@ -0,0 +1,60 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +SharedLibrary('AccessibleHandler') + +EXPORTS.mozilla.a11y += ['AccessibleHandler.h'] + +LOCAL_INCLUDES += [ + '/accessible/interfaces/ia2', + '/ipc/mscom/oop', +] + +SOURCES += [ + '!dlldata.c', + '!HandlerData_c.c', + '!HandlerData_i.c', + '!HandlerData_p.c', + 'AccessibleHandler.cpp', + 'AccessibleHandlerControl.cpp', +] + +GENERATED_FILES += [ + 'dlldata.c', + 'HandlerData.h', + 'HandlerData.tlb', + 'HandlerData_c.c', + 'HandlerData_i.c', + 'HandlerData_p.c', +] + +DEFFILE = SRCDIR + '/AccessibleHandler.def' + +USE_LIBS += [ + 'mscom_oop', +] + +OS_LIBS += [ + 'rpcrt4', +] + +RCINCLUDE = 'AccessibleHandler.rc' + +# Since we are defining our own COM entry points (DllRegisterServer et al), +# but we still want to be able to delegate some work to the generated code, +# we add the prefix "Proxy" to all of the generated counterparts. +DEFINES['ENTRY_PREFIX'] = 'Proxy' +DEFINES['REGISTER_PROXY_DLL'] = True + +# This DLL may be loaded into other processes, so we need static libs for +# Windows 7 and Windows 8. +USE_STATIC_LIBS = True + +LIBRARY_DEFINES['UNICODE'] = True +LIBRARY_DEFINES['_UNICODE'] = True +LIBRARY_DEFINES['MOZ_NO_MOZALLOC'] = True +DISABLE_STL_WRAPPING = True + diff --git a/accessible/ipc/win/moz.build b/accessible/ipc/win/moz.build index 4bbcec417ccb..9d683b0afa87 100644 --- a/accessible/ipc/win/moz.build +++ b/accessible/ipc/win/moz.build @@ -5,7 +5,10 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. if CONFIG['COMPILE_ENVIRONMENT']: - DIRS += ['typelib'] + DIRS += [ + 'handler', + 'typelib', + ] # With --disable-accessibility, we need to compile PDocAccessible.ipdl (which # also depends on COMPtrTypes.h), but not the C++. @@ -15,13 +18,16 @@ EXPORTS.mozilla.a11y += ['COMPtrTypes.h'] if CONFIG['ACCESSIBILITY']: EXPORTS.mozilla.a11y += [ 'DocAccessibleChild.h', + 'HandlerProvider.h', 'PlatformChild.h', 'ProxyAccessible.h' ] SOURCES += [ + '!./handler/HandlerData_c.c', 'COMPtrTypes.cpp', 'DocAccessibleChild.cpp', + 'HandlerProvider.cpp', 'PlatformChild.cpp', 'ProxyAccessible.cpp', ] diff --git a/accessible/windows/msaa/AccessibleWrap.cpp b/accessible/windows/msaa/AccessibleWrap.cpp index 0f30c0581b5c..e31f1f4ca189 100644 --- a/accessible/windows/msaa/AccessibleWrap.cpp +++ b/accessible/windows/msaa/AccessibleWrap.cpp @@ -62,6 +62,7 @@ static gAccessibles = 0; #endif MsaaIdGenerator AccessibleWrap::sIDGen; +StaticAutoPtr> AccessibleWrap::sHandlerControllers; static const VARIANT kVarChildIdSelf = {VT_I4}; @@ -1606,3 +1607,23 @@ AccessibleWrap::ReleaseContentProcessIdFor(dom::ContentParentId aIPCContentId) { sIDGen.ReleaseContentProcessIDFor(aIPCContentId); } + +/* static */ +void +AccessibleWrap::SetHandlerControl(DWORD aPid, RefPtr aCtrl) +{ + MOZ_ASSERT(XRE_IsParentProcess() && NS_IsMainThread()); + + if (!sHandlerControllers) { + sHandlerControllers = new nsTArray(); + ClearOnShutdown(&sHandlerControllers); + } + + HandlerControllerData ctrlData(aPid, Move(aCtrl)); + if (sHandlerControllers->Contains(ctrlData)) { + return; + } + + sHandlerControllers->AppendElement(Move(ctrlData)); +} + diff --git a/accessible/windows/msaa/AccessibleWrap.h b/accessible/windows/msaa/AccessibleWrap.h index eb97c2667917..5830ec9ff240 100644 --- a/accessible/windows/msaa/AccessibleWrap.h +++ b/accessible/windows/msaa/AccessibleWrap.h @@ -14,9 +14,11 @@ #include "ia2AccessibleComponent.h" #include "ia2AccessibleHyperlink.h" #include "ia2AccessibleValue.h" +#include "mozilla/a11y/AccessibleHandler.h" #include "mozilla/a11y/MsaaIdGenerator.h" #include "mozilla/a11y/ProxyAccessible.h" #include "mozilla/Attributes.h" +#include "mozilla/mscom/Utils.h" #ifdef __GNUC__ // Inheriting from both XPCOM and MSCOM interfaces causes a lot of warnings @@ -188,6 +190,8 @@ public: // construction, destruction static uint32_t GetContentProcessIdFor(dom::ContentParentId aIPCContentId); static void ReleaseContentProcessIdFor(dom::ContentParentId aIPCContentId); + static void SetHandlerControl(DWORD aPid, RefPtr aCtrl); + protected: virtual ~AccessibleWrap(); @@ -242,6 +246,39 @@ protected: NAVRELATION_ERROR = 0x1017, NAVRELATION_ERROR_FOR = 0x1018 }; + + struct HandlerControllerData final + { + HandlerControllerData(DWORD aPid, RefPtr&& aCtrl) + : mPid(aPid) + , mCtrl(Move(aCtrl)) + { + mIsProxy = mozilla::mscom::IsProxy(mCtrl); + } + + HandlerControllerData(HandlerControllerData&& aOther) + : mPid(aOther.mPid) + , mIsProxy(aOther.mIsProxy) + , mCtrl(Move(aOther.mCtrl)) + { + } + + bool operator==(const HandlerControllerData& aOther) const + { + return mPid == aOther.mPid; + } + + bool operator==(const DWORD& aPid) const + { + return mPid == aPid; + } + + DWORD mPid; + bool mIsProxy; + RefPtr mCtrl; + }; + + static StaticAutoPtr> sHandlerControllers; }; static inline AccessibleWrap* diff --git a/browser/base/content/test/webrtc/browser.ini b/browser/base/content/test/webrtc/browser.ini index c5aae9decefd..c4053f8a69d3 100644 --- a/browser/base/content/test/webrtc/browser.ini +++ b/browser/base/content/test/webrtc/browser.ini @@ -10,7 +10,7 @@ skip-if = (os == "linux" && debug) # linux: bug 976544 [browser_devices_get_user_media_anim.js] [browser_devices_get_user_media_in_frame.js] [browser_devices_get_user_media_multi_process.js] -skip-if = (e10s && debug) # bug 1347625 +skip-if = e10s && (asan || debug) # bug 1347625 [browser_devices_get_user_media_screen.js] skip-if = (os == "linux") || (os == "win" && !debug) # bug 1320994 for linux opt, bug 1338038 for windows and linux debug [browser_devices_get_user_media_tear_off_tab.js] diff --git a/devtools/client/inspector/boxmodel/components/BoxModelEditable.js b/devtools/client/inspector/boxmodel/components/BoxModelEditable.js index 9b20cd560338..150e3cd3e075 100644 --- a/devtools/client/inspector/boxmodel/components/BoxModelEditable.js +++ b/devtools/client/inspector/boxmodel/components/BoxModelEditable.js @@ -44,6 +44,7 @@ module.exports = createClass({ let rotate = direction && (direction == "left" || direction == "right") && + box !== "position" && textContent.toString().length > LONG_TEXT_ROTATE_LIMIT; return dom.p( diff --git a/devtools/client/locales/en-US/netmonitor.properties b/devtools/client/locales/en-US/netmonitor.properties index 6bc853783b61..58db274e3d0a 100644 --- a/devtools/client/locales/en-US/netmonitor.properties +++ b/devtools/client/locales/en-US/netmonitor.properties @@ -646,6 +646,14 @@ netmonitor.security.connection=Connection: # in the security tab describing the server certificate section. netmonitor.security.certificate=Certificate: +# LOCALIZATION NOTE (netmonitor.context.copy): This is the label displayed +# for the copy sub-menu in the context menu for a request +netmonitor.context.copy=Copy + +# LOCALIZATION NOTE (netmonitor.context.copy.accesskey): This is the access key +# for the copy sub-menu displayed in the context menu for a request +netmonitor.context.copy.accesskey=C + # LOCALIZATION NOTE (netmonitor.context.copyUrl): This is the label displayed # on the context menu that copies the selected request's url netmonitor.context.copyUrl=Copy URL diff --git a/devtools/client/netmonitor/src/request-list-context-menu.js b/devtools/client/netmonitor/src/request-list-context-menu.js index 73503a2968f2..88f7d02ce801 100644 --- a/devtools/client/netmonitor/src/request-list-context-menu.js +++ b/devtools/client/netmonitor/src/request-list-context-menu.js @@ -50,7 +50,9 @@ RequestListContextMenu.prototype = { let selectedRequest = this.selectedRequest; let menu = new Menu(); - menu.append(new MenuItem({ + let copySubmenu = new Menu(); + + copySubmenu.append(new MenuItem({ id: "request-list-context-copy-url", label: L10N.getStr("netmonitor.context.copyUrl"), accesskey: L10N.getStr("netmonitor.context.copyUrl.accesskey"), @@ -58,7 +60,7 @@ RequestListContextMenu.prototype = { click: () => this.copyUrl(), })); - menu.append(new MenuItem({ + copySubmenu.append(new MenuItem({ id: "request-list-context-copy-url-params", label: L10N.getStr("netmonitor.context.copyUrlParams"), accesskey: L10N.getStr("netmonitor.context.copyUrlParams.accesskey"), @@ -66,7 +68,7 @@ RequestListContextMenu.prototype = { click: () => this.copyUrlParams(), })); - menu.append(new MenuItem({ + copySubmenu.append(new MenuItem({ id: "request-list-context-copy-post-data", label: L10N.getStr("netmonitor.context.copyPostData"), accesskey: L10N.getStr("netmonitor.context.copyPostData.accesskey"), @@ -74,7 +76,7 @@ RequestListContextMenu.prototype = { click: () => this.copyPostData(), })); - menu.append(new MenuItem({ + copySubmenu.append(new MenuItem({ id: "request-list-context-copy-as-curl", label: L10N.getStr("netmonitor.context.copyAsCurl"), accesskey: L10N.getStr("netmonitor.context.copyAsCurl.accesskey"), @@ -82,12 +84,12 @@ RequestListContextMenu.prototype = { click: () => this.copyAsCurl(), })); - menu.append(new MenuItem({ + copySubmenu.append(new MenuItem({ type: "separator", visible: !!selectedRequest, })); - menu.append(new MenuItem({ + copySubmenu.append(new MenuItem({ id: "request-list-context-copy-request-headers", label: L10N.getStr("netmonitor.context.copyRequestHeaders"), accesskey: L10N.getStr("netmonitor.context.copyRequestHeaders.accesskey"), @@ -95,7 +97,7 @@ RequestListContextMenu.prototype = { click: () => this.copyRequestHeaders(), })); - menu.append(new MenuItem({ + copySubmenu.append(new MenuItem({ id: "response-list-context-copy-response-headers", label: L10N.getStr("netmonitor.context.copyResponseHeaders"), accesskey: L10N.getStr("netmonitor.context.copyResponseHeaders.accesskey"), @@ -103,7 +105,7 @@ RequestListContextMenu.prototype = { click: () => this.copyResponseHeaders(), })); - menu.append(new MenuItem({ + copySubmenu.append(new MenuItem({ id: "request-list-context-copy-response", label: L10N.getStr("netmonitor.context.copyResponse"), accesskey: L10N.getStr("netmonitor.context.copyResponse.accesskey"), @@ -114,7 +116,7 @@ RequestListContextMenu.prototype = { click: () => this.copyResponse(), })); - menu.append(new MenuItem({ + copySubmenu.append(new MenuItem({ id: "request-list-context-copy-image-as-data-uri", label: L10N.getStr("netmonitor.context.copyImageAsDataUri"), accesskey: L10N.getStr("netmonitor.context.copyImageAsDataUri.accesskey"), @@ -124,12 +126,12 @@ RequestListContextMenu.prototype = { click: () => this.copyImageAsDataUri(), })); - menu.append(new MenuItem({ + copySubmenu.append(new MenuItem({ type: "separator", visible: !!selectedRequest, })); - menu.append(new MenuItem({ + copySubmenu.append(new MenuItem({ id: "request-list-context-copy-all-as-har", label: L10N.getStr("netmonitor.context.copyAllAsHar"), accesskey: L10N.getStr("netmonitor.context.copyAllAsHar.accesskey"), @@ -137,6 +139,13 @@ RequestListContextMenu.prototype = { click: () => this.copyAllAsHar(), })); + menu.append(new MenuItem({ + label: L10N.getStr("netmonitor.context.copy"), + accesskey: L10N.getStr("netmonitor.context.copy.accesskey"), + visible: !!selectedRequest, + submenu: copySubmenu, + })); + menu.append(new MenuItem({ id: "request-list-context-save-all-as-har", label: L10N.getStr("netmonitor.context.saveAllAsHar"), diff --git a/devtools/client/netmonitor/test/browser_net_curl-utils.js b/devtools/client/netmonitor/test/browser_net_curl-utils.js index bc7bb5bb0934..e667cf514b17 100644 --- a/devtools/client/netmonitor/test/browser_net_curl-utils.js +++ b/devtools/client/netmonitor/test/browser_net_curl-utils.js @@ -41,6 +41,7 @@ add_task(function* () { data = yield createCurlData(requests.post, getLongString); testIsUrlEncodedRequest(data); testWritePostDataTextParams(data); + testWriteEmptyPostDataTextParams(data); testDataArgumentOnGeneratedCommand(data); data = yield createCurlData(requests.multipart, getLongString); @@ -103,6 +104,12 @@ function testWritePostDataTextParams(data) { "Should return a serialized representation of the request parameters"); } +function testWriteEmptyPostDataTextParams(data) { + let params = CurlUtils.writePostDataTextParams(null); + is(params, "", + "Should return a empty string when no parameters provided"); +} + function testDataArgumentOnGeneratedCommand(data) { let curlCommand = Curl.generateCommand(data); ok(curlCommand.includes("--data"), diff --git a/devtools/client/shared/curl.js b/devtools/client/shared/curl.js index bab330807e48..13536ef98a63 100644 --- a/devtools/client/shared/curl.js +++ b/devtools/client/shared/curl.js @@ -202,6 +202,9 @@ const CurlUtils = { * Post data parameters. */ writePostDataTextParams: function (postDataText) { + if (!postDataText) { + return ""; + } let lines = postDataText.split("\r\n"); return lines[lines.length - 1]; }, diff --git a/dom/animation/KeyframeEffect.h b/dom/animation/KeyframeEffect.h index d4329abe5501..c92a75adb1e2 100644 --- a/dom/animation/KeyframeEffect.h +++ b/dom/animation/KeyframeEffect.h @@ -69,7 +69,7 @@ public: // we are in the middle of updating style. If we need to use this when // updating style, we should pass the nsStyleContext into this method and use // that to update the properties rather than calling - // GetStyleContextForElement. + // GetStyleContext. void SetTarget(const Nullable& aTarget); void GetSpacing(nsString& aRetVal, CallerType aCallerType) diff --git a/dom/animation/KeyframeEffectReadOnly.cpp b/dom/animation/KeyframeEffectReadOnly.cpp index 1328c1e29588..d23be755a7c1 100644 --- a/dom/animation/KeyframeEffectReadOnly.cpp +++ b/dom/animation/KeyframeEffectReadOnly.cpp @@ -22,7 +22,7 @@ #include "mozilla/Telemetry.h" #include "mozilla/TypeTraits.h" #include "Layers.h" // For Layer -#include "nsComputedDOMStyle.h" // nsComputedDOMStyle::GetStyleContextForElement +#include "nsComputedDOMStyle.h" // nsComputedDOMStyle::GetStyleContext #include "nsContentUtils.h" // nsContentUtils::ReportToConsole #include "nsCSSPropertyIDSet.h" #include "nsCSSProps.h" // For nsCSSProps::PropHasFlags @@ -1061,9 +1061,7 @@ KeyframeEffectReadOnly::GetTargetStyleContext() ? nsCSSPseudoElements::GetPseudoAtom(mTarget->mPseudoType) : nullptr; - return nsComputedDOMStyle::GetStyleContextForElement(mTarget->mElement, - pseudo, - shell); + return nsComputedDOMStyle::GetStyleContext(mTarget->mElement, pseudo, shell); } #ifdef DEBUG diff --git a/dom/animation/KeyframeEffectReadOnly.h b/dom/animation/KeyframeEffectReadOnly.h index 997652f60f05..7982b0363a55 100644 --- a/dom/animation/KeyframeEffectReadOnly.h +++ b/dom/animation/KeyframeEffectReadOnly.h @@ -342,9 +342,9 @@ protected: // Looks up the style context associated with the target element, if any. // We need to be careful to *not* call this when we are updating the style - // context. That's because calling GetStyleContextForElement when we are in - // the process of building a style context may trigger various forms of - // infinite recursion. + // context. That's because calling GetStyleContext when we are in the process + // of building a style context may trigger various forms of infinite + // recursion. already_AddRefed GetTargetStyleContext(); // A wrapper for marking cascade update according to the current diff --git a/dom/base/DocGroup.h b/dom/base/DocGroup.h index 77b2bcd76728..e3cb24af5798 100644 --- a/dom/base/DocGroup.h +++ b/dom/base/DocGroup.h @@ -93,6 +93,12 @@ public: mTabGroup->ValidateAccess(); } + // Like ValidateAccess, but it returns a bool rather than asserting. + bool AccessAllowed() const + { + return mTabGroup->AccessAllowed(); + } + // Return a pointer that can be continually checked to see if access to this // DocGroup is valid. This pointer should live at least as long as the // DocGroup. diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp index dc497fa5009b..d44aec01e9de 100644 --- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -458,7 +458,7 @@ Element::GetBindingURL(nsIDocument *aDocument, css::URLValue **aResult) // Get the computed -moz-binding directly from the style context RefPtr sc = - nsComputedDOMStyle::GetStyleContextForElementNoFlush(this, nullptr, shell); + nsComputedDOMStyle::GetStyleContextNoFlush(this, nullptr, shell); NS_ENSURE_TRUE(sc, false); NS_IF_ADDREF(*aResult = sc->StyleDisplay()->mBinding); diff --git a/dom/base/StructuredCloneHolder.cpp b/dom/base/StructuredCloneHolder.cpp index b89c9a946e26..6bab1904282f 100644 --- a/dom/base/StructuredCloneHolder.cpp +++ b/dom/base/StructuredCloneHolder.cpp @@ -310,6 +310,7 @@ StructuredCloneHolder::Read(nsISupports* aParent, mBlobImplArray.Clear(); mWasmModuleArray.Clear(); mClonedSurfaces.Clear(); + mInputStreamArray.Clear(); Clear(); } } @@ -1042,6 +1043,45 @@ WriteWasmModule(JSStructuredCloneWriter* aWriter, return false; } +JSObject* +ReadInputStream(JSContext* aCx, + uint32_t aIndex, + StructuredCloneHolder* aHolder) +{ + MOZ_ASSERT(aHolder); + MOZ_ASSERT(aIndex < aHolder->InputStreams().Length()); + nsCOMPtr inputStream = aHolder->InputStreams()[aIndex]; + + JS::RootedValue result(aCx); + nsresult rv = nsContentUtils::WrapNative(aCx, inputStream, + &NS_GET_IID(nsIInputStream), + &result); + if (NS_FAILED(rv)) { + return nullptr; + } + + return &result.toObject(); +} + +bool +WriteInputStream(JSStructuredCloneWriter* aWriter, + nsIInputStream* aInputStream, + StructuredCloneHolder* aHolder) +{ + MOZ_ASSERT(aWriter); + MOZ_ASSERT(aInputStream); + MOZ_ASSERT(aHolder); + + // We store the position of the inputStream in the array as index. + if (JS_WriteUint32Pair(aWriter, SCTAG_DOM_INPUTSTREAM, + aHolder->InputStreams().Length())) { + aHolder->InputStreams().AppendElement(aInputStream); + return true; + } + + return false; +} + } // anonymous namespace JSObject* @@ -1084,6 +1124,10 @@ StructuredCloneHolder::CustomReadHandler(JSContext* aCx, return ReadWasmModule(aCx, aIndex, this); } + if (aTag == SCTAG_DOM_INPUTSTREAM) { + return ReadInputStream(aCx, aIndex, this); + } + return ReadFullySerializableObjects(aCx, aReader, aTag); } @@ -1149,6 +1193,14 @@ StructuredCloneHolder::CustomWriteHandler(JSContext* aCx, return WriteWasmModule(aWriter, module, this); } + { + nsCOMPtr base = xpc::UnwrapReflectorToISupports(aObj); + nsCOMPtr inputStream = do_QueryInterface(base); + if (inputStream) { + return WriteInputStream(aWriter, inputStream, this); + } + } + return WriteFullySerializableObjects(aCx, aWriter, aObj); } diff --git a/dom/base/StructuredCloneHolder.h b/dom/base/StructuredCloneHolder.h index e6953d0f36f8..a21bcea76d71 100644 --- a/dom/base/StructuredCloneHolder.h +++ b/dom/base/StructuredCloneHolder.h @@ -18,6 +18,8 @@ #include "nsIThread.h" #endif +class nsIInputStream; + namespace mozilla { class ErrorResult; namespace layers { @@ -180,7 +182,8 @@ public: { return !mBlobImplArray.IsEmpty() || !mWasmModuleArray.IsEmpty() || - !mClonedSurfaces.IsEmpty(); + !mClonedSurfaces.IsEmpty() || + !mInputStreamArray.IsEmpty(); } nsTArray>& BlobImpls() @@ -195,6 +198,12 @@ public: return mWasmModuleArray; } + nsTArray>& InputStreams() + { + MOZ_ASSERT(mSupportsCloning, "InputStreams cannot be taken/set if cloning is not supported."); + return mInputStreamArray; + } + StructuredCloneScope CloneScope() const { return mStructuredCloneScope; @@ -303,6 +312,9 @@ protected: // Used for cloning JS::WasmModules in the structured cloning algorithm. nsTArray> mWasmModuleArray; + // Used for cloning InputStream in the structured cloning algorithm. + nsTArray> mInputStreamArray; + // This is used for sharing the backend of ImageBitmaps. // The DataSourceSurface object must be thread-safely reference-counted. // The DataSourceSurface object will not be written ever via any ImageBitmap diff --git a/dom/base/StructuredCloneTags.h b/dom/base/StructuredCloneTags.h index a19305311187..d5e8dec74a7b 100644 --- a/dom/base/StructuredCloneTags.h +++ b/dom/base/StructuredCloneTags.h @@ -60,6 +60,8 @@ enum StructuredCloneTags { // This tag is used by both main thread and workers. SCTAG_DOM_URLSEARCHPARAMS, + SCTAG_DOM_INPUTSTREAM, + // When adding a new tag for IDB, please don't add it to the end of the list! // Tags that are supported by IDB must not ever change. See the static assert // in IDBObjectStore.cpp, method CommonStructuredCloneReadCallback. diff --git a/dom/base/WebKitCSSMatrix.cpp b/dom/base/WebKitCSSMatrix.cpp index 2cb6d6bf09c5..35d69824b11b 100644 --- a/dom/base/WebKitCSSMatrix.cpp +++ b/dom/base/WebKitCSSMatrix.cpp @@ -10,6 +10,7 @@ #include "mozilla/dom/WebKitCSSMatrixBinding.h" #include "mozilla/Preferences.h" #include "nsCSSParser.h" +#include "nsPresContext.h" #include "nsStyleTransformMatrix.h" #include "RuleNodeCacheConditions.h" diff --git a/dom/base/nsContentSink.cpp b/dom/base/nsContentSink.cpp index 743293f38f9e..762b49ab8883 100644 --- a/dom/base/nsContentSink.cpp +++ b/dom/base/nsContentSink.cpp @@ -537,7 +537,7 @@ nsContentSink::ProcessLinkHeader(const nsAString& aLinkData) while (ch != kNullCh && ch != kSemicolon && ch != kComma) { ++end; - ch = *end; + ch = *(end + 1); } } } diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index c4c345f8e007..c74ad5fbeab9 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -2444,7 +2444,7 @@ ComputeAnimationValue(nsCSSPropertyID aProperty, } RefPtr styleContext = - nsComputedDOMStyle::GetStyleContextForElement(aElement, nullptr, shell); + nsComputedDOMStyle::GetStyleContext(aElement, nullptr, shell); if (!StyleAnimationValue::ComputeValue(aProperty, aElement, styleContext, aInput, false, aOutput)) { @@ -2734,7 +2734,7 @@ nsDOMWindowUtils::ComputeAnimationDistance(nsIDOMElement* aElement, nsIPresShell* shell = element->GetUncomposedDoc()->GetShell(); RefPtr styleContext = shell - ? nsComputedDOMStyle::GetStyleContextForElement(element, nullptr, shell) + ? nsComputedDOMStyle::GetStyleContext(element, nullptr, shell) : nullptr; if (!StyleAnimationValue::ComputeDistance(property, v1, v2, styleContext, *aResult)) { diff --git a/dom/base/nsFrameMessageManager.cpp b/dom/base/nsFrameMessageManager.cpp index 2707d380103a..ad92f7cb88a8 100644 --- a/dom/base/nsFrameMessageManager.cpp +++ b/dom/base/nsFrameMessageManager.cpp @@ -1717,6 +1717,7 @@ nsMessageManagerScriptExecutor::InitChildGlobalInternal( // Set the location information for the new global, so that tools like // about:memory may use that information. xpc::SetLocationForGlobal(global, aID); + xpc::SetDocGroupValidation(global); DidCreateGlobal(); return true; diff --git a/dom/base/nsPlainTextSerializer.cpp b/dom/base/nsPlainTextSerializer.cpp index ef6bdcac7f05..6613749c2875 100644 --- a/dom/base/nsPlainTextSerializer.cpp +++ b/dom/base/nsPlainTextSerializer.cpp @@ -1816,8 +1816,7 @@ bool nsPlainTextSerializer::IsElementPreformatted(Element* aElement) { RefPtr styleContext = - nsComputedDOMStyle::GetStyleContextForElementNoFlush(aElement, nullptr, - nullptr); + nsComputedDOMStyle::GetStyleContextNoFlush(aElement, nullptr, nullptr); if (styleContext) { const nsStyleText* textStyle = styleContext->StyleText(); return textStyle->WhiteSpaceOrNewlineIsSignificant(); @@ -1830,8 +1829,7 @@ bool nsPlainTextSerializer::IsElementBlock(Element* aElement) { RefPtr styleContext = - nsComputedDOMStyle::GetStyleContextForElementNoFlush(aElement, nullptr, - nullptr); + nsComputedDOMStyle::GetStyleContextNoFlush(aElement, nullptr, nullptr); if (styleContext) { const nsStyleDisplay* displayStyle = styleContext->StyleDisplay(); return displayStyle->IsBlockOutsideStyle(); diff --git a/dom/base/nsRange.cpp b/dom/base/nsRange.cpp index 183fb59a967d..80741082aa02 100644 --- a/dom/base/nsRange.cpp +++ b/dom/base/nsRange.cpp @@ -3467,8 +3467,7 @@ ElementIsVisibleNoFlush(Element* aElement) return false; } RefPtr sc = - nsComputedDOMStyle::GetStyleContextForElementNoFlush(aElement, nullptr, - nullptr); + nsComputedDOMStyle::GetStyleContextNoFlush(aElement, nullptr, nullptr); return sc && sc->StyleVisibility()->IsVisible(); } diff --git a/dom/base/nsXHTMLContentSerializer.cpp b/dom/base/nsXHTMLContentSerializer.cpp index 8d56e5b9155f..ef87b8168611 100644 --- a/dom/base/nsXHTMLContentSerializer.cpp +++ b/dom/base/nsXHTMLContentSerializer.cpp @@ -817,8 +817,8 @@ nsXHTMLContentSerializer::IsElementPreformatted(nsIContent* aNode) return false; } RefPtr styleContext = - nsComputedDOMStyle::GetStyleContextForElementNoFlush(aNode->AsElement(), - nullptr, nullptr); + nsComputedDOMStyle::GetStyleContextNoFlush(aNode->AsElement(), + nullptr, nullptr); if (styleContext) { const nsStyleText* textStyle = styleContext->StyleText(); return textStyle->WhiteSpaceOrNewlineIsSignificant(); diff --git a/dom/base/test/browser.ini b/dom/base/test/browser.ini index c77936616fbf..a76b297729e9 100644 --- a/dom/base/test/browser.ini +++ b/dom/base/test/browser.ini @@ -9,6 +9,7 @@ support-files = file_bug1303838.html file_bug1303838_target.html file_bug1303838_with_iframe.html + file_docgroup_forbid.html file_messagemanager_unload.html file_pluginAudio.html file_use_counter_outer.html @@ -26,6 +27,8 @@ support-files = tags = mcb [browser_bug1011748.js] [browser_bug1058164.js] +[browser_docgroup_forbid.js] +skip-if = !e10s [browser_messagemanager_loadprocessscript.js] [browser_messagemanager_targetframeloader.js] [browser_messagemanager_unload.js] @@ -37,3 +40,4 @@ skip-if = true # Bug 1271028 [browser_bug1307747.js] [browser_timeout_throttling_with_audio_playback.js] [browser_bug1303838.js] +[browser_inputStream_structuredClone.js] diff --git a/dom/base/test/browser_docgroup_forbid.js b/dom/base/test/browser_docgroup_forbid.js new file mode 100644 index 000000000000..21dfa1c537ef --- /dev/null +++ b/dom/base/test/browser_docgroup_forbid.js @@ -0,0 +1,74 @@ +const url = "https://example.com/browser/dom/base/test/file_docgroup_forbid.html"; + +function frameScript() { + let e = Services.ww.getWindowEnumerator(); + let exception = false; + while (e.hasMoreElements()) { + try { + /* + * If this is a window we're not supposed to touch, we'll get an + * error very early here (during the QI). + */ + var window = e.getNext().QueryInterface(Components.interfaces.nsIDOMWindow); + var doc = window.document; + } catch (e) { + if (/accessing object in wrong DocGroup/.test(e.toString())) { + exception = true; + break; + } + throw e; + } + + /* + * Do some stuff that will trigger the DocGroup assertions if we + * didn't throw. + */ + + let elt = doc.createElement("div"); + elt.innerHTML = "hello!"; + doc.body.appendChild(elt); + + let evt = new window.CustomEvent("foopy"); + doc.dispatchEvent(evt); + } + sendAsyncMessage("DocGroupTest:Done", exception); +} + +function promiseMessage(messageManager, message) { + return new Promise(resolve => { + let listener = (msg) => { + messageManager.removeMessageListener(message, listener); + resolve(msg); + }; + + messageManager.addMessageListener(message, listener); + }) +} + +add_task(function*() { + // This pref is normally disabled during tests, but we want to test + // it here, so we enable it. + yield new Promise(go => { + SpecialPowers.pushPrefEnv({set: [["extensions.throw_on_docgroup_mismatch.enabled", true]]}, go) + }); + + let url1 = url + "?tab=1"; + let url2 = url + "?tab=2"; + + let browser1 = gBrowser.selectedBrowser; + + let tab2 = gBrowser.addTab(url2, {sameProcessAsFrameLoader: browser1.frameLoader}); + let browser2 = tab2.linkedBrowser; + yield BrowserTestUtils.browserLoaded(browser2, false, url2); + + browser1.loadURI(url1); + yield BrowserTestUtils.browserLoaded(browser1, false, url1); + + browser1.messageManager.loadFrameScript(`data:,(${frameScript})();`, false); + + let exception = yield promiseMessage(browser1.messageManager, "DocGroupTest:Done"); + + ok(exception, "Touching two windows threw an exception (that's good!)"); + + gBrowser.removeTab(tab2); +}); diff --git a/dom/base/test/browser_inputStream_structuredClone.js b/dom/base/test/browser_inputStream_structuredClone.js new file mode 100644 index 000000000000..dc9d96909fd4 --- /dev/null +++ b/dom/base/test/browser_inputStream_structuredClone.js @@ -0,0 +1,56 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +const { classes: Cc, interfaces: Ci } = Components; +const URIs = [ + "about:about", + "http://example.com/browser/dom/base/test/empty.html" +]; + +function* runTest(input, url) { + let tab = gBrowser.addTab(url); + let browser = gBrowser.getBrowserForTab(tab); + + yield BrowserTestUtils.browserLoaded(browser); + + let stream = Cc['@mozilla.org/io/string-input-stream;1'] + .createInstance(Ci.nsIStringInputStream); + stream.setData(input, input.length); + + let data = { + inputStream: stream + }; + + is(data.inputStream.available(), input.length, "The length of the inputStream matches: " + input.length); + + let dataBack = yield ContentTask.spawn(browser, data, function(data) { + let dataBack = { + inputStream: data.inputStream, + check: true, + }; + + if (content.location.href.startsWith('about:')) { + dataBack.check = data.inputStream instanceof content.Components.interfaces.nsIInputStream; + } + + return dataBack; + }); + + ok(dataBack.check, "The inputStream is a nsIInputStream also on content."); + ok(data.inputStream instanceof Ci.nsIInputStream, "The original object was an inputStream"); + ok(dataBack.inputStream instanceof Ci.nsIInputStream, "We have an inputStream back from the content."); + + yield BrowserTestUtils.removeTab(tab); +} + +add_task(function* test() { + let a = "a"; + for (let i = 0; i < 25; ++i) { + a+=a; + } + + for (let i = 0; i < URIs.length; ++i) { + yield runTest("Hello world", URIs[i]); + yield runTest(a, URIs[i]); + } +}); diff --git a/dom/base/test/file_docgroup_forbid.html b/dom/base/test/file_docgroup_forbid.html new file mode 100644 index 000000000000..644cf5d88504 --- /dev/null +++ b/dom/base/test/file_docgroup_forbid.html @@ -0,0 +1,7 @@ + + + + + diff --git a/dom/base/test/mochitest.ini b/dom/base/test/mochitest.ini index 611da9b081f8..6725abf1f4c8 100644 --- a/dom/base/test/mochitest.ini +++ b/dom/base/test/mochitest.ini @@ -653,7 +653,7 @@ skip-if = (toolkit == 'android') # Android: Bug 775227 [test_innersize_scrollport.html] [test_integer_attr_with_leading_zero.html] [test_intersectionobservers.html] -skip-if = true # Track Bug 1320704 +skip-if = (os == "android") # Timing issues [test_link_prefetch.html] skip-if = !e10s # Track Bug 1281415 [test_link_stylesheet.html] diff --git a/dom/bindings/parser/WebIDL.py b/dom/bindings/parser/WebIDL.py index f51f4235011b..e71af4eba38c 100644 --- a/dom/bindings/parser/WebIDL.py +++ b/dom/bindings/parser/WebIDL.py @@ -1555,10 +1555,8 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins): def hasMembersInSlots(self): return self._ownMembersInSlots != 0 - conditionExtendedAttributes = [ "Pref", "ChromeOnly", "Func", "AvailableIn", - "SecureContext", - "CheckAnyPermissions", - "CheckAllPermissions" ] + conditionExtendedAttributes = [ "Pref", "ChromeOnly", "Func", + "SecureContext" ] def isExposedConditionally(self, exclusions=[]): return any(((not a in exclusions) and self.getExtendedAttribute(a)) for a in self.conditionExtendedAttributes) diff --git a/dom/broadcastchannel/PBroadcastChannel.ipdl b/dom/broadcastchannel/PBroadcastChannel.ipdl index 4b4a53106761..f68a2b5e1155 100644 --- a/dom/broadcastchannel/PBroadcastChannel.ipdl +++ b/dom/broadcastchannel/PBroadcastChannel.ipdl @@ -4,6 +4,9 @@ include protocol PBackground; include protocol PBlob; +include protocol PChildToParentStream; +include protocol PFileDescriptorSet; +include protocol PParentToChildStream; include DOMTypes; using struct mozilla::SerializedStructuredCloneBuffer from "ipc/IPCMessageUtils.h"; diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp index 117d23d9ad36..24387ef3012e 100644 --- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -1146,8 +1146,9 @@ CanvasRenderingContext2D::ParseColor(const nsAString& aString, RefPtr parentContext; if (mCanvasElement && mCanvasElement->IsInUncomposedDoc()) { // Inherit from the canvas element. - parentContext = nsComputedDOMStyle::GetStyleContextForElement( - mCanvasElement, nullptr, presShell); + parentContext = nsComputedDOMStyle::GetStyleContext(mCanvasElement, + nullptr, + presShell); } Unused << nsRuleNode::ComputeColor( @@ -1939,9 +1940,7 @@ CanvasRenderingContext2D::ClearTarget() nsCOMPtr presShell = GetPresShell(); if (presShell) { canvasStyle = - nsComputedDOMStyle::GetStyleContextForElement(mCanvasElement, - nullptr, - presShell); + nsComputedDOMStyle::GetStyleContext(mCanvasElement, nullptr, presShell); if (canvasStyle) { WritingMode wm(canvasStyle); if (wm.IsVertical() && !wm.IsSideways()) { @@ -2649,8 +2648,7 @@ GetFontParentStyleContext(Element* aElement, nsIPresShell* aPresShell, if (aElement && aElement->IsInUncomposedDoc()) { // Inherit from the canvas element. RefPtr result = - nsComputedDOMStyle::GetStyleContextForElement(aElement, nullptr, - aPresShell); + nsComputedDOMStyle::GetStyleContext(aElement, nullptr, aPresShell); if (!result) { aError.Throw(NS_ERROR_FAILURE); return nullptr; @@ -4202,9 +4200,7 @@ CanvasRenderingContext2D::DrawOrMeasureText(const nsAString& aRawText, if (mCanvasElement && mCanvasElement->IsInUncomposedDoc()) { // try to find the closest context canvasStyle = - nsComputedDOMStyle::GetStyleContextForElement(mCanvasElement, - nullptr, - presShell); + nsComputedDOMStyle::GetStyleContext(mCanvasElement, nullptr, presShell); if (!canvasStyle) { return NS_ERROR_FAILURE; } diff --git a/dom/html/nsGenericHTMLElement.cpp b/dom/html/nsGenericHTMLElement.cpp index 8561f273b9e5..423912a55e1c 100644 --- a/dom/html/nsGenericHTMLElement.cpp +++ b/dom/html/nsGenericHTMLElement.cpp @@ -2950,8 +2950,8 @@ IsOrHasAncestorWithDisplayNone(Element* aElement, nsIPresShell* aPresShell) if (sc) { sc = styleSet->ResolveStyleFor(elementsToCheck[i], sc, LazyComputeBehavior::Assert); } else { - sc = nsComputedDOMStyle::GetStyleContextForElementNoFlush(elementsToCheck[i], - nullptr, aPresShell); + sc = nsComputedDOMStyle::GetStyleContextNoFlush(elementsToCheck[i], + nullptr, aPresShell); } if (sc->StyleDisplay()->mDisplay == StyleDisplay::None) { return true; diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index 90faec5cfc79..659a7105305f 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -172,6 +172,9 @@ #ifdef ACCESSIBILITY #include "nsAccessibilityService.h" +#ifdef XP_WIN +#include "mozilla/a11y/AccessibleWrap.h" +#endif #endif #include "mozilla/dom/File.h" diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 281c608a25dd..0c07c3451477 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -4925,6 +4925,20 @@ ContentParent::RecvGetA11yContentId(uint32_t* aContentId) #endif } +mozilla::ipc::IPCResult +ContentParent::RecvA11yHandlerControl(const uint32_t& aPid, + const IHandlerControlHolder& aHandlerControl) +{ +#if defined(XP_WIN32) && defined(ACCESSIBILITY) + MOZ_ASSERT(!aHandlerControl.IsNull()); + RefPtr proxy(aHandlerControl.Get()); + a11y::AccessibleWrap::SetHandlerControl(aPid, Move(proxy)); + return IPC_OK(); +#else + return IPC_FAIL_NO_REASON(this); +#endif +} + } // namespace dom } // namespace mozilla diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index 9c2eb2801f40..4e7d12019541 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -599,6 +599,10 @@ public: virtual mozilla::ipc::IPCResult RecvGetA11yContentId(uint32_t* aContentId) override; + virtual mozilla::ipc::IPCResult + RecvA11yHandlerControl(const uint32_t& aPid, + const IHandlerControlHolder& aHandlerControl) override; + virtual int32_t Pid() const override; virtual PURLClassifierParent* diff --git a/dom/ipc/DOMTypes.ipdlh b/dom/ipc/DOMTypes.ipdlh index 48ca4e932baa..41e8624c2820 100644 --- a/dom/ipc/DOMTypes.ipdlh +++ b/dom/ipc/DOMTypes.ipdlh @@ -6,6 +6,8 @@ include protocol PBlob; include protocol PMemoryStream; + +include IPCStream; include ProtocolTypes; using struct mozilla::void_t @@ -40,6 +42,7 @@ struct ClonedMessageData { SerializedStructuredCloneBuffer data; PBlob[] blobs; + IPCStream[] inputStreams; MessagePortIdentifier[] identfiers; }; diff --git a/dom/ipc/PBrowser.ipdl b/dom/ipc/PBrowser.ipdl index b608d65b7600..525ddb45ed9d 100644 --- a/dom/ipc/PBrowser.ipdl +++ b/dom/ipc/PBrowser.ipdl @@ -16,6 +16,9 @@ include protocol PIndexedDBPermissionRequest; include protocol PRenderFrame; include protocol PPluginWidget; include protocol PRemotePrintJob; +include protocol PChildToParentStream; +include protocol PParentToChildStream; +include protocol PFileDescriptorSet; include DOMTypes; include JavaScriptTypes; include URIParams; diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index 21e183f9db32..6d72b6b2dcbd 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -73,6 +73,7 @@ using base::ProcessId from "base/process.h"; using struct IPC::Permission from "mozilla/net/NeckoMessageUtils.h"; using class IPC::Principal from "mozilla/dom/PermissionMessageUtils.h"; using struct mozilla::null_t from "ipc/IPCMessageUtils.h"; +using mozilla::a11y::IHandlerControlHolder from "mozilla/a11y/IPCTypes.h"; using mozilla::dom::NativeThreadId from "mozilla/dom/TabMessageUtils.h"; using mozilla::hal::ProcessPriority from "mozilla/HalTypes.h"; using mozilla::gfx::IntSize from "mozilla/gfx/2D.h"; @@ -1083,6 +1084,8 @@ parent: async RecordChildEvents(ChildEventData[] events); sync GetA11yContentId() returns (uint32_t aContentId); + async A11yHandlerControl(uint32_t aPid, + IHandlerControlHolder aHandlerControl); async AddMemoryReport(MemoryReport aReport); async FinishMemoryReport(uint32_t aGeneration); diff --git a/dom/ipc/StructuredCloneData.cpp b/dom/ipc/StructuredCloneData.cpp index 13ca391183a3..3bbad9a40f65 100644 --- a/dom/ipc/StructuredCloneData.cpp +++ b/dom/ipc/StructuredCloneData.cpp @@ -14,7 +14,7 @@ #include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/BlobBinding.h" #include "mozilla/dom/File.h" -#include "mozilla/dom/ToJSValue.h" +#include "mozilla/ipc/IPCStreamUtils.h" #include "nsContentUtils.h" #include "nsJSEnvironment.h" #include "MainThreadUtils.h" @@ -25,6 +25,37 @@ namespace mozilla { namespace dom { namespace ipc { +StructuredCloneData::StructuredCloneData() + : StructuredCloneData(StructuredCloneHolder::TransferringSupported) +{} + +StructuredCloneData::StructuredCloneData(StructuredCloneData&& aOther) + : StructuredCloneData(StructuredCloneHolder::TransferringSupported) +{ + *this = Move(aOther); +} + +StructuredCloneData::StructuredCloneData(TransferringSupport aSupportsTransferring) + : StructuredCloneHolder(StructuredCloneHolder::CloningSupported, + aSupportsTransferring, + StructuredCloneHolder::StructuredCloneScope::DifferentProcess) + , mInitialized(false) +{} + +StructuredCloneData::~StructuredCloneData() +{} + +StructuredCloneData& +StructuredCloneData::operator=(StructuredCloneData&& aOther) +{ + mExternalData = Move(aOther.mExternalData); + mSharedData = Move(aOther.mSharedData); + mIPCStreams = Move(aOther.mIPCStreams); + mInitialized = aOther.mInitialized; + + return *this; +} + bool StructuredCloneData::Copy(const StructuredCloneData& aData) { @@ -50,6 +81,9 @@ StructuredCloneData::Copy(const StructuredCloneData& aData) MOZ_ASSERT(GetSurfaces().IsEmpty()); MOZ_ASSERT(WasmModules().IsEmpty()); + MOZ_ASSERT(InputStreams().IsEmpty()); + InputStreams().AppendElements(aData.InputStreams()); + mInitialized = true; return true; @@ -224,6 +258,26 @@ BuildClonedMessageData(typename ParentManagerTraits::Conc blobList.AppendElement(protocolActor); } } + + const nsTArray>& inputStreams = aData.InputStreams(); + + if (!inputStreams.IsEmpty()) { + InfallibleTArray& streams = aClonedData.inputStreams(); + uint32_t length = inputStreams.Length(); + streams.SetCapacity(length); + for (uint32_t i = 0; i < length; ++i) { + AutoIPCStream* stream = aData.IPCStreams().AppendElement(fallible); + if (NS_WARN_IF(!stream)) { + return false; + } + + if (!stream->Serialize(inputStreams[i], aManager)) { + return false; + } + streams.AppendElement(stream->TakeValue()); + } + } + return true; } @@ -343,6 +397,16 @@ UnpackClonedMessageData(typename MemoryTraits::ClonedMessageType& aData.BlobImpls().AppendElement(blobImpl); } } + + const InfallibleTArray& streams = aClonedData.inputStreams(); + if (!streams.IsEmpty()) { + uint32_t length = streams.Length(); + aData.InputStreams().SetCapacity(length); + for (uint32_t i = 0; i < length; ++i) { + nsCOMPtr stream = DeserializeIPCStream(streams[i]); + aData.InputStreams().AppendElement(stream); + } + } } void diff --git a/dom/ipc/StructuredCloneData.h b/dom/ipc/StructuredCloneData.h index 847f8bd2083b..06a8af23ee35 100644 --- a/dom/ipc/StructuredCloneData.h +++ b/dom/ipc/StructuredCloneData.h @@ -11,6 +11,7 @@ #include "mozilla/RefPtr.h" #include "mozilla/dom/StructuredCloneHolder.h" #include "nsISupportsImpl.h" +#include "nsIInputStream.h" namespace IPC { class Message; @@ -20,6 +21,7 @@ class PickleIterator; namespace mozilla { namespace ipc { +class AutoIPCStream; class PBackgroundChild; class PBackgroundParent; @@ -150,22 +152,19 @@ private: class StructuredCloneData : public StructuredCloneHolder { public: - StructuredCloneData() - : StructuredCloneData(StructuredCloneHolder::TransferringSupported) - {} + StructuredCloneData(); StructuredCloneData(const StructuredCloneData&) = delete; - StructuredCloneData(StructuredCloneData&& aOther) = default; + StructuredCloneData(StructuredCloneData&& aOther); - ~StructuredCloneData() - {} + ~StructuredCloneData(); StructuredCloneData& operator=(const StructuredCloneData& aOther) = delete; StructuredCloneData& - operator=(StructuredCloneData&& aOther) = default; + operator=(StructuredCloneData&& aOther); const nsTArray>& BlobImpls() const { @@ -177,6 +176,16 @@ public: return mBlobImplArray; } + const nsTArray>& InputStreams() const + { + return mInputStreamArray; + } + + nsTArray>& InputStreams() + { + return mInputStreamArray; + } + bool Copy(const StructuredCloneData& aData); void Read(JSContext* aCx, @@ -276,29 +285,33 @@ public: return mSupportsTransferring; } + FallibleTArray& IPCStreams() + { + return mIPCStreams; + } + // For IPC serialization void WriteIPCParams(IPC::Message* aMessage) const; bool ReadIPCParams(const IPC::Message* aMessage, PickleIterator* aIter); protected: - explicit StructuredCloneData(TransferringSupport aSupportsTransferring) - : StructuredCloneHolder(StructuredCloneHolder::CloningSupported, - aSupportsTransferring, - StructuredCloneHolder::StructuredCloneScope::DifferentProcess) - , mInitialized(false) - {} - + explicit StructuredCloneData(TransferringSupport aSupportsTransferring); private: JSStructuredCloneData mExternalData; RefPtr mSharedData; + + // This array is needed because AutoIPCStream DTOR must be executed after the + // sending of the data via IPC. This will be fixed by bug 1353475. + FallibleTArray mIPCStreams; bool mInitialized; }; /** * For use when transferring should not be supported. */ -class StructuredCloneDataNoTransfers : public StructuredCloneData { +class StructuredCloneDataNoTransfers : public StructuredCloneData +{ public: StructuredCloneDataNoTransfers() : StructuredCloneData(StructuredCloneHolder::TransferringNotSupported) diff --git a/dom/media/test/crashtests/buffer-source-slow-resampling-1.html b/dom/media/test/crashtests/buffer-source-slow-resampling-1.html new file mode 100644 index 000000000000..5d8a50442b1a --- /dev/null +++ b/dom/media/test/crashtests/buffer-source-slow-resampling-1.html @@ -0,0 +1,34 @@ + + + diff --git a/dom/media/test/crashtests/crashtests.list b/dom/media/test/crashtests/crashtests.list index 1df0c61e2c6e..2e9d362f712b 100644 --- a/dom/media/test/crashtests/crashtests.list +++ b/dom/media/test/crashtests/crashtests.list @@ -92,6 +92,7 @@ load audiocontext-double-suspend.html load buffer-source-duration-1.html load buffer-source-ended-1.html load buffer-source-resampling-start-1.html +load buffer-source-slow-resampling-1.html load doppler-1.html HTTP load media-element-source-seek-1.html load offline-buffer-source-ended-1.html diff --git a/dom/media/webaudio/WebAudioUtils.h b/dom/media/webaudio/WebAudioUtils.h index 868ccbea8f7f..3754a1569f1e 100644 --- a/dom/media/webaudio/WebAudioUtils.h +++ b/dom/media/webaudio/WebAudioUtils.h @@ -190,13 +190,15 @@ namespace WebAudioUtils { MOZ_CRASH("We should never see a NaN here"); } - if (f > FloatType(numeric_limits::max())) { - // If the floating point value is outside of the range of maximum - // integral value for this type, just clamp to the maximum value. + // If the floating point value is outside of the range of maximum + // integral value for this type, just clamp to the maximum value. + // The equality case must also return max() due to loss of precision when + // converting max() to float. + if (f >= FloatType(numeric_limits::max())) { return numeric_limits::max(); } - if (f < FloatType(numeric_limits::min())) { + if (f <= FloatType(numeric_limits::min())) { // If the floating point value is outside of the range of minimum // integral value for this type, just clamp to the minimum value. return numeric_limits::min(); diff --git a/dom/messagechannel/PMessagePort.ipdl b/dom/messagechannel/PMessagePort.ipdl index b4a8a8980361..09f56ed76b9a 100644 --- a/dom/messagechannel/PMessagePort.ipdl +++ b/dom/messagechannel/PMessagePort.ipdl @@ -3,6 +3,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ include protocol PBackground; +include protocol PChildToParentStream; +include protocol PFileDescriptorSet; +include protocol PParentToChildStream; include protocol PBlob; include DOMTypes; diff --git a/dom/notification/Notification.cpp b/dom/notification/Notification.cpp index 332418d21e70..6288fb8b5168 100644 --- a/dom/notification/Notification.cpp +++ b/dom/notification/Notification.cpp @@ -203,7 +203,8 @@ public: NotificationGetRunnable(const nsAString& aOrigin, const nsAString& aTag, nsINotificationStorageCallback* aCallback) - : mOrigin(aOrigin), mTag(aTag), mCallback(aCallback) + : Runnable("NotificationGetRunnable") + , mOrigin(aOrigin), mTag(aTag), mCallback(aCallback) {} NS_IMETHOD @@ -310,7 +311,8 @@ class FocusWindowRunnable final : public Runnable nsMainThreadPtrHandle mWindow; public: explicit FocusWindowRunnable(const nsMainThreadPtrHandle& aWindow) - : mWindow(aWindow) + : Runnable("FocusWindowRunnable") + , mWindow(aWindow) { } NS_IMETHOD @@ -502,8 +504,10 @@ public: eClose }; - NotificationTask(UniquePtr aRef, NotificationAction aAction) - : mNotificationRef(Move(aRef)), mAction(aAction) + NotificationTask(const char* aName, UniquePtr aRef, + NotificationAction aAction) + : Runnable(aName) + , mNotificationRef(Move(aRef)), mAction(aAction) {} NS_IMETHOD @@ -619,8 +623,13 @@ NotificationPermissionRequest::GetRequester(nsIContentPermissionRequester** aReq inline nsresult NotificationPermissionRequest::DispatchResolvePromise() { - return NS_DispatchToMainThread(NewRunnableMethod(this, - &NotificationPermissionRequest::ResolvePromise)); + nsCOMPtr resolver = + NewRunnableMethod("NotificationPermissionRequest::DispatchResolvePromise", + this, &NotificationPermissionRequest::ResolvePromise); + if (nsIEventTarget* target = mWindow->EventTargetFor(TaskCategory::Other)) { + return target->Dispatch(resolver.forget(), nsIEventTarget::DISPATCH_NORMAL); + } + return NS_ERROR_FAILURE; } nsresult @@ -1399,7 +1408,7 @@ public: MOZ_ASSERT_IF(mWorkerPrivate->IsServiceWorker(), !doDefaultAction); if (doDefaultAction) { RefPtr r = new FocusWindowRunnable(mWindow); - NS_DispatchToMainThread(r); + mWorkerPrivate->DispatchToMainThread(r.forget()); } } }; @@ -1831,11 +1840,14 @@ Notification::RequestPermissionEnabledForScope(JSContext* aCx, JSObject* /* unus return NS_IsMainThread(); } +// static already_AddRefed Notification::RequestPermission(const GlobalObject& aGlobal, const Optional >& aCallback, ErrorResult& aRv) { + AssertIsOnMainThread(); + // Get principal from global to make permission request for notifications. nsCOMPtr window = do_QueryInterface(aGlobal.GetAsSupports()); nsCOMPtr sop = do_QueryInterface(aGlobal.GetAsSupports()); @@ -1857,7 +1869,9 @@ Notification::RequestPermission(const GlobalObject& aGlobal, nsCOMPtr request = new NotificationPermissionRequest(principal, window, promise, permissionCallback); - NS_DispatchToMainThread(request); + global->Dispatch("Notification::RequestPermission", TaskCategory::Other, + request.forget()); + return promise.forget(); } @@ -2026,6 +2040,7 @@ Notification::Get(nsPIDOMWindowInner* aWindow, const nsAString& aScope, ErrorResult& aRv) { + AssertIsOnMainThread(); MOZ_ASSERT(aWindow); nsCOMPtr doc = aWindow->GetExtantDoc(); @@ -2052,7 +2067,8 @@ Notification::Get(nsPIDOMWindowInner* aWindow, RefPtr r = new NotificationGetRunnable(origin, aFilter.mTag, callback); - aRv = NS_DispatchToMainThread(r); + aRv = global->Dispatch("Notification::Get", TaskCategory::Other, + r.forget()); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } @@ -2172,7 +2188,8 @@ public: WorkerGetRunnable(PromiseWorkerProxy* aProxy, const nsAString& aTag, const nsAString& aScope) - : mPromiseProxy(aProxy), mTag(aTag), mScope(aScope) + : Runnable("WorkerGetRunnable") + , mPromiseProxy(aProxy), mTag(aTag), mScope(aScope) { MOZ_ASSERT(mPromiseProxy); } @@ -2219,6 +2236,7 @@ private: {} }; +// static already_AddRefed Notification::WorkerGet(WorkerPrivate* aWorkerPrivate, const GetNotificationOptions& aFilter, @@ -2243,7 +2261,7 @@ Notification::WorkerGet(WorkerPrivate* aWorkerPrivate, new WorkerGetRunnable(proxy, aFilter.mTag, aScope); // Since this is called from script via // ServiceWorkerRegistration::GetNotifications, we can assert dispatch. - MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r)); + MOZ_ALWAYS_SUCCEEDS(aWorkerPrivate->DispatchToMainThread(r.forget())); return p.forget(); } @@ -2263,8 +2281,9 @@ Notification::Close() } nsCOMPtr closeNotificationTask = - new NotificationTask(Move(ref), NotificationTask::eClose); - nsresult rv = NS_DispatchToMainThread(closeNotificationTask); + new NotificationTask("Notification::Close", Move(ref), + NotificationTask::eClose); + nsresult rv = DispatchToMainThread(closeNotificationTask.forget()); if (NS_FAILED(rv)) { DispatchTrustedEvent(NS_LITERAL_STRING("error")); @@ -2703,8 +2722,12 @@ Notification::CreateAndShow(JSContext* aCx, // Queue a task to show the notification. nsCOMPtr showNotificationTask = - new NotificationTask(Move(ref), NotificationTask::eShow); - nsresult rv = NS_DispatchToMainThread(showNotificationTask); + new NotificationTask("Notification::CreateAndShow", Move(ref), + NotificationTask::eShow); + + nsresult rv = + notification->DispatchToMainThread(showNotificationTask.forget()); + if (NS_WARN_IF(NS_FAILED(rv))) { notification->DispatchTrustedEvent(NS_LITERAL_STRING("error")); } @@ -2763,5 +2786,22 @@ Notification::Observe(nsISupports* aSubject, const char* aTopic, return NS_OK; } +nsresult +Notification::DispatchToMainThread(already_AddRefed&& aRunnable) +{ + if (mWorkerPrivate) { + return mWorkerPrivate->DispatchToMainThread(Move(aRunnable)); + } + AssertIsOnMainThread(); + if (nsCOMPtr global = GetOwnerGlobal()) { + if (nsIEventTarget* target = global->EventTargetFor(TaskCategory::Other)) { + return target->Dispatch(Move(aRunnable), nsIEventTarget::DISPATCH_NORMAL); + } + } + nsCOMPtr mainThread = do_GetMainThread(); + MOZ_ASSERT(mainThread); + return mainThread->Dispatch(Move(aRunnable), nsIEventTarget::DISPATCH_NORMAL); +} + } // namespace dom } // namespace mozilla diff --git a/dom/notification/Notification.h b/dom/notification/Notification.h index a2c4b5c6828b..4c7da2044b35 100644 --- a/dom/notification/Notification.h +++ b/dom/notification/Notification.h @@ -327,6 +327,8 @@ public: static nsresult RemovePermission(nsIPrincipal* aPrincipal); static nsresult OpenSettings(nsIPrincipal* aPrincipal); + + nsresult DispatchToMainThread(already_AddRefed&& aRunnable); protected: Notification(nsIGlobalObject* aGlobal, const nsAString& aID, const nsAString& aTitle, const nsAString& aBody, diff --git a/dom/smil/nsSMILCSSProperty.cpp b/dom/smil/nsSMILCSSProperty.cpp index 53f3e0fbf754..97e9bce9aed7 100644 --- a/dom/smil/nsSMILCSSProperty.cpp +++ b/dom/smil/nsSMILCSSProperty.cpp @@ -10,51 +10,22 @@ #include "mozilla/dom/Element.h" #include "mozilla/Move.h" +#include "mozilla/StyleAnimationValue.h" +#include "nsICSSDeclaration.h" #include "nsSMILCSSValueType.h" #include "nsSMILValue.h" -#include "nsComputedDOMStyle.h" #include "nsCSSProps.h" -#include "nsIDOMElement.h" -#include "nsIDocument.h" +using namespace mozilla; using namespace mozilla::dom; -// Helper function -static bool -GetCSSComputedValue(Element* aElem, - nsCSSPropertyID aPropID, - nsAString& aResult) -{ - MOZ_ASSERT(!nsCSSProps::IsShorthand(aPropID), - "Can't look up computed value of shorthand property"); - MOZ_ASSERT(nsSMILCSSProperty::IsPropertyAnimatable(aPropID), - "Shouldn't get here for non-animatable properties"); - - nsIDocument* doc = aElem->GetUncomposedDoc(); - if (!doc) { - // This can happen if we process certain types of restyles mid-sample - // and remove anonymous animated content from the document as a result. - // See bug 534975. - return false; - } - - nsIPresShell* shell = doc->GetShell(); - if (!shell) { - NS_WARNING("Unable to look up computed style -- no pres shell"); - return false; - } - - RefPtr computedStyle = - NS_NewComputedDOMStyle(aElem, EmptyString(), shell); - - computedStyle->GetPropertyValue(aPropID, aResult); - return true; -} - // Class Methods nsSMILCSSProperty::nsSMILCSSProperty(nsCSSPropertyID aPropID, - Element* aElement) - : mPropID(aPropID), mElement(aElement) + Element* aElement, + nsStyleContext* aBaseStyleContext) + : mPropID(aPropID) + , mElement(aElement) + , mBaseStyleContext(aBaseStyleContext) { MOZ_ASSERT(IsPropertyAnimatable(mPropID), "Creating a nsSMILCSSProperty for a property " @@ -71,7 +42,10 @@ nsSMILCSSProperty::GetBaseValue() const // SPECIAL CASE: (a) Shorthands // (b) 'display' - if (nsCSSProps::IsShorthand(mPropID) || mPropID == eCSSProperty_display) { + // (c) No base style context + if (nsCSSProps::IsShorthand(mPropID) || + mPropID == eCSSProperty_display || + !mBaseStyleContext) { // We can't look up the base (computed-style) value of shorthand // properties because they aren't guaranteed to have a consistent computed // value. @@ -80,47 +54,26 @@ nsSMILCSSProperty::GetBaseValue() const // doing so involves clearing and resetting the property which can cause // frames to be recreated which we'd like to avoid. // - // In either case, just return a dummy value (initialized with the right + // Furthermore, if we don't (yet) have a base style context we obviously + // can't resolve a base value. + // + // In any case, just return a dummy value (initialized with the right // type, so as not to indicate failure). nsSMILValue tmpVal(&nsSMILCSSValueType::sSingleton); Swap(baseValue, tmpVal); return baseValue; } - // GENERAL CASE: Non-Shorthands - // (1) Put empty string in override style for property mPropID - // (saving old override style value, so we can set it again when we're done) - nsICSSDeclaration* overrideDecl = mElement->GetSMILOverrideStyle(); - nsAutoString cachedOverrideStyleVal; - if (overrideDecl) { - overrideDecl->GetPropertyValue(mPropID, cachedOverrideStyleVal); - // (Don't bother clearing override style if it's already empty) - if (!cachedOverrideStyleVal.IsEmpty()) { - overrideDecl->SetPropertyValue(mPropID, EmptyString()); - } + StyleAnimationValue computedValue; + if (!StyleAnimationValue::ExtractComputedValue(mPropID, + mBaseStyleContext, + computedValue)) { + return baseValue; } - // (2) Get Computed Style - nsAutoString computedStyleVal; - bool didGetComputedVal = GetCSSComputedValue(mElement, mPropID, - computedStyleVal); - - // (3) Put cached override style back (if it's non-empty) - if (overrideDecl && !cachedOverrideStyleVal.IsEmpty()) { - overrideDecl->SetPropertyValue(mPropID, cachedOverrideStyleVal); - } - - // (4) Populate our nsSMILValue from the computed style - if (didGetComputedVal) { - // When we parse animation values we check if they are context-sensitive or - // not so that we don't cache animation values whose meaning may change. - // For base values however this is unnecessary since on each sample the - // compositor will fetch the (computed) base value and compare it against - // the cached (computed) value and detect changes for us. - nsSMILCSSValueType::ValueFromString(mPropID, mElement, - computedStyleVal, baseValue, - nullptr); - } + baseValue = + nsSMILCSSValueType::ValueFromAnimationValue(mPropID, mElement, + computedValue); return baseValue; } diff --git a/dom/smil/nsSMILCSSProperty.h b/dom/smil/nsSMILCSSProperty.h index 028a9aaa2de3..6e2b7b72a493 100644 --- a/dom/smil/nsSMILCSSProperty.h +++ b/dom/smil/nsSMILCSSProperty.h @@ -15,6 +15,8 @@ #include "nsCSSPropertyID.h" #include "nsCSSValue.h" +class nsStyleContext; + namespace mozilla { namespace dom { class Element; @@ -33,8 +35,14 @@ public: * Constructs a new nsSMILCSSProperty. * @param aPropID The CSS property we're interested in animating. * @param aElement The element whose CSS property is being animated. + * @param aBaseStyleContext The style context to use when getting the base + * value. If this is nullptr and GetBaseValue is + * called, an empty nsSMILValue initialized with + * the nsSMILCSSValueType will be returned. */ - nsSMILCSSProperty(nsCSSPropertyID aPropID, mozilla::dom::Element* aElement); + nsSMILCSSProperty(nsCSSPropertyID aPropID, + mozilla::dom::Element* aElement, + nsStyleContext* aBaseStyleContext); // nsISMILAttr methods virtual nsresult ValueFromString(const nsAString& aStr, @@ -62,6 +70,12 @@ protected: // as the Compositing step, and DOM elements don't get a chance to die during // that time. mozilla::dom::Element* mElement; + + // The style context to use when fetching base styles. + // As with mElement, since an nsISMILAttr only lives as long as the + // compositing step and since ComposeAttribute holds an owning reference to + // the base style context, we can use a non-owning reference here. + nsStyleContext* mBaseStyleContext; }; #endif // NS_SMILCSSPROPERTY_H_ diff --git a/dom/smil/nsSMILCSSValueType.cpp b/dom/smil/nsSMILCSSValueType.cpp index 85fe72085eac..240155d31f5c 100644 --- a/dom/smil/nsSMILCSSValueType.cpp +++ b/dom/smil/nsSMILCSSValueType.cpp @@ -7,6 +7,8 @@ /* representation of a value for a SMIL-animated CSS property */ #include "nsSMILCSSValueType.h" + +#include "nsComputedDOMStyle.h" #include "nsString.h" #include "nsSMILParserUtils.h" #include "nsSMILValue.h" @@ -361,8 +363,8 @@ ValueFromStringHelper(nsCSSPropertyID aPropID, } } RefPtr styleContext = - nsComputedDOMStyle::GetStyleContextForElement(aTargetElement, nullptr, - aPresContext->PresShell()); + nsComputedDOMStyle::GetStyleContext(aTargetElement, nullptr, + aPresContext->PresShell()); if (!styleContext) { return false; } @@ -417,6 +419,34 @@ nsSMILCSSValueType::ValueFromString(nsCSSPropertyID aPropID, } } +// static +nsSMILValue +nsSMILCSSValueType::ValueFromAnimationValue(nsCSSPropertyID aPropID, + Element* aTargetElement, + const StyleAnimationValue& aValue) +{ + nsSMILValue result; + + nsIDocument* doc = aTargetElement->GetUncomposedDoc(); + // We'd like to avoid serializing |aValue| if possible, and since the + // string passed to CSPAllowsInlineStyle is only used for reporting violations + // and an intermediate CSS value is not likely to be particularly useful + // in that case, we just use a generic placeholder string instead. + static const nsLiteralString kPlaceholderText = + NS_LITERAL_STRING("[SVG animation of CSS]"); + if (doc && !nsStyleUtil::CSPAllowsInlineStyle(nullptr, + doc->NodePrincipal(), + doc->GetDocumentURI(), + 0, kPlaceholderText, nullptr)) { + return result; + } + + sSingleton.Init(result); + result.mU.mPtr = new ValueWrapper(aPropID, aValue); + + return result; +} + // static bool nsSMILCSSValueType::ValueToString(const nsSMILValue& aValue, diff --git a/dom/smil/nsSMILCSSValueType.h b/dom/smil/nsSMILCSSValueType.h index 0c71605f0576..f244639010b3 100644 --- a/dom/smil/nsSMILCSSValueType.h +++ b/dom/smil/nsSMILCSSValueType.h @@ -16,6 +16,7 @@ class nsAString; namespace mozilla { +class StyleAnimationValue; namespace dom { class Element; } // namespace dom @@ -28,6 +29,7 @@ class nsSMILCSSValueType : public nsISMILType { public: typedef mozilla::dom::Element Element; + typedef mozilla::StyleAnimationValue StyleAnimationValue; // Singleton for nsSMILValue objects to hold onto. static nsSMILCSSValueType sSingleton; @@ -84,6 +86,21 @@ public: nsSMILValue& aValue, bool* aIsContextSensitive); + /** + * Creates an nsSMILValue to wrap the given animation value. + * + * @param aPropID The property that |aValue| corresponds to. + * @param aTargetElement The target element to which the animation value + * applies. + * @param aValue The animation value to use. + * @return A new nsSMILValue. On failure, returns an + * nsSMILValue with the null type (i.e. rv.IsNull() + * returns true). + */ + static nsSMILValue ValueFromAnimationValue(nsCSSPropertyID aPropID, + Element* aTargetElement, + const StyleAnimationValue& aValue); + /** * Creates a string representation of the given nsSMILValue. * diff --git a/dom/smil/nsSMILCompositor.cpp b/dom/smil/nsSMILCompositor.cpp index 8f114a696fa5..67a0c80a343b 100644 --- a/dom/smil/nsSMILCompositor.cpp +++ b/dom/smil/nsSMILCompositor.cpp @@ -6,6 +6,7 @@ #include "nsSMILCompositor.h" +#include "nsComputedDOMStyle.h" #include "nsCSSProps.h" #include "nsHashKeys.h" #include "nsSMILCSSProperty.h" @@ -54,9 +55,18 @@ nsSMILCompositor::ComposeAttribute(bool& aMightHavePendingStyleUpdates) if (!mKey.mElement) return; + // If we might need to resolve base styles, grab a suitable style context + // for initializing our nsISMILAttr with. + RefPtr baseStyleContext; + if (MightNeedBaseStyle()) { + baseStyleContext = + nsComputedDOMStyle::GetUnanimatedStyleContextNoFlush(mKey.mElement, + nullptr, nullptr); + } + // FIRST: Get the nsISMILAttr (to grab base value from, and to eventually // give animated value to) - UniquePtr smilAttr = CreateSMILAttr(); + UniquePtr smilAttr = CreateSMILAttr(baseStyleContext); if (!smilAttr) { // Target attribute not found (or, out of memory) return; @@ -115,7 +125,7 @@ nsSMILCompositor::ClearAnimationEffects() if (!mKey.mElement || !mKey.mAttributeName) return; - UniquePtr smilAttr = CreateSMILAttr(); + UniquePtr smilAttr = CreateSMILAttr(nullptr); if (!smilAttr) { // Target attribute not found (or, out of memory) return; @@ -126,12 +136,13 @@ nsSMILCompositor::ClearAnimationEffects() // Protected Helper Functions // -------------------------- UniquePtr -nsSMILCompositor::CreateSMILAttr() +nsSMILCompositor::CreateSMILAttr(nsStyleContext* aBaseStyleContext) { nsCSSPropertyID propID = GetCSSPropertyToAnimate(); if (propID != eCSSProperty_UNKNOWN) { - return MakeUnique(propID, mKey.mElement.get()); + return MakeUnique(propID, mKey.mElement.get(), + aBaseStyleContext); } return mKey.mElement->GetAnimatedAttr(mKey.mAttributeNamespaceID, @@ -173,6 +184,24 @@ nsSMILCompositor::GetCSSPropertyToAnimate() const return propID; } +bool +nsSMILCompositor::MightNeedBaseStyle() const +{ + if (GetCSSPropertyToAnimate() == eCSSProperty_UNKNOWN) { + return false; + } + + // We should return true if at least one animation function might build on + // the base value. + for (const nsSMILAnimationFunction* func : mAnimationFunctions) { + if (!func->WillReplace()) { + return true; + } + } + + return false; +} + uint32_t nsSMILCompositor::GetFirstFuncToAffectSandwich() { diff --git a/dom/smil/nsSMILCompositor.h b/dom/smil/nsSMILCompositor.h index 93f759cc4193..0c780d54c73b 100644 --- a/dom/smil/nsSMILCompositor.h +++ b/dom/smil/nsSMILCompositor.h @@ -74,12 +74,26 @@ public: private: // Create a nsISMILAttr for my target, on the heap. - mozilla::UniquePtr CreateSMILAttr(); + // + // @param aBaseStyleContext An optional style context which, if set, will be + // used when fetching the base style. + mozilla::UniquePtr + CreateSMILAttr(nsStyleContext* aBaseStyleContext); // Returns the CSS property this compositor should animate, or // eCSSProperty_UNKNOWN if this compositor does not animate a CSS property. nsCSSPropertyID GetCSSPropertyToAnimate() const; + // Returns true if we might need to refer to base styles (i.e. we are + // targeting a CSS property and have one or more animation functions that + // don't just replace the underlying value). + // + // This might return true in some cases where we don't actually need the base + // style since it doesn't build up the animation sandwich to check if the + // functions that appear to need the base style are actually replaced by + // a function further up the stack. + bool MightNeedBaseStyle() const; + // Finds the index of the first function that will affect our animation // sandwich. Also toggles the 'mForceCompositing' flag if it finds that any // (used) functions have changed. diff --git a/dom/smil/test/mochitest.ini b/dom/smil/test/mochitest.ini index 483c308baf52..ae99d69969ce 100644 --- a/dom/smil/test/mochitest.ini +++ b/dom/smil/test/mochitest.ini @@ -57,5 +57,6 @@ skip-if = toolkit == 'android' #TIMED_OUT [test_smilTimingZeroIntervals.xhtml] [test_smilUpdatedInterval.xhtml] [test_smilValues.xhtml] +[test_smilWithTransition.html] [test_smilWithXlink.xhtml] [test_smilXHR.xhtml] diff --git a/dom/smil/test/test_smilWithTransition.html b/dom/smil/test/test_smilWithTransition.html new file mode 100644 index 000000000000..33d9a169fe70 --- /dev/null +++ b/dom/smil/test/test_smilWithTransition.html @@ -0,0 +1,72 @@ + + + + + + Test SMIL does not trigger CSS Transitions (bug 1315874) + + + + +Mozilla Bug + 1315874 + + + + + +
+
+
+ + diff --git a/dom/svg/SVGContentUtils.cpp b/dom/svg/SVGContentUtils.cpp index 48ea42fc4726..4503384352ea 100644 --- a/dom/svg/SVGContentUtils.cpp +++ b/dom/svg/SVGContentUtils.cpp @@ -179,8 +179,7 @@ SVGContentUtils::GetStrokeOptions(AutoStrokeOptions* aStrokeOptions, styleContext = aStyleContext; } else { styleContext = - nsComputedDOMStyle::GetStyleContextForElementNoFlush(aElement, nullptr, - nullptr); + nsComputedDOMStyle::GetStyleContextNoFlush(aElement, nullptr, nullptr); } if (!styleContext) { @@ -253,8 +252,7 @@ SVGContentUtils::GetStrokeWidth(nsSVGElement* aElement, styleContext = aStyleContext; } else { styleContext = - nsComputedDOMStyle::GetStyleContextForElementNoFlush(aElement, nullptr, - nullptr); + nsComputedDOMStyle::GetStyleContextNoFlush(aElement, nullptr, nullptr); } if (!styleContext) { @@ -276,9 +274,8 @@ SVGContentUtils::GetFontSize(Element *aElement) if (!aElement) return 1.0f; - RefPtr styleContext = - nsComputedDOMStyle::GetStyleContextForElementNoFlush(aElement, - nullptr, nullptr); + RefPtr styleContext = + nsComputedDOMStyle::GetStyleContextNoFlush(aElement, nullptr, nullptr); if (!styleContext) { // ReportToConsole NS_WARNING("Couldn't get style context for content in GetFontStyle"); @@ -314,9 +311,8 @@ SVGContentUtils::GetFontXHeight(Element *aElement) if (!aElement) return 1.0f; - RefPtr styleContext = - nsComputedDOMStyle::GetStyleContextForElementNoFlush(aElement, - nullptr, nullptr); + RefPtr styleContext = + nsComputedDOMStyle::GetStyleContextNoFlush(aElement, nullptr, nullptr); if (!styleContext) { // ReportToConsole NS_WARNING("Couldn't get style context for content in GetFontStyle"); @@ -325,7 +321,7 @@ SVGContentUtils::GetFontXHeight(Element *aElement) return GetFontXHeight(styleContext); } - + float SVGContentUtils::GetFontXHeight(nsIFrame *aFrame) { diff --git a/dom/svg/SVGGeometryElement.cpp b/dom/svg/SVGGeometryElement.cpp index ebacee8ad588..a7d79cfb822f 100644 --- a/dom/svg/SVGGeometryElement.cpp +++ b/dom/svg/SVGGeometryElement.cpp @@ -127,9 +127,8 @@ SVGGeometryElement::GetFillRule() FillRule fillRule = FillRule::FILL_WINDING; // Equivalent to StyleFillRule::Nonzero RefPtr styleContext = - nsComputedDOMStyle::GetStyleContextForElementNoFlush(this, nullptr, - nullptr); - + nsComputedDOMStyle::GetStyleContextNoFlush(this, nullptr, nullptr); + if (styleContext) { MOZ_ASSERT(styleContext->StyleSVG()->mFillRule == StyleFillRule::Nonzero || styleContext->StyleSVG()->mFillRule == StyleFillRule::Evenodd); diff --git a/dom/svg/SVGPathElement.cpp b/dom/svg/SVGPathElement.cpp index b6d4719dd9d4..dfaec789f155 100644 --- a/dom/svg/SVGPathElement.cpp +++ b/dom/svg/SVGPathElement.cpp @@ -328,7 +328,7 @@ SVGPathElement::BuildPath(PathBuilder* aBuilder) Float strokeWidth = 0; RefPtr styleContext = - nsComputedDOMStyle::GetStyleContextForElementNoFlush(this, nullptr, nullptr); + nsComputedDOMStyle::GetStyleContextNoFlush(this, nullptr, nullptr); if (styleContext) { const nsStyleSVG* style = styleContext->StyleSVG(); // Note: the path that we return may be used for hit-testing, and SVG diff --git a/dom/tests/mochitest/general/test_interfaces.js b/dom/tests/mochitest/general/test_interfaces.js index 93cc4d5e65a7..c1fe204b3ea0 100644 --- a/dom/tests/mochitest/general/test_interfaces.js +++ b/dom/tests/mochitest/general/test_interfaces.js @@ -596,9 +596,9 @@ var interfaceNamesInGlobalScope = // IMPORTANT: Do not change this list without review from a DOM peer! "InstallTrigger", // IMPORTANT: Do not change this list without review from a DOM peer! - {name: "IntersectionObserver", disabled: true}, + "IntersectionObserver", // IMPORTANT: Do not change this list without review from a DOM peer! - {name: "IntersectionObserverEntry", disabled: true}, + "IntersectionObserverEntry", // IMPORTANT: Do not change this list without review from a DOM peer! "KeyEvent", // IMPORTANT: Do not change this list without review from a DOM peer! diff --git a/editor/libeditor/EditorBase.cpp b/editor/libeditor/EditorBase.cpp index 6b4e7ed52ba4..854eff864f67 100644 --- a/editor/libeditor/EditorBase.cpp +++ b/editor/libeditor/EditorBase.cpp @@ -3535,8 +3535,7 @@ IsElementVisible(Element* aElement) // lazy frame construction and it hasn't happened yet. Check which // one it is. RefPtr styleContext = - nsComputedDOMStyle::GetStyleContextForElementNoFlush(aElement, - nullptr, nullptr); + nsComputedDOMStyle::GetStyleContextNoFlush(aElement, nullptr, nullptr); if (styleContext) { return styleContext->StyleDisplay()->mDisplay != StyleDisplay::None; } @@ -3867,9 +3866,9 @@ EditorBase::IsPreformatted(nsIDOMNode* aNode, content = content->GetParent(); } if (content && content->IsElement()) { - elementStyle = nsComputedDOMStyle::GetStyleContextForElementNoFlush(content->AsElement(), - nullptr, - ps); + elementStyle = + nsComputedDOMStyle::GetStyleContextNoFlush(content->AsElement(), + nullptr, ps); } if (!elementStyle) { diff --git a/ipc/mscom/IHandlerPayload.h b/ipc/mscom/IHandlerPayload.h deleted file mode 100644 index e82d2e8244cc..000000000000 --- a/ipc/mscom/IHandlerPayload.h +++ /dev/null @@ -1,37 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* 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/. */ - -#ifndef mozilla_mscom_IHandlerPayload_h -#define mozilla_mscom_IHandlerPayload_h - -#include "mozilla/mscom/Ptr.h" - -#include - -namespace mozilla { -namespace mscom { - -struct HandlerPayload -{ - virtual STDMETHODIMP GetHandler(CLSID* aHandlerClsid) = 0; - virtual STDMETHODIMP GetHandlerPayloadSize(REFIID aIid, - InterceptorTargetPtr aTarget, - DWORD* aOutPayloadSize) = 0; - virtual STDMETHODIMP WriteHandlerPayload(IStream* aStream, REFIID aIid, - InterceptorTargetPtr aTarget) = 0; - virtual REFIID MarshalAs(REFIID aIid) = 0; -}; - -struct IHandlerPayload : public IUnknown - , public HandlerPayload -{ - virtual STDMETHODIMP Clone(IHandlerPayload** aOutNewPayload) = 0; -}; - -} // namespace mscom -} // namespace mozilla - -#endif // mozilla_mscom_IHandlerPayload_h diff --git a/ipc/mscom/IHandlerProvider.h b/ipc/mscom/IHandlerProvider.h new file mode 100644 index 000000000000..4691d4e2b50a --- /dev/null +++ b/ipc/mscom/IHandlerProvider.h @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_mscom_IHandlerProvider_h +#define mozilla_mscom_IHandlerProvider_h + +#include "mozilla/NotNull.h" +#include "mozilla/mscom/Ptr.h" + +#include + +namespace mozilla { +namespace mscom { + +struct HandlerProvider +{ + virtual STDMETHODIMP GetHandler(NotNull aHandlerClsid) = 0; + virtual STDMETHODIMP GetHandlerPayloadSize(NotNull aOutPayloadSize) = 0; + virtual STDMETHODIMP WriteHandlerPayload(NotNull aStream) = 0; + virtual STDMETHODIMP_(REFIID) MarshalAs(REFIID aIid) = 0; +}; + +struct IHandlerProvider : public IUnknown + , public HandlerProvider +{ + virtual STDMETHODIMP NewInstance(REFIID aIid, + InterceptorTargetPtr aTarget, + NotNull aOutNewPayload) = 0; +}; + +} // namespace mscom +} // namespace mozilla + +#endif // mozilla_mscom_IHandlerProvider_h diff --git a/ipc/mscom/Interceptor.cpp b/ipc/mscom/Interceptor.cpp index 8ebe477110d5..6bedd5f7c57e 100644 --- a/ipc/mscom/Interceptor.cpp +++ b/ipc/mscom/Interceptor.cpp @@ -78,7 +78,7 @@ Interceptor::GetClassForHandler(DWORD aDestContext, void* aDestContextPtr, return E_INVALIDARG; } MOZ_ASSERT(mEventSink); - return mEventSink->GetHandler(aHandlerClsid); + return mEventSink->GetHandler(WrapNotNull(aHandlerClsid)); } HRESULT @@ -101,10 +101,8 @@ Interceptor::GetMarshalSizeMax(REFIID riid, void* pv, DWORD dwDestContext, return hr; } - InterceptorTargetPtr targetParam(mTarget.get()); - DWORD payloadSize = 0; - hr = mEventSink->GetHandlerPayloadSize(riid, Move(targetParam), &payloadSize); + hr = mEventSink->GetHandlerPayloadSize(WrapNotNull(&payloadSize)); *pSize += payloadSize; return hr; } @@ -120,8 +118,7 @@ Interceptor::MarshalInterface(IStream* pStm, REFIID riid, void* pv, return hr; } - InterceptorTargetPtr targetParam(mTarget.get()); - return mEventSink->WriteHandlerPayload(pStm, riid, Move(targetParam)); + return mEventSink->WriteHandlerPayload(WrapNotNull(pStm)); } HRESULT @@ -367,7 +364,7 @@ Interceptor::ThreadSafeQueryInterface(REFIID aIid, IUnknown** aOutInterface) // support it. We'll check that by looking for a successful call to // IInterceptorSink::GetHandler() CLSID dummy; - if (FAILED(mEventSink->GetHandler(&dummy))) { + if (FAILED(mEventSink->GetHandler(WrapNotNull(&dummy)))) { return E_NOINTERFACE; } @@ -381,7 +378,7 @@ Interceptor::ThreadSafeQueryInterface(REFIID aIid, IUnknown** aOutInterface) // support it. We'll check that by looking for a successful call to // IInterceptorSink::GetHandler() CLSID dummy; - if (FAILED(mEventSink->GetHandler(&dummy))) { + if (FAILED(mEventSink->GetHandler(WrapNotNull(&dummy)))) { return E_NOINTERFACE; } diff --git a/ipc/mscom/Interceptor.h b/ipc/mscom/Interceptor.h index f523a6031014..298c625ab331 100644 --- a/ipc/mscom/Interceptor.h +++ b/ipc/mscom/Interceptor.h @@ -10,7 +10,7 @@ #include "mozilla/Move.h" #include "mozilla/Mutex.h" #include "nsTArray.h" -#include "mozilla/mscom/IHandlerPayload.h" +#include "mozilla/mscom/IHandlerProvider.h" #include "mozilla/mscom/Ptr.h" #include "mozilla/mscom/WeakRef.h" #include "mozilla/RefPtr.h" @@ -26,7 +26,7 @@ DEFINE_GUID(IID_IInterceptorSink, 0x8831eb53, 0xa937, 0x42bc, 0x99, 0x21, 0xb3, 0xe1, 0x12, 0x1f, 0xdf, 0x86); struct IInterceptorSink : public ICallFrameEvents - , public HandlerPayload + , public HandlerProvider { virtual STDMETHODIMP SetInterceptor(IWeakReference* aInterceptor) = 0; }; diff --git a/ipc/mscom/MainThreadHandoff.cpp b/ipc/mscom/MainThreadHandoff.cpp index 2560751415c6..07dc35155c23 100644 --- a/ipc/mscom/MainThreadHandoff.cpp +++ b/ipc/mscom/MainThreadHandoff.cpp @@ -159,16 +159,16 @@ namespace mozilla { namespace mscom { /* static */ HRESULT -MainThreadHandoff::Create(IHandlerPayload* aHandlerPayload, +MainThreadHandoff::Create(IHandlerProvider* aHandlerProvider, IInterceptorSink** aOutput) { - RefPtr handoff(new MainThreadHandoff(aHandlerPayload)); + RefPtr handoff(new MainThreadHandoff(aHandlerProvider)); return handoff->QueryInterface(IID_IInterceptorSink, (void**) aOutput); } -MainThreadHandoff::MainThreadHandoff(IHandlerPayload* aHandlerPayload) +MainThreadHandoff::MainThreadHandoff(IHandlerProvider* aHandlerProvider) : mRefCnt(0) - , mHandlerPayload(aHandlerPayload) + , mHandlerProvider(aHandlerProvider) { } @@ -448,43 +448,39 @@ MainThreadHandoff::SetInterceptor(IWeakReference* aInterceptor) } HRESULT -MainThreadHandoff::GetHandler(CLSID* aHandlerClsid) +MainThreadHandoff::GetHandler(NotNull aHandlerClsid) { - if (!mHandlerPayload) { + if (!mHandlerProvider) { return E_NOTIMPL; } - return mHandlerPayload->GetHandler(aHandlerClsid); + return mHandlerProvider->GetHandler(aHandlerClsid); } HRESULT -MainThreadHandoff::GetHandlerPayloadSize(REFIID aIid, - InterceptorTargetPtr aTarget, - DWORD* aOutPayloadSize) +MainThreadHandoff::GetHandlerPayloadSize(NotNull aOutPayloadSize) { - if (!mHandlerPayload) { + if (!mHandlerProvider) { return E_NOTIMPL; } - return mHandlerPayload->GetHandlerPayloadSize(aIid, Move(aTarget), - aOutPayloadSize); + return mHandlerProvider->GetHandlerPayloadSize(aOutPayloadSize); } HRESULT -MainThreadHandoff::WriteHandlerPayload(IStream* aStream, REFIID aIid, - InterceptorTargetPtr aTarget) +MainThreadHandoff::WriteHandlerPayload(NotNull aStream) { - if (!mHandlerPayload) { + if (!mHandlerProvider) { return E_NOTIMPL; } - return mHandlerPayload->WriteHandlerPayload(aStream, aIid, Move(aTarget)); + return mHandlerProvider->WriteHandlerPayload(aStream); } REFIID MainThreadHandoff::MarshalAs(REFIID aIid) { - if (!mHandlerPayload) { + if (!mHandlerProvider) { return aIid; } - return mHandlerPayload->MarshalAs(aIid); + return mHandlerProvider->MarshalAs(aIid); } HRESULT @@ -529,24 +525,31 @@ MainThreadHandoff::OnWalkInterface(REFIID aIid, PVOID* aInterface, InterceptorTargetPtr existingTarget; hr = interceptor->GetTargetForIID(aIid, existingTarget); if (SUCCEEDED(hr)) { - bool areIUnknownsEqual = false; + // We'll start by checking the raw pointers. If they are equal, then the + // objects are equal. OTOH, if they differ, we must compare their + // IUnknown pointers to know for sure. + bool areTargetsEqual = existingTarget.get() == origInterface.get(); - // This check must be done on the main thread - auto checkFn = [&existingTarget, &origInterface, &areIUnknownsEqual]() -> void { - RefPtr unkExisting; - HRESULT hrExisting = - existingTarget->QueryInterface(IID_IUnknown, - (void**)getter_AddRefs(unkExisting)); - RefPtr unkNew; - HRESULT hrNew = - origInterface->QueryInterface(IID_IUnknown, - (void**)getter_AddRefs(unkNew)); - areIUnknownsEqual = SUCCEEDED(hrExisting) && SUCCEEDED(hrNew) && + if (!areTargetsEqual) { + // This check must be done on the main thread + auto checkFn = [&existingTarget, &origInterface, &areTargetsEqual]() -> void { + RefPtr unkExisting; + HRESULT hrExisting = + existingTarget->QueryInterface(IID_IUnknown, + (void**)getter_AddRefs(unkExisting)); + RefPtr unkNew; + HRESULT hrNew = + origInterface->QueryInterface(IID_IUnknown, + (void**)getter_AddRefs(unkNew)); + areTargetsEqual = SUCCEEDED(hrExisting) && SUCCEEDED(hrNew) && unkExisting == unkNew; - }; + }; - MainThreadInvoker invoker; - if (invoker.Invoke(NS_NewRunnableFunction(checkFn)) && areIUnknownsEqual) { + MainThreadInvoker invoker; + invoker.Invoke(NS_NewRunnableFunction(checkFn)); + } + + if (areTargetsEqual) { // The existing interface and the new interface both belong to the same // target object. Let's just use the existing one. void* intercepted = nullptr; @@ -560,9 +563,11 @@ MainThreadHandoff::OnWalkInterface(REFIID aIid, PVOID* aInterface, } } - RefPtr payload; - if (mHandlerPayload) { - hr = mHandlerPayload->Clone(getter_AddRefs(payload)); + RefPtr payload; + if (mHandlerProvider) { + hr = mHandlerProvider->NewInstance(aIid, + ToInterceptorTargetPtr(origInterface), + WrapNotNull((IHandlerProvider**)getter_AddRefs(payload))); MOZ_ASSERT(SUCCEEDED(hr)); if (FAILED(hr)) { return hr; diff --git a/ipc/mscom/MainThreadHandoff.h b/ipc/mscom/MainThreadHandoff.h index f8fd90bb9585..32033ff4c1ab 100644 --- a/ipc/mscom/MainThreadHandoff.h +++ b/ipc/mscom/MainThreadHandoff.h @@ -24,7 +24,7 @@ class MainThreadHandoff final : public IInterceptorSink , public ICallFrameWalker { public: - static HRESULT Create(IHandlerPayload* aHandlerPayload, + static HRESULT Create(IHandlerProvider* aHandlerProvider, IInterceptorSink** aOutput); template @@ -37,12 +37,12 @@ public: template static HRESULT WrapInterface(STAUniquePtr aTargetInterface, - IHandlerPayload* aHandlerPayload, + IHandlerProvider* aHandlerProvider, Interface** aOutInterface) { MOZ_ASSERT(!IsProxy(aTargetInterface.get())); RefPtr handoff; - HRESULT hr = MainThreadHandoff::Create(aHandlerPayload, + HRESULT hr = MainThreadHandoff::Create(aHandlerProvider, getter_AddRefs(handoff)); if (FAILED(hr)) { return hr; @@ -60,29 +60,26 @@ public: // IInterceptorSink STDMETHODIMP SetInterceptor(IWeakReference* aInterceptor) override; - STDMETHODIMP GetHandler(CLSID* aHandlerClsid) override; - STDMETHODIMP GetHandlerPayloadSize(REFIID aIid, - InterceptorTargetPtr aTarget, - DWORD* aOutPayloadSize) override; - STDMETHODIMP WriteHandlerPayload(IStream* aStream, REFIID aIid, - InterceptorTargetPtr aTarget) override; - REFIID MarshalAs(REFIID aIid) override; + STDMETHODIMP GetHandler(NotNull aHandlerClsid) override; + STDMETHODIMP GetHandlerPayloadSize(NotNull aOutPayloadSize) override; + STDMETHODIMP WriteHandlerPayload(NotNull aStream) override; + STDMETHODIMP_(REFIID) MarshalAs(REFIID aIid) override; // ICallFrameWalker STDMETHODIMP OnWalkInterface(REFIID aIid, PVOID* aInterface, BOOL aIsInParam, BOOL aIsOutParam) override; private: - explicit MainThreadHandoff(IHandlerPayload* aHandlerPayload); + explicit MainThreadHandoff(IHandlerProvider* aHandlerProvider); ~MainThreadHandoff(); HRESULT FixArrayElements(ICallFrame* aFrame, const ArrayData& aArrayData); HRESULT FixIServiceProvider(ICallFrame* aFrame); private: - ULONG mRefCnt; - RefPtr mInterceptor; - RefPtr mHandlerPayload; + ULONG mRefCnt; + RefPtr mInterceptor; + RefPtr mHandlerProvider; }; } // namespace mscom diff --git a/ipc/mscom/Ptr.h b/ipc/mscom/Ptr.h index 9fd3c6115c6f..800abaf93530 100644 --- a/ipc/mscom/Ptr.h +++ b/ipc/mscom/Ptr.h @@ -153,6 +153,14 @@ ToSTAUniquePtr(RefPtr&& aRefPtr) return STAUniquePtr(aRefPtr.forget().take()); } +template +inline STAUniquePtr +ToSTAUniquePtr(const RefPtr& aRefPtr) +{ + MOZ_ASSERT(NS_IsMainThread()); + return STAUniquePtr(do_AddRef(aRefPtr).take()); +} + template inline STAUniquePtr ToSTAUniquePtr(T* aRawPtr) @@ -164,6 +172,15 @@ ToSTAUniquePtr(T* aRawPtr) return STAUniquePtr(aRawPtr); } +template +inline STAUniquePtr +ToSTAUniquePtr(const InterceptorTargetPtr& aTarget) +{ + MOZ_ASSERT(NS_IsMainThread()); + RefPtr newRef(static_cast(aTarget.get())); + return ToSTAUniquePtr(Move(newRef)); +} + template inline MTAUniquePtr ToMTAUniquePtr(RefPtr&& aRefPtr) @@ -171,6 +188,14 @@ ToMTAUniquePtr(RefPtr&& aRefPtr) return MTAUniquePtr(aRefPtr.forget().take()); } +template +inline MTAUniquePtr +ToMTAUniquePtr(const RefPtr& aRefPtr) +{ + MOZ_ASSERT(IsCurrentThreadMTA()); + return MTAUniquePtr(do_AddRef(aRefPtr).take()); +} + template inline MTAUniquePtr ToMTAUniquePtr(T* aRawPtr) @@ -189,6 +214,17 @@ ToProxyUniquePtr(RefPtr&& aRefPtr) return ProxyUniquePtr(aRefPtr.forget().take()); } +template +inline ProxyUniquePtr +ToProxyUniquePtr(const RefPtr& aRefPtr) +{ + MOZ_ASSERT(IsProxy(aRawPtr)); + MOZ_ASSERT((XRE_IsParentProcess() && NS_IsMainThread()) || + (XRE_IsContentProcess() && IsCurrentThreadMTA())); + + return ProxyUniquePtr(do_AddRef(aRefPtr).take()); +} + template inline ProxyUniquePtr ToProxyUniquePtr(T* aRawPtr) @@ -203,6 +239,13 @@ ToProxyUniquePtr(T* aRawPtr) return ProxyUniquePtr(aRawPtr); } +template +inline InterceptorTargetPtr +ToInterceptorTargetPtr(const UniquePtr& aTargetPtr) +{ + return InterceptorTargetPtr(aTargetPtr.get()); +} + template inline detail::UniquePtrGetterAddRefs getter_AddRefs(UniquePtr& aSmartPtr) diff --git a/ipc/mscom/moz.build b/ipc/mscom/moz.build index 06896eb7ed7c..d0177cbfab30 100644 --- a/ipc/mscom/moz.build +++ b/ipc/mscom/moz.build @@ -33,7 +33,7 @@ if CONFIG['ACCESSIBILITY']: EXPORTS.mozilla.mscom += [ 'ActivationContext.h', 'DispatchForwarder.h', - 'IHandlerPayload.h', + 'IHandlerProvider.h', 'Interceptor.h', 'InterceptorLog.h', 'MainThreadHandoff.h', diff --git a/ipc/mscom/oop/Handler.cpp b/ipc/mscom/oop/Handler.cpp index d497f21e42b7..a9e190e19d84 100644 --- a/ipc/mscom/oop/Handler.cpp +++ b/ipc/mscom/oop/Handler.cpp @@ -8,6 +8,7 @@ #include "Module.h" #include "mozilla/ArrayUtils.h" +#include "mozilla/Assertions.h" #include "nsWindowsHelpers.h" #include @@ -21,27 +22,29 @@ namespace mozilla { namespace mscom { -Handler::Handler(IUnknown* aOuter, HRESULT& aResult) +Handler::Handler(IUnknown* aOuter, HRESULT* aResult) : mRefCnt(0) , mOuter(aOuter) , mUnmarshal(nullptr) , mHasPayload(false) { + MOZ_ASSERT(aResult); + if (!aOuter) { - aResult = E_INVALIDARG; + *aResult = E_INVALIDARG; return; } StabilizedRefCount stabilizer(mRefCnt); - aResult = ::CoGetStdMarshalEx(aOuter, SMEXF_HANDLER, - getter_AddRefs(mInnerUnk)); - if (FAILED(aResult)) { + *aResult = ::CoGetStdMarshalEx(aOuter, SMEXF_HANDLER, + getter_AddRefs(mInnerUnk)); + if (FAILED(*aResult)) { return; } - aResult = mInnerUnk->QueryInterface(IID_IMarshal, (void**)&mUnmarshal); - if (FAILED(aResult)) { + *aResult = mInnerUnk->QueryInterface(IID_IMarshal, (void**)&mUnmarshal); + if (FAILED(*aResult)) { return; } @@ -90,11 +93,12 @@ Handler::InternalAddRef() ULONG Handler::InternalRelease() { - if (--mRefCnt == 0) { + ULONG newRefCnt = --mRefCnt; + if (newRefCnt == 0) { delete this; Module::Unlock(); } - return mRefCnt; + return newRefCnt; } HRESULT diff --git a/ipc/mscom/oop/Handler.h b/ipc/mscom/oop/Handler.h index 8353313c11e0..36d0a2a03ac9 100644 --- a/ipc/mscom/oop/Handler.h +++ b/ipc/mscom/oop/Handler.h @@ -106,7 +106,7 @@ public: static HRESULT Unregister(REFCLSID aClsid); protected: - Handler(IUnknown* aOuter, HRESULT& aResult); + Handler(IUnknown* aOuter, HRESULT* aResult); virtual ~Handler() {} bool HasPayload() const { return mHasPayload; } IUnknown* GetOuter() const { return mOuter; } diff --git a/js/src/devtools/automation/autospider.py b/js/src/devtools/automation/autospider.py index a409b5849c98..842963375205 100755 --- a/js/src/devtools/automation/autospider.py +++ b/js/src/devtools/automation/autospider.py @@ -38,6 +38,8 @@ parser = argparse.ArgumentParser( description='Run a spidermonkey shell build job') parser.add_argument('--dep', action='store_true', help='do not clobber the objdir before building') +parser.add_argument('--keep', action='store_true', + help='do not delete the sanitizer output directory (for testing)') parser.add_argument('--platform', '-p', type=str, metavar='PLATFORM', default='', help='build platform, including a suffix ("-debug" or "") used by buildbot to override the variant\'s "debug" setting. The platform can be used to specify 32 vs 64 bits.') parser.add_argument('--timeout', '-t', type=int, metavar='TIMEOUT', @@ -275,7 +277,7 @@ timer.daemon = True timer.start() ensure_dir_exists(OBJDIR, clobber=not args.dep and not args.nobuild) -ensure_dir_exists(OUTDIR) +ensure_dir_exists(OUTDIR, clobber=not args.keep) def run_command(command, check=False, **kwargs): @@ -373,6 +375,8 @@ test_suites |= set(normalize_tests(variant.get('extra-tests', {}).get('all', []) # Now adjust the variant's default test list with command-line arguments. test_suites |= set(normalize_tests(args.run_tests.split(","))) test_suites -= set(normalize_tests(args.skip_tests.split(","))) +if 'all' in args.skip_tests.split(","): + test_suites = [] # Always run all enabled tests, even if earlier ones failed. But return the # first failed status. @@ -408,6 +412,7 @@ if args.variant in ('tsan', 'msan'): # Summarize results sites = Counter() + errors = Counter() for filename in fullfiles: with open(os.path.join(OUTDIR, filename), 'rb') as fh: for line in fh: @@ -427,11 +432,48 @@ if args.variant in ('tsan', 'msan'): print >> outfh, "%d %s" % (count, location) print(open(summary_filename, 'rb').read()) + max_allowed = None if 'max-errors' in variant: - print("Found %d errors out of %d allowed" % (len(sites), variant['max-errors'])) - if len(sites) > variant['max-errors']: + max_allowed = variant['max-errors'] + elif 'expect-errors' in variant: + max_allowed = len(variant['expect-errors']) + + if max_allowed is not None: + print("Found %d errors out of %d allowed" % (len(sites), max_allowed)) + if len(sites) > max_allowed: results.append(1) + if 'expect-errors' in variant: + # Line numbers may shift around between versions, so just look for + # matching filenames and function names. This will still produce false + # positives when functions are renamed or moved between files, or + # things change so that the actual race is in a different place. But it + # still seems preferable to saying "You introduced an additional race. + # Here are the 21 races detected; please ignore the 20 known ones in + # this other list." + + for site in sites: + # Grab out the file and function names. + m = re.search(r'/([^/]+):\d+ in (.+)', site) + if m: + error = tuple(m.groups()) + else: + # will get here if eg tsan symbolication fails + error = (site, '(unknown)') + errors[error] += 1 + + remaining = Counter(errors) + for expect in variant['expect-errors']: + # expect-errors is an array of (filename, function) tuples. + expect = tuple(expect) + if remaining[expect] == 0: + print("Did not see expected error in %s function %s" % expect) + else: + remaining[expect] -= 1 + + for filename, function in (e for e, c in remaining.items() if c > 0): + print("*** tsan error in %s function %s" % (filename, function)) + # Gather individual results into a tarball. Note that these are # distinguished only by pid of the JS process running within each test, so # given the 16-bit limitation of pids, it's totally possible that some of diff --git a/js/src/devtools/automation/variants/tsan b/js/src/devtools/automation/variants/tsan index f831a5b041c7..8d9e1a841d39 100644 --- a/js/src/devtools/automation/variants/tsan +++ b/js/src/devtools/automation/variants/tsan @@ -7,7 +7,29 @@ "LLVM_SYMBOLIZER": "{TOOLTOOL_CHECKOUT}/clang/bin/llvm-symbolizer", "JITTEST_EXTRA_ARGS": "--jitflags=debug --ignore-timeouts={DIR}/cgc-jittest-timeouts.txt", "JSTESTS_EXTRA_ARGS": "--exclude-file={DIR}/cgc-jstests-slow.txt", - "TSAN_OPTIONS": "log_path={OUTDIR}/sanitize_log" + "TSAN_OPTIONS": "exitcode=0 log_path={OUTDIR}/sanitize_log" }, - "max-errors": 14 + "[comment on expect-errors]": "Note that expect-errors may contain duplicates. These indicate that tsan reports errors as two distinct line numbers. We cannot just insert line numbers, because they will shift around between versions.", + "expect-errors": [ + [ "Shape.h", "inDictionary" ], + [ "jsfriendapi.h", "GetObjectClass" ], + [ "Shape.h", "maybeSlot" ], + [ "Barrier.h", "set" ], + [ "jitprofiling.c", "iJIT_GetNewMethodID" ], + [ "Statistics.h", "count" ], + [ "Shape.h", "setOverwritten" ], + [ "TestingFunctions.cpp", "js::DefineTestingFunctions(JSContext*, JS::Handle, bool, bool)" ], + [ "TestingFunctions.cpp", "js::DefineTestingFunctions(JSContext*, JS::Handle, bool, bool)" ], + [ "OSObject.cpp", "js::shell::DefineOS(JSContext*, JS::Handle, bool, js::shell::RCFile**, js::shell::RCFile**)" ], + [ "OSObject.cpp", "js::shell::DefineOS(JSContext*, JS::Handle, bool, js::shell::RCFile**, js::shell::RCFile**)" ], + [ "ObjectGroup.h", "addendumKind" ], + [ "jsfriendapi.h", "numFixedSlots" ], + [ "Marking.cpp", "js::GCMarker::reset()" ], + [ "jsfun.h", "setResolvedLength" ], + [ "Shape.h", "incrementNumLinearSearches" ], + [ "Statistics.h", "js::gc::GCRuntime::pickChunk(js::AutoLockGC const&, js::gc::AutoMaybeStartBackgroundAllocation&)" ], + [ "jsfun.h", "needsSomeEnvironmentObject" ], + [ "TypeInference-inl.h", "setBasePropertyCount" ], + [ "Statistics.h", "js::gc::GCRuntime::getOrAllocChunk(js::AutoLockGC const&, js::gc::AutoMaybeStartBackgroundAllocation&)" ] + ] } diff --git a/js/src/jit-test/tests/ion/recover-newarrayiterator.js b/js/src/jit-test/tests/ion/recover-newarrayiterator.js new file mode 100644 index 000000000000..7947ac77c997 --- /dev/null +++ b/js/src/jit-test/tests/ion/recover-newarrayiterator.js @@ -0,0 +1,63 @@ +var max = 40; +setJitCompilerOption("ion.warmup.trigger", max - 10); + +function selfhosted() { + if (typeof getSelfHostedValue === "undefined") + return; + + var NewArrayIterator = getSelfHostedValue("NewArrayIterator"); + var iter = NewArrayIterator(); + bailout(); + // assertRecoveredOnBailout(iter, true); +} + +function iterator(i) { + var array = [1, i]; + var iter = array[Symbol.iterator](); + assertEq(iter.next().value, 1); + bailout(); + // This sometimes fails + // assertRecoveredOnBailout(iter, true); + var result = iter.next(); + assertEq(result.value, i); + assertEq(result.done, false); + assertEq(iter.next().done, true); +} + +function forof(i) { + var array = [1, i]; + var first = true; + + for (var x of array) { + if (first) { + assertEq(x, 1); + bailout(); + first = false; + } else { + assertEq(x, i); + } + } +} + +var data = { + a: 'foo', + b: {c: 'd'}, + arr: [1, 2, 3] +}; + +function fn() { + var {a, b:{c:b}, arr:[, c]} = data; + return c; +} + +function destructuring() { + for (var i = 0; i < max; i++) + assertEq(fn(), 2); +} + +for (var i = 0; i < max; i++) { + selfhosted(); + iterator(i); + forof(i); + destructuring(); +} diff --git a/js/src/jit/JitcodeMap.cpp b/js/src/jit/JitcodeMap.cpp index 33c74fc15153..944abb78fc9e 100644 --- a/js/src/jit/JitcodeMap.cpp +++ b/js/src/jit/JitcodeMap.cpp @@ -12,8 +12,6 @@ #include "mozilla/SizePrintfMacros.h" #include "mozilla/Sprintf.h" -#include - #include "jsprf.h" #include "gc/Marking.h" @@ -645,14 +643,14 @@ JitcodeGlobalTable::generateTowerHeight() rand_ ^= mozilla::RotateLeft(rand_, 5) ^ mozilla::RotateLeft(rand_, 24); rand_ += 0x37798849; - // Return number of lowbit zeros in new randval. + // Return 1 + number of lowbit zeros in new randval, capped at MAX_HEIGHT. unsigned result = 0; - for (unsigned i = 0; i < 32; i++) { + for (unsigned i = 0; i < JitcodeSkiplistTower::MAX_HEIGHT - 1; i++) { if ((rand_ >> i) & 0x1) break; result++; } - return (std::max)(1U, result); + return result + 1; } JitcodeSkiplistTower* diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index 02ee711a89e6..70becbdd88fe 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -4932,10 +4932,12 @@ MObjectState::templateObjectOf(MDefinition* obj) return obj->toNewObject()->templateObject(); else if (obj->isCreateThisWithTemplate()) return obj->toCreateThisWithTemplate()->templateObject(); - else + else if (obj->isNewCallObject()) return obj->toNewCallObject()->templateObject(); + else if (obj->isNewArrayIterator()) + return obj->toNewArrayIterator()->templateObject(); - return nullptr; + MOZ_CRASH("unreachable"); } bool diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 0857f23dd3e4..f5e2df92c69d 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -3556,6 +3556,11 @@ class MNewArrayIterator AliasSet getAliasSet() const override { return AliasSet::None(); } + + MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override; + bool canRecoverOnBailout() const override { + return true; + } }; diff --git a/js/src/jit/Recover.cpp b/js/src/jit/Recover.cpp index f55389658ac4..78e6876a74a0 100644 --- a/js/src/jit/Recover.cpp +++ b/js/src/jit/Recover.cpp @@ -10,6 +10,7 @@ #include "jsapi.h" #include "jscntxt.h" +#include "jsiter.h" #include "jsmath.h" #include "jsobj.h" #include "jsstr.h" @@ -1365,6 +1366,34 @@ RNewArray::recover(JSContext* cx, SnapshotIterator& iter) const return true; } +bool +MNewArrayIterator::writeRecoverData(CompactBufferWriter& writer) const +{ + MOZ_ASSERT(canRecoverOnBailout()); + writer.writeUnsigned(uint32_t(RInstruction::Recover_NewArrayIterator)); + return true; +} + +RNewArrayIterator::RNewArrayIterator(CompactBufferReader& reader) +{ +} + +bool +RNewArrayIterator::recover(JSContext* cx, SnapshotIterator& iter) const +{ + RootedObject templateObject(cx, &iter.read().toObject()); + RootedValue result(cx); + + + JSObject* resultObject = NewArrayIteratorObject(cx); + if (!resultObject) + return false; + + result.setObject(*resultObject); + iter.storeInstructionResult(result); + return true; +} + bool MNewDerivedTypedObject::writeRecoverData(CompactBufferWriter& writer) const { diff --git a/js/src/jit/Recover.h b/js/src/jit/Recover.h index 709bfa796087..ff50179073b2 100644 --- a/js/src/jit/Recover.h +++ b/js/src/jit/Recover.h @@ -102,6 +102,7 @@ namespace jit { _(NewObject) \ _(NewTypedArray) \ _(NewArray) \ + _(NewArrayIterator) \ _(NewDerivedTypedObject) \ _(CreateThisWithTemplate) \ _(Lambda) \ @@ -593,6 +594,14 @@ class RNewArray final : public RInstruction MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const override; }; +class RNewArrayIterator final : public RInstruction +{ + public: + RINSTRUCTION_HEADER_NUM_OP_(NewArrayIterator, 1) + + MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const override; +}; + class RNewDerivedTypedObject final : public RInstruction { public: diff --git a/js/src/jit/ScalarReplacement.cpp b/js/src/jit/ScalarReplacement.cpp index 4614b21627de..0c87d15e7dc7 100644 --- a/js/src/jit/ScalarReplacement.cpp +++ b/js/src/jit/ScalarReplacement.cpp @@ -134,6 +134,13 @@ IsLambdaEscaped(MLambda* lambda, JSObject* obj) return false; } +static inline bool +IsOptimizableObjectInstruction(MInstruction* ins) +{ + return ins->isNewObject() || ins->isCreateThisWithTemplate() || ins->isNewCallObject() || + ins->isNewArrayIterator(); +} + // Returns False if the object is not escaped and if it is optimizable by // ScalarReplacementOfObject. // @@ -143,8 +150,8 @@ static bool IsObjectEscaped(MInstruction* ins, JSObject* objDefault) { MOZ_ASSERT(ins->type() == MIRType::Object); - MOZ_ASSERT(ins->isNewObject() || ins->isGuardShape() || ins->isCreateThisWithTemplate() || - ins->isNewCallObject() || ins->isFunctionEnvironment()); + MOZ_ASSERT(IsOptimizableObjectInstruction(ins) || ins->isGuardShape() || + ins->isFunctionEnvironment()); JitSpewDef(JitSpew_Escape, "Check object\n", ins); JitSpewIndent spewIndent(JitSpew_Escape); @@ -1311,8 +1318,7 @@ ScalarReplacement(MIRGenerator* mir, MIRGraph& graph) return false; for (MInstructionIterator ins = block->begin(); ins != block->end(); ins++) { - if ((ins->isNewObject() || ins->isCreateThisWithTemplate() || ins->isNewCallObject()) && - !IsObjectEscaped(*ins)) + if (IsOptimizableObjectInstruction(*ins) && !IsObjectEscaped(*ins)) { ObjectMemoryView view(graph.alloc(), *ins); if (!replaceObject.run(view)) diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 8070fc77303d..202956dc0f89 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -4511,10 +4511,10 @@ JSCompartment::findDeadProxyZoneEdges(bool* foundAny) if (IsDeadProxyObject(&value.toObject())) { *foundAny = true; CrossCompartmentKey& key = e.front().mutableKey(); - Zone* wrapperZone = key.as()->zone(); - if (!wrapperZone->isGCMarking()) + Zone* wrappedZone = key.as()->zone(); + if (!wrappedZone->isGCMarking()) continue; - if (!wrapperZone->gcSweepGroupEdges().put(zone())) + if (!wrappedZone->gcSweepGroupEdges().put(zone())) return false; } } diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index c70d22095a7d..e90d6830fa74 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -1947,8 +1947,12 @@ Debugger::slowPathOnNewScript(JSContext* cx, HandleScript script) return JSTRAP_CONTINUE; }); - if (status == JSTRAP_ERROR) + // dispatchHook may fail due to OOM. This OOM is not handlable at the + // callsites of onNewScript in the engine. + if (status == JSTRAP_ERROR) { + cx->clearPendingException(); return; + } MOZ_ASSERT(status == JSTRAP_CONTINUE); } @@ -1967,8 +1971,12 @@ Debugger::slowPathOnNewWasmInstance(JSContext* cx, Handle w return JSTRAP_CONTINUE; }); - if (status == JSTRAP_ERROR) + // dispatchHook may fail due to OOM. This OOM is not handlable at the + // callsites of onNewWasmInstance in the engine. + if (status == JSTRAP_ERROR) { + cx->clearPendingException(); return; + } MOZ_ASSERT(status == JSTRAP_CONTINUE); } diff --git a/js/xpconnect/src/XPCJSContext.cpp b/js/xpconnect/src/XPCJSContext.cpp index cccedc81b12d..d724d2291ab8 100644 --- a/js/xpconnect/src/XPCJSContext.cpp +++ b/js/xpconnect/src/XPCJSContext.cpp @@ -148,7 +148,7 @@ public: uint32_t((TimeStamp::Now() - start).ToMilliseconds())); if (hadSnowWhiteObjects && !mContinuation) { mContinuation = true; - if (NS_FAILED(NS_DispatchToCurrentThread(this))) { + if (NS_FAILED(Dispatch())) { mActive = false; } } else { @@ -169,13 +169,25 @@ public: return NS_OK; } - void Dispatch(bool aContinuation = false, bool aPurge = false) + nsresult Dispatch() + { + if (NS_IsMainThread()) { + nsCOMPtr self(this); + return SystemGroup::Dispatch("AsyncFreeSnowWhite", + TaskCategory::GarbageCollection, + self.forget()); + } else { + return NS_DispatchToCurrentThread(this); + } + } + + void Start(bool aContinuation = false, bool aPurge = false) { if (mContinuation) { mContinuation = aContinuation; } mPurge = aPurge; - if (!mActive && NS_SUCCEEDED(NS_DispatchToCurrentThread(this))) { + if (!mActive && NS_SUCCEEDED(Dispatch())) { mActive = true; } } @@ -735,7 +747,7 @@ XPCJSContext::EndCycleCollectionCallback(CycleCollectorResults& aResults) void XPCJSContext::DispatchDeferredDeletion(bool aContinuation, bool aPurge) { - mAsyncSnowWhiteFreer->Dispatch(aContinuation, aPurge); + mAsyncSnowWhiteFreer->Start(aContinuation, aPurge); } void diff --git a/js/xpconnect/src/XPCWrappedNativeScope.cpp b/js/xpconnect/src/XPCWrappedNativeScope.cpp index be86e0878158..e11fb542d5dc 100644 --- a/js/xpconnect/src/XPCWrappedNativeScope.cpp +++ b/js/xpconnect/src/XPCWrappedNativeScope.cpp @@ -97,6 +97,7 @@ XPCWrappedNativeScope::XPCWrappedNativeScope(JSContext* cx, mNext(nullptr), mGlobalJSObject(aGlobal), mHasCallInterpositions(false), + mDocGroupValidation(false), mIsContentXBLScope(false), mIsAddonScope(false) { @@ -167,6 +168,9 @@ XPCWrappedNativeScope::XPCWrappedNativeScope(JSContext* cx, if (addonId) { // We forbid CPOWs unless they're specifically allowed. priv->allowCPOWs = gAllowCPOWAddonSet ? gAllowCPOWAddonSet->has(addonId) : false; + + // Automatically opt into DocGroup validation for add-on compartments. + mDocGroupValidation = true; } } diff --git a/js/xpconnect/src/nsXPConnect.cpp b/js/xpconnect/src/nsXPConnect.cpp index 2ba0ddaf9fb7..983f07136836 100644 --- a/js/xpconnect/src/nsXPConnect.cpp +++ b/js/xpconnect/src/nsXPConnect.cpp @@ -1373,6 +1373,12 @@ AllowCPOWsInAddon(const nsACString& addonIdStr, bool allow) return XPCWrappedNativeScope::AllowCPOWsInAddon(jsapi.cx(), addonId, allow); } +void +SetDocGroupValidation(JSObject* global) +{ + CompartmentPrivate::Get(global)->scope->SetDocGroupValidation(); +} + } // namespace xpc namespace mozilla { diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index 9f2dbe04c208..adaa832a517e 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -866,6 +866,9 @@ public: bool AttachComponentsObject(JSContext* aCx); + void SetDocGroupValidation() { mDocGroupValidation = true; } + bool HasDocGroupValidation() const { return mDocGroupValidation; } + // Returns the JS object reflection of the Components object. bool GetComponentsJSObject(JS::MutableHandleObject obj); @@ -1067,6 +1070,12 @@ private: // from this scope if the caller scope has mInterposition set. bool mHasCallInterpositions; + // If this flag is set, Xray wrappers from this compartment to content + // compartments will be DocGroupValidationWrappers of some + // sort. Consequently, this compartment will throw instead of asserting for + // DocGroup mismatches. + bool mDocGroupValidation; + nsAutoPtr mDOMExpandoSet; JS::WeakMapPtr mXrayExpandos; diff --git a/js/xpconnect/src/xpcpublic.h b/js/xpconnect/src/xpcpublic.h index 46fc230f30c7..98dcaf4a837e 100644 --- a/js/xpconnect/src/xpcpublic.h +++ b/js/xpconnect/src/xpcpublic.h @@ -415,6 +415,8 @@ void NukeAllWrappersForCompartment(JSContext* cx, JSCompartment* compartment, void SetLocationForGlobal(JSObject* global, const nsACString& location); void SetLocationForGlobal(JSObject* global, nsIURI* locationURI); +void SetDocGroupValidation(JSObject* global); + // ReportJSRuntimeExplicitTreeStats will expect this in the |extra| member // of JS::ZoneStats. class ZoneStatsExtras { diff --git a/js/xpconnect/wrappers/DocGroupValidationWrapper.cpp b/js/xpconnect/wrappers/DocGroupValidationWrapper.cpp new file mode 100644 index 000000000000..d6b848d246cb --- /dev/null +++ b/js/xpconnect/wrappers/DocGroupValidationWrapper.cpp @@ -0,0 +1,274 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=4 et sw=4 tw=99: */ +/* 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 "DocGroupValidationWrapper.h" +#include "AddonWrapper.h" +#include "WaiveXrayWrapper.h" + +#include "mozilla/dom/DocGroup.h" +#include "nsGlobalWindow.h" + +using namespace xpc; + +static bool +AccessAllowed(JSContext* cx, JS::Handle wrapper) +{ + RootedObject unwrapped(cx, UncheckedUnwrap(wrapper)); + RefPtr window = WindowGlobalOrNull(unwrapped); + if (!window) { + // Access to non-windows is always kosher. + return true; + } + + if (window->GetDocGroup()->AccessAllowed()) + return true; + + static bool sThrowOnMismatch; + static bool sPrefInitialized; + if (!sPrefInitialized) { + sPrefInitialized = true; + Preferences::AddBoolVarCache(&sThrowOnMismatch, + "extensions.throw_on_docgroup_mismatch.enabled"); + } + + // If DocGroup validation is disabled, don't throw. + if (!sThrowOnMismatch) + return true; + + JS_ReportErrorASCII(cx, "accessing object in wrong DocGroup"); + return false; +} + +template +bool +DocGroupValidationWrapper::getOwnPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id, + MutableHandle desc) const +{ + return AccessAllowed(cx, wrapper) && + Base::getOwnPropertyDescriptor(cx, wrapper, id, desc); +} + +template +bool +DocGroupValidationWrapper::defineProperty(JSContext* cx, HandleObject wrapper, HandleId id, + Handle desc, + ObjectOpResult& result) const +{ + return AccessAllowed(cx, wrapper) && + Base::defineProperty(cx, wrapper, id, desc, result); +} + +template +bool +DocGroupValidationWrapper::ownPropertyKeys(JSContext* cx, HandleObject wrapper, + AutoIdVector& props) const +{ + return AccessAllowed(cx, wrapper) && + Base::ownPropertyKeys(cx, wrapper, props); +} + +template +bool +DocGroupValidationWrapper::delete_(JSContext* cx, HandleObject wrapper, HandleId id, + ObjectOpResult& result) const +{ + return AccessAllowed(cx, wrapper) && + Base::delete_(cx, wrapper, id, result); +} + +template +bool +DocGroupValidationWrapper::enumerate(JSContext* cx, HandleObject wrapper, MutableHandleObject objp) const +{ + return AccessAllowed(cx, wrapper) && + Base::enumerate(cx, wrapper, objp); +} + +template +bool +DocGroupValidationWrapper::getPrototype(JSContext* cx, HandleObject proxy, + MutableHandleObject protop) const +{ + return AccessAllowed(cx, proxy) && + Base::getPrototype(cx, proxy, protop); +} + +template +bool +DocGroupValidationWrapper::setPrototype(JSContext* cx, HandleObject proxy, HandleObject proto, + ObjectOpResult& result) const +{ + return AccessAllowed(cx, proxy) && + Base::setPrototype(cx, proxy, proto, result); +} + +template +bool +DocGroupValidationWrapper::getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* isOrdinary, + MutableHandleObject protop) const +{ + return AccessAllowed(cx, proxy) && + Base::getPrototypeIfOrdinary(cx, proxy, isOrdinary, protop); +} + +template +bool +DocGroupValidationWrapper::setImmutablePrototype(JSContext* cx, HandleObject proxy, + bool* succeeded) const +{ + return AccessAllowed(cx, proxy) && + Base::setImmutablePrototype(cx, proxy, succeeded); +} + +template +bool +DocGroupValidationWrapper::preventExtensions(JSContext* cx, HandleObject wrapper, + ObjectOpResult& result) const +{ + return AccessAllowed(cx, wrapper) && + Base::preventExtensions(cx, wrapper, result); +} + +template +bool +DocGroupValidationWrapper::isExtensible(JSContext* cx, HandleObject wrapper, bool* extensible) const +{ + return AccessAllowed(cx, wrapper) && + Base::isExtensible(cx, wrapper, extensible); +} + +template +bool +DocGroupValidationWrapper::has(JSContext* cx, HandleObject wrapper, HandleId id, bool* bp) const +{ + return AccessAllowed(cx, wrapper) && + Base::has(cx, wrapper, id, bp); +} + +template +bool +DocGroupValidationWrapper::get(JSContext* cx, HandleObject wrapper, HandleValue receiver, + HandleId id, MutableHandleValue vp) const +{ + return AccessAllowed(cx, wrapper) && + Base::get(cx, wrapper, receiver, id, vp); +} + +template +bool +DocGroupValidationWrapper::set(JSContext* cx, HandleObject wrapper, HandleId id, HandleValue v, + HandleValue receiver, ObjectOpResult& result) const +{ + return AccessAllowed(cx, wrapper) && + Base::set(cx, wrapper, id, v, receiver, result); +} + +template +bool +DocGroupValidationWrapper::call(JSContext* cx, HandleObject wrapper, const CallArgs& args) const +{ + return AccessAllowed(cx, wrapper) && + Base::call(cx, wrapper, args); +} + +template +bool +DocGroupValidationWrapper::construct(JSContext* cx, HandleObject wrapper, const CallArgs& args) const +{ + return AccessAllowed(cx, wrapper) && + Base::construct(cx, wrapper, args); +} + +template +bool +DocGroupValidationWrapper::getPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id, + MutableHandle desc) const +{ + return AccessAllowed(cx, wrapper) && + Base::getPropertyDescriptor(cx, wrapper, id, desc); +} + +template +bool +DocGroupValidationWrapper::hasOwn(JSContext* cx, HandleObject wrapper, HandleId id, bool* bp) const +{ + return AccessAllowed(cx, wrapper) && + Base::hasOwn(cx, wrapper, id, bp); +} + +template +bool +DocGroupValidationWrapper::getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject wrapper, + AutoIdVector& props) const +{ + return AccessAllowed(cx, wrapper) && + Base::getOwnEnumerablePropertyKeys(cx, wrapper, props); +} + +template +bool +DocGroupValidationWrapper::hasInstance(JSContext* cx, HandleObject wrapper, MutableHandleValue v, + bool* bp) const +{ + return AccessAllowed(cx, wrapper) && + Base::hasInstance(cx, wrapper, v, bp); +} + +template +const char* +DocGroupValidationWrapper::className(JSContext* cx, HandleObject proxy) const +{ + return AccessAllowed(cx, proxy) + ? Base::className(cx, proxy) + : "forbidden"; +} + +template +JSString* +DocGroupValidationWrapper::fun_toString(JSContext* cx, HandleObject wrapper, + unsigned indent) const +{ + return AccessAllowed(cx, wrapper) + ? Base::fun_toString(cx, wrapper, indent) + : nullptr; +} + +template +bool +DocGroupValidationWrapper::regexp_toShared(JSContext* cx, HandleObject proxy, MutableHandle g) const +{ + return AccessAllowed(cx, proxy) && + Base::regexp_toShared(cx, proxy, g); +} + +template +bool +DocGroupValidationWrapper::boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const +{ + return AccessAllowed(cx, proxy) && + Base::boxedValue_unbox(cx, proxy, vp); +} + +namespace xpc { + +template +const DocGroupValidationWrapper DocGroupValidationWrapper::singleton(0); + +#define DEFINE_SINGLETON(Base) \ + template class DocGroupValidationWrapper + +DEFINE_SINGLETON(CrossCompartmentWrapper); +DEFINE_SINGLETON(PermissiveXrayXPCWN); +DEFINE_SINGLETON(PermissiveXrayDOM); +DEFINE_SINGLETON(PermissiveXrayJS); + +DEFINE_SINGLETON(AddonWrapper); +DEFINE_SINGLETON(AddonWrapper); +DEFINE_SINGLETON(AddonWrapper); + +DEFINE_SINGLETON(WaiveXrayWrapper); + +} // namespace xpc diff --git a/js/xpconnect/wrappers/DocGroupValidationWrapper.h b/js/xpconnect/wrappers/DocGroupValidationWrapper.h new file mode 100644 index 000000000000..e586cb078dd5 --- /dev/null +++ b/js/xpconnect/wrappers/DocGroupValidationWrapper.h @@ -0,0 +1,87 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=4 et sw=4 tw=99: */ +/* 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/. */ + +#ifndef xpc_DocGroupValidationWrapper_h +#define xpc_DocGroupValidationWrapper_h + +#include "mozilla/Attributes.h" + +#include "jswrapper.h" +#include "WaiveXrayWrapper.h" +#include "XrayWrapper.h" + +namespace xpc { + +template +class DocGroupValidationWrapper : public Base { + public: + explicit constexpr DocGroupValidationWrapper(unsigned flags) : Base(flags) { } + + /* Standard internal methods. */ + virtual bool getOwnPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id, + MutableHandle desc) const override; + virtual bool defineProperty(JSContext* cx, HandleObject wrapper, HandleId id, + Handle desc, + ObjectOpResult& result) const override; + virtual bool ownPropertyKeys(JSContext* cx, HandleObject wrapper, + AutoIdVector& props) const override; + virtual bool delete_(JSContext* cx, HandleObject wrapper, HandleId id, + ObjectOpResult& result) const override; + virtual bool enumerate(JSContext* cx, HandleObject wrapper, MutableHandleObject objp) const override; + virtual bool getPrototype(JSContext* cx, HandleObject proxy, + MutableHandleObject protop) const override; + virtual bool setPrototype(JSContext* cx, HandleObject proxy, HandleObject proto, + ObjectOpResult& result) const override; + + virtual bool getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* isOrdinary, + MutableHandleObject protop) const override; + virtual bool setImmutablePrototype(JSContext* cx, HandleObject proxy, + bool* succeeded) const override; + virtual bool preventExtensions(JSContext* cx, HandleObject wrapper, + ObjectOpResult& result) const override; + virtual bool isExtensible(JSContext* cx, HandleObject wrapper, bool* extensible) const override; + virtual bool has(JSContext* cx, HandleObject wrapper, HandleId id, bool* bp) const override; + virtual bool get(JSContext* cx, HandleObject wrapper, HandleValue receiver, + HandleId id, MutableHandleValue vp) const override; + virtual bool set(JSContext* cx, HandleObject wrapper, HandleId id, HandleValue v, + HandleValue receiver, ObjectOpResult& result) const override; + virtual bool call(JSContext* cx, HandleObject wrapper, const CallArgs& args) const override; + virtual bool construct(JSContext* cx, HandleObject wrapper, const CallArgs& args) const override; + + /* SpiderMonkey extensions. */ + virtual bool getPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id, + MutableHandle desc) const override; + virtual bool hasOwn(JSContext* cx, HandleObject wrapper, HandleId id, bool* bp) const override; + virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject wrapper, + AutoIdVector& props) const override; + virtual bool hasInstance(JSContext* cx, HandleObject wrapper, MutableHandleValue v, + bool* bp) const override; + virtual const char* className(JSContext* cx, HandleObject proxy) const override; + virtual JSString* fun_toString(JSContext* cx, HandleObject wrapper, + unsigned indent) const override; + virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, MutableHandle g) const override; + virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const override; + + static const DocGroupValidationWrapper singleton; +}; + +#define DECLARE_SINGLETON(Base) \ + extern template class DocGroupValidationWrapper + +DECLARE_SINGLETON(CrossCompartmentWrapper); +DECLARE_SINGLETON(PermissiveXrayXPCWN); +DECLARE_SINGLETON(PermissiveXrayDOM); +DECLARE_SINGLETON(PermissiveXrayJS); + +DECLARE_SINGLETON(AddonWrapper); +DECLARE_SINGLETON(AddonWrapper); +DECLARE_SINGLETON(AddonWrapper); + +DECLARE_SINGLETON(WaiveXrayWrapper); + +} // namespace xpc + +#endif // xpc_DocGroupValidationWrapper_h diff --git a/js/xpconnect/wrappers/WrapperFactory.cpp b/js/xpconnect/wrappers/WrapperFactory.cpp index 72ef062c7c85..66eb3b00fe28 100644 --- a/js/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/xpconnect/wrappers/WrapperFactory.cpp @@ -7,6 +7,7 @@ #include "WaiveXrayWrapper.h" #include "FilteringWrapper.h" #include "AddonWrapper.h" +#include "DocGroupValidationWrapper.h" #include "XrayWrapper.h" #include "AccessCheck.h" #include "XPCWrapper.h" @@ -447,6 +448,66 @@ SelectAddonWrapper(JSContext* cx, HandleObject obj, const Wrapper* wrapper) return wrapper; } +static bool +NeedsDocGroupValidationWrapper(JSContext* cx, HandleObject obj, + JSCompartment* origin, JSCompartment* target, + CompartmentPrivate* targetCompartmentPrivate) +{ + // We do DocGroup validation in the following circumstances: + // - Only if the target compartment has the DocGroupValidation() + // flag set (which includes any add-on compartments). + // - Only if we're in the content process. + // - Only if the origin compartment is not system principled and is not a sandbox. + // - Only if the target compartment is system principled. + + if (!targetCompartmentPrivate->scope->HasDocGroupValidation()) + return false; + + if (!XRE_IsContentProcess()) + return false; + + if (!AccessCheck::isChrome(target)) + return false; + + if (AccessCheck::isChrome(origin) || + IsSandbox(js::GetGlobalForObjectCrossCompartment(obj))) + { + return false; + } + + return true; +} + +static const Wrapper* +SelectDocGroupValidationWrapper(JSContext* cx, HandleObject obj, const Wrapper* wrapper) +{ + // Validation wrappers only supports certain wrapper types, so we check if + // we would have used one of the supported ones. + if (wrapper == &CrossCompartmentWrapper::singleton) + return &DocGroupValidationWrapper::singleton; + else if (wrapper == &PermissiveXrayXPCWN::singleton) + return &DocGroupValidationWrapper::singleton; + else if (wrapper == &PermissiveXrayDOM::singleton) + return &DocGroupValidationWrapper::singleton; + else if (wrapper == &PermissiveXrayJS::singleton) + return &DocGroupValidationWrapper::singleton; + else if (wrapper == &AddonWrapper::singleton) + return &DocGroupValidationWrapper>::singleton; + else if (wrapper == &AddonWrapper::singleton) + return &DocGroupValidationWrapper>::singleton; + else if (wrapper == &AddonWrapper::singleton) + return &DocGroupValidationWrapper>::singleton; + else if (wrapper == &WaiveXrayWrapper::singleton) + return &DocGroupValidationWrapper::singleton; + + // If NeedsDocGroupValidationWrapper is true, then securityWrapper should be false + // and xrayType should never be NotXray (since the target always subsumes the origin + // and the origin never subsumes the target). + MOZ_ASSERT(wrapper == &WaiveXrayWrapper::singleton || + wrapper == &(PermissiveXrayOpaque::singleton)); + return wrapper; +} + JSObject* WrapperFactory::Rewrap(JSContext* cx, HandleObject existing, HandleObject obj) { @@ -556,6 +617,13 @@ WrapperFactory::Rewrap(JSContext* cx, HandleObject existing, HandleObject obj) // then we try to "upgrade" the wrapper to an interposing one. if (targetCompartmentPrivate->scope->HasInterposition()) wrapper = SelectAddonWrapper(cx, obj, wrapper); + + // Now try another "upgrade" to a DocGroupValidationWrapper. + if (NeedsDocGroupValidationWrapper(cx, obj, origin, target, + targetCompartmentPrivate)) + { + wrapper = SelectDocGroupValidationWrapper(cx, obj, wrapper); + } } if (!targetSubsumesOrigin) { diff --git a/js/xpconnect/wrappers/moz.build b/js/xpconnect/wrappers/moz.build index 386e960a0277..d295f9482dc6 100644 --- a/js/xpconnect/wrappers/moz.build +++ b/js/xpconnect/wrappers/moz.build @@ -12,6 +12,7 @@ UNIFIED_SOURCES += [ 'AccessCheck.cpp', 'AddonWrapper.cpp', 'ChromeObjectWrapper.cpp', + 'DocGroupValidationWrapper.cpp', 'FilteringWrapper.cpp', 'WaiveXrayWrapper.cpp', 'WrapperFactory.cpp', diff --git a/layout/generic/CSSOrderAwareFrameIterator.cpp b/layout/generic/CSSOrderAwareFrameIterator.cpp new file mode 100644 index 000000000000..e77b5b12a7f4 --- /dev/null +++ b/layout/generic/CSSOrderAwareFrameIterator.cpp @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +/* Iterator class for frame lists that respect CSS "order" during layout */ + +#include "CSSOrderAwareFrameIterator.h" + +namespace mozilla { + +template<> +bool +CSSOrderAwareFrameIterator::CSSOrderComparator(nsIFrame* const& a, + nsIFrame* const& b) +{ return a->StylePosition()->mOrder < b->StylePosition()->mOrder; } + +template<> +bool +CSSOrderAwareFrameIterator::CSSBoxOrdinalGroupComparator(nsIFrame* const& a, + nsIFrame* const& b) +{ return a->StyleXUL()->mBoxOrdinal < b->StyleXUL()->mBoxOrdinal; } + +template<> +bool +CSSOrderAwareFrameIterator::IsForward() const { return true; } + +template<> +nsFrameList::iterator +CSSOrderAwareFrameIterator::begin(const nsFrameList& aList) +{ return aList.begin(); } + +template<> +nsFrameList::iterator CSSOrderAwareFrameIterator::end(const nsFrameList& aList) +{ return aList.end(); } + +template<> +bool +ReverseCSSOrderAwareFrameIterator::CSSOrderComparator(nsIFrame* const& a, + nsIFrame* const& b) +{ return a->StylePosition()->mOrder > b->StylePosition()->mOrder; } + +template<> +bool +ReverseCSSOrderAwareFrameIterator::CSSBoxOrdinalGroupComparator(nsIFrame* const& a, + nsIFrame* const& b) +{ return a->StyleXUL()->mBoxOrdinal > b->StyleXUL()->mBoxOrdinal; } + +template<> +bool +ReverseCSSOrderAwareFrameIterator::IsForward() const +{ return false; } + +template<> +nsFrameList::reverse_iterator +ReverseCSSOrderAwareFrameIterator::begin(const nsFrameList& aList) +{ return aList.rbegin(); } + +template<> +nsFrameList::reverse_iterator +ReverseCSSOrderAwareFrameIterator::end(const nsFrameList& aList) +{ return aList.rend(); } + +} // namespace mozilla diff --git a/layout/generic/CSSOrderAwareFrameIterator.h b/layout/generic/CSSOrderAwareFrameIterator.h new file mode 100644 index 000000000000..0da173e7b660 --- /dev/null +++ b/layout/generic/CSSOrderAwareFrameIterator.h @@ -0,0 +1,256 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +/* Iterator class for frame lists that respect CSS "order" during layout */ + +#ifndef mozilla_CSSOrderAwareFrameIterator_h +#define mozilla_CSSOrderAwareFrameIterator_h + +#include +#include "nsFrameList.h" +#include "nsIFrame.h" +#include "mozilla/Maybe.h" +#include "mozilla/Assertions.h" + +#if defined(__clang__) && __clang_major__ == 3 && __clang_minor__ <= 8 +#define CLANG_CRASH_BUG 1 +#endif + +namespace mozilla { + +template +class CSSOrderAwareFrameIteratorT +{ +public: + enum OrderState { eUnknownOrder, eKnownOrdered, eKnownUnordered }; + enum ChildFilter { eSkipPlaceholders, eIncludeAll }; + enum OrderingProperty { + eUseOrder, // Default behavior: use "order". + eUseBoxOrdinalGroup // Legacy behavior: use prefixed "box-ordinal-group". + }; + CSSOrderAwareFrameIteratorT(nsIFrame* aContainer, + nsIFrame::ChildListID aListID, + ChildFilter aFilter = eSkipPlaceholders, + OrderState aState = eUnknownOrder, + OrderingProperty aOrderProp = eUseOrder) + : mChildren(aContainer->GetChildList(aListID)) + , mArrayIndex(0) + , mItemIndex(0) + , mSkipPlaceholders(aFilter == eSkipPlaceholders) +#ifdef DEBUG + , mContainer(aContainer) + , mListID(aListID) +#endif + { + size_t count = 0; + bool isOrdered = aState != eKnownUnordered; + if (aState == eUnknownOrder) { + auto maxOrder = std::numeric_limits::min(); + for (auto child : mChildren) { + ++count; + + int32_t order; + if (aOrderProp == eUseBoxOrdinalGroup) { + // We'll be using mBoxOrdinal, which has type uint32_t. However, the + // modern 'order' property (whose functionality we're co-opting) has + // type int32_t. So: if we happen to have a uint32_t value that's + // greater than INT32_MAX, we clamp it rather than letting it + // overflow. Chances are, this is just an author using BIG_VALUE + // anyway, so the clamped value should be fine. + uint32_t clampedBoxOrdinal = + std::min(child->StyleXUL()->mBoxOrdinal, + static_cast(INT32_MAX)); + order = static_cast(clampedBoxOrdinal); + } else { + order = child->StylePosition()->mOrder; + } + + if (order < maxOrder) { + isOrdered = false; + break; + } + maxOrder = order; + } + } + if (isOrdered) { + mIter.emplace(begin(mChildren)); + mIterEnd.emplace(end(mChildren)); + } else { + count *= 2; // XXX somewhat arbitrary estimate for now... + mArray.emplace(count); + for (Iterator i(begin(mChildren)), iEnd(end(mChildren)); i != iEnd; ++i) { + mArray->AppendElement(*i); + } + auto comparator = (aOrderProp == eUseBoxOrdinalGroup) + ? CSSBoxOrdinalGroupComparator + : CSSOrderComparator; + + // XXX replace this with nsTArray::StableSort when bug 1147091 is fixed. + std::stable_sort(mArray->begin(), mArray->end(), comparator); + } + + if (mSkipPlaceholders) { + SkipPlaceholders(); + } + } + ~CSSOrderAwareFrameIteratorT() + { + MOZ_ASSERT(IsForward() == mItemCount.isNothing()); + } + + bool IsForward() const; + Iterator begin(const nsFrameList& aList); + Iterator end(const nsFrameList& aList); + + nsIFrame* operator*() const + { + MOZ_ASSERT(!AtEnd()); + if (mIter.isSome()) { + return **mIter; + } + return (*mArray)[mArrayIndex]; + } + + /** + * Return the child index of the current item, placeholders not counted. + * It's forbidden to call this method when the current frame is placeholder. + */ + size_t ItemIndex() const + { + MOZ_ASSERT(!AtEnd()); + MOZ_ASSERT((**this)->GetType() != nsGkAtoms::placeholderFrame, + "MUST not call this when at a placeholder"); + MOZ_ASSERT(IsForward() || mItemIndex < *mItemCount, + "Returning an out-of-range mItemIndex..."); + return mItemIndex; + } + + void SetItemCount(size_t aItemCount) + { +#ifndef CLANG_CRASH_BUG + MOZ_ASSERT(mIter.isSome() || aItemCount <= mArray->Length(), + "item count mismatch"); +#endif + mItemCount.emplace(aItemCount); + // Note: it's OK if mItemIndex underflows -- ItemIndex() + // will not be called unless there is at least one item. + mItemIndex = IsForward() ? 0 : *mItemCount - 1; + } + + /** + * Skip over placeholder children. + */ + void SkipPlaceholders() + { + if (mIter.isSome()) { + for (; *mIter != *mIterEnd; ++*mIter) { + nsIFrame* child = **mIter; + if (child->GetType() != nsGkAtoms::placeholderFrame) { + return; + } + } + } else { + for (; mArrayIndex < mArray->Length(); ++mArrayIndex) { + nsIFrame* child = (*mArray)[mArrayIndex]; + if (child->GetType() != nsGkAtoms::placeholderFrame) { + return; + } + } + } + } + + bool AtEnd() const + { +#ifndef CLANG_CRASH_BUG + // Clang 3.6.2 crashes when compiling this assertion: + MOZ_ASSERT(mIter.isSome() || mArrayIndex <= mArray->Length()); +#endif + return mIter ? (*mIter == *mIterEnd) : mArrayIndex >= mArray->Length(); + } + + void Next() + { +#ifdef DEBUG + MOZ_ASSERT(!AtEnd()); + nsFrameList list = mContainer->GetChildList(mListID); + MOZ_ASSERT(list.FirstChild() == mChildren.FirstChild() && + list.LastChild() == mChildren.LastChild(), + "the list of child frames must not change while iterating!"); +#endif + if (mSkipPlaceholders || + (**this)->GetType() != nsGkAtoms::placeholderFrame) { + IsForward() ? ++mItemIndex : --mItemIndex; + } + if (mIter.isSome()) { + ++*mIter; + } else { + ++mArrayIndex; + } + if (mSkipPlaceholders) { + SkipPlaceholders(); + } + } + + void Reset(ChildFilter aFilter = eSkipPlaceholders) + { + if (mIter.isSome()) { + mIter.reset(); + mIter.emplace(begin(mChildren)); + mIterEnd.reset(); + mIterEnd.emplace(end(mChildren)); + } else { + mArrayIndex = 0; + } + mItemIndex = IsForward() ? 0 : *mItemCount - 1; + mSkipPlaceholders = aFilter == eSkipPlaceholders; + if (mSkipPlaceholders) { + SkipPlaceholders(); + } + } + + bool IsValid() const { return mIter.isSome() || mArray.isSome(); } + + void Invalidate() + { + mIter.reset(); + mArray.reset(); + mozWritePoison(&mChildren, sizeof(mChildren)); + } + + bool ItemsAreAlreadyInOrder() const { return mIter.isSome(); } + + static bool CSSOrderComparator(nsIFrame* const& a, nsIFrame* const& b); + static bool CSSBoxOrdinalGroupComparator(nsIFrame* const& a, nsIFrame* const& b); +private: + nsFrameList mChildren; + // Used if child list is already in ascending 'order'. + Maybe mIter; + Maybe mIterEnd; + // Used if child list is *not* in ascending 'order'. + // This array is pre-sorted in reverse order for a reverse iterator. + Maybe> mArray; + size_t mArrayIndex; + // The index of the current item (placeholders excluded). + size_t mItemIndex; + // The number of items (placeholders excluded). + // It's only initialized and used in a reverse iterator. + Maybe mItemCount; + // Skip placeholder children in the iteration? + bool mSkipPlaceholders; +#ifdef DEBUG + nsIFrame* mContainer; + nsIFrame::ChildListID mListID; +#endif +}; + +typedef CSSOrderAwareFrameIteratorT + CSSOrderAwareFrameIterator; +typedef CSSOrderAwareFrameIteratorT + ReverseCSSOrderAwareFrameIterator; + +} // namespace mozilla + +#endif // mozilla_CSSOrderAwareFrameIterator_h diff --git a/layout/generic/ReflowInput.cpp b/layout/generic/ReflowInput.cpp index 11fbd668e9fa..b8bff5bdbcd9 100644 --- a/layout/generic/ReflowInput.cpp +++ b/layout/generic/ReflowInput.cpp @@ -534,6 +534,9 @@ IsQuirkContainingBlockHeight(const ReflowInput* rs, nsIAtom* aFrameType) void ReflowInput::InitResizeFlags(nsPresContext* aPresContext, nsIAtom* aFrameType) { + SetBResize(false); + SetIResize(false); + const WritingMode wm = mWritingMode; // just a shorthand // We should report that we have a resize in the inline dimension if // *either* the border-box size or the content-box size in that diff --git a/layout/generic/moz.build b/layout/generic/moz.build index c3ae38cc3152..1bd7454abeda 100644 --- a/layout/generic/moz.build +++ b/layout/generic/moz.build @@ -110,6 +110,7 @@ EXPORTS += [ EXPORTS.mozilla += [ 'CSSAlignUtils.h', + 'CSSOrderAwareFrameIterator.h', 'ReflowInput.h', 'ReflowOutput.h', 'ViewportFrame.h', @@ -129,6 +130,7 @@ UNIFIED_SOURCES += [ 'BlockReflowInput.cpp', 'BRFrame.cpp', 'CSSAlignUtils.cpp', + 'CSSOrderAwareFrameIterator.cpp', 'DetailsFrame.cpp', 'FrameChildList.cpp', 'MathMLTextRunFactory.cpp', diff --git a/layout/generic/nsFlexContainerFrame.cpp b/layout/generic/nsFlexContainerFrame.cpp index 8b5f101273b3..08dbd5487545 100644 --- a/layout/generic/nsFlexContainerFrame.cpp +++ b/layout/generic/nsFlexContainerFrame.cpp @@ -7,7 +7,6 @@ /* rendering object for CSS "display: flex" */ -#include "mozilla/UniquePtr.h" #include "nsFlexContainerFrame.h" #include "nsContentUtils.h" #include "nsCSSAnonBoxes.h" @@ -18,10 +17,12 @@ #include "nsPresContext.h" #include "nsRenderingContext.h" #include "nsStyleContext.h" +#include "mozilla/CSSOrderAwareFrameIterator.h" #include "mozilla/Logging.h" #include #include "mozilla/LinkedList.h" #include "mozilla/FloatingPoint.h" +#include "mozilla/UniquePtr.h" #include "WritingModes.h" using namespace mozilla; @@ -101,6 +102,16 @@ IsLegacyBox(const nsIFrame* aFlexContainer) return aFlexContainer->HasAnyStateBits(NS_STATE_FLEX_IS_LEGACY_WEBKIT_BOX); } +// Returns the OrderingProperty enum that we should pass to +// CSSOrderAwareFrameIterator (depending on whether it's a legacy box). +static CSSOrderAwareFrameIterator::OrderingProperty +OrderingPropertyForIter(const nsFlexContainerFrame* aFlexContainer) +{ + return IsLegacyBox(aFlexContainer) + ? CSSOrderAwareFrameIterator::OrderingProperty::eUseBoxOrdinalGroup + : CSSOrderAwareFrameIterator::OrderingProperty::eUseOrder; +} + // Returns the "align-items" value that's equivalent to the legacy "box-align" // value in the given style struct. static uint8_t @@ -1013,196 +1024,6 @@ BuildStrutInfoFromCollapsedItems(const FlexLine* aFirstLine, } } -// Convenience function to get either the "order" or the "box-ordinal-group" -// property-value for a flex item (depending on whether the container is a -// modern flex container or a legacy box). -static int32_t -GetOrderOrBoxOrdinalGroup(nsIFrame* aFlexItem, bool aIsLegacyBox) -{ - if (aIsLegacyBox) { - // We'll be using mBoxOrdinal, which has type uint32_t. However, the modern - // 'order' property (whose functionality we're co-opting) has type int32_t. - // So: if we happen to have a uint32_t value that's greater than INT32_MAX, - // we clamp it rather than letting it overflow. Chances are, this is just - // an author using BIG_VALUE anyway, so the clamped value should be fine. - // (particularly since sufficiently-huge values are busted in Chrome/WebKit - // per https://bugs.chromium.org/p/chromium/issues/detail?id=599645 ) - uint32_t clampedBoxOrdinal = std::min(aFlexItem->StyleXUL()->mBoxOrdinal, - static_cast(INT32_MAX)); - return static_cast(clampedBoxOrdinal); - } - - // Normal case: just use modern 'order' property. - return aFlexItem->StylePosition()->mOrder; -} - -// Helper-function to find the first non-anonymous-box descendent of aFrame. -static nsIFrame* -GetFirstNonAnonBoxDescendant(nsIFrame* aFrame) -{ - while (aFrame) { - nsIAtom* pseudoTag = aFrame->StyleContext()->GetPseudo(); - - // If aFrame isn't an anonymous container, then it'll do. - if (!pseudoTag || // No pseudotag. - !nsCSSAnonBoxes::IsAnonBox(pseudoTag) || // Pseudotag isn't anon. - nsCSSAnonBoxes::IsNonElement(pseudoTag)) { // Text, not a container. - break; - } - - // Otherwise, descend to its first child and repeat. - - // SPECIAL CASE: if we're dealing with an anonymous table, then it might - // be wrapping something non-anonymous in its caption or col-group lists - // (instead of its principal child list), so we have to look there. - // (Note: For anonymous tables that have a non-anon cell *and* a non-anon - // column, we'll always return the column. This is fine; we're really just - // looking for a handle to *anything* with a meaningful content node inside - // the table, for use in DOM comparisons to things outside of the table.) - if (MOZ_UNLIKELY(aFrame->GetType() == nsGkAtoms::tableWrapperFrame)) { - nsIFrame* captionDescendant = - GetFirstNonAnonBoxDescendant(aFrame->GetChildList(kCaptionList).FirstChild()); - if (captionDescendant) { - return captionDescendant; - } - } else if (MOZ_UNLIKELY(aFrame->GetType() == nsGkAtoms::tableFrame)) { - nsIFrame* colgroupDescendant = - GetFirstNonAnonBoxDescendant(aFrame->GetChildList(kColGroupList).FirstChild()); - if (colgroupDescendant) { - return colgroupDescendant; - } - } - - // USUAL CASE: Descend to the first child in principal list. - aFrame = aFrame->PrincipalChildList().FirstChild(); - } - return aFrame; -} - -/** - * Sorting helper-function that compares two frames' "order" property-values, - * and if they're equal, compares the DOM positions of their corresponding - * content nodes. Returns true if aFrame1 is "less than or equal to" aFrame2 - * according to this comparison. - * - * Note: This can't be a static function, because we need to pass it as a - * template argument. (Only functions with external linkage can be passed as - * template arguments.) - * - * @return true if the computed "order" property of aFrame1 is less than that - * of aFrame2, or if the computed "order" values are equal and aFrame1's - * corresponding DOM node is earlier than aFrame2's in the DOM tree. - * Otherwise, returns false. - */ -bool -IsOrderLEQWithDOMFallback(nsIFrame* aFrame1, - nsIFrame* aFrame2) -{ - MOZ_ASSERT(aFrame1->IsFlexItem() && aFrame2->IsFlexItem(), - "this method only intended for comparing flex items"); - MOZ_ASSERT(aFrame1->GetParent() == aFrame2->GetParent(), - "this method only intended for comparing siblings"); - if (aFrame1 == aFrame2) { - // Anything is trivially LEQ itself, so we return "true" here... but it's - // probably bad if we end up actually needing this, so let's assert. - NS_ERROR("Why are we checking if a frame is LEQ itself?"); - return true; - } - - if (aFrame1->GetType() == nsGkAtoms::placeholderFrame || - aFrame2->GetType() == nsGkAtoms::placeholderFrame) { - // Treat placeholders (for abspos/fixedpos frames) as LEQ everything. This - // ensures we don't reorder them w.r.t. one another, which is sufficient to - // prevent them from noticeably participating in "order" reordering. - return true; - } - - const bool isInLegacyBox = IsLegacyBox(aFrame1->GetParent()); - - int32_t order1 = GetOrderOrBoxOrdinalGroup(aFrame1, isInLegacyBox); - int32_t order2 = GetOrderOrBoxOrdinalGroup(aFrame2, isInLegacyBox); - - if (order1 != order2) { - return order1 < order2; - } - - // The "order" values are equal, so we need to fall back on DOM comparison. - // For that, we need to dig through any anonymous box wrapper frames to find - // the actual frame that corresponds to our child content. - aFrame1 = GetFirstNonAnonBoxDescendant(aFrame1); - aFrame2 = GetFirstNonAnonBoxDescendant(aFrame2); - MOZ_ASSERT(aFrame1 && aFrame2, - "why do we have an anonymous box without any " - "non-anonymous descendants?"); - - - // Special case: - // If either frame is for generated content from ::before or ::after, then - // we can't use nsContentUtils::PositionIsBefore(), since that method won't - // recognize generated content as being an actual sibling of other nodes. - // We know where ::before and ::after nodes *effectively* insert in the DOM - // tree, though (at the beginning & end), so we can just special-case them. - nsIAtom* pseudo1 = aFrame1->StyleContext()->GetPseudo(); - nsIAtom* pseudo2 = aFrame2->StyleContext()->GetPseudo(); - - if (pseudo1 == nsCSSPseudoElements::before || - pseudo2 == nsCSSPseudoElements::after) { - // frame1 is ::before and/or frame2 is ::after => frame1 is LEQ frame2. - return true; - } - if (pseudo1 == nsCSSPseudoElements::after || - pseudo2 == nsCSSPseudoElements::before) { - // frame1 is ::after and/or frame2 is ::before => frame1 is not LEQ frame2. - return false; - } - - // Usual case: Compare DOM position. - nsIContent* content1 = aFrame1->GetContent(); - nsIContent* content2 = aFrame2->GetContent(); - MOZ_ASSERT(content1 != content2, - "Two different flex items are using the same nsIContent node for " - "comparison, so we may be sorting them in an arbitrary order"); - - return nsContentUtils::PositionIsBefore(content1, content2); -} - -/** - * Sorting helper-function that compares two frames' "order" property-values. - * Returns true if aFrame1 is "less than or equal to" aFrame2 according to this - * comparison. - * - * Note: This can't be a static function, because we need to pass it as a - * template argument. (Only functions with external linkage can be passed as - * template arguments.) - * - * @return true if the computed "order" property of aFrame1 is less than or - * equal to that of aFrame2. Otherwise, returns false. - */ -bool -IsOrderLEQ(nsIFrame* aFrame1, - nsIFrame* aFrame2) -{ - MOZ_ASSERT(aFrame1->IsFlexItem() && aFrame2->IsFlexItem(), - "this method only intended for comparing flex items"); - MOZ_ASSERT(aFrame1->GetParent() == aFrame2->GetParent(), - "this method only intended for comparing siblings"); - - if (aFrame1->GetType() == nsGkAtoms::placeholderFrame || - aFrame2->GetType() == nsGkAtoms::placeholderFrame) { - // Treat placeholders (for abspos/fixedpos frames) as LEQ everything. This - // ensures we don't reorder them w.r.t. one another, which is sufficient to - // prevent them from noticeably participating in "order" reordering. - return true; - } - - const bool isInLegacyBox = IsLegacyBox(aFrame1->GetParent()); - - int32_t order1 = GetOrderOrBoxOrdinalGroup(aFrame1, isInLegacyBox); - int32_t order2 = GetOrderOrBoxOrdinalGroup(aFrame2, isInLegacyBox); - - return order1 <= order2; -} - uint8_t SimplifyAlignOrJustifyContentForOneItem(uint16_t aAlignmentVal, bool aIsAlign) @@ -2383,18 +2204,6 @@ nsFlexContainerFrame::Init(nsIContent* aContent, } } -template -/* static */ bool -nsFlexContainerFrame::SortChildrenIfNeeded() -{ - if (nsIFrame::IsFrameListSorted(mFrames)) { - return false; - } - - nsIFrame::SortFrameList(mFrames); - return true; -} - /* virtual */ nsIAtom* nsFlexContainerFrame::GetType() const @@ -2446,22 +2255,24 @@ nsFlexContainerFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsRect& aDirtyRect, const nsDisplayListSet& aLists) { - // XXXdholbert hacky temporary band-aid for bug 1059138: Trivially pass this - // assertion (skip it, basically) if the first child is part of a shadow DOM. - // (IsOrderLEQWithDOMFallback doesn't know how to compare tree-position of a - // shadow-DOM element vs. a non-shadow-DOM element.) - NS_ASSERTION( - (!mFrames.IsEmpty() && - mFrames.FirstChild()->GetContent()->GetContainingShadow()) || - nsIFrame::IsFrameListSorted(mFrames), - "Child frames aren't sorted correctly"); - DisplayBorderBackgroundOutline(aBuilder, aLists); // Our children are all block-level, so their borders/backgrounds all go on // the BlockBorderBackgrounds list. nsDisplayListSet childLists(aLists, aLists.BlockBorderBackgrounds()); - for (nsIFrame* childFrame : mFrames) { + + typedef CSSOrderAwareFrameIterator::OrderState OrderState; + OrderState orderState = + HasAnyStateBits(NS_STATE_FLEX_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER) + ? OrderState::eKnownOrdered + : OrderState::eKnownUnordered; + + CSSOrderAwareFrameIterator iter(this, kPrincipalList, + CSSOrderAwareFrameIterator::eIncludeAll, + orderState, + OrderingPropertyForIter(this)); + for (; !iter.AtEnd(); iter.Next()) { + nsIFrame* childFrame = *iter; BuildDisplayListForChild(aBuilder, childFrame, aDirtyRect, childLists, GetDisplayFlagsForFlexItem(childFrame)); } @@ -3764,7 +3575,19 @@ nsFlexContainerFrame::GenerateFlexLines( // checked against entries in aStruts.) uint32_t itemIdxInContainer = 0; - for (nsIFrame* childFrame : mFrames) { + CSSOrderAwareFrameIterator iter(this, kPrincipalList, + CSSOrderAwareFrameIterator::eIncludeAll, + CSSOrderAwareFrameIterator::eUnknownOrder, + OrderingPropertyForIter(this)); + + if (iter.ItemsAreAlreadyInOrder()) { + AddStateBits(NS_STATE_FLEX_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER); + } else { + RemoveStateBits(NS_STATE_FLEX_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER); + } + + for (; !iter.AtEnd(); iter.Next()) { + nsIFrame* childFrame = *iter; // Don't create flex items / lines for placeholder frames: if (childFrame->GetType() == nsGkAtoms::placeholderFrame) { aPlaceholders.AppendElement(childFrame); @@ -4155,24 +3978,6 @@ nsFlexContainerFrame::Reflow(nsPresContext* aPresContext, AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE); } - // If we've never reordered our children, then we can trust that they're - // already in DOM-order, and we only need to consider their "order" property - // when checking them for sortedness & sorting them. - // - // After we actually sort them, though, we can't trust that they're in DOM - // order anymore. So, from that point on, our sort & sorted-order-checking - // operations need to use a fancier LEQ function that also takes DOM order - // into account, so that we can honor the spec's requirement that frames w/ - // equal "order" values are laid out in DOM order. - - if (!HasAnyStateBits(NS_STATE_FLEX_CHILDREN_REORDERED)) { - if (SortChildrenIfNeeded()) { - AddStateBits(NS_STATE_FLEX_CHILDREN_REORDERED); - } - } else { - SortChildrenIfNeeded(); - } - RenumberList(); const FlexboxAxisTracker axisTracker(this, aReflowInput.GetWritingMode()); diff --git a/layout/generic/nsFrameStateBits.h b/layout/generic/nsFrameStateBits.h index c0a955598d3f..91d11cb62ffc 100644 --- a/layout/generic/nsFrameStateBits.h +++ b/layout/generic/nsFrameStateBits.h @@ -310,9 +310,9 @@ FRAME_STATE_BIT(Box, 61, NS_FRAME_MOUSE_THROUGH_NEVER) FRAME_STATE_GROUP(FlexContainer, nsFlexContainerFrame) -// Set for a flex container whose children have been reordered due to 'order'. -// (Means that we have to be more thorough about checking them for sortedness.) -FRAME_STATE_BIT(FlexContainer, 20, NS_STATE_FLEX_CHILDREN_REORDERED) +// True iff the normal flow children are already in CSS 'order' in the +// order they occur in the child frame list. +FRAME_STATE_BIT(FlexContainer, 20, NS_STATE_FLEX_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER) // Set for a flex container that is emulating a legacy // 'display:-webkit-{inline-}box' container. diff --git a/layout/generic/nsGridContainerFrame.cpp b/layout/generic/nsGridContainerFrame.cpp index b26e80e37616..fd2dddf23316 100644 --- a/layout/generic/nsGridContainerFrame.cpp +++ b/layout/generic/nsGridContainerFrame.cpp @@ -12,6 +12,7 @@ #include #include #include "mozilla/CSSAlignUtils.h" +#include "mozilla/CSSOrderAwareFrameIterator.h" #include "mozilla/dom/GridBinding.h" #include "mozilla/Maybe.h" #include "mozilla/PodOperations.h" // for PodZero @@ -31,10 +32,6 @@ #include "nsStyleContext.h" #include "nsTableWrapperFrame.h" -#if defined(__clang__) && __clang_major__ == 3 && __clang_minor__ <= 8 -#define CLANG_CRASH_BUG 1 -#endif - using namespace mozilla; typedef nsAbsoluteContainingBlock::AbsPosReflowFlags AbsPosReflowFlags; @@ -385,248 +382,6 @@ MergeSortedFrameListsFor(nsFrameList& aDest, nsFrameList& aSrc, MergeSortedFrameLists(aDest, aSrc, aParent->GetContent()); } -template -class nsGridContainerFrame::GridItemCSSOrderIteratorT -{ -public: - enum OrderState { eUnknownOrder, eKnownOrdered, eKnownUnordered }; - enum ChildFilter { eSkipPlaceholders, eIncludeAll }; - GridItemCSSOrderIteratorT(nsIFrame* aGridContainer, - nsIFrame::ChildListID aListID, - ChildFilter aFilter = eSkipPlaceholders, - OrderState aState = eUnknownOrder) - : mChildren(aGridContainer->GetChildList(aListID)) - , mArrayIndex(0) - , mGridItemIndex(0) - , mSkipPlaceholders(aFilter == eSkipPlaceholders) -#ifdef DEBUG - , mGridContainer(aGridContainer) - , mListID(aListID) -#endif - { - size_t count = 0; - bool isOrdered = aState != eKnownUnordered; - if (aState == eUnknownOrder) { - auto maxOrder = std::numeric_limits::min(); - for (auto child : mChildren) { - ++count; - int32_t order = child->StylePosition()->mOrder; - if (order < maxOrder) { - isOrdered = false; - break; - } - maxOrder = order; - } - } - if (isOrdered) { - mIter.emplace(begin(mChildren)); - mIterEnd.emplace(end(mChildren)); - } else { - count *= 2; // XXX somewhat arbitrary estimate for now... - mArray.emplace(count); - for (Iterator i(begin(mChildren)), iEnd(end(mChildren)); i != iEnd; ++i) { - mArray->AppendElement(*i); - } - // XXX replace this with nsTArray::StableSort when bug 1147091 is fixed. - std::stable_sort(mArray->begin(), mArray->end(), CSSOrderComparator); - } - - if (mSkipPlaceholders) { - SkipPlaceholders(); - } - } - ~GridItemCSSOrderIteratorT() - { - MOZ_ASSERT(IsForward() == mGridItemCount.isNothing()); - } - - bool IsForward() const; - Iterator begin(const nsFrameList& aList); - Iterator end(const nsFrameList& aList); - - nsIFrame* operator*() const - { - MOZ_ASSERT(!AtEnd()); - if (mIter.isSome()) { - return **mIter; - } - return (*mArray)[mArrayIndex]; - } - - /** - * Return the child index of the current item, placeholders not counted. - * It's forbidden to call this method when the current frame is placeholder. - */ - size_t GridItemIndex() const - { - MOZ_ASSERT(!AtEnd()); - MOZ_ASSERT((**this)->GetType() != nsGkAtoms::placeholderFrame, - "MUST not call this when at a placeholder"); - MOZ_ASSERT(IsForward() || mGridItemIndex < *mGridItemCount, - "Returning an out-of-range mGridItemIndex..."); - return mGridItemIndex; - } - - void SetGridItemCount(size_t aGridItemCount) - { -#ifndef CLANG_CRASH_BUG - MOZ_ASSERT(mIter.isSome() || aGridItemCount <= mArray->Length(), - "grid item count mismatch"); -#endif - mGridItemCount.emplace(aGridItemCount); - // Note: it's OK if mGridItemIndex underflows -- GridItemIndex() - // will not be called unless there is at least one item. - mGridItemIndex = IsForward() ? 0 : *mGridItemCount - 1; - } - - /** - * Skip over placeholder children. - */ - void SkipPlaceholders() - { - if (mIter.isSome()) { - for (; *mIter != *mIterEnd; ++*mIter) { - nsIFrame* child = **mIter; - if (child->GetType() != nsGkAtoms::placeholderFrame) { - return; - } - } - } else { - for (; mArrayIndex < mArray->Length(); ++mArrayIndex) { - nsIFrame* child = (*mArray)[mArrayIndex]; - if (child->GetType() != nsGkAtoms::placeholderFrame) { - return; - } - } - } - } - - bool AtEnd() const - { -#ifndef CLANG_CRASH_BUG - // Clang 3.6.2 crashes when compiling this assertion: - MOZ_ASSERT(mIter.isSome() || mArrayIndex <= mArray->Length()); -#endif - return mIter ? (*mIter == *mIterEnd) : mArrayIndex >= mArray->Length(); - } - - void Next() - { -#ifdef DEBUG - MOZ_ASSERT(!AtEnd()); - nsFrameList list = mGridContainer->GetChildList(mListID); - MOZ_ASSERT(list.FirstChild() == mChildren.FirstChild() && - list.LastChild() == mChildren.LastChild(), - "the list of child frames must not change while iterating!"); -#endif - if (mSkipPlaceholders || - (**this)->GetType() != nsGkAtoms::placeholderFrame) { - IsForward() ? ++mGridItemIndex : --mGridItemIndex; - } - if (mIter.isSome()) { - ++*mIter; - } else { - ++mArrayIndex; - } - if (mSkipPlaceholders) { - SkipPlaceholders(); - } - } - - void Reset(ChildFilter aFilter = eSkipPlaceholders) - { - if (mIter.isSome()) { - mIter.reset(); - mIter.emplace(begin(mChildren)); - mIterEnd.reset(); - mIterEnd.emplace(end(mChildren)); - } else { - mArrayIndex = 0; - } - mGridItemIndex = IsForward() ? 0 : *mGridItemCount - 1; - mSkipPlaceholders = aFilter == eSkipPlaceholders; - if (mSkipPlaceholders) { - SkipPlaceholders(); - } - } - - bool IsValid() const { return mIter.isSome() || mArray.isSome(); } - - void Invalidate() - { - mIter.reset(); - mArray.reset(); - mozWritePoison(&mChildren, sizeof(mChildren)); - } - - bool ItemsAreAlreadyInOrder() const { return mIter.isSome(); } - - static bool CSSOrderComparator(nsIFrame* const& a, nsIFrame* const& b); -private: - nsFrameList mChildren; - // Used if child list is already in ascending 'order'. - Maybe mIter; - Maybe mIterEnd; - // Used if child list is *not* in ascending 'order'. - // This array is pre-sorted in reverse order for a reverse iterator. - Maybe> mArray; - size_t mArrayIndex; - // The index of the current grid item (placeholders excluded). - size_t mGridItemIndex; - // The number of grid items (placeholders excluded). - // It's only initialized and used in a reverse iterator. - Maybe mGridItemCount; - // Skip placeholder children in the iteration? - bool mSkipPlaceholders; -#ifdef DEBUG - nsIFrame* mGridContainer; - nsIFrame::ChildListID mListID; -#endif -}; - -using GridItemCSSOrderIterator = nsGridContainerFrame::GridItemCSSOrderIterator; -using ReverseGridItemCSSOrderIterator = nsGridContainerFrame::ReverseGridItemCSSOrderIterator; - -template<> -bool -GridItemCSSOrderIterator::CSSOrderComparator(nsIFrame* const& a, - nsIFrame* const& b) -{ return a->StylePosition()->mOrder < b->StylePosition()->mOrder; } - -template<> -bool -GridItemCSSOrderIterator::IsForward() const { return true; } - -template<> -nsFrameList::iterator -GridItemCSSOrderIterator::begin(const nsFrameList& aList) -{ return aList.begin(); } - -template<> -nsFrameList::iterator GridItemCSSOrderIterator::end(const nsFrameList& aList) -{ return aList.end(); } - -template<> -bool -ReverseGridItemCSSOrderIterator::CSSOrderComparator(nsIFrame* const& a, - nsIFrame* const& b) -{ return a->StylePosition()->mOrder > b->StylePosition()->mOrder; } - -template<> -bool -ReverseGridItemCSSOrderIterator::IsForward() const -{ return false; } - -template<> -nsFrameList::reverse_iterator -ReverseGridItemCSSOrderIterator::begin(const nsFrameList& aList) -{ return aList.rbegin(); } - -template<> -nsFrameList::reverse_iterator -ReverseGridItemCSSOrderIterator::end(const nsFrameList& aList) -{ return aList.rend(); } - /** * A LineRange can be definite or auto - when it's definite it represents * a consecutive set of tracks between a starting line and an ending line. @@ -2105,7 +1860,7 @@ struct MOZ_STACK_CLASS nsGridContainerFrame::GridReflowInput const LogicalPoint& aGridOrigin, const LogicalRect& aGridCB) const; - GridItemCSSOrderIterator mIter; + CSSOrderAwareFrameIterator mIter; const nsStylePosition* const mGridStyle; Tracks mCols; Tracks mRows; @@ -3356,8 +3111,8 @@ nsGridContainerFrame::Grid::PlaceGridItems(GridReflowInput& aState, colLineNameMap, rowLineNameMap, gridStyle))); - MOZ_ASSERT(aState.mIter.GridItemIndex() == aState.mGridItems.Length() - 1, - "GridItemIndex() is broken"); + MOZ_ASSERT(aState.mIter.ItemIndex() == aState.mGridItems.Length() - 1, + "ItemIndex() is broken"); GridArea& area = info->mArea; if (area.mCols.IsDefinite()) { minCol = std::min(minCol, area.mCols.mUntranslatedStart); @@ -3378,7 +3133,7 @@ nsGridContainerFrame::Grid::PlaceGridItems(GridReflowInput& aState, mGridRowEnd += offsetToRowZero; aState.mIter.Reset(); for (; !aState.mIter.AtEnd(); aState.mIter.Next()) { - GridArea& area = aState.mGridItems[aState.mIter.GridItemIndex()].mArea; + GridArea& area = aState.mGridItems[aState.mIter.ItemIndex()].mArea; if (area.mCols.IsDefinite()) { area.mCols.mStart = area.mCols.mUntranslatedStart + offsetToColZero; area.mCols.mEnd = area.mCols.mUntranslatedEnd + offsetToColZero; @@ -3409,7 +3164,7 @@ nsGridContainerFrame::Grid::PlaceGridItems(GridReflowInput& aState, : &Grid::PlaceAutoRow; aState.mIter.Reset(); for (; !aState.mIter.AtEnd(); aState.mIter.Next()) { - GridArea& area = aState.mGridItems[aState.mIter.GridItemIndex()].mArea; + GridArea& area = aState.mGridItems[aState.mIter.ItemIndex()].mArea; LineRange& major = isRowOrder ? area.mRows : area.mCols; LineRange& minor = isRowOrder ? area.mCols : area.mRows; if (major.IsDefinite() && minor.IsAuto()) { @@ -3443,8 +3198,8 @@ nsGridContainerFrame::Grid::PlaceGridItems(GridReflowInput& aState, : &Grid::PlaceAutoCol; aState.mIter.Reset(); for (; !aState.mIter.AtEnd(); aState.mIter.Next()) { - GridArea& area = aState.mGridItems[aState.mIter.GridItemIndex()].mArea; - MOZ_ASSERT(*aState.mIter == aState.mGridItems[aState.mIter.GridItemIndex()].mFrame, + GridArea& area = aState.mGridItems[aState.mIter.ItemIndex()].mArea; + MOZ_ASSERT(*aState.mIter == aState.mGridItems[aState.mIter.ItemIndex()].mFrame, "iterator out of sync with aState.mGridItems"); LineRange& major = isRowOrder ? area.mRows : area.mCols; LineRange& minor = isRowOrder ? area.mCols : area.mRows; @@ -4118,11 +3873,11 @@ nsGridContainerFrame::Tracks::InitializeItemBaselines( nsTArray lastBaselineItems; WritingMode wm = aState.mWM; nsStyleContext* containerSC = aState.mFrame->StyleContext(); - GridItemCSSOrderIterator& iter = aState.mIter; + CSSOrderAwareFrameIterator& iter = aState.mIter; iter.Reset(); for (; !iter.AtEnd(); iter.Next()) { nsIFrame* child = *iter; - GridItemInfo& gridItem = aGridItems[iter.GridItemIndex()]; + GridItemInfo& gridItem = aGridItems[iter.ItemIndex()]; uint32_t baselineTrack = kAutoLine; auto state = ItemState(0); auto childWM = child->GetWritingMode(); @@ -4376,7 +4131,7 @@ nsGridContainerFrame::Tracks::ResolveIntrinsicSize( // FindUsedFlexFraction later. AutoTArray stateBitsPerSpan; nsTArray step2Items; - GridItemCSSOrderIterator& iter = aState.mIter; + CSSOrderAwareFrameIterator& iter = aState.mIter; nsRenderingContext* rc = &aState.mRenderingContext; WritingMode wm = aState.mWM; uint32_t maxSpan = 0; // max span of the step2Items items @@ -4391,7 +4146,7 @@ nsGridContainerFrame::Tracks::ResolveIntrinsicSize( TrackSize::eMaxContentMinSizing; iter.Reset(); for (; !iter.AtEnd(); iter.Next()) { - auto& gridItem = aGridItems[iter.GridItemIndex()]; + auto& gridItem = aGridItems[iter.ItemIndex()]; const GridArea& area = gridItem.mArea; const LineRange& lineRange = area.*aRange; uint32_t span = lineRange.Extent(); @@ -4709,12 +4464,12 @@ nsGridContainerFrame::Tracks::FindUsedFlexFraction( } WritingMode wm = aState.mWM; nsRenderingContext* rc = &aState.mRenderingContext; - GridItemCSSOrderIterator& iter = aState.mIter; + CSSOrderAwareFrameIterator& iter = aState.mIter; iter.Reset(); // ... the result of 'finding the size of an fr' for each item that spans // a flex track with its max-content contribution as 'space to fill' for (; !iter.AtEnd(); iter.Next()) { - const GridItemInfo& item = aGridItems[iter.GridItemIndex()]; + const GridItemInfo& item = aGridItems[iter.ItemIndex()]; if (item.mState[mAxis] & ItemState::eIsFlexing) { // XXX optimize: bug 1194446 nscoord spaceToFill = ContentContribution(item, aState, rc, wm, mAxis, @@ -5347,11 +5102,11 @@ nsGridContainerFrame::ReflowInFragmentainer(GridReflowInput& aState, // and put them in a separate array. nsTArray sortedItems(aState.mGridItems.Length()); nsTArray placeholders(aState.mAbsPosItems.Length()); - aState.mIter.Reset(GridItemCSSOrderIterator::eIncludeAll); + aState.mIter.Reset(CSSOrderAwareFrameIterator::eIncludeAll); for (; !aState.mIter.AtEnd(); aState.mIter.Next()) { nsIFrame* child = *aState.mIter; if (child->GetType() != nsGkAtoms::placeholderFrame) { - const GridItemInfo* info = &aState.mGridItems[aState.mIter.GridItemIndex()]; + const GridItemInfo* info = &aState.mGridItems[aState.mIter.ItemIndex()]; sortedItems.AppendElement(info); } else { placeholders.AppendElement(child); @@ -5863,12 +5618,12 @@ nsGridContainerFrame::ReflowChildren(GridReflowInput& aState, bSize = ReflowInFragmentainer(aState, aContentArea, aDesiredSize, aStatus, *fragmentainer, containerSize); } else { - aState.mIter.Reset(GridItemCSSOrderIterator::eIncludeAll); + aState.mIter.Reset(CSSOrderAwareFrameIterator::eIncludeAll); for (; !aState.mIter.AtEnd(); aState.mIter.Next()) { nsIFrame* child = *aState.mIter; const GridItemInfo* info = nullptr; if (child->GetType() != nsGkAtoms::placeholderFrame) { - info = &aState.mGridItems[aState.mIter.GridItemIndex()]; + info = &aState.mGridItems[aState.mIter.ItemIndex()]; } ReflowInFlowChild(*aState.mIter, info, containerSize, Nothing(), nullptr, aState, aContentArea, aDesiredSize, aStatus); @@ -6228,7 +5983,7 @@ nsGridContainerFrame::Reflow(nsPresContext* aPresContext, gridReflowInput.mNextFragmentStartRow == len) { baselines = BaselineSet(baselines | BaselineSet::eLast); } - Maybe iter; + Maybe iter; Maybe> gridItems; if (baselines != BaselineSet::eNone) { // We need to create a new iterator and GridItemInfo array because we @@ -6239,8 +5994,8 @@ nsGridContainerFrame::Reflow(nsPresContext* aPresContext, // An unordered list can become ordered in a number of cases, but we // ignore that here and guess that the child list is still unordered. // XXX this is O(n^2) in the number of items in this fragment: bug 1306705 - using Filter = GridItemCSSOrderIterator::ChildFilter; - using Order = GridItemCSSOrderIterator::OrderState; + using Filter = CSSOrderAwareFrameIterator::ChildFilter; + using Order = CSSOrderAwareFrameIterator::OrderState; bool ordered = gridReflowInput.mIter.ItemsAreAlreadyInOrder(); auto orderState = ordered ? Order::eKnownOrdered : Order::eKnownUnordered; iter.emplace(this, kPrincipalList, Filter::eSkipPlaceholders, orderState); @@ -6600,12 +6355,12 @@ nsGridContainerFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, // Our children are all grid-level boxes, which behave the same as // inline-blocks in painting, so their borders/backgrounds all go on // the BlockBorderBackgrounds list. - typedef GridItemCSSOrderIterator::OrderState OrderState; + typedef CSSOrderAwareFrameIterator::OrderState OrderState; OrderState order = HasAnyStateBits(NS_STATE_GRID_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER) ? OrderState::eKnownOrdered : OrderState::eKnownUnordered; - GridItemCSSOrderIterator iter(this, kPrincipalList, - GridItemCSSOrderIterator::eIncludeAll, order); + CSSOrderAwareFrameIterator iter(this, kPrincipalList, + CSSOrderAwareFrameIterator::eIncludeAll, order); for (; !iter.AtEnd(); iter.Next()) { nsIFrame* child = *iter; BuildDisplayListForChild(aBuilder, child, aDirtyRect, aLists, @@ -6773,7 +6528,7 @@ nsGridContainerFrame::SynthesizeBaseline( void nsGridContainerFrame::CalculateBaselines( BaselineSet aBaselineSet, - GridItemCSSOrderIterator* aIter, + CSSOrderAwareFrameIterator* aIter, const nsTArray* aGridItems, const Tracks& aTracks, uint32_t aFragmentStartTrack, @@ -6822,12 +6577,12 @@ nsGridContainerFrame::CalculateBaselines( } else if (lastBaseline == NS_INTRINSIC_WIDTH_UNKNOWN) { // For finding items for the 'last baseline' we need to create a reverse // iterator ('aIter' is the forward iterator from the GridReflowInput). - using Iter = ReverseGridItemCSSOrderIterator; + using Iter = ReverseCSSOrderAwareFrameIterator; auto orderState = aIter->ItemsAreAlreadyInOrder() ? Iter::OrderState::eKnownOrdered : Iter::OrderState::eKnownUnordered; Iter iter(this, kPrincipalList, Iter::ChildFilter::eSkipPlaceholders, orderState); - iter.SetGridItemCount(aGridItems->Length()); + iter.SetItemCount(aGridItems->Length()); FindItemInGridOrderResult gridOrderLastItem = FindLastItemInGridOrder(iter, *aGridItems, axis == eLogicalAxisBlock ? &GridArea::mRows : &GridArea::mCols, @@ -6916,7 +6671,7 @@ nsGridContainerFrame::MergeSortedExcessOverflowContainers(nsFrameList& aList) /* static */ nsGridContainerFrame::FindItemInGridOrderResult nsGridContainerFrame::FindFirstItemInGridOrder( - GridItemCSSOrderIterator& aIter, + CSSOrderAwareFrameIterator& aIter, const nsTArray& aGridItems, LineRange GridArea::* aMajor, LineRange GridArea::* aMinor, @@ -6927,7 +6682,7 @@ nsGridContainerFrame::FindFirstItemInGridOrder( uint32_t minMinor = kTranslatedMaxLine + 1; aIter.Reset(); for (; !aIter.AtEnd(); aIter.Next()) { - const GridItemInfo& item = aGridItems[aIter.GridItemIndex()]; + const GridItemInfo& item = aGridItems[aIter.ItemIndex()]; if ((item.mArea.*aMajor).mEnd <= aFragmentStartTrack) { continue; // item doesn't span any track in this fragment } @@ -6945,7 +6700,7 @@ nsGridContainerFrame::FindFirstItemInGridOrder( /* static */ nsGridContainerFrame::FindItemInGridOrderResult nsGridContainerFrame::FindLastItemInGridOrder( - ReverseGridItemCSSOrderIterator& aIter, + ReverseCSSOrderAwareFrameIterator& aIter, const nsTArray& aGridItems, LineRange GridArea::* aMajor, LineRange GridArea::* aMinor, @@ -6958,7 +6713,7 @@ nsGridContainerFrame::FindLastItemInGridOrder( aIter.Reset(); int32_t lastMajorTrack = int32_t(aFirstExcludedTrack) - 1; for (; !aIter.AtEnd(); aIter.Next()) { - const GridItemInfo& item = aGridItems[aIter.GridItemIndex()]; + const GridItemInfo& item = aGridItems[aIter.ItemIndex()]; // Subtract 1 from the end line to get the item's last track index. int32_t major = (item.mArea.*aMajor).mEnd - 1; // Currently, this method is only called with aFirstExcludedTrack == diff --git a/layout/generic/nsGridContainerFrame.h b/layout/generic/nsGridContainerFrame.h index 2e52fcecb436..3c8249b50d5b 100644 --- a/layout/generic/nsGridContainerFrame.h +++ b/layout/generic/nsGridContainerFrame.h @@ -23,6 +23,14 @@ nsContainerFrame* NS_NewGridContainerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); namespace mozilla { + +// Forward-declare typedefs for grid item iterator helper-class: +template class CSSOrderAwareFrameIteratorT; +typedef CSSOrderAwareFrameIteratorT + CSSOrderAwareFrameIterator; +typedef CSSOrderAwareFrameIteratorT + ReverseCSSOrderAwareFrameIterator; + /** * The number of implicit / explicit tracks and their sizes. */ @@ -219,11 +227,6 @@ public: struct TrackSize; struct GridItemInfo; struct GridReflowInput; - template class GridItemCSSOrderIteratorT; - typedef GridItemCSSOrderIteratorT - GridItemCSSOrderIterator; - typedef GridItemCSSOrderIteratorT - ReverseGridItemCSSOrderIterator; struct FindItemInGridOrderResult { // The first(last) item in (reverse) grid order. @@ -238,6 +241,9 @@ protected: typedef mozilla::LogicalPoint LogicalPoint; typedef mozilla::LogicalRect LogicalRect; typedef mozilla::LogicalSize LogicalSize; + typedef mozilla::CSSOrderAwareFrameIterator CSSOrderAwareFrameIterator; + typedef mozilla::ReverseCSSOrderAwareFrameIterator + ReverseCSSOrderAwareFrameIterator; typedef mozilla::WritingMode WritingMode; typedef mozilla::css::GridNamedArea GridNamedArea; typedef mozilla::layout::AutoFrameListPtr AutoFrameListPtr; @@ -324,7 +330,7 @@ protected: eBoth = eFirst | eLast, }; void CalculateBaselines(BaselineSet aBaselineSet, - GridItemCSSOrderIterator* aIter, + CSSOrderAwareFrameIterator* aIter, const nsTArray* aGridItems, const Tracks& aTracks, uint32_t aFragmentStartTrack, @@ -351,7 +357,7 @@ protected: * axis as aMajor. Pass zero if that's not the axis we're fragmenting in. */ static FindItemInGridOrderResult - FindFirstItemInGridOrder(GridItemCSSOrderIterator& aIter, + FindFirstItemInGridOrder(CSSOrderAwareFrameIterator& aIter, const nsTArray& aGridItems, LineRange GridArea::* aMajor, LineRange GridArea::* aMinor, @@ -365,7 +371,7 @@ protected: * Pass the number of tracks if that's not the axis we're fragmenting in. */ static FindItemInGridOrderResult - FindLastItemInGridOrder(ReverseGridItemCSSOrderIterator& aIter, + FindLastItemInGridOrder(ReverseCSSOrderAwareFrameIterator& aIter, const nsTArray& aGridItems, LineRange GridArea::* aMajor, LineRange GridArea::* aMinor, diff --git a/layout/inspector/inDOMUtils.cpp b/layout/inspector/inDOMUtils.cpp index 24d481192dc7..0e937371192d 100644 --- a/layout/inspector/inDOMUtils.cpp +++ b/layout/inspector/inDOMUtils.cpp @@ -1174,7 +1174,7 @@ inDOMUtils::GetCleanStyleContextForElement(dom::Element* aElement, presContext->EnsureSafeToHandOutCSSRules(); RefPtr styleContext = - nsComputedDOMStyle::GetStyleContextForElement(aElement, aPseudo, presShell); + nsComputedDOMStyle::GetStyleContext(aElement, aPseudo, presShell); return styleContext.forget(); } diff --git a/layout/reftests/list-item/numbering-5-ref.html b/layout/reftests/list-item/numbering-5-ref.html new file mode 100644 index 000000000000..74f5c37d678e --- /dev/null +++ b/layout/reftests/list-item/numbering-5-ref.html @@ -0,0 +1,24 @@ + + + + Reference for reordered 'li' flex items + + + +
    +
    4. List item 4
    +
    2. List item 2
    +
    3. List item 3
    +
    5. List item 5
    +
    6. List item 6
    +
    8. List item 8
    +
    1. List item 1
    +
    7. List item 7
    +
+ + diff --git a/layout/reftests/list-item/numbering-5.html b/layout/reftests/list-item/numbering-5.html new file mode 100644 index 000000000000..20c9f7e7a0a9 --- /dev/null +++ b/layout/reftests/list-item/numbering-5.html @@ -0,0 +1,29 @@ + + + + Testcase for reordered 'li' flex items + + + +
    + +
  1. List item 1
  2. +
  3. List item 2
  4. +
  5. List item 3
  6. + +
  7. List item 4
  8. +
  9. List item 5
  10. + +
  11. List item 6
  12. + +
  13. List item 7
  14. +
  15. List item 8
  16. +
+ + diff --git a/layout/reftests/list-item/reftest.list b/layout/reftests/list-item/reftest.list index 0328c1891b35..034c173927eb 100644 --- a/layout/reftests/list-item/reftest.list +++ b/layout/reftests/list-item/reftest.list @@ -2,6 +2,7 @@ fuzzy-if(OSX,55,4) == numbering-1.html numbering-1-ref.html == numbering-2.html numbering-2-ref.html pref(layout.css.grid.enabled,true) fuzzy-if(OSX,8,1) == numbering-3.html numbering-3-ref.html fuzzy-if(OSX,72,2) == numbering-4.html numbering-4-ref.html +== numbering-5.html numbering-5-ref.html == ol-reversed-1a.html ol-reversed-1-ref.html asserts(1) == ol-reversed-1b.html ol-reversed-1-ref.html # bug 478135 == ol-reversed-1c.html ol-reversed-1-ref.html diff --git a/layout/reftests/svg/fragmentIdentifier-01.xhtml b/layout/reftests/svg/fragmentIdentifier-01.xhtml index 9173f62fd5a5..5a2682825e2e 100644 --- a/layout/reftests/svg/fragmentIdentifier-01.xhtml +++ b/layout/reftests/svg/fragmentIdentifier-01.xhtml @@ -1,22 +1,29 @@ Testcases for SVG fragment identifiers +
- - - +