author | Carsten "Tomcat" Book <cbook@mozilla.com> |
Thu, 06 Oct 2016 11:59:54 +0200 | |
changeset 316742 | 777fb63db8de8d78b64b8141d1a998397275dd03 |
parent 316673 | c3b0354515b6bb59a5324adc21e7e891d6217cae (current diff) |
parent 316741 | 8a4a43911b776d61be3817ec11073b34fdcb672d (diff) |
child 316743 | fef343991976c7ae6d9bb1960f03aa8b3c634288 |
child 316746 | 38442ad9a422c1028f6e40b5144f3d7994e7acf3 |
child 316818 | d0eb4cc9fd0d5f19949e1068863d482343873245 |
child 316861 | 5ef613528deaca7fb841e0f2b58f57b99c04cf7b |
push id | 32902 |
push user | cbook@mozilla.com |
push date | Thu, 06 Oct 2016 10:30:08 +0000 |
treeherder | autoland@fef343991976 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 52.0a1 |
first release with | nightly linux32
777fb63db8de
/
52.0a1
/
20161006030208
/
files
nightly linux64
777fb63db8de
/
52.0a1
/
20161006030208
/
files
nightly mac
777fb63db8de
/
52.0a1
/
20161006030208
/
files
nightly win32
777fb63db8de
/
52.0a1
/
20161006030208
/
files
nightly win64
777fb63db8de
/
52.0a1
/
20161006030208
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
52.0a1
/
20161006030208
/
pushlog to previous
nightly linux64
52.0a1
/
20161006030208
/
pushlog to previous
nightly mac
52.0a1
/
20161006030208
/
pushlog to previous
nightly win32
52.0a1
/
20161006030208
/
pushlog to previous
nightly win64
52.0a1
/
20161006030208
/
pushlog to previous
|
--- a/accessible/aom/moz.build +++ b/accessible/aom/moz.build @@ -30,9 +30,11 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'co LOCAL_INCLUDES += [ '/accessible/mac', ] else: LOCAL_INCLUDES += [ '/accessible/other', ] +include('/ipc/chromium/chromium-config.mozbuild') + FINAL_LIBRARY = 'xul'
--- a/accessible/base/EventTree.cpp +++ b/accessible/base/EventTree.cpp @@ -253,17 +253,17 @@ EventTree::Process(const RefPtr<DocAcces mDependentEvents.Clear(); } EventTree* EventTree::FindOrInsert(Accessible* aContainer) { if (!mFirst) { - mFirst.reset(new EventTree(aContainer, true)); + mFirst.reset(new EventTree(aContainer, mDependentEvents.IsEmpty())); return mFirst.get(); } EventTree* prevNode = nullptr; EventTree* node = mFirst.get(); do { MOZ_ASSERT(!node->mContainer->IsApplication(), "No event for application accessible is expected here");
--- a/accessible/base/NotificationController.cpp +++ b/accessible/base/NotificationController.cpp @@ -420,18 +420,18 @@ NotificationController::WillRefresh(mozi ipcDoc = new DocAccessibleChild(childDoc); childDoc->SetIPCDoc(ipcDoc); nsCOMPtr<nsITabChild> tabChild = do_GetInterface(mDocument->DocumentNode()->GetDocShell()); if (tabChild) { static_cast<TabChild*>(tabChild.get())-> SendPDocAccessibleConstructor(ipcDoc, parentIPCDoc, id); #if defined(XP_WIN) - IAccessibleHolder holder(CreateHolderFromAccessible(childDoc)); - ipcDoc->SendCOMProxy(holder); + MOZ_ASSERT(parentIPCDoc); + ipcDoc->SendMsaaID(AccessibleWrap::GetChildIDFor(childDoc)); #endif } } } mObservingState = eRefreshObserving; if (!mDocument) return;
--- a/accessible/html/moz.build +++ b/accessible/html/moz.build @@ -37,12 +37,14 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'co LOCAL_INCLUDES += [ '/accessible/mac', ] else: LOCAL_INCLUDES += [ '/accessible/other', ] +include('/ipc/chromium/chromium-config.mozbuild') + FINAL_LIBRARY = 'xul' if CONFIG['GNU_CXX']: CXXFLAGS += ['-Wno-error=shadow']
--- a/accessible/ipc/DocAccessibleChildBase.cpp +++ b/accessible/ipc/DocAccessibleChildBase.cpp @@ -49,89 +49,49 @@ DocAccessibleChildBase::InterfacesFor(Ac return interfaces; } /* static */ void DocAccessibleChildBase::SerializeTree(Accessible* aRoot, nsTArray<AccessibleData>& aTree) { uint64_t id = reinterpret_cast<uint64_t>(aRoot->UniqueID()); +#if defined(XP_WIN) + int32_t msaaId = AccessibleWrap::GetChildIDFor(aRoot); +#endif uint32_t role = aRoot->Role(); uint32_t childCount = aRoot->ChildCount(); uint32_t interfaces = InterfacesFor(aRoot); -#if defined(XP_WIN) - IAccessibleHolder holder(CreateHolderFromAccessible(aRoot)); -#endif - // OuterDocAccessibles are special because we don't want to serialize the // child doc here, we'll call PDocAccessibleConstructor in // NotificationController. MOZ_ASSERT(!aRoot->IsDoc(), "documents shouldn't be serialized"); if (aRoot->IsOuterDoc()) { childCount = 0; } #if defined(XP_WIN) - aTree.AppendElement(AccessibleData(id, role, childCount, interfaces, - holder)); + aTree.AppendElement(AccessibleData(id, msaaId, role, childCount, interfaces)); #else aTree.AppendElement(AccessibleData(id, role, childCount, interfaces)); #endif for (uint32_t i = 0; i < childCount; i++) { SerializeTree(aRoot->GetChildAt(i), aTree); } } -#if defined(XP_WIN) -/* static */ void -DocAccessibleChildBase::SetMsaaIds(Accessible* aRoot, - uint32_t& aMsaaIdIndex, - const nsTArray<MsaaMapping>& aNewMsaaIds) -{ - const MsaaMapping& mapping = aNewMsaaIds[aMsaaIdIndex]; -#if defined(DEBUG) - uint64_t id = reinterpret_cast<uint64_t>(aRoot->UniqueID()); - MOZ_ASSERT(mapping.ID() == id); -#endif // defined(DEBUG) - static_cast<AccessibleWrap*>(aRoot)->SetID(mapping.MsaaID()); - ++aMsaaIdIndex; - if (aRoot->IsOuterDoc()) { - // This needs to match the tree traversal in SerializeTree - return; - } - for (uint32_t i = 0, n = aRoot->ChildCount(); i < n; ++i) { - SetMsaaIds(aRoot->GetChildAt(i), aMsaaIdIndex, aNewMsaaIds); - } -} -#endif // defined(XP_WIN) - void DocAccessibleChildBase::ShowEvent(AccShowEvent* aShowEvent) { Accessible* parent = aShowEvent->Parent(); uint64_t parentID = parent->IsDoc() ? 0 : reinterpret_cast<uint64_t>(parent->UniqueID()); uint32_t idxInParent = aShowEvent->InsertionIndex(); nsTArray<AccessibleData> shownTree; ShowEventData data(parentID, idxInParent, shownTree); SerializeTree(aShowEvent->GetAccessible(), data.NewTree()); -#if defined(XP_WIN) - nsTArray<MsaaMapping> newMsaaIds; - SendShowEventInfo(data, &newMsaaIds); - // newMsaaIds could be empty if something went wrong in SendShowEvent() - if (!newMsaaIds.IsEmpty()) { - uint32_t index = 0; - SetMsaaIds(aShowEvent->GetAccessible(), index, newMsaaIds); - } - // NB: On Windows, SendShowEvent attaches the subtree and generates new IDs, - // but does *NOT* fire the native event. We need to do that after - // we've called SetMsaaIds. - SendEvent(reinterpret_cast<uint64_t>(aShowEvent->GetAccessible()->UniqueID()), - nsIAccessibleEvent::EVENT_SHOW); -#else SendShowEvent(data, aShowEvent->IsFromUserInput()); -#endif // defined(XP_WIN) } } // namespace a11y } // namespace mozilla
--- a/accessible/ipc/DocAccessibleChildBase.h +++ b/accessible/ipc/DocAccessibleChildBase.h @@ -55,20 +55,16 @@ public: mDoc->SetIPCDoc(nullptr); mDoc = nullptr; } protected: static uint32_t InterfacesFor(Accessible* aAcc); static void SerializeTree(Accessible* aRoot, nsTArray<AccessibleData>& aTree); -#if defined(XP_WIN) - static void SetMsaaIds(Accessible* aRoot, uint32_t& aMsaaIdIndex, - const nsTArray<MsaaMapping>& aNewMsaaIds); -#endif DocAccessible* mDoc; }; } // namespace a11y } // namespace mozilla #endif // mozilla_a11y_DocAccessibleChildBase_h
--- a/accessible/ipc/DocAccessibleParent.cpp +++ b/accessible/ipc/DocAccessibleParent.cpp @@ -11,23 +11,18 @@ #include "xpcAccEvents.h" #include "nsAccUtils.h" #include "nsCoreUtils.h" namespace mozilla { namespace a11y { bool -#if defined(XP_WIN) -DocAccessibleParent::RecvShowEventInfo(const ShowEventData& aData, - nsTArray<MsaaMapping>* aNewMsaaIds) -#else DocAccessibleParent::RecvShowEvent(const ShowEventData& aData, const bool& aFromUser) -#endif // defined(XP_WIN) { if (mShutdown) return true; MOZ_DIAGNOSTIC_ASSERT(CheckDocTree()); if (aData.NewTree().IsEmpty()) { NS_ERROR("no children being added"); @@ -44,23 +39,17 @@ DocAccessibleParent::RecvShowEvent(const } uint32_t newChildIdx = aData.Idx(); if (newChildIdx > parent->ChildrenCount()) { NS_ERROR("invalid index to add child at"); return true; } -#if defined(XP_WIN) - aNewMsaaIds->SetCapacity(aData.NewTree().Length()); - uint32_t consumed = AddSubtree(parent, aData.NewTree(), 0, newChildIdx, - aNewMsaaIds); -#else uint32_t consumed = AddSubtree(parent, aData.NewTree(), 0, newChildIdx); -#endif MOZ_ASSERT(consumed == aData.NewTree().Length()); // XXX This shouldn't happen, but if we failed to add children then the below // is pointless and can crash. if (!consumed) { return true; } @@ -68,46 +57,38 @@ DocAccessibleParent::RecvShowEvent(const for (uint32_t i = 0; i < consumed; i++) { uint64_t id = aData.NewTree()[i].ID(); MOZ_ASSERT(mAccessibles.GetEntry(id)); } #endif MOZ_DIAGNOSTIC_ASSERT(CheckDocTree()); - // NB: On Windows we dispatch the native event via a subsequent call to - // RecvEvent(). -#if !defined(XP_WIN) ProxyAccessible* target = parent->ChildAt(newChildIdx); ProxyShowHideEvent(target, parent, true, aFromUser); if (!nsCoreUtils::AccEventObserversExist()) { return true; } uint32_t type = nsIAccessibleEvent::EVENT_SHOW; xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(target); xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this); nsIDOMNode* node = nullptr; RefPtr<xpcAccEvent> event = new xpcAccEvent(type, xpcAcc, doc, node, aFromUser); nsCoreUtils::DispatchAccEvent(Move(event)); -#endif return true; } uint32_t DocAccessibleParent::AddSubtree(ProxyAccessible* aParent, const nsTArray<a11y::AccessibleData>& aNewTree, - uint32_t aIdx, uint32_t aIdxInParent -#if defined(XP_WIN) - , nsTArray<MsaaMapping>* aNewMsaaIds -#endif - ) + uint32_t aIdx, uint32_t aIdxInParent) { if (aNewTree.Length() <= aIdx) { NS_ERROR("bad index in serialized tree!"); return 0; } const AccessibleData& newChild = aNewTree[aIdx]; if (newChild.Role() > roles::LAST_ROLE) { @@ -117,53 +98,32 @@ DocAccessibleParent::AddSubtree(ProxyAcc if (mAccessibles.Contains(newChild.ID())) { NS_ERROR("ID already in use"); return 0; } auto role = static_cast<a11y::role>(newChild.Role()); -#if defined(XP_WIN) - const IAccessibleHolder& proxyStream = newChild.COMProxy(); - RefPtr<IAccessible> comPtr(proxyStream.Get()); - if (!comPtr) { - NS_ERROR("Could not obtain remote IAccessible interface"); - return 0; - } - - ProxyAccessible* newProxy = - new ProxyAccessible(newChild.ID(), aParent, this, role, - newChild.Interfaces(), comPtr); -#else ProxyAccessible* newProxy = new ProxyAccessible(newChild.ID(), aParent, this, role, newChild.Interfaces()); -#endif aParent->AddChildAt(aIdxInParent, newProxy); mAccessibles.PutEntry(newChild.ID())->mProxy = newProxy; ProxyCreated(newProxy, newChild.Interfaces()); #if defined(XP_WIN) - Accessible* idForAcc = WrapperFor(newProxy); - MOZ_ASSERT(idForAcc); - uint32_t newMsaaId = AccessibleWrap::GetChildIDFor(idForAcc); - MOZ_ASSERT(newMsaaId); - aNewMsaaIds->AppendElement(MsaaMapping(newChild.ID(), newMsaaId)); -#endif // defined(XP_WIN) + WrapperFor(newProxy)->SetID(newChild.MsaaID()); +#endif uint32_t accessibles = 1; uint32_t kids = newChild.ChildrenCount(); for (uint32_t i = 0; i < kids; i++) { - uint32_t consumed = AddSubtree(newProxy, aNewTree, aIdx + accessibles, i -#if defined(XP_WIN) - , aNewMsaaIds -#endif - ); + uint32_t consumed = AddSubtree(newProxy, aNewTree, aIdx + accessibles, i); if (!consumed) return 0; accessibles += consumed; } MOZ_ASSERT(newProxy->ChildrenCount() == kids); @@ -495,36 +455,45 @@ DocAccessibleParent::GetXPCAccessible(Pr xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this); MOZ_ASSERT(doc); return doc->GetXPCAccessible(aProxy); } #if defined(XP_WIN) /** + * @param aMsaaID The MSAA ID that was generated by content that the chrome + * process should assign to this DocAccessibleParent. * @param aCOMProxy COM Proxy to the document in the content process. * @param aParentCOMProxy COM Proxy to the OuterDocAccessible that is * the parent of the document. The content process will use this * proxy when traversing up across the content/chrome boundary. */ bool -DocAccessibleParent::RecvCOMProxy(const IAccessibleHolder& aCOMProxy, - IAccessibleHolder* aParentCOMProxy, - uint32_t* aMsaaID) +DocAccessibleParent::RecvCOMProxy(const int32_t& aMsaaID, + const IAccessibleHolder& aCOMProxy, + IAccessibleHolder* aParentCOMProxy) { + WrapperFor(this)->SetID(aMsaaID); + RefPtr<IAccessible> ptr(aCOMProxy.Get()); SetCOMInterface(ptr); Accessible* outerDoc = OuterDocOfRemoteBrowser(); IAccessible* rawNative = nullptr; if (outerDoc) { outerDoc->GetNativeInterface((void**) &rawNative); } aParentCOMProxy->Set(IAccessibleHolder::COMPtrType(rawNative)); - Accessible* wrapper = WrapperFor(this); - *aMsaaID = AccessibleWrap::GetChildIDFor(wrapper); + return true; +} + +bool +DocAccessibleParent::RecvMsaaID(const int32_t& aMsaaID) +{ + WrapperFor(this)->SetID(aMsaaID); return true; } #endif // defined(XP_WIN) } // a11y } // mozilla
--- a/accessible/ipc/DocAccessibleParent.h +++ b/accessible/ipc/DocAccessibleParent.h @@ -45,23 +45,18 @@ public: /* * Called when a message from a document in a child process notifies the main * process it is firing an event. */ virtual bool RecvEvent(const uint64_t& aID, const uint32_t& aType) override; -#if defined(XP_WIN) - virtual bool RecvShowEventInfo(const ShowEventData& aData, - nsTArray<MsaaMapping>* aNewMsaaIds) override; -#else virtual bool RecvShowEvent(const ShowEventData& aData, const bool& aFromUser) override; -#endif // defined(XP_WIN) virtual bool RecvHideEvent(const uint64_t& aRootID, const bool& aFromUser) override; virtual bool RecvStateChangeEvent(const uint64_t& aID, const uint64_t& aState, const bool& aEnabled) override final; virtual bool RecvCaretMoveEvent(const uint64_t& aID, const int32_t& aOffset) override final; @@ -73,16 +68,17 @@ public: virtual bool RecvSelectionEvent(const uint64_t& aID, const uint64_t& aWidgetID, const uint32_t& aType) override; virtual bool RecvRoleChangedEvent(const uint32_t& aRole) override final; virtual bool RecvBindChildDoc(PDocAccessibleParent* aChildDoc, const uint64_t& aID) override; + void Unbind() { mParent = nullptr; if (DocAccessibleParent* parent = ParentDoc()) { parent->mChildDocs.RemoveElement(this); } mParentDoc = nullptr; @@ -143,19 +139,21 @@ public: const ProxyAccessible* GetAccessible(uintptr_t aID) const { return const_cast<DocAccessibleParent*>(this)->GetAccessible(aID); } size_t ChildDocCount() const { return mChildDocs.Length(); } const DocAccessibleParent* ChildDocAt(size_t aIdx) const { return mChildDocs[aIdx]; } #if defined(XP_WIN) - virtual bool RecvCOMProxy(const IAccessibleHolder& aCOMProxy, - IAccessibleHolder* aParentCOMProxy, - uint32_t* aMsaaID) override; + virtual bool RecvCOMProxy(const int32_t& aMsaaID, + const IAccessibleHolder& aCOMProxy, + IAccessibleHolder* aParentCOMProxy) override; + + virtual bool RecvMsaaID(const int32_t& aMsaaID) override; #endif private: class ProxyEntry : public PLDHashEntryHdr { public: explicit ProxyEntry(const void*) : mProxy(nullptr) {} @@ -175,21 +173,17 @@ private: enum { ALLOW_MEMMOVE = true }; ProxyAccessible* mProxy; }; uint32_t AddSubtree(ProxyAccessible* aParent, const nsTArray<AccessibleData>& aNewTree, uint32_t aIdx, - uint32_t aIdxInParent -#if defined(XP_WIN) - , nsTArray<MsaaMapping>* aNewMsaaIds -#endif // defined(XP_WIN) - ); + uint32_t aIdxInParent); MOZ_MUST_USE bool CheckDocTree() const; xpcAccessibleGeneric* GetXPCAccessible(ProxyAccessible* aProxy); nsTArray<DocAccessibleParent*> mChildDocs; DocAccessibleParent* mParentDoc; /* * Conceptually this is a map from IDs to proxies, but we store the ID in the
--- a/accessible/ipc/win/DocAccessibleChild.cpp +++ b/accessible/ipc/win/DocAccessibleChild.cpp @@ -28,18 +28,17 @@ DocAccessibleChild::DocAccessibleChild(D DocAccessibleChild::~DocAccessibleChild() { MOZ_COUNT_DTOR_INHERITED(DocAccessibleChild, DocAccessibleChildBase); } void DocAccessibleChild::SendCOMProxy(const IAccessibleHolder& aProxy) { + int32_t msaaID = AccessibleWrap::GetChildIDFor(mDoc); IAccessibleHolder parentProxy; - uint32_t msaaID = AccessibleWrap::kNoID; - PDocAccessibleChild::SendCOMProxy(aProxy, &parentProxy, &msaaID); + PDocAccessibleChild::SendCOMProxy(msaaID, aProxy, &parentProxy); mParentProxy.reset(parentProxy.Release()); - mDoc->SetID(msaaID); } } // namespace a11y } // namespace mozilla
--- a/accessible/ipc/win/PDocAccessible.ipdl +++ b/accessible/ipc/win/PDocAccessible.ipdl @@ -10,35 +10,29 @@ include protocol PBrowser; using mozilla::a11y::IAccessibleHolder from "mozilla/a11y/COMPtrTypes.h"; namespace mozilla { namespace a11y { struct AccessibleData { uint64_t ID; + int32_t MsaaID; uint32_t Role; uint32_t ChildrenCount; uint32_t Interfaces; - IAccessibleHolder COMProxy; }; struct ShowEventData { uint64_t ID; uint32_t Idx; AccessibleData[] NewTree; }; -struct MsaaMapping -{ - uint64_t ID; - uint32_t MsaaID; -}; - struct Attribute { nsCString Name; nsString Value; }; sync protocol PDocAccessible { @@ -47,34 +41,36 @@ sync protocol PDocAccessible parent: async Shutdown(); /* * Notify the parent process the document in the child process is firing an * event. */ async Event(uint64_t aID, uint32_t type); - sync ShowEventInfo(ShowEventData data) returns (MsaaMapping[] aNewMsaaIds); + async ShowEvent(ShowEventData data, bool aFromUser); async HideEvent(uint64_t aRootID, bool aFromUser); async StateChangeEvent(uint64_t aID, uint64_t aState, bool aEnabled); async CaretMoveEvent(uint64_t aID, int32_t aOffset); async TextChangeEvent(uint64_t aID, nsString aStr, int32_t aStart, uint32_t aLen, bool aIsInsert, bool aFromUser); async SelectionEvent(uint64_t aID, uint64_t aWidgetID, uint32_t aType); async RoleChangedEvent(uint32_t aRole); /* * Tell the parent document to bind the existing document as a new child * document. */ async BindChildDoc(PDocAccessible aChildDoc, uint64_t aID); // For now we'll add the command to send the proxy here. This might move to // PDocAccessible constructor in PBrowser. - sync COMProxy(IAccessibleHolder aDocCOMProxy) - returns(IAccessibleHolder aParentCOMProxy, uint32_t aMsaaID); + sync COMProxy(int32_t aMsaaID, IAccessibleHolder aDocCOMProxy) + returns(IAccessibleHolder aParentCOMProxy); + + async MsaaID(int32_t aMsaaID); child: async __delete__(); }; } }
--- a/accessible/ipc/win/ProxyAccessible.cpp +++ b/accessible/ipc/win/ProxyAccessible.cpp @@ -14,107 +14,106 @@ #include "mozilla/Unused.h" #include "mozilla/a11y/Platform.h" #include "RelationType.h" #include "mozilla/a11y/Role.h" #include "xpcAccessibleDocument.h" #include <comutil.h> +static const VARIANT kChildIdSelf = {VT_I4}; + namespace mozilla { namespace a11y { bool ProxyAccessible::GetCOMInterface(void** aOutAccessible) const { if (!aOutAccessible) { return false; } + + if (!mCOMProxy) { + // See if we can lazily obtain a COM proxy + AccessibleWrap* wrap = WrapperFor(this); + bool isDefunct = false; + ProxyAccessible* thisPtr = const_cast<ProxyAccessible*>(this); + thisPtr->mCOMProxy = wrap->GetIAccessibleFor(kChildIdSelf, &isDefunct); + } + RefPtr<IAccessible> addRefed = mCOMProxy; addRefed.forget(aOutAccessible); return !!mCOMProxy; } void ProxyAccessible::Name(nsString& aName) const { aName.Truncate(); RefPtr<IAccessible> acc; if (!GetCOMInterface((void**)getter_AddRefs(acc))) { return; } - VARIANT id; - id.vt = VT_I4; - id.lVal = CHILDID_SELF; BSTR result; - HRESULT hr = acc->get_accName(id, &result); + HRESULT hr = acc->get_accName(kChildIdSelf, &result); _bstr_t resultWrap(result, false); if (FAILED(hr)) { return; } aName = (wchar_t*)resultWrap; } void ProxyAccessible::Value(nsString& aValue) const { aValue.Truncate(); RefPtr<IAccessible> acc; if (!GetCOMInterface((void**)getter_AddRefs(acc))) { return; } - VARIANT id; - id.vt = VT_I4; - id.lVal = CHILDID_SELF; BSTR result; - HRESULT hr = acc->get_accValue(id, &result); + HRESULT hr = acc->get_accValue(kChildIdSelf, &result); _bstr_t resultWrap(result, false); if (FAILED(hr)) { return; } aValue = (wchar_t*)resultWrap; } void ProxyAccessible::Description(nsString& aDesc) const { aDesc.Truncate(); RefPtr<IAccessible> acc; if (!GetCOMInterface((void**)getter_AddRefs(acc))) { return; } - VARIANT id; - id.vt = VT_I4; - id.lVal = CHILDID_SELF; BSTR result; - HRESULT hr = acc->get_accDescription(id, &result); + HRESULT hr = acc->get_accDescription(kChildIdSelf, &result); _bstr_t resultWrap(result, false); if (FAILED(hr)) { return; } aDesc = (wchar_t*)resultWrap; } uint64_t ProxyAccessible::State() const { uint64_t state = 0; RefPtr<IAccessible> acc; if (!GetCOMInterface((void**)getter_AddRefs(acc))) { return state; } - VARIANT id; - id.vt = VT_I4; - id.lVal = CHILDID_SELF; VARIANT varState; - HRESULT hr = acc->get_accState(id, &varState); + HRESULT hr = acc->get_accState(kChildIdSelf, &varState); if (FAILED(hr)) { return state; } return uint64_t(varState.lVal); } nsIntRect ProxyAccessible::Bounds() @@ -125,20 +124,17 @@ ProxyAccessible::Bounds() if (!GetCOMInterface((void**)getter_AddRefs(acc))) { return rect; } long left; long top; long width; long height; - VARIANT id; - id.vt = VT_I4; - id.lVal = CHILDID_SELF; - HRESULT hr = acc->accLocation(&left, &top, &width, &height, id); + HRESULT hr = acc->accLocation(&left, &top, &width, &height, kChildIdSelf); if (FAILED(hr)) { return rect; } rect.x = left; rect.y = top; rect.width = width; rect.height = height; return rect;
--- a/accessible/ipc/win/ProxyAccessible.h +++ b/accessible/ipc/win/ProxyAccessible.h @@ -20,21 +20,18 @@ namespace mozilla { namespace a11y { class ProxyAccessible : public ProxyAccessibleBase<ProxyAccessible> { public: ProxyAccessible(uint64_t aID, ProxyAccessible* aParent, - DocAccessibleParent* aDoc, role aRole, uint32_t aInterfaces, - const RefPtr<IAccessible>& aIAccessible) + DocAccessibleParent* aDoc, role aRole, uint32_t aInterfaces) : ProxyAccessibleBase(aID, aParent, aDoc, aRole, aInterfaces) - , mCOMProxy(aIAccessible) - { MOZ_COUNT_CTOR(ProxyAccessible); } ~ProxyAccessible() { MOZ_COUNT_DTOR(ProxyAccessible); }
--- a/accessible/tests/mochitest/treeupdate/test_ariaowns.html +++ b/accessible/tests/mochitest/treeupdate/test_ariaowns.html @@ -72,17 +72,18 @@ function removeARIAOwns() { this.eventSeq = [ new invokerChecker(EVENT_HIDE, getNode("t1_subdiv")), new invokerChecker(EVENT_SHOW, getNode("t1_subdiv")), new invokerChecker(EVENT_HIDE, getNode("t1_button")), new invokerChecker(EVENT_SHOW, getNode("t1_button")), - new invokerChecker(EVENT_REORDER, getNode("t1_container")) + new invokerChecker(EVENT_REORDER, getNode("t1_container")), + new unexpectedInvokerChecker(EVENT_REORDER, getNode("t1_checkbox")) ]; this.invoke = function removeARIAOwns_invoke() { getNode("t1_container").removeAttribute("aria-owns"); } this.finalCheck = function removeARIAOwns_finalCheck()
--- a/accessible/windows/msaa/AccessibleWrap.cpp +++ b/accessible/windows/msaa/AccessibleWrap.cpp @@ -37,16 +37,17 @@ #include "nsIServiceManager.h" #include "nsNameSpaceManager.h" #include "nsTextFormatter.h" #include "nsView.h" #include "nsViewManager.h" #include "nsEventMap.h" #include "nsArrayUtils.h" #include "mozilla/Preferences.h" +#include "nsIXULRuntime.h" #include "oleacc.h" using namespace mozilla; using namespace mozilla::a11y; const uint32_t USE_ROLE_STRING = 0; @@ -55,37 +56,36 @@ const uint32_t USE_ROLE_STRING = 0; */ //#define DEBUG_LEAKS #ifdef DEBUG_LEAKS static gAccessibles = 0; #endif -#ifdef _WIN64 -IDSet AccessibleWrap::sIDGen; -#endif +MsaaIdGenerator AccessibleWrap::sIDGen; + +static const VARIANT kVarChildIdSelf = {VT_I4}; static const int32_t kIEnumVariantDisconnected = -1; //////////////////////////////////////////////////////////////////////////////// // AccessibleWrap //////////////////////////////////////////////////////////////////////////////// AccessibleWrap::AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc) : Accessible(aContent, aDoc) , mID(kNoID) { } AccessibleWrap::~AccessibleWrap() { -#ifdef _WIN64 - if (mID != kNoID && XRE_IsParentProcess()) - sIDGen.ReleaseID(mID); -#endif + if (mID != kNoID) { + sIDGen.ReleaseID(this); + } } ITypeInfo* AccessibleWrap::gTypeInfo = nullptr; NS_IMPL_ISUPPORTS_INHERITED0(AccessibleWrap, Accessible) void AccessibleWrap::Shutdown() @@ -244,56 +244,116 @@ AccessibleWrap::get_accChild( return E_INVALIDARG; *ppdispChild = nullptr; if (IsDefunct()) return CO_E_OBJNOTCONNECTED; // IAccessible::accChild is used to return this accessible or child accessible // at the given index or to get an accessible by child ID in the case of - // document accessible (it's handled by overriden GetXPAccessibleFor method - // on the document accessible). The getting an accessible by child ID is used - // by AccessibleObjectFromEvent() called by AT when AT handles our MSAA event. - Accessible* child = GetXPAccessibleFor(varChild); - if (!child) + // document accessible. + // The getting an accessible by child ID is used by AccessibleObjectFromEvent() + // called by AT when AT handles our MSAA event. + bool isDefunct = false; + RefPtr<IAccessible> child = GetIAccessibleFor(varChild, &isDefunct); + if (!child) { return E_INVALIDARG; + } - if (child->IsDefunct()) + if (isDefunct) { return CO_E_OBJNOTCONNECTED; + } - *ppdispChild = NativeAccessible(child); + child.forget(ppdispChild); return S_OK; A11Y_TRYBLOCK_END } +/** + * This function is a helper for implementing IAccessible methods that accept + * a Child ID as a parameter. If the child ID is CHILDID_SELF, the function + * returns S_OK but a null *aOutInterface. Otherwise, *aOutInterface points + * to the resolved IAccessible. + * + * The CHILDID_SELF case is special because in that case we actually execute + * the implementation of the IAccessible method, whereas in the non-self case, + * we delegate the method call to that object for execution. + * + * A sample invocation of this would look like: + * + * RefPtr<IAccessible> accessible; + * HRESULT hr = ResolveChild(varChild, getter_AddRefs(accessible)); + * if (FAILED(hr)) { + * return hr; + * } + * + * if (accessible) { + * return accessible->get_accFoo(kVarChildIdSelf, pszName); + * } + * + * // Implementation for CHILDID_SELF case goes here + */ +HRESULT +AccessibleWrap::ResolveChild(const VARIANT& aVarChild, + IAccessible** aOutInterface) +{ + MOZ_ASSERT(aOutInterface); + *aOutInterface = nullptr; + + if (aVarChild.vt != VT_I4) { + return E_INVALIDARG; + } + + if (IsDefunct()) { + return CO_E_OBJNOTCONNECTED; + } + + if (aVarChild.lVal == CHILDID_SELF) { + return S_OK; + } + + bool isDefunct = false; + RefPtr<IAccessible> accessible = GetIAccessibleFor(aVarChild, &isDefunct); + if (!accessible) { + return E_INVALIDARG; + } + + if (isDefunct) { + return CO_E_OBJNOTCONNECTED; + } + + accessible.forget(aOutInterface); + return S_OK; +} + STDMETHODIMP AccessibleWrap::get_accName( /* [optional][in] */ VARIANT varChild, /* [retval][out] */ BSTR __RPC_FAR *pszName) { A11Y_TRYBLOCK_BEGIN - if (!pszName) + if (!pszName || varChild.vt != VT_I4) return E_INVALIDARG; *pszName = nullptr; - if (IsDefunct()) - return CO_E_OBJNOTCONNECTED; + RefPtr<IAccessible> accessible; + HRESULT hr = ResolveChild(varChild, getter_AddRefs(accessible)); + if (FAILED(hr)) { + return hr; + } - Accessible* xpAccessible = GetXPAccessibleFor(varChild); - if (!xpAccessible) - return E_INVALIDARG; - - if (xpAccessible->IsDefunct()) - return CO_E_OBJNOTCONNECTED; + if (accessible) { + return accessible->get_accName(kVarChildIdSelf, pszName); + } nsAutoString name; - xpAccessible->Name(name); + Name(name); // The name was not provided, e.g. no alt attribute for an image. A screen // reader may choose to invent its own accessible name, e.g. from an image src // attribute. Refer to eNoNameOnPurpose return value. if (name.IsVoid()) return S_FALSE; *pszName = ::SysAllocStringLen(name.get(), name.Length()); @@ -312,28 +372,28 @@ AccessibleWrap::get_accValue( { A11Y_TRYBLOCK_BEGIN if (!pszValue) return E_INVALIDARG; *pszValue = nullptr; - if (IsDefunct()) - return CO_E_OBJNOTCONNECTED; + RefPtr<IAccessible> accessible; + HRESULT hr = ResolveChild(varChild, getter_AddRefs(accessible)); + if (FAILED(hr)) { + return hr; + } - Accessible* xpAccessible = GetXPAccessibleFor(varChild); - if (!xpAccessible) - return E_INVALIDARG; - - if (xpAccessible->IsDefunct()) - return CO_E_OBJNOTCONNECTED; + if (accessible) { + return accessible->get_accValue(kVarChildIdSelf, pszValue); + } nsAutoString value; - xpAccessible->Value(value); + Value(value); // See bug 438784: need to expose URL on doc's value attribute. For this, // reverting part of fix for bug 425693 to make this MSAA method behave // IAccessible2-style. if (value.IsEmpty()) return S_FALSE; *pszValue = ::SysAllocStringLen(value.get(), value.Length()); @@ -350,28 +410,28 @@ AccessibleWrap::get_accDescription(VARIA { A11Y_TRYBLOCK_BEGIN if (!pszDescription) return E_INVALIDARG; *pszDescription = nullptr; - if (IsDefunct()) - return CO_E_OBJNOTCONNECTED; + RefPtr<IAccessible> accessible; + HRESULT hr = ResolveChild(varChild, getter_AddRefs(accessible)); + if (FAILED(hr)) { + return hr; + } - Accessible* xpAccessible = GetXPAccessibleFor(varChild); - if (!xpAccessible) - return E_INVALIDARG; - - if (xpAccessible->IsDefunct()) - return CO_E_OBJNOTCONNECTED; + if (accessible) { + return accessible->get_accDescription(kVarChildIdSelf, pszDescription); + } nsAutoString description; - xpAccessible->Description(description); + Description(description); *pszDescription = ::SysAllocStringLen(description.get(), description.Length()); return *pszDescription ? S_OK : E_OUTOFMEMORY; A11Y_TRYBLOCK_END } @@ -382,33 +442,33 @@ AccessibleWrap::get_accRole( { A11Y_TRYBLOCK_BEGIN if (!pvarRole) return E_INVALIDARG; VariantInit(pvarRole); - if (IsDefunct()) - return CO_E_OBJNOTCONNECTED; + RefPtr<IAccessible> accessible; + HRESULT hr = ResolveChild(varChild, getter_AddRefs(accessible)); + if (FAILED(hr)) { + return hr; + } - Accessible* xpAccessible = GetXPAccessibleFor(varChild); - if (!xpAccessible) - return E_INVALIDARG; - - if (xpAccessible->IsDefunct()) - return CO_E_OBJNOTCONNECTED; + if (accessible) { + return accessible->get_accRole(kVarChildIdSelf, pvarRole); + } a11y::role geckoRole; #ifdef DEBUG - NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(xpAccessible), + NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(this), "Does not support Text when it should"); #endif - geckoRole = xpAccessible->Role(); + geckoRole = Role(); uint32_t msaaRole = 0; #define ROLE(_geckoRole, stringRole, atkRole, macRole, \ _msaaRole, ia2Role, nameRule) \ case roles::_geckoRole: \ msaaRole = _msaaRole; \ break; @@ -435,17 +495,17 @@ AccessibleWrap::get_accRole( pvarRole->vt = VT_I4; pvarRole->lVal = msaaRole; // Normal enumerated role return S_OK; } // -- Try BSTR role // Could not map to known enumerated MSAA role like ROLE_BUTTON // Use BSTR role to expose role attribute or tag name + namespace - nsIContent *content = xpAccessible->GetContent(); + nsIContent *content = GetContent(); if (!content) return E_FAIL; if (content->IsElement()) { nsAutoString roleString; if (msaaRole != ROLE_SYSTEM_CLIENT && !content->GetAttr(kNameSpaceID_None, nsGkAtoms::role, roleString)) { nsIDocument * document = content->GetUncomposedDoc(); @@ -484,25 +544,25 @@ AccessibleWrap::get_accState( if (!pvarState) return E_INVALIDARG; VariantInit(pvarState); pvarState->vt = VT_I4; pvarState->lVal = 0; - if (IsDefunct()) - return CO_E_OBJNOTCONNECTED; + RefPtr<IAccessible> accessible; + HRESULT hr = ResolveChild(varChild, getter_AddRefs(accessible)); + if (FAILED(hr)) { + return hr; + } - Accessible* xpAccessible = GetXPAccessibleFor(varChild); - if (!xpAccessible) - return E_INVALIDARG; - - if (xpAccessible->IsDefunct()) - return CO_E_OBJNOTCONNECTED; + if (accessible) { + return accessible->get_accState(kVarChildIdSelf, pvarState); + } // MSAA only has 31 states and the lowest 31 bits of our state bit mask // are the same states as MSAA. // Note: we map the following Gecko states to different MSAA states: // REQUIRED -> ALERT_LOW // ALERT -> ALERT_MEDIUM // INVALID -> ALERT_HIGH // CHECKABLE -> MARQUEED @@ -558,29 +618,30 @@ AccessibleWrap::get_accKeyboardShortcut( /* [retval][out] */ BSTR __RPC_FAR *pszKeyboardShortcut) { A11Y_TRYBLOCK_BEGIN if (!pszKeyboardShortcut) return E_INVALIDARG; *pszKeyboardShortcut = nullptr; - if (IsDefunct()) - return CO_E_OBJNOTCONNECTED; - - Accessible* acc = GetXPAccessibleFor(varChild); - if (!acc) - return E_INVALIDARG; + RefPtr<IAccessible> accessible; + HRESULT hr = ResolveChild(varChild, getter_AddRefs(accessible)); + if (FAILED(hr)) { + return hr; + } - if (acc->IsDefunct()) - return CO_E_OBJNOTCONNECTED; + if (accessible) { + return accessible->get_accKeyboardShortcut(kVarChildIdSelf, + pszKeyboardShortcut); + } - KeyBinding keyBinding = acc->AccessKey(); + KeyBinding keyBinding = AccessKey(); if (keyBinding.IsEmpty()) - keyBinding = acc->KeyboardShortcut(); + keyBinding = KeyboardShortcut(); nsAutoString shortcut; keyBinding.ToString(shortcut); *pszKeyboardShortcut = ::SysAllocStringLen(shortcut.get(), shortcut.Length()); return *pszKeyboardShortcut ? S_OK : E_OUTOFMEMORY; @@ -791,81 +852,80 @@ AccessibleWrap::get_accDefaultAction( { A11Y_TRYBLOCK_BEGIN if (!pszDefaultAction) return E_INVALIDARG; *pszDefaultAction = nullptr; - if (IsDefunct()) - return CO_E_OBJNOTCONNECTED; + RefPtr<IAccessible> accessible; + HRESULT hr = ResolveChild(varChild, getter_AddRefs(accessible)); + if (FAILED(hr)) { + return hr; + } - Accessible* xpAccessible = GetXPAccessibleFor(varChild); - if (!xpAccessible) - return E_INVALIDARG; - - if (xpAccessible->IsDefunct()) - return CO_E_OBJNOTCONNECTED; + if (accessible) { + return accessible->get_accDefaultAction(kVarChildIdSelf, pszDefaultAction); + } nsAutoString defaultAction; - xpAccessible->ActionNameAt(0, defaultAction); + ActionNameAt(0, defaultAction); *pszDefaultAction = ::SysAllocStringLen(defaultAction.get(), defaultAction.Length()); return *pszDefaultAction ? S_OK : E_OUTOFMEMORY; A11Y_TRYBLOCK_END } STDMETHODIMP AccessibleWrap::accSelect( /* [in] */ long flagsSelect, /* [optional][in] */ VARIANT varChild) { A11Y_TRYBLOCK_BEGIN - if (IsDefunct()) - return CO_E_OBJNOTCONNECTED; + RefPtr<IAccessible> accessible; + HRESULT hr = ResolveChild(varChild, getter_AddRefs(accessible)); + if (FAILED(hr)) { + return hr; + } - // currently only handle focus and selection - Accessible* xpAccessible = GetXPAccessibleFor(varChild); - if (!xpAccessible) - return E_INVALIDARG; - - if (xpAccessible->IsDefunct()) - return CO_E_OBJNOTCONNECTED; + if (accessible) { + return accessible->accSelect(flagsSelect, kVarChildIdSelf); + } if (flagsSelect & SELFLAG_TAKEFOCUS) { if (XRE_IsContentProcess()) { // In this case we might have been invoked while the IPC MessageChannel is // waiting on a sync reply. We cannot dispatch additional IPC while that // is happening, so we dispatch TakeFocus from the main thread to // guarantee that we are outside any IPC. nsCOMPtr<nsIRunnable> runnable = - mozilla::NewRunnableMethod(xpAccessible, &Accessible::TakeFocus); + mozilla::NewRunnableMethod(this, &Accessible::TakeFocus); NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL); return S_OK; } - xpAccessible->TakeFocus(); + TakeFocus(); return S_OK; } if (flagsSelect & SELFLAG_TAKESELECTION) { - xpAccessible->TakeSelection(); + TakeSelection(); return S_OK; } if (flagsSelect & SELFLAG_ADDSELECTION) { - xpAccessible->SetSelected(true); + SetSelected(true); return S_OK; } if (flagsSelect & SELFLAG_REMOVESELECTION) { - xpAccessible->SetSelected(false); + SetSelected(false); return S_OK; } return E_FAIL; A11Y_TRYBLOCK_END } @@ -882,27 +942,28 @@ AccessibleWrap::accLocation( if (!pxLeft || !pyTop || !pcxWidth || !pcyHeight) return E_INVALIDARG; *pxLeft = 0; *pyTop = 0; *pcxWidth = 0; *pcyHeight = 0; - if (IsDefunct()) - return CO_E_OBJNOTCONNECTED; + RefPtr<IAccessible> accessible; + HRESULT hr = ResolveChild(varChild, getter_AddRefs(accessible)); + if (FAILED(hr)) { + return hr; + } - Accessible* xpAccessible = GetXPAccessibleFor(varChild); - if (!xpAccessible) - return E_INVALIDARG; + if (accessible) { + return accessible->accLocation(pxLeft, pyTop, pcxWidth, pcyHeight, + kVarChildIdSelf); + } - if (xpAccessible->IsDefunct()) - return CO_E_OBJNOTCONNECTED; - - nsIntRect rect = xpAccessible->Bounds(); + nsIntRect rect = Bounds(); *pxLeft = rect.x; *pyTop = rect.y; *pcxWidth = rect.width; *pcyHeight = rect.height; return S_OK; A11Y_TRYBLOCK_END @@ -916,64 +977,64 @@ AccessibleWrap::accNavigate( { A11Y_TRYBLOCK_BEGIN if (!pvarEndUpAt) return E_INVALIDARG; VariantInit(pvarEndUpAt); - if (IsDefunct()) - return CO_E_OBJNOTCONNECTED; + RefPtr<IAccessible> accessible; + HRESULT hr = ResolveChild(varStart, getter_AddRefs(accessible)); + if (FAILED(hr)) { + return hr; + } - Accessible* accessible = GetXPAccessibleFor(varStart); - if (!accessible) - return E_INVALIDARG; - - if (accessible->IsDefunct()) - return CO_E_OBJNOTCONNECTED; + if (accessible) { + return accessible->accNavigate(navDir, kVarChildIdSelf, pvarEndUpAt); + } Accessible* navAccessible = nullptr; Maybe<RelationType> xpRelation; #define RELATIONTYPE(geckoType, stringType, atkType, msaaType, ia2Type) \ case msaaType: \ xpRelation.emplace(RelationType::geckoType); \ break; switch(navDir) { case NAVDIR_FIRSTCHILD: - if (accessible->IsProxy()) { - if (!accessible->Proxy()->MustPruneChildren()) { - navAccessible = WrapperFor(accessible->Proxy()->FirstChild()); + if (IsProxy()) { + if (!Proxy()->MustPruneChildren()) { + navAccessible = WrapperFor(Proxy()->FirstChild()); } } else { - if (!nsAccUtils::MustPrune(accessible)) - navAccessible = accessible->FirstChild(); + if (!nsAccUtils::MustPrune(this)) + navAccessible = FirstChild(); } break; case NAVDIR_LASTCHILD: - if (accessible->IsProxy()) { - if (!accessible->Proxy()->MustPruneChildren()) { - navAccessible = WrapperFor(accessible->Proxy()->LastChild()); + if (IsProxy()) { + if (!Proxy()->MustPruneChildren()) { + navAccessible = WrapperFor(Proxy()->LastChild()); } } else { - if (!nsAccUtils::MustPrune(accessible)) - navAccessible = accessible->LastChild(); + if (!nsAccUtils::MustPrune(this)) + navAccessible = LastChild(); } break; case NAVDIR_NEXT: - navAccessible = accessible->IsProxy() - ? WrapperFor(accessible->Proxy()->NextSibling()) - : accessible->NextSibling(); + navAccessible = IsProxy() + ? WrapperFor(Proxy()->NextSibling()) + : NextSibling(); break; case NAVDIR_PREVIOUS: - navAccessible = accessible->IsProxy() - ? WrapperFor(accessible->Proxy()->PrevSibling()) - : accessible->PrevSibling(); + navAccessible = IsProxy() + ? WrapperFor(Proxy()->PrevSibling()) + : PrevSibling(); break; case NAVDIR_DOWN: case NAVDIR_LEFT: case NAVDIR_RIGHT: case NAVDIR_UP: return E_NOTIMPL; // MSAA relationship extensions to accNavigate @@ -1041,27 +1102,27 @@ AccessibleWrap::accHitTest( } STDMETHODIMP AccessibleWrap::accDoDefaultAction( /* [optional][in] */ VARIANT varChild) { A11Y_TRYBLOCK_BEGIN - if (IsDefunct()) - return CO_E_OBJNOTCONNECTED; + RefPtr<IAccessible> accessible; + HRESULT hr = ResolveChild(varChild, getter_AddRefs(accessible)); + if (FAILED(hr)) { + return hr; + } - Accessible* xpAccessible = GetXPAccessibleFor(varChild); - if (!xpAccessible) - return E_INVALIDARG; + if (accessible) { + return accessible->accDoDefaultAction(kVarChildIdSelf); + } - if (xpAccessible->IsDefunct()) - return CO_E_OBJNOTCONNECTED; - - return xpAccessible->DoAction(0) ? S_OK : E_INVALIDARG; + return DoAction(0) ? S_OK : E_INVALIDARG; A11Y_TRYBLOCK_END } STDMETHODIMP AccessibleWrap::put_accName( /* [optional][in] */ VARIANT varChild, /* [in] */ BSTR szName) @@ -1143,23 +1204,18 @@ AccessibleWrap::GetNativeInterface(void* { *aOutAccessible = static_cast<IAccessible*>(this); NS_ADDREF_THIS(); } void AccessibleWrap::SetID(uint32_t aID) { - MOZ_ASSERT(XRE_IsContentProcess()); + MOZ_ASSERT(XRE_IsParentProcess() && IsProxy()); mID = aID; - DocAccessibleWrap* doc = static_cast<DocAccessibleWrap*>(Document()); - DebugOnly<AccessibleWrap*> checkAcc = nullptr; - MOZ_ASSERT(!(checkAcc = doc->GetAccessibleByID(aID)) || - checkAcc->GetExistingID() == aID); - doc->AddID(aID, this); } void AccessibleWrap::FireWinEvent(Accessible* aTarget, uint32_t aEventType) { MOZ_ASSERT(XRE_IsParentProcess()); static_assert(sizeof(gWinEventMap)/sizeof(gWinEventMap[0]) == nsIAccessibleEvent::EVENT_LAST_ENTRY, "MSAA event map skewed"); @@ -1252,54 +1308,38 @@ AccessibleWrap::GetChildIDFor(Accessible // A child ID of the window is required, when we use NotifyWinEvent, // so that the 3rd party application can call back and get the IAccessible // the event occurred on. if (!aAccessible) { return 0; } - // Content should use mID which has been generated by the chrome process. - if (XRE_IsContentProcess() && !aAccessible->IsApplication()) { + // Chrome should use mID which has been generated by the content process. + if (aAccessible->IsProxy()) { const uint32_t id = static_cast<AccessibleWrap*>(aAccessible)->mID; MOZ_ASSERT(id != kNoID); return id; } -#ifdef _WIN64 - if (!aAccessible->Document() && !aAccessible->IsProxy()) + if (!aAccessible->Document()) return 0; uint32_t* id = & static_cast<AccessibleWrap*>(aAccessible)->mID; if (*id != kNoID) return *id; *id = sIDGen.GetID(); - if (aAccessible->IsProxy()) { - DocProxyAccessibleWrap* doc = - static_cast<AccessibleWrap*>(aAccessible)->DocProxyWrapper(); - doc->AddID(*id, static_cast<AccessibleWrap*>(aAccessible)); - } else { - DocAccessibleWrap* doc = - static_cast<DocAccessibleWrap*>(aAccessible->Document()); - doc->AddID(*id, static_cast<AccessibleWrap*>(aAccessible)); - } + MOZ_ASSERT(!aAccessible->IsProxy()); + DocAccessibleWrap* doc = + static_cast<DocAccessibleWrap*>(aAccessible->Document()); + doc->AddID(*id, static_cast<AccessibleWrap*>(aAccessible)); return *id; -#else - int32_t id = - reinterpret_cast<intptr_t>(aAccessible); - if (aAccessible->IsProxy()) { - DocProxyAccessibleWrap* doc = - static_cast<AccessibleWrap*>(aAccessible)->DocProxyWrapper(); - doc->AddID(id, static_cast<AccessibleWrap*>(aAccessible)); - } - - return id; -#endif } HWND AccessibleWrap::GetHWNDFor(Accessible* aAccessible) { if (!aAccessible) { return nullptr; } @@ -1391,169 +1431,191 @@ GetAccessibleInSubtree(DocAccessible* aD child = GetAccessibleInSubtree(aDoc->GetChildDocumentAt(i), aID); if (child) return child; } return nullptr; } -static AccessibleWrap* -GetProxiedAccessibleInSubtree(const DocAccessibleParent* aDoc, uint32_t aID) +static already_AddRefed<IDispatch> +GetProxiedAccessibleInSubtree(const DocAccessibleParent* aDoc, + const VARIANT& aVarChild) { auto wrapper = static_cast<DocProxyAccessibleWrap*>(WrapperFor(aDoc)); - AccessibleWrap* child = wrapper->GetAccessibleByID(aID); - if (child) { - return child; + RefPtr<IAccessible> comProxy; + int32_t wrapperChildId = AccessibleWrap::GetChildIDFor(wrapper); + if (wrapperChildId == aVarChild.lVal) { + wrapper->GetNativeInterface(getter_AddRefs(comProxy)); + return comProxy.forget(); } - size_t childDocs = aDoc->ChildDocCount(); - for (size_t i = 0; i < childDocs; i++) { - const DocAccessibleParent* childDoc = aDoc->ChildDocAt(i); - child = GetProxiedAccessibleInSubtree(childDoc, aID); - if (child) { - return child; - } + MOZ_ASSERT(aDoc->IsTopLevel()); + if (!aDoc->IsTopLevel()) { + return nullptr; } - return nullptr; + wrapper->GetNativeInterface(getter_AddRefs(comProxy)); + + RefPtr<IDispatch> disp; + if (FAILED(comProxy->get_accChild(aVarChild, getter_AddRefs(disp)))) { + return nullptr; + } + + return disp.forget(); } -Accessible* -AccessibleWrap::GetXPAccessibleFor(const VARIANT& aVarChild) +already_AddRefed<IAccessible> +AccessibleWrap::GetIAccessibleFor(const VARIANT& aVarChild, bool* aIsDefunct) { if (aVarChild.vt != VT_I4) return nullptr; - // if its us real easy - this seems to always be the case - if (aVarChild.lVal == CHILDID_SELF) - return this; + VARIANT varChild = aVarChild; + + MOZ_ASSERT(aIsDefunct); + *aIsDefunct = false; + + RefPtr<IAccessible> result; + + if (varChild.lVal == CHILDID_SELF) { + *aIsDefunct = IsDefunct(); + if (*aIsDefunct) { + return nullptr; + } + GetNativeInterface(getter_AddRefs(result)); + if (result) { + return result.forget(); + } + // If we're not a proxy, there's nothing more we can do to attempt to + // resolve the IAccessible, so we just fail. + if (!IsProxy()) { + return nullptr; + } + // Otherwise, since we're a proxy and we have a null native interface, this + // indicates that we need to obtain a COM proxy. To do this, we'll replace + // CHILDID_SELF with our real MSAA ID and continue the search from there. + varChild.lVal = GetExistingID(); + } if (IsProxy() ? Proxy()->MustPruneChildren() : nsAccUtils::MustPrune(this)) { return nullptr; } - if (aVarChild.lVal > 0) { + // If the MSAA ID is not a chrome id then we already know that we won't + // find it here and should look remotely instead. + if (XRE_IsParentProcess() && !sIDGen.IsChromeID(varChild.lVal)) { + return GetRemoteIAccessibleFor(varChild); + } + MOZ_ASSERT(XRE_IsParentProcess() || + sIDGen.IsIDForThisContentProcess(varChild.lVal)); + + if (varChild.lVal > 0) { // Gecko child indices are 0-based in contrast to indices used in MSAA. - if (IsProxy()) { - if (static_cast<uint32_t>(aVarChild.lVal) > Proxy()->ChildrenCount()) { - return nullptr; - } - - return WrapperFor(Proxy()->ChildAt(aVarChild.lVal - 1)); - } else { - return GetChildAt(aVarChild.lVal - 1); + MOZ_ASSERT(!IsProxy()); + Accessible* xpAcc = GetChildAt(varChild.lVal - 1); + if (!xpAcc) { + return nullptr; } + *aIsDefunct = xpAcc->IsDefunct(); + static_cast<AccessibleWrap*>(xpAcc)->GetNativeInterface(getter_AddRefs(result)); + return result.forget(); } // If lVal negative then it is treated as child ID and we should look for // accessible through whole accessible subtree including subdocuments. // Otherwise we treat lVal as index in parent. - // Convert child ID to unique ID. // First handle the case that both this accessible and the id'd one are in // this process. if (!IsProxy()) { - void* uniqueID = reinterpret_cast<void*>(intptr_t(-aVarChild.lVal)); - DocAccessible* document = Document(); Accessible* child = -#ifdef _WIN64 - GetAccessibleInSubtree(document, static_cast<uint32_t>(aVarChild.lVal)); -#else - XRE_IsContentProcess() ? - GetAccessibleInSubtree(document, static_cast<uint32_t>(aVarChild.lVal)) : - document->GetAccessibleByUniqueIDInSubtree(uniqueID); -#endif + GetAccessibleInSubtree(document, static_cast<uint32_t>(varChild.lVal)); // If it is a document then just return an accessible. - if (child && IsDoc()) - return child; + if (child && IsDoc()) { + *aIsDefunct = child->IsDefunct(); + static_cast<AccessibleWrap*>(child)->GetNativeInterface(getter_AddRefs(result)); + return result.forget(); + } // Otherwise check whether the accessible is a child (this path works for // ARIA documents and popups). Accessible* parent = child; while (parent && parent != document) { - if (parent == this) - return child; + if (parent == this) { + *aIsDefunct = child->IsDefunct(); + static_cast<AccessibleWrap*>(child)->GetNativeInterface(getter_AddRefs(result)); + return result.forget(); + } parent = parent->Parent(); } } // Now see about the case that both this accessible and the target one are // proxied. - uint32_t id = aVarChild.lVal; if (IsProxy()) { DocAccessibleParent* proxyDoc = Proxy()->Document(); - AccessibleWrap* wrapper = GetProxiedAccessibleInSubtree(proxyDoc, id); - if (!wrapper) + RefPtr<IDispatch> disp = GetProxiedAccessibleInSubtree(proxyDoc, varChild); + if (!disp) { return nullptr; - - MOZ_ASSERT(wrapper->IsProxy()); - - if (proxyDoc == this->Proxy()) { - return wrapper; } - ProxyAccessible* parent = wrapper->Proxy(); - while (parent && parent != proxyDoc) { - if (parent == this->Proxy()) { - return wrapper; - } - - parent = parent->Parent(); - } - - return nullptr; + MOZ_ASSERT(mscom::IsProxy(disp)); + DebugOnly<HRESULT> hr = disp->QueryInterface(IID_IAccessible, + getter_AddRefs(result)); + MOZ_ASSERT(SUCCEEDED(hr)); + return result.forget(); } - // Finally we need to handle the case that this accessible is in the main - // process, but the target is proxied. This is the case when the target - // accessible is in a child document of this one. + return nullptr; +} + +already_AddRefed<IAccessible> +AccessibleWrap::GetRemoteIAccessibleFor(const VARIANT& aVarChild) +{ DocAccessibleParent* proxyDoc = nullptr; DocAccessible* doc = Document(); const nsTArray<DocAccessibleParent*>* remoteDocs = DocManager::TopLevelRemoteDocs(); if (!remoteDocs) { return nullptr; } + RefPtr<IAccessible> result; + size_t docCount = remoteDocs->Length(); for (size_t i = 0; i < docCount; i++) { - Accessible* outerDoc = remoteDocs->ElementAt(i)->OuterDocOfRemoteBrowser(); + DocAccessibleParent* remoteDoc = remoteDocs->ElementAt(i); + + uint32_t remoteDocMsaaId = WrapperFor(remoteDoc)->GetExistingID(); + if (!sIDGen.IsSameContentProcessFor(aVarChild.lVal, remoteDocMsaaId)) { + continue; + } + + Accessible* outerDoc = remoteDoc->OuterDocOfRemoteBrowser(); if (!outerDoc) { continue; } if (outerDoc->Document() != doc) { continue; } - if (doc == this) { - AccessibleWrap* proxyWrapper = - GetProxiedAccessibleInSubtree(remoteDocs->ElementAt(i), id); - if (proxyWrapper) { - return proxyWrapper; - } - + RefPtr<IDispatch> disp = + GetProxiedAccessibleInSubtree(remoteDoc, aVarChild); + if (!disp) { continue; } - Accessible* parent = outerDoc; - while (parent && parent != doc) { - if (parent == this) { - AccessibleWrap* proxyWrapper = - GetProxiedAccessibleInSubtree(remoteDocs->ElementAt(i), id); - if (proxyWrapper) { - return proxyWrapper; - } - } - - parent = parent->Parent(); - } + DebugOnly<HRESULT> hr = disp->QueryInterface(IID_IAccessible, + getter_AddRefs(result)); + MOZ_ASSERT(SUCCEEDED(hr)); + return result.forget(); } return nullptr; } void AccessibleWrap::UpdateSystemCaretFor(Accessible* aAccessible) { @@ -1598,8 +1660,22 @@ AccessibleWrap::GetTI(LCID lcid) hr = typeLib->GetTypeInfoOfGuid(IID_IAccessible, &gTypeInfo); typeLib->Release(); if (FAILED(hr)) return nullptr; return gTypeInfo; } + +/* static */ +uint32_t +AccessibleWrap::GetContentProcessIdFor(dom::ContentParentId aIPCContentId) +{ + return sIDGen.GetContentProcessIDFor(aIPCContentId); +} + +/* static */ +void +AccessibleWrap::ReleaseContentProcessIdFor(dom::ContentParentId aIPCContentId) +{ + sIDGen.ReleaseContentProcessIDFor(aIPCContentId); +}
--- a/accessible/windows/msaa/AccessibleWrap.h +++ b/accessible/windows/msaa/AccessibleWrap.h @@ -9,18 +9,19 @@ #include "nsCOMPtr.h" #include "Accessible.h" #include "Accessible2.h" #include "ia2Accessible.h" #include "ia2AccessibleComponent.h" #include "ia2AccessibleHyperlink.h" #include "ia2AccessibleValue.h" +#include "mozilla/a11y/MsaaIdGenerator.h" #include "mozilla/a11y/ProxyAccessible.h" -#include "mozilla/a11y/IDSet.h" +#include "mozilla/Attributes.h" #ifdef __GNUC__ // Inheriting from both XPCOM and MSCOM interfaces causes a lot of warnings // about virtual functions being hidden by each other. This is done by // design, so silence the warning. #pragma GCC diagnostic ignored "-Woverloaded-virtual" #endif @@ -168,47 +169,57 @@ public: // construction, destruction * We will use an invisible system caret. * Gecko is still responsible for drawing its own caret */ void UpdateSystemCaretFor(Accessible* aAccessible); /** * Find an accessible by the given child ID in cached documents. */ - Accessible* GetXPAccessibleFor(const VARIANT& aVarChild); + MOZ_MUST_USE already_AddRefed<IAccessible> + GetIAccessibleFor(const VARIANT& aVarChild, bool* aIsDefunct); virtual void GetNativeInterface(void **aOutAccessible) override; static IDispatch* NativeAccessible(Accessible* aAccessible); uint32_t GetExistingID() const { return mID; } static const uint32_t kNoID = 0; - // This is only valid to call in content void SetID(uint32_t aID); + static uint32_t GetContentProcessIdFor(dom::ContentParentId aIPCContentId); + static void ReleaseContentProcessIdFor(dom::ContentParentId aIPCContentId); + protected: virtual ~AccessibleWrap(); uint32_t mID; + HRESULT + ResolveChild(const VARIANT& aVarChild, IAccessible** aOutInterface); + + /** + * Find a remote accessible by the given child ID. + */ + MOZ_MUST_USE already_AddRefed<IAccessible> + GetRemoteIAccessibleFor(const VARIANT& aVarChild); + /** * Return the wrapper for the document's proxy. */ DocProxyAccessibleWrap* DocProxyWrapper() const; /** * Creates ITypeInfo for LIBID_Accessibility if it's needed and returns it. */ static ITypeInfo* GetTI(LCID lcid); static ITypeInfo* gTypeInfo; -#ifdef _WIN64 - static IDSet sIDGen; -#endif + static MsaaIdGenerator sIDGen; enum navRelations { NAVRELATION_CONTROLLED_BY = 0x1000, NAVRELATION_CONTROLLER_FOR = 0x1001, NAVRELATION_LABEL_FOR = 0x1002, NAVRELATION_LABELLED_BY = 0x1003, NAVRELATION_MEMBER_OF = 0x1004, NAVRELATION_NODE_CHILD_OF = 0x1005,
--- a/accessible/windows/msaa/IDSet.h +++ b/accessible/windows/msaa/IDSet.h @@ -14,72 +14,81 @@ #include "mozilla/Attributes.h" #include "mozilla/MathAlgorithms.h" #include "mozilla/SplayTree.h" namespace mozilla { namespace a11y { /** - * On windows an accessible's id must be a negative 32 bit integer. It is + * On windows an accessible's id must be a negative 32 bit integer. It is * important to support recycling arbitrary IDs because accessibles can be * created and destroyed at any time in the life of a page. IDSet provides 2 - * operations: generate an ID in the range [ - 2^31, 0 ), and release an ID so + * operations: generate an ID in the range (0, mMaxId], and release an ID so * it can be allocated again. Allocated ID are tracked by a sparse bitmap * implemented with a splay tree. Nodes in the tree are keyed by the upper N - * bits of the bitwise negation of the ID, and the node contains a bitmap - * tracking the allocation of 2^(32 - N) IDs. + * bits of the ID, and the node contains a bitmap tracking the allocation of + * 2^(ceil(log2(mMaxId)) - N) IDs. + * + * Note that negation is handled by MsaaIdGenerator as it performs additional + * decoration on the ID generated by IDSet. + * @see mozilla::a11y::MsaaIdGenerator */ class IDSet { public: - constexpr IDSet() : mBitSet(), mIdx(0) {} + constexpr explicit IDSet(const uint32_t aMaxIdBits) + : mBitSet() + , mIdx(0) + , mMaxId((1UL << aMaxIdBits) - 1UL) + , mMaxIdx(mMaxId / bitsPerElt) + { + } /** * Return a new unique id. */ uint32_t GetID() { uint32_t idx = mIdx; while (true) { BitSetElt* elt = mBitSet.findOrInsert(BitSetElt(idx)); if (elt->mBitvec[0] != UINT64_MAX) { uint32_t i = CountTrailingZeroes64(~elt->mBitvec[0]); elt->mBitvec[0] |= (1ull << i); mIdx = idx; - return ~(elt->mIdx * bitsPerElt + i); + return (elt->mIdx * bitsPerElt + i); } if (elt->mBitvec[1] != UINT64_MAX) { uint32_t i = CountTrailingZeroes64(~elt->mBitvec[1]); elt->mBitvec[1] |= (1ull << i); mIdx = idx; - return ~(elt->mIdx * bitsPerElt + bitsPerWord + i); + return (elt->mIdx * bitsPerElt + bitsPerWord + i); } idx++; - if (idx > sMaxIdx) { + if (idx > mMaxIdx) { idx = 0; } if (idx == mIdx) { MOZ_CRASH("used up all the available ids"); } } } /** * Free a no longer required id so it may be allocated again. */ void ReleaseID(uint32_t aID) { - aID = ~aID; - MOZ_ASSERT(aID < static_cast<uint32_t>(INT32_MAX)); + MOZ_ASSERT(aID < mMaxId); uint32_t idx = aID / bitsPerElt; mIdx = idx; BitSetElt* elt = mBitSet.find(BitSetElt(idx)); MOZ_ASSERT(elt); uint32_t vecIdx = (aID % bitsPerElt) / bitsPerWord; elt->mBitvec[vecIdx] &= ~(1ull << (aID % bitsPerWord)); @@ -87,17 +96,16 @@ public: delete mBitSet.remove(*elt); } } private: static const unsigned int wordsPerElt = 2; static const unsigned int bitsPerWord = 64; static const unsigned int bitsPerElt = wordsPerElt * bitsPerWord; - static const uint32_t sMaxIdx = INT32_MAX / bitsPerElt; struct BitSetElt : mozilla::SplayTreeNode<BitSetElt> { explicit BitSetElt(uint32_t aIdx) : mIdx(aIdx) { mBitvec[0] = mBitvec[1] = 0; } uint64_t mBitvec[wordsPerElt]; @@ -113,14 +121,16 @@ private: return -1; } return 1; } }; SplayTree<BitSetElt, BitSetElt> mBitSet; uint32_t mIdx; + const uint32_t mMaxId; + const uint32_t mMaxIdx; }; } } #endif
new file mode 100644 --- /dev/null +++ b/accessible/windows/msaa/MsaaIdGenerator.cpp @@ -0,0 +1,246 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=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 "MsaaIdGenerator.h" + +#include "mozilla/a11y/AccessibleWrap.h" +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/dom/ContentChild.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/Unused.h" +#include "nsDataHashtable.h" +#include "nsIXULRuntime.h" + +// These constants may be adjusted to modify the proportion of the Child ID +// allocated to the content ID vs proportion allocated to the unique ID. They +// must always sum to 31, ie. the width of a 32-bit integer less the sign bit. + +// NB: kNumContentProcessIDBits must be large enough to successfully hold the +// maximum permitted number of e10s content processes. If the e10s maximum +// number of content processes changes, then kNumContentProcessIDBits must also +// be updated if necessary to accommodate that new value! +static const uint32_t kNumContentProcessIDBits = 7UL; +static const uint32_t kNumUniqueIDBits = (31UL - kNumContentProcessIDBits); + +static_assert(kNumContentProcessIDBits + kNumUniqueIDBits == 31, + "Allocation of Content ID bits and Unique ID bits must sum to 31"); + +namespace mozilla { +namespace a11y { +namespace detail { + +typedef nsDataHashtable<nsUint64HashKey, uint32_t> ContentParentIdMap; + +#pragma pack(push, 1) +union MsaaID +{ + int32_t mInt32; + uint32_t mUInt32; + struct + { + uint32_t mUniqueID:kNumUniqueIDBits; + uint32_t mContentProcessID:kNumContentProcessIDBits; + uint32_t mSignBit:1; + } + mCracked; +}; +#pragma pack(pop) + +static uint32_t +BuildMsaaID(const uint32_t aID, const uint32_t aContentProcessID) +{ + MsaaID id; + id.mCracked.mSignBit = 0; + id.mCracked.mUniqueID = aID; + id.mCracked.mContentProcessID = aContentProcessID; + return ~id.mUInt32; +} + +class MsaaIDCracker +{ +public: + explicit MsaaIDCracker(const uint32_t aMsaaID) + { + mID.mUInt32 = ~aMsaaID; + } + + uint32_t GetContentProcessId() + { + return mID.mCracked.mContentProcessID; + } + + uint32_t GetUniqueId() + { + return mID.mCracked.mUniqueID; + } + +private: + MsaaID mID; +}; + +} // namespace detail + +constexpr MsaaIdGenerator::MsaaIdGenerator() + : mIDSet(kNumUniqueIDBits) + , mContentProcessID(0) +{} + +uint32_t +MsaaIdGenerator::GetID() +{ + static const uint32_t kContentProcessId = ResolveContentProcessID(); + uint32_t id = mIDSet.GetID(); + MOZ_ASSERT(id <= ((1UL << kNumUniqueIDBits) - 1UL)); + return detail::BuildMsaaID(id, kContentProcessId); +} + +void +MsaaIdGenerator::ReleaseID(AccessibleWrap* aAccWrap) +{ + MOZ_ASSERT(aAccWrap); + uint32_t id = aAccWrap->GetExistingID(); + MOZ_ASSERT(id != AccessibleWrap::kNoID); + detail::MsaaIDCracker cracked(id); + if (cracked.GetContentProcessId() != mContentProcessID) { + // This may happen if chrome holds a proxy whose ID was originally generated + // by a content process. Since ReleaseID only has meaning in the process + // that originally generated that ID, we ignore ReleaseID calls for any ID + // that did not come from the current process. + MOZ_ASSERT(aAccWrap->IsProxy()); + return; + } + mIDSet.ReleaseID(cracked.GetUniqueId()); +} + +bool +MsaaIdGenerator::IsChromeID(uint32_t aID) +{ + detail::MsaaIDCracker cracked(aID); + return cracked.GetContentProcessId() == 0; +} + +bool +MsaaIdGenerator::IsIDForThisContentProcess(uint32_t aID) +{ + MOZ_ASSERT(XRE_IsContentProcess()); + static const uint32_t kContentProcessId = ResolveContentProcessID(); + detail::MsaaIDCracker cracked(aID); + return cracked.GetContentProcessId() == kContentProcessId; +} + +bool +MsaaIdGenerator::IsIDForContentProcess(uint32_t aID, + dom::ContentParentId aIPCContentProcessId) +{ + MOZ_ASSERT(XRE_IsParentProcess()); + detail::MsaaIDCracker cracked(aID); + return cracked.GetContentProcessId() == + GetContentProcessIDFor(aIPCContentProcessId); +} + +bool +MsaaIdGenerator::IsSameContentProcessFor(uint32_t aFirstID, uint32_t aSecondID) +{ + detail::MsaaIDCracker firstCracked(aFirstID); + detail::MsaaIDCracker secondCracked(aSecondID); + return firstCracked.GetContentProcessId() == + secondCracked.GetContentProcessId(); +} + +uint32_t +MsaaIdGenerator::ResolveContentProcessID() +{ + if (XRE_IsParentProcess()) { + return 0; + } + + dom::ContentChild* contentChild = dom::ContentChild::GetSingleton(); + Unused << contentChild->SendGetA11yContentId(&mContentProcessID); + + MOZ_ASSERT(mContentProcessID); + return mContentProcessID; +} + +/** + * Each dom::ContentParent has a 64-bit ID. This ID is monotonically increasing + * with each new content process, so those IDs are effectively single-use. OTOH, + * MSAA requires 32-bit IDs. Since we only allocate kNumContentProcessIDBits for + * the content process ID component, the MSAA content process ID value must be + * reusable. sContentParentIdMap holds the current associations between + * dom::ContentParent IDs and the MSAA content parent IDs that have been + * allocated to them. + */ +static StaticAutoPtr<detail::ContentParentIdMap> sContentParentIdMap; + +static const uint32_t kBitsPerByte = 8UL; +// Set sContentProcessIdBitmap[0] to 1 to reserve the Chrome process's id +static uint64_t sContentProcessIdBitmap[(1UL << kNumContentProcessIDBits) / + (sizeof(uint64_t) * kBitsPerByte)] = {1ULL}; +static const uint32_t kBitsPerElement = sizeof(sContentProcessIdBitmap[0]) * + kBitsPerByte; + +uint32_t +MsaaIdGenerator::GetContentProcessIDFor(dom::ContentParentId aIPCContentProcessID) +{ + MOZ_ASSERT(XRE_IsParentProcess() && NS_IsMainThread()); + if (!sContentParentIdMap) { + sContentParentIdMap = new detail::ContentParentIdMap(); + ClearOnShutdown(&sContentParentIdMap); + } + + uint32_t value = 0; + if (sContentParentIdMap->Get(aIPCContentProcessID, &value)) { + return value; + } + + uint32_t index = 0; + for (; index < ArrayLength(sContentProcessIdBitmap); ++index) { + if (sContentProcessIdBitmap[index] == UINT64_MAX) { + continue; + } + uint32_t bitIndex = CountTrailingZeroes64(~sContentProcessIdBitmap[index]); + MOZ_ASSERT(!(sContentProcessIdBitmap[index] & (1ULL << bitIndex))); + MOZ_ASSERT(bitIndex != 0 || index != 0); + sContentProcessIdBitmap[index] |= (1ULL << bitIndex); + value = index * kBitsPerElement + bitIndex; + break; + } + + // If we run out of content process IDs, we're in trouble + MOZ_RELEASE_ASSERT(index < ArrayLength(sContentProcessIdBitmap)); + + sContentParentIdMap->Put(aIPCContentProcessID, value); + return value; +} + +void +MsaaIdGenerator::ReleaseContentProcessIDFor(dom::ContentParentId aIPCContentProcessID) +{ + MOZ_ASSERT(XRE_IsParentProcess() && NS_IsMainThread()); + if (!sContentParentIdMap) { + // Since Content IDs are generated lazily, ContentParent might attempt + // to release an ID that was never allocated to begin with. + return; + } + + Maybe<uint32_t> mapping = sContentParentIdMap->GetAndRemove(aIPCContentProcessID); + if (!mapping) { + // Since Content IDs are generated lazily, ContentParent might attempt + // to release an ID that was never allocated to begin with. + return; + } + + uint32_t index = mapping.ref() / kBitsPerElement; + MOZ_ASSERT(index < ArrayLength(sContentProcessIdBitmap)); + + uint64_t mask = 1ULL << (mapping.ref() % kBitsPerElement); + MOZ_ASSERT(sContentProcessIdBitmap[index] & mask); + + sContentProcessIdBitmap[index] &= ~mask; +} + +} // namespace a11y +} // namespace mozilla
new file mode 100644 --- /dev/null +++ b/accessible/windows/msaa/MsaaIdGenerator.h @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=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_MsaaIdGenerator_h +#define mozilla_a11y_MsaaIdGenerator_h + +#include "mozilla/a11y/IDSet.h" + +#include "mozilla/dom/ipc/IdType.h" + +namespace mozilla { +namespace a11y { + +class AccessibleWrap; + +/** + * This class is responsible for generating child IDs used by our MSAA + * implementation. Since e10s requires us to differentiate IDs based on the + * originating process of the accessible, a portion of the ID's bits are + * allocated to storing that information. The remaining bits represent the + * unique ID of the accessible, within that content process. + * + * The constants kNumContentProcessIDBits and kNumUniqueIDBits in the + * implementation are responsible for determining the proportion of bits that + * are allocated for each purpose. + */ +class MsaaIdGenerator +{ +public: + constexpr MsaaIdGenerator(); + + uint32_t GetID(); + void ReleaseID(AccessibleWrap* aAccWrap); + bool IsChromeID(uint32_t aID); + bool IsIDForThisContentProcess(uint32_t aID); + bool IsIDForContentProcess(uint32_t aID, + dom::ContentParentId aIPCContentProcessId); + bool IsSameContentProcessFor(uint32_t aFirstID, uint32_t aSecondID); + + uint32_t GetContentProcessIDFor(dom::ContentParentId aIPCContentProcessID); + void ReleaseContentProcessIDFor(dom::ContentParentId aIPCContentProcessID); + +private: + uint32_t ResolveContentProcessID(); + +private: + IDSet mIDSet; + uint32_t mContentProcessID; +}; + +} // namespace a11y +} // namespace mozilla + +#endif // mozilla_a11y_MsaaIdGenerator_h
--- a/accessible/windows/msaa/Platform.cpp +++ b/accessible/windows/msaa/Platform.cpp @@ -80,30 +80,16 @@ void a11y::ProxyDestroyed(ProxyAccessible* aProxy) { AccessibleWrap* wrapper = reinterpret_cast<AccessibleWrap*>(aProxy->GetWrapper()); MOZ_ASSERT(wrapper); if (!wrapper) return; - auto doc = - static_cast<DocProxyAccessibleWrap*>(WrapperFor(aProxy->Document())); - MOZ_ASSERT(doc); - if (doc) { -#ifdef _WIN64 - uint32_t id = wrapper->GetExistingID(); - if (id != AccessibleWrap::kNoID) { - doc->RemoveID(id); - } -#else - doc->RemoveID(-reinterpret_cast<int32_t>(wrapper)); -#endif - } - wrapper->Shutdown(); aProxy->SetWrapper(0); wrapper->Release(); } void a11y::ProxyEvent(ProxyAccessible* aTarget, uint32_t aEventType) {
--- a/accessible/windows/msaa/moz.build +++ b/accessible/windows/msaa/moz.build @@ -8,31 +8,33 @@ EXPORTS += [ 'IUnknownImpl.h', ] EXPORTS.mozilla.a11y += [ 'AccessibleWrap.h', 'Compatibility.h', 'HyperTextAccessibleWrap.h', 'IDSet.h', + 'MsaaIdGenerator.h', ] UNIFIED_SOURCES += [ 'AccessibleWrap.cpp', 'ApplicationAccessibleWrap.cpp', 'ARIAGridAccessibleWrap.cpp', 'ChildIDThunk.cpp', 'Compatibility.cpp', 'DocAccessibleWrap.cpp', 'EnumVariant.cpp', 'HTMLTableAccessibleWrap.cpp', 'HTMLWin32ObjectAccessible.cpp', 'HyperTextAccessibleWrap.cpp', 'ImageAccessibleWrap.cpp', 'IUnknownImpl.cpp', + 'MsaaIdGenerator.cpp', 'nsWinUtils.cpp', 'Platform.cpp', 'RootAccessibleWrap.cpp', 'TextLeafAccessibleWrap.cpp', ] # This file cannot be built in unified mode because it includes ISimpleDOMNode_i.c. SOURCES += [
--- a/accessible/windows/sdn/moz.build +++ b/accessible/windows/sdn/moz.build @@ -14,9 +14,11 @@ LOCAL_INCLUDES += [ '/accessible/base', '/accessible/generic', '/accessible/html', '/accessible/windows/msaa', '/accessible/xpcom', '/accessible/xul', ] +include('/ipc/chromium/chromium-config.mozbuild') + FINAL_LIBRARY = 'xul'
--- a/accessible/windows/uia/moz.build +++ b/accessible/windows/uia/moz.build @@ -12,9 +12,11 @@ LOCAL_INCLUDES += [ '/accessible/base', '/accessible/generic', '/accessible/html', '/accessible/windows/msaa', '/accessible/xpcom', '/accessible/xul', ] +include('/ipc/chromium/chromium-config.mozbuild') + FINAL_LIBRARY = 'xul'
--- a/accessible/xul/moz.build +++ b/accessible/xul/moz.build @@ -42,12 +42,14 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'co LOCAL_INCLUDES += [ '/accessible/mac', ] else: LOCAL_INCLUDES += [ '/accessible/other', ] +include('/ipc/chromium/chromium-config.mozbuild') + FINAL_LIBRARY = 'xul' if CONFIG['GNU_CXX']: CXXFLAGS += ['-Wno-error=shadow']
--- a/browser/base/content/browser-fullScreenAndPointerLock.js +++ b/browser/base/content/browser-fullScreenAndPointerLock.js @@ -380,17 +380,20 @@ var FullScreen = { receiveMessage: function(aMessage) { let browser = aMessage.target; switch (aMessage.name) { case "DOMFullscreen:Request": { this._windowUtils.remoteFrameFullscreenChanged(browser); break; } case "DOMFullscreen:NewOrigin": { - PointerlockFsWarning.showFullScreen(aMessage.data.originNoSuffix); + // Don't show the warning if we've already exit fullscreen. + if (document.fullscreen) { + PointerlockFsWarning.showFullScreen(aMessage.data.originNoSuffix); + } break; } case "DOMFullscreen:Exit": { this._windowUtils.remoteFrameFullscreenReverted(); break; } case "DOMFullscreen:Painted": { Services.obs.notifyObservers(window, "fullscreen-painted", ""); @@ -405,38 +408,42 @@ var FullScreen = { if (!document.fullscreenElement) { return; } // If we have a current pointerlock warning shown then hide it // before transition. PointerlockFsWarning.close(); + // If it is a remote browser, send a message to ask the content + // to enter fullscreen state. We don't need to do so if it is an + // in-process browser, since all related document should have + // entered fullscreen state at this point. + // This should be done before the active tab check below to ensure + // that the content document handles the pending request. Doing so + // before the check is fine since we also check the activeness of + // the requesting document in content-side handling code. + if (this._isRemoteBrowser(aBrowser)) { + aBrowser.messageManager.sendAsyncMessage("DOMFullscreen:Entered"); + } + // If we've received a fullscreen notification, we have to ensure that the // element that's requesting fullscreen belongs to the browser that's currently // active. If not, we exit fullscreen since the "full-screen document" isn't // actually visible now. if (!aBrowser || gBrowser.selectedBrowser != aBrowser || // The top-level window has lost focus since the request to enter // full-screen was made. Cancel full-screen. Services.focus.activeWindow != window) { // This function is called synchronously in fullscreen change, so // we have to avoid calling exitFullscreen synchronously here. setTimeout(() => document.exitFullscreen(), 0); return; } - // If it is a remote browser, send a message to ask the content - // to enter fullscreen state. We don't need to do so if it is an - // in-process browser, since all related document should have - // entered fullscreen state at this point. - if (this._isRemoteBrowser(aBrowser)) { - aBrowser.messageManager.sendAsyncMessage("DOMFullscreen:Entered"); - } - document.documentElement.setAttribute("inDOMFullscreen", true); if (gFindBarInitialized) { gFindBar.close(true); } // Exit DOM full-screen mode upon open, close, or change tab. gBrowser.tabContainer.addEventListener("TabOpen", this.exitDomFullScreen);
--- a/browser/modules/PluginContent.jsm +++ b/browser/modules/PluginContent.jsm @@ -49,16 +49,18 @@ PluginContent.prototype = { global.addEventListener("HiddenPlugin", this, true); global.addMessageListener("BrowserPlugins:ActivatePlugins", this); global.addMessageListener("BrowserPlugins:NotificationShown", this); global.addMessageListener("BrowserPlugins:ContextMenuCommand", this); global.addMessageListener("BrowserPlugins:NPAPIPluginProcessCrashed", this); global.addMessageListener("BrowserPlugins:CrashReportSubmitted", this); global.addMessageListener("BrowserPlugins:Test:ClearCrashData", this); + + Services.obs.addObserver(this, "decoder-doctor-notification", false); }, uninit: function() { let global = this.global; global.removeEventListener("PluginBindingAttached", this, true); global.removeEventListener("PluginCrashed", this, true); global.removeEventListener("PluginOutdated", this, true); @@ -70,16 +72,19 @@ PluginContent.prototype = { global.removeEventListener("HiddenPlugin", this, true); global.removeMessageListener("BrowserPlugins:ActivatePlugins", this); global.removeMessageListener("BrowserPlugins:NotificationShown", this); global.removeMessageListener("BrowserPlugins:ContextMenuCommand", this); global.removeMessageListener("BrowserPlugins:NPAPIPluginProcessCrashed", this); global.removeMessageListener("BrowserPlugins:CrashReportSubmitted", this); global.removeMessageListener("BrowserPlugins:Test:ClearCrashData", this); + + Services.obs.removeObserver(this, "decoder-doctor-notification"); + delete this.global; delete this.content; }, receiveMessage: function (msg) { switch (msg.name) { case "BrowserPlugins:ActivatePlugins": this.activatePlugins(msg.data.pluginInfo, msg.data.newState); @@ -113,16 +118,36 @@ PluginContent.prototype = { case "BrowserPlugins:Test:ClearCrashData": // This message should ONLY ever be sent by automated tests. if (Services.prefs.getBoolPref("plugins.testmode")) { this.pluginCrashData.clear(); } } }, + observe: function observe(aSubject, aTopic, aData) { + switch (aTopic) { + case "decoder-doctor-notification": + let data = JSON.parse(aData); + if (this.haveShownNotification && + aSubject.top.document == this.content.document && + data.formats.toLowerCase().includes("application/x-mpegurl", 0)) { + let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost); + let principal = this.content.document.nodePrincipal; + let location = this.content.document.location.href; + this.global.content.pluginRequiresReload = true; + this.global.sendAsyncMessage("PluginContent:ShowClickToPlayNotification", + { plugins: [... this.pluginData.values()], + showNow: true, + location: location, + }, null, principal); + } + } + }, + onPageShow: function (event) { // Ignore events that aren't from the main document. if (!this.content || event.target != this.content.document) { return; } // The PluginClickToPlay events are not fired when navigating using the // BF cache. |persisted| is true when the page is loaded from the @@ -135,16 +160,17 @@ PluginContent.prototype = { onPageHide: function (event) { // Ignore events that aren't from the main document. if (!this.content || event.target != this.content.document) { return; } this._finishRecordingFlashPluginTelemetry(); this.clearPluginCaches(); + this.haveShownNotification = false; }, getPluginUI: function (plugin, anonid) { return plugin.ownerDocument. getAnonymousElementByAttribute(plugin, "anonid", anonid); }, _getPluginInfo: function (pluginElement) { @@ -720,19 +746,21 @@ PluginContent.prototype = { if (overlay) { overlay.removeEventListener("click", this, true); } plugin.playPlugin(); } } } - // If there are no instances of the plugin on the page any more, what the + // If there are no instances of the plugin on the page any more or if we've + // noted that the content needs to be reloaded due to replacing HLS, what the // user probably needs is for us to allow and then refresh. - if (newState != "block" && !pluginFound) { + if (newState != "block" && + (!pluginFound || contentWindow.pluginRequiresReload)) { this.reloadPage(); } this.updateNotificationUI(); }, _showClickToPlayNotification: function (plugin, showNow) { let plugins = []; @@ -784,16 +812,18 @@ PluginContent.prototype = { else { pluginInfo.pluginPermissionPrePath = principal.originNoSuffix; pluginInfo.pluginPermissionType = undefined; } this.pluginData.set(pluginInfo.permissionString, pluginInfo); } + this.haveShownNotification = true; + this.global.sendAsyncMessage("PluginContent:ShowClickToPlayNotification", { plugins: [... this.pluginData.values()], showNow: showNow, location: location, }, null, principal); }, /**
--- a/build/autoconf/sanitize.m4 +++ b/build/autoconf/sanitize.m4 @@ -20,51 +20,57 @@ if test -n "$MOZ_ASAN"; then MOZ_PATH_PROG(MOZ_CLANG_RT_ASAN_LIB_PATH, $MOZ_CLANG_RT_ASAN_LIB) if test -z "$MOZ_CLANG_RT_ASAN_LIB_PATH"; then AC_MSG_ERROR([Couldn't find $MOZ_CLANG_RT_ASAN_LIB. It should be available in the same location as clang-cl.]) fi AC_SUBST(MOZ_CLANG_RT_ASAN_LIB_PATH) fi CFLAGS="-fsanitize=address $CFLAGS" CXXFLAGS="-fsanitize=address $CXXFLAGS" - LDFLAGS="-fsanitize=address $LDFLAGS" + if test -z "$CLANG_CL"; then + LDFLAGS="-fsanitize=address $LDFLAGS" + fi AC_DEFINE(MOZ_ASAN) MOZ_PATH_PROG(LLVM_SYMBOLIZER, llvm-symbolizer) fi AC_SUBST(MOZ_ASAN) dnl ======================================================== dnl = Use Memory Sanitizer dnl ======================================================== MOZ_ARG_ENABLE_BOOL(memory-sanitizer, [ --enable-memory-sanitizer Enable Memory Sanitizer (default=no)], MOZ_MSAN=1, MOZ_MSAN= ) if test -n "$MOZ_MSAN"; then MOZ_LLVM_HACKS=1 CFLAGS="-fsanitize=memory -fsanitize-memory-track-origins $CFLAGS" CXXFLAGS="-fsanitize=memory -fsanitize-memory-track-origins $CXXFLAGS" - LDFLAGS="-fsanitize=memory -fsanitize-memory-track-origins $LDFLAGS" + if test -z "$CLANG_CL"; then + LDFLAGS="-fsanitize=memory -fsanitize-memory-track-origins $LDFLAGS" + fi AC_DEFINE(MOZ_MSAN) MOZ_PATH_PROG(LLVM_SYMBOLIZER, llvm-symbolizer) fi AC_SUBST(MOZ_MSAN) dnl ======================================================== dnl = Use Thread Sanitizer dnl ======================================================== MOZ_ARG_ENABLE_BOOL(thread-sanitizer, [ --enable-thread-sanitizer Enable Thread Sanitizer (default=no)], MOZ_TSAN=1, MOZ_TSAN= ) if test -n "$MOZ_TSAN"; then MOZ_LLVM_HACKS=1 CFLAGS="-fsanitize=thread $CFLAGS" CXXFLAGS="-fsanitize=thread $CXXFLAGS" - LDFLAGS="-fsanitize=thread $LDFLAGS" + if test -z "$CLANG_CL"; then + LDFLAGS="-fsanitize=thread $LDFLAGS" + fi AC_DEFINE(MOZ_TSAN) MOZ_PATH_PROG(LLVM_SYMBOLIZER, llvm-symbolizer) fi AC_SUBST(MOZ_TSAN) # The LLVM symbolizer is used by all sanitizers AC_SUBST(LLVM_SYMBOLIZER)
--- a/caps/nsScriptSecurityManager.cpp +++ b/caps/nsScriptSecurityManager.cpp @@ -329,28 +329,32 @@ nsScriptSecurityManager::GetChannelResul } nsresult nsScriptSecurityManager::GetChannelResultPrincipal(nsIChannel* aChannel, nsIPrincipal** aPrincipal, bool aIgnoreSandboxing) { NS_PRECONDITION(aChannel, "Must have channel!"); + // Check whether we have an nsILoadInfo that says what we should do. + nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo(); + if (loadInfo && loadInfo->GetForceInheritPrincipalOverruleOwner()) { + NS_ADDREF(*aPrincipal = loadInfo->PrincipalToInherit()); + return NS_OK; + } + nsCOMPtr<nsISupports> owner; aChannel->GetOwner(getter_AddRefs(owner)); if (owner) { CallQueryInterface(owner, aPrincipal); if (*aPrincipal) { return NS_OK; } } - // Check whether we have an nsILoadInfo that says what we should do. - nsCOMPtr<nsILoadInfo> loadInfo; - aChannel->GetLoadInfo(getter_AddRefs(loadInfo)); if (loadInfo) { if (!aIgnoreSandboxing && loadInfo->GetLoadingSandboxed()) { RefPtr<nsNullPrincipal> prin; if (loadInfo->LoadingPrincipal()) { prin = nsNullPrincipal::CreateWithInheritedAttributes(loadInfo->LoadingPrincipal()); } else { NeckoOriginAttributes nAttrs;
--- a/docshell/test/mochitest.ini +++ b/docshell/test/mochitest.ini @@ -103,8 +103,9 @@ skip-if = (buildapp == 'b2g' && toolkit [test_framedhistoryframes.html] skip-if = (buildapp == 'b2g' && toolkit != 'gonk') || toolkit == 'android' #Bug 931116, b2g desktop specific, initial triage, and also bug 784321 support-files = file_framedhistoryframes.html [test_pushState_after_document_open.html] [test_windowedhistoryframes.html] skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_bug1121701.html] skip-if = (buildapp == 'b2g' || buildapp == 'mulet') +[test_forceinheritprincipal_overrule_owner.html]
new file mode 100644 --- /dev/null +++ b/docshell/test/test_forceinheritprincipal_overrule_owner.html @@ -0,0 +1,51 @@ +<!DOCTYPE html> +<html> +<head> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> + +<script type="text/javascript"> + +var channel = SpecialPowers.wrap(document).docShell.currentDocumentChannel; +var loadInfo = channel.loadInfo; + +// 1) perform some sanity checks +var triggeringPrincipal = channel.loadInfo.triggeringPrincipal.URI.asciiSpec; +var loadingPrincipal = channel.loadInfo.loadingPrincipal.URI.asciiSpec; +var principalToInherit = channel.loadInfo.principalToInherit.URI.asciiSpec; + +ok(triggeringPrincipal.startsWith("http://mochi.test:8888/"), + "initial triggeringPrincipal correct"); +ok(loadingPrincipal.startsWith("http://mochi.test:8888/"), + "initial loadingPrincipal correct"); +ok(principalToInherit.startsWith("http://mochi.test:8888/"), + "initial principalToInherit correct"); + +// reset principals on the loadinfo +loadInfo.resetPrincipalsToNullPrincipal(); + +// 2) verify loadInfo contains the correct principals +var triggeringPrincipal = channel.loadInfo.triggeringPrincipal; +var loadingPrincipal = channel.loadInfo.loadingPrincipal; +var principalToInherit = channel.loadInfo.principalToInherit; + +ok(triggeringPrincipal.isNullPrincipal, + "triggeringPrincipal after resetting correct"); +ok(loadingPrincipal.isNullPrincipal, + "triggeringPrincipal after resetting correct"); +ok(triggeringPrincipal.isNullPrincipal, + "principalToInherit after resetting correct"); + +// 3) verify that getChannelResultPrincipal returns right principal +var resultPrincipal = SpecialPowers.Services.scriptSecurityManager + .getChannelResultPrincipal(channel); + +ok(resultPrincipal.isNullPrincipal, + "resultPrincipal after resetting correct"); + +</script> +</pre> +</body> +</html>
--- a/dom/base/nsFrameLoader.cpp +++ b/dom/base/nsFrameLoader.cpp @@ -1190,30 +1190,16 @@ nsFrameLoader::SwapWithOtherLoader(nsFra ourContent->HasAttr(kNameSpaceID_None, nsGkAtoms::srcdoc); bool otherHasSrcdoc = otherContent->IsHTMLElement(nsGkAtoms::iframe) && otherContent->HasAttr(kNameSpaceID_None, nsGkAtoms::srcdoc); if (ourHasSrcdoc || otherHasSrcdoc) { // Ignore this case entirely for now, since we support XUL <-> HTML swapping return NS_ERROR_NOT_IMPLEMENTED; } - bool ourPassPointerEvents = - ourContent->AttrValueIs(kNameSpaceID_None, - nsGkAtoms::mozpasspointerevents, - nsGkAtoms::_true, - eCaseMatters); - bool otherPassPointerEvents = - otherContent->AttrValueIs(kNameSpaceID_None, - nsGkAtoms::mozpasspointerevents, - nsGkAtoms::_true, - eCaseMatters); - if (ourPassPointerEvents != otherPassPointerEvents) { - return NS_ERROR_NOT_IMPLEMENTED; - } - bool ourFullscreenAllowed = ourContent->IsXULElement() || (OwnerIsMozBrowserOrAppFrame() && (ourContent->HasAttr(kNameSpaceID_None, nsGkAtoms::allowfullscreen) || ourContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozallowfullscreen))); bool otherFullscreenAllowed = otherContent->IsXULElement() || (aOther->OwnerIsMozBrowserOrAppFrame() && @@ -2657,23 +2643,16 @@ nsFrameLoader::TryRemoteBrowser() nsCOMPtr<nsIDOMChromeWindow> rootChromeWin = do_QueryInterface(rootWin); if (rootChromeWin) { nsCOMPtr<nsIBrowserDOMWindow> browserDOMWin; rootChromeWin->GetBrowserDOMWindow(getter_AddRefs(browserDOMWin)); mRemoteBrowser->SetBrowserDOMWindow(browserDOMWin); } - if (mOwnerContent->AttrValueIs(kNameSpaceID_None, - nsGkAtoms::mozpasspointerevents, - nsGkAtoms::_true, - eCaseMatters)) { - Unused << mRemoteBrowser->SendSetUpdateHitRegion(true); - } - ReallyLoadFrameScripts(); InitializeBrowserAPI(); return true; } mozilla::dom::PBrowserParent* nsFrameLoader::GetRemoteBrowser() const @@ -2703,30 +2682,16 @@ NS_IMETHODIMP nsFrameLoader::DeactivateRemoteFrame() { if (mRemoteBrowser) { mRemoteBrowser->Deactivate(); return NS_OK; } return NS_ERROR_UNEXPECTED; } -void -nsFrameLoader::ActivateUpdateHitRegion() { - if (mRemoteBrowser) { - Unused << mRemoteBrowser->SendSetUpdateHitRegion(true); - } -} - -void -nsFrameLoader::DeactivateUpdateHitRegion() { - if (mRemoteBrowser) { - Unused << mRemoteBrowser->SendSetUpdateHitRegion(false); - } -} - NS_IMETHODIMP nsFrameLoader::SendCrossProcessMouseEvent(const nsAString& aType, float aX, float aY, int32_t aButton, int32_t aClickCount, int32_t aModifiers, bool aIgnoreRootScrollFrame)
--- a/dom/base/nsFrameLoader.h +++ b/dom/base/nsFrameLoader.h @@ -213,19 +213,16 @@ public: * Applies a new set of sandbox flags. These are merged with the sandbox * flags from our owning content's owning document with a logical OR, this * ensures that we can only add restrictions and never remove them. */ void ApplySandboxFlags(uint32_t sandboxFlags); void GetURL(nsString& aURL); - void ActivateUpdateHitRegion(); - void DeactivateUpdateHitRegion(); - // Properly retrieves documentSize of any subdocument type. nsresult GetWindowDimensions(nsIntRect& aRect); virtual nsIMessageSender* GetProcessMessageManager() const override; // public because a callback needs these. RefPtr<nsFrameMessageManager> mMessageManager; nsCOMPtr<nsIInProcessContentFrameMessageManager> mChildMessageManager;
--- a/dom/base/nsGkAtomList.h +++ b/dom/base/nsGkAtomList.h @@ -623,17 +623,16 @@ GK_ATOM(mousedown, "mousedown") GK_ATOM(mousemove, "mousemove") GK_ATOM(mouseout, "mouseout") GK_ATOM(mouseover, "mouseover") GK_ATOM(mousethrough, "mousethrough") GK_ATOM(mouseup, "mouseup") GK_ATOM(mozaudiochannel, "mozaudiochannel") GK_ATOM(mozfullscreenchange, "mozfullscreenchange") GK_ATOM(mozfullscreenerror, "mozfullscreenerror") -GK_ATOM(mozpasspointerevents, "mozpasspointerevents") GK_ATOM(mozpointerlockchange, "mozpointerlockchange") GK_ATOM(mozpointerlockerror, "mozpointerlockerror") GK_ATOM(mozprivatebrowsing, "mozprivatebrowsing") GK_ATOM(moz_opaque, "moz-opaque") GK_ATOM(moz_action_hint, "mozactionhint") GK_ATOM(x_moz_errormessage, "x-moz-errormessage") GK_ATOM(msthemecompatible, "msthemecompatible") GK_ATOM(multicol, "multicol")
--- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -6107,22 +6107,26 @@ nsGlobalWindow::SetFullScreen(bool aFull nsresult nsGlobalWindow::SetFullScreen(bool aFullScreen) { FORWARD_TO_OUTER(SetFullScreen, (aFullScreen), NS_ERROR_NOT_INITIALIZED); return SetFullscreenInternal(FullscreenReason::ForFullscreenMode, aFullScreen); } -void +static void FinishDOMFullscreenChange(nsIDocument* aDoc, bool aInDOMFullscreen) { if (aInDOMFullscreen) { // Ask the document to handle any pending DOM fullscreen change. - nsIDocument::HandlePendingFullscreenRequests(aDoc); + if (!nsIDocument::HandlePendingFullscreenRequests(aDoc)) { + // If we don't end up having anything in fullscreen, + // async request exiting fullscreen. + nsIDocument::AsyncExitFullscreen(aDoc); + } } else { // If the window is leaving fullscreen state, also ask the document // to exit from DOM Fullscreen. nsIDocument::ExitFullscreenInDocTree(aDoc); } } struct FullscreenTransitionDuration
--- a/dom/base/nsImageLoadingContent.cpp +++ b/dom/base/nsImageLoadingContent.cpp @@ -655,17 +655,17 @@ nsImageLoadingContent::ForceReload(const // defaults to true bool notify = !aNotify.WasPassed() || aNotify.Value(); // We keep this flag around along with the old URI even for failed requests // without a live request object ImageLoadType loadType = \ (mCurrentRequestFlags & REQUEST_IS_IMAGESET) ? eImageLoadType_Imageset : eImageLoadType_Normal; - nsresult rv = LoadImage(currentURI, true, notify, loadType, nullptr, + nsresult rv = LoadImage(currentURI, true, notify, loadType, true, nullptr, nsIRequest::VALIDATE_ALWAYS); if (NS_FAILED(rv)) { aError.Throw(rv); } } NS_IMETHODIMP nsImageLoadingContent::ForceReload(bool aNotify /* = true */, @@ -746,40 +746,49 @@ nsImageLoadingContent::LoadImage(const n if (aNewURI.IsEmpty()) { // Cancel image requests and then fire only error event per spec. CancelImageRequests(aNotify); FireEvent(NS_LITERAL_STRING("error")); return NS_OK; } - // Second, parse the URI string to get image URI + // Fire loadstart event + FireEvent(NS_LITERAL_STRING("loadstart")); + + // Parse the URI string to get image URI nsCOMPtr<nsIURI> imageURI; nsresult rv = StringToURI(aNewURI, doc, getter_AddRefs(imageURI)); if (NS_FAILED(rv)) { // Cancel image requests and then fire error and loadend events per spec CancelImageRequests(aNotify); FireEvent(NS_LITERAL_STRING("error")); FireEvent(NS_LITERAL_STRING("loadend")); return NS_OK; } NS_TryToSetImmutable(imageURI); - return LoadImage(imageURI, aForce, aNotify, aImageLoadType, doc); + return LoadImage(imageURI, aForce, aNotify, aImageLoadType, false, doc); } nsresult nsImageLoadingContent::LoadImage(nsIURI* aNewURI, bool aForce, bool aNotify, ImageLoadType aImageLoadType, + bool aLoadStart, nsIDocument* aDocument, nsLoadFlags aLoadFlags) { + // Fire loadstart event if required + if (aLoadStart) { + FireEvent(NS_LITERAL_STRING("loadstart")); + } + if (!mLoadingEnabled) { // XXX Why fire an error here? seems like the callers to SetLoadingEnabled // don't want/need it. FireEvent(NS_LITERAL_STRING("error")); FireEvent(NS_LITERAL_STRING("loadend")); return NS_OK; }
--- a/dom/base/nsImageLoadingContent.h +++ b/dom/base/nsImageLoadingContent.h @@ -121,23 +121,25 @@ protected: * URI object already available, they should use this method. * * @param aNewURI the URI to be loaded * @param aForce If true, make sure to load the URI. If false, only * load if the URI is different from the currently loaded URI. * @param aNotify If true, nsIDocumentObserver state change notifications * will be sent as needed. * @param aImageLoadType The ImageLoadType for this request + * @param aLoadStart If true, dispatch "loadstart" event. * @param aDocument Optional parameter giving the document this node is in. * This is purely a performance optimization. * @param aLoadFlags Optional parameter specifying load flags to use for * the image load */ nsresult LoadImage(nsIURI* aNewURI, bool aForce, bool aNotify, - ImageLoadType aImageLoadType, nsIDocument* aDocument = nullptr, + ImageLoadType aImageLoadType, bool aLoadStart = true, + nsIDocument* aDocument = nullptr, nsLoadFlags aLoadFlags = nsIRequest::LOAD_NORMAL); /** * helpers to get the document for this content (from the nodeinfo * and such). Not named GetOwnerDoc/GetCurrentDoc to prevent ambiguous * method names in subclasses * * @return the document we belong to
--- a/dom/base/nsPluginArray.cpp +++ b/dom/base/nsPluginArray.cpp @@ -18,16 +18,17 @@ #include "nsPluginTags.h" #include "nsIObserverService.h" #include "nsIWeakReference.h" #include "mozilla/Services.h" #include "nsIInterfaceRequestorUtils.h" #include "nsContentUtils.h" #include "nsIPermissionManager.h" #include "nsIDocument.h" +#include "nsIBlocklistService.h" using namespace mozilla; using namespace mozilla::dom; nsPluginArray::nsPluginArray(nsPIDOMWindowInner* aWindow) : mWindow(aWindow) { } @@ -355,17 +356,20 @@ nsPluginArray::EnsurePlugins() // need to wrap each of these with a nsPluginElement, which is // scriptable. for (uint32_t i = 0; i < pluginTags.Length(); ++i) { nsCOMPtr<nsPluginTag> pluginTag = do_QueryInterface(pluginTags[i]); if (!pluginTag) { mPlugins.AppendElement(new nsPluginElement(mWindow, pluginTags[i])); } else if (pluginTag->IsActive()) { uint32_t permission = nsIPermissionManager::ALLOW_ACTION; - if (pluginTag->IsClicktoplay()) { + uint32_t blocklistState; + if (pluginTag->IsClicktoplay() && + NS_SUCCEEDED(pluginTag->GetBlocklistState(&blocklistState)) && + blocklistState == nsIBlocklistService::STATE_NOT_BLOCKED) { nsCString name; pluginTag->GetName(name); if (PluginShouldBeHidden(name)) { RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst(); nsCString permString; nsresult rv = pluginHost->GetPermissionStringForTag(pluginTag, 0, permString); if (rv == NS_OK) { nsIPrincipal* principal = mWindow->GetExtantDoc()->NodePrincipal();
--- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -4436,49 +4436,16 @@ CanvasRenderingContext2D::GetLineJoin(ns aLinejoinStyle.AssignLiteral("miter"); break; default: aError.Throw(NS_ERROR_FAILURE); } } void -CanvasRenderingContext2D::SetMozDash(JSContext* aCx, - const JS::Value& aMozDash, - ErrorResult& aError) -{ - nsTArray<Float> dash; - aError = JSValToDashArray(aCx, aMozDash, dash); - if (!aError.Failed()) { - ContextState& state = CurrentState(); - state.dash = Move(dash); - if (state.dash.IsEmpty()) { - state.dashOffset = 0; - } - } -} - -void -CanvasRenderingContext2D::GetMozDash(JSContext* aCx, - JS::MutableHandle<JS::Value> aRetval, - ErrorResult& aError) -{ - DashArrayToJSVal(CurrentState().dash, aCx, aRetval, aError); -} - -void -CanvasRenderingContext2D::SetMozDashOffset(double aMozDashOffset) -{ - ContextState& state = CurrentState(); - if (!state.dash.IsEmpty()) { - state.dashOffset = aMozDashOffset; - } -} - -void CanvasRenderingContext2D::SetLineDash(const Sequence<double>& aSegments, ErrorResult& aRv) { nsTArray<mozilla::gfx::Float> dash; for (uint32_t x = 0; x < aSegments.Length(); x++) { if (aSegments[x] < 0.0) { // Pattern elements must be finite "numbers" >= 0, with "finite"
--- a/dom/canvas/CanvasRenderingContext2D.h +++ b/dom/canvas/CanvasRenderingContext2D.h @@ -360,34 +360,24 @@ public: void GetMozCurrentTransformInverse(JSContext* aCx, JS::MutableHandle<JSObject*> aResult, mozilla::ErrorResult& aError); void SetMozCurrentTransformInverse(JSContext* aCx, JS::Handle<JSObject*> aCurrentTransform, mozilla::ErrorResult& aError); void GetFillRule(nsAString& aFillRule); void SetFillRule(const nsAString& aFillRule); - void GetMozDash(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval, - mozilla::ErrorResult& aError); - void SetMozDash(JSContext* aCx, const JS::Value& aMozDash, - mozilla::ErrorResult& aError); void SetLineDash(const Sequence<double>& aSegments, mozilla::ErrorResult& aRv); void GetLineDash(nsTArray<double>& aSegments) const; void SetLineDashOffset(double aOffset); double LineDashOffset() const; - double MozDashOffset() - { - return CurrentState().dashOffset; - } - void SetMozDashOffset(double aMozDashOffset); - void GetMozTextStyle(nsAString& aMozTextStyle) { GetFont(aMozTextStyle); } void SetMozTextStyle(const nsAString& aMozTextStyle, mozilla::ErrorResult& aError) {
--- a/dom/canvas/test/mochitest.ini +++ b/dom/canvas/test/mochitest.ini @@ -245,17 +245,16 @@ tags = imagebitmap [test_imagebitmap_structuredclone_iframe.html] tags = imagebitmap [test_imagebitmap_structuredclone_window.html] tags = imagebitmap [test_imagebitmap_transfer.html] tags = imagebitmap [test_ImageData_ctor.html] [test_isPointInStroke.html] -[test_mozDashOffset.html] [test_mozGetAsFile.html] [test_strokeText_throw.html] [test_toBlob.html] [test_toDataURL_alpha.html] [test_toDataURL_lowercase_ascii.html] [test_toDataURL_parameters.html] [test_windingRuleUndefined.html] [test_2d.fillText.gradient.html]
--- a/dom/canvas/test/reftest/drawFocusIfNeeded-ref.html +++ b/dom/canvas/test/reftest/drawFocusIfNeeded-ref.html @@ -1,17 +1,17 @@ <!--docytpe html--> <html><head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <meta charset="UTF-8"> <script> window.onload=function(){ var c=document.getElementById("myCanvas").getContext("2d"); c.beginPath(); -c.mozDash = [1,1]; +c.setLineDash([1,1]); c.strokeRect(10, 10, 200, 200); } </script> </head> <body> <canvas id="myCanvas" height="500" width="500" style="border:1px solid black"> </canvas>
deleted file mode 100644 --- a/dom/canvas/test/test_mozDashOffset.html +++ /dev/null @@ -1,39 +0,0 @@ -<!DOCTYPE HTML> -<html> -<head> - <title>Test for mozDashOffset</title> - <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> -</head> -<body> -<p id="display"> -<canvas id="c" width="100" height="100"><p class="fallback">FAIL (fallback content)</p></canvas> -</p> -<div id="content" style="display: none"> -</div> -<pre id="test"> -<script> -try { - var canvas = document.getElementById('c'); - var ctx = canvas.getContext('2d'); - - ctx.mozDash = [1, 1]; - - ctx.mozDashOffset = 1; - ctx.mozDashOffset = Infinity; - ok(ctx.mozDashOffset === 1, "ctx.mozDashOffset === 1"); - - ctx.mozDashOffset = 1; - ctx.mozDashOffset = -Infinity; - ok(ctx.mozDashOffset === 1, "ctx.mozDashOffset === 1"); - - ctx.mozDashOffset = 1; - ctx.mozDashOffset = NaN; - ok(ctx.mozDashOffset === 1, "ctx.mozDashOffset === 1"); -} catch(e) { - ok(false, "unexpected exception thrown in: test_mozDashOffset.html"); -} -</script> -</pre> -</body> -</html>
--- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -29,16 +29,19 @@ #include "BlobParent.h" #include "CrashReporterParent.h" #include "DeviceStorageStatics.h" #include "GMPServiceParent.h" #include "HandlerServiceParent.h" #include "IHistory.h" #include "imgIContainer.h" #include "mozIApplication.h" +#if defined(XP_WIN) && defined(ACCESSIBILITY) +#include "mozilla/a11y/AccessibleWrap.h" +#endif #include "mozilla/ClearOnShutdown.h" #include "mozilla/StyleSheetInlines.h" #include "mozilla/DataStorage.h" #include "mozilla/devtools/HeapSnapshotTempFileHelperParent.h" #include "mozilla/docshell/OfflineCacheUpdateParent.h" #include "mozilla/dom/DataTransfer.h" #include "mozilla/dom/DOMStorageIPC.h" #include "mozilla/dom/Element.h" @@ -1949,16 +1952,20 @@ ContentParent::ActorDestroy(ActorDestroy } // Unregister all the BlobURLs registered by the ContentChild. for (uint32_t i = 0; i < mBlobURLs.Length(); ++i) { nsHostObjectProtocolHandler::RemoveDataEntry(mBlobURLs[i]); } mBlobURLs.Clear(); + +#if defined(XP_WIN32) && defined(ACCESSIBILITY) + a11y::AccessibleWrap::ReleaseContentProcessIdFor(ChildID()); +#endif } void ContentParent::NotifyTabDestroying(const TabId& aTabId, const ContentParentId& aCpId) { if (XRE_IsParentProcess()) { // There can be more than one PBrowser for a given app process @@ -5343,16 +5350,28 @@ ContentParent::RecvUnstoreAndBroadcastBl nsHostObjectProtocolHandler::RemoveDataEntry(aURI, false /* Don't broadcast */); BroadcastBlobURLUnregistration(aURI, this); mBlobURLs.RemoveElement(aURI); return true; } +bool +ContentParent::RecvGetA11yContentId(uint32_t* aContentId) +{ +#if defined(XP_WIN32) && defined(ACCESSIBILITY) + *aContentId = a11y::AccessibleWrap::GetContentProcessIdFor(ChildID()); + MOZ_ASSERT(*aContentId); + return true; +#else + return false; +#endif +} + } // namespace dom } // namespace mozilla NS_IMPL_ISUPPORTS(ParentIdleListener, nsIObserver) NS_IMETHODIMP ParentIdleListener::Observe(nsISupports*, const char* aTopic, const char16_t* aData) {
--- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -560,16 +560,19 @@ public: virtual bool RecvStoreAndBroadcastBlobURLRegistration(const nsCString& aURI, PBlobParent* aBlobParent, const Principal& aPrincipal) override; virtual bool RecvUnstoreAndBroadcastBlobURLUnregistration(const nsCString& aURI) override; + virtual bool + RecvGetA11yContentId(uint32_t* aContentId) override; + virtual int32_t Pid() const override; // Use the PHangMonitor channel to ask the child to repaint a tab. void ForceTabPaint(TabParent* aTabParent, uint64_t aLayerObserverEpoch); protected: void OnChannelConnected(int32_t pid) override;
--- a/dom/ipc/PBrowser.ipdl +++ b/dom/ipc/PBrowser.ipdl @@ -741,23 +741,16 @@ child: /** * Sent by the chrome process when it no longer wants this remote * <browser>. The child side cleans up in response, then * finalizing its death by sending back __delete__() to the * parent. */ async Destroy(); - - /** - * Tell the child side if it has to update it's touchable region - * to the parent. - */ - async SetUpdateHitRegion(bool aEnabled); - /** * Update the child side docShell active (resource use) state. * * @param aIsActive * Whether to activate or deactivate the docshell. * @param aPreserveLayers * Whether layer trees should be preserved for inactive docshells. * @param aLayerObserverEpoch
--- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -1231,16 +1231,18 @@ parent: async UnstoreAndBroadcastBlobURLUnregistration(nsCString url); /** * Messages for communicating child Telemetry to the parent process */ async AccumulateChildHistogram(Accumulation[] accumulations); async AccumulateChildKeyedHistogram(KeyedAccumulation[] accumulations); + sync GetA11yContentId() returns (uint32_t aContentId); + both: async AsyncMessage(nsString aMessage, CpowEntry[] aCpows, Principal aPrincipal, ClonedMessageData aData); /** * Notify `push-subscription-modified` observers in the parent and child. */ async NotifyPushSubscriptionModifiedObservers(nsCString scope,
--- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -530,30 +530,28 @@ TabChild::TabChild(nsIContentChild* aMan , mChromeFlags(aChromeFlags) , mActiveSuppressDisplayport(0) , mLayersId(0) , mAppPackageFileDescriptorRecved(false) , mDidFakeShow(false) , mNotified(false) , mTriedBrowserInit(false) , mOrientation(eScreenOrientation_PortraitPrimary) - , mUpdateHitRegion(false) , mIgnoreKeyPressEvent(false) , mHasValidInnerSize(false) , mDestroyed(false) , mUniqueId(aTabId) , mDPI(0) , mRounding(0) , mDefaultScale(0) , mIsTransparent(false) , mIPCOpen(false) , mParentIsActive(false) , mDidSetRealShowInfo(false) , mDidLoadURLInit(false) - , mAPZChild(nullptr) , mLayerObserverEpoch(0) #if defined(XP_WIN) && defined(ACCESSIBILITY) , mNativeWindowHandle(0) #endif { // In the general case having the TabParent tell us if APZ is enabled or not // doesn't really work because the TabParent itself may not have a reference // to the owning widget during initialization. Instead we assume that this @@ -2637,38 +2635,16 @@ TabChild::RecvDestroy() // that were just generated to have a chance to run. nsCOMPtr<nsIRunnable> deleteRunnable = new DelayedDeleteRunnable(this); MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(deleteRunnable)); return true; } bool -TabChild::RecvSetUpdateHitRegion(const bool& aEnabled) -{ - mUpdateHitRegion = aEnabled; - - // We need to trigger a repaint of the child frame to ensure that it - // recomputes and sends its region. - if (!mUpdateHitRegion) { - return true; - } - - nsCOMPtr<nsIDocument> document(GetDocument()); - NS_ENSURE_TRUE(document, true); - nsCOMPtr<nsIPresShell> presShell = document->GetShell(); - NS_ENSURE_TRUE(presShell, true); - RefPtr<nsPresContext> presContext = presShell->GetPresContext(); - NS_ENSURE_TRUE(presContext, true); - presContext->InvalidatePaintedLayers(); - - return true; -} - -bool TabChild::RecvSetDocShellIsActive(const bool& aIsActive, const bool& aPreserveLayers, const uint64_t& aLayerObserverEpoch) { // Since SetDocShellIsActive requests come in from both the hang monitor // channel and the PContent channel, we have an ordering problem. This code // ensures that we respect the order in which the requests were made and // ignore stale requests. @@ -3003,25 +2979,16 @@ TabChild::MakeHidden() // round-trip from CompositorBridgeChild to CompositorBridgeParent. compositor->RecvClearCachedResources(mLayersId); if (mPuppetWidget) { mPuppetWidget->Show(false); } } -void -TabChild::UpdateHitRegion(const nsRegion& aRegion) -{ - mRemoteFrame->SendUpdateHitRegion(aRegion); - if (mAPZChild) { - mAPZChild->SendUpdateHitRegion(aRegion); - } -} - NS_IMETHODIMP TabChild::GetMessageManager(nsIContentFrameMessageManager** aResult) { if (mTabChildGlobal) { NS_ADDREF(*aResult = mTabChildGlobal); return NS_OK; } *aResult = nullptr;
--- a/dom/ipc/TabChild.h +++ b/dom/ipc/TabChild.h @@ -513,20 +513,16 @@ public: nsICachedFileDescriptorListener* aCallback); void CancelCachedFileDescriptorCallback( const nsAString& aPath, nsICachedFileDescriptorListener* aCallback); nsIContentChild* Manager() const { return mManager; } - bool GetUpdateHitRegion() const { return mUpdateHitRegion; } - - void UpdateHitRegion(const nsRegion& aRegion); - static inline TabChild* GetFrom(nsIDocShell* aDocShell) { if (!aDocShell) { return nullptr; } nsCOMPtr<nsITabChild> tc = aDocShell->GetTabChild(); @@ -646,39 +642,32 @@ public: const layers::GeckoContentController::APZStateChange& aChange, const int& aArg); void StartScrollbarDrag(const layers::AsyncDragMetrics& aDragMetrics); void ZoomToRect(const uint32_t& aPresShellId, const FrameMetrics::ViewID& aViewId, const CSSRect& aRect, const uint32_t& aFlags); - void SetAPZChild(layers::APZChild* aAPZChild) - { - mAPZChild = aAPZChild; - } - // Request that the docshell be marked as active. void ForcePaint(uint64_t aLayerObserverEpoch); #if defined(XP_WIN) && defined(ACCESSIBILITY) uintptr_t GetNativeWindowHandle() const { return mNativeWindowHandle; } #endif protected: virtual ~TabChild(); virtual PRenderFrameChild* AllocPRenderFrameChild() override; virtual bool DeallocPRenderFrameChild(PRenderFrameChild* aFrame) override; virtual bool RecvDestroy() override; - virtual bool RecvSetUpdateHitRegion(const bool& aEnabled) override; - virtual bool RecvSetDocShellIsActive(const bool& aIsActive, const bool& aIsHidden, const uint64_t& aLayerObserverEpoch) override; virtual bool RecvNavigateByKey(const bool& aForward, const bool& aForDocumentNavigation) override; virtual bool RecvRequestNotifyAfterRemotePaint() override; @@ -760,17 +749,16 @@ private: // At present only 1 of these is really expected. AutoTArray<nsAutoPtr<CachedFileDescriptorInfo>, 1> mCachedFileDescriptorInfos; nscolor mLastBackgroundColor; bool mDidFakeShow; bool mNotified; bool mTriedBrowserInit; ScreenOrientationInternal mOrientation; - bool mUpdateHitRegion; bool mIgnoreKeyPressEvent; RefPtr<APZEventState> mAPZEventState; SetAllowedTouchBehaviorCallback mSetAllowedTouchBehaviorCallback; bool mHasValidInnerSize; bool mDestroyed; // Position of client area relative to the outer window LayoutDeviceIntPoint mClientOffset;
--- a/dom/webidl/CanvasRenderingContext2D.webidl +++ b/dom/webidl/CanvasRenderingContext2D.webidl @@ -148,22 +148,16 @@ interface CanvasRenderingContext2D { // Mozilla-specific stuff // FIXME Bug 768048 mozCurrentTransform/mozCurrentTransformInverse should return a WebIDL array. [Throws] attribute object mozCurrentTransform; // [ m11, m12, m21, m22, dx, dy ], i.e. row major [Throws] attribute object mozCurrentTransformInverse; - [Throws] - attribute any mozDash; /* default |null| */ - - [LenientFloat] - attribute double mozDashOffset; /* default 0.0 */ - [SetterThrows] attribute DOMString mozTextStyle; // image smoothing mode -- if disabled, images won't be smoothed // if scaled. [Deprecated="PrefixedImageSmoothingEnabled"] attribute boolean mozImageSmoothingEnabled;
--- a/gfx/ipc/GraphicsMessages.ipdlh +++ b/gfx/ipc/GraphicsMessages.ipdlh @@ -14,16 +14,17 @@ using gfxImageFormat from "mozilla/gfx/T namespace mozilla { namespace gfx { struct D3D11DeviceStatus { bool isWARP; bool textureSharingWorks; + bool alphaTextureSharingWorks; uint32_t featureLevel; DxgiAdapterDesc adapter; }; struct DevicePrefs { FeatureStatus hwCompositing; FeatureStatus d3d11Compositing;
--- a/gfx/layers/IMFYCbCrImage.cpp +++ b/gfx/layers/IMFYCbCrImage.cpp @@ -232,16 +232,20 @@ IMFYCbCrImage::GetTextureClient(KnowsCom if (!device || backend != LayersBackend::LAYERS_D3D11) { if (backend == LayersBackend::LAYERS_D3D9 || backend == LayersBackend::LAYERS_D3D11) { return GetD3D9TextureClient(aForwarder); } return nullptr; } + if (!gfx::DeviceManagerDx::Get()->AlphaTextureSharingWorks()) { + return nullptr; + } + if (mData.mYStride < 0 || mData.mCbCrStride < 0) { // D3D11 only supports unsigned stride values. return nullptr; } CD3D11_TEXTURE2D_DESC newDesc(DXGI_FORMAT_R8_UNORM, mData.mYSize.width, mData.mYSize.height, 1, 1);
--- a/gfx/layers/apz/public/GeckoContentController.h +++ b/gfx/layers/apz/public/GeckoContentController.h @@ -102,33 +102,16 @@ public: */ virtual bool IsRepaintThread() = 0; /** * Runs the given task on the "repaint" thread. */ virtual void DispatchToRepaintThread(already_AddRefed<Runnable> aTask) = 0; - /** - * APZ uses |FrameMetrics::mCompositionBounds| for hit testing. Sometimes, - * widget code has knowledge of a touch-sensitive region that should - * additionally constrain hit testing for all frames associated with the - * controller. This method allows APZ to query the controller for such a - * region. A return value of true indicates that the controller has such a - * region, and it is returned in |aOutRegion|. - * This method needs to be called on the main thread. - * TODO: once bug 928833 is implemented, this should be removed, as - * APZ can then get the correct touch-sensitive region for each frame - * directly from the layer. - */ - virtual bool GetTouchSensitiveRegion(CSSRect* aOutRegion) - { - return false; - } - enum class APZStateChange { /** * APZ started modifying the view (including panning, zooming, and fling). */ eTransformBegin, /** * APZ finished modifying the view. */
--- a/gfx/layers/apz/src/APZCTreeManager.cpp +++ b/gfx/layers/apz/src/APZCTreeManager.cpp @@ -271,40 +271,16 @@ ComputeClipRegion(GeckoContentController } else { // if there is no clip on this layer (which should only happen for the // root scrollable layer in a process, or for some of the LayerMetrics // expansions of a multi-metrics layer), fall back to using the comp // bounds which should be equivalent. clipRegion = RoundedToInt(aLayer.Metrics().GetCompositionBounds()); } - // Optionally, the GeckoContentController can provide a touch-sensitive - // region that constrains all frames associated with the controller. - // In this case we intersect the composition bounds with that region. - CSSRect touchSensitiveRegion; - if (aController->GetTouchSensitiveRegion(&touchSensitiveRegion)) { - // Here we assume 'touchSensitiveRegion' is in the CSS pixels of the - // parent frame. To convert it to ParentLayer pixels, we therefore need - // the cumulative resolution of the parent frame. We approximate this as - // the quotient of our cumulative resolution and our pres shell resolution; - // this approximation may not be accurate in the presence of a css-driven - // resolution. - LayoutDeviceToParentLayerScale2D parentCumulativeResolution = - aLayer.Metrics().GetCumulativeResolution() - / ParentLayerToLayerScale(aLayer.Metrics().GetPresShellResolution()); - // Not sure what rounding option is the most correct here, but if we ever - // figure it out we can change this. For now I'm rounding in to minimize - // the chances of getting a complex region. - ParentLayerIntRegion extraClip = RoundedIn( - touchSensitiveRegion - * aLayer.Metrics().GetDevPixelsPerCSSPixel() - * parentCumulativeResolution); - clipRegion.AndWith(extraClip); - } - return clipRegion; } void APZCTreeManager::PrintAPZCInfo(const LayerMetricsWrapper& aLayer, const AsyncPanZoomController* apzc) { const FrameMetrics& metrics = aLayer.Metrics();
--- a/gfx/layers/apz/util/ContentProcessController.cpp +++ b/gfx/layers/apz/util/ContentProcessController.cpp @@ -94,18 +94,16 @@ ContentProcessController::ContentProcess : mBrowser(nullptr) { } ContentProcessController::~ContentProcessController() { if (mObserver) { nsCOMPtr<nsIObserverService> os = services::GetObserverService(); os->RemoveObserver(mObserver, "tab-child-created"); - } else if (mBrowser) { - mBrowser->SetAPZChild(nullptr); } } void ContentProcessController::SetObserver(nsIObserver* aObserver) { MOZ_ASSERT(!mBrowser); mObserver = aObserver;
--- a/gfx/layers/ipc/PAPZ.ipdl +++ b/gfx/layers/ipc/PAPZ.ipdl @@ -41,18 +41,16 @@ namespace layers { * must be updated to handle it. */ sync protocol PAPZ { manager PCompositorBridge; parent: - async UpdateHitRegion(nsRegion aRegion); - async __delete__(); child: async RequestContentRepaint(FrameMetrics frame); async UpdateOverscrollVelocity(float aX, float aY, bool aIsRootContent);
--- a/gfx/layers/ipc/RemoteContentController.cpp +++ b/gfx/layers/ipc/RemoteContentController.cpp @@ -25,17 +25,16 @@ namespace mozilla { namespace layers { using namespace mozilla::gfx; RemoteContentController::RemoteContentController() : mCompositorThread(MessageLoop::current()) , mCanSend(true) - , mMutex("RemoteContentController") { } RemoteContentController::~RemoteContentController() { } void @@ -45,19 +44,19 @@ RemoteContentController::RequestContentR if (mCanSend) { Unused << SendRequestContentRepaint(aFrameMetrics); } } void RemoteContentController::HandleTapOnMainThread(TapType aTapType, - const LayoutDevicePoint& aPoint, + LayoutDevicePoint aPoint, Modifiers aModifiers, - const ScrollableLayerGuid& aGuid, + ScrollableLayerGuid aGuid, uint64_t aInputBlockId) { MOZ_ASSERT(NS_IsMainThread()); dom::TabParent* tab = dom::TabParent::GetTabParentFromLayersId(aGuid.mLayersId); if (tab) { tab->SendHandleTap(aTapType, aPoint, aModifiers, aGuid, aInputBlockId); } @@ -88,17 +87,17 @@ RemoteContentController::HandleTap(TapTy MOZ_ASSERT(XRE_IsParentProcess()); if (NS_IsMainThread()) { HandleTapOnMainThread(aTapType, aPoint, aModifiers, aGuid, aInputBlockId); } else { // We don't want to get the TabParent or call TabParent::SendHandleTap() from a non-main thread (this might happen // on Android, where this is called from the Java UI thread) - NS_DispatchToMainThread(NewRunnableMethod<TapType, const LayoutDevicePoint&, Modifiers, const ScrollableLayerGuid&, uint64_t> + NS_DispatchToMainThread(NewRunnableMethod<TapType, LayoutDevicePoint, Modifiers, ScrollableLayerGuid, uint64_t> (this, &RemoteContentController::HandleTapOnMainThread, aTapType, aPoint, aModifiers, aGuid, aInputBlockId)); } } void RemoteContentController::NotifyPinchGesture(PinchGestureInput::PinchGestureType aType, const ScrollableLayerGuid& aGuid, LayoutDeviceCoord aSpanChange, @@ -155,28 +154,16 @@ RemoteContentController::IsRepaintThread } void RemoteContentController::DispatchToRepaintThread(already_AddRefed<Runnable> aTask) { mCompositorThread->PostTask(Move(aTask)); } -bool -RemoteContentController::GetTouchSensitiveRegion(CSSRect* aOutRegion) -{ - MutexAutoLock lock(mMutex); - if (mTouchSensitiveRegion.IsEmpty()) { - return false; - } - - *aOutRegion = CSSRect::FromAppUnits(mTouchSensitiveRegion.GetBounds()); - return true; -} - void RemoteContentController::NotifyAPZStateChange(const ScrollableLayerGuid& aGuid, APZStateChange aChange, int aArg) { if (MessageLoop::current() != mCompositorThread) { // We have to send messages from the compositor thread mCompositorThread->PostTask(NewRunnableMethod<ScrollableLayerGuid, @@ -259,24 +246,16 @@ RemoteContentController::NotifyFlushComp { MOZ_ASSERT(IsRepaintThread()); if (mCanSend) { Unused << SendNotifyFlushComplete(); } } -bool -RemoteContentController::RecvUpdateHitRegion(const nsRegion& aRegion) -{ - MutexAutoLock lock(mMutex); - mTouchSensitiveRegion = aRegion; - return true; -} - void RemoteContentController::ActorDestroy(ActorDestroyReason aWhy) { // This controller could possibly be kept alive longer after this // by a RefPtr, but it is no longer valid to send messages. mCanSend = false; }
--- a/gfx/layers/ipc/RemoteContentController.h +++ b/gfx/layers/ipc/RemoteContentController.h @@ -54,51 +54,43 @@ public: Modifiers aModifiers) override; virtual void PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs) override; virtual bool IsRepaintThread() override; virtual void DispatchToRepaintThread(already_AddRefed<Runnable> aTask) override; - virtual bool GetTouchSensitiveRegion(CSSRect* aOutRegion) override; - virtual void NotifyAPZStateChange(const ScrollableLayerGuid& aGuid, APZStateChange aChange, int aArg) override; virtual void UpdateOverscrollVelocity(float aX, float aY, bool aIsRootContent) override; virtual void UpdateOverscrollOffset(float aX, float aY, bool aIsRootContent) override; virtual void SetScrollingRootContent(bool aIsRootContent) override; virtual void NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId, const nsString& aEvent) override; virtual void NotifyFlushComplete() override; - virtual bool RecvUpdateHitRegion(const nsRegion& aRegion) override; - virtual void ActorDestroy(ActorDestroyReason aWhy) override; virtual void Destroy() override; private: MessageLoop* mCompositorThread; bool mCanSend; void HandleTapOnMainThread(TapType aType, - const LayoutDevicePoint& aPoint, + LayoutDevicePoint aPoint, Modifiers aModifiers, - const ScrollableLayerGuid& aGuid, + ScrollableLayerGuid aGuid, uint64_t aInputBlockId); - - // Mutex protecting members below accessed from multiple threads. - mozilla::Mutex mMutex; - nsRegion mTouchSensitiveRegion; }; } // namespace layers } // namespace mozilla #endif // mozilla_layers_RemoteContentController_h
--- a/gfx/thebes/DeviceManagerDx.cpp +++ b/gfx/thebes/DeviceManagerDx.cpp @@ -323,16 +323,17 @@ DeviceManagerDx::CreateCompositorDevice( } } else { mCompositorDeviceSupportsVideo = true; } // Only test this when not using WARP since it can fail and cause // GetDeviceRemovedReason to return weird values. bool textureSharingWorks = D3D11Checks::DoesTextureSharingWork(device); + bool alphaTextureSharingWorks = D3D11Checks::DoesAlphaTextureSharingWork(device); DXGI_ADAPTER_DESC desc; PodZero(&desc); adapter->GetDesc(&desc); if (!textureSharingWorks) { gfxConfig::SetFailed(Feature::D3D11_HW_ANGLE, FeatureStatus::Broken, @@ -350,16 +351,17 @@ DeviceManagerDx::CreateCompositorDevice( int featureLevel = device->GetFeatureLevel(); { MutexAutoLock lock(mDeviceLock); mCompositorDevice = device; mDeviceStatus = Some(D3D11DeviceStatus( false, textureSharingWorks, + alphaTextureSharingWorks, featureLevel, DxgiAdapterDesc::From(desc))); } mCompositorDevice->SetExceptionMode(0); } bool DeviceManagerDx::CreateDevice(IDXGIAdapter* aAdapter, @@ -404,30 +406,33 @@ DeviceManagerDx::CreateWARPCompositorDev d3d11.SetFailed(FeatureStatus::Failed, "Failed to create a D3D11 WARP device", NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_WARP_DEVICE2")); return; } // Only test for texture sharing on Windows 8 since it puts the device into // an unusable state if used on Windows 7 bool textureSharingWorks = false; + bool alphaTextureSharingWorks = false; if (IsWin8OrLater()) { textureSharingWorks = D3D11Checks::DoesTextureSharingWork(device); + alphaTextureSharingWorks = D3D11Checks::DoesAlphaTextureSharingWork(device); } DxgiAdapterDesc nullAdapter; PodZero(&nullAdapter); int featureLevel = device->GetFeatureLevel(); { MutexAutoLock lock(mDeviceLock); mCompositorDevice = device; mDeviceStatus = Some(D3D11DeviceStatus( true, textureSharingWorks, + alphaTextureSharingWorks, featureLevel, nullAdapter)); } mCompositorDevice->SetExceptionMode(0); reporterWARP.SetSuccessful(); } @@ -666,16 +671,26 @@ DeviceManagerDx::TextureSharingWorks() MutexAutoLock lock(mDeviceLock); if (!mDeviceStatus) { return false; } return mDeviceStatus->textureSharingWorks(); } bool +DeviceManagerDx::AlphaTextureSharingWorks() +{ + MutexAutoLock lock(mDeviceLock); + if (!mDeviceStatus) { + return false; + } + return mDeviceStatus->alphaTextureSharingWorks(); +} + +bool DeviceManagerDx::IsWARP() { MutexAutoLock lock(mDeviceLock); if (!mDeviceStatus) { return false; } return mDeviceStatus->isWARP(); }
--- a/gfx/thebes/DeviceManagerDx.h +++ b/gfx/thebes/DeviceManagerDx.h @@ -53,16 +53,17 @@ public: RefPtr<ID3D11Device> GetCompositorDevice(); RefPtr<ID3D11Device> GetContentDevice(); RefPtr<ID3D11Device> CreateDecoderDevice(); IDirectDraw7* GetDirectDraw(); unsigned GetCompositorFeatureLevel() const; bool TextureSharingWorks(); + bool AlphaTextureSharingWorks(); bool IsWARP(); bool CreateCompositorDevices(); void CreateContentDevices(); void ImportDeviceInfo(const D3D11DeviceStatus& aDeviceStatus); void ExportDeviceInfo(D3D11DeviceStatus* aOut);
--- a/js/src/builtin/SIMD.cpp +++ b/js/src/builtin/SIMD.cpp @@ -19,16 +19,17 @@ #include "jsapi.h" #include "jsfriendapi.h" #include "jsnum.h" #include "jsprf.h" #include "builtin/TypedObject.h" #include "jit/InlinableNatives.h" +#include "js/GCAPI.h" #include "js/Value.h" #include "jsobjinlines.h" using namespace js; using mozilla::ArrayLength; using mozilla::IsFinite; @@ -168,35 +169,36 @@ js::ToSimdConstant(JSContext* cx, Handle { typedef typename V::Elem Elem; Rooted<TypeDescr*> typeDescr(cx, GetTypeDescr<V>(cx)); if (!typeDescr) return false; if (!IsVectorObject<V>(v)) return ErrorWrongTypeArg(cx, 1, typeDescr); - Elem* mem = reinterpret_cast<Elem*>(v.toObject().as<TypedObject>().typedMem()); + JS::AutoCheckCannotGC nogc(cx); + Elem* mem = reinterpret_cast<Elem*>(v.toObject().as<TypedObject>().typedMem(nogc)); *out = jit::SimdConstant::CreateSimd128(mem); return true; } template bool js::ToSimdConstant<Int8x16>(JSContext* cx, HandleValue v, jit::SimdConstant* out); template bool js::ToSimdConstant<Int16x8>(JSContext* cx, HandleValue v, jit::SimdConstant* out); template bool js::ToSimdConstant<Int32x4>(JSContext* cx, HandleValue v, jit::SimdConstant* out); template bool js::ToSimdConstant<Float32x4>(JSContext* cx, HandleValue v, jit::SimdConstant* out); template bool js::ToSimdConstant<Bool8x16>(JSContext* cx, HandleValue v, jit::SimdConstant* out); template bool js::ToSimdConstant<Bool16x8>(JSContext* cx, HandleValue v, jit::SimdConstant* out); template bool js::ToSimdConstant<Bool32x4>(JSContext* cx, HandleValue v, jit::SimdConstant* out); template<typename Elem> static Elem -TypedObjectMemory(HandleValue v) +TypedObjectMemory(HandleValue v, const JS::AutoAssertOnGC& nogc) { TypedObject& obj = v.toObject().as<TypedObject>(); - return reinterpret_cast<Elem>(obj.typedMem()); + return reinterpret_cast<Elem>(obj.typedMem(nogc)); } static const ClassOps SimdTypeDescrClassOps = { nullptr, /* addProperty */ nullptr, /* delProperty */ nullptr, /* getProperty */ nullptr, /* setProperty */ nullptr, /* enumerate */ @@ -417,17 +419,21 @@ template <typename T> static bool FillLanes(JSContext* cx, Handle<TypedObject*> result, const CallArgs& args) { typedef typename T::Elem Elem; Elem tmp; for (unsigned i = 0; i < T::lanes; i++) { if (!T::Cast(cx, args.get(i), &tmp)) return false; - reinterpret_cast<Elem*>(result->typedMem())[i] = tmp; + // Reassure typedMem() that we won't GC while holding onto the returned + // pointer, even though we could GC on every iteration of this loop + // (but it is safe because we re-fetch each time.) + JS::AutoCheckCannotGC nogc(cx); + reinterpret_cast<Elem*>(result->typedMem(nogc))[i] = tmp; } args.rval().setObject(*result); return true; } bool SimdTypeDescr::call(JSContext* cx, unsigned argc, Value* vp) { @@ -634,17 +640,18 @@ js::CreateSimd(JSContext* cx, const type Rooted<TypeDescr*> typeDescr(cx, GetTypeDescr<V>(cx)); if (!typeDescr) return nullptr; Rooted<TypedObject*> result(cx, TypedObject::createZeroed(cx, typeDescr, 0)); if (!result) return nullptr; - Elem* resultMem = reinterpret_cast<Elem*>(result->typedMem()); + JS::AutoCheckCannotGC nogc(cx); + Elem* resultMem = reinterpret_cast<Elem*>(result->typedMem(nogc)); memcpy(resultMem, data, sizeof(Elem) * V::lanes); return result; } #define InstantiateCreateSimd_(Type) \ template JSObject* js::CreateSimd<Type>(JSContext* cx, const Type::Elem* data); FOR_EACH_SIMD(InstantiateCreateSimd_) @@ -830,31 +837,60 @@ StoreResult(JSContext* cx, CallArgs& arg { RootedObject obj(cx, CreateSimd<Out>(cx, result)); if (!obj) return false; args.rval().setObject(*obj); return true; } +// StoreResult can GC, and it is commonly used after pulling something out of a +// TypedObject: +// +// Elem result = op(TypedObjectMemory<Elem>(args[0])); +// StoreResult<Out>(..., result); +// +// The pointer extracted from the typed object in args[0] in the above example +// could be an interior pointer, and therefore be invalidated by GC. +// TypedObjectMemory() requires an assertion token to be passed in to prove +// that we won't GC, but the scope of eg an AutoCheckCannotGC RAII object +// extends to the end of its containing scope -- which would include the call +// to StoreResult, resulting in a rooting hazard. +// +// TypedObjectElemArray fixes this by wrapping the problematic pointer in a +// type, and the analysis is able to see that it is dead before calling +// StoreResult. (But if another GC called is made before the pointer is dead, +// it will correctly report a hazard.) +// +template <typename Elem> +class TypedObjectElemArray { + Elem* elements; + public: + explicit TypedObjectElemArray(HandleValue objVal) { + JS::AutoCheckCannotGC nogc; + elements = TypedObjectMemory<Elem*>(objVal, nogc); + } + Elem& operator[](int i) { return elements[i]; } +} JS_HAZ_GC_POINTER; + // Coerces the inputs of type In to the type Coercion, apply the operator Op // and converts the result to the type Out. template<typename In, typename Coercion, template<typename C> class Op, typename Out> static bool CoercedUnaryFunc(JSContext* cx, unsigned argc, Value* vp) { typedef typename Coercion::Elem CoercionElem; typedef typename Out::Elem RetElem; CallArgs args = CallArgsFromVp(argc, vp); if (args.length() != 1 || !IsVectorObject<In>(args[0])) return ErrorBadArgs(cx); CoercionElem result[Coercion::lanes]; - CoercionElem* val = TypedObjectMemory<CoercionElem*>(args[0]); + TypedObjectElemArray<CoercionElem> val(args[0]); for (unsigned i = 0; i < Coercion::lanes; i++) result[i] = Op<CoercionElem>::apply(val[i]); return StoreResult<Out>(cx, args, (RetElem*) result); } // Coerces the inputs of type In to the type Coercion, apply the operator Op // and converts the result to the type Out. template<typename In, typename Coercion, template<typename C> class Op, typename Out> @@ -864,18 +900,18 @@ CoercedBinaryFunc(JSContext* cx, unsigne typedef typename Coercion::Elem CoercionElem; typedef typename Out::Elem RetElem; CallArgs args = CallArgsFromVp(argc, vp); if (args.length() != 2 || !IsVectorObject<In>(args[0]) || !IsVectorObject<In>(args[1])) return ErrorBadArgs(cx); CoercionElem result[Coercion::lanes]; - CoercionElem* left = TypedObjectMemory<CoercionElem*>(args[0]); - CoercionElem* right = TypedObjectMemory<CoercionElem*>(args[1]); + TypedObjectElemArray<CoercionElem> left(args[0]); + TypedObjectElemArray<CoercionElem> right(args[1]); for (unsigned i = 0; i < Coercion::lanes; i++) result[i] = Op<CoercionElem>::apply(left[i], right[i]); return StoreResult<Out>(cx, args, (RetElem*) result); } // Same as above, with no coercion, i.e. Coercion == In. template<typename In, template<typename C> class Op, typename Out> static bool @@ -900,33 +936,35 @@ ExtractLane(JSContext* cx, unsigned argc CallArgs args = CallArgsFromVp(argc, vp); if (args.length() < 2 || !IsVectorObject<V>(args[0])) return ErrorBadArgs(cx); unsigned lane; if (!ArgumentToLaneIndex(cx, args[1], V::lanes, &lane)) return false; - Elem* vec = TypedObjectMemory<Elem*>(args[0]); + JS::AutoCheckCannotGC nogc(cx); + Elem* vec = TypedObjectMemory<Elem*>(args[0], nogc); Elem val = vec[lane]; args.rval().set(V::ToValue(val)); return true; } template<typename V> static bool AllTrue(JSContext* cx, unsigned argc, Value* vp) { typedef typename V::Elem Elem; CallArgs args = CallArgsFromVp(argc, vp); if (args.length() < 1 || !IsVectorObject<V>(args[0])) return ErrorBadArgs(cx); - Elem* vec = TypedObjectMemory<Elem*>(args[0]); + JS::AutoCheckCannotGC nogc(cx); + Elem* vec = TypedObjectMemory<Elem*>(args[0], nogc); bool allTrue = true; for (unsigned i = 0; allTrue && i < V::lanes; i++) allTrue = vec[i]; args.rval().setBoolean(allTrue); return true; } @@ -935,17 +973,18 @@ static bool AnyTrue(JSContext* cx, unsigned argc, Value* vp) { typedef typename V::Elem Elem; CallArgs args = CallArgsFromVp(argc, vp); if (args.length() < 1 || !IsVectorObject<V>(args[0])) return ErrorBadArgs(cx); - Elem* vec = TypedObjectMemory<Elem*>(args[0]); + JS::AutoCheckCannotGC nogc(cx); + Elem* vec = TypedObjectMemory<Elem*>(args[0], nogc); bool anyTrue = false; for (unsigned i = 0; !anyTrue && i < V::lanes; i++) anyTrue = vec[i]; args.rval().setBoolean(anyTrue); return true; } @@ -963,17 +1002,17 @@ ReplaceLane(JSContext* cx, unsigned argc unsigned lane; if (!ArgumentToLaneIndex(cx, args[1], V::lanes, &lane)) return false; Elem value; if (!V::Cast(cx, args.get(2), &value)) return false; - Elem* vec = TypedObjectMemory<Elem*>(args[0]); + TypedObjectElemArray<Elem> vec(args[0]); Elem result[V::lanes]; for (unsigned i = 0; i < V::lanes; i++) result[i] = i == lane ? value : vec[i]; return StoreResult<V>(cx, args, result); } template<typename V> @@ -987,18 +1026,17 @@ Swizzle(JSContext* cx, unsigned argc, Va return ErrorBadArgs(cx); unsigned lanes[V::lanes]; for (unsigned i = 0; i < V::lanes; i++) { if (!ArgumentToLaneIndex(cx, args[i + 1], V::lanes, &lanes[i])) return false; } - Elem* val = TypedObjectMemory<Elem*>(args[0]); - + TypedObjectElemArray<Elem> val(args[0]); Elem result[V::lanes]; for (unsigned i = 0; i < V::lanes; i++) result[i] = val[lanes[i]]; return StoreResult<V>(cx, args, result); } template<typename V> @@ -1012,23 +1050,26 @@ Shuffle(JSContext* cx, unsigned argc, Va return ErrorBadArgs(cx); unsigned lanes[V::lanes]; for (unsigned i = 0; i < V::lanes; i++) { if (!ArgumentToLaneIndex(cx, args[i + 2], 2 * V::lanes, &lanes[i])) return false; } - Elem* lhs = TypedObjectMemory<Elem*>(args[0]); - Elem* rhs = TypedObjectMemory<Elem*>(args[1]); + Elem result[V::lanes]; + { + JS::AutoCheckCannotGC nogc(cx); + Elem* lhs = TypedObjectMemory<Elem*>(args[0], nogc); + Elem* rhs = TypedObjectMemory<Elem*>(args[1], nogc); - Elem result[V::lanes]; - for (unsigned i = 0; i < V::lanes; i++) { - Elem* selectedInput = lanes[i] < V::lanes ? lhs : rhs; - result[i] = selectedInput[lanes[i] % V::lanes]; + for (unsigned i = 0; i < V::lanes; i++) { + Elem* selectedInput = lanes[i] < V::lanes ? lhs : rhs; + result[i] = selectedInput[lanes[i] % V::lanes]; + } } return StoreResult<V>(cx, args, result); } template<typename V, template<typename T> class Op> static bool BinaryScalar(JSContext* cx, unsigned argc, Value* vp) @@ -1041,18 +1082,18 @@ BinaryScalar(JSContext* cx, unsigned arg if (!IsVectorObject<V>(args[0])) return ErrorBadArgs(cx); int32_t bits; if (!ToInt32(cx, args[1], &bits)) return false; + TypedObjectElemArray<Elem> val(args[0]); Elem result[V::lanes]; - Elem* val = TypedObjectMemory<Elem*>(args[0]); for (unsigned i = 0; i < V::lanes; i++) result[i] = Op<Elem>::apply(val[i], bits); return StoreResult<V>(cx, args, result); } template<typename In, template<typename C> class Op, typename Out> static bool @@ -1061,18 +1102,18 @@ CompareFunc(JSContext* cx, unsigned argc typedef typename In::Elem InElem; typedef typename Out::Elem OutElem; CallArgs args = CallArgsFromVp(argc, vp); if (args.length() != 2 || !IsVectorObject<In>(args[0]) || !IsVectorObject<In>(args[1])) return ErrorBadArgs(cx); OutElem result[Out::lanes]; - InElem* left = TypedObjectMemory<InElem*>(args[0]); - InElem* right = TypedObjectMemory<InElem*>(args[1]); + TypedObjectElemArray<InElem> left(args[0]); + TypedObjectElemArray<InElem> right(args[1]); for (unsigned i = 0; i < Out::lanes; i++) { unsigned j = (i * In::lanes) / Out::lanes; result[i] = Op<InElem>::apply(left[j], right[j]) ? -1 : 0; } return StoreResult<Out>(cx, args, result); } @@ -1156,18 +1197,17 @@ FuncConvert(JSContext* cx, unsigned argc static_assert(V::lanes == Vret::lanes, "Can only convert from same number of lanes"); static_assert(!mozilla::IsIntegral<Elem>::value || !mozilla::IsIntegral<RetElem>::value, "Cannot convert between integer SIMD types"); CallArgs args = CallArgsFromVp(argc, vp); if (args.length() != 1 || !IsVectorObject<V>(args[0])) return ErrorBadArgs(cx); - Elem* val = TypedObjectMemory<Elem*>(args[0]); - + TypedObjectElemArray<Elem> val(args[0]); RetElem result[Vret::lanes]; for (unsigned i = 0; i < V::lanes; i++) { if (ThrowOnConvert<Elem, RetElem>::value(val[i])) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_SIMD_FAILED_CONVERSION); return false; } result[i] = ConvertScalar<RetElem>(val[i]); } @@ -1190,17 +1230,20 @@ FuncConvertBits(JSContext* cx, unsigned if (args.length() != 1 || !IsVectorObject<V>(args[0])) return ErrorBadArgs(cx); // While we could just pass the typedMem of args[0] as StoreResults' last // argument, a GC could move the pointer to its memory in the meanwhile. // For consistency with other SIMD functions, simply copy the input in a // temporary array. RetElem copy[Vret::lanes]; - memcpy(copy, TypedObjectMemory<RetElem*>(args[0]), Vret::lanes * sizeof(RetElem)); + { + JS::AutoCheckCannotGC nogc(cx); + memcpy(copy, TypedObjectMemory<RetElem*>(args[0], nogc), Vret::lanes * sizeof(RetElem)); + } return StoreResult<Vret>(cx, args, copy); } template<typename Vret> static bool FuncSplat(JSContext* cx, unsigned argc, Value* vp) { typedef typename Vret::Elem RetElem; @@ -1239,19 +1282,19 @@ SelectBits(JSContext* cx, unsigned argc, CallArgs args = CallArgsFromVp(argc, vp); if (args.length() != 3 || !IsVectorObject<MaskType>(args[0]) || !IsVectorObject<V>(args[1]) || !IsVectorObject<V>(args[2])) { return ErrorBadArgs(cx); } - MaskTypeElem* val = TypedObjectMemory<MaskTypeElem*>(args[0]); - MaskTypeElem* tv = TypedObjectMemory<MaskTypeElem*>(args[1]); - MaskTypeElem* fv = TypedObjectMemory<MaskTypeElem*>(args[2]); + TypedObjectElemArray<MaskTypeElem> val(args[0]); + TypedObjectElemArray<MaskTypeElem> tv(args[1]); + TypedObjectElemArray<MaskTypeElem> fv(args[2]); MaskTypeElem tr[MaskType::lanes]; for (unsigned i = 0; i < MaskType::lanes; i++) tr[i] = And<MaskTypeElem>::apply(val[i], tv[i]); MaskTypeElem fr[MaskType::lanes]; for (unsigned i = 0; i < MaskType::lanes; i++) fr[i] = And<MaskTypeElem>::apply(Not<MaskTypeElem>::apply(val[i]), fv[i]); @@ -1273,19 +1316,19 @@ Select(JSContext* cx, unsigned argc, Val CallArgs args = CallArgsFromVp(argc, vp); if (args.length() != 3 || !IsVectorObject<MaskType>(args[0]) || !IsVectorObject<V>(args[1]) || !IsVectorObject<V>(args[2])) { return ErrorBadArgs(cx); } - MaskTypeElem* mask = TypedObjectMemory<MaskTypeElem*>(args[0]); - Elem* tv = TypedObjectMemory<Elem*>(args[1]); - Elem* fv = TypedObjectMemory<Elem*>(args[2]); + TypedObjectElemArray<MaskTypeElem> mask(args[0]); + TypedObjectElemArray<Elem> tv(args[1]); + TypedObjectElemArray<Elem> fv(args[2]); Elem result[V::lanes]; for (unsigned i = 0; i < V::lanes; i++) result[i] = mask[i] ? tv[i] : fv[i]; return StoreResult<V>(cx, args, result); } @@ -1356,19 +1399,20 @@ Load(JSContext* cx, unsigned argc, Value Rooted<TypeDescr*> typeDescr(cx, GetTypeDescr<V>(cx)); if (!typeDescr) return false; Rooted<TypedObject*> result(cx, TypedObject::createZeroed(cx, typeDescr, 0)); if (!result) return false; + JS::AutoCheckCannotGC nogc(cx); SharedMem<Elem*> src = typedArray->as<TypedArrayObject>().viewDataEither().addBytes(byteStart).cast<Elem*>(); - Elem* dst = reinterpret_cast<Elem*>(result->typedMem()); + Elem* dst = reinterpret_cast<Elem*>(result->typedMem(nogc)); jit::AtomicOperations::podCopySafeWhenRacy(SharedMem<Elem*>::unshared(dst), src, NumElem); args.rval().setObject(*result); return true; } template<class V, unsigned NumElem> static bool @@ -1383,17 +1427,18 @@ Store(JSContext* cx, unsigned argc, Valu size_t byteStart; RootedObject typedArray(cx); if (!TypedArrayFromArgs(cx, args, sizeof(Elem) * NumElem, &typedArray, &byteStart)) return false; if (!IsVectorObject<V>(args[2])) return ErrorBadArgs(cx); - Elem* src = TypedObjectMemory<Elem*>(args[2]); + JS::AutoCheckCannotGC nogc(cx); + Elem* src = TypedObjectMemory<Elem*>(args[2], nogc); SharedMem<Elem*> dst = typedArray->as<TypedArrayObject>().viewDataEither().addBytes(byteStart).cast<Elem*>(); js::jit::AtomicOperations::podCopySafeWhenRacy(dst, SharedMem<Elem*>::unshared(src), NumElem); args.rval().setObject(args[2].toObject()); return true; }
--- a/js/src/builtin/TypedObject.cpp +++ b/js/src/builtin/TypedObject.cpp @@ -1506,17 +1506,18 @@ OutlineTypedObject::attach(JSContext* cx owner = &typedObj.as<OutlineTypedObject>().owner(); offset += typedObj.offset(); } if (owner->is<ArrayBufferObject>()) { attach(cx, owner->as<ArrayBufferObject>(), offset); } else { MOZ_ASSERT(owner->is<InlineTypedObject>()); - setOwnerAndData(owner, owner->as<InlineTypedObject>().inlineTypedMem() + offset); + JS::AutoCheckCannotGC nogc(cx); + setOwnerAndData(owner, owner->as<InlineTypedObject>().inlineTypedMem(nogc) + offset); } } // Returns a suitable JS_TYPEDOBJ_SLOT_LENGTH value for an instance of // the type `type`. static int32_t TypedObjLengthFromType(TypeDescr& descr) { @@ -1559,17 +1560,18 @@ TypedObject::createZeroed(JSContext* cx, { // If possible, create an object with inline data. if ((size_t) descr->size() <= InlineTypedObject::MaximumSize) { AutoSetNewObjectMetadata metadata(cx); InlineTypedObject* obj = InlineTypedObject::create(cx, descr, heap); if (!obj) return nullptr; - descr->initInstances(cx->runtime(), obj->inlineTypedMem(), 1); + JS::AutoCheckCannotGC nogc(cx); + descr->initInstances(cx->runtime(), obj->inlineTypedMem(nogc), 1); return obj; } // Create unattached wrapper object. Rooted<OutlineTypedObject*> obj(cx, OutlineTypedObject::createUnattached(cx, descr, length, heap)); if (!obj) return nullptr; @@ -2450,17 +2452,17 @@ js::SetTypedObjectOffset(JSContext*, uns MOZ_ASSERT(args.length() == 2); MOZ_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>()); MOZ_ASSERT(args[1].isInt32()); OutlineTypedObject& typedObj = args[0].toObject().as<OutlineTypedObject>(); int32_t offset = args[1].toInt32(); MOZ_ASSERT(typedObj.isAttached()); - typedObj.setData(typedObj.typedMemBase() + offset); + typedObj.resetOffset(offset); args.rval().setUndefined(); return true; } bool js::ObjectIsTypeDescr(JSContext*, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); @@ -2574,31 +2576,32 @@ js::GetSimdTypeDescr(JSContext* cx, unsi MOZ_ASSERT(global); auto* obj = GlobalObject::getOrCreateSimdTypeDescr(cx, global, SimdType(simdTypeRepr)); args.rval().setObject(*obj); return true; } #define JS_STORE_SCALAR_CLASS_IMPL(_constant, T, _name) \ bool \ -js::StoreScalar##T::Func(JSContext*, unsigned argc, Value* vp) \ +js::StoreScalar##T::Func(JSContext* cx, unsigned argc, Value* vp) \ { \ CallArgs args = CallArgsFromVp(argc, vp); \ MOZ_ASSERT(args.length() == 3); \ MOZ_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>()); \ MOZ_ASSERT(args[1].isInt32()); \ MOZ_ASSERT(args[2].isNumber()); \ \ TypedObject& typedObj = args[0].toObject().as<TypedObject>(); \ int32_t offset = args[1].toInt32(); \ \ /* Should be guaranteed by the typed objects API: */ \ MOZ_ASSERT(offset % MOZ_ALIGNOF(T) == 0); \ \ - T* target = reinterpret_cast<T*>(typedObj.typedMem(offset)); \ + JS::AutoCheckCannotGC nogc(cx); \ + T* target = reinterpret_cast<T*>(typedObj.typedMem(offset, nogc)); \ double d = args[2].toNumber(); \ *target = ConvertScalar<T>(d); \ args.rval().setUndefined(); \ return true; \ } #define JS_STORE_REFERENCE_CLASS_IMPL(_constant, T, _name) \ bool \ @@ -2615,59 +2618,62 @@ js::StoreReference##_name::Func(JSContex \ jsid id = args[2].isString() \ ? IdToTypeId(AtomToId(&args[2].toString()->asAtom())) \ : JSID_VOID; \ \ /* Should be guaranteed by the typed objects API: */ \ MOZ_ASSERT(offset % MOZ_ALIGNOF(T) == 0); \ \ - T* target = reinterpret_cast<T*>(typedObj.typedMem(offset)); \ + JS::AutoCheckCannotGC nogc(cx); \ + T* target = reinterpret_cast<T*>(typedObj.typedMem(offset, nogc)); \ if (!store(cx, target, args[3], &typedObj, id)) \ return false; \ args.rval().setUndefined(); \ return true; \ } #define JS_LOAD_SCALAR_CLASS_IMPL(_constant, T, _name) \ bool \ -js::LoadScalar##T::Func(JSContext*, unsigned argc, Value* vp) \ +js::LoadScalar##T::Func(JSContext* cx, unsigned argc, Value* vp) \ { \ CallArgs args = CallArgsFromVp(argc, vp); \ MOZ_ASSERT(args.length() == 2); \ MOZ_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>()); \ MOZ_ASSERT(args[1].isInt32()); \ \ TypedObject& typedObj = args[0].toObject().as<TypedObject>(); \ int32_t offset = args[1].toInt32(); \ \ /* Should be guaranteed by the typed objects API: */ \ MOZ_ASSERT(offset % MOZ_ALIGNOF(T) == 0); \ \ - T* target = reinterpret_cast<T*>(typedObj.typedMem(offset)); \ + JS::AutoCheckCannotGC nogc(cx); \ + T* target = reinterpret_cast<T*>(typedObj.typedMem(offset, nogc)); \ args.rval().setNumber((double) *target); \ return true; \ } #define JS_LOAD_REFERENCE_CLASS_IMPL(_constant, T, _name) \ bool \ -js::LoadReference##_name::Func(JSContext*, unsigned argc, Value* vp) \ +js::LoadReference##_name::Func(JSContext* cx, unsigned argc, Value* vp) \ { \ CallArgs args = CallArgsFromVp(argc, vp); \ MOZ_ASSERT(args.length() == 2); \ MOZ_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>()); \ MOZ_ASSERT(args[1].isInt32()); \ \ TypedObject& typedObj = args[0].toObject().as<TypedObject>(); \ int32_t offset = args[1].toInt32(); \ \ /* Should be guaranteed by the typed objects API: */ \ MOZ_ASSERT(offset % MOZ_ALIGNOF(T) == 0); \ \ - T* target = reinterpret_cast<T*>(typedObj.typedMem(offset)); \ + JS::AutoCheckCannotGC nogc(cx); \ + T* target = reinterpret_cast<T*>(typedObj.typedMem(offset, nogc)); \ load(target, args.rval()); \ return true; \ } // Because the precise syntax for storing values/objects/strings // differs, we abstract it away using specialized variants of the // private methods `store()` and `load()`.
--- a/js/src/builtin/TypedObject.h +++ b/js/src/builtin/TypedObject.h @@ -528,44 +528,47 @@ class TypedObject : public ShapedObject MutableHandle<PropertyDescriptor> desc); static MOZ_MUST_USE bool obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id, ObjectOpResult& result); static MOZ_MUST_USE bool obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties, bool enumerableOnly); + + uint8_t* typedMem() const; + uint8_t* typedMemBase() const; + public: TypedProto& typedProto() const { // Typed objects' prototypes can't be modified. return staticPrototype()->as<TypedProto>(); } TypeDescr& typeDescr() const { return group()->typeDescr(); } int32_t offset() const; int32_t length() const; - uint8_t* typedMem() const; - uint8_t* typedMemBase() const; + uint8_t* typedMem(const JS::AutoAssertOnGC&) const { return typedMem(); } bool isAttached() const; int32_t size() const { return typeDescr().size(); } - uint8_t* typedMem(size_t offset) const { + uint8_t* typedMem(size_t offset, const JS::AutoAssertOnGC& nogc) const { // It seems a bit surprising that one might request an offset // == size(), but it can happen when taking the "address of" a // 0-sized value. (In other words, we maintain the invariant // that `offset + size <= size()` -- this is always checked in // the caller's side.) MOZ_ASSERT(offset <= (size_t) size()); - return typedMem() + offset; + return typedMem(nogc) + offset; } inline MOZ_MUST_USE bool opaque() const; // Creates a new typed object whose memory is freshly allocated and // initialized with zeroes (or, in the case of references, an appropriate // default value). static TypedObject* createZeroed(JSContext* cx, HandleTypeDescr typeObj, int32_t length, @@ -613,16 +616,21 @@ class OutlineTypedObject : public TypedO uint8_t* outOfLineTypedMem() const { return data_; } void setData(uint8_t* data) { data_ = data; } + void resetOffset(size_t offset) { + MOZ_ASSERT(offset <= (size_t) size()); + setData(typedMemBase() + offset); + } + // Helper for createUnattached() static OutlineTypedObject* createUnattachedWithClass(JSContext* cx, const Class* clasp, HandleTypeDescr type, int32_t length, gc::InitialHeap heap = gc::DefaultHeap); // Creates an unattached typed object or handle (depending on the @@ -671,31 +679,42 @@ class OutlineOpaqueTypedObject : public { public: static const Class class_; }; // Class for a typed object whose data is allocated inline. class InlineTypedObject : public TypedObject { + friend class TypedObject; + // Start of the inline data, which immediately follows the shape and type. uint8_t data_[1]; + protected: + uint8_t* inlineTypedMem() const { + return (uint8_t*) &data_; + } + public: static const size_t MaximumSize = JSObject::MAX_BYTE_SIZE - sizeof(TypedObject); static gc::AllocKind allocKindForTypeDescriptor(TypeDescr* descr) { size_t nbytes = descr->size(); MOZ_ASSERT(nbytes <= MaximumSize); return gc::GetGCObjectKindForBytes(nbytes + sizeof(TypedObject)); } - uint8_t* inlineTypedMem() const { - return (uint8_t*) &data_; + uint8_t* inlineTypedMem(const JS::AutoAssertOnGC&) const { + return inlineTypedMem(); + } + + uint8_t* inlineTypedMemForGC() const { + return inlineTypedMem(); } static void obj_trace(JSTracer* trace, JSObject* object); static void objectMovedDuringMinorGC(JSTracer* trc, JSObject* dst, JSObject* src); static size_t offsetOfDataStart() { return offsetof(InlineTypedObject, data_); } @@ -709,16 +728,20 @@ class InlineTypedObject : public TypedOb // Class for a transparent typed object with inline data, which may have a // lazily allocated array buffer. class InlineTransparentTypedObject : public InlineTypedObject { public: static const Class class_; ArrayBufferObject* getOrCreateBuffer(JSContext* cx); + + uint8_t* inlineTypedMem() const { + return InlineTypedObject::inlineTypedMem(); + } }; // Class for an opaque typed object with inline data and no array buffer. class InlineOpaqueTypedObject : public InlineTypedObject { public: static const Class class_; };
--- a/js/src/gc/Marking.cpp +++ b/js/src/gc/Marking.cpp @@ -1475,17 +1475,17 @@ CallTraceHook(Functor f, JSTracer* trc, return &obj->as<NativeObject>(); if (clasp->isTrace(InlineTypedObject::obj_trace)) { Shape** pshape = obj->as<InlineTypedObject>().addressOfShapeFromGC(); f(pshape, mozilla::Forward<Args>(args)...); InlineTypedObject& tobj = obj->as<InlineTypedObject>(); if (tobj.typeDescr().hasTraceList()) { - VisitTraceList(f, tobj.typeDescr().traceList(), tobj.inlineTypedMem(), + VisitTraceList(f, tobj.typeDescr().traceList(), tobj.inlineTypedMemForGC(), mozilla::Forward<Args>(args)...); } return nullptr; } if (clasp == &UnboxedPlainObject::class_) { JSObject** pexpando = obj->as<UnboxedPlainObject>().addressOfExpando();
--- a/js/src/jit-test/lib/wasm.js +++ b/js/src/jit-test/lib/wasm.js @@ -108,8 +108,31 @@ function createI64(val) { val = val.slice(2).padStart(16, '0'); ret = { low: parseInt(val.slice(8, 16), 16), high: parseInt(val.slice(0, 8), 16) }; } return ret; } + +// Fully test a module: +// - ensure it validates. +// - ensure it compiles and produces the expected result. +// - ensure textToBinary(binaryToText(binary)) = binary +// Preconditions: +// - the binary module must export a function called "run". +function wasmFullPass(text, expected, maybeImports) { + let binary = wasmTextToBinary(text); + assertEq(WebAssembly.validate(binary), true, "Must validate."); + + let module = new WebAssembly.Module(binary); + let instance = new WebAssembly.Instance(module, maybeImports); + assertEq(instance.exports.run(), expected, "Initial module must return the expected result."); + + let retext = wasmBinaryToText(binary); + let rebinary = wasmTextToBinary(retext); + + assertEq(WebAssembly.validate(rebinary), true, "Recreated binary must validate."); + let remodule = new WebAssembly.Module(rebinary); + let reinstance = new WebAssembly.Instance(remodule, maybeImports); + assertEq(reinstance.exports.run(), expected, "Reformed module must return the expected result"); +}
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/debug/bug1302432.js @@ -0,0 +1,10 @@ +setJitCompilerOption('ion.warmup.trigger', 0); +gczeal(7, 1); +var dbgGlobal = newGlobal(); +var dbg = new dbgGlobal.Debugger(); +dbg.addDebuggee(this); +function f(x, await = () => Array.isArray(revocable.proxy), ...get) { + dbg.getNewestFrame().older.eval("print(a)"); +} +function a() {} +for (var i = 0; i < 10; i++) f();
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/wasm/full-cycle.js @@ -0,0 +1,8 @@ +// |jit-test| test-also-wasm-baseline +load(libdir + "wasm.js"); + +wasmFullPass(`(module + (func $test (result i32) (param i32) (param i32) (i32.add (get_local 0) (get_local 1))) + (func $run (result i32) (call $test (i32.const 1) (i32.const ${Math.pow(2, 31) - 1}))) + (export "run" $run) +)`, -Math.pow(2, 31));
--- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -2554,16 +2554,21 @@ jit::CanEnter(JSContext* cx, RunState& s // If --ion-eager is used, compile with Baseline first, so that we // can directly enter IonMonkey. if (JitOptions.eagerCompilation && !rscript->hasBaselineScript()) { MethodStatus status = CanEnterBaselineMethod(cx, state); if (status != Method_Compiled) return status; } + // Skip if the script is being compiled off thread (again). + // MaybeCreateThisForConstructor could have started an ion compilation. + if (rscript->isIonCompilingOffThread()) + return Method_Skipped; + // Attempt compilation. Returns Method_Compiled if already compiled. bool constructing = state.isInvoke() && state.asInvoke()->constructing(); MethodStatus status = Compile(cx, rscript, nullptr, nullptr, constructing); if (status != Method_Compiled) { if (status == Method_CantCompile) ForbidCompilation(cx, rscript); return status; }
--- a/js/src/jit/MacroAssembler.cpp +++ b/js/src/jit/MacroAssembler.cpp @@ -1256,18 +1256,19 @@ MacroAssembler::initGCThing(Register obj if (ntemplate->hasPrivate() && !ntemplate->is<TypedArrayObject>()) { uint32_t nfixed = ntemplate->numFixedSlots(); storePtr(ImmPtr(ntemplate->getPrivate()), Address(obj, NativeObject::getPrivateDataOffset(nfixed))); } } } else if (templateObj->is<InlineTypedObject>()) { + JS::AutoAssertOnGC nogc; // off-thread, so cannot GC size_t nbytes = templateObj->as<InlineTypedObject>().size(); - const uint8_t* memory = templateObj->as<InlineTypedObject>().inlineTypedMem(); + const uint8_t* memory = templateObj->as<InlineTypedObject>().inlineTypedMem(nogc); // Memcpy the contents of the template object to the new object. size_t offset = 0; while (nbytes) { uintptr_t value = *(uintptr_t*)(memory + offset); storePtr(ImmWord(value), Address(obj, InlineTypedObject::offsetOfDataStart() + offset)); nbytes = (nbytes < sizeof(uintptr_t)) ? 0 : nbytes - sizeof(uintptr_t);
--- a/js/src/jit/RematerializedFrame.cpp +++ b/js/src/jit/RematerializedFrame.cpp @@ -142,17 +142,17 @@ RematerializedFrame::trace(JSTracer* trc TraceRoot(trc, &script_, "remat ion frame script"); TraceRoot(trc, &envChain_, "remat ion frame env chain"); if (callee_) TraceRoot(trc, &callee_, "remat ion frame callee"); if (argsObj_) TraceRoot(trc, &argsObj_, "remat ion frame argsobj"); TraceRoot(trc, &returnValue_, "remat ion frame return value"); TraceRoot(trc, &thisArgument_, "remat ion frame this"); - TraceRootRange(trc, numActualArgs_ + isConstructing_ + script_->nfixed(), + TraceRootRange(trc, numArgSlots() + isConstructing_ + script_->nfixed(), slots_, "remat ion frame stack"); } void RematerializedFrame::dump() { fprintf(stderr, " Rematerialized Ion Frame%s\n", inlined() ? " (inlined)" : ""); if (isFunctionFrame()) {
--- a/js/src/jit/arm/Assembler-arm.cpp +++ b/js/src/jit/arm/Assembler-arm.cpp @@ -3396,16 +3396,18 @@ uint32_t Assembler::GetNopFill() { static bool isSet = false; if (!isSet) { char* fillStr = getenv("ARM_ASM_NOP_FILL"); uint32_t fill; if (fillStr && sscanf(fillStr, "%u", &fill) == 1) NopFill = fill; + if (NopFill > 8) + MOZ_CRASH("Nop fill > 8 is not supported"); isSet = true; } return NopFill; } uint32_t Assembler::AsmPoolMaxOffset = 1024; uint32_t
--- a/js/src/jit/arm/Assembler-arm.h +++ b/js/src/jit/arm/Assembler-arm.h @@ -493,21 +493,21 @@ struct Imm8mData public: uint32_t encode() const { MOZ_ASSERT(!invalid); return data | rot << 8; }; // Default constructor makes an invalid immediate. Imm8mData() - : data(0xff), rot(0xf), invalid(1) + : data(0xff), rot(0xf), buff(0), invalid(1) { } Imm8mData(uint32_t data_, uint32_t rot_) - : data(data_), rot(rot_), invalid(0) + : data(data_), rot(rot_), buff(0), invalid(0) { MOZ_ASSERT(data == data_); MOZ_ASSERT(rot == rot_); } }; struct Imm8Data {
--- a/js/src/jit/x86-shared/Assembler-x86-shared.cpp +++ b/js/src/jit/x86-shared/Assembler-x86-shared.cpp @@ -46,39 +46,41 @@ AssemblerX86Shared::copyPreBarrierTable( memcpy(dest, preBarriers_.buffer(), preBarriers_.length()); } static void TraceDataRelocations(JSTracer* trc, uint8_t* buffer, CompactBufferReader& reader) { while (reader.more()) { size_t offset = reader.readUnsigned(); - void** ptr = X86Encoding::GetPointerRef(buffer + offset); + void* ptr = X86Encoding::GetPointer(buffer + offset); #ifdef JS_PUNBOX64 // All pointers on x64 will have the top bits cleared. If those bits // are not cleared, this must be a Value. - uintptr_t* word = reinterpret_cast<uintptr_t*>(ptr); - if (*word >> JSVAL_TAG_SHIFT) { + uintptr_t word = reinterpret_cast<uintptr_t>(ptr); + if (word >> JSVAL_TAG_SHIFT) { jsval_layout layout; - layout.asBits = *word; + layout.asBits = word; Value v = IMPL_TO_JSVAL(layout); - TraceManuallyBarrieredEdge(trc, &v, "ion-masm-value"); - if (*word != JSVAL_TO_IMPL(v).asBits) { + TraceManuallyBarrieredEdge(trc, &v, "jit-masm-value"); + if (word != JSVAL_TO_IMPL(v).asBits) { // Only update the code if the Value changed, because the code // is not writable if we're not moving objects. - *word = JSVAL_TO_IMPL(v).asBits; + X86Encoding::SetPointer(buffer + offset, (void*)JSVAL_TO_IMPL(v).asBits); } continue; } #endif // No barrier needed since these are constants. - TraceManuallyBarrieredGenericPointerEdge(trc, reinterpret_cast<gc::Cell**>(ptr), - "ion-masm-ptr"); + gc::Cell* cellPtr = reinterpret_cast<gc::Cell*>(ptr); + TraceManuallyBarrieredGenericPointerEdge(trc, &cellPtr, "jit-masm-ptr"); + if (cellPtr != ptr) + X86Encoding::SetPointer(buffer + offset, cellPtr); } } void AssemblerX86Shared::TraceDataRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader) { ::TraceDataRelocations(trc, code->raw(), reader);
--- a/js/src/jit/x86-shared/Patching-x86-shared.h +++ b/js/src/jit/x86-shared/Patching-x86-shared.h @@ -10,41 +10,39 @@ namespace js { namespace jit { namespace X86Encoding { inline void* GetPointer(const void* where) { - return reinterpret_cast<void* const*>(where)[-1]; -} - -inline void** -GetPointerRef(void* where) -{ - return &reinterpret_cast<void**>(where)[-1]; + void* res; + memcpy(&res, (const char*)where - sizeof(void*), sizeof(void*)); + return res; } inline void SetPointer(void* where, const void* value) { - reinterpret_cast<const void**>(where)[-1] = value; + memcpy((char*)where - sizeof(void*), &value, sizeof(void*)); } inline int32_t GetInt32(const void* where) { - return reinterpret_cast<const int32_t*>(where)[-1]; + int32_t res; + memcpy(&res, (const char*)where - sizeof(int32_t), sizeof(int32_t)); + return res; } inline void SetInt32(void* where, int32_t value) { - reinterpret_cast<int32_t*>(where)[-1] = value; + memcpy((char*)where - sizeof(int32_t), &value, sizeof(int32_t)); } inline void SetRel32(void* from, void* to) { intptr_t offset = reinterpret_cast<intptr_t>(to) - reinterpret_cast<intptr_t>(from); MOZ_ASSERT(offset == static_cast<int32_t>(offset), "offset is too great for a 32-bit relocation");
--- a/js/src/vm/ArrayBufferObject.cpp +++ b/js/src/vm/ArrayBufferObject.cpp @@ -341,17 +341,18 @@ void ArrayBufferObject::changeViewContents(JSContext* cx, ArrayBufferViewObject* view, uint8_t* oldDataPointer, BufferContents newContents) { MOZ_ASSERT(!view->isSharedMemory()); // Watch out for NULL data pointers in views. This means that the view // is not fully initialized (in which case it'll be initialized later // with the correct pointer). - uint8_t* viewDataPointer = view->dataPointerUnshared(); + JS::AutoCheckCannotGC nogc(cx); + uint8_t* viewDataPointer = view->dataPointerUnshared(nogc); if (viewDataPointer) { MOZ_ASSERT(newContents); ptrdiff_t offset = viewDataPointer - oldDataPointer; viewDataPointer = static_cast<uint8_t*>(newContents.data()) + offset; view->setDataPointerUnshared(viewDataPointer); } // Notify compiled jit code that the base pointer has moved. @@ -1443,17 +1444,17 @@ ArrayBufferViewObject::trace(JSTracer* t JSObject* view = buf.firstView(); // Mark the object to move it into the tenured space. TraceManuallyBarrieredEdge(trc, &view, "typed array nursery owner"); MOZ_ASSERT(view->is<InlineTypedObject>()); MOZ_ASSERT(view != obj); void* srcData = obj->getPrivate(); - void* dstData = view->as<InlineTypedObject>().inlineTypedMem() + offset; + void* dstData = view->as<InlineTypedObject>().inlineTypedMemForGC() + offset; obj->setPrivateUnbarriered(dstData); // We can't use a direct forwarding pointer here, as there might // not be enough bytes available, and other views might have data // pointers whose forwarding pointers would overlap this one. trc->runtime()->gc.nursery.maybeSetForwardingPointer(trc, srcData, dstData, /* direct = */ false); } else { @@ -1492,25 +1493,25 @@ ArrayBufferViewObject::notifyBufferDetac return; as<TypedArrayObject>().notifyBufferDetached(cx, newData); } else { as<OutlineTypedObject>().notifyBufferDetached(newData); } } uint8_t* -ArrayBufferViewObject::dataPointerUnshared() +ArrayBufferViewObject::dataPointerUnshared(const JS::AutoAssertOnGC& nogc) { if (is<DataViewObject>()) return static_cast<uint8_t*>(as<DataViewObject>().dataPointer()); if (is<TypedArrayObject>()) { MOZ_ASSERT(!as<TypedArrayObject>().isSharedMemory()); return static_cast<uint8_t*>(as<TypedArrayObject>().viewDataUnshared()); } - return as<TypedObject>().typedMem(); + return as<TypedObject>().typedMem(nogc); } #ifdef DEBUG bool ArrayBufferViewObject::isSharedMemory() { if (is<TypedArrayObject>()) return as<TypedArrayObject>().isSharedMemory();
--- a/js/src/vm/ArrayBufferObject.h +++ b/js/src/vm/ArrayBufferObject.h @@ -435,17 +435,17 @@ class ArrayBufferViewObject : public JSO void notifyBufferDetached(JSContext* cx, void* newData); #ifdef DEBUG bool isSharedMemory(); #endif // By construction we only need unshared variants here. See // comments in ArrayBufferObject.cpp. - uint8_t* dataPointerUnshared(); + uint8_t* dataPointerUnshared(const JS::AutoAssertOnGC&); void setDataPointerUnshared(uint8_t* data); static void trace(JSTracer* trc, JSObject* obj); }; bool ToClampedIndex(JSContext* cx, HandleValue v, uint32_t length, uint32_t* out);
--- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -7206,17 +7206,17 @@ DebuggerFrame::initClass(JSContext* cx, } /* static */ DebuggerFrame* DebuggerFrame::create(JSContext* cx, HandleObject proto, AbstractFramePtr referent, const ScriptFrameIter* maybeIter, HandleNativeObject debugger) { JSObject* obj = NewObjectWithGivenProto(cx, &DebuggerFrame::class_, proto); if (!obj) - return nullptr; + return nullptr; DebuggerFrame& frame = obj->as<DebuggerFrame>(); // Eagerly copy ScriptFrameIter data if we've already walked the stack. if (maybeIter) { AbstractFramePtr data = maybeIter->copyDataAsAbstractFramePtr(); if (!data) return nullptr;
--- a/js/src/vm/TypeInference.cpp +++ b/js/src/vm/TypeInference.cpp @@ -147,17 +147,18 @@ TypeSet::TypeString(TypeSet::Type type) /* static */ const char* TypeSet::ObjectGroupString(ObjectGroup* group) { return TypeString(TypeSet::ObjectType(group)); } #ifdef DEBUG -static bool InferSpewActive(SpewChannel channel) +bool +js::InferSpewActive(SpewChannel channel) { static bool active[SPEW_COUNT]; static bool checked = false; if (!checked) { checked = true; PodArrayZero(active); const char* env = getenv("INFERFLAGS"); if (!env) @@ -217,29 +218,28 @@ js::InferSpewColor(TypeSet* types) static const char * const colors[] = { "\x1b[1;31m", "\x1b[1;32m", "\x1b[1;33m", "\x1b[1;34m", "\x1b[1;35m", "\x1b[1;36m", "\x1b[1;37m" }; if (!InferSpewColorable()) return ""; return colors[DefaultHasher<TypeSet*>::hash(types) % 7]; } +#ifdef DEBUG void -js::InferSpew(SpewChannel channel, const char* fmt, ...) +js::InferSpewImpl(const char* fmt, ...) { - if (!InferSpewActive(channel)) - return; - va_list ap; va_start(ap, fmt); fprintf(stderr, "[infer] "); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); va_end(ap); } +#endif MOZ_NORETURN MOZ_COLD static void TypeFailure(JSContext* cx, const char* fmt, ...) { char msgbuf[1024]; /* Larger error messages will be truncated */ char errbuf[1024]; va_list ap;
--- a/js/src/vm/TypeInference.h +++ b/js/src/vm/TypeInference.h @@ -1285,31 +1285,34 @@ struct TypeZone enum SpewChannel { ISpewOps, /* ops: New constraints and types. */ ISpewResult, /* result: Final type sets. */ SPEW_COUNT }; #ifdef DEBUG +bool InferSpewActive(SpewChannel channel); const char * InferSpewColorReset(); const char * InferSpewColor(TypeConstraint* constraint); const char * InferSpewColor(TypeSet* types); -void InferSpew(SpewChannel which, const char* fmt, ...); +#define InferSpew(channel, ...) if (InferSpewActive(channel)) { InferSpewImpl(__VA_ARGS__); } else {} +void InferSpewImpl(const char* fmt, ...); /* Check that the type property for id in group contains value. */ bool ObjectGroupHasProperty(JSContext* cx, ObjectGroup* group, jsid id, const Value& value); #else inline const char * InferSpewColorReset() { return nullptr; } inline const char * InferSpewColor(TypeConstraint* constraint) { return nullptr; } inline const char * InferSpewColor(TypeSet* types) { return nullptr; } -inline void InferSpew(SpewChannel which, const char* fmt, ...) {} + +#define InferSpew(channel, ...) do {} while (0) #endif // Prints type information for a context if spew is enabled or force is set. void PrintTypes(JSContext* cx, JSCompartment* comp, bool force); } /* namespace js */
--- a/layout/base/nsBidi.cpp +++ b/layout/base/nsBidi.cpp @@ -1634,25 +1634,16 @@ nsresult nsBidi::GetDirection(nsBidiDire } nsresult nsBidi::GetParaLevel(nsBidiLevel* aParaLevel) { *aParaLevel = mParaLevel; return NS_OK; } -nsresult nsBidi::GetCharTypeAt(int32_t aCharIndex, nsCharType* pType) -{ - if(aCharIndex<0 || mLength<=aCharIndex) { - return NS_ERROR_INVALID_ARG; - } - *pType = (nsCharType)mDirProps[aCharIndex]; - return NS_OK; -} - nsresult nsBidi::GetLogicalRun(int32_t aLogicalStart, int32_t *aLogicalLimit, nsBidiLevel *aLevel) { int32_t length = mLength; if(aLogicalStart<0 || length<=aLogicalStart) { return NS_ERROR_INVALID_ARG; }
--- a/layout/base/nsBidi.h +++ b/layout/base/nsBidi.h @@ -524,25 +524,16 @@ public: * * @param aParaLevel receives a <code>NSBIDI_XXX</code> value indicating the paragraph level * * @see nsBidiLevel */ nsresult GetParaLevel(nsBidiLevel* aParaLevel); /** - * Get the bidirectional type for one character. - * - * @param aCharIndex the index of a character. - * - * @param aType receives the bidirectional type of the character at aCharIndex. - */ - nsresult GetCharTypeAt(int32_t aCharIndex, nsCharType* aType); - - /** * Get a logical run. * This function returns information about a run and is used * to retrieve runs in logical order.<p> * This is especially useful for line-breaking on a paragraph. * * @param aLogicalStart is the first character of the run. * * @param aLogicalLimit will receive the limit of the run.
--- a/layout/base/nsBidiPresUtils.cpp +++ b/layout/base/nsBidiPresUtils.cpp @@ -1904,28 +1904,34 @@ nsBidiPresUtils::CalculateCharType(nsBid { bool strongTypeFound = false; int32_t offset; nsCharType charType; aCharType = eCharType_OtherNeutral; - for (offset = aOffset; offset < aCharTypeLimit; offset++) { + int32_t charLen; + for (offset = aOffset; offset < aCharTypeLimit; offset += charLen) { // Make sure we give RTL chartype to all characters that would be classified // as Right-To-Left by a bidi platform. // (May differ from the UnicodeData, eg we set RTL chartype to some NSMs.) - if (IS_HEBREW_CHAR(aText[offset]) ) { + charLen = 1; + uint32_t ch = aText[offset]; + if (IS_HEBREW_CHAR(ch) ) { charType = eCharType_RightToLeft; - } - else if (IS_ARABIC_ALPHABETIC(aText[offset]) ) { + } else if (IS_ARABIC_ALPHABETIC(ch) ) { charType = eCharType_RightToLeftArabic; - } - else { - aBidiEngine->GetCharTypeAt(offset, &charType); + } else { + if (NS_IS_HIGH_SURROGATE(ch) && offset + 1 < aCharTypeLimit && + NS_IS_LOW_SURROGATE(aText[offset + 1])) { + ch = SURROGATE_TO_UCS4(ch, aText[offset + 1]); + charLen = 2; + } + charType = GetBidiCat(ch); } if (!CHARTYPE_IS_WEAK(charType) ) { if (strongTypeFound && (charType != aPrevCharType) && (CHARTYPE_IS_RTL(charType) || CHARTYPE_IS_RTL(aPrevCharType) ) ) { // Stop at this point to ensure uni-directionality of the text
--- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -2021,20 +2021,16 @@ GetMouseThrough(const nsIFrame* aFrame) frame = nsBox::GetParentXULBox(frame); } return false; } static bool IsFrameReceivingPointerEvents(nsIFrame* aFrame) { - nsSubDocumentFrame* frame = do_QueryFrame(aFrame); - if (frame && frame->PassPointerEventsToChildren()) { - return true; - } return NS_STYLE_POINTER_EVENTS_NONE != aFrame->StyleUserInterface()->GetEffectivePointerEvents(aFrame); } // A list of frames, and their z depth. Used for sorting // the results of hit testing. struct FramesWithDepth { @@ -3674,17 +3670,17 @@ nsDisplayEventReceiver::HitTest(nsDispla void nsDisplayLayerEventRegions::AddFrame(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) { NS_ASSERTION(aBuilder->FindReferenceFrameFor(aFrame) == aBuilder->FindReferenceFrameFor(mFrame), "Reference frame mismatch"); if (aBuilder->IsInsidePointerEventsNoneDoc()) { // Somewhere up the parent document chain is a subdocument with pointer- - // events:none set on it (and without a mozpasspointerevents). + // events:none set on it. return; } if (!aFrame->GetParent()) { MOZ_ASSERT(aFrame->GetType() == nsGkAtoms::viewportFrame); // Viewport frames are never event targets, other frames, like canvas frames, // are the event targets for any regions viewport frames may cover. return; }
--- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -514,17 +514,17 @@ public: /** * Get the caret associated with the current presshell. */ nsCaret* GetCaret(); /** * Notify the display list builder that we're entering a presshell. * aReferenceFrame should be a frame in the new presshell. * aPointerEventsNoneDoc should be set to true if the frame generating this - * document is pointer-events:none without mozpasspointerevents. + * document is pointer-events:none. */ void EnterPresShell(nsIFrame* aReferenceFrame, bool aPointerEventsNoneDoc = false); /** * For print-preview documents, we sometimes need to build display items for * the same frames multiple times in the same presentation, with different * clipping. Between each such batch of items, call * ResetMarkedFramesForDisplayList to make sure that the results of @@ -1171,17 +1171,17 @@ private: struct PresShellState { nsIPresShell* mPresShell; nsIFrame* mCaretFrame; nsRect mCaretRect; uint32_t mFirstFrameMarkedForDisplay; bool mIsBackgroundOnly; // This is a per-document flag turning off event handling for all content // in the document, and is set when we enter a subdocument for a pointer- - // events:none frame that doesn't have mozpasspointerevents set. + // events:none frame. bool mInsidePointerEventsNoneDoc; }; PresShellState* CurrentPresShellState() { NS_ASSERTION(mPresShellStates.Length() > 0, "Someone forgot to enter a presshell"); return &mPresShellStates[mPresShellStates.Length() - 1]; }
--- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -8487,21 +8487,16 @@ nsLayoutUtils::CalculateRootCompositionS /* static */ nsRect nsLayoutUtils::CalculateScrollableRectForFrame(nsIScrollableFrame* aScrollableFrame, nsIFrame* aRootFrame) { nsRect contentBounds; if (aScrollableFrame) { contentBounds = aScrollableFrame->GetScrollRange(); - // We ifndef the below code for Fennec because it requires special behaviour - // on the APZC side. Because Fennec has it's own PZC implementation which doesn't - // provide the special behaviour, this code will cause it to break. We can remove - // the ifndef once Fennec switches over to APZ or if we add the special handling - // to Fennec nsPoint scrollPosition = aScrollableFrame->GetScrollPosition(); if (aScrollableFrame->GetScrollbarStyles().mVertical == NS_STYLE_OVERFLOW_HIDDEN) { contentBounds.y = scrollPosition.y; contentBounds.height = 0; } if (aScrollableFrame->GetScrollbarStyles().mHorizontal == NS_STYLE_OVERFLOW_HIDDEN) { contentBounds.x = scrollPosition.x; contentBounds.width = 0;
--- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -6224,57 +6224,16 @@ public: mShell->GetPresContext()->NotifyDidPaintForSubtree(mFlags); } private: PresShell* mShell; uint32_t mFlags; }; -class AutoUpdateHitRegion -{ -public: - AutoUpdateHitRegion(PresShell* aShell, nsIFrame* aFrame) - : mShell(aShell), mFrame(aFrame) - { - } - ~AutoUpdateHitRegion() - { - if (!XRE_IsContentProcess() || - !mFrame || !mShell) { - return; - } - TabChild* tabChild = TabChild::GetFrom(mShell); - if (!tabChild || !tabChild->GetUpdateHitRegion()) { - return; - } - nsRegion region; - nsDisplayListBuilder builder(mFrame, - nsDisplayListBuilderMode::EVENT_DELIVERY, - /* aBuildCert= */ false); - nsDisplayList list; - AutoTArray<nsIFrame*, 100> outFrames; - nsDisplayItem::HitTestState hitTestState; - builder.EnterPresShell(mFrame); - nsRect bounds = mShell->GetPresContext()->GetVisibleArea(); - mFrame->BuildDisplayListForStackingContext(&builder, bounds, &list); - builder.LeavePresShell(mFrame); - list.HitTest(&builder, bounds, &hitTestState, &outFrames); - list.DeleteAll(); - for (int32_t i = outFrames.Length() - 1; i >= 0; --i) { - region.Or(region, nsLayoutUtils::TransformFrameRectToAncestor( - outFrames[i], nsRect(nsPoint(0, 0), outFrames[i]->GetSize()), mFrame)); - } - tabChild->UpdateHitRegion(region); - } -private: - PresShell* mShell; - nsIFrame* mFrame; -}; - void PresShell::RecordShadowStyleChange(ShadowRoot* aShadowRoot) { mChangedScopeStyleRoots.AppendElement(aShadowRoot->GetHost()->AsElement()); } void PresShell::Paint(nsView* aViewToPaint, @@ -6299,17 +6258,16 @@ PresShell::Paint(nsView* aViewToP nsIFrame* frame = aViewToPaint->GetFrame(); LayerManager* layerManager = aViewToPaint->GetWidget()->GetLayerManager(); NS_ASSERTION(layerManager, "Must be in paint event"); bool shouldInvalidate = layerManager->NeedsWidgetInvalidation(); nsAutoNotifyDidPaint notifyDidPaint(this, aFlags); - AutoUpdateHitRegion updateHitRegion(this, frame); // Whether or not we should set first paint when painting is // suppressed is debatable. For now we'll do it because // B2G relies on first paint to configure the viewport and // we only want to do that when we have real content to paint. // See Bug 798245 if (mIsFirstPaint && !mPaintingSuppressed) { layerManager->SetIsFirstPaint();
--- a/layout/base/tests/chrome/chrome.ini +++ b/layout/base/tests/chrome/chrome.ini @@ -6,18 +6,16 @@ support-files = bug495648.rdf bug551434_childframe.html chrome_content_integration_window.xul chrome_over_plugin_window.xul default_background_window.xul dialog_with_positioning_window.xul no_clip_iframe_subdoc.html no_clip_iframe_window.xul - passpointerevents_window.html - passpointerevents_dynamically_window.html printpreview_bug396024_helper.xul printpreview_bug482976_helper.xul printpreview_helper.xul file_bug1018265.xul [test_bug396367-1.html] [test_bug396367-2.html] [test_bug420499.xul] @@ -52,20 +50,16 @@ skip-if = buildapp == 'b2g' tags = openwindow skip-if = buildapp == 'b2g' [test_fixed_bg_scrolling_repaints.html] skip-if = buildapp == 'b2g' [test_leaf_layers_partition_browser_window.xul] skip-if = (!debug) || (toolkit == "cocoa") || (os == "linux") || (buildapp == 'b2g') # Disabled on Mac and Linux because of Bug 992311 [test_no_clip_iframe.xul] skip-if = buildapp == 'b2g' -[test_passpointerevents.html] -skip-if = buildapp == 'b2g' -[test_passpointerevents_dynamic.html] -skip-if = buildapp == 'b2g' [test_prerendered_transforms.html] skip-if = buildapp == 'b2g' [test_printpreview.xul] skip-if = buildapp == 'b2g' || (os == "linux" && bits == 32) # Disabled on Linux32 for bug 1278957 [test_printpreview_bug396024.xul] skip-if = buildapp == 'b2g' [test_printpreview_bug482976.xul] skip-if = buildapp == 'b2g'
deleted file mode 100644 --- a/layout/base/tests/chrome/passpointerevents_dynamically_window.html +++ /dev/null @@ -1,28 +0,0 @@ -<!DOCTYPE HTML> -<html> -<head> - <title>Test that mozpasspointerevents works after setting it dynamically</title> -</head> -<body onload="startTest()"> -<iframe id="f" style="border:none; width:200px; height:200px; pointer-events:none" - src="data:text/html,<html style='pointer-events:none'><div style='margin:100px; width:100px; height:100px; background:yellow; pointer-events:auto'>"> -</iframe> - -<script type="application/javascript"> -var SimpleTest = window.opener.SimpleTest; -var is = window.opener.is; - -function startTest() { - var f = document.getElementById("f"); - f.setAttribute("mozpasspointerevents", true); - var fRect = f.getBoundingClientRect(); - var e1 = document.elementFromPoint(fRect.left + 10, fRect.top + 10); - is(e1, document.body, "check point in transparent region of the iframe"); - var e2 = document.elementFromPoint(fRect.left + 110, fRect.top + 110); - is(e2, f, "check point in opaque region of the iframe"); - window.close(); - SimpleTest.finish(); -} -</script> -</body> -</html>
deleted file mode 100644 --- a/layout/base/tests/chrome/passpointerevents_window.html +++ /dev/null @@ -1,27 +0,0 @@ -<!DOCTYPE HTML> -<html> -<head> - <title>Test that mozpasspointerevents works</title> -</head> -<body onload="startTest()"> -<iframe id="f" style="border:none; width:200px; height:200px; pointer-events:none" mozpasspointerevents - src="data:text/html,<html style='pointer-events:none'><div style='margin:100px; width:100px; height:100px; background:yellow; pointer-events:auto'>"> -</iframe> - -<script type="application/javascript"> -var SimpleTest = window.opener.SimpleTest; -var is = window.opener.is; - -function startTest() { - var f = document.getElementById("f"); - var fRect = f.getBoundingClientRect(); - var e1 = document.elementFromPoint(fRect.left + 10, fRect.top + 10); - is(e1, document.body, "check point in transparent region of the iframe"); - var e2 = document.elementFromPoint(fRect.left + 110, fRect.top + 110); - is(e2, f, "check point in opaque region of the iframe"); - window.close(); - SimpleTest.finish(); -} -</script> -</body> -</html>
deleted file mode 100644 --- a/layout/base/tests/chrome/test_passpointerevents.html +++ /dev/null @@ -1,21 +0,0 @@ -<!DOCTYPE HTML> -<html> -<head> - <title>Test that mozpasspointerevents works</title> - <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> - <script type="application/javascript" src="chrome://mochikit/content/chrome-harness.js"></script> - <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/> -</head> -<body> - -<pre id="test"> -<script type="application/javascript"> -SimpleTest.waitForExplicitFinish(); - -var root = getRootDirectory(window.location.href); -window.openDialog(root + "passpointerevents_window.html", "passpointerevents", - "chrome,width=400,height=400"); -</script> -</pre> -</body> -</html>
deleted file mode 100644 --- a/layout/base/tests/chrome/test_passpointerevents_dynamic.html +++ /dev/null @@ -1,21 +0,0 @@ -<!DOCTYPE HTML> -<html> -<head> - <title>Test that mozpasspointerevents works after setting it dynamically</title> - <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> - <script type="application/javascript" src="chrome://mochikit/content/chrome-harness.js"></script> - <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/> -</head> -<body> - -<pre id="test"> -<script type="application/javascript"> -SimpleTest.waitForExplicitFinish(); - -var root = getRootDirectory(window.location.href); -window.openDialog(root + "passpointerevents_dynamically_window.html", - "passpointerevents", "chrome,width=400,height=400"); -</script> -</pre> -</body> -</html>
--- a/layout/base/tests/mochitest.ini +++ b/layout/base/tests/mochitest.ini @@ -239,17 +239,16 @@ skip-if = buildapp == 'mulet' || buildap [test_bug646757.html] [test_bug718809.html] [test_bug725426.html] [test_bug731777.html] [test_bug761572.html] [test_bug770106.html] [test_remote_frame.html] skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_remote_passpointerevents.html] [test_bug842853.html] [test_bug842853-2.html] [test_bug849219.html] [test_bug851485.html] [test_bug851445.html] support-files = bug851445_helper.html [test_bug970964.html] support-files = bug970964_inner.html
deleted file mode 100644 --- a/layout/base/tests/test_remote_passpointerevents.html +++ /dev/null @@ -1,52 +0,0 @@ -<!DOCTYPE HTML> -<html> - <head> - <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> - </head> - <body> - <script type="application/javascript;version=1.7"> - "use strict"; - - SimpleTest.waitForExplicitFinish(); - - function checkPointerEvents() { - let iframe = this; - let fRect = iframe.getBoundingClientRect(); - let e1 = document.elementFromPoint(fRect.left + 10, fRect.top + 10); - let e2 = document.elementFromPoint(fRect.left + 110, fRect.top + 110); - if (e1 === document.body && e2 === iframe) { - is(e1, document.body, "check point in transparent region of the iframe"); - is(e2, iframe, "check point in opaque region of the iframe"); - SimpleTest.finish(); - } - else { - SimpleTest.executeSoon(checkPointerEvents.bind(iframe)); - } - } - - function runTest() { - let iframe = document.createElement("iframe"); - SpecialPowers.wrap(iframe).setAttribute('mozbrowser', 'true'); - SpecialPowers.wrap(iframe).setAttribute('mozpasspointerevents', 'true'); - SpecialPowers.wrap(iframe).setAttribute('remote', 'true'); - iframe.style = "border:none; width:400px; height:400px; pointer-events:none"; - iframe.src = "data:text/html,<html style='pointer-events:none'><div style='margin:100px; width:100px; height:100px; background:yellow; pointer-events:auto'>"; - - document.body.appendChild(iframe); - - SimpleTest.executeSoon(checkPointerEvents.bind(iframe)); - } - addEventListener("load", function() { - SpecialPowers.addPermission("browser", true, document); - SpecialPowers.addPermission("embed-apps", true, document); - SpecialPowers.pushPrefEnv({ - "set": [ - ["dom.ipc.browser_frames.oop_by_default", true], - ["dom.mozBrowserFramesEnabled", true] - ] - }, runTest); - }); - </script> - </body> -</html>
--- a/layout/generic/nsSubDocumentFrame.cpp +++ b/layout/generic/nsSubDocumentFrame.cpp @@ -302,41 +302,16 @@ nsSubDocumentFrame::GetSubdocumentSize() docSizeAppUnits = destRect.Size(); } return ScreenIntSize(presContext->AppUnitsToDevPixels(docSizeAppUnits.width), presContext->AppUnitsToDevPixels(docSizeAppUnits.height)); } } -bool -nsSubDocumentFrame::PassPointerEventsToChildren() -{ - // Limit use of mozpasspointerevents to documents with embedded:apps/chrome - // permission, because this could be used by the parent document to discover - // which parts of the subdocument are transparent to events (if subdocument - // uses pointer-events:none on its root element, which is admittedly - // unlikely) - if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozpasspointerevents)) { - if (PresContext()->IsChrome()) { - return true; - } - - nsCOMPtr<nsIPermissionManager> permMgr = - services::GetPermissionManager(); - if (permMgr) { - uint32_t permission = nsIPermissionManager::DENY_ACTION; - permMgr->TestPermissionFromPrincipal(GetContent()->NodePrincipal(), - "embed-apps", &permission); - return permission == nsIPermissionManager::ALLOW_ACTION; - } - } - return false; -} - static void WrapBackgroundColorInOwnLayer(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList) { nsDisplayList tempItems; nsDisplayItem* item; while ((item = aList->RemoveBottom()) != nullptr) { @@ -375,32 +350,24 @@ nsSubDocumentFrame::BuildDisplayList(nsD // own layer so we generate a ColorLayer. This is helpful for optimizing // compositing; we can skip compositing the ColorLayer when the // remote content is opaque. WrapBackgroundColorInOwnLayer(aBuilder, this, decorations.BorderBackground()); } decorations.MoveTo(aLists); } - // We only care about mozpasspointerevents if we're doing hit-testing - // related things. - bool passPointerEventsToChildren = - (aBuilder->IsForEventDelivery() || aBuilder->IsBuildingLayerEventRegions()) - ? PassPointerEventsToChildren() : false; - - // If mozpasspointerevents is set, then we should allow subdocument content - // to handle events even if we're pointer-events:none. - if (aBuilder->IsForEventDelivery() && pointerEventsNone && !passPointerEventsToChildren) { + if (aBuilder->IsForEventDelivery() && pointerEventsNone) { return; } // If we're passing pointer events to children then we have to descend into // subdocuments no matter what, to determine which parts are transparent for // hit-testing or event regions. - bool needToDescend = aBuilder->GetDescendIntoSubdocuments() || passPointerEventsToChildren; + bool needToDescend = aBuilder->GetDescendIntoSubdocuments(); if (!mInnerView || !needToDescend) { return; } if (rfp) { rfp->BuildDisplayList(aBuilder, this, aDirtyRect, aLists); return; } @@ -443,18 +410,17 @@ nsSubDocumentFrame::BuildDisplayList(nsD ignoreViewportScrolling = presShell->IgnoringViewportScrolling(); if (ignoreViewportScrolling) { savedIgnoreScrollFrame = aBuilder->GetIgnoreScrollFrame(); aBuilder->SetIgnoreScrollFrame(rootScrollFrame); } } - aBuilder->EnterPresShell(subdocRootFrame, - pointerEventsNone && !passPointerEventsToChildren); + aBuilder->EnterPresShell(subdocRootFrame, pointerEventsNone); } else { dirty = aDirtyRect; } DisplayListClipState::AutoSaveRestore clipState(aBuilder); if (ShouldClipSubdocument()) { clipState.ClipContainingBlockDescendantsToContentBox(aBuilder, this); } @@ -908,26 +874,16 @@ nsSubDocumentFrame::AttributeChanged(int // Retrieve the attributes CSSIntSize margins = GetMarginAttributes(); // Notify the frameloader RefPtr<nsFrameLoader> frameloader = FrameLoader(); if (frameloader) frameloader->MarginsChanged(margins.width, margins.height); } - else if (aAttribute == nsGkAtoms::mozpasspointerevents) { - RefPtr<nsFrameLoader> frameloader = FrameLoader(); - if (frameloader) { - if (aModType == nsIDOMMutationEvent::ADDITION) { - frameloader->ActivateUpdateHitRegion(); - } else if (aModType == nsIDOMMutationEvent::REMOVAL) { - frameloader->DeactivateUpdateHitRegion(); - } - } - } return NS_OK; } nsIFrame* NS_NewSubDocumentFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) { return new (aPresShell) nsSubDocumentFrame(aContext);
--- a/layout/ipc/PRenderFrame.ipdl +++ b/layout/ipc/PRenderFrame.ipdl @@ -25,15 +25,13 @@ namespace layout { */ sync protocol PRenderFrame { manager PBrowser; parent: async NotifyCompositorTransaction(); - async UpdateHitRegion(nsRegion aRegion); - async __delete__(); }; } // namespace layout } // namespace mozilla
--- a/layout/ipc/RenderFrameParent.cpp +++ b/layout/ipc/RenderFrameParent.cpp @@ -236,23 +236,16 @@ RenderFrameParent::ActorDestroy(ActorDes bool RenderFrameParent::RecvNotifyCompositorTransaction() { TriggerRepaint(); return true; } -bool -RenderFrameParent::RecvUpdateHitRegion(const nsRegion& aRegion) -{ - mTouchRegion = aRegion; - return true; -} - void RenderFrameParent::TriggerRepaint() { nsIFrame* docFrame = mFrameLoader->GetPrimaryFrameOfOwningContent(); if (!docFrame) { // Bad, but nothing we can do about it (XXX/cjones: or is there? // maybe bug 589337?). When the new frame is created, we'll // probably still be the current render frame and will get to draw @@ -283,22 +276,16 @@ RenderFrameParent::BuildDisplayList(nsDi nsPoint offset = aBuilder->ToReferenceFrame(aFrame); nsRect bounds = aFrame->EnsureInnerView()->GetBounds() + offset; clipState.ClipContentDescendants(bounds); aLists.Content()->AppendToTop( new (aBuilder) nsDisplayRemote(aBuilder, aFrame, this)); } -bool -RenderFrameParent::HitTest(const nsRect& aRect) -{ - return mTouchRegion.Contains(aRect); -} - void RenderFrameParent::GetTextureFactoryIdentifier(TextureFactoryIdentifier* aTextureFactoryIdentifier) { RefPtr<LayerManager> lm = mFrameLoader ? GetFrom(mFrameLoader) : nullptr; // Perhaps the document containing this frame currently has no presentation? if (lm && lm->AsClientLayerManager()) { *aTextureFactoryIdentifier = lm->AsClientLayerManager()->GetTextureFactoryIdentifier(); } else { @@ -349,17 +336,16 @@ nsDisplayRemote::nsDisplayRemote(nsDispl nsSubDocumentFrame* aFrame, RenderFrameParent* aRemoteFrame) : nsDisplayItem(aBuilder, aFrame) , mRemoteFrame(aRemoteFrame) , mEventRegionsOverride(EventRegionsOverride::NoOverride) { if (aBuilder->IsBuildingLayerEventRegions()) { bool frameIsPointerEventsNone = - !aFrame->PassPointerEventsToChildren() && aFrame->StyleUserInterface()->GetEffectivePointerEvents(aFrame) == NS_STYLE_POINTER_EVENTS_NONE; if (aBuilder->IsInsidePointerEventsNoneDoc() || frameIsPointerEventsNone) { mEventRegionsOverride |= EventRegionsOverride::ForceEmptyHitRegion; } if (nsLayoutUtils::HasDocumentLevelListenersForApzAwareEvents(aFrame->PresContext()->PresShell())) { mEventRegionsOverride |= EventRegionsOverride::ForceDispatchToContent; } @@ -375,18 +361,8 @@ nsDisplayRemote::BuildLayer(nsDisplayLis nsIntRect visibleRect = GetVisibleRect().ToNearestPixels(appUnitsPerDevPixel); visibleRect += aContainerParameters.mOffset; RefPtr<Layer> layer = mRemoteFrame->BuildLayer(aBuilder, mFrame, aManager, visibleRect, this, aContainerParameters); if (layer && layer->AsContainerLayer()) { layer->AsContainerLayer()->SetEventRegionsOverride(mEventRegionsOverride); } return layer.forget(); } - -void -nsDisplayRemote::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, - HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) -{ - if (mRemoteFrame->HitTest(aRect)) { - aOutFrames->AppendElement(mFrame); - } -} -
--- a/layout/ipc/RenderFrameParent.h +++ b/layout/ipc/RenderFrameParent.h @@ -86,18 +86,16 @@ public: void EnsureLayersConnected(); protected: void ActorDestroy(ActorDestroyReason why) override; virtual bool RecvNotifyCompositorTransaction() override; - virtual bool RecvUpdateHitRegion(const nsRegion& aRegion) override; - private: void TriggerRepaint(); void DispatchEventForPanZoomController(const InputEvent& aEvent); uint64_t GetLayerTreeId() const; // When our child frame is pushing transactions directly to the // compositor, this is the ID of its layer tree in the compositor's @@ -118,18 +116,16 @@ private: // Prefer the extra bit of state to null'ing out mFrameLoader in // Destroy() so that less code needs to be special-cased for after // Destroy(). // // It's possible for mFrameLoader==null and // mFrameLoaderDestroyed==false. bool mFrameLoaderDestroyed; - nsRegion mTouchRegion; - bool mAsyncPanZoomEnabled; bool mInitted; }; } // namespace layout } // namespace mozilla /** @@ -149,19 +145,16 @@ public: LayerManager* aManager, const ContainerLayerParameters& aParameters) override { return mozilla::LAYER_ACTIVE_FORCE; } virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aContainerParameters) override; - void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, - HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) override; - NS_DISPLAY_DECL_NAME("Remote", TYPE_REMOTE) private: RenderFrameParent* mRemoteFrame; mozilla::layers::EventRegionsOverride mEventRegionsOverride; };
--- a/layout/reftests/canvas/dash-1.html +++ b/layout/reftests/canvas/dash-1.html @@ -1,30 +1,30 @@ <html> <head> <script type="text/javascript"> window.onload = function() { var ctx = document.getElementById("c1").getContext("2d"); ctx.lineWidth = 5; - ctx.mozDash = [ 5, 10 ]; // 5 on, 10 off + ctx.setLineDash([ 5, 10 ]); // 5 on, 10 off ctx.moveTo(50, 50); ctx.lineTo(250, 50); ctx.stroke(); ctx.beginPath(); - ctx.mozDashOffset = 5; + ctx.lineDashOffset = 5; ctx.moveTo(50, 100); ctx.lineTo(250, 100); ctx.stroke(); ctx.beginPath(); - ctx.mozDashOffset = 5; - ctx.mozDash = [ 5 ]; // 5 on, 5 off + ctx.lineDashOffset = 5; + ctx.setLineDash([ 5 ]); // 5 on, 5 off ctx.moveTo(50, 150); ctx.lineTo(250, 150); ctx.stroke(); ctx.beginPath(); } </script> </head>
--- a/layout/reftests/canvas/dash-sanity.html +++ b/layout/reftests/canvas/dash-sanity.html @@ -1,80 +1,93 @@ <html> <head> <script type="text/javascript"> function assert(cond, msg) { if (!cond) { throw msg; } } window.onload = function() { try { var ctx = document.getElementById("c1").getContext("2d"); - assert(null === ctx.mozDash, - "Default dash is null (no dash)"); - assert(0 == ctx.mozDashOffset, - "Default dashOffset is 0 (no dash)"); + assert(0 === ctx.getLineDash().length, + "Default dash is [ ] (none)"); + assert(0 === ctx.lineDashOffset, + "Default dashOffset is 0 (none)"); - ctx.mozDash = [ 2 ]; - assert(1 == ctx.mozDash.length && 2 == ctx.mozDash[0], + ctx.setLineDash([ 2 ]); + assert(2 === ctx.getLineDash().length && + 2 === ctx.getLineDash()[0] && + 2 === ctx.getLineDash()[1], "dash = [ 2 ] works"); - ctx.mozDash = null; - assert(null === ctx.mozDash, - "dash = null resets to null"); - ctx.mozDash = [ 2 ]; - ctx.mozDash = undefined; - assert(null === ctx.mozDash, - "dash = undefined resets to null"); - ctx.mozDash = [ 2 ]; - ctx.mozDash = [ ]; - assert(null === ctx.mozDash, - "dash = [] resets to null"); + ctx.setLineDash([ 2 ]); + ctx.setLineDash([ ]); + assert(0 === ctx.getLineDash().length, + "dash = [ ] works"); + ctx.setLineDash([ 2 ]); + ctx.setLineDash([ 0, 0, 0 ]); + assert(6 === ctx.getLineDash().length, + 0 === ctx.getLineDash()[0] && + 0 === ctx.getLineDash()[1] && + 0 === ctx.getLineDash()[2] && + 0 === ctx.getLineDash()[3] && + 0 === ctx.getLineDash()[4] && + 0 === ctx.getLineDash()[5], + "dash = [ 0, 0, 0 ] works"); - ctx.mozDash = [ 2 ]; - assert(0 == ctx.mozDashOffset, "dashOffset is 0"); - ctx.mozDashOffset = 1; - assert(1 == ctx.mozDashOffset, "Setting dashOffset succeeded"); - ctx.mozDash = null; - assert(0 == ctx.mozDashOffset, "Disabling dash resets dashOffset"); - ctx.mozDash = [ 2 ]; - assert(0 == ctx.mozDashOffset, "Previous dashOffset isn't remembered"); - ctx.mozDash = null; + ctx.setLineDash([ 2 ]); + assert(0 === ctx.lineDashOffset, "dashOffset is 0"); + ctx.lineDashOffset = 1; + assert(1 === ctx.lineDashOffset, "Setting dashOffset succeeded"); + ctx.setLineDash([ ]); + assert(1 === ctx.lineDashOffset, "Changing dash does not reset dashOffset"); // NB: might want to add a |.dash = number| special case, // don't test that it fails here. Might also want to add a // |.dash = [0]| special case for resetting, so don't test // that either. var badVals = [ -1, + null, + undefined, + "", "string", - /* According to the WebIDL sequence-ifying - * (really they mean array-ifying here) - * algorithm, objects without .length - * properties convert to a 0-length arrays. - * This seems ... odd, since by the book we're - * forced to accept |ctx.dash = Function|, - * e.g., but there it is. - */ - // { obj: true }, + { obj: true }, + function() {} + ] + ctx.setLineDash([ 2 ]); + for (var i = 0; i < badVals.length; ++i) { + var error = false; + try { ctx.setLineDash(badVals[i]); } + catch(e) { error = true; } + assert(error && + 2 === ctx.getLineDash().length && + 2 === ctx.getLineDash()[0] && + 2 === ctx.getLineDash()[1], + "Expected setLineDash("+ badVals[i] +") to throw exception and not change dash"); + } + + var ignoredVals = [ [ "array of string" ], [ -1 ], - [ 0, 0, 0 ], [ 2, "string" ], ]; - ctx.mozDash = [ 2 ]; - for (var i = 0; i < badVals.length; ++i) { - var error = false; - try { ctx.mozDash = badVals[i]; } - catch(e) { error = true; } - assert(error && 1 == ctx.mozDash.length && 2 == ctx.mozDash[0], - "Expected |dash = "+ badVals[i] +"| to throw exception and not change .dash"); + ctx.setLineDash([ 2 ]); + for (var i = 0; i < ignoredVals.length; ++i) { + ctx.setLineDash(ignoredVals[i]); + assert(2 === ctx.getLineDash().length && + 2 === ctx.getLineDash()[0] && + 2 === ctx.getLineDash()[1], + "Expected |setLineDash(" + ignoredVals[i] + ") to not change dash"); } - ctx.mozDash = null; + ctx.setLineDash([ 2 ]); ctx.save(); - ctx.mozDash = [ 2 ]; + ctx.setLineDash([ 1, 1, 1, 1 ]); ctx.restore(); - assert(null === ctx.mozDash, + assert(2 === ctx.getLineDash().length && + 2 === ctx.getLineDash()[0] && + 2 === ctx.getLineDash()[1], "dash was saved then restored"); } catch (e) { document.body.innerHTML = "FAIL: "+ e.toString(); return; } document.body.innerHTML = "Pass"; } </script>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/smil/anim-clipPath-viewBox-ref.svg @@ -0,0 +1,5 @@ +<svg xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + width="600" height="400"> + <circle cx="300" cy="200" r="100" style="fill:blue;"/> +</svg> \ No newline at end of file
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/smil/anim-clipPath-viewBox.svg @@ -0,0 +1,12 @@ +<svg xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + width="600" height="400" preserveAspectRatio="none" + viewBox="50 100 500 200" + class="reftest-wait" + onload="setTimeAndSnapshot(1, true)"> + <script xlink:href="smil-util.js" type="text/javascript"/> + <rect x="50" y="100" width="500" height="200" style="fill:blue;" + clip-path="circle(100px at center) view-box"/> + <animate attributeName="viewBox" values="50 100 500 200; 0 0 600 400" + dur="0.5s" fill="freeze"/> +</svg> \ No newline at end of file
--- a/layout/reftests/svg/smil/reftest.list +++ b/layout/reftests/svg/smil/reftest.list @@ -274,8 +274,10 @@ fuzzy-if(cocoaWidget&&layersGPUAccelerat == anim-defs-gradient-property.svg lime.svg == anim-defs-gradient-attribute.svg lime.svg == anim-defs-fill.svg lime.svg == anim-defs-width.svg lime.svg # Test animation that changes 'display' attribute == anim-display.svg lime.svg == anim-display-in-g-element.svg lime.svg + +pref(layout.css.clip-path-shapes.enabled,true) fuzzy(63,146) == anim-clipPath-viewBox.svg anim-clipPath-viewBox-ref.svg
--- a/media/libcubeb/src/cubeb_wasapi.cpp +++ b/media/libcubeb/src/cubeb_wasapi.cpp @@ -682,17 +682,17 @@ refill_callback_duplex(cubeb_stream * st /* This can only happen when debugging, and having breakpoints set in the * callback in a way that it makes the stream underrun. */ if (output_frames == 0) { return true; } // When WASAPI has not filled the input buffer yet, send silence. double output_duration = double(output_frames) / stm->output_mix_params.rate; - double input_duration = double(stm->linear_input_buffer.length() / stm->input_mix_params.channels) / stm->input_mix_params.rate; + double input_duration = double(stm->linear_input_buffer.length() / stm->input_stream_params.channels) / stm->input_mix_params.rate; if (input_duration < output_duration) { size_t padding = size_t(round((output_duration - input_duration) * stm->input_mix_params.rate)); LOG("padding silence: out=%f in=%f pad=%u\n", output_duration, input_duration, padding); stm->linear_input_buffer.push_front_silence(padding * stm->input_stream_params.channels); } refill(stm, stm->linear_input_buffer.data(),
--- a/media/libcubeb/update.sh +++ b/media/libcubeb/update.sh @@ -54,8 +54,10 @@ if [ -n "$rev" ]; then sed -i.bak -e "/The git commit ID used was/ s/[0-9a-f]\{40\}\(-dirty\)\{0,1\}\./$version./" README_MOZILLA rm README_MOZILLA.bak else echo "Remember to update README_MOZILLA with the version details." fi echo "Applying a patch on top of $version" patch -p1 < ./wasapi-drift.patch +echo "Applying another patch on top of $version" +patch -p1 < ./wasapi-stereo-mic.patch
new file mode 100644 --- /dev/null +++ b/media/libcubeb/wasapi-stereo-mic.patch @@ -0,0 +1,28 @@ +From 50d92c0eaa05a93f4dd8ff5072e983c6c84d0369 Mon Sep 17 00:00:00 2001 +From: Paul Adenot <paul@paul.cx> +Date: Tue, 4 Oct 2016 07:18:04 -0700 +Subject: [PATCH] Divide by the number of channel of the stream to compute the + duratio of the buffer. + +We downmix right before, so this is in frames in with the channel count of the +stream, not the mix. +--- + src/cubeb_wasapi.cpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/cubeb_wasapi.cpp b/src/cubeb_wasapi.cpp +index bd2cbed..87aa305 100644 +--- a/src/cubeb_wasapi.cpp ++++ b/src/cubeb_wasapi.cpp +@@ -687,7 +687,7 @@ refill_callback_duplex(cubeb_stream * stm) + + // When WASAPI has not filled the input buffer yet, send silence. + double output_duration = double(output_frames) / stm->output_mix_params.rate; +- double input_duration = double(stm->linear_input_buffer.length() / stm->input_mix_params.channels) / stm->input_mix_params.rate; ++ double input_duration = double(stm->linear_input_buffer.length() / stm->input_stream_params.channels) / stm->input_mix_params.rate; + if (input_duration < output_duration) { + size_t padding = size_t(round((output_duration - input_duration) * stm->input_mix_params.rate)); + LOG("padding silence: out=%f in=%f pad=%u\n", output_duration, input_duration, padding); +-- +2.7.4 +
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_mac.mm +++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_mac.mm @@ -32,16 +32,17 @@ class AppCapturerMac : public AppCapture // AppCapturer interface. virtual bool GetAppList(AppList* apps) override; virtual bool SelectApp(ProcessId processId) override; virtual bool BringAppToFront() override; // DesktopCapturer interface. virtual void Start(Callback* callback) override; + virtual void Stop() override; virtual void Capture(const DesktopRegion& region) override; private: Callback* callback_; ProcessId process_id_; DISALLOW_COPY_AND_ASSIGN(AppCapturerMac); }; @@ -73,16 +74,20 @@ bool AppCapturerMac::BringAppToFront() { // DesktopCapturer interface. void AppCapturerMac::Start(Callback* callback) { assert(!callback_); assert(callback); callback_ = callback; } +void AppCapturerMac::Stop() { + callback_ = NULL; +} + void AppCapturerMac::Capture(const DesktopRegion& region) { // Check that selected process exists NSRunningApplication *ra = [NSRunningApplication runningApplicationWithProcessIdentifier:process_id_]; if (!ra) { callback_->OnCaptureCompleted(NULL); return; }
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_null.cc +++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_null.cc @@ -25,16 +25,17 @@ public: // AppCapturer interface. virtual bool GetAppList(AppList* apps) override; virtual bool SelectApp(ProcessId id) override; virtual bool BringAppToFront() override; // DesktopCapturer interface. virtual void Start(Callback* callback) override; + virtual void Stop() override; virtual void Capture(const DesktopRegion& region) override; private: Callback* callback_; DISALLOW_COPY_AND_ASSIGN(AppCapturerNull); }; @@ -63,16 +64,20 @@ bool AppCapturerNull::BringAppToFront() // DesktopCapturer interface. void AppCapturerNull::Start(Callback* callback) { assert(!callback_); assert(callback); callback_ = callback; } +void AppCapturerNull::Stop() { + callback_ = NULL; +} + void AppCapturerNull::Capture(const DesktopRegion& region) { // Not implemented yet: See Bug 1036653 callback_->OnCaptureCompleted(NULL); } } // namespace // static
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_win.cc +++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_win.cc @@ -75,16 +75,17 @@ public: // AppCapturer interface. virtual bool GetAppList(AppList* apps) override; virtual bool SelectApp(ProcessId processId) override; virtual bool BringAppToFront() override; // DesktopCapturer interface. virtual void Start(Callback* callback) override; + virtual void Stop() override; virtual void Capture(const DesktopRegion& region) override; struct WindowItem { HWND handle; RECT bounds; bool owned; }; @@ -160,16 +161,19 @@ bool AppCapturerWin::BringAppToFront() { // DesktopCapturer interface. void AppCapturerWin::Start(Callback* callback) { assert(!callback_); assert(callback); callback_ = callback; } +void AppCapturerWin::Stop() { + callback_ = NULL; +} void AppCapturerWin::Capture(const DesktopRegion& region) { assert(IsGUIThread(false)); CaptureBySample(region); } BOOL CALLBACK AppCapturerWin::EnumWindowsProc(HWND handle, LPARAM lParam) { EnumWindowsCtx *pEnumWindowsCtx = reinterpret_cast<EnumWindowsCtx *>(lParam); if (!pEnumWindowsCtx) {
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_x11.cc +++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_x11.cc @@ -79,16 +79,17 @@ public: // AppCapturer interface. virtual bool GetAppList(AppList* apps) override; virtual bool SelectApp(ProcessId processId) override; virtual bool BringAppToFront() override; // DesktopCapturer interface. virtual void Start(Callback* callback) override; + virtual void Stop() override; virtual void Capture(const DesktopRegion& region) override; protected: Display* GetDisplay() { return x_display_->display(); } bool UpdateRegions(); void CaptureWebRTC(const DesktopRegion& region); void CaptureSample(const DesktopRegion& region); @@ -152,16 +153,20 @@ bool AppCapturerLinux::BringAppToFront() // DesktopCapturer interface. void AppCapturerLinux::Start(Callback* callback) { assert(!callback_); assert(callback); callback_ = callback; } +void AppCapturerLinux::Stop() { + callback_ = NULL; +} + void AppCapturerLinux::Capture(const DesktopRegion& region) { CaptureSample(region); } void AppCapturerLinux::CaptureWebRTC(const DesktopRegion& region) { XErrorTrap error_trap(GetDisplay()); int nScreenWidth = DisplayWidth(GetDisplay(), DefaultScreen(GetDisplay()));
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/cropping_window_capturer.cc +++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/cropping_window_capturer.cc @@ -26,16 +26,21 @@ CroppingWindowCapturer::CroppingWindowCa CroppingWindowCapturer::~CroppingWindowCapturer() {} void CroppingWindowCapturer::Start(DesktopCapturer::Callback* callback) { callback_ = callback; window_capturer_->Start(callback); } +void CroppingWindowCapturer::Stop() { + window_capturer_->Stop(); + callback_ = NULL; +} + void CroppingWindowCapturer::Capture(const DesktopRegion& region) { if (ShouldUseScreenCapturer()) { if (!screen_capturer_.get()) { screen_capturer_.reset(ScreenCapturer::Create(options_)); if (excluded_window_) { screen_capturer_->SetExcludedWindow(excluded_window_); } screen_capturer_->Start(this);
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/cropping_window_capturer.h +++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/cropping_window_capturer.h @@ -24,16 +24,17 @@ namespace webrtc { class CroppingWindowCapturer : public WindowCapturer, public DesktopCapturer::Callback { public: static WindowCapturer* Create(const DesktopCaptureOptions& options); virtual ~CroppingWindowCapturer(); // DesktopCapturer implementation. void Start(DesktopCapturer::Callback* callback) override; + void Stop() override; void Capture(const DesktopRegion& region) override; void SetExcludedWindow(WindowId window) override; // WindowCapturer implementation. bool GetWindowList(WindowList* windows) override; bool SelectWindow(WindowId id) override; bool BringSelectedWindowToFront() override;
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_and_cursor_composer.cc +++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_and_cursor_composer.cc @@ -127,20 +127,27 @@ DesktopAndCursorComposer::DesktopAndCurs mouse_monitor_(mouse_monitor) { } DesktopAndCursorComposer::~DesktopAndCursorComposer() {} void DesktopAndCursorComposer::Start(DesktopCapturer::Callback* callback) { callback_ = callback; if (mouse_monitor_.get()) - mouse_monitor_->Init(this, MouseCursorMonitor::SHAPE_AND_POSITION); + mouse_monitor_->Start(this, MouseCursorMonitor::SHAPE_AND_POSITION); desktop_capturer_->Start(this); } +void DesktopAndCursorComposer::Stop() { + desktop_capturer_->Stop(); + if (mouse_monitor_.get()) + mouse_monitor_->Stop(); + callback_ = NULL; +} + void DesktopAndCursorComposer::Capture(const DesktopRegion& region) { if (mouse_monitor_.get()) mouse_monitor_->Capture(); desktop_capturer_->Capture(region); } void DesktopAndCursorComposer::SetExcludedWindow(WindowId window) { desktop_capturer_->SetExcludedWindow(window);
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_and_cursor_composer.h +++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_and_cursor_composer.h @@ -28,16 +28,17 @@ class DesktopAndCursorComposer : public // |mouse_monitor| is NULL the frames are passed unmodified. Takes ownership // of both arguments. DesktopAndCursorComposer(DesktopCapturer* desktop_capturer, MouseCursorMonitor* mouse_monitor); virtual ~DesktopAndCursorComposer(); // DesktopCapturer interface. void Start(DesktopCapturer::Callback* callback) override; + void Stop() override; void Capture(const DesktopRegion& region) override; void SetExcludedWindow(WindowId window) override; private: // DesktopCapturer::Callback interface. SharedMemory* CreateSharedMemory(size_t size) override; void OnCaptureCompleted(DesktopFrame* frame) override;
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capturer.h +++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capturer.h @@ -39,18 +39,19 @@ class DesktopCapturer { protected: virtual ~Callback() {} }; virtual ~DesktopCapturer() {} // Called at the beginning of a capturing session. |callback| must remain - // valid until capturer is destroyed. + // valid until capturer is destroyed or until Stop() is called virtual void Start(Callback* callback) = 0; + virtual void Stop() = 0; // Captures next frame. |region| specifies region of the capture target that // should be fresh in the resulting frame. The frame may also include fresh // data for areas outside |region|. In that case capturer will include these // areas in updated_region() of the frame. |region| is specified relative to // the top left corner of the capture target. Pending capture operations are // canceled when DesktopCapturer is deleted. virtual void Capture(const DesktopRegion& region) = 0;
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor.h +++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor.h @@ -68,18 +68,21 @@ class MouseCursorMonitor { // the entire desktop. // // TODO(sergeyu): Provide a way to select a specific screen. static MouseCursorMonitor* CreateForScreen( const DesktopCaptureOptions& options, ScreenId screen); // Initializes the monitor with the |callback|, which must remain valid until - // capturer is destroyed. - virtual void Init(Callback* callback, Mode mode) = 0; + // capturer is destroyed or until Stop() + virtual void Start(Callback* callback, Mode mode) = 0; + + // clears the callback + virtual void Stop() = 0; // Captures current cursor shape and position (depending on the |mode| passed // to Init()). Calls Callback::OnMouseCursor() if cursor shape has // changed since the last call (or when Capture() is called for the first // time) and then Callback::OnMouseCursorPosition() if mode is set to // SHAPE_AND_POSITION. virtual void Capture() = 0; };
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_mac.mm +++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_mac.mm @@ -30,17 +30,18 @@ namespace webrtc { class MouseCursorMonitorMac : public MouseCursorMonitor { public: MouseCursorMonitorMac(const DesktopCaptureOptions& options, CGWindowID window_id, ScreenId screen_id); virtual ~MouseCursorMonitorMac(); - void Init(Callback* callback, Mode mode) override; + void Start(Callback* callback, Mode mode) override; + void Stop() override; void Capture() override; private: static void DisplaysReconfiguredCallback(CGDirectDisplayID display, CGDisplayChangeSummaryFlags flags, void *user_parameter); void DisplaysReconfigured(CGDirectDisplayID display, CGDisplayChangeSummaryFlags flags); @@ -73,24 +74,28 @@ MouseCursorMonitorMac::MouseCursorMonito rtc::GetOSVersionName() < rtc::kMacOSLion) { // Single screen capture is not supported on pre OS X 10.7. screen_id_ = kFullDesktopScreenId; } } MouseCursorMonitorMac::~MouseCursorMonitorMac() {} -void MouseCursorMonitorMac::Init(Callback* callback, Mode mode) { +void MouseCursorMonitorMac::Start(Callback* callback, Mode mode) { assert(!callback_); assert(callback); callback_ = callback; mode_ = mode; } +void MouseCursorMonitorMac::Stop() { + callback_ = NULL; +} + void MouseCursorMonitorMac::Capture() { assert(callback_); CaptureImage(); if (mode_ != SHAPE_AND_POSITION) return;
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_unittest.cc +++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_unittest.cc @@ -62,17 +62,17 @@ class MouseCursorMonitorTest : public te #endif TEST_F(MouseCursorMonitorTest, MAYBE(FromScreen)) { rtc::scoped_ptr<MouseCursorMonitor> capturer( MouseCursorMonitor::CreateForScreen( DesktopCaptureOptions::CreateDefault(), webrtc::kFullDesktopScreenId)); assert(capturer.get()); - capturer->Init(this, MouseCursorMonitor::SHAPE_AND_POSITION); + capturer->Start(this, MouseCursorMonitor::SHAPE_AND_POSITION); capturer->Capture(); EXPECT_TRUE(cursor_image_.get()); EXPECT_GE(cursor_image_->hotspot().x(), 0); EXPECT_LE(cursor_image_->hotspot().x(), cursor_image_->image()->size().width()); EXPECT_GE(cursor_image_->hotspot().y(), 0); EXPECT_LE(cursor_image_->hotspot().y(), @@ -101,31 +101,31 @@ TEST_F(MouseCursorMonitorTest, MAYBE(Fro cursor_image_.reset(); position_received_ = false; rtc::scoped_ptr<MouseCursorMonitor> capturer( MouseCursorMonitor::CreateForWindow( DesktopCaptureOptions::CreateDefault(), windows[i].id)); assert(capturer.get()); - capturer->Init(this, MouseCursorMonitor::SHAPE_AND_POSITION); + capturer->Start(this, MouseCursorMonitor::SHAPE_AND_POSITION); capturer->Capture(); EXPECT_TRUE(cursor_image_.get()); EXPECT_TRUE(position_received_); } } // Make sure that OnMouseCursorPosition() is not called in the SHAPE_ONLY mode. TEST_F(MouseCursorMonitorTest, MAYBE(ShapeOnly)) { rtc::scoped_ptr<MouseCursorMonitor> capturer( MouseCursorMonitor::CreateForScreen( DesktopCaptureOptions::CreateDefault(), webrtc::kFullDesktopScreenId)); assert(capturer.get()); - capturer->Init(this, MouseCursorMonitor::SHAPE_ONLY); + capturer->Start(this, MouseCursorMonitor::SHAPE_ONLY); capturer->Capture(); EXPECT_TRUE(cursor_image_.get()); EXPECT_FALSE(position_received_); } } // namespace webrtc
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_win.cc +++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_win.cc @@ -21,17 +21,18 @@ namespace webrtc { class MouseCursorMonitorWin : public MouseCursorMonitor { public: explicit MouseCursorMonitorWin(HWND window); explicit MouseCursorMonitorWin(ScreenId screen); virtual ~MouseCursorMonitorWin(); - void Init(Callback* callback, Mode mode) override; + void Start(Callback* callback, Mode mode) override; + void Stop() override; void Capture() override; private: // Get the rect of the currently selected screen, relative to the primary // display's top-left. If the screen is disabled or disconnected, or any error // happens, an empty rect is returned. DesktopRect GetScreenRect(); @@ -65,27 +66,35 @@ MouseCursorMonitorWin::MouseCursorMonito assert(screen >= kFullDesktopScreenId); } MouseCursorMonitorWin::~MouseCursorMonitorWin() { if (desktop_dc_) ReleaseDC(NULL, desktop_dc_); } -void MouseCursorMonitorWin::Init(Callback* callback, Mode mode) { +void MouseCursorMonitorWin::Start(Callback* callback, Mode mode) { assert(!callback_); assert(callback); assert(IsGUIThread(false)); callback_ = callback; mode_ = mode; desktop_dc_ = GetDC(NULL); } +void MouseCursorMonitorWin::Stop() { + callback_ = NULL; + + if (desktop_dc_) + ReleaseDC(NULL, desktop_dc_); + desktop_dc_ = NULL; +} + void MouseCursorMonitorWin::Capture() { assert(IsGUIThread(false)); assert(callback_); CURSORINFO cursor_info; cursor_info.cbSize = sizeof(CURSORINFO); if (!GetCursorInfo(&cursor_info)) { LOG_F(LS_ERROR) << "Unable to get cursor info. Error = " << GetLastError();
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_x11.cc +++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_x11.cc @@ -59,17 +59,18 @@ Window GetTopLevelWindow(Display* displa namespace webrtc { class MouseCursorMonitorX11 : public MouseCursorMonitor, public SharedXDisplay::XEventHandler { public: MouseCursorMonitorX11(const DesktopCaptureOptions& options, Window window, Window inner_window); virtual ~MouseCursorMonitorX11(); - void Init(Callback* callback, Mode mode) override; + void Start(Callback* callback, Mode mode) override; + void Stop() override; void Capture() override; private: // SharedXDisplay::XEventHandler interface. bool HandleXEvent(const XEvent& event) override; Display* display() { return x_display_->display(); } @@ -98,24 +99,21 @@ MouseCursorMonitorX11::MouseCursorMonito mode_(SHAPE_AND_POSITION), window_(window), inner_window_(inner_window), have_xfixes_(false), xfixes_event_base_(-1), xfixes_error_base_(-1) {} MouseCursorMonitorX11::~MouseCursorMonitorX11() { - if (have_xfixes_) { - x_display_->RemoveEventHandler(xfixes_event_base_ + XFixesCursorNotify, - this); - } + Stop(); } -void MouseCursorMonitorX11::Init(Callback* callback, Mode mode) { - // Init can be called only once per instance of MouseCursorMonitor. +void MouseCursorMonitorX11::Start(Callback* callback, Mode mode) { + // Start can be called only if not started assert(!callback_); assert(callback); callback_ = callback; mode_ = mode; have_xfixes_ = XFixesQueryExtension(display(), &xfixes_event_base_, &xfixes_error_base_); @@ -127,16 +125,24 @@ void MouseCursorMonitorX11::Init(Callbac x_display_->AddEventHandler(xfixes_event_base_ + XFixesCursorNotify, this); CaptureCursor(); } else { LOG(LS_INFO) << "X server does not support XFixes."; } } +void MouseCursorMonitorX11::Stop() { + callback_ = NULL; + if (have_xfixes_) { + x_display_->RemoveEventHandler(xfixes_event_base_ + XFixesCursorNotify, + this); + } +} + void MouseCursorMonitorX11::Capture() { assert(callback_); // Process X11 events in case XFixes has sent cursor notification. x_display_->ProcessPendingXEvents(); // cursor_shape_| is set only if we were notified of a cursor shape change. if (cursor_shape_.get())
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_mac.mm +++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_mac.mm @@ -192,16 +192,17 @@ class ScreenCapturerMac : public ScreenC explicit ScreenCapturerMac( scoped_refptr<DesktopConfigurationMonitor> desktop_config_monitor); virtual ~ScreenCapturerMac(); bool Init(); // Overridden from ScreenCapturer: void Start(Callback* callback) override; + void Stop() override; void Capture(const DesktopRegion& region) override; void SetExcludedWindow(WindowId window) override; bool GetScreenList(ScreenList* screens) override; bool SelectScreen(ScreenId id) override; private: void GlBlitFast(const DesktopFrame& frame, const DesktopRegion& region); @@ -380,16 +381,29 @@ void ScreenCapturerMac::Start(Callback* // This assertion ensures that the display is woken up if it already asleep // (as used by Apple Remote Desktop). IOPMAssertionCreateWithName(CFSTR("UserIsActive"), kIOPMAssertionLevelOn, CFSTR("Chrome Remote Desktop connection active"), &power_assertion_id_user_); } +void ScreenCapturerMac::Stop() { + if (power_assertion_id_display_ != kIOPMNullAssertionID) { + IOPMAssertionRelease(power_assertion_id_display_); + power_assertion_id_display_ = kIOPMNullAssertionID; + } + if (power_assertion_id_user_ != kIOPMNullAssertionID) { + IOPMAssertionRelease(power_assertion_id_user_); + power_assertion_id_user_ = kIOPMNullAssertionID; + } + + callback_ = NULL; +} + void ScreenCapturerMac::Capture(const DesktopRegion& region_to_capture) { TickTime capture_start_time = TickTime::Now(); queue_.MoveToNextFrame(); desktop_config_monitor_->Lock(); MacDesktopConfiguration new_config = desktop_config_monitor_->desktop_configuration();
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_x11.cc +++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_x11.cc @@ -46,16 +46,17 @@ class ScreenCapturerLinux : public Scree ScreenCapturerLinux(); virtual ~ScreenCapturerLinux(); // TODO(ajwong): Do we really want this to be synchronous? bool Init(const DesktopCaptureOptions& options); // DesktopCapturer interface. void Start(Callback* delegate) override; + void Stop() override; void Capture(const DesktopRegion& region) override; // ScreenCapturer interface. bool GetScreenList(ScreenList* screens) override; bool SelectScreen(ScreenId id) override; private: Display* display() { return options_.x_display()->display(); } @@ -234,16 +235,20 @@ void ScreenCapturerLinux::InitXDamage() void ScreenCapturerLinux::Start(Callback* callback) { DCHECK(!callback_); DCHECK(callback); callback_ = callback; } +void ScreenCapturerLinux::Stop() { + callback_ = NULL; +} + void ScreenCapturerLinux::Capture(const DesktopRegion& region) { TickTime capture_start_time = TickTime::Now(); queue_.MoveToNextFrame(); // Process XEvents for XDamage and cursor shape tracking. options_.x_display()->ProcessPendingXEvents();
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capturer_win_gdi.cc +++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capturer_win_gdi.cc @@ -54,26 +54,17 @@ ScreenCapturerWinGdi::ScreenCapturerWinG composition_enabled_func_ = reinterpret_cast<DwmIsCompositionEnabledFunc> (GetProcAddress(dwmapi_library_, "DwmIsCompositionEnabled")); } disable_composition_ = options.disable_effects(); } ScreenCapturerWinGdi::~ScreenCapturerWinGdi() { - if (desktop_dc_) - ReleaseDC(NULL, desktop_dc_); - if (memory_dc_) - DeleteDC(memory_dc_); - - if (disable_composition_) { - // Restore Aero. - if (composition_func_) - (*composition_func_)(DWM_EC_ENABLECOMPOSITION); - } + Stop(); if (dwmapi_library_) FreeLibrary(dwmapi_library_); } void ScreenCapturerWinGdi::Capture(const DesktopRegion& region) { assert(IsGUIThread(false)); TickTime capture_start_time = TickTime::Now(); @@ -160,16 +151,34 @@ void ScreenCapturerWinGdi::Start(Callbac // Vote to disable Aero composited desktop effects while capturing. Windows // will restore Aero automatically if the process exits. This has no effect // under Windows 8 or higher. See crbug.com/124018. if (composition_func_) (*composition_func_)(DWM_EC_DISABLECOMPOSITION); } } +void ScreenCapturerWinGdi::Stop() { + if (desktop_dc_) { + ReleaseDC(NULL, desktop_dc_); + desktop_dc_ = NULL; + } + if (memory_dc_) { + DeleteDC(memory_dc_); + memory_dc_ = NULL; + } + + if (disable_composition_) { + // Restore Aero. + if (composition_func_) + (*composition_func_)(DWM_EC_ENABLECOMPOSITION); + } + callback_ = NULL; +} + void ScreenCapturerWinGdi::PrepareCaptureResources() { assert(IsGUIThread(false)); // Switch to the desktop receiving user input if different from the current // one. rtc::scoped_ptr<Desktop> input_desktop(Desktop::GetInputDesktop()); if (input_desktop.get() != NULL && !desktop_.IsSame(*input_desktop)) { // Release GDI resources otherwise SetThreadDesktop will fail. if (desktop_dc_) {
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capturer_win_gdi.h +++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capturer_win_gdi.h @@ -29,16 +29,17 @@ class Differ; // ScreenCapturerWinGdi is double-buffered as required by ScreenCapturer. class ScreenCapturerWinGdi : public ScreenCapturer { public: explicit ScreenCapturerWinGdi(const DesktopCaptureOptions& options); virtual ~ScreenCapturerWinGdi(); // Overridden from ScreenCapturer: void Start(Callback* callback) override; + void Stop() override; void Capture(const DesktopRegion& region) override; bool GetScreenList(ScreenList* screens) override; bool SelectScreen(ScreenId id) override; private: typedef HRESULT (WINAPI * DwmEnableCompositionFunc)(UINT); typedef HRESULT (WINAPI * DwmIsCompositionEnabledFunc)(BOOL*);
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.cc +++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.cc @@ -52,39 +52,53 @@ ScreenCapturerWinMagnifier::ScreenCaptur set_image_scaling_callback_func_(NULL), host_window_(NULL), magnifier_window_(NULL), magnifier_initialized_(false), magnifier_capture_succeeded_(true) { } ScreenCapturerWinMagnifier::~ScreenCapturerWinMagnifier() { - // DestroyWindow must be called before MagUninitialize. magnifier_window_ is - // destroyed automatically when host_window_ is destroyed. - if (host_window_) - DestroyWindow(host_window_); - - if (magnifier_initialized_) - mag_uninitialize_func_(); - - if (mag_lib_handle_) - FreeLibrary(mag_lib_handle_); - - if (desktop_dc_) - ReleaseDC(NULL, desktop_dc_); + Stop(); } void ScreenCapturerWinMagnifier::Start(Callback* callback) { assert(!callback_); assert(callback); callback_ = callback; InitializeMagnifier(); } +void ScreenCapturerWinMagnifier::Stop() { + callback_ = NULL; + + // DestroyWindow must be called before MagUninitialize. magnifier_window_ is + // destroyed automatically when host_window_ is destroyed. + if (host_window_) { + DestroyWindow(host_window_); + host_window_ = NULL; + } + + if (magnifier_initialized_) { + mag_uninitialize_func_(); + magnifier_initialized_ = false; + } + + if (mag_lib_handle_) { + FreeLibrary(mag_lib_handle_); + mag_lib_handle_ = NULL; + } + + if (desktop_dc_) { + ReleaseDC(NULL, desktop_dc_); + desktop_dc_ = NULL; + } +} + void ScreenCapturerWinMagnifier::Capture(const DesktopRegion& region) { TickTime capture_start_time = TickTime::Now(); queue_.MoveToNextFrame(); // Request that the system not power-down the system, or the display hardware. if (!SetThreadExecutionState(ES_DISPLAY_REQUIRED | ES_SYSTEM_REQUIRED)) { if (!set_thread_execution_state_failed_) {
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.h +++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.h @@ -39,16 +39,17 @@ class ScreenCapturerWinMagnifier : publi // screen is being captured, or the OS does not support Magnification API, or // the magnifier capturer fails (e.g. in Windows8 Metro mode). explicit ScreenCapturerWinMagnifier( rtc::scoped_ptr<ScreenCapturer> fallback_capturer); virtual ~ScreenCapturerWinMagnifier(); // Overridden from ScreenCapturer: void Start(Callback* callback) override; + void Stop() override; void Capture(const DesktopRegion& region) override; bool GetScreenList(ScreenList* screens) override; bool SelectScreen(ScreenId id) override; void SetExcludedWindow(WindowId window) override; private: typedef BOOL(WINAPI* MagImageScalingCallback)(HWND hwnd, void* srcdata,
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_mac.mm +++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_mac.mm @@ -51,16 +51,17 @@ class WindowCapturerMac : public WindowC // WindowCapturer interface. bool GetWindowList(WindowList* windows) override; bool SelectWindow(WindowId id) override; bool BringSelectedWindowToFront() override; // DesktopCapturer interface. void Start(Callback* callback) override; + void Stop() override; void Capture(const DesktopRegion& region) override; private: Callback* callback_; // The window being captured. CGWindowID window_id_; @@ -182,16 +183,20 @@ bool WindowCapturerMac::BringSelectedWin void WindowCapturerMac::Start(Callback* callback) { assert(!callback_); assert(callback); callback_ = callback; } +void WindowCapturerMac::Stop() { + callback_ = NULL; +} + void WindowCapturerMac::Capture(const DesktopRegion& region) { if (!IsWindowValid(window_id_)) { callback_->OnCaptureCompleted(NULL); return; } CGWindowID on_screen_window = window_id_; if (full_screen_chrome_window_detector_) {
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_null.cc +++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_null.cc @@ -25,16 +25,17 @@ class WindowCapturerNull : public Window // WindowCapturer interface. bool GetWindowList(WindowList* windows) override; bool SelectWindow(WindowId id) override; bool BringSelectedWindowToFront() override; // DesktopCapturer interface. void Start(Callback* callback) override; + void Stop() override; void Capture(const DesktopRegion& region) override; private: Callback* callback_; DISALLOW_COPY_AND_ASSIGN(WindowCapturerNull); }; @@ -62,16 +63,20 @@ bool WindowCapturerNull::BringSelectedWi void WindowCapturerNull::Start(Callback* callback) { assert(!callback_); assert(callback); callback_ = callback; } +void WindowCapturerNull::Stop() { + callback_ = NULL; +} + void WindowCapturerNull::Capture(const DesktopRegion& region) { // Not implemented yet. callback_->OnCaptureCompleted(NULL); } } // namespace // static
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_win.cc +++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_win.cc @@ -89,16 +89,17 @@ class WindowCapturerWin : public WindowC // WindowCapturer interface. bool GetWindowList(WindowList* windows) override; bool SelectWindow(WindowId id) override; bool BringSelectedWindowToFront() override; // DesktopCapturer interface. void Start(Callback* callback) override; + void Stop() override; void Capture(const DesktopRegion& region) override; private: bool IsAeroEnabled(); Callback* callback_; // HWND and HDC for the currently selected window or NULL if window is not @@ -174,16 +175,20 @@ bool WindowCapturerWin::BringSelectedWin void WindowCapturerWin::Start(Callback* callback) { assert(!callback_); assert(callback); callback_ = callback; } +void WindowCapturerWin::Stop() { + callback_ = NULL; +} + void WindowCapturerWin::Capture(const DesktopRegion& region) { assert(IsGUIThread(false)); if (!window_) { LOG(LS_ERROR) << "Window hasn't been selected: " << GetLastError(); callback_->OnCaptureCompleted(NULL); return; }
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_x11.cc +++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_x11.cc @@ -41,16 +41,17 @@ class WindowCapturerLinux : public Windo // WindowCapturer interface. bool GetWindowList(WindowList* windows) override; bool SelectWindow(WindowId id) override; bool BringSelectedWindowToFront() override; // DesktopCapturer interface. void Start(Callback* callback) override; + void Stop() override; void Capture(const DesktopRegion& region) override; // SharedXDisplay::XEventHandler interface. bool HandleXEvent(const XEvent& event) override; private: Display* display() { return x_display_->display(); } @@ -230,16 +231,20 @@ bool WindowCapturerLinux::BringSelectedW void WindowCapturerLinux::Start(Callback* callback) { assert(!callback_); assert(callback); callback_ = callback; } +void WindowCapturerLinux::Stop() { + callback_ = NULL; +} + void WindowCapturerLinux::Capture(const DesktopRegion& region) { x_display_->ProcessPendingXEvents(); if (!x_server_pixel_buffer_.IsWindowValid()) { LOG(LS_INFO) << "The window is no longer valid."; callback_->OnCaptureCompleted(NULL); return; }
--- a/media/webrtc/trunk/webrtc/video_engine/desktop_capture_impl.cc +++ b/media/webrtc/trunk/webrtc/video_engine/desktop_capture_impl.cc @@ -467,20 +467,21 @@ DesktopCaptureImpl::DesktopCaptureImpl(c _lastProcessFrameCount(TickTime::Now()), _rotateFrame(kVideoRotation_0), last_capture_time_(TickTime::MillisecondTimestamp()), delta_ntp_internal_ms_( Clock::GetRealTimeClock()->CurrentNtpInMilliseconds() - TickTime::MillisecondTimestamp()), time_event_(EventWrapper::Create()), #if defined(_WIN32) - capturer_thread_(ThreadWrapper::CreateUIThread(Run, this, "ScreenCaptureThread")) { + capturer_thread_(ThreadWrapper::CreateUIThread(Run, this, "ScreenCaptureThread")), #else - capturer_thread_(ThreadWrapper::CreateThread(Run, this, "ScreenCaptureThread")) { + capturer_thread_(ThreadWrapper::CreateThread(Run, this, "ScreenCaptureThread")), #endif + started_(false) { capturer_thread_->SetPriority(kHighPriority); _requestedCapability.width = kDefaultWidth; _requestedCapability.height = kDefaultHeight; _requestedCapability.maxFPS = 30; _requestedCapability.rawType = kVideoI420; _requestedCapability.codecType = kVideoCodecUnknown; memset(_incomingFrameTimes, 0, sizeof(_incomingFrameTimes)); } @@ -778,26 +779,33 @@ int32_t DesktopCaptureImpl::StartCapture _requestedCapability = capability; #if defined(_WIN32) uint32_t maxFPSNeeded = 1000/_requestedCapability.maxFPS; capturer_thread_->RequestCallbackTimer(maxFPSNeeded); #endif desktop_capturer_cursor_composer_->Start(this); capturer_thread_->Start(); + started_ = true; return 0; } int32_t DesktopCaptureImpl::StopCapture() { + if (started_) { + capturer_thread_->Stop(); // thread is guaranteed stopped before this returns + desktop_capturer_cursor_composer_->Stop(); + started_ = false; + return 0; + } return -1; } bool DesktopCaptureImpl::CaptureStarted() { - return false; + return started_; } int32_t DesktopCaptureImpl::CaptureSettings(VideoCaptureCapability& settings) { return -1; } void DesktopCaptureImpl::OnCaptureCompleted(DesktopFrame* frame) { if (frame == NULL) return;
--- a/media/webrtc/trunk/webrtc/video_engine/desktop_capture_impl.h +++ b/media/webrtc/trunk/webrtc/video_engine/desktop_capture_impl.h @@ -256,13 +256,14 @@ public: return true; }; void process(); private: rtc::scoped_ptr<DesktopAndCursorComposer> desktop_capturer_cursor_composer_; rtc::scoped_ptr<EventWrapper> time_event_; rtc::scoped_ptr<ThreadWrapper> capturer_thread_; + bool started_; }; } // namespace webrtc #endif // WEBRTC_MODULES_DESKTOP_CAPTURE_MAIN_SOURCE_DESKTOP_CAPTURE_IMPL_H_
--- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -386,17 +386,17 @@ pref("media.apple.mp4.enabled", true); // media.gmp.storage.version.observed, and if the versions don't match, // we clear storage and set media.gmp.storage.version.observed=expected. // This provides a mechanism to clear GMP storage when non-compatible // changes are made. pref("media.gmp.storage.version.expected", 1); // Filter what triggers user notifications. // See DecoderDoctorDocumentWatcher::ReportAnalysis for details. -pref("media.decoder-doctor.notifications-allowed", "MediaWMFNeeded,MediaWidevineNoWMFNoSilverlight,MediaCannotInitializePulseAudio"); +pref("media.decoder-doctor.notifications-allowed", "MediaWMFNeeded,MediaWidevineNoWMFNoSilverlight,MediaCannotInitializePulseAudio,MediaCannotPlayNoDecoders,MediaNoDecoders"); // Whether we report partial failures. pref("media.decoder-doctor.verbose", false); // Whether DD should consider WMF-disabled a WMF failure, useful for testing. pref("media.decoder-doctor.wmf-disabled-is-failure", false); // Whether to suspend decoding of videos in background tabs. #ifdef NIGHTLY_BUILD pref("media.suspend-bkgnd-video.enabled", true);
--- a/netwerk/base/LoadInfo.cpp +++ b/netwerk/base/LoadInfo.cpp @@ -16,16 +16,17 @@ #include "nsIDOMDocument.h" #include "nsIFrameLoader.h" #include "nsIInterfaceRequestorUtils.h" #include "nsISupportsImpl.h" #include "nsISupportsUtils.h" #include "nsContentUtils.h" #include "nsDocShell.h" #include "nsGlobalWindow.h" +#include "nsNullPrincipal.h" using namespace mozilla::dom; namespace mozilla { namespace net { static void InheritOriginAttributes(nsIPrincipal* aLoadingPrincipal, NeckoOriginAttributes& aAttrs) @@ -539,16 +540,24 @@ NS_IMETHODIMP LoadInfo::GetForceInheritPrincipal(bool* aInheritPrincipal) { *aInheritPrincipal = (mSecurityFlags & nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL); return NS_OK; } NS_IMETHODIMP +LoadInfo::GetForceInheritPrincipalOverruleOwner(bool* aInheritPrincipal) +{ + *aInheritPrincipal = + (mSecurityFlags & nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL_OVERRULE_OWNER); + return NS_OK; +} + +NS_IMETHODIMP LoadInfo::GetLoadingSandboxed(bool* aLoadingSandboxed) { *aLoadingSandboxed = (mSecurityFlags & nsILoadInfo::SEC_SANDBOXED); return NS_OK; } NS_IMETHODIMP LoadInfo::GetAboutBlankInherits(bool* aResult) @@ -682,16 +691,44 @@ LoadInfo::GetScriptableOriginAttributes( { if (NS_WARN_IF(!ToJSValue(aCx, mOriginAttributes, aOriginAttributes))) { return NS_ERROR_FAILURE; } return NS_OK; } NS_IMETHODIMP +LoadInfo::ResetPrincipalsToNullPrincipal() +{ + // take the originAttributes from the LoadInfo and create + // a new NullPrincipal using those origin attributes. + PrincipalOriginAttributes pAttrs; + pAttrs.InheritFromNecko(mOriginAttributes); + nsCOMPtr<nsIPrincipal> newNullPrincipal = nsNullPrincipal::Create(pAttrs); + + MOZ_ASSERT(mInternalContentPolicyType != nsIContentPolicy::TYPE_DOCUMENT || + !mLoadingPrincipal, + "LoadingPrincipal should be null for toplevel loads"); + + // the loadingPrincipal for toplevel loads is always a nullptr; + if (mInternalContentPolicyType != nsIContentPolicy::TYPE_DOCUMENT) { + mLoadingPrincipal = newNullPrincipal; + } + mTriggeringPrincipal = newNullPrincipal; + mPrincipalToInherit = newNullPrincipal; + + // setting SEC_FORCE_INHERIT_PRINCIPAL_OVERRULE_OWNER will overrule + // any non null owner set on the channel and will return the principal + // form the loadinfo instead. + mSecurityFlags |= SEC_FORCE_INHERIT_PRINCIPAL_OVERRULE_OWNER; + + return NS_OK; +} + +NS_IMETHODIMP LoadInfo::SetScriptableOriginAttributes(JSContext* aCx, JS::Handle<JS::Value> aOriginAttributes) { NeckoOriginAttributes attrs; if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) { return NS_ERROR_INVALID_ARG; }
--- a/netwerk/base/nsILoadInfo.idl +++ b/netwerk/base/nsILoadInfo.idl @@ -176,16 +176,28 @@ interface nsILoadInfo : nsISupports /** * Load an error page, it should be one of following : about:neterror, * about:certerror, about:blocked, or about:tabcrashed. */ const unsigned long SEC_LOAD_ERROR_PAGE = (1<<13); /** + * Force inheriting of the principalToInherit, overruling any owner + * that might be set on the channel. (Please note that channel.owner + * is deprecated and will be removed within Bug 1286838). + * Setting this flag will cause GetChannelResultPrincipal to return the + * principalToInherit set in the loadInfo. + * + * This will happen independently of the scheme of the URI that the + * channel is loading. + */ + const unsigned long SEC_FORCE_INHERIT_PRINCIPAL_OVERRULE_OWNER = (1<<14); + + /** * This is the principal of the network request's caller/requester where * the resulting resource will be used. I.e. it is the principal which * will get access to the result of the request. (Where "get access to" * might simply mean "embed" depending on the type of resource that is * loaded). * * For example for an image, it is the principal of the document where * the image is rendered. For a stylesheet it is the principal of the @@ -348,16 +360,24 @@ interface nsILoadInfo : nsISupports * If forceInheritPrincipal is true, the data coming from the channel should * use loadingPrincipal for its principal, even when the data is loaded over * http:// or another protocol that would normally use a URI-based principal. * This attribute will never be true when loadingSandboxed is true. */ [infallible] readonly attribute boolean forceInheritPrincipal; /** + * If forceInheritPrincipalOverruleOwner is true, the data coming from the + * channel should use principalToInherit for its principal, even when the + * data is loaded over http:// or another protocol that would normally use + * a URI-based principal. + */ + [infallible] readonly attribute boolean forceInheritPrincipalOverruleOwner; + + /** * If loadingSandboxed is true, the data coming from the channel is * being loaded sandboxed, so it should have a nonce origin and * hence should use a NullPrincipal. */ [infallible] readonly attribute boolean loadingSandboxed; /** * If aboutBlankInherits is true, then about:blank should inherit @@ -473,16 +493,32 @@ interface nsILoadInfo : nsISupports * the frameOuterWindowID is the outer window containing the * foo.html document. * * Note: For other cases, frameOuterWindowID is 0. */ [infallible] readonly attribute unsigned long long frameOuterWindowID; /** + * For all loads of none TYPE_DOUCMENT this function resets the + * LoadingPrincipal, the TriggeringPrincipal and the + * PrincipalToInherit to a freshly created NullPrincipal which inherits + * the current origin attributes from the loadinfo. + * For loads of TYPE_DOCUMENT this function resets only the + * TriggeringPrincipal as well as the PrincipalToInherit to a freshly + * created NullPrincipal which inherits the origin attributes from + * the loadInfo. (Please note that the LoadingPrincipal for TYPE_DOCUMENT + * loads is always null.) + * + * WARNING: Please only use that function if you know exactly what + * you are doing!!! + */ + void resetPrincipalsToNullPrincipal(); + + /** * Customized NeckoOriginAttributes within LoadInfo to allow overwriting of the * default originAttributes from the loadingPrincipal. * * In chrome side, originAttributes.privateBrowsingId will always be 0 even if * the usePrivateBrowsing is true, because chrome docshell won't set * privateBrowsingId on origin attributes (See bug 1278664). This is to make * sure nsILoadInfo and nsILoadContext have the same origin attributes. */
--- a/netwerk/ipc/NeckoParent.cpp +++ b/netwerk/ipc/NeckoParent.cpp @@ -99,77 +99,109 @@ PBOverrideStatusFromLoadContext(const Se if (!aSerialized.IsNotNull() && aSerialized.IsPrivateBitValid()) { return (aSerialized.mOriginAttributes.mPrivateBrowsingId > 0) ? kPBOverride_Private : kPBOverride_NotPrivate; } return kPBOverride_Unset; } +// Bug 1289001 - If GetValidatedAppInfo returns an error string, that usually +// leads to a content crash with very little info about the cause. +// We prefer to crash on the parent, so we get the reason in the crash report. +static MOZ_NORETURN MOZ_COLD +void CrashWithReason(const char * reason) +{ +#ifndef RELEASE_BUILD + MOZ_CRASH_ANNOTATE(reason); + MOZ_REALLY_CRASH(); +#endif +} + const char* NeckoParent::GetValidatedAppInfo(const SerializedLoadContext& aSerialized, PContentParent* aContent, DocShellOriginAttributes& aAttrs) { if (UsingNeckoIPCSecurity()) { if (!aSerialized.IsNotNull()) { + CrashWithReason("GetValidatedAppInfo | SerializedLoadContext from child is null"); return "SerializedLoadContext from child is null"; } } + nsAutoCString debugString; nsTArray<TabContext> contextArray = static_cast<ContentParent*>(aContent)->GetManagedTabContext(); for (uint32_t i = 0; i < contextArray.Length(); i++) { TabContext tabContext = contextArray[i]; uint32_t appId = tabContext.OwnOrContainingAppId(); bool inBrowserElement = aSerialized.IsNotNull() ? aSerialized.mOriginAttributes.mInIsolatedMozBrowser : tabContext.IsIsolatedMozBrowserElement(); if (appId == NECKO_UNKNOWN_APP_ID) { + debugString.Append("u,"); continue; } // We may get appID=NO_APP if child frame is neither a browser nor an app if (appId == NECKO_NO_APP_ID && tabContext.HasOwnApp()) { // NECKO_NO_APP_ID but also is an app? Weird, skip. + debugString.Append("h,"); continue; } if (!aSerialized.mOriginAttributes.mSignedPkg.IsEmpty() && aSerialized.mOriginAttributes.mSignedPkg != tabContext.OriginAttributesRef().mSignedPkg) { + debugString.Append("s,"); continue; } if (aSerialized.mOriginAttributes.mUserContextId != tabContext.OriginAttributesRef().mUserContextId) { + debugString.Append("("); + debugString.AppendInt(aSerialized.mOriginAttributes.mUserContextId); + debugString.Append(","); + debugString.AppendInt(tabContext.OriginAttributesRef().mUserContextId); + debugString.Append(")"); continue; } aAttrs = DocShellOriginAttributes(); aAttrs.mAppId = appId; aAttrs.mInIsolatedMozBrowser = inBrowserElement; aAttrs.mSignedPkg = aSerialized.mOriginAttributes.mSignedPkg; aAttrs.mUserContextId = aSerialized.mOriginAttributes.mUserContextId; aAttrs.mPrivateBrowsingId = aSerialized.mOriginAttributes.mPrivateBrowsingId; aAttrs.mFirstPartyDomain = aSerialized.mOriginAttributes.mFirstPartyDomain; return nullptr; } if (contextArray.Length() != 0) { + nsAutoCString errorString; + errorString.Append("GetValidatedAppInfo | App does not have permission -"); + errorString.Append(debugString); + + // Leak the buffer on the heap to make sure that it lives long enough, as + // MOZ_CRASH_ANNOTATE expects the pointer passed to it to live to the end of + // the program. + char * error = strdup(errorString.BeginReading()); + CrashWithReason(error); return "App does not have permission"; } if (!UsingNeckoIPCSecurity()) { // We are running xpcshell tests if (aSerialized.IsNotNull()) { aAttrs = aSerialized.mOriginAttributes; } else { aAttrs = DocShellOriginAttributes(NECKO_NO_APP_ID, false); } return nullptr; } + CrashWithReason("GetValidatedAppInfo | ContentParent does not have any PBrowsers"); return "ContentParent does not have any PBrowsers"; } const char * NeckoParent::CreateChannelLoadContext(const PBrowserOrId& aBrowser, PContentParent* aContent, const SerializedLoadContext& aSerialized, nsCOMPtr<nsILoadContext> &aResult)
--- a/security/nss/cmd/derdump/derdump.c +++ b/security/nss/cmd/derdump/derdump.c @@ -29,47 +29,48 @@ Usage(char *progName) } int main(int argc, char **argv) { char *progName; FILE *outFile; PRFileDesc *inFile; - SECItem der; + SECItem der = { siBuffer, NULL, 0 }; SECStatus rv; PRInt16 xp_error; PRBool raw = PR_FALSE; PLOptState *optstate; PLOptStatus status; + int retval = -1; progName = strrchr(argv[0], '/'); progName = progName ? progName + 1 : argv[0]; /* Parse command line arguments */ inFile = 0; outFile = 0; optstate = PL_CreateOptState(argc, argv, "i:o:r"); while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { switch (optstate->option) { case 'i': inFile = PR_Open(optstate->value, PR_RDONLY, 0); if (!inFile) { fprintf(stderr, "%s: unable to open \"%s\" for reading\n", progName, optstate->value); - return -1; + goto cleanup; } break; case 'o': outFile = fopen(optstate->value, "w"); if (!outFile) { fprintf(stderr, "%s: unable to open \"%s\" for writing\n", progName, optstate->value); - return -1; + goto cleanup; } break; case 'r': raw = PR_TRUE; break; default: @@ -80,30 +81,48 @@ main(int argc, char **argv) if (status == PL_OPT_BAD) Usage(progName); if (!inFile) inFile = PR_STDIN; if (!outFile) outFile = stdout; - rv = NSS_NoDB_Init(NULL); /* XXX */ + rv = NSS_NoDB_Init(NULL); if (rv != SECSuccess) { SECU_PrintPRandOSError(progName); - return -1; + goto cleanup; } rv = SECU_ReadDERFromFile(&der, inFile, PR_FALSE, PR_FALSE); if (rv == SECSuccess) { rv = DER_PrettyPrint(outFile, &der, raw); - if (rv == SECSuccess) - return 0; + if (rv == SECSuccess) { + retval = 0; + goto cleanup; + } } xp_error = PORT_GetError(); if (xp_error) { SECU_PrintError(progName, "error %d", xp_error); } if (errno) { SECU_PrintSystemError(progName, "errno=%d", errno); } - return 1; + retval = 1; + +cleanup: + retval |= NSS_Shutdown(); + if (inFile) { + PR_Close(inFile); + } + if (outFile) { + fflush(outFile); + fclose(outFile); + } + PL_DestroyOptState(optstate); + if (der.data) { + PORT_Free(der.data); + } + + return retval; }
--- a/security/nss/coreconf/coreconf.dep +++ b/security/nss/coreconf/coreconf.dep @@ -5,8 +5,9 @@ /* * A dummy header file that is a dependency for all the object files. * Used to force a full recompilation of NSS in Mozilla's Tinderbox * depend builds. See comments in rules.mk. */ #error "Do not include this header file." +
--- a/security/nss/coreconf/sanitizers.mk +++ b/security/nss/coreconf/sanitizers.mk @@ -1,13 +1,19 @@ # Address Sanitizer support; include this in OS-specific .mk files # *after* defining the variables that are appended to here. ifeq ($(USE_ASAN), 1) -SANITIZER_FLAGS_COMMON = -fsanitize=address $(EXTRA_SANITIZER_FLAGS) +SANITIZER_FLAGS_COMMON = -fsanitize=address + +ifeq ($(USE_UBSAN), 1) +SANITIZER_FLAGS_COMMON += -fsanitize=undefined -fno-sanitize-recover=undefined +endif + +SANITIZER_FLAGS_COMMON += $(EXTRA_SANITIZER_FLAGS) SANITIZER_CFLAGS = $(SANITIZER_FLAGS_COMMON) SANITIZER_LDFLAGS = $(SANITIZER_FLAGS_COMMON) OS_CFLAGS += $(SANITIZER_CFLAGS) LDFLAGS += $(SANITIZER_LDFLAGS) # ASan needs frame pointers to save stack traces for allocation/free sites. # (Warning: some platforms, like ARM Linux in Thumb mode, don't have useful # frame pointers even with this option.)
--- a/security/nss/external_tests/ssl_gtest/databuffer.h +++ b/security/nss/external_tests/ssl_gtest/databuffer.h @@ -59,29 +59,34 @@ class DataBuffer { data_ = nullptr; len_ = 0; } } // Write will do a new allocation and expand the size of the buffer if needed. // Returns the offset of the end of the write. size_t Write(size_t index, const uint8_t* val, size_t count) { + assert(val); if (index + count > len_) { size_t newlen = index + count; uint8_t* tmp = new uint8_t[newlen]; // Always > 0. - memcpy(static_cast<void*>(tmp), static_cast<const void*>(data_), len_); + if (data_) { + memcpy(static_cast<void*>(tmp), static_cast<const void*>(data_), len_); + } if (index > len_) { memset(static_cast<void*>(tmp + len_), 0, index - len_); } delete[] data_; data_ = tmp; len_ = newlen; } - memcpy(static_cast<void*>(data_ + index), static_cast<const void*>(val), - count); + if (data_) { + memcpy(static_cast<void*>(data_ + index), static_cast<const void*>(val), + count); + } return index + count; } size_t Write(size_t index, const DataBuffer& buf) { return Write(index, buf.data(), buf.len()); } // Write an integer, also performing host-to-network order conversion. @@ -111,16 +116,17 @@ class DataBuffer { // Starting at |index|, remove |remove| bytes and replace them with the // contents of |buf|. void Splice(const DataBuffer& buf, size_t index, size_t remove = 0) { Splice(buf.data(), buf.len(), index, remove); } void Splice(const uint8_t* ins, size_t ins_len, size_t index, size_t remove = 0) { + assert(ins); uint8_t* old_value = data_; size_t old_len = len_; // The amount of stuff remaining from the tail of the old. size_t tail_len = old_len - std::min(old_len, index + remove); // The new length: the head of the old, the new, and the tail of the old. len_ = index + ins_len + tail_len; data_ = new uint8_t[len_ ? len_ : 1];
--- a/security/nss/external_tests/ssl_gtest/libssl_internals.c +++ b/security/nss/external_tests/ssl_gtest/libssl_internals.c @@ -142,18 +142,19 @@ PRBool SSLInt_CheckSecretsDestroyed(PRFi sslSocket *ss = ssl_FindSocket(fd); if (!ss) { return PR_FALSE; } CHECK_SECRET(currentSecret); CHECK_SECRET(resumptionPsk); CHECK_SECRET(dheSecret); - CHECK_SECRET(earlyTrafficSecret); - CHECK_SECRET(hsTrafficSecret); + CHECK_SECRET(clientEarlyTrafficSecret); + CHECK_SECRET(clientHsTrafficSecret); + CHECK_SECRET(serverHsTrafficSecret); return PR_TRUE; } PRBool sslint_DamageTrafficSecret(PRFileDesc *fd, size_t offset) { unsigned char data[32] = {0}; PK11SymKey **keyPtr; PK11SlotInfo *slot = PK11_GetInternalSlot(); @@ -175,24 +176,29 @@ PRBool sslint_DamageTrafficSecret(PRFile PK11_FreeSlot(slot); if (!*keyPtr) { return PR_FALSE; } return PR_TRUE; } -PRBool SSLInt_DamageHsTrafficSecret(PRFileDesc *fd) { +PRBool SSLInt_DamageClientHsTrafficSecret(PRFileDesc *fd) { return sslint_DamageTrafficSecret( - fd, offsetof(SSL3HandshakeState, hsTrafficSecret)); + fd, offsetof(SSL3HandshakeState, clientHsTrafficSecret)); +} + +PRBool SSLInt_DamageServerHsTrafficSecret(PRFileDesc *fd) { + return sslint_DamageTrafficSecret( + fd, offsetof(SSL3HandshakeState, serverHsTrafficSecret)); } PRBool SSLInt_DamageEarlyTrafficSecret(PRFileDesc *fd) { return sslint_DamageTrafficSecret( - fd, offsetof(SSL3HandshakeState, earlyTrafficSecret)); + fd, offsetof(SSL3HandshakeState, clientEarlyTrafficSecret)); } SECStatus SSLInt_Set0RttAlpn(PRFileDesc *fd, PRUint8 *data, unsigned int len) { sslSocket *ss = ssl_FindSocket(fd); if (!ss) { return SECFailure; }
--- a/security/nss/external_tests/ssl_gtest/libssl_internals.h +++ b/security/nss/external_tests/ssl_gtest/libssl_internals.h @@ -23,17 +23,18 @@ SECStatus SSLInt_UpdateSSLv2ClientRandom size_t msg_len); PRBool SSLInt_ExtensionNegotiated(PRFileDesc *fd, PRUint16 ext); void SSLInt_ClearSessionTicketKey(); PRInt32 SSLInt_CountTls13CipherSpecs(PRFileDesc *fd); void SSLInt_ForceTimerExpiry(PRFileDesc *fd); SECStatus SSLInt_SetMTU(PRFileDesc *fd, PRUint16 mtu); PRBool SSLInt_CheckSecretsDestroyed(PRFileDesc *fd); -PRBool SSLInt_DamageHsTrafficSecret(PRFileDesc *fd); +PRBool SSLInt_DamageClientHsTrafficSecret(PRFileDesc *fd); +PRBool SSLInt_DamageServerHsTrafficSecret(PRFileDesc *fd); PRBool SSLInt_DamageEarlyTrafficSecret(PRFileDesc *fd); SECStatus SSLInt_Set0RttAlpn(PRFileDesc *fd, PRUint8 *data, unsigned int len); PRBool SSLInt_HasCertWithAuthType(PRFileDesc *fd, SSLAuthType authType); PRBool SSLInt_SendAlert(PRFileDesc *fd, uint8_t level, uint8_t type); SECStatus SSLInt_AdvanceWriteSeqNum(PRFileDesc *fd, PRUint64 to); SECStatus SSLInt_AdvanceReadSeqNum(PRFileDesc *fd, PRUint64 to); SECStatus SSLInt_AdvanceWriteSeqByAWindow(PRFileDesc *fd, PRInt32 extra); SSLKEAType SSLInt_GetKEAType(SSLNamedGroup group);
--- a/security/nss/external_tests/ssl_gtest/ssl_0rtt_unittest.cc +++ b/security/nss/external_tests/ssl_gtest/ssl_0rtt_unittest.cc @@ -22,41 +22,61 @@ extern "C" { namespace nss_test { TEST_P(TlsConnectTls13, ZeroRtt) { SetupForZeroRtt(); client_->Set0RttEnabled(true); server_->Set0RttEnabled(true); ExpectResumption(RESUME_TICKET); - ZeroRttSendReceive(true); + ZeroRttSendReceive(true, true); Handshake(); ExpectEarlyDataAccepted(true); CheckConnected(); SendReceive(); } TEST_P(TlsConnectTls13, ZeroRttServerRejectByOption) { SetupForZeroRtt(); client_->Set0RttEnabled(true); ExpectResumption(RESUME_TICKET); - ZeroRttSendReceive(false); + ZeroRttSendReceive(true, false); + Handshake(); + CheckConnected(); + SendReceive(); +} + +// Test that we don't try to send 0-RTT data when the server sent +// us a ticket without the 0-RTT flags. +TEST_P(TlsConnectTls13, ZeroRttOptionsSetLate) { + ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); + Connect(); + SendReceive(); // Need to read so that we absorb the session ticket. + CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign); + Reset(); + server_->StartConnect(); + client_->StartConnect(); + // Now turn on 0-RTT but too late for the ticket. + client_->Set0RttEnabled(true); + server_->Set0RttEnabled(true); + ExpectResumption(RESUME_TICKET); + ZeroRttSendReceive(false, false); Handshake(); CheckConnected(); SendReceive(); } TEST_P(TlsConnectTls13, ZeroRttServerForgetTicket) { SetupForZeroRtt(); client_->Set0RttEnabled(true); server_->Set0RttEnabled(true); ClearServerCache(); ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); ExpectResumption(RESUME_NONE); - ZeroRttSendReceive(false); + ZeroRttSendReceive(true, false); Handshake(); CheckConnected(); SendReceive(); } TEST_P(TlsConnectTls13, ZeroRttServerOnly) { ExpectResumption(RESUME_NONE); server_->Set0RttEnabled(true); @@ -82,17 +102,17 @@ TEST_P(TlsConnectTls13, ZeroRttServerOnl TEST_P(TlsConnectTls13, TestTls13ZeroRttAlpn) { EnableAlpn(); SetupForZeroRtt(); EnableAlpn(); client_->Set0RttEnabled(true); server_->Set0RttEnabled(true); ExpectResumption(RESUME_TICKET); ExpectEarlyDataAccepted(true); - ZeroRttSendReceive(true, [this]() { + ZeroRttSendReceive(true, true, [this]() { client_->CheckAlpn(SSL_NEXT_PROTO_EARLY_VALUE, "a"); return true; }); Handshake(); CheckConnected(); SendReceive(); CheckAlpn("a"); } @@ -104,17 +124,17 @@ TEST_P(TlsConnectTls13, TestTls13ZeroRtt SetupForZeroRtt(); static const uint8_t client_alpn[] = {0x01, 0x61, 0x01, 0x62}; // "a", "b" static const uint8_t server_alpn[] = {0x01, 0x62}; // "b" client_->EnableAlpn(client_alpn, sizeof(client_alpn)); server_->EnableAlpn(server_alpn, sizeof(server_alpn)); client_->Set0RttEnabled(true); server_->Set0RttEnabled(true); ExpectResumption(RESUME_TICKET); - ZeroRttSendReceive(false, [this]() { + ZeroRttSendReceive(true, false, [this]() { client_->CheckAlpn(SSL_NEXT_PROTO_EARLY_VALUE, "a"); return true; }); Handshake(); CheckConnected(); SendReceive(); CheckAlpn("b"); } @@ -125,17 +145,17 @@ TEST_P(TlsConnectTls13, TestTls13ZeroRtt // should then fail the connection. TEST_P(TlsConnectTls13, TestTls13ZeroRttNoAlpnServer) { EnableAlpn(); SetupForZeroRtt(); client_->Set0RttEnabled(true); server_->Set0RttEnabled(true); EnableAlpn(); ExpectResumption(RESUME_TICKET); - ZeroRttSendReceive(true, [this]() { + ZeroRttSendReceive(true, true, [this]() { PRUint8 b[] = {'b'}; client_->CheckAlpn(SSL_NEXT_PROTO_EARLY_VALUE, "a"); EXPECT_EQ(SECSuccess, SSLInt_Set0RttAlpn(client_->ssl_fd(), b, sizeof(b))); client_->CheckAlpn(SSL_NEXT_PROTO_EARLY_VALUE, "b"); return true; }); Handshake(); client_->CheckErrorCode(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID); @@ -145,17 +165,17 @@ TEST_P(TlsConnectTls13, TestTls13ZeroRtt // Set up with no ALPN and then set the client so it thinks it has ALPN. // The server responds without the extension and the client returns an // error. TEST_P(TlsConnectTls13, TestTls13ZeroRttNoAlpnClient) { SetupForZeroRtt(); client_->Set0RttEnabled(true); server_->Set0RttEnabled(true); ExpectResumption(RESUME_TICKET); - ZeroRttSendReceive(true, [this]() { + ZeroRttSendReceive(true, true, [this]() { PRUint8 b[] = {'b'}; EXPECT_EQ(SECSuccess, SSLInt_Set0RttAlpn(client_->ssl_fd(), b, 1)); client_->CheckAlpn(SSL_NEXT_PROTO_EARLY_VALUE, "b"); return true; }); Handshake(); client_->CheckErrorCode(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID); server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT); @@ -165,17 +185,17 @@ TEST_P(TlsConnectTls13, TestTls13ZeroRtt TEST_P(TlsConnectTls13, TestTls13ZeroRttAlpnChangeBoth) { EnableAlpn(); SetupForZeroRtt(); static const uint8_t alpn[] = {0x01, 0x62}; // "b" EnableAlpn(alpn, sizeof(alpn)); client_->Set0RttEnabled(true); server_->Set0RttEnabled(true); ExpectResumption(RESUME_TICKET); - ZeroRttSendReceive(false, [this]() { + ZeroRttSendReceive(true, false, [this]() { client_->CheckAlpn(SSL_NEXT_PROTO_NO_SUPPORT); return false; }); Handshake(); CheckConnected(); SendReceive(); CheckAlpn("b"); }
--- a/security/nss/external_tests/ssl_gtest/ssl_damage_unittest.cc +++ b/security/nss/external_tests/ssl_gtest/ssl_damage_unittest.cc @@ -29,17 +29,17 @@ TEST_F(TlsConnectTest, DamageSecretHandl SSL_LIBRARY_VERSION_TLS_1_3); server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1, SSL_LIBRARY_VERSION_TLS_1_3); server_->StartConnect(); client_->StartConnect(); client_->Handshake(); server_->Handshake(); std::cerr << "Damaging HS secret\n"; - SSLInt_DamageHsTrafficSecret(server_->ssl_fd()); + SSLInt_DamageClientHsTrafficSecret(server_->ssl_fd()); client_->Handshake(); server_->Handshake(); // The client thinks it has connected. EXPECT_EQ(TlsAgent::STATE_CONNECTED, client_->state()); server_->CheckErrorCode(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE); client_->Handshake(); client_->CheckErrorCode(SSL_ERROR_DECRYPT_ERROR_ALERT); } @@ -47,15 +47,15 @@ TEST_F(TlsConnectTest, DamageSecretHandl TEST_F(TlsConnectTest, DamageSecretHandleServerFinished) { client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1, SSL_LIBRARY_VERSION_TLS_1_3); server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1, SSL_LIBRARY_VERSION_TLS_1_3); server_->SetPacketFilter(new AfterRecordN( server_, client_, 0, // ServerHello. - [this]() { SSLInt_DamageHsTrafficSecret(client_->ssl_fd()); })); + [this]() { SSLInt_DamageServerHsTrafficSecret(client_->ssl_fd()); })); ConnectExpectFail(); client_->CheckErrorCode(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE); server_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ); } } // namespace nspr_test
--- a/security/nss/external_tests/ssl_gtest/ssl_ecdh_unittest.cc +++ b/security/nss/external_tests/ssl_gtest/ssl_ecdh_unittest.cc @@ -255,16 +255,33 @@ TEST_P(TlsConnectGeneric, P256andCurve25 client_->ConfigNamedGroups(client_groups); server_->ConfigNamedGroups(server_groups); Connect(); CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign, 255); } +TEST_P(TlsConnectGeneric, P256ClientAndCurve25519Server) { + EnsureTlsSetup(); + client_->DisableAllCiphers(); + client_->EnableCiphersByKeyExchange(ssl_kea_ecdh); + + // the client sends a P256 key share while the server prefers 25519. + const std::vector<SSLNamedGroup> server_groups = {ssl_grp_ec_curve25519}; + const std::vector<SSLNamedGroup> client_groups = {ssl_grp_ec_secp256r1}; + + client_->ConfigNamedGroups(client_groups); + server_->ConfigNamedGroups(server_groups); + + ConnectExpectFail(); + client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP); + server_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP); +} + // Replace the point in the client key exchange message with an empty one class ECCClientKEXFilter : public TlsHandshakeFilter { public: ECCClientKEXFilter() {} protected: virtual PacketFilter::Action FilterHandshake(const HandshakeHeader &header, const DataBuffer &input,
--- a/security/nss/external_tests/ssl_gtest/ssl_extension_unittest.cc +++ b/security/nss/external_tests/ssl_gtest/ssl_extension_unittest.cc @@ -84,28 +84,32 @@ class TlsExtensionInjector : public TlsH offset = parser.consumed(); } else { return KEEP; } *output = input; // Increase the size of the extensions. - uint16_t* len_addr = reinterpret_cast<uint16_t*>(output->data() + offset); - *len_addr = htons(ntohs(*len_addr) + data_.len() + 4); + uint16_t ext_len; + memcpy(&ext_len, output->data() + offset, sizeof(ext_len)); + ext_len = htons(ntohs(ext_len) + data_.len() + 4); + memcpy(output->data() + offset, &ext_len, sizeof(ext_len)); // Insert the extension type and length. DataBuffer type_length; type_length.Allocate(4); type_length.Write(0, extension_, 2); type_length.Write(2, data_.len(), 2); output->Splice(type_length, offset + 2); // Insert the payload. - output->Splice(data_, offset + 6); + if (data_.len() > 0) { + output->Splice(data_, offset + 6); + } return CHANGE; } private: const uint16_t extension_; const DataBuffer data_; }; @@ -178,16 +182,25 @@ class TlsExtensionTest12 class TlsExtensionTest13 : public TlsExtensionTestBase, public ::testing::WithParamInterface<std::string> { public: TlsExtensionTest13() : TlsExtensionTestBase(TlsConnectTestBase::ToMode(GetParam()), SSL_LIBRARY_VERSION_TLS_1_3) {} + void ConnectWithBogusVersionList(const uint8_t* buf, size_t len) { + DataBuffer versions_buf(buf, len); + client_->SetPacketFilter(new TlsExtensionReplacer( + ssl_tls13_supported_versions_xtn, versions_buf)); + ConnectExpectFail(); + client_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT); + server_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO); + } + void ConnectWithReplacementVersionList(uint16_t version) { DataBuffer versions_buf; size_t index = versions_buf.Write(0, 2, 1); versions_buf.Write(index, version, 2); client_->SetPacketFilter(new TlsExtensionReplacer( ssl_tls13_supported_versions_xtn, versions_buf)); ConnectExpectFail(); @@ -509,16 +522,55 @@ TEST_P(TlsExtensionTest13, EmptyClientKe TEST_F(TlsExtensionTest13Stream, DropServerKeyShare) { EnsureTlsSetup(); server_->SetPacketFilter(new TlsExtensionDropper(ssl_tls13_key_share_xtn)); ConnectExpectFail(); EXPECT_EQ(SSL_ERROR_MISSING_KEY_SHARE, client_->error_code()); EXPECT_EQ(SSL_ERROR_BAD_MAC_READ, server_->error_code()); } +TEST_F(TlsExtensionTest13Stream, WrongServerKeyShare) { + const uint16_t wrong_group = ssl_grp_ec_secp384r1; + + static const uint8_t key_share[] = { + wrong_group >> 8, + wrong_group & 0xff, // Group we didn't offer. + 0x00, + 0x02, // length = 2 + 0x01, + 0x02}; + DataBuffer buf(key_share, sizeof(key_share)); + EnsureTlsSetup(); + server_->SetPacketFilter( + new TlsExtensionReplacer(ssl_tls13_key_share_xtn, buf)); + ConnectExpectFail(); + EXPECT_EQ(SSL_ERROR_RX_MALFORMED_KEY_SHARE, client_->error_code()); + EXPECT_EQ(SSL_ERROR_BAD_MAC_READ, server_->error_code()); +} + +// TODO(ekr@rtfm.com): This is the wrong error code. See bug 1307269. +TEST_F(TlsExtensionTest13Stream, UnknownServerKeyShare) { + const uint16_t wrong_group = 0xffff; + + static const uint8_t key_share[] = { + wrong_group >> 8, + wrong_group & 0xff, // Group we didn't offer. + 0x00, + 0x02, // length = 2 + 0x01, + 0x02}; + DataBuffer buf(key_share, sizeof(key_share)); + EnsureTlsSetup(); + server_->SetPacketFilter( + new TlsExtensionReplacer(ssl_tls13_key_share_xtn, buf)); + ConnectExpectFail(); + EXPECT_EQ(SSL_ERROR_MISSING_KEY_SHARE, client_->error_code()); + EXPECT_EQ(SSL_ERROR_BAD_MAC_READ, server_->error_code()); +} + TEST_F(TlsExtensionTest13Stream, DropServerSignatureAlgorithms) { EnsureTlsSetup(); server_->SetPacketFilter( new TlsExtensionDropper(ssl_signature_algorithms_xtn)); ConnectExpectFail(); EXPECT_EQ(SSL_ERROR_MISSING_SIGNATURE_ALGORITHMS_EXTENSION, client_->error_code()); EXPECT_EQ(SSL_ERROR_BAD_MAC_READ, server_->error_code()); @@ -719,16 +771,26 @@ TEST_P(TlsExtensionTest13, RemoveTls13Fr client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_SERVER_HELLO); server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT); #else client_->CheckErrorCode(SSL_ERROR_DECRYPT_ERROR_ALERT); server_->CheckErrorCode(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE); #endif } +TEST_P(TlsExtensionTest13, EmptyVersionList) { + static const uint8_t ext[] = {0x00, 0x00}; + ConnectWithBogusVersionList(ext, sizeof(ext)); +} + +TEST_P(TlsExtensionTest13, OddVersionList) { + static const uint8_t ext[] = {0x00, 0x01, 0x00}; + ConnectWithBogusVersionList(ext, sizeof(ext)); +} + INSTANTIATE_TEST_CASE_P(ExtensionStream, TlsExtensionTestGeneric, ::testing::Combine(TlsConnectTestBase::kTlsModesStream, TlsConnectTestBase::kTlsVAll)); INSTANTIATE_TEST_CASE_P(ExtensionDatagram, TlsExtensionTestGeneric, ::testing::Combine(TlsConnectTestBase::kTlsModesAll, TlsConnectTestBase::kTlsV11Plus)); INSTANTIATE_TEST_CASE_P(ExtensionDatagramOnly, TlsExtensionTestDtls, TlsConnectTestBase::kTlsV11Plus);
--- a/security/nss/external_tests/ssl_gtest/ssl_hrr_unittest.cc +++ b/security/nss/external_tests/ssl_gtest/ssl_hrr_unittest.cc @@ -178,65 +178,103 @@ TEST_F(TlsConnectTest, Select12AfterHell server_->StartConnect(); Handshake(); EXPECT_EQ(SSL_ERROR_ILLEGAL_PARAMETER_ALERT, server_->error_code()); EXPECT_EQ(SSL_ERROR_RX_MALFORMED_SERVER_HELLO, client_->error_code()); } class HelloRetryRequestAgentTest : public TlsAgentTestClient { protected: - void MakeHelloRetryRequestRecord(SSLNamedGroup group, DataBuffer* hrr_record, - uint32_t seq_num = 0) const { - const uint8_t canned_hrr[] = { - SSL_LIBRARY_VERSION_TLS_1_3 >> 8, - SSL_LIBRARY_VERSION_TLS_1_3 & 0xff, - 0, - 0, // The cipher suite is ignored. - static_cast<uint8_t>(group >> 8), - static_cast<uint8_t>(group), - 0, - 0 // no extensions - }; + void SetUp() override { + TlsAgentTestClient::SetUp(); + EnsureInit(); + agent_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_3, + SSL_LIBRARY_VERSION_TLS_1_3); + agent_->StartConnect(); + } + + void MakeCannedHrr(const uint8_t* body, size_t len, DataBuffer* hrr_record, + uint32_t seq_num = 0) const { + DataBuffer hrr_data; + hrr_data.Allocate(len + 4); + size_t i = 0; + i = hrr_data.Write(i, static_cast<uint32_t>(SSL_LIBRARY_VERSION_TLS_1_3), + 2); + i = hrr_data.Write(i, static_cast<uint32_t>(len), 2); + if (len) { + hrr_data.Write(i, body, len); + } DataBuffer hrr; - MakeHandshakeMessage(kTlsHandshakeHelloRetryRequest, canned_hrr, - sizeof(canned_hrr), &hrr, seq_num); + MakeHandshakeMessage(kTlsHandshakeHelloRetryRequest, hrr_data.data(), + hrr_data.len(), &hrr, seq_num); MakeRecord(kTlsHandshakeType, SSL_LIBRARY_VERSION_TLS_1_3, hrr.data(), hrr.len(), hrr_record, seq_num); } + + void MakeGroupHrr(SSLNamedGroup group, DataBuffer* hrr_record, + uint32_t seq_num = 0) const { + const uint8_t group_hrr[] = { + static_cast<uint8_t>(ssl_tls13_key_share_xtn >> 8), + static_cast<uint8_t>(ssl_tls13_key_share_xtn), + 0, + 2, // length of key share extension + static_cast<uint8_t>(group >> 8), + static_cast<uint8_t>(group)}; + MakeCannedHrr(group_hrr, sizeof(group_hrr), hrr_record, seq_num); + } }; // Send two HelloRetryRequest messages in response to the ClientHello. The are // constructed to appear legitimate by asking for a new share in each, so that // the client has to count to work out that the server is being unreasonable. TEST_P(HelloRetryRequestAgentTest, SendSecondHelloRetryRequest) { - EnsureInit(); - agent_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_3, - SSL_LIBRARY_VERSION_TLS_1_3); - agent_->StartConnect(); - - DataBuffer hrr_record; - MakeHelloRetryRequestRecord(ssl_grp_ec_secp384r1, &hrr_record, 0); - ProcessMessage(hrr_record, TlsAgent::STATE_CONNECTING); - MakeHelloRetryRequestRecord(ssl_grp_ec_secp521r1, &hrr_record, 1); - ProcessMessage(hrr_record, TlsAgent::STATE_ERROR, + DataBuffer hrr; + MakeGroupHrr(ssl_grp_ec_secp384r1, &hrr, 0); + ProcessMessage(hrr, TlsAgent::STATE_CONNECTING); + MakeGroupHrr(ssl_grp_ec_secp521r1, &hrr, 1); + ProcessMessage(hrr, TlsAgent::STATE_ERROR, SSL_ERROR_RX_UNEXPECTED_HELLO_RETRY_REQUEST); } // Here the client receives a HelloRetryRequest with a group that they already // provided a share for. TEST_P(HelloRetryRequestAgentTest, HandleBogusHelloRetryRequest) { - EnsureInit(); - agent_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_3, - SSL_LIBRARY_VERSION_TLS_1_3); - agent_->StartConnect(); + DataBuffer hrr; + MakeGroupHrr(ssl_grp_ec_secp256r1, &hrr); + ProcessMessage(hrr, TlsAgent::STATE_ERROR, + SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST); +} + +TEST_P(HelloRetryRequestAgentTest, HandleNoopHelloRetryRequest) { + DataBuffer hrr; + MakeCannedHrr(nullptr, 0U, &hrr); + ProcessMessage(hrr, TlsAgent::STATE_ERROR, + SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST); +} - DataBuffer hrr_record; - MakeHelloRetryRequestRecord(ssl_grp_ec_secp256r1, &hrr_record); - ProcessMessage(hrr_record, TlsAgent::STATE_ERROR, - SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST); +TEST_P(HelloRetryRequestAgentTest, HandleHelloRetryRequestCookie) { + const uint8_t canned_cookie_hrr[] = { + static_cast<uint8_t>(ssl_tls13_cookie_xtn >> 8), + static_cast<uint8_t>(ssl_tls13_cookie_xtn), + 0, + 5, // length of cookie extension + 0, + 3, // cookie value length + 0xc0, + 0x0c, + 0x13}; + DataBuffer hrr; + MakeCannedHrr(canned_cookie_hrr, sizeof(canned_cookie_hrr), &hrr); + TlsExtensionCapture* capture = new TlsExtensionCapture(ssl_tls13_cookie_xtn); + agent_->SetPacketFilter(capture); + ProcessMessage(hrr, TlsAgent::STATE_CONNECTING); + const size_t cookie_pos = 2 + 2; // cookie_xtn, extension len + DataBuffer cookie(canned_cookie_hrr + cookie_pos, + sizeof(canned_cookie_hrr) - cookie_pos); + EXPECT_EQ(cookie, capture->extension()); } INSTANTIATE_TEST_CASE_P(HelloRetryRequestAgentTests, HelloRetryRequestAgentTest, TlsConnectTestBase::kTlsModesAll); #ifndef NSS_DISABLE_TLS_1_3 INSTANTIATE_TEST_CASE_P(HelloRetryRequestKeyExchangeTests, TlsKeyExchange13, ::testing::Combine(TlsConnectTestBase::kTlsModesAll, TlsConnectTestBase::kTlsV13));
--- a/security/nss/external_tests/ssl_gtest/ssl_loopback_unittest.cc +++ b/security/nss/external_tests/ssl_gtest/ssl_loopback_unittest.cc @@ -34,16 +34,30 @@ TEST_P(TlsConnectGeneric, Connect) { TEST_P(TlsConnectGeneric, ConnectEcdsa) { SetExpectedVersion(std::get<1>(GetParam())); Reset(TlsAgent::kServerEcdsa256); Connect(); CheckKeys(ssl_kea_ecdh, ssl_auth_ecdsa); } +TEST_P(TlsConnectGenericPre13, CipherSuiteMismatch) { + EnsureTlsSetup(); + if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) { + client_->EnableSingleCipher(TLS_AES_128_GCM_SHA256); + server_->EnableSingleCipher(TLS_AES_256_GCM_SHA384); + } else { + client_->EnableSingleCipher(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA); + server_->EnableSingleCipher(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA); + } + ConnectExpectFail(); + client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP); + server_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP); +} + TEST_P(TlsConnectGenericPre13, ConnectFalseStart) { client_->EnableFalseStart(); Connect(); SendReceive(); } TEST_P(TlsConnectGeneric, ConnectAlpn) { EnableAlpn();
--- a/security/nss/external_tests/ssl_gtest/tls_connect.cc +++ b/security/nss/external_tests/ssl_gtest/tls_connect.cc @@ -481,27 +481,32 @@ void TlsConnectTestBase::SetupForResume( Connect(); SendReceive(); // Need to read so that we absorb the session ticket. CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign); Reset(); } void TlsConnectTestBase::ZeroRttSendReceive( - bool expect_readable, std::function<bool()> post_clienthello_check) { + bool expect_writable, bool expect_readable, + std::function<bool()> post_clienthello_check) { const char* k0RttData = "ABCDEF"; const PRInt32 k0RttDataLen = static_cast<PRInt32>(strlen(k0RttData)); client_->Handshake(); // Send ClientHello. if (post_clienthello_check) { if (!post_clienthello_check()) return; } PRInt32 rv = PR_Write(client_->ssl_fd(), k0RttData, k0RttDataLen); // 0-RTT write. - EXPECT_EQ(k0RttDataLen, rv); + if (expect_writable) { + EXPECT_EQ(k0RttDataLen, rv); + } else { + EXPECT_EQ(SECFailure, rv); + } server_->Handshake(); // Consume ClientHello, EE, Finished. std::vector<uint8_t> buf(k0RttDataLen); rv = PR_Read(server_->ssl_fd(), buf.data(), k0RttDataLen); // 0-RTT read if (expect_readable) { std::cerr << "0-RTT read " << rv << " bytes\n"; EXPECT_EQ(k0RttDataLen, rv); } else {
--- a/security/nss/external_tests/ssl_gtest/tls_connect.h +++ b/security/nss/external_tests/ssl_gtest/tls_connect.h @@ -94,17 +94,17 @@ class TlsConnectTestBase : public ::test void EnsureModelSockets(); void CheckAlpn(const std::string& val); void EnableSrtp(); void CheckSrtp() const; void SendReceive(); void SetupForZeroRtt(); void SetupForResume(); void ZeroRttSendReceive( - bool expect_readable, + bool expect_writable, bool expect_readable, std::function<bool()> post_clienthello_check = nullptr); void Receive(size_t amount); void ExpectExtendedMasterSecret(bool expected); void ExpectEarlyDataAccepted(bool expected); protected: Mode mode_; TlsAgent* client_;
--- a/security/nss/external_tests/ssl_gtest/tls_filter.cc +++ b/security/nss/external_tests/ssl_gtest/tls_filter.cc @@ -400,17 +400,19 @@ PacketFilter::Action TlsExtensionFilter: std::cerr << "extension old: " << extension << std::endl; std::cerr << "extension new: " << filtered << std::endl; source = &filtered; } // Write out extension. offset = output->Write(offset, extension_type, 2); offset = output->Write(offset, source->len(), 2); - offset = output->Write(offset, *source); + if (source->len() > 0) { + offset = output->Write(offset, *source); + } } output->Truncate(offset); if (changed) { size_t newlen = output->len() - length_offset - 2; EXPECT_GT(0x10000U, newlen); if (newlen >= 0x10000) { return KEEP; // bad: size increased too much
--- a/security/nss/external_tests/util_gtest/util_utf8_unittest.cc +++ b/security/nss/external_tests/util_gtest/util_utf8_unittest.cc @@ -942,19 +942,26 @@ const char *const kUtf8BadCases[] = { "\xED\xBF\xBF", "\xED\xA0\x80\xE0\xBF\xBF", }; // Invalid UTF-16 sequences (0-terminated) const Utf16BadCase kUtf16BadCases[] = { // Leading surrogate not followed by trailing surrogate: - {{0xD800, 0, 0}}, {{0xD800, 0x41, 0}}, {{0xD800, 0xfe, 0}}, - {{0xD800, 0x3bb, 0}}, {{0xD800, 0xD800, 0}}, {{0xD800, 0xFEFF, 0}}, + {{0xD800, 0, 0}}, + {{0xD800, 0x41, 0}}, + {{0xD800, 0xfe, 0}}, + {{0xD800, 0x3bb, 0}}, + {{0xD800, 0xD800, 0}}, + {{0xD800, 0xFEFF, 0}}, {{0xD800, 0xFFFD, 0}}, + // Trailing surrogate, not preceded by a leading one. + {{0xDC00, 0, 0}}, + {{0xDE6D, 0xD834, 0}}, }; // Parameterized test instantiations: INSTANTIATE_TEST_CASE_P(Ucs4TestCases, Ucs4Test, ::testing::ValuesIn(kUcs4Cases)); INSTANTIATE_TEST_CASE_P(Iso88591TestCases, Ucs2Test,
--- a/security/nss/lib/softoken/legacydb/keydb.c +++ b/security/nss/lib/softoken/legacydb/keydb.c @@ -125,17 +125,19 @@ encode_dbkey(NSSLOWKEYDBKey *dbkey, unsi PORT_Assert(dbkey->salt.len < 256); buf[1] = dbkey->salt.len; /* set length of nickname */ PORT_Assert(nnlen < 256); buf[2] = nnlen; /* copy salt */ - PORT_Memcpy(&buf[3], dbkey->salt.data, dbkey->salt.len); + if (dbkey->salt.len > 0) { + PORT_Memcpy(&buf[3], dbkey->salt.data, dbkey->salt.len); + } /* copy nickname */ PORT_Memcpy(&buf[3 + dbkey->salt.len], nn, nnlen); /* copy encrypted key */ PORT_Memcpy(&buf[3 + dbkey->salt.len + nnlen], dbkey->derPK.data, dbkey->derPK.len);
--- a/security/nss/lib/softoken/legacydb/pcertdb.c +++ b/security/nss/lib/softoken/legacydb/pcertdb.c @@ -2185,18 +2185,20 @@ EncodeDBSubjectEntry(certDBEntrySubject tmpbuf += 2; } for (i = 0; i < ncerts; i++) { PORT_Memcpy(tmpbuf, certKeys[i].data, certKeys[i].len); tmpbuf += certKeys[i].len; } for (i = 0; i < ncerts; i++) { - PORT_Memcpy(tmpbuf, keyIDs[i].data, keyIDs[i].len); - tmpbuf += keyIDs[i].len; + if (keyIDs[i].len) { + PORT_Memcpy(tmpbuf, keyIDs[i].data, keyIDs[i].len); + tmpbuf += keyIDs[i].len; + } } if (entry->emailAddrs) { tmpbuf[0] = (PRUint8)(entry->nemailAddrs >> 8); tmpbuf[1] = (PRUint8)(entry->nemailAddrs); tmpbuf += 2; for (i = 0; i < entry->nemailAddrs; i++) { int nameLen = PORT_Strlen(entry->emailAddrs[i]) + 1;
--- a/security/nss/lib/softoken/pkcs11c.c +++ b/security/nss/lib/softoken/pkcs11c.c @@ -687,27 +687,32 @@ sftk_ChaCha20Poly1305_CreateContext(cons if (ChaCha20Poly1305_InitContext(&ctx->freeblCtx, key, keyLen, params->ulTagLen) != SECSuccess) { PORT_Free(ctx); return NULL; } PORT_Memcpy(ctx->nonce, params->pNonce, sizeof(ctx->nonce)); + /* AAD data and length must both be null, or both non-null. */ + PORT_Assert((params->pAAD == NULL) == (params->ulAADLen == 0)); + if (params->ulAADLen > sizeof(ctx->ad)) { /* Need to allocate an overflow buffer for the additional data. */ ctx->adOverflow = (unsigned char *)PORT_Alloc(params->ulAADLen); if (!ctx->adOverflow) { PORT_Free(ctx); return NULL; } PORT_Memcpy(ctx->adOverflow, params->pAAD, params->ulAADLen); } else { ctx->adOverflow = NULL; - PORT_Memcpy(ctx->ad, params->pAAD, params->ulAADLen); + if (params->pAAD) { + PORT_Memcpy(ctx->ad, params->pAAD, params->ulAADLen); + } } ctx->adLen = params->ulAADLen; return ctx; } static void sftk_ChaCha20Poly1305_DestroyContext(SFTKChaCha20Poly1305Info *ctx,
--- a/security/nss/lib/ssl/dtlscon.c +++ b/security/nss/lib/ssl/dtlscon.c @@ -995,17 +995,16 @@ dtls_SetMTU(sslSocket *ss, PRUint16 adve * Caller must hold Handshake and RecvBuf locks. */ SECStatus dtls_HandleHelloVerifyRequest(sslSocket *ss, SSL3Opaque *b, PRUint32 length) { int errCode = SSL_ERROR_RX_MALFORMED_HELLO_VERIFY_REQUEST; SECStatus rv; PRInt32 temp; - SECItem cookie = { siBuffer, NULL, 0 }; SSL3AlertDescription desc = illegal_parameter; SSL_TRC(3, ("%d: SSL3[%d]: handle hello_verify_request handshake", SSL_GETPID(), ss->fd)); PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss)); PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); if (ss->ssl3.hs.ws != wait_server_hello) { @@ -1020,29 +1019,28 @@ dtls_HandleHelloVerifyRequest(sslSocket goto loser; /* alert has been sent */ } if (temp != SSL_LIBRARY_VERSION_DTLS_1_0_WIRE && temp != SSL_LIBRARY_VERSION_DTLS_1_2_WIRE) { goto alert_loser; } - /* The cookie */ - rv = ssl3_ConsumeHandshakeVariable(ss, &cookie, 1, &b, &length); + /* Read the cookie. + * IMPORTANT: The value of ss->ssl3.hs.cookie is only valid while the + * HelloVerifyRequest message remains valid. */ + rv = ssl3_ConsumeHandshakeVariable(ss, &ss->ssl3.hs.cookie, 1, &b, &length); if (rv != SECSuccess) { goto loser; /* alert has been sent */ } - if (cookie.len > DTLS_COOKIE_BYTES) { + if (ss->ssl3.hs.cookie.len > DTLS_COOKIE_BYTES) { desc = decode_error; goto alert_loser; /* malformed. */ } - PORT_Memcpy(ss->ssl3.hs.cookie, cookie.data, cookie.len); - ss->ssl3.hs.cookieLen = cookie.len; - ssl_GetXmitBufLock(ss); /*******************************/ /* Now re-send the client hello */ rv = ssl3_SendClientHello(ss, client_hello_retransmit); ssl_ReleaseXmitBufLock(ss); /*******************************/ if (rv == SECSuccess)
--- a/security/nss/lib/ssl/ssl3con.c +++ b/security/nss/lib/ssl/ssl3con.c @@ -5152,17 +5152,17 @@ ssl3_SendClientHello(sslSocket *ss, sslC length = sizeof(SSL3ProtocolVersion) + SSL3_RANDOM_LENGTH + 1 + (sid->version >= SSL_LIBRARY_VERSION_TLS_1_3 ? 0 : sid->u.ssl3.sessionIDLength) + 2 + num_suites * sizeof(ssl3CipherSuite) + 1 + numCompressionMethods + total_exten_len; if (IS_DTLS(ss)) { - length += 1 + ss->ssl3.hs.cookieLen; + length += 1 + ss->ssl3.hs.cookie.len; } /* A padding extension may be included to ensure that the record containing * the ClientHello doesn't have a length between 256 and 511 bytes * (inclusive). Initial, ClientHello records with such lengths trigger bugs * in F5 devices. * * This is not done for DTLS, for renegotiation, or when there are no @@ -5232,17 +5232,17 @@ ssl3_SendClientHello(sslSocket *ss, sslC if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); } return rv; /* err set by ssl3_AppendHandshake* */ } if (IS_DTLS(ss)) { rv = ssl3_AppendHandshakeVariable( - ss, ss->ssl3.hs.cookie, ss->ssl3.hs.cookieLen, 1); + ss, ss->ssl3.hs.cookie.data, ss->ssl3.hs.cookie.len, 1); if (rv != SECSuccess) { if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); } return rv; /* err set by ssl3_AppendHandshake* */ } } @@ -6891,17 +6891,19 @@ ssl3_HandleServerHelloPart2(sslSocket *s /* get a new sid */ ss->sec.ci.sid = sid = ssl3_NewSessionID(ss, PR_FALSE); if (sid == NULL) { goto alert_loser; /* memory error is set. */ } sid->version = ss->version; sid->u.ssl3.sessionIDLength = sidBytes->len; - PORT_Memcpy(sid->u.ssl3.sessionID, sidBytes->data, sidBytes->len); + if (sidBytes->len > 0) { + PORT_Memcpy(sid->u.ssl3.sessionID, sidBytes->data, sidBytes->len); + } sid->u.ssl3.keys.extendedMasterSecretUsed = ssl3_ExtensionNegotiated(ss, ssl_extended_master_secret_xtn); /* Copy Signed Certificate Timestamps, if any. */ if (ss->xtnData.signedCertTimestamps.data) { rv = SECITEM_CopyItem(NULL, &sid->u.ssl3.signedCertTimestamps, &ss->xtnData.signedCertTimestamps); @@ -8248,18 +8250,18 @@ ssl3_HandleClientHello(sslSocket *ss, SS goto loser; /* malformed */ } } versionExtension = ssl3_FindExtension(ss, ssl_tls13_supported_versions_xtn); if (versionExtension) { rv = tls13_NegotiateVersion(ss, versionExtension); if (rv != SECSuccess) { - desc = protocol_version; - errCode = SSL_ERROR_UNSUPPORTED_VERSION; + errCode = PORT_GetError(); + desc = (errCode == SSL_ERROR_UNSUPPORTED_VERSION) ? protocol_version : illegal_parameter; goto alert_loser; } } else { /* The PR_MIN here ensures that we never negotiate 1.3 if the * peer didn't offer "supported_versions". */ rv = ssl3_NegotiateVersion(ss, PR_MIN(version, SSL_LIBRARY_VERSION_TLS_1_2), @@ -12719,18 +12721,21 @@ ssl3_InitState(sslSocket *ss) } ss->ssl3.hs.clientHelloHash = NULL; PR_INIT_CLIST(&ss->ssl3.hs.remoteKeyShares); ss->ssl3.hs.currentSecret = NULL; ss->ssl3.hs.resumptionPsk = NULL; ss->ssl3.hs.resumptionContext = nullItem; ss->ssl3.hs.dheSecret = NULL; - ss->ssl3.hs.trafficSecret = NULL; - ss->ssl3.hs.hsTrafficSecret = NULL; + ss->ssl3.hs.clientEarlyTrafficSecret = NULL; + ss->ssl3.hs.clientHsTrafficSecret = NULL; + ss->ssl3.hs.serverHsTrafficSecret = NULL; + ss->ssl3.hs.clientTrafficSecret = NULL; + ss->ssl3.hs.serverTrafficSecret = NULL; ss->ssl3.hs.certificateRequest = NULL; PR_INIT_CLIST(&ss->ssl3.hs.cipherSpecs); PORT_Assert(!ss->ssl3.hs.messages.buf && !ss->ssl3.hs.messages.space); ss->ssl3.hs.messages.buf = NULL; ss->ssl3.hs.messages.space = 0; ss->ssl3.hs.receivedNewSessionTicket = PR_FALSE; @@ -13055,22 +13060,26 @@ ssl3_DestroySSL3Info(sslSocket *ss) if (ss->ssl3.hs.currentSecret) PK11_FreeSymKey(ss->ssl3.hs.currentSecret); if (ss->ssl3.hs.resumptionPsk) PK11_FreeSymKey(ss->ssl3.hs.resumptionPsk); if (ss->ssl3.hs.dheSecret) PK11_FreeSymKey(ss->ssl3.hs.dheSecret); if (ss->ssl3.hs.resumptionContext.data) SECITEM_FreeItem(&ss->ssl3.hs.resumptionContext, PR_FALSE); - if (ss->ssl3.hs.earlyTrafficSecret) - PK11_FreeSymKey(ss->ssl3.hs.earlyTrafficSecret); - if (ss->ssl3.hs.hsTrafficSecret) - PK11_FreeSymKey(ss->ssl3.hs.hsTrafficSecret); - if (ss->ssl3.hs.trafficSecret) - PK11_FreeSymKey(ss->ssl3.hs.trafficSecret); + if (ss->ssl3.hs.clientEarlyTrafficSecret) + PK11_FreeSymKey(ss->ssl3.hs.clientEarlyTrafficSecret); + if (ss->ssl3.hs.clientHsTrafficSecret) + PK11_FreeSymKey(ss->ssl3.hs.clientHsTrafficSecret); + if (ss->ssl3.hs.serverHsTrafficSecret) + PK11_FreeSymKey(ss->ssl3.hs.serverHsTrafficSecret); + if (ss->ssl3.hs.clientTrafficSecret) + PK11_FreeSymKey(ss->ssl3.hs.clientTrafficSecret); + if (ss->ssl3.hs.serverTrafficSecret) + PK11_FreeSymKey(ss->ssl3.hs.serverTrafficSecret); ss->ssl3.hs.zeroRttState = ssl_0rtt_none; /* Destroy TLS 1.3 buffered early data. */ tls13_DestroyEarlyData(&ss->ssl3.hs.bufferedEarlyData); ss->ssl3.initialized = PR_FALSE; SECITEM_FreeItem(&ss->ssl3.nextProto, PR_FALSE);
--- a/security/nss/lib/ssl/ssl3ext.c +++ b/security/nss/lib/ssl/ssl3ext.c @@ -91,16 +91,19 @@ static PRInt32 ssl3_SendExtendedMasterSe static SECStatus ssl3_HandleExtendedMasterSecretXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data); static PRInt32 tls13_ClientSendKeyShareXtn(sslSocket *ss, PRBool append, PRUint32 maxBytes); static SECStatus tls13_ClientHandleKeyShareXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data); +static SECStatus tls13_ClientHandleKeyShareXtnHrr(sslSocket *ss, + PRUint16 ex_type, + SECItem *data); static SECStatus tls13_ServerHandleKeyShareXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data); static PRInt32 tls13_ClientSendPreSharedKeyXtn(sslSocket *ss, PRBool append, PRUint32 maxBytes); static SECStatus tls13_ServerHandlePreSharedKeyXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data); @@ -117,16 +120,21 @@ static SECStatus tls13_ClientHandleEarly static SECStatus tls13_ClientHandleTicketEarlyDataInfoXtn( sslSocket *ss, PRUint16 ex_type, SECItem *data); static SECStatus tls13_ClientHandleSigAlgsXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data); static PRInt32 tls13_ClientSendSupportedVersionsXtn(sslSocket *ss, PRBool append, PRUint32 maxBytes); +static SECStatus tls13_ClientHandleHrrCookie(sslSocket *ss, PRUint16 ex_type, + SECItem *data); +static PRInt32 tls13_ClientSendHrrCookieXtn(sslSocket *ss, + PRBool append, + PRUint32 maxBytes); /* * Write bytes. Using this function means the SECItem structure * cannot be freed. The caller is expected to call this function * on a shallow copy of the structure. */ static SECStatus ssl3_AppendToItem(SECItem *item, const unsigned char *buf, PRUint32 bytes) @@ -282,16 +290,22 @@ static const ssl3ExtensionHandler server { ssl_signed_cert_timestamp_xtn, &ssl3_ClientHandleSignedCertTimestampXtn }, { ssl_tls13_key_share_xtn, &tls13_ClientHandleKeyShareXtn }, { ssl_tls13_pre_shared_key_xtn, &tls13_ClientHandlePreSharedKeyXtn }, { ssl_tls13_early_data_xtn, &tls13_ClientHandleEarlyDataXtn }, { ssl_signature_algorithms_xtn, &tls13_ClientHandleSigAlgsXtn }, { -1, NULL } }; +static const ssl3ExtensionHandler helloRetryRequestHandlers[] = { + { ssl_tls13_key_share_xtn, tls13_ClientHandleKeyShareXtnHrr }, + { ssl_tls13_cookie_xtn, tls13_ClientHandleHrrCookie }, + { -1, NULL } +}; + static const ssl3ExtensionHandler serverHelloHandlersSSL3[] = { { ssl_renegotiation_info_xtn, &ssl3_HandleRenegotiationInfoXtn }, { -1, NULL } }; static const ssl3ExtensionHandler newSessionTicketHandlers[] = { { ssl_tls13_ticket_early_data_info_xtn, &tls13_ClientHandleTicketEarlyDataInfoXtn }, @@ -324,17 +338,18 @@ static const ssl3HelloExtensionSender cl { ssl_tls13_key_share_xtn, &tls13_ClientSendKeyShareXtn }, { ssl_tls13_pre_shared_key_xtn, &tls13_ClientSendPreSharedKeyXtn }, { ssl_tls13_early_data_xtn, &tls13_ClientSendEarlyDataXtn }, /* Some servers (e.g. WebSphere Application Server 7.0 and Tomcat) will * time out or terminate the connection if the last extension in the * client hello is empty. They are not intolerant of TLS 1.2, so list * signature_algorithms at the end. See bug 1243641. */ { ssl_tls13_supported_versions_xtn, &tls13_ClientSendSupportedVersionsXtn }, - { ssl_signature_algorithms_xtn, &ssl3_ClientSendSigAlgsXtn } + { ssl_signature_algorithms_xtn, &ssl3_ClientSendSigAlgsXtn }, + { ssl_tls13_cookie_xtn, &tls13_ClientSendHrrCookieXtn } /* any extra entries will appear as { 0, NULL } */ }; static const ssl3HelloExtensionSender clientHelloSendersSSL3[SSL_MAX_EXTENSIONS] = { { ssl_renegotiation_info_xtn, &ssl3_SendRenegotiationInfoXtn } /* any extra entries will appear as { 0, NULL } */ }; @@ -2160,16 +2175,20 @@ ssl3_HandleParsedExtensions(sslSocket *s switch (handshakeMessage) { case client_hello: handlers = clientHelloHandlers; break; case new_session_ticket: PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3); handlers = newSessionTicketHandlers; break; + case hello_retry_request: + PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3); + handlers = helloRetryRequestHandlers; + break; case encrypted_extensions: PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3); /* fall through */ case server_hello: if (ss->version > SSL_LIBRARY_VERSION_3_0) { handlers = serverHelloHandlersTLS; } else { handlers = serverHelloHandlersSSL3; @@ -2186,17 +2205,18 @@ ssl3_HandleParsedExtensions(sslSocket *s cursor = PR_NEXT_LINK(cursor)) { TLSExtension *extension = (TLSExtension *)cursor; const ssl3ExtensionHandler *handler; /* Check whether the server sent an extension which was not advertised * in the ClientHello */ if (!ss->sec.isServer && !ssl3_ClientExtensionAdvertised(ss, extension->type) && - (handshakeMessage != new_session_ticket)) { + (handshakeMessage != new_session_ticket) && + (extension->type != ssl_tls13_cookie_xtn)) { (void)SSL3_SendAlert(ss, alert_fatal, unsupported_extension); PORT_SetError(SSL_ERROR_RX_UNEXPECTED_EXTENSION); return SECFailure; } /* Check that this is a legal extension in TLS 1.3 */ if (isTLS13 && !tls13_ExtensionAllowed(extension->type, handshakeMessage)) { if (handshakeMessage == client_hello) { @@ -3165,16 +3185,59 @@ tls13_ClientHandleKeyShareXtn(sslSocket if (data->len) { PORT_SetError(SSL_ERROR_RX_MALFORMED_KEY_SHARE); return SECFailure; } return SECSuccess; } +static SECStatus +tls13_ClientHandleKeyShareXtnHrr(sslSocket *ss, PRUint16 ex_type, SECItem *data) +{ + SECStatus rv; + PRInt32 tmp; + const sslNamedGroupDef *group; + + PORT_Assert(!ss->sec.isServer); + PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3); + + SSL_TRC(3, ("%d: SSL3[%d]: handle key_share extension in HRR", + SSL_GETPID(), ss->fd)); + + tmp = ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, &data->len); + if (tmp < 0) { + return SECFailure; /* error code already set */ + } + if (data->len) { + (void)SSL3_SendAlert(ss, alert_fatal, decode_error); + PORT_SetError(SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST); + return SECFailure; + } + + group = ssl_LookupNamedGroup((SSLNamedGroup)tmp); + /* If the group is not enabled, or we already have a share for the + * requested group, abort. */ + if (!ssl_NamedGroupEnabled(ss, group) || + ssl_LookupEphemeralKeyPair(ss, group)) { + (void)SSL3_SendAlert(ss, alert_fatal, illegal_parameter); + PORT_SetError(SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST); + return SECFailure; + } + + rv = tls13_CreateKeyShare(ss, group); + if (rv != SECSuccess) { + (void)SSL3_SendAlert(ss, alert_fatal, internal_error); + PORT_SetError(SEC_ERROR_KEYGEN_FAIL); + return SECFailure; + } + + return SECSuccess; +} + /* Handle an incoming KeyShare extension at the server and copy to * |ss->ssl3.hs.remoteKeyShares| for future use. The key * share is processed in tls13_HandleClientKeyShare(). */ static SECStatus tls13_ServerHandleKeyShareXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data) { SECStatus rv; PRInt32 length; @@ -3755,17 +3818,17 @@ tls13_ClientSendSupportedVersionsXtn(ssl SSL_TRC(3, ("%d: TLS13[%d]: send supported_versions extension", SSL_GETPID(), ss->fd)); /* Extension type, extension len fiels, vector len field, * length of the values. */ extensions_len = 2 + 2 + 1 + 2 * (ss->vrange.max - ss->vrange.min + 1); - if (maxBytes < extensions_len) { + if (maxBytes < (PRUint32)extensions_len) { PORT_Assert(0); return 0; } if (append) { rv = ssl3_AppendHandshakeNumber(ss, ssl_tls13_supported_versions_xtn, 2); if (rv != SECSuccess) return -1; @@ -3784,16 +3847,84 @@ tls13_ClientSendSupportedVersionsXtn(ssl if (rv != SECSuccess) return -1; } } return extensions_len; } +/* + * struct { + * opaque cookie<1..2^16-1>; + * } Cookie; + */ +SECStatus +tls13_ClientHandleHrrCookie(sslSocket *ss, PRUint16 ex_type, SECItem *data) +{ + SECStatus rv; + + SSL_TRC(3, ("%d: TLS13[%d]: handle cookie extension", + SSL_GETPID(), ss->fd)); + + PORT_Assert(ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_3); + + /* IMPORTANT: this is only valid while the HelloRetryRequest is still valid. */ + rv = ssl3_ConsumeHandshakeVariable(ss, &ss->ssl3.hs.cookie, 2, + &data->data, &data->len); + if (rv != SECSuccess) { + PORT_SetError(SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST); + return SECFailure; + } + if (!ss->ssl3.hs.cookie.len || data->len) { + (void)SSL3_SendAlert(ss, alert_fatal, decode_error); + PORT_SetError(SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST); + return SECFailure; + } + + return SECSuccess; +} + +PRInt32 +tls13_ClientSendHrrCookieXtn(sslSocket *ss, PRBool append, PRUint32 maxBytes) +{ + PRInt32 extension_len; + + if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3 || + !ss->ssl3.hs.cookie.len) { + return 0; + } + + SSL_TRC(3, ("%d: TLS13[%d]: send cookie extension", SSL_GETPID(), ss->fd)); + + /* Extension type, length, cookie length, cookie value. */ + extension_len = 2 + 2 + 2 + ss->ssl3.hs.cookie.len; + + if (maxBytes < (PRUint32)extension_len) { + PORT_Assert(0); + return 0; + } + + if (append) { + SECStatus rv = ssl3_AppendHandshakeNumber(ss, ssl_tls13_cookie_xtn, 2); + if (rv != SECSuccess) + return -1; + + rv = ssl3_AppendHandshakeNumber(ss, extension_len - 4, 2); + if (rv != SECSuccess) + return -1; + + rv = ssl3_AppendHandshakeVariable(ss, ss->ssl3.hs.cookie.data, + ss->ssl3.hs.cookie.len, 2); + if (rv != SECSuccess) + return -1; + } + return extension_len; +} + void ssl3_DestroyRemoteExtensions(PRCList *list) { PRCList *cur_p; while (!PR_CLIST_IS_EMPTY(list)) { cur_p = PR_LIST_TAIL(list); PR_REMOVE_LINK(cur_p);
--- a/security/nss/lib/ssl/sslimpl.h +++ b/security/nss/lib/ssl/sslimpl.h @@ -932,40 +932,40 @@ typedef struct SSL3HandshakeStateStr { PRUint16 maxMessageSent; /* The largest message we sent */ PRUint16 recvMessageSeq; /* The receiving message sequence * number */ sslBuffer recvdFragments; /* The fragments we have received in * a bitmask */ PRInt32 recvdHighWater; /* The high water mark for fragments * received. -1 means no reassembly * in progress. */ - unsigned char cookie[32]; /* The cookie */ - unsigned char cookieLen; /* The length of the cookie */ + SECItem cookie; /* The Hello(Retry|Verify)Request cookie. */ PRIntervalTime rtTimerStarted; /* When the timer was started */ DTLSTimerCb rtTimerCb; /* The function to call on expiry */ PRUint32 rtTimeoutMs; /* The length of the current timeout * used for backoff (in ms) */ PRUint32 rtRetries; /* The retry counter */ SECItem srvVirtName; /* for server: name that was negotiated * with a client. For client - is * always set to NULL.*/ /* This group of values is used for TLS 1.3 and above */ - PK11Context *clientHelloHash; /* The client hello hash state, used + PK11Context *clientHelloHash; /* The client hello hash state, used * by the server for 0-RTT. */ - PRCList remoteKeyShares; /* The other side's public keys */ - PK11SymKey *currentSecret; /* The secret down the "left hand side" + PRCList remoteKeyShares; /* The other side's public keys */ + PK11SymKey *currentSecret; /* The secret down the "left hand side" * of the TLS 1.3 key schedule. */ - PK11SymKey *resumptionPsk; /* The resumption PSK. */ - SECItem resumptionContext; /* The resumption context. */ - PK11SymKey *dheSecret; /* The (EC)DHE shared secret. */ - PK11SymKey *earlyTrafficSecret; /* The secret we use for 0-RTT. */ - PK11SymKey *hsTrafficSecret; /* The handshake traffic secret. */ - PK11SymKey *trafficSecret; /* The source key to use to generate - * traffic keys */ + PK11SymKey *resumptionPsk; /* The resumption PSK. */ + SECItem resumptionContext; /* The resumption context. */ + PK11SymKey *dheSecret; /* The (EC)DHE shared secret. */ + PK11SymKey *clientEarlyTrafficSecret; /* The secret we use for 0-RTT. */ + PK11SymKey *clientHsTrafficSecret; /* The source keys for handshake */ + PK11SymKey *serverHsTrafficSecret; /* traffic keys. */ + PK11SymKey *clientTrafficSecret; /* The source keys for application */ + PK11SymKey *serverTrafficSecret; /* traffic keys */ /* The certificate request from the server. */ TLS13CertificateRequest *certificateRequest; PRCList cipherSpecs; /* The cipher specs in the sequence they * will be applied. */ ssl3CipherSpec *nullSpec; /* In case 0-RTT is rejected. */ sslZeroRttState zeroRttState; /* Are we doing a 0-RTT handshake? */ sslZeroRttIgnore zeroRttIgnore; /* Are we ignoring 0-RTT? */ PRCList bufferedEarlyData; /* Buffered TLS 1.3 early data
--- a/security/nss/lib/ssl/sslt.h +++ b/security/nss/lib/ssl/sslt.h @@ -309,29 +309,33 @@ typedef enum { ssl_signed_cert_timestamp_xtn = 18, ssl_padding_xtn = 21, ssl_extended_master_secret_xtn = 23, ssl_session_ticket_xtn = 35, ssl_tls13_key_share_xtn = 40, ssl_tls13_pre_shared_key_xtn = 41, ssl_tls13_early_data_xtn = 42, ssl_tls13_supported_versions_xtn = 43, + ssl_tls13_cookie_xtn = 44, ssl_next_proto_nego_xtn = 13172, ssl_renegotiation_info_xtn = 0xff01 } SSLExtensionType; typedef enum { ssl_tls13_ticket_early_data_info_xtn = 1 } TLS13TicketExtensionType; /* This is the old name for the supported_groups extensions. */ #define ssl_elliptic_curves_xtn ssl_supported_groups_xtn -#define SSL_MAX_EXTENSIONS 16 /* doesn't include ssl_padding_xtn or \ - * TLS 1.3 NewSessionTicket extensions. */ +/* SSL_MAX_EXTENSIONS doesn't include ssl_padding_xtn. It includes the maximum + * number of extensions that are supported for any single message type. That + * is, a ClientHello; ServerHello and TLS 1.3 NewSessionTicket and + * HelloRetryRequest extensions are smaller. */ +#define SSL_MAX_EXTENSIONS 17 /* Deprecated */ typedef enum { ssl_dhe_group_none = 0, ssl_ff_dhe_2048_group = 1, ssl_ff_dhe_3072_group = 2, ssl_ff_dhe_4096_group = 3, ssl_ff_dhe_6144_group = 4,
--- a/security/nss/lib/ssl/tls13con.c +++ b/security/nss/lib/ssl/tls13con.c @@ -64,17 +64,19 @@ static SECStatus tls13_HandleCertificate static SECStatus tls13_HandleCertificateRequest(sslSocket *ss, SSL3Opaque *b, PRUint32 length); static SECStatus tls13_SendCertificateVerify(sslSocket *ss, SECKEYPrivateKey *privKey); static SECStatus tls13_HandleCertificateVerify( sslSocket *ss, SSL3Opaque *b, PRUint32 length, TLS13CombinedHash *hashes); static SECStatus -tls13_DeriveSecret(sslSocket *ss, PK11SymKey *key, const char *label, +tls13_DeriveSecret(sslSocket *ss, PK11SymKey *key, + const char *prefix, + const char *suffix, const TLS13CombinedHash *hashes, PK11SymKey **dest); static void tls13_SetNullCipherSpec(sslSocket *ss, ssl3CipherSpec **specp); static SECStatus tls13_SendEndOfEarlyData(sslSocket *ss); static SECStatus tls13_SendFinished(sslSocket *ss, PK11SymKey *baseKey); static SECStatus tls13_VerifyFinished(sslSocket *ss, PK11SymKey *secret, SSL3Opaque *b, PRUint32 length, const TLS13CombinedHash *hashes); @@ -100,35 +102,37 @@ static SECStatus tls13_ComputeApplicatio static SECStatus tls13_ComputeFinalSecrets(sslSocket *ss); static SECStatus tls13_ComputeFinished( sslSocket *ss, PK11SymKey *baseKey, const TLS13CombinedHash *hashes, PRBool sending, PRUint8 *output, unsigned int *outputLen, unsigned int maxOutputLen); static SECStatus tls13_SendClientSecondRound(sslSocket *ss); static SECStatus tls13_FinishHandshake(sslSocket *ss); +const char kHkdfLabelClient[] = "client"; +const char kHkdfLabelServer[] = "server"; const char kHkdfLabelEarlyTrafficSecret[] = "early traffic secret"; const char kHkdfLabelHandshakeTrafficSecret[] = "handshake traffic secret"; const char kHkdfLabelApplicationTrafficSecret[] = "application traffic secret"; -const char kHkdfLabelClientFinishedSecret[] = "client finished"; -const char kHkdfLabelServerFinishedSecret[] = "server finished"; +const char kHkdfLabelFinishedSecret[] = "finished"; const char kHkdfLabelResumptionMasterSecret[] = "resumption master secret"; const char kHkdfLabelResumptionPsk[] = "resumption psk"; const char kHkdfLabelResumptionContext[] = "resumption context"; const char kHkdfLabelExporterMasterSecret[] = "exporter master secret"; const char kHkdfPhaseEarlyHandshakeDataKeys[] = "early handshake key expansion"; const char kHkdfPhaseEarlyApplicationDataKeys[] = "early application data key expansion"; const char kHkdfPhaseHandshakeKeys[] = "handshake key expansion"; const char kHkdfPhaseApplicationDataKeys[] = "application data key expansion"; -const char kHkdfPurposeClientWriteKey[] = "client write key"; -const char kHkdfPurposeServerWriteKey[] = "server write key"; -const char kHkdfPurposeClientWriteIv[] = "client write iv"; -const char kHkdfPurposeServerWriteIv[] = "server write iv"; -const char kClientFinishedLabel[] = "client finished"; -const char kServerFinishedLabel[] = "server finished"; +const char kHkdfPurposeKey[] = "key"; +const char kHkdfPurposeIv[] = "iv"; + +#define TRAFFIC_SECRET(ss, dir, name) ((ss->sec.isServer ^ \ + (dir == CipherSpecWrite)) \ + ? ss->ssl3.hs.client##name \ + : ss->ssl3.hs.server##name) const SSL3ProtocolVersion kTlsRecordVersion = SSL_LIBRARY_VERSION_TLS_1_0; const SSL3ProtocolVersion kDtlsRecordVersion = SSL_LIBRARY_VERSION_TLS_1_1; /* Belt and suspenders in case we ever add a TLS 1.4. */ PR_STATIC_ASSERT(SSL_LIBRARY_VERSION_MAX_SUPPORTED <= SSL_LIBRARY_VERSION_TLS_1_3); @@ -688,55 +692,62 @@ loser: } return SECFailure; } /* Key Derivation Functions. * * Below is the key schedule from [draft-ietf-tls-tls13]. * - * The relevant functions from this file are indicated by tls13_Foo(). + * * The relevant functions from this file are indicated by tls13_Foo(). + * 0 + * | + * v + * PSK -> HKDF-Extract + * | + * v + * Early Secret ---> Derive-Secret(., "client early traffic secret", + * | ClientHello) + * | = client_early_traffic_secret + * v + * (EC)DHE -> HKDF-Extract + * | + * v + * Handshake Secret + * | + * +---------> Derive-Secret(., "client handshake traffic secret", + * | ClientHello...ServerHello) + * | = client_handshake_traffic_secret + * | + * +---------> Derive-Secret(., "server handshake traffic secret", + * | ClientHello...ServerHello) + * | = server_handshake_traffic_secret + * | + * v + * 0 -> HKDF-Extract + * | + * v + * Master Secret + * | + * +---------> Derive-Secret(., "client application traffic secret", + * | ClientHello...Server Finished) + * | = client_traffic_secret_0 + * | + * +---------> Derive-Secret(., "server application traffic secret", + * | ClientHello...Server Finished) + * | = server_traffic_secret_0 + * | + * +---------> Derive-Secret(., "exporter master secret", + * | ClientHello...Client Finished) + * | = exporter_secret + * | + * +---------> Derive-Secret(., "resumption master secret", + * ClientHello...Client Finished) + * = resumption_secret * - * 0 - * | - * v - * PSK -> HKDF-Extract tls13_ComputeEarlySecrets() - * | - * v - * Early Secret --> Derive-Secret(., "early traffic secret", - * | ClientHello) - * | = early_traffic_secret - * v - * (EC)DHE -> HKDF-Extract tls13_ComputeHandshakeSecrets() - * | - * v - * Handshake - * Secret -----> Derive-Secret(., "handshake traffic secret", - * | ClientHello + ServerHello) - * | = handshake_traffic_secret - * v - * 0 -> HKDF-Extract tls13_ComputeApplicationSecret - * | - * v - * Master Secret - * | - * +---------> Derive-Secret(., "application traffic secret", - * | ClientHello...Server Finished) - * | = traffic_secret_0 - * | - * | - * | tls13_ComputeFinalSecrets() - * | - * +---------> Derive-Secret(., "exporter master secret", - * | ClientHello...Client Finished) - * | = exporter_secret - * | - * +---------> Derive-Secret(., "resumption master secret", - * ClientHello...Client Finished) - * = resumption_secret */ static SECStatus tls13_ComputeEarlySecrets(sslSocket *ss, PRBool setup0Rtt) { SECStatus rv = SECSuccess; PK11Context *ctx; PRUint8 hash[HASH_LENGTH_MAX]; @@ -794,19 +805,20 @@ tls13_ComputeEarlySecrets(sslSocket *ss, tls13_GetHashSize(ss)) == NULL) { return SECFailure; } PORT_Memcpy(ss->ssl3.hs.resumptionContext.data, hash, len); if (setup0Rtt) { /* Derive the early secret. */ rv = tls13_DeriveSecret(ss, ss->ssl3.hs.currentSecret, + kHkdfLabelClient, kHkdfLabelEarlyTrafficSecret, NULL, - &ss->ssl3.hs.earlyTrafficSecret); + &ss->ssl3.hs.clientEarlyTrafficSecret); if (rv != SECSuccess) return SECFailure; } return SECSuccess; } static SECStatus @@ -824,20 +836,29 @@ tls13_ComputeHandshakeSecrets(sslSocket LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE); return rv; } PK11_FreeSymKey(ss->ssl3.hs.dheSecret); ss->ssl3.hs.dheSecret = NULL; PK11_FreeSymKey(ss->ssl3.hs.currentSecret); ss->ssl3.hs.currentSecret = newSecret; - /* Now compute |hsTrafficSecret| */ + /* Now compute |*HsTrafficSecret| */ rv = tls13_DeriveSecret(ss, ss->ssl3.hs.currentSecret, + kHkdfLabelClient, kHkdfLabelHandshakeTrafficSecret, NULL, - &ss->ssl3.hs.hsTrafficSecret); + &ss->ssl3.hs.clientHsTrafficSecret); + if (rv != SECSuccess) { + LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE); + return rv; + } + rv = tls13_DeriveSecret(ss, ss->ssl3.hs.currentSecret, + kHkdfLabelServer, + kHkdfLabelHandshakeTrafficSecret, NULL, + &ss->ssl3.hs.serverHsTrafficSecret); if (rv != SECSuccess) { LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE); return rv; } /* Crank HKDF forward to make master secret, which we * stuff in current secret. */ rv = tls13_HkdfExtract(ss->ssl3.hs.currentSecret, @@ -856,19 +877,28 @@ tls13_ComputeHandshakeSecrets(sslSocket } static SECStatus tls13_ComputeApplicationSecrets(sslSocket *ss) { SECStatus rv; rv = tls13_DeriveSecret(ss, ss->ssl3.hs.currentSecret, + kHkdfLabelClient, kHkdfLabelApplicationTrafficSecret, NULL, - &ss->ssl3.hs.trafficSecret); + &ss->ssl3.hs.clientTrafficSecret); + if (rv != SECSuccess) { + return SECFailure; + } + rv = tls13_DeriveSecret(ss, ss->ssl3.hs.currentSecret, + kHkdfLabelServer, + kHkdfLabelApplicationTrafficSecret, + NULL, + &ss->ssl3.hs.serverTrafficSecret); if (rv != SECSuccess) { return SECFailure; } return SECSuccess; } static SECStatus @@ -876,17 +906,17 @@ tls13_ComputeFinalSecrets(sslSocket *ss) { SECStatus rv; PK11SymKey *resumptionMasterSecret = NULL; PORT_Assert(!ss->ssl3.crSpec->master_secret); PORT_Assert(!ss->ssl3.cwSpec->master_secret); rv = tls13_DeriveSecret(ss, ss->ssl3.hs.currentSecret, - kHkdfLabelResumptionMasterSecret, + NULL, kHkdfLabelResumptionMasterSecret, NULL, &resumptionMasterSecret); PK11_FreeSymKey(ss->ssl3.hs.currentSecret); ss->ssl3.hs.currentSecret = NULL; if (rv != SECSuccess) { return SECFailure; } /* This is pretty gross. TLS 1.3 uses a number of master secrets: @@ -1370,45 +1400,52 @@ tls13_SendHelloRetryRequest(sslSocket *s if (ss->ssl3.hs.helloRetry) { FATAL_ERROR(ss, SSL_ERROR_BAD_2ND_CLIENT_HELLO, illegal_parameter); return SECFailure; } ssl_GetXmitBufLock(ss); rv = ssl3_AppendHandshakeHeader(ss, hello_retry_request, 2 + /* version */ - 2 + /* cipher suite */ - 2 + /* group */ - 2 /* (empty) extensions */); + 2 + /* extension length */ + 2 + /* group extension id */ + 2 + /* group extension length */ + 2 /* group */); if (rv != SECSuccess) { FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); goto loser; } rv = ssl3_AppendHandshakeNumber( ss, tls13_EncodeDraftVersion(ss->version), 2); if (rv != SECSuccess) { FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); goto loser; } - rv = ssl3_AppendHandshakeNumber(ss, ss->ssl3.hs.cipher_suite, 2); + /* Length of extensions. */ + rv = ssl3_AppendHandshakeNumber(ss, 2 + 2 + 2, 2); if (rv != SECSuccess) { FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); goto loser; } - rv = ssl3_AppendHandshakeNumber(ss, selectedGroup->name, 2); + /* Key share extension - currently the only reason we send this. */ + rv = ssl3_AppendHandshakeNumber(ss, ssl_tls13_key_share_xtn, 2); if (rv != SECSuccess) { FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); goto loser; } - - /* Send an empty extensions block. */ - rv = ssl3_AppendHandshakeNumber(ss, 0, 2); + /* Key share extension length. */ + rv = ssl3_AppendHandshakeNumber(ss, 2, 2); + if (rv != SECSuccess) { + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); + goto loser; + } + rv = ssl3_AppendHandshakeNumber(ss, selectedGroup->name, 2); if (rv != SECSuccess) { FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); goto loser; } rv = ssl3_FlushHandshake(ss, 0); if (rv != SECSuccess) { goto loser; /* error code set by ssl3_FlushHandshake */ @@ -1573,17 +1610,16 @@ tls13_SendCertificateRequest(sslSocket * } static SECStatus tls13_HandleHelloRetryRequest(sslSocket *ss, SSL3Opaque *b, PRUint32 length) { SECStatus rv; PRInt32 tmp; PRUint32 version; - const sslNamedGroupDef *group; SSL_TRC(3, ("%d: TLS13[%d]: handle hello retry request", SSL_GETPID(), ss->fd)); PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss)); PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); /* Client */ @@ -1614,55 +1650,34 @@ tls13_HandleHelloRetryRequest(sslSocket } version = tls13_DecodeDraftVersion((PRUint16)tmp); if (version > ss->vrange.max || version < SSL_LIBRARY_VERSION_TLS_1_3) { FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST, protocol_version); return SECFailure; } - /* Ignore the cipher suite, it's going to be removed soon anyway. */ - tmp = ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length); - if (tmp < 0) { - return SECFailure; /* error code already set */ - } - - tmp = ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length); - if (tmp < 0) { - return SECFailure; /* error code already set */ - } - group = ssl_LookupNamedGroup((SSLNamedGroup)tmp); - if (!ssl_NamedGroupEnabled(ss, group)) { - FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST, - illegal_parameter); - return SECFailure; - } - tmp = ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length); if (tmp < 0) { return SECFailure; /* error code already set */ } - /* Ignore the extensions. */ - if (tmp != length) { + /* Extensions must be non-empty and use the remainder of the message. + * This means that a HelloRetryRequest cannot be a no-op: we must have an + * extension, it must be one that we understand and recognize as being valid + * for HelloRetryRequest, and all the extensions we permit cause us to + * modify our ClientHello in some way. */ + if (!tmp || tmp != length) { FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST, decode_error); return SECFailure; } - /* If there is already a share for the requested group, abort. */ - if (ssl_LookupEphemeralKeyPair(ss, group)) { - FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST, - illegal_parameter); - return SECFailure; - } - - rv = tls13_CreateKeyShare(ss, group); + rv = ssl3_HandleExtensions(ss, &b, &length, hello_retry_request); if (rv != SECSuccess) { - FATAL_ERROR(ss, SEC_ERROR_KEYGEN_FAIL, internal_error); - return SECFailure; + return SECFailure; /* Error code set below */ } ss->ssl3.hs.helloRetry = PR_TRUE; ssl_GetXmitBufLock(ss); rv = ssl3_SendClientHello(ss, client_hello_retry); ssl_ReleaseXmitBufLock(ss); if (rv != SECSuccess) { @@ -1808,17 +1823,17 @@ tls13_SendEncryptedServerSequence(sslSoc svrPrivKey = ss->sec.serverCert->serverKeyPair->privKey; rv = tls13_SendCertificateVerify(ss, svrPrivKey); if (rv != SECSuccess) { return SECFailure; /* err code is set. */ } } - rv = tls13_SendFinished(ss, ss->ssl3.hs.hsTrafficSecret); + rv = tls13_SendFinished(ss, ss->ssl3.hs.serverHsTrafficSecret); if (rv != SECSuccess) { return SECFailure; /* error code is set. */ } return SECSuccess; } /* Called from: ssl3_HandleClientHello */ @@ -2243,22 +2258,39 @@ loser: } /* * Derive-Secret(Secret, Label, Messages) = * HKDF-Expand-Label(Secret, Label, * Hash(Messages) + Hash(resumption_context), L)) */ static SECStatus -tls13_DeriveSecret(sslSocket *ss, PK11SymKey *key, const char *label, +tls13_DeriveSecret(sslSocket *ss, PK11SymKey *key, + const char *prefix, + const char *suffix, const TLS13CombinedHash *hashes, PK11SymKey **dest) { SECStatus rv; TLS13CombinedHash hashesTmp; + char buf[100]; + const char *label; + + if (prefix) { + if ((strlen(prefix) + strlen(suffix) + 2) > sizeof(buf)) { + PORT_Assert(0); + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + (void)PR_snprintf(buf, sizeof(buf), "%s %s", + prefix, suffix); + label = buf; + } else { + label = suffix; + } SSL_TRC(3, ("%d: TLS13[%d]: deriving secret '%s'", SSL_GETPID(), ss->fd, label)); if (!hashes) { rv = tls13_ComputeHandshakeHashes(ss, &hashesTmp); if (rv != SECSuccess) { PORT_Assert(0); /* Should never fail */ ssl_MapLowLevelError(SEC_ERROR_LIBRARY_FAILURE); @@ -2289,96 +2321,96 @@ tls13_DeriveTrafficKeys(sslSocket *ss, s size_t keySize = spec->cipher_def->key_size; size_t ivSize = spec->cipher_def->iv_size + spec->cipher_def->explicit_nonce_size; /* This isn't always going to * work, but it does for * AES-GCM */ CK_MECHANISM_TYPE bulkAlgorithm = ssl3_Alg2Mech(spec->cipher_def->calg); PK11SymKey **prkp = NULL; PK11SymKey *prk = NULL; + PRBool clientKey; + ssl3KeyMaterial *target; const char *phase; char label[256]; /* Arbitrary buffer large enough to hold the label */ SECStatus rv; + if (ss->sec.isServer ^ (direction == CipherSpecWrite)) { + clientKey = PR_TRUE; + target = &spec->client; + } else { + clientKey = PR_FALSE; + target = &spec->server; + } + #define FORMAT_LABEL(phase_, purpose_) \ do { \ PRUint32 n = PR_snprintf(label, sizeof(label), "%s, %s", phase_, purpose_); \ /* Check for getting close. */ \ if ((n + 1) >= sizeof(label)) { \ LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE); \ PORT_Assert(0); \ goto loser; \ } \ } while (0) -#define EXPAND_TRAFFIC_KEY(purpose_, target_) \ - do { \ - FORMAT_LABEL(phase, purpose_); \ - rv = tls13_HkdfExpandLabel(prk, tls13_GetHash(ss), \ - NULL, 0, \ - label, strlen(label), \ - bulkAlgorithm, keySize, &spec->target_); \ - if (rv != SECSuccess) { \ - LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE); \ - PORT_Assert(0); \ - goto loser; \ - } \ - } while (0) - -#define EXPAND_TRAFFIC_IV(purpose_, target_) \ - do { \ - FORMAT_LABEL(phase, purpose_); \ - rv = tls13_HkdfExpandLabelRaw(prk, tls13_GetHash(ss), \ - NULL, 0, \ - label, strlen(label), \ - spec->target_, ivSize); \ - if (rv != SECSuccess) { \ - LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE); \ - PORT_Assert(0); \ - goto loser; \ - } \ - } while (0) PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); switch (type) { case TrafficKeyEarlyHandshake: + PORT_Assert(clientKey); phase = kHkdfPhaseEarlyHandshakeDataKeys; - prkp = &ss->ssl3.hs.earlyTrafficSecret; + prkp = &ss->ssl3.hs.clientEarlyTrafficSecret; break; case TrafficKeyEarlyApplicationData: + PORT_Assert(clientKey); phase = kHkdfPhaseEarlyApplicationDataKeys; - prkp = &ss->ssl3.hs.earlyTrafficSecret; + prkp = &ss->ssl3.hs.clientEarlyTrafficSecret; break; case TrafficKeyHandshake: phase = kHkdfPhaseHandshakeKeys; - prkp = &ss->ssl3.hs.hsTrafficSecret; + prkp = clientKey ? &ss->ssl3.hs.clientHsTrafficSecret : &ss->ssl3.hs.serverHsTrafficSecret; break; case TrafficKeyApplicationData: phase = kHkdfPhaseApplicationDataKeys; - prkp = &ss->ssl3.hs.trafficSecret; + prkp = clientKey ? &ss->ssl3.hs.clientTrafficSecret : &ss->ssl3.hs.serverTrafficSecret; break; default: LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE); PORT_Assert(0); return SECFailure; } PORT_Assert(prkp != NULL); prk = *prkp; SSL_TRC(3, ("%d: TLS13[%d]: deriving traffic keys phase='%s'", SSL_GETPID(), ss->fd, phase)); PORT_Assert(phase); spec->phase = phase; - if ((direction == CipherSpecWrite) ^ (ss->sec.isServer)) { - EXPAND_TRAFFIC_KEY(kHkdfPurposeClientWriteKey, client.write_key); - EXPAND_TRAFFIC_IV(kHkdfPurposeClientWriteIv, client.write_iv); - } else { - EXPAND_TRAFFIC_KEY(kHkdfPurposeServerWriteKey, server.write_key); - EXPAND_TRAFFIC_IV(kHkdfPurposeServerWriteIv, server.write_iv); + FORMAT_LABEL(phase, kHkdfPurposeKey); + rv = tls13_HkdfExpandLabel(prk, tls13_GetHash(ss), + NULL, 0, + label, strlen(label), + bulkAlgorithm, keySize, + &target->write_key); + if (rv != SECSuccess) { + LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE); + PORT_Assert(0); + goto loser; + } + + FORMAT_LABEL(phase, kHkdfPurposeIv); + rv = tls13_HkdfExpandLabelRaw(prk, tls13_GetHash(ss), + NULL, 0, + label, strlen(label), + target->write_iv, ivSize); + if (rv != SECSuccess) { + LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE); + PORT_Assert(0); + goto loser; } if (deleteSecret) { PK11_FreeSymKey(prk); *prkp = NULL; } return SECSuccess; @@ -3030,17 +3062,17 @@ tls13_ComputeFinished(sslSocket *ss, PK1 PRBool sending, PRUint8 *output, unsigned int *outputLen, unsigned int maxOutputLen) { SECStatus rv; PK11Context *hmacCtx = NULL; CK_MECHANISM_TYPE macAlg = tls13_GetHmacMechanism(ss); SECItem param = { siBuffer, NULL, 0 }; unsigned int outputLenUint; - const char *label = (ss->sec.isServer ^ sending) ? kHkdfLabelClientFinishedSecret : kHkdfLabelServerFinishedSecret; + const char *label = kHkdfLabelFinishedSecret; PK11SymKey *secret = NULL; PORT_Assert(baseKey); PRINT_BUF(50, (NULL, "Handshake hash", hashes->hash, hashes->len)); /* Now derive the appropriate finished secret from the base secret. */ rv = tls13_HkdfExpandLabel(baseKey, tls13_GetHash(ss), @@ -3181,17 +3213,17 @@ tls13_ClientHandleFinished(sslSocket *ss SSL_GETPID(), ss->fd)); rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_FINISHED, wait_finished); if (rv != SECSuccess) { return SECFailure; } - rv = tls13_VerifyFinished(ss, ss->ssl3.hs.hsTrafficSecret, + rv = tls13_VerifyFinished(ss, ss->ssl3.hs.serverHsTrafficSecret, b, length, hashes); if (rv != SECSuccess) return SECFailure; return tls13_SendClientSecondRound(ss); } static SECStatus @@ -3209,19 +3241,19 @@ tls13_ServerHandleFinished(sslSocket *ss rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_FINISHED, wait_finished, wait_0rtt_finished); if (rv != SECSuccess) { return SECFailure; } if (TLS13_IN_HS_STATE(ss, wait_finished)) { - secret = ss->ssl3.hs.hsTrafficSecret; + secret = ss->ssl3.hs.clientHsTrafficSecret; } else { - secret = ss->ssl3.hs.earlyTrafficSecret; + secret = ss->ssl3.hs.clientEarlyTrafficSecret; } rv = tls13_VerifyFinished(ss, secret, b, length, hashes); if (rv != SECSuccess) return SECFailure; if (TLS13_IN_HS_STATE(ss, wait_0rtt_finished)) { /* Reset the hashes. */ @@ -3282,18 +3314,20 @@ tls13_FinishHandshake(sslSocket *ss) rv = tls13_ComputeFinalSecrets(ss); if (rv != SECSuccess) return SECFailure; /* The first handshake is now completed. */ ss->handshake = NULL; /* Don't need this. */ - PK11_FreeSymKey(ss->ssl3.hs.hsTrafficSecret); - ss->ssl3.hs.hsTrafficSecret = NULL; + PK11_FreeSymKey(ss->ssl3.hs.clientHsTrafficSecret); + ss->ssl3.hs.clientHsTrafficSecret = NULL; + PK11_FreeSymKey(ss->ssl3.hs.serverHsTrafficSecret); + ss->ssl3.hs.serverHsTrafficSecret = NULL; TLS13_SET_HS_STATE(ss, idle_handshake); ssl_FinishHandshake(ss); return SECSuccess; } @@ -3384,17 +3418,17 @@ tls13_SendClientSecondRound(sslSocket *s rv = tls13_SendCertificateVerify(ss, ss->ssl3.clientPrivateKey); SECKEY_DestroyPrivateKey(ss->ssl3.clientPrivateKey); ss->ssl3.clientPrivateKey = NULL; if (rv != SECSuccess) { goto loser; /* err is set. */ } } - rv = tls13_SendFinished(ss, ss->ssl3.hs.hsTrafficSecret); + rv = tls13_SendFinished(ss, ss->ssl3.hs.clientHsTrafficSecret); if (rv != SECSuccess) { goto loser; /* err code was set. */ } rv = ssl3_FlushHandshake(ss, IS_DTLS(ss) ? ssl_SEND_FLAG_NO_RETRANSMIT : 0); if (rv != SECSuccess) { goto loser; } @@ -3664,16 +3698,18 @@ tls13_HandleNewSessionTicket(sslSocket * return SECSuccess; } typedef enum { ExtensionNotUsed, ExtensionClientOnly, ExtensionSendClear, + ExtensionSendClearOrHrr, + ExtensionSendHrr, ExtensionSendEncrypted, ExtensionNewSessionTicket } Tls13ExtensionStatus; static const struct { PRUint16 ex_value; Tls13ExtensionStatus status; } KnownExtensions[] = { @@ -3681,33 +3717,35 @@ static const struct { { ssl_supported_groups_xtn, ExtensionSendEncrypted }, { ssl_ec_point_formats_xtn, ExtensionNotUsed }, { ssl_signature_algorithms_xtn, ExtensionSendClear }, { ssl_use_srtp_xtn, ExtensionSendEncrypted }, { ssl_app_layer_protocol_xtn, ExtensionSendEncrypted }, { ssl_padding_xtn, ExtensionNotUsed }, { ssl_extended_master_secret_xtn, ExtensionNotUsed }, { ssl_session_ticket_xtn, ExtensionClientOnly }, - { ssl_tls13_key_share_xtn, ExtensionSendClear }, + { ssl_tls13_key_share_xtn, ExtensionSendClearOrHrr }, { ssl_tls13_pre_shared_key_xtn, ExtensionSendClear }, { ssl_tls13_early_data_xtn, ExtensionSendEncrypted }, { ssl_next_proto_nego_xtn, ExtensionNotUsed }, { ssl_renegotiation_info_xtn, ExtensionNotUsed }, { ssl_signed_cert_timestamp_xtn, ExtensionSendEncrypted }, { ssl_cert_status_xtn, ExtensionSendEncrypted }, - { ssl_tls13_ticket_early_data_info_xtn, ExtensionNewSessionTicket } + { ssl_tls13_ticket_early_data_info_xtn, ExtensionNewSessionTicket }, + { ssl_tls13_cookie_xtn, ExtensionSendHrr } }; PRBool tls13_ExtensionAllowed(PRUint16 extension, SSL3HandshakeType message) { unsigned int i; PORT_Assert((message == client_hello) || (message == server_hello) || + (message == hello_retry_request) || (message == encrypted_extensions) || (message == new_session_ticket)); for (i = 0; i < PR_ARRAY_SIZE(KnownExtensions); i++) { if (KnownExtensions[i].ex_value == extension) break; } if (i == PR_ARRAY_SIZE(KnownExtensions)) { @@ -3719,16 +3757,23 @@ tls13_ExtensionAllowed(PRUint16 extensio switch (KnownExtensions[i].status) { case ExtensionNotUsed: return PR_FALSE; case ExtensionClientOnly: return message == client_hello; case ExtensionSendClear: return message == client_hello || message == server_hello; + case ExtensionSendClearOrHrr: + return message == client_hello || + message == server_hello || + message == hello_retry_request; + case ExtensionSendHrr: + return message == client_hello || + message == hello_retry_request; case ExtensionSendEncrypted: return message == client_hello || message == encrypted_extensions; case ExtensionNewSessionTicket: return message == new_session_ticket; } PORT_Assert(0); @@ -4013,17 +4058,17 @@ tls13_MaybeDo0RTTHandshake(sslSocket *ss rv = tls13_SetCipherSpec(ss, TrafficKeyEarlyHandshake, CipherSpecWrite, PR_FALSE); if (rv != SECSuccess) { FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); return SECFailure; } - rv = tls13_SendFinished(ss, ss->ssl3.hs.earlyTrafficSecret); + rv = tls13_SendFinished(ss, ss->ssl3.hs.clientEarlyTrafficSecret); if (rv != SECSuccess) { return SECFailure; } /* Restore the handshake hashes to where they were before we * sent Finished. */ ss->ssl3.hs.messages.len = bufferLen;
--- a/security/nss/lib/ssl/tls13con.h +++ b/security/nss/lib/ssl/tls13con.h @@ -47,16 +47,17 @@ SECStatus tls13_HandleClientHelloPart2(s const SECItem *suites, sslSessionID *sid); SECStatus tls13_HandleServerHelloPart2(sslSocket *ss); SECStatus tls13_HandlePostHelloHandshakeMessage(sslSocket *ss, SSL3Opaque *b, PRUint32 length, SSL3Hashes *hashesPtr); void tls13_DestroyKeyShareEntry(TLS13KeyShareEntry *entry); void tls13_DestroyKeyShares(PRCList *list); +SECStatus tls13_CreateKeyShare(sslSocket *ss, const sslNamedGroupDef *groupDef); void tls13_DestroyEarlyData(PRCList *list); void tls13_CipherSpecAddRef(ssl3CipherSpec *spec); void tls13_CipherSpecRelease(ssl3CipherSpec *spec); void tls13_DestroyCipherSpecs(PRCList *list); PRBool tls13_ExtensionAllowed(PRUint16 extension, SSL3HandshakeType message); SECStatus tls13_ProtectRecord(sslSocket *ss, ssl3CipherSpec *cwSpec, SSL3ContentType type,
--- a/security/nss/lib/util/secasn1e.c +++ b/security/nss/lib/util/secasn1e.c @@ -1415,18 +1415,20 @@ static void sec_asn1e_encode_item_store(void *arg, const char *buf, unsigned long len, int depth, SEC_ASN1EncodingPart data_kind) { SECItem *dest; dest = (SECItem *)arg; PORT_Assert(dest != NULL); - PORT_Memcpy(dest->data + dest->len, buf, len); - dest->len += len; + if (len > 0) { + PORT_Memcpy(dest->data + dest->len, buf, len); + dest->len += len; + } } /* * Allocate an entire SECItem, or just the data part of it, to hold * "len" bytes of stuff. Allocate from the given pool, if specified, * otherwise just do a vanilla PORT_Alloc. * * XXX This seems like a reasonable general-purpose function (for SECITEM_)?
--- a/security/nss/lib/util/utf8.c +++ b/security/nss/lib/util/utf8.c @@ -317,25 +317,28 @@ sec_port_ucs2_utf8_conversion_function( return PR_FALSE; } for (i = 0; i < inBufLen; i += 2) { if ((inBuf[i + H_0] == 0x00) && ((inBuf[i + H_1] & 0x80) == 0x00)) len += 1; else if (inBuf[i + H_0] < 0x08) len += 2; - else if (((inBuf[i + 0 + H_0] & 0xFC) == 0xD8)) { + else if (((inBuf[i + H_0] & 0xFC) == 0xD8)) { if (((inBufLen - i) > 2) && ((inBuf[i + 2 + H_0] & 0xFC) == 0xDC)) { i += 2; len += 4; } else { return PR_FALSE; } - } else + } else if ((inBuf[i + H_0] & 0xFC) == 0xDC) { + return PR_FALSE; + } else { len += 3; + } } if (len > maxOutBufLen) { *outBufLen = len; return PR_FALSE; } len = 0;
--- a/taskcluster/ci/docker-image/image.yml +++ b/taskcluster/ci/docker-image/image.yml @@ -30,16 +30,17 @@ task: CONTEXT_URL: '{{context_url}}' CONTEXT_PATH: '{{context_path}}' BASE_REPOSITORY: '{{base_repository}}' HEAD_REPOSITORY: '{{head_repository}}' HEAD_REV: '{{head_rev}}' HEAD_REF: '{{head_ref}}' features: dind: true + chainOfTrust: true image: '{{#docker_image}}image_builder{{/docker_image}}' command: - /bin/bash - -c - /home/worker/bin/build_image.sh maxRunTime: 3600 artifacts: '{{artifact_path}}':
--- a/taskcluster/taskgraph/transforms/build.py +++ b/taskcluster/taskgraph/transforms/build.py @@ -16,9 +16,10 @@ transforms = TransformSequence() @transforms.add def set_defaults(config, jobs): """Set defaults, including those that differ per worker implementation""" for job in jobs: job['treeherder'].setdefault('kind', 'build') job['treeherder'].setdefault('tier', 1) if job['worker']['implementation'] in ('docker-worker', 'docker-engine'): job['worker'].setdefault('docker-image', {'in-tree': 'desktop-build'}) + job['worker']['chainOfTrust'] = True yield job
--- a/taskcluster/taskgraph/transforms/task.py +++ b/taskcluster/taskgraph/transforms/task.py @@ -152,16 +152,17 @@ task_description_schema = Schema({ # a raw Docker image path (repo/image:tag) basestring, # an in-tree generated docker image (from `testing/docker/<name>`) {'in-tree': basestring} ), # worker features that should be enabled Required('relengapi-proxy', default=False): bool, + Required('chainOfTrust', default=False): bool, Required('taskcluster-proxy', default=False): bool, Required('allow-ptrace', default=False): bool, Required('loopback-video', default=False): bool, Required('loopback-audio', default=False): bool, # caches to set up for the task Optional('caches'): [{ # only one type is supported by any of the workers right now @@ -321,16 +322,19 @@ def build_docker_worker_payload(config, if worker.get('taskcluster-proxy'): features['taskclusterProxy'] = True if worker.get('allow-ptrace'): features['allowPtrace'] = True task_def['scopes'].append('docker-worker:feature:allowPtrace') + if worker.get('chainOfTrust'): + features['chainOfTrust'] = True + capabilities = {} for lo in 'audio', 'video': if worker.get('loopback-' + lo): capitalized = 'loopback' + lo.capitalize() devices = capabilities.setdefault('devices', {}) devices[capitalized] = True task_def['scopes'].append('docker-worker:capability:device:' + capitalized)
--- a/testing/tools/fileid/linux_fileid.cpp +++ b/testing/tools/fileid/linux_fileid.cpp @@ -1,51 +1,41 @@ /* 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 <stdio.h> +#include <string> #include "common/linux/file_id.h" +#include "common/memory.h" -//TODO: move this somewhere common, this is copied from dump_symbols.cc -// Format the Elf file identifier in IDENTIFIER as a UUID with the -// dashes removed. -void FormatIdentifier(unsigned char identifier[google_breakpad::kMDGUIDSize], - char result_guid[40]) { - char identifier_str[40]; - google_breakpad::FileID::ConvertIdentifierToString( - identifier, - identifier_str, - sizeof(identifier_str)); - int bufpos = 0; - for (int i = 0; identifier_str[i] != '\0'; ++i) - if (identifier_str[i] != '-') - result_guid[bufpos++] = identifier_str[i]; - // Add an extra "0" by the end. PDB files on Windows have an 'age' - // number appended to the end of the file identifier; this isn't - // really used or necessary on other platforms, but let's preserve - // the pattern. - result_guid[bufpos++] = '0'; - // And null terminate. - result_guid[bufpos] = '\0'; -} +using std::string; +using google_breakpad::auto_wasteful_vector; +using google_breakpad::FileID; +using google_breakpad::PageAllocator; int main(int argc, char** argv) { + if (argc != 2) { fprintf(stderr, "usage: fileid <elf file>\n"); return 1; } - unsigned char identifier[google_breakpad::kMDGUIDSize]; - google_breakpad::FileID file_id(argv[1]); + PageAllocator allocator; + auto_wasteful_vector<uint8_t, sizeof(MDGUID)> identifier(&allocator); + FileID file_id(argv[1]); if (!file_id.ElfFileIdentifier(identifier)) { fprintf(stderr, "%s: unable to generate file identifier\n", argv[1]); return 1; } - char result_guid[40]; - FormatIdentifier(identifier, result_guid); - printf("%s\n", result_guid); + + string result_guid = FileID::ConvertIdentifierToUUIDString(identifier); + + // Add an extra "0" at the end. PDB files on Windows have an 'age' + // number appended to the end of the file identifier; this isn't + // really used or necessary on other platforms, but be consistent. + printf("%s0\n", result_guid.c_str()); return 0; }
--- a/toolkit/components/telemetry/TelemetryHistogram.cpp +++ b/toolkit/components/telemetry/TelemetryHistogram.cpp @@ -1547,17 +1547,20 @@ internal_JSHistogram_Add(JSContext *cx, } if (!JS::ToUint32(cx, args[0], &value)) { JS_ReportErrorASCII(cx, "Failed to convert argument"); return false; } } - internal_Accumulate(*h, value); + { + StaticMutexAutoLock locker(gTelemetryHistogramMutex); + internal_Accumulate(*h, value); + } return true; } bool internal_JSHistogram_Snapshot(JSContext *cx, unsigned argc, JS::Value *vp) { JS::CallArgs args = JS::CallArgsFromVp(argc, vp); JSObject *obj = JS_THIS_OBJECT(cx, vp); @@ -1795,17 +1798,20 @@ internal_JSKeyedHistogram_Add(JSContext return false; } if (!JS::ToInt32(cx, args[1], &value)) { return false; } } - internal_Accumulate(*keyed, NS_ConvertUTF16toUTF8(key), value); + { + StaticMutexAutoLock locker(gTelemetryHistogramMutex); + internal_Accumulate(*keyed, NS_ConvertUTF16toUTF8(key), value); + } return true; } bool internal_JSKeyedHistogram_Keys(JSContext *cx, unsigned argc, JS::Value *vp) { JSObject *obj = JS_THIS_OBJECT(cx, vp); if (!obj) {
--- a/toolkit/crashreporter/google-breakpad/.gitignore +++ b/toolkit/crashreporter/google-breakpad/.gitignore @@ -26,30 +26,32 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # Ignore other VCSs. .svn/ # Ignore common compiled artifacts. *~ +*.dwo *.o lib*.a /breakpad.pc /breakpad-client.pc /src/client/linux/linux_client_unittest_shlib /src/client/linux/linux_dumper_unittest_helper /src/processor/microdump_stackwalk /src/processor/minidump_dump /src/processor/minidump_stackwalk /src/tools/linux/core2md/core2md /src/tools/linux/dump_syms/dump_syms /src/tools/linux/md2core/minidump-2-core /src/tools/linux/symupload/minidump_upload /src/tools/linux/symupload/sym_upload +/src/tools/mac/dump_syms/dump_syms # Ignore autotools generated artifacts. .deps .dirstamp autom4te.cache/ /config.cache config.h /config.log
--- a/toolkit/crashreporter/google-breakpad/DEPS +++ b/toolkit/crashreporter/google-breakpad/DEPS @@ -30,33 +30,41 @@ # SVN) based checkouts of Breakpad. As such, its use is entirely optional. If # using a manually managed SVN checkout as opposed to a gclient managed checkout # you can still use the hooks mechanism for generating project files by calling # 'gclient runhooks' rather than 'gclient sync'. deps = { # Logging code. "src/src/third_party/glog": - "http://google-glog.googlecode.com/svn/trunk@97", + "https://github.com/google/glog.git" + + "@v0.3.4", # Testing libraries and utilities. - "src/src/testing": "http://googlemock.googlecode.com/svn/trunk@408", - "src/src/testing/gtest": "http://googletest.googlecode.com/svn/trunk@615", + "src/src/testing": + "https://github.com/google/googlemock.git" + + "@release-1.7.0", + "src/src/testing/gtest": + "https://github.com/google/googletest.git" + + "@release-1.7.0", # Protobuf. "src/src/third_party/protobuf/protobuf": - "http://protobuf.googlecode.com/svn/trunk@407", + "https://github.com/google/protobuf.git" + + "@cb6dd4ef5f82e41e06179dcd57d3b1d9246ad6ac", # GYP project generator. - "src/src/tools/gyp": "http://gyp.googlecode.com/svn/trunk@1886", + "src/src/tools/gyp": + "https://chromium.googlesource.com/external/gyp/" + + "@e8ab0833a42691cd2184bd4c45d779e43821d3e0", # Linux syscall support. "src/src/third_party/lss": "https://chromium.googlesource.com/linux-syscall-support/" + - "@9292030109847793f7a6689adac1ddafb412fe14" + "@3f6478ac95edf86cd3da300c2c0d34a438f5dbeb", } hooks = [ { # TODO(chrisha): Fix the GYP files so that they work without # --no-circular-check. "pattern": ".", "action": ["python",
--- a/toolkit/crashreporter/google-breakpad/GIT-INFO +++ b/toolkit/crashreporter/google-breakpad/GIT-INFO @@ -1,1 +1,1 @@ -c53ed143108948eb7e2d7ee77dc8c0d92050ce7c +704f41ec901c419f8c321742114b415e6f5ceacc
--- a/toolkit/crashreporter/google-breakpad/Makefile.am +++ b/toolkit/crashreporter/google-breakpad/Makefile.am @@ -78,16 +78,19 @@ dist_doc_DATA = \ NEWS \ README.md ## Headers if LINUX_HOST includeclhdir = $(includedir)/$(PACKAGE)/client/linux/handler includeclh_HEADERS = $(top_srcdir)/src/client/linux/handler/*.h +includecldwcdir = $(includedir)/$(PACKAGE)/client/linux/dump_writer_common +includecldwc_HEADERS = $(top_srcdir)/src/client/linux/dump_writer_common/*.h + includeclmdir = $(includedir)/$(PACKAGE)/client/linux/minidump_writer includeclm_HEADERS = $(top_srcdir)/src/client/linux/minidump_writer/*.h includeclcdir = $(includedir)/$(PACKAGE)/client/linux/crash_generation includeclc_HEADERS = $(top_srcdir)/src/client/linux/crash_generation/*.h includelssdir = $(includedir)/$(PACKAGE)/third_party/lss includelss_HEADERS = $(top_srcdir)/src/third_party/lss/*.h @@ -104,21 +107,51 @@ includec_HEADERS = $(top_srcdir)/src/com includepdir = $(includedir)/$(PACKAGE)/processor includep_HEADERS = $(top_srcdir)/src/processor/*.h ## pkgconfig files pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = +## Common test logic +if SYSTEM_TEST_LIBS +TEST_CFLAGS = $(GTEST_CFLAGS) $(GMOCK_CFLAGS) +TEST_LIBS = $(GTEST_LIBS) -lgtest_main $(GMOCK_LIBS) +TEST_DEPS = +else +TEST_CFLAGS = \ + -I$(top_srcdir)/src/testing/include \ + -I$(top_srcdir)/src/testing/gtest/include \ + -I$(top_srcdir)/src/testing/gtest \ + -I$(top_srcdir)/src/testing +TEST_LIBS = src/testing/libtesting.a +TEST_DEPS = $(TEST_LIBS) +endif + ## Libraries +check_LIBRARIES = noinst_LIBRARIES = lib_LIBRARIES = bin_PROGRAMS = check_PROGRAMS = +EXTRA_PROGRAMS = +CLEANFILES = + +check_LIBRARIES += src/testing/libtesting.a + +if !SYSTEM_TEST_LIBS +src_testing_libtesting_a_SOURCES = \ + src/breakpad_googletest_includes.h \ + src/testing/gtest/src/gtest-all.cc \ + src/testing/gtest/src/gtest_main.cc \ + src/testing/src/gmock-all.cc +src_testing_libtesting_a_CPPFLAGS = \ + $(AM_CPPFLAGS) $(TEST_CFLAGS) +endif if !DISABLE_PROCESSOR lib_LIBRARIES += src/libbreakpad.a pkgconfig_DATA += breakpad.pc noinst_LIBRARIES += src/third_party/libdisasm/libdisasm.a endif if LINUX_HOST @@ -126,29 +159,43 @@ lib_LIBRARIES += src/client/linux/libbre pkgconfig_DATA += breakpad-client.pc src_client_linux_libbreakpad_client_a_SOURCES = \ src/client/linux/crash_generation/crash_generation_client.cc \ src/client/linux/crash_generation/crash_generation_server.cc \ src/client/linux/dump_writer_common/thread_info.cc \ src/client/linux/dump_writer_common/ucontext_reader.cc \ src/client/linux/handler/exception_handler.cc \ + src/client/linux/handler/exception_handler.h \ src/client/linux/handler/minidump_descriptor.cc \ + src/client/linux/handler/minidump_descriptor.h \ src/client/linux/log/log.cc \ + src/client/linux/log/log.h \ src/client/linux/microdump_writer/microdump_writer.cc \ + src/client/linux/microdump_writer/microdump_writer.h \ + src/client/linux/minidump_writer/linux_core_dumper.cc \ src/client/linux/minidump_writer/linux_dumper.cc \ src/client/linux/minidump_writer/linux_ptrace_dumper.cc \ src/client/linux/minidump_writer/minidump_writer.cc \ + src/client/minidump_file_writer-inl.h \ src/client/minidump_file_writer.cc \ + src/client/minidump_file_writer.h \ src/common/convert_UTF.c \ + src/common/convert_UTF.h \ src/common/md5.cc \ + src/common/md5.h \ src/common/string_conversion.cc \ + src/common/string_conversion.h \ + src/common/linux/elf_core_dump.cc \ src/common/linux/elfutils.cc \ + src/common/linux/elfutils.h \ src/common/linux/file_id.cc \ + src/common/linux/file_id.h \ src/common/linux/guid_creator.cc \ + src/common/linux/guid_creator.h \ src/common/linux/linux_libc_support.cc \ src/common/linux/memory_mapped_file.cc \ src/common/linux/safe_readlink.cc if ANDROID_HOST src_client_linux_libbreakpad_client_a_SOURCES += \ src/common/android/breakpad_getcontext.S endif endif LINUX_HOST @@ -265,18 +312,16 @@ src_libbreakpad_a_SOURCES = \ src/processor/static_map.h \ src/processor/static_range_map-inl.h \ src/processor/static_range_map.h \ src/processor/symbolic_constants_win.cc \ src/processor/symbolic_constants_win.h \ src/processor/tokenize.cc \ src/processor/tokenize.h -src_libbreakpad_a_LIBADD = src/third_party/libdisasm/libdisasm.a - src_third_party_libdisasm_libdisasm_a_SOURCES = \ src/third_party/libdisasm/ia32_implicit.c \ src/third_party/libdisasm/ia32_implicit.h \ src/third_party/libdisasm/ia32_insn.c \ src/third_party/libdisasm/ia32_insn.h \ src/third_party/libdisasm/ia32_invariant.c \ src/third_party/libdisasm/ia32_invariant.h \ src/third_party/libdisasm/ia32_modrm.c \ @@ -303,27 +348,32 @@ src_third_party_libdisasm_libdisasm_a_SO ## Programs bin_PROGRAMS += \ src/processor/microdump_stackwalk \ src/processor/minidump_dump \ src/processor/minidump_stackwalk endif !DISABLE_PROCESSOR if LINUX_HOST -bin_PROGRAMS += \ +EXTRA_PROGRAMS += \ + src/client/linux/linux_dumper_unittest_helper +CLEANFILES += \ src/client/linux/linux_dumper_unittest_helper if !DISABLE_TOOLS bin_PROGRAMS += \ src/tools/linux/core2md/core2md \ src/tools/linux