diff options
Diffstat (limited to 'dom')
84 files changed, 861 insertions, 374 deletions
diff --git a/dom/base/DOMIntersectionObserver.cpp b/dom/base/DOMIntersectionObserver.cpp index 169c3fe7a..e39abf1a6 100644 --- a/dom/base/DOMIntersectionObserver.cpp +++ b/dom/base/DOMIntersectionObserver.cpp @@ -47,6 +47,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMIntersectionObserver) NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback) NS_IMPL_CYCLE_COLLECTION_UNLINK(mRoot) NS_IMPL_CYCLE_COLLECTION_UNLINK(mQueuedEntries) + tmp->Disconnect(); NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMIntersectionObserver) @@ -184,9 +185,10 @@ DOMIntersectionObserver::Connect() if (mConnected) { return; } + mConnected = true; + nsIDocument* document = mOwner->GetExtantDoc(); document->AddIntersectionObserver(this); - mConnected = true; } void @@ -202,7 +204,9 @@ DOMIntersectionObserver::Disconnect() mObservationTargets.Clear(); if (mOwner) { nsIDocument* document = mOwner->GetExtantDoc(); - document->RemoveIntersectionObserver(this); + if (document) { + document->RemoveIntersectionObserver(this); + } } mConnected = false; } @@ -248,6 +252,12 @@ EdgeInclusiveIntersection(const nsRect& aRect, const nsRect& aOtherRect) return Some(nsRect(left, top, right - left, bottom - top)); } +enum class BrowsingContextInfo { + SimilarOriginBrowsingContext, + DifferentOriginBrowsingContext, + UnknownBrowsingContext +}; + void DOMIntersectionObserver::Update(nsIDocument* aDocument, DOMHighResTimeStamp time) { @@ -359,11 +369,22 @@ DOMIntersectionObserver::Update(nsIDocument* aDocument, DOMHighResTimeStamp time } } - nsRect rootIntersectionRect = rootRect; - bool isInSimilarOriginBrowsingContext = rootFrame && targetFrame && - CheckSimilarOrigin(root, target); + nsRect rootIntersectionRect; + BrowsingContextInfo isInSimilarOriginBrowsingContext = + BrowsingContextInfo::UnknownBrowsingContext; + + if (rootFrame && targetFrame) { + rootIntersectionRect = rootRect; + } + + if (root && target) { + isInSimilarOriginBrowsingContext = CheckSimilarOrigin(root, target) ? + BrowsingContextInfo::SimilarOriginBrowsingContext : + BrowsingContextInfo::DifferentOriginBrowsingContext; + } - if (isInSimilarOriginBrowsingContext) { + if (isInSimilarOriginBrowsingContext == + BrowsingContextInfo::SimilarOriginBrowsingContext) { rootIntersectionRect.Inflate(rootMargin); } @@ -413,7 +434,9 @@ DOMIntersectionObserver::Update(nsIDocument* aDocument, DOMHighResTimeStamp time if (target->UpdateIntersectionObservation(this, threshold)) { QueueIntersectionObserverEntry( target, time, - isInSimilarOriginBrowsingContext ? Some(rootIntersectionRect) : Nothing(), + isInSimilarOriginBrowsingContext == + BrowsingContextInfo::DifferentOriginBrowsingContext ? + Nothing() : Some(rootIntersectionRect), targetRect, intersectionRect, intersectionRatio ); } diff --git a/dom/base/DOMIntersectionObserver.h b/dom/base/DOMIntersectionObserver.h index 3eb10ad38..8144fc5c5 100644 --- a/dom/base/DOMIntersectionObserver.h +++ b/dom/base/DOMIntersectionObserver.h @@ -101,9 +101,7 @@ protected: class DOMIntersectionObserver final : public nsISupports, public nsWrapperCache { - virtual ~DOMIntersectionObserver() { - Disconnect(); - } + virtual ~DOMIntersectionObserver() { } public: DOMIntersectionObserver(already_AddRefed<nsPIDOMWindowInner>&& aOwner, diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp index 092755590..79b36a314 100644 --- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -1230,6 +1230,42 @@ Element::GetAttribute(const nsAString& aName, DOMString& aReturn) } } +bool +Element::ToggleAttribute(const nsAString& aName, + const Optional<bool>& aForce, + ErrorResult& aError) +{ + aError = nsContentUtils::CheckQName(aName, false); + if (aError.Failed()) { + return false; + } + + nsAutoString nameToUse; + const nsAttrName* name = InternalGetAttrNameFromQName(aName, &nameToUse); + if (!name) { + if (aForce.WasPassed() && !aForce.Value()) { + return false; + } + nsCOMPtr<nsIAtom> nameAtom = NS_Atomize(nameToUse); + if (!nameAtom) { + aError.Throw(NS_ERROR_OUT_OF_MEMORY); + return false; + } + aError = SetAttr(kNameSpaceID_None, nameAtom, EmptyString(), true); + return true; + } + if (aForce.WasPassed() && aForce.Value()) { + return true; + } + // Hold a strong reference here so that the atom or nodeinfo doesn't go + // away during UnsetAttr. If it did UnsetAttr would be left with a + // dangling pointer as argument without knowing it. + nsAttrName tmp(*name); + + aError = UnsetAttr(name->NamespaceID(), name->LocalName(), true); + return false; +} + void Element::SetAttribute(const nsAString& aName, const nsAString& aValue, @@ -3912,44 +3948,55 @@ Element::ClearDataset() slots->mDataset = nullptr; } -nsTArray<Element::nsDOMSlots::IntersectionObserverRegistration>* +nsDataHashtable<nsPtrHashKey<DOMIntersectionObserver>, int32_t>* Element::RegisteredIntersectionObservers() { nsDOMSlots* slots = DOMSlots(); return &slots->mRegisteredIntersectionObservers; } +enum nsPreviousIntersectionThreshold { + eUninitialized = -2, + eNonIntersecting = -1 +}; + void Element::RegisterIntersectionObserver(DOMIntersectionObserver* aObserver) { - RegisteredIntersectionObservers()->AppendElement( - nsDOMSlots::IntersectionObserverRegistration { aObserver, -1 }); + nsDataHashtable<nsPtrHashKey<DOMIntersectionObserver>, int32_t>* observers = + RegisteredIntersectionObservers(); + if (observers->Contains(aObserver)) { + return; + } + + // Value can be: + // -2: Makes sure next calculated threshold always differs, leading to a + // notification task being scheduled. + // -1: Non-intersecting. + // >= 0: Intersecting, valid index of aObserver->mThresholds. + RegisteredIntersectionObservers()->Put(aObserver, eUninitialized); } void Element::UnregisterIntersectionObserver(DOMIntersectionObserver* aObserver) { - nsTArray<nsDOMSlots::IntersectionObserverRegistration>* observers = + nsDataHashtable<nsPtrHashKey<DOMIntersectionObserver>, int32_t>* observers = RegisteredIntersectionObservers(); - for (uint32_t i = 0; i < observers->Length(); ++i) { - nsDOMSlots::IntersectionObserverRegistration reg = observers->ElementAt(i); - if (reg.observer == aObserver) { - observers->RemoveElementAt(i); - break; - } - } + observers->Remove(aObserver); } bool Element::UpdateIntersectionObservation(DOMIntersectionObserver* aObserver, int32_t aThreshold) { - nsTArray<nsDOMSlots::IntersectionObserverRegistration>* observers = + nsDataHashtable<nsPtrHashKey<DOMIntersectionObserver>, int32_t>* observers = RegisteredIntersectionObservers(); - for (auto& reg : *observers) { - if (reg.observer == aObserver && reg.previousThreshold != aThreshold) { - reg.previousThreshold = aThreshold; - return true; - } + if (!observers->Contains(aObserver)) { + return false; + } + int32_t previousThreshold = observers->Get(aObserver); + if (previousThreshold != aThreshold) { + observers->Put(aObserver, aThreshold); + return true; } return false; } diff --git a/dom/base/Element.h b/dom/base/Element.h index cf1d197e2..c269ab14a 100644 --- a/dom/base/Element.h +++ b/dom/base/Element.h @@ -686,6 +686,8 @@ public: void GetAttributeNS(const nsAString& aNamespaceURI, const nsAString& aLocalName, nsAString& aReturn); + bool ToggleAttribute(const nsAString& aName, const Optional<bool>& aForce, + ErrorResult& aError); void SetAttribute(const nsAString& aName, const nsAString& aValue, ErrorResult& aError); void SetAttributeNS(const nsAString& aNamespaceURI, @@ -1380,7 +1382,7 @@ protected: nsDOMTokenList* GetTokenList(nsIAtom* aAtom, const DOMTokenListSupportedTokenArray aSupportedTokens = nullptr); - nsTArray<nsDOMSlots::IntersectionObserverRegistration>* RegisteredIntersectionObservers(); + nsDataHashtable<nsPtrHashKey<DOMIntersectionObserver>, int32_t>* RegisteredIntersectionObservers(); private: /** diff --git a/dom/base/File.cpp b/dom/base/File.cpp index 8602a3064..7d86dfe8a 100755 --- a/dom/base/File.cpp +++ b/dom/base/File.cpp @@ -912,7 +912,7 @@ BlobImplFile::GetType(nsAString& aType) new GetTypeRunnable(workerPrivate, this); ErrorResult rv; - runnable->Dispatch(rv); + runnable->Dispatch(Terminating, rv); if (NS_WARN_IF(rv.Failed())) { rv.SuppressException(); } diff --git a/dom/base/FragmentOrElement.cpp b/dom/base/FragmentOrElement.cpp index b22a0d4ff..fde983e7c 100644 --- a/dom/base/FragmentOrElement.cpp +++ b/dom/base/FragmentOrElement.cpp @@ -608,6 +608,7 @@ FragmentOrElement::nsDOMSlots::Unlink(bool aIsXUL) mLabelsList = nullptr; mCustomElementData = nullptr; mClassList = nullptr; + mRegisteredIntersectionObservers.Clear(); } size_t @@ -1359,6 +1360,13 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FragmentOrElement) { nsDOMSlots *slots = tmp->GetExistingDOMSlots(); if (slots) { + if (tmp->IsElement()) { + Element* elem = tmp->AsElement(); + for (auto iter = slots->mRegisteredIntersectionObservers.Iter(); !iter.Done(); iter.Next()) { + DOMIntersectionObserver* observer = iter.Key(); + observer->UnlinkTarget(*elem); + } + } slots->Unlink(tmp->IsXULElement()); } } diff --git a/dom/base/FragmentOrElement.h b/dom/base/FragmentOrElement.h index 1cd8033bb..7c74e9cd4 100644 --- a/dom/base/FragmentOrElement.h +++ b/dom/base/FragmentOrElement.h @@ -21,6 +21,7 @@ #include "nsIWeakReference.h" // base class #include "nsNodeUtils.h" // class member nsNodeUtils::CloneNodeImpl #include "nsIHTMLCollection.h" +#include "nsDataHashtable.h" class ContentUnbinder; class nsContentList; @@ -353,12 +354,7 @@ public: /** * Registered Intersection Observers on the element. */ - struct IntersectionObserverRegistration { - DOMIntersectionObserver* observer; - int32_t previousThreshold; - }; - - nsTArray<IntersectionObserverRegistration> mRegisteredIntersectionObservers; + nsDataHashtable<nsPtrHashKey<DOMIntersectionObserver>, int32_t> mRegisteredIntersectionObservers; }; protected: diff --git a/dom/base/Location.cpp b/dom/base/Location.cpp index b6b95aaa6..7b3722f09 100644 --- a/dom/base/Location.cpp +++ b/dom/base/Location.cpp @@ -393,6 +393,10 @@ Location::GetHost(nsAString& aHost) NS_IMETHODIMP Location::SetHost(const nsAString& aHost) { + if (aHost.IsEmpty()) { + return NS_OK; // Ignore empty string + } + nsCOMPtr<nsIURI> uri; nsresult rv = GetWritableURI(getter_AddRefs(uri)); if (NS_WARN_IF(NS_FAILED(rv) || !uri)) { @@ -424,6 +428,10 @@ Location::GetHostname(nsAString& aHostname) NS_IMETHODIMP Location::SetHostname(const nsAString& aHostname) { + if (aHostname.IsEmpty()) { + return NS_OK; // Ignore empty string + } + nsCOMPtr<nsIURI> uri; nsresult rv = GetWritableURI(getter_AddRefs(uri)); if (NS_WARN_IF(NS_FAILED(rv) || !uri)) { @@ -507,6 +515,10 @@ nsresult Location::SetHrefWithBase(const nsAString& aHref, nsIURI* aBase, bool aReplace) { + if (aHref.IsEmpty()) { + return NS_OK; // Ignore empty string + } + nsresult result; nsCOMPtr<nsIURI> newUri; @@ -694,6 +706,10 @@ Location::GetProtocol(nsAString& aProtocol) NS_IMETHODIMP Location::SetProtocol(const nsAString& aProtocol) { + if (aProtocol.IsEmpty()) { + return NS_OK; // Ignore empty string + } + nsCOMPtr<nsIURI> uri; nsresult rv = GetWritableURI(getter_AddRefs(uri)); if (NS_WARN_IF(NS_FAILED(rv) || !uri)) { @@ -747,6 +763,10 @@ Location::GetSearch(nsAString& aSearch) NS_IMETHODIMP Location::SetSearch(const nsAString& aSearch) { + if (aSearch.IsEmpty()) { + return NS_OK; // Ignore empty string + } + nsresult rv = SetSearchInternal(aSearch); if (NS_FAILED(rv)) { return rv; diff --git a/dom/base/StructuredCloneHolder.cpp b/dom/base/StructuredCloneHolder.cpp index 1c27c632e..8e7cee340 100644 --- a/dom/base/StructuredCloneHolder.cpp +++ b/dom/base/StructuredCloneHolder.cpp @@ -1075,9 +1075,9 @@ StructuredCloneHolder::CustomReadHandler(JSContext* aCx, return ReadFormData(aCx, aReader, aIndex, this); } - if (aTag == SCTAG_DOM_IMAGEBITMAP) { - MOZ_ASSERT(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread || - mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread); + if (aTag == SCTAG_DOM_IMAGEBITMAP && + (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread || + mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread)) { // Get the current global object. // This can be null. @@ -1087,7 +1087,9 @@ StructuredCloneHolder::CustomReadHandler(JSContext* aCx, parent, GetSurfaces(), aIndex); } - if (aTag == SCTAG_DOM_WASM) { + if (aTag == SCTAG_DOM_WASM && + (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread || + mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread)) { return ReadWasmModule(aCx, aIndex, this); } @@ -1202,9 +1204,9 @@ StructuredCloneHolder::CustomReadTransferHandler(JSContext* aCx, return true; } - if (aTag == SCTAG_DOM_CANVAS) { - MOZ_ASSERT(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread || - mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread); + if (aTag == SCTAG_DOM_CANVAS && + (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread || + mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread)) { MOZ_ASSERT(aContent); OffscreenCanvasCloneData* data = static_cast<OffscreenCanvasCloneData*>(aContent); @@ -1222,9 +1224,9 @@ StructuredCloneHolder::CustomReadTransferHandler(JSContext* aCx, return true; } - if (aTag == SCTAG_DOM_IMAGEBITMAP) { - MOZ_ASSERT(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread || - mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread); + if (aTag == SCTAG_DOM_IMAGEBITMAP && + (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread || + mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread)) { MOZ_ASSERT(aContent); ImageBitmapCloneData* data = static_cast<ImageBitmapCloneData*>(aContent); @@ -1328,9 +1330,9 @@ StructuredCloneHolder::CustomFreeTransferHandler(uint32_t aTag, return; } - if (aTag == SCTAG_DOM_CANVAS) { - MOZ_ASSERT(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread || - mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread); + if (aTag == SCTAG_DOM_CANVAS && + (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread || + mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread)) { MOZ_ASSERT(aContent); OffscreenCanvasCloneData* data = static_cast<OffscreenCanvasCloneData*>(aContent); @@ -1338,9 +1340,9 @@ StructuredCloneHolder::CustomFreeTransferHandler(uint32_t aTag, return; } - if (aTag == SCTAG_DOM_IMAGEBITMAP) { - MOZ_ASSERT(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread || - mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread); + if (aTag == SCTAG_DOM_IMAGEBITMAP && + (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread || + mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread)) { MOZ_ASSERT(aContent); ImageBitmapCloneData* data = static_cast<ImageBitmapCloneData*>(aContent); diff --git a/dom/base/WebSocket.cpp b/dom/base/WebSocket.cpp index d85bae82b..af4b7858b 100644 --- a/dom/base/WebSocket.cpp +++ b/dom/base/WebSocket.cpp @@ -340,7 +340,7 @@ WebSocketImpl::PrintErrorOnConsole(const char *aBundleURI, new PrintErrorOnConsoleRunnable(this, aBundleURI, aError, aFormatStrings, aFormatStringsLen); ErrorResult rv; - runnable->Dispatch(rv); + runnable->Dispatch(Killing, rv); // XXXbz this seems totally broken. We should be propagating this out, but // none of our callers really propagate anything usefully. Come to think of // it, why is this a syncrunnable anyway? Can't this be a fire-and-forget @@ -629,7 +629,7 @@ WebSocketImpl::Disconnect() RefPtr<DisconnectInternalRunnable> runnable = new DisconnectInternalRunnable(this); ErrorResult rv; - runnable->Dispatch(rv); + runnable->Dispatch(Killing, rv); // XXXbz this seems totally broken. We should be propagating this out, but // where to, exactly? rv.SuppressException(); @@ -1293,7 +1293,7 @@ WebSocket::ConstructorCommon(const GlobalObject& aGlobal, new InitRunnable(webSocketImpl, !!aTransportProvider, aUrl, protocolArray, nsDependentCString(file.get()), lineno, column, aRv, &connectionFailed); - runnable->Dispatch(aRv); + runnable->Dispatch(Terminating, aRv); } if (NS_WARN_IF(aRv.Failed())) { @@ -1377,7 +1377,7 @@ WebSocket::ConstructorCommon(const GlobalObject& aGlobal, "not yet implemented"); RefPtr<AsyncOpenRunnable> runnable = new AsyncOpenRunnable(webSocket->mImpl, aRv); - runnable->Dispatch(aRv); + runnable->Dispatch(Terminating, aRv); } if (NS_WARN_IF(aRv.Failed())) { diff --git a/dom/base/nsContentPolicyUtils.h b/dom/base/nsContentPolicyUtils.h index ed0544226..600b24c56 100644 --- a/dom/base/nsContentPolicyUtils.h +++ b/dom/base/nsContentPolicyUtils.h @@ -134,6 +134,7 @@ NS_CP_ContentTypeName(uint32_t contentType) CASE_RETURN( TYPE_INTERNAL_IMAGE_FAVICON ); CASE_RETURN( TYPE_INTERNAL_STYLESHEET ); CASE_RETURN( TYPE_INTERNAL_STYLESHEET_PRELOAD ); + CASE_RETURN( TYPE_SAVEAS_DOWNLOAD ); default: return "<Unknown Type>"; } diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index fd3b52948..8acfd901a 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -12507,7 +12507,8 @@ nsDocument::ScheduleIntersectionObserverNotification() void nsDocument::NotifyIntersectionObservers() { - for (const auto& observer : mIntersectionObservers) { + nsTArray<RefPtr<DOMIntersectionObserver>> observers(mIntersectionObservers); + for (const auto& observer : observers) { observer->Notify(); } } diff --git a/dom/base/nsFrameMessageManager.cpp b/dom/base/nsFrameMessageManager.cpp index 049bc0a1a..6fffd376b 100644 --- a/dom/base/nsFrameMessageManager.cpp +++ b/dom/base/nsFrameMessageManager.cpp @@ -271,10 +271,10 @@ BuildClonedMessageData(typename BlobTraits<Flavor>::ConcreteContentManagerType* ClonedMessageData& aClonedData) { SerializedStructuredCloneBuffer& buffer = aClonedData.data(); - auto iter = aData.Data().Iter(); + auto iter = aData.Data().Start(); size_t size = aData.Data().Size(); bool success; - buffer.data = aData.Data().Borrow<js::SystemAllocPolicy>(iter, size, &success); + buffer.data = aData.Data().Borrow(iter, size, &success); if (NS_WARN_IF(!success)) { return false; } @@ -1286,6 +1286,7 @@ nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget, if (aRetVal) { ErrorResult rv; StructuredCloneData* data = aRetVal->AppendElement(); + data->InitScope(JS::StructuredCloneScope::DifferentProcess); data->Write(cx, rval, rv); if (NS_WARN_IF(rv.Failed())) { aRetVal->RemoveElementAt(aRetVal->Length() - 1); diff --git a/dom/base/nsGkAtomList.h b/dom/base/nsGkAtomList.h index 50b4449ec..8fefa0e02 100644 --- a/dom/base/nsGkAtomList.h +++ b/dom/base/nsGkAtomList.h @@ -2240,6 +2240,7 @@ GK_ATOM(windows_accent_color_applies, "windows-accent-color-applies") GK_ATOM(windows_accent_color_is_dark, "windows-accent-color-is-dark") GK_ATOM(windows_default_theme, "windows-default-theme") GK_ATOM(mac_graphite_theme, "mac-graphite-theme") +GK_ATOM(mac_lion_theme, "mac-lion-theme") GK_ATOM(mac_yosemite_theme, "mac-yosemite-theme") GK_ATOM(windows_compositor, "windows-compositor") GK_ATOM(windows_glass, "windows-glass") @@ -2271,6 +2272,7 @@ GK_ATOM(_moz_windows_accent_color_applies, "-moz-windows-accent-color-applies") GK_ATOM(_moz_windows_accent_color_is_dark, "-moz-windows-accent-color-is-dark") GK_ATOM(_moz_windows_default_theme, "-moz-windows-default-theme") GK_ATOM(_moz_mac_graphite_theme, "-moz-mac-graphite-theme") +GK_ATOM(_moz_mac_lion_theme, "-moz-mac-lion-theme") GK_ATOM(_moz_mac_yosemite_theme, "-moz-mac-yosemite-theme") GK_ATOM(_moz_windows_compositor, "-moz-windows-compositor") GK_ATOM(_moz_windows_classic, "-moz-windows-classic") diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 4e384c4d2..884ad69ca 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -14633,7 +14633,9 @@ nsGlobalWindow::NotifyDefaultButtonLoaded(Element& aDefaultButton, return; } LayoutDeviceIntRect buttonRect = - LayoutDeviceIntRect::FromUnknownRect(frame->GetScreenRect()); + LayoutDeviceIntRect::FromAppUnitsToNearest( + frame->GetScreenRectInAppUnits(), + frame->PresContext()->AppUnitsPerDevPixel()); // Get the widget rect in screen coordinates. nsIWidget *widget = GetNearestWidget(); diff --git a/dom/base/nsIContentPolicy.idl b/dom/base/nsIContentPolicy.idl index a73565a9a..200b97fbc 100644 --- a/dom/base/nsIContentPolicy.idl +++ b/dom/base/nsIContentPolicy.idl @@ -20,7 +20,7 @@ interface nsIPrincipal; * by launching a dialog to prompt the user for something). */ -[scriptable,uuid(caad4f1f-d047-46ac-ae9d-dc598e4fb91b)] +[scriptable,uuid(64a5ae16-6836-475c-9938-4b6cc1eee8fb)] interface nsIContentPolicy : nsIContentPolicyBase { /** diff --git a/dom/base/nsIContentPolicyBase.idl b/dom/base/nsIContentPolicyBase.idl index 884e3d96d..908e562a8 100644 --- a/dom/base/nsIContentPolicyBase.idl +++ b/dom/base/nsIContentPolicyBase.idl @@ -24,7 +24,7 @@ typedef unsigned long nsContentPolicyType; * by launching a dialog to prompt the user for something). */ -[scriptable,uuid(17418187-d86f-48dd-92d1-238838df0a4e)] +[scriptable,uuid(d6ab1d11-8e24-4db4-8582-c40a78281737)] interface nsIContentPolicyBase : nsISupports { /** @@ -329,11 +329,17 @@ interface nsIContentPolicyBase : nsISupports */ const nsContentPolicyType TYPE_INTERNAL_IMAGE_FAVICON = 41; + /** + * Indicates an save-as link download from the front-end code. + */ + const nsContentPolicyType TYPE_SAVEAS_DOWNLOAD = 42; + /* When adding new content types, please update nsContentBlocker, - * NS_CP_ContentTypeName, nsCSPContext, all nsIContentPolicy - * implementations, the static_assert in dom/cache/DBSchema.cpp, - * and other things that are not listed here that are related to - * nsIContentPolicy. */ + * NS_CP_ContentTypeName, nsCSPContext, CSP_ContentTypeToDirective, + * DoContentSecurityChecks, all nsIContentPolicy implementations, the + * static_assert in dom/cache/DBSchema.cpp, nsPermissionManager.cpp, + * and other things that are not listed here that are related + * to nsIContentPolicy. */ ////////////////////////////////////////////////////////////////////// diff --git a/dom/base/nsINode.cpp b/dom/base/nsINode.cpp index 715ca93ea..09e848710 100644 --- a/dom/base/nsINode.cpp +++ b/dom/base/nsINode.cpp @@ -1777,8 +1777,8 @@ nsINode::Before(const Sequence<OwningNodeOrString>& aNodes, nsCOMPtr<nsINode> viablePreviousSibling = FindViablePreviousSibling(*this, aNodes); - nsCOMPtr<nsINode> node = - ConvertNodesOrStringsIntoNode(aNodes, OwnerDoc(), aRv); + nsCOMPtr<nsIDocument> doc = OwnerDoc(); + nsCOMPtr<nsINode> node = ConvertNodesOrStringsIntoNode(aNodes, doc, aRv); if (aRv.Failed()) { return; } @@ -1800,8 +1800,8 @@ nsINode::After(const Sequence<OwningNodeOrString>& aNodes, nsCOMPtr<nsINode> viableNextSibling = FindViableNextSibling(*this, aNodes); - nsCOMPtr<nsINode> node = - ConvertNodesOrStringsIntoNode(aNodes, OwnerDoc(), aRv); + nsCOMPtr<nsIDocument> doc = OwnerDoc(); + nsCOMPtr<nsINode> node = ConvertNodesOrStringsIntoNode(aNodes, doc, aRv); if (aRv.Failed()) { return; } @@ -1820,8 +1820,8 @@ nsINode::ReplaceWith(const Sequence<OwningNodeOrString>& aNodes, nsCOMPtr<nsINode> viableNextSibling = FindViableNextSibling(*this, aNodes); - nsCOMPtr<nsINode> node = - ConvertNodesOrStringsIntoNode(aNodes, OwnerDoc(), aRv); + nsCOMPtr<nsIDocument> doc = OwnerDoc(); + nsCOMPtr<nsINode> node = ConvertNodesOrStringsIntoNode(aNodes, doc, aRv); if (aRv.Failed()) { return; } @@ -1880,8 +1880,8 @@ void nsINode::Prepend(const Sequence<OwningNodeOrString>& aNodes, ErrorResult& aRv) { - nsCOMPtr<nsINode> node = - ConvertNodesOrStringsIntoNode(aNodes, OwnerDoc(), aRv); + nsCOMPtr<nsIDocument> doc = OwnerDoc(); + nsCOMPtr<nsINode> node = ConvertNodesOrStringsIntoNode(aNodes, doc, aRv); if (aRv.Failed()) { return; } @@ -1894,8 +1894,8 @@ void nsINode::Append(const Sequence<OwningNodeOrString>& aNodes, ErrorResult& aRv) { - nsCOMPtr<nsINode> node = - ConvertNodesOrStringsIntoNode(aNodes, OwnerDoc(), aRv); + nsCOMPtr<nsIDocument> doc = OwnerDoc(); + nsCOMPtr<nsINode> node = ConvertNodesOrStringsIntoNode(aNodes, doc, aRv); if (aRv.Failed()) { return; } diff --git a/dom/base/nsISimpleContentPolicy.idl b/dom/base/nsISimpleContentPolicy.idl index 493aee1a5..dc0474736 100644 --- a/dom/base/nsISimpleContentPolicy.idl +++ b/dom/base/nsISimpleContentPolicy.idl @@ -28,7 +28,7 @@ interface nsIDOMElement; * by launching a dialog to prompt the user for something). */ -[scriptable,uuid(b9df71e3-a9b3-4706-b2d5-e6c0d3d68ec7)] +[scriptable,uuid(1553a476-8a14-410b-8ecc-47f48e937392)] interface nsISimpleContentPolicy : nsIContentPolicyBase { /** diff --git a/dom/base/nsImageLoadingContent.cpp b/dom/base/nsImageLoadingContent.cpp index d25dd6319..0c6c37b44 100644 --- a/dom/base/nsImageLoadingContent.cpp +++ b/dom/base/nsImageLoadingContent.cpp @@ -94,8 +94,7 @@ nsImageLoadingContent::nsImageLoadingContent() mNewRequestsWillNeedAnimationReset(false), mStateChangerDepth(0), mCurrentRequestRegistered(false), - mPendingRequestRegistered(false), - mFrameCreateCalled(false) + mPendingRequestRegistered(false) { if (!nsContentUtils::GetImgLoaderForChannel(nullptr, nullptr)) { mLoadingEnabled = false; @@ -489,10 +488,8 @@ nsImageLoadingContent::FrameCreated(nsIFrame* aFrame) { NS_ASSERTION(aFrame, "aFrame is null"); - mFrameCreateCalled = true; - - TrackImage(mCurrentRequest); - TrackImage(mPendingRequest); + TrackImage(mCurrentRequest, aFrame); + TrackImage(mPendingRequest, aFrame); // We need to make sure that our image request is registered, if it should // be registered. @@ -513,8 +510,6 @@ nsImageLoadingContent::FrameDestroyed(nsIFrame* aFrame) { NS_ASSERTION(aFrame, "aFrame is null"); - mFrameCreateCalled = false; - // We need to make sure that our image request is deregistered. nsPresContext* presContext = GetFramePresContext(); if (mCurrentRequest) { @@ -1486,7 +1481,8 @@ nsImageLoadingContent::OnVisibilityChange(Visibility aNewVisibility, } void -nsImageLoadingContent::TrackImage(imgIRequest* aImage) +nsImageLoadingContent::TrackImage(imgIRequest* aImage, + nsIFrame* aFrame /*= nullptr */) { if (!aImage) return; @@ -1499,13 +1495,21 @@ nsImageLoadingContent::TrackImage(imgIRequest* aImage) return; } - // We only want to track this request if we're visible. Ordinarily we check - // the visible count, but that requires a frame; in cases where - // GetOurPrimaryFrame() cannot obtain a frame (e.g. <feImage>), we assume - // we're visible if FrameCreated() was called. - nsIFrame* frame = GetOurPrimaryFrame(); - if ((frame && frame->GetVisibility() == Visibility::APPROXIMATELY_NONVISIBLE) || - (!frame && !mFrameCreateCalled)) { + if (!aFrame) { + aFrame = GetOurPrimaryFrame(); + } + + /* This line is deceptively simple. It hides a lot of subtlety. Before we + * create an nsImageFrame we call nsImageFrame::ShouldCreateImageFrameFor + * to determine if we should create an nsImageFrame or create a frame based + * on the display of the element (ie inline, block, etc). Inline, block, etc + * frames don't register for visibility tracking so they will return UNTRACKED + * from GetVisibility(). So this line is choosing to mark such images as + * visible. Once the image loads we will get an nsImageFrame and the proper + * visibility. This is a pitfall of tracking the visibility on the frames + * instead of the content node. + */ + if (!aFrame || aFrame->GetVisibility() == Visibility::APPROXIMATELY_NONVISIBLE) { return; } diff --git a/dom/base/nsImageLoadingContent.h b/dom/base/nsImageLoadingContent.h index 85db2bd2c..5f7daff72 100644 --- a/dom/base/nsImageLoadingContent.h +++ b/dom/base/nsImageLoadingContent.h @@ -364,6 +364,11 @@ protected: * * No-op if aImage is null. * + * @param aFrame If called from FrameCreated the frame passed to FrameCreated. + * This is our frame, but at the time of the FrameCreated call + * our primary frame pointer hasn't been set yet, so this is + * only way to get our frame. + * * @param aNonvisibleAction A requested action if the frame has become * nonvisible. If Nothing(), no action is * requested. If DISCARD_IMAGES is specified, the @@ -371,7 +376,7 @@ protected: * associated with to discard their surfaces if * possible. */ - void TrackImage(imgIRequest* aImage); + void TrackImage(imgIRequest* aImage, nsIFrame* aFrame = nullptr); void UntrackImage(imgIRequest* aImage, const Maybe<OnNonvisible>& aNonvisibleAction = Nothing()); @@ -454,9 +459,6 @@ private: // registered with the refresh driver. bool mCurrentRequestRegistered; bool mPendingRequestRegistered; - - // True when FrameCreate has been called but FrameDestroy has not. - bool mFrameCreateCalled; }; #endif // nsImageLoadingContent_h__ diff --git a/dom/base/nsNodeUtils.cpp b/dom/base/nsNodeUtils.cpp index 69a9414fe..ecea95dc1 100644 --- a/dom/base/nsNodeUtils.cpp +++ b/dom/base/nsNodeUtils.cpp @@ -297,15 +297,6 @@ nsNodeUtils::LastRelease(nsINode* aNode) NodeWillBeDestroyed, (aNode)); } - if (aNode->IsElement()) { - Element* elem = aNode->AsElement(); - FragmentOrElement::nsDOMSlots* domSlots = - static_cast<FragmentOrElement::nsDOMSlots*>(slots); - for (auto& reg : domSlots->mRegisteredIntersectionObservers) { - reg.observer->UnlinkTarget(*elem); - } - } - delete slots; aNode->mSlots = nullptr; } diff --git a/dom/base/nsStructuredCloneContainer.cpp b/dom/base/nsStructuredCloneContainer.cpp index 8c2cdc091..ea2d38bc8 100644 --- a/dom/base/nsStructuredCloneContainer.cpp +++ b/dom/base/nsStructuredCloneContainer.cpp @@ -137,7 +137,7 @@ nsStructuredCloneContainer::GetDataAsBase64(nsAString &aOut) return NS_ERROR_FAILURE; } - auto iter = Data().Iter(); + auto iter = Data().Start(); size_t size = Data().Size(); nsAutoCString binaryData; binaryData.SetLength(size); diff --git a/dom/base/test/test_intersectionobservers.html b/dom/base/test/test_intersectionobservers.html index e7875e3af..10e3d7712 100644 --- a/dom/base/test/test_intersectionobservers.html +++ b/dom/base/test/test_intersectionobservers.html @@ -325,7 +325,7 @@ limitations under the License. }); - it('does not trigger if target does not intersect when observing begins', + it('does trigger if target does not intersect when observing begins', function(done) { var spy = sinon.spy(); @@ -334,7 +334,7 @@ limitations under the License. targetEl2.style.top = '-40px'; io.observe(targetEl2); callDelayed(function() { - expect(spy.callCount).to.be(0); + expect(spy.callCount).to.be(1); done(); }, ASYNC_TIMEOUT); }); @@ -528,7 +528,7 @@ limitations under the License. spy.waitForNotification(function() { expect(spy.callCount).to.be(1); var records = sortRecords(spy.lastCall.args[0]); - expect(records.length).to.be(2); + expect(records.length).to.be(3); expect(records[0].target).to.be(targetEl1); expect(records[0].intersectionRatio).to.be(0.25); expect(records[1].target).to.be(targetEl2); @@ -636,10 +636,10 @@ limitations under the License. expect(records.length).to.be(3); expect(records[0].target).to.be(targetEl1); expect(records[0].intersectionRatio).to.be(0.5); - expect(records[1].target).to.be(targetEl3); - expect(records[1].intersectionRatio).to.be(0.5); - expect(records[2].target).to.be(targetEl4); + expect(records[2].target).to.be(targetEl3); expect(records[2].intersectionRatio).to.be(0.5); + expect(records[3].target).to.be(targetEl4); + expect(records[3].intersectionRatio).to.be(0.5); io.disconnect(); done(); }, {root: rootEl, rootMargin: '-10px 10%'}); @@ -652,11 +652,11 @@ limitations under the License. function(done) { io = new IntersectionObserver(function(records) { records = sortRecords(records); - expect(records.length).to.be(2); + expect(records.length).to.be(4); expect(records[0].target).to.be(targetEl1); expect(records[0].intersectionRatio).to.be(0.5); - expect(records[1].target).to.be(targetEl4); - expect(records[1].intersectionRatio).to.be(0.5); + expect(records[3].target).to.be(targetEl4); + expect(records[3].intersectionRatio).to.be(0.5); io.disconnect(); done(); }, {root: rootEl, rootMargin: '-5% -2.5% 0px'}); @@ -669,13 +669,13 @@ limitations under the License. function(done) { io = new IntersectionObserver(function(records) { records = sortRecords(records); - expect(records.length).to.be(3); + expect(records.length).to.be(4); expect(records[0].target).to.be(targetEl1); expect(records[0].intersectionRatio).to.be(0.5); expect(records[1].target).to.be(targetEl2); expect(records[1].intersectionRatio).to.be(0.5); - expect(records[2].target).to.be(targetEl4); - expect(records[2].intersectionRatio).to.be(0.25); + expect(records[3].target).to.be(targetEl4); + expect(records[3].intersectionRatio).to.be(0.25); io.disconnect(); done(); }, {root: rootEl, rootMargin: '5% -2.5% -10px -190px'}); @@ -705,9 +705,9 @@ limitations under the License. spy.waitForNotification(function() { expect(spy.callCount).to.be(1); var records = sortRecords(spy.lastCall.args[0]); - expect(records.length).to.be(1); - expect(records[0].intersectionRatio).to.be(0); - expect(records[0].target).to.be(targetEl2); + expect(records.length).to.be(2); + expect(records[1].intersectionRatio).to.be(0); + expect(records[1].target).to.be(targetEl2); done(); }, ASYNC_TIMEOUT); }, @@ -797,14 +797,14 @@ limitations under the License. function(done) { document.getElementById('fixtures').appendChild(rootEl); callDelayed(function() { - expect(spy.callCount).to.be(0); + expect(spy.callCount).to.be(1); done(); }, ASYNC_TIMEOUT); }, function(done) { parentEl.insertBefore(targetEl1, targetEl2); spy.waitForNotification(function() { - expect(spy.callCount).to.be(1); + expect(spy.callCount).to.be(2); var records = sortRecords(spy.lastCall.args[0]); expect(records.length).to.be(1); expect(records[0].intersectionRatio).to.be(1); @@ -815,7 +815,7 @@ limitations under the License. function(done) { grandParentEl.parentNode.removeChild(grandParentEl); spy.waitForNotification(function() { - expect(spy.callCount).to.be(2); + expect(spy.callCount).to.be(3); var records = sortRecords(spy.lastCall.args[0]); expect(records.length).to.be(1); expect(records[0].intersectionRatio).to.be(0); @@ -826,7 +826,7 @@ limitations under the License. function(done) { rootEl.appendChild(targetEl1); spy.waitForNotification(function() { - expect(spy.callCount).to.be(3); + expect(spy.callCount).to.be(4); var records = sortRecords(spy.lastCall.args[0]); expect(records.length).to.be(1); expect(records[0].intersectionRatio).to.be(1); @@ -837,7 +837,7 @@ limitations under the License. function(done) { rootEl.parentNode.removeChild(rootEl); spy.waitForNotification(function() { - expect(spy.callCount).to.be(4); + expect(spy.callCount).to.be(5); var records = sortRecords(spy.lastCall.args[0]); expect(records.length).to.be(1); expect(records[0].intersectionRatio).to.be(0); @@ -867,8 +867,14 @@ limitations under the License. targetEl1.style.top = '220px'; targetEl1.style.left = '220px'; + + var callCount = 0; io = new IntersectionObserver(function(records) { + callCount++; + if (callCount <= 1) { + return; + } expect(records.length).to.be(1); expect(records[0].intersectionRatio).to.be(1); done(); @@ -891,6 +897,19 @@ limitations under the License. var win = window.open("intersectionobserver_window.html"); }); + it('triggers only once if observed multiple times (and does not crash when collected)', function(done) { + var spy = sinon.spy(); + io = new IntersectionObserver(spy, {root: rootEl}); + io.observe(targetEl1); + io.observe(targetEl1); + io.observe(targetEl1); + + callDelayed(function () { + expect(spy.callCount).to.be(1); + done(); + }, ASYNC_TIMEOUT); + }); + }); describe('observe subframe', function () { diff --git a/dom/bindings/DOMJSProxyHandler.cpp b/dom/bindings/DOMJSProxyHandler.cpp index 65e540bc1..23f0abd88 100644 --- a/dom/bindings/DOMJSProxyHandler.cpp +++ b/dom/bindings/DOMJSProxyHandler.cpp @@ -166,6 +166,10 @@ DOMProxyHandler::EnsureExpandoObject(JSContext* cx, JS::Handle<JSObject*> obj) nsISupports* native = UnwrapDOMObject<nsISupports>(obj); nsWrapperCache* cache; CallQueryInterface(native, &cache); + if (!cache) { + return expando; + } + if (expandoAndGeneration) { cache->PreserveWrapper(native); expandoAndGeneration->expando.setObject(*expando); diff --git a/dom/broadcastchannel/BroadcastChannel.cpp b/dom/broadcastchannel/BroadcastChannel.cpp index c3c2d448b..d154b6562 100644 --- a/dom/broadcastchannel/BroadcastChannel.cpp +++ b/dom/broadcastchannel/BroadcastChannel.cpp @@ -154,8 +154,8 @@ public: bool success; SerializedStructuredCloneBuffer& buffer = message.data(); - auto iter = mData->BufferData().Iter(); - buffer.data = mData->BufferData().Borrow<js::SystemAllocPolicy>(iter, mData->BufferData().Size(), &success); + auto iter = mData->BufferData().Start(); + buffer.data = mData->BufferData().Borrow(iter, mData->BufferData().Size(), &success); if (NS_WARN_IF(!success)) { return NS_OK; } @@ -369,7 +369,7 @@ BroadcastChannel::Constructor(const GlobalObject& aGlobal, RefPtr<InitializeRunnable> runnable = new InitializeRunnable(workerPrivate, origin, principalInfo, aRv); - runnable->Dispatch(aRv); + runnable->Dispatch(Closing, aRv); } if (aRv.Failed()) { diff --git a/dom/cache/DBSchema.cpp b/dom/cache/DBSchema.cpp index d16ba2d6a..176e7b9d1 100644 --- a/dom/cache/DBSchema.cpp +++ b/dom/cache/DBSchema.cpp @@ -287,7 +287,8 @@ static_assert(nsIContentPolicy::TYPE_INVALID == 0 && nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD == 38 && nsIContentPolicy::TYPE_INTERNAL_STYLESHEET == 39 && nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD == 40 && - nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON == 41, + nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON == 41 && + nsIContentPolicy::TYPE_SAVEAS_DOWNLOAD == 42, "nsContentPolicyType values are as expected"); namespace { diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp index f4c4259f6..18af28e9f 100644 --- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -1862,8 +1862,6 @@ CanvasRenderingContext2D::GetHeight() const NS_IMETHODIMP CanvasRenderingContext2D::SetDimensions(int32_t aWidth, int32_t aHeight) { - ClearTarget(); - // Zero sized surfaces can cause problems. mZero = false; if (aHeight == 0) { @@ -1874,14 +1872,14 @@ CanvasRenderingContext2D::SetDimensions(int32_t aWidth, int32_t aHeight) aWidth = 1; mZero = true; } - mWidth = aWidth; - mHeight = aHeight; + + ClearTarget(aWidth, aHeight); return NS_OK; } void -CanvasRenderingContext2D::ClearTarget() +CanvasRenderingContext2D::ClearTarget(int32_t aWidth, int32_t aHeight) { Reset(); @@ -1889,6 +1887,12 @@ CanvasRenderingContext2D::ClearTarget() SetInitialState(); + // Update dimensions only if new (strictly positive) values were passed. + if (aWidth > 0 && aHeight > 0) { + mWidth = aWidth; + mHeight = aHeight; + } + // For vertical writing-mode, unless text-orientation is sideways, // we'll modify the initial value of textBaseline to 'middle'. RefPtr<nsStyleContext> canvasStyle; diff --git a/dom/canvas/CanvasRenderingContext2D.h b/dom/canvas/CanvasRenderingContext2D.h index d5dff8f3b..848b3ee08 100644 --- a/dom/canvas/CanvasRenderingContext2D.h +++ b/dom/canvas/CanvasRenderingContext2D.h @@ -669,8 +669,11 @@ protected: /** * Disposes an old target and prepares to lazily create a new target. + * + * Parameters are the new dimensions to be used, or if either is negative, + * existing dimensions will be left unchanged. */ - void ClearTarget(); + void ClearTarget(int32_t aWidth = -1, int32_t aHeight = -1); /* * Returns the target to the buffer provider. i.e. this will queue a frame for diff --git a/dom/canvas/ImageBitmap.cpp b/dom/canvas/ImageBitmap.cpp index 6588e0aa3..e45cdfc6f 100644 --- a/dom/canvas/ImageBitmap.cpp +++ b/dom/canvas/ImageBitmap.cpp @@ -950,7 +950,7 @@ ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, ImageData& aImageData, imageSize, aCropRect, getter_AddRefs(data)); - task->Dispatch(aRv); + task->Dispatch(Terminating, aRv); } if (NS_WARN_IF(!data)) { @@ -1377,10 +1377,10 @@ private: RefPtr<DecodeBlobInMainThreadSyncTask> task = new DecodeBlobInMainThreadSyncTask(mWorkerPrivate, *mBlob, mCropRect, getter_AddRefs(data), sourceSize); - task->Dispatch(rv); // This is a synchronous call. + task->Dispatch(Terminating, rv); // This is a synchronous call. + // In case the worker is terminating, this rejection can be handled. if (NS_WARN_IF(rv.Failed())) { - // XXXbz does this really make sense if we're shutting down? Ah, well. mPromise->MaybeReject(rv); return nullptr; } @@ -2104,7 +2104,10 @@ ImageBitmap::Create(nsIGlobalObject* aGlobal, aFormat, aLayout, getter_AddRefs(data)); - task->Dispatch(aRv); + task->Dispatch(Terminating, aRv); + if (aRv.Failed()) { + return promise.forget(); + } } if (NS_WARN_IF(!data)) { diff --git a/dom/canvas/WebGLContextDraw.cpp b/dom/canvas/WebGLContextDraw.cpp index 867e47cbd..fd9ee4957 100644 --- a/dom/canvas/WebGLContextDraw.cpp +++ b/dom/canvas/WebGLContextDraw.cpp @@ -216,7 +216,21 @@ WebGLContext::BindFakeBlack(uint32_t texUnit, TexTarget target, FakeBlackType fa UniquePtr<FakeBlackTexture>& fakeBlackTex = *slot; if (!fakeBlackTex) { + gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1); + if (IsWebGL2()) { + gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_PIXELS, 0); + gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_ROWS, 0); + gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES, 0); + } + fakeBlackTex = FakeBlackTexture::Create(gl, target, fakeBlack); + + gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, mPixelStore_UnpackAlignment); + if (IsWebGL2()) { + gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_PIXELS, mPixelStore_UnpackSkipPixels); + gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_ROWS, mPixelStore_UnpackSkipRows); + gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES, mPixelStore_UnpackSkipImages); + } if (!fakeBlackTex) { return false; } @@ -1212,13 +1226,8 @@ WebGLContext::FakeBlackTexture::Create(gl::GLContext* gl, TexTarget target, gl->fTexParameteri(target.get(), LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_NEAREST); gl->fTexParameteri(target.get(), LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_NEAREST); - // We allocate our zeros on the heap, and we overallocate (16 bytes instead of 4) to - // minimize the risk of running into a driver bug in texImage2D, as it is a bit - // unusual maybe to create 1x1 textures, and the stack may not have the alignment that - // TexImage2D expects. - const webgl::DriverUnpackInfo dui = {texFormat, texFormat, LOCAL_GL_UNSIGNED_BYTE}; - UniqueBuffer zeros = moz_xcalloc(1, 16); // Infallible allocation. + UniqueBuffer zeros = moz_xcalloc(1, 4); // Infallible allocation. MOZ_ASSERT(gl->IsCurrent()); diff --git a/dom/canvas/WebGLShaderValidator.cpp b/dom/canvas/WebGLShaderValidator.cpp index 80ba359a3..fda31e212 100644 --- a/dom/canvas/WebGLShaderValidator.cpp +++ b/dom/canvas/WebGLShaderValidator.cpp @@ -28,20 +28,39 @@ IdentifierHashFunc(const char* name, size_t len) return hash[0]; } -static ShCompileOptions +static int ChooseValidatorCompileOptions(const ShBuiltInResources& resources, const mozilla::gl::GLContext* gl) { - ShCompileOptions options = SH_VARIABLES | - SH_ENFORCE_PACKING_RESTRICTIONS | - SH_OBJECT_CODE | - SH_INIT_GL_POSITION; - + int options = SH_VARIABLES |
+ SH_ENFORCE_PACKING_RESTRICTIONS |
+ SH_INIT_VARYINGS_WITHOUT_STATIC_USE | + SH_OBJECT_CODE |
+ SH_INIT_GL_POSITION;
+
+ if (resources.MaxExpressionComplexity > 0) {
+ options |= SH_LIMIT_EXPRESSION_COMPLEXITY;
+ } // Sampler arrays indexed with non-constant expressions are forbidden in // GLSL 1.30 and later. // ESSL 3 requires constant-integral-expressions for this as well. // Just do it universally. options |= SH_UNROLL_FOR_LOOP_WITH_SAMPLER_ARRAY_INDEX; + + // Needed for driver bug detection + options |= SH_EMULATE_BUILT_IN_FUNCTIONS; + + if (gfxPrefs::WebGLAllANGLEOptions()) {
+ return options |
+ SH_VALIDATE_LOOP_INDEXING |
+ SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX |
+ SH_UNROLL_FOR_LOOP_WITH_SAMPLER_ARRAY_INDEX |
+ SH_CLAMP_INDIRECT_ARRAY_BOUNDS |
+ SH_UNFOLD_SHORT_CIRCUIT |
+ SH_SCALARIZE_VEC_AND_MAT_CONSTRUCTOR_ARGS |
+ SH_INIT_OUTPUT_VARIABLES |
+ SH_REGENERATE_STRUCT_NAMES;
+ }
#ifndef XP_MACOSX // We want to do this everywhere, but to do this on Mac, we need @@ -55,30 +74,23 @@ ChooseValidatorCompileOptions(const ShBuiltInResources& resources, // Work around https://bugs.webkit.org/show_bug.cgi?id=124684, // https://chromium.googlesource.com/angle/angle/+/5e70cf9d0b1bb options |= SH_UNFOLD_SHORT_CIRCUIT; + + // OS X 10.7/10.8 specific: + + // Work around bug 665578 and bug 769810 + if (gl->Vendor() == gl::GLVendor::ATI) { + options |= SH_EMULATE_BUILT_IN_FUNCTIONS; + } + // Work around bug 735560 + if (gl->Vendor() == gl::GLVendor::Intel) { + options |= SH_EMULATE_BUILT_IN_FUNCTIONS; + } // Work around that Mac drivers handle struct scopes incorrectly. options |= SH_REGENERATE_STRUCT_NAMES; - options |= SH_INIT_OUTPUT_VARIABLES; } #endif - if (gfxPrefs::WebGLAllANGLEOptions()) { - options = -1; - - options ^= SH_INTERMEDIATE_TREE; - options ^= SH_LINE_DIRECTIVES; - options ^= SH_SOURCE_PATH; - - options ^= SH_LIMIT_EXPRESSION_COMPLEXITY; - options ^= SH_LIMIT_CALL_STACK_DEPTH; - - options ^= SH_EXPAND_SELECT_HLSL_INTEGER_POW_EXPRESSIONS; - options ^= SH_HLSL_GET_DIMENSIONS_IGNORES_BASE_LEVEL; - - options ^= SH_DONT_REMOVE_INVARIANT_FOR_FRAGMENT_INPUT; - options ^= SH_REMOVE_INVARIANT_AND_CENTROID_FOR_ESSL3; - } - if (resources.MaxExpressionComplexity > 0) { options |= SH_LIMIT_EXPRESSION_COMPLEXITY; } @@ -173,7 +185,7 @@ WebGLContext::CreateShaderValidator(GLenum shaderType) const #endif } - const auto compileOptions = webgl::ChooseValidatorCompileOptions(resources, gl); + int compileOptions = webgl::ChooseValidatorCompileOptions(resources, gl); return webgl::ShaderValidator::Create(shaderType, spec, outputLanguage, resources, compileOptions); } @@ -186,7 +198,7 @@ namespace webgl { ShaderValidator::Create(GLenum shaderType, ShShaderSpec spec, ShShaderOutput outputLanguage, const ShBuiltInResources& resources, - ShCompileOptions compileOptions) + int compileOptions) { ShHandle handle = ShConstructCompiler(shaderType, spec, outputLanguage, &resources); if (!handle) @@ -283,8 +295,8 @@ ShaderValidator::CanLinkTo(const ShaderValidator* prev, nsCString* const out_log } } { - const auto vertVars = sh::GetInterfaceBlocks(prev->mHandle); - const auto fragVars = sh::GetInterfaceBlocks(mHandle); + const auto vertVars = ShGetInterfaceBlocks(prev->mHandle); + const auto fragVars = ShGetInterfaceBlocks(mHandle); if (!vertVars || !fragVars) { nsPrintfCString error("Could not create uniform block list."); *out_log = error; diff --git a/dom/canvas/WebGLShaderValidator.h b/dom/canvas/WebGLShaderValidator.h index deb1c7c7f..ba50def28 100644 --- a/dom/canvas/WebGLShaderValidator.h +++ b/dom/canvas/WebGLShaderValidator.h @@ -17,7 +17,7 @@ namespace webgl { class ShaderValidator final { const ShHandle mHandle; - const ShCompileOptions mCompileOptions; + const int mCompileOptions; const int mMaxVaryingVectors; bool mHasRun; @@ -25,10 +25,10 @@ public: static ShaderValidator* Create(GLenum shaderType, ShShaderSpec spec, ShShaderOutput outputLanguage, const ShBuiltInResources& resources, - ShCompileOptions compileOptions); + int compileOptions); private: - ShaderValidator(ShHandle handle, ShCompileOptions compileOptions, + ShaderValidator(ShHandle handle, int compileOptions, int maxVaryingVectors) : mHandle(handle) , mCompileOptions(compileOptions) diff --git a/dom/events/Event.cpp b/dom/events/Event.cpp index 4b9776c0a..f33bfa5a8 100755 --- a/dom/events/Event.cpp +++ b/dom/events/Event.cpp @@ -280,16 +280,10 @@ Event::GetType(nsAString& aType) return NS_OK; } -static EventTarget* -GetDOMEventTarget(nsIDOMEventTarget* aTarget) -{ - return aTarget ? aTarget->GetTargetForDOMEvent() : nullptr; -} - EventTarget* Event::GetTarget() const { - return GetDOMEventTarget(mEvent->mTarget); + return mEvent->GetDOMEventTarget(); } NS_IMETHODIMP @@ -302,7 +296,7 @@ Event::GetTarget(nsIDOMEventTarget** aTarget) EventTarget* Event::GetCurrentTarget() const { - return GetDOMEventTarget(mEvent->mCurrentTarget); + return mEvent->GetCurrentDOMEventTarget(); } NS_IMETHODIMP @@ -349,11 +343,7 @@ Event::GetExplicitOriginalTarget(nsIDOMEventTarget** aRealEventTarget) EventTarget* Event::GetOriginalTarget() const { - if (mEvent->mOriginalTarget) { - return GetDOMEventTarget(mEvent->mOriginalTarget); - } - - return GetTarget(); + return mEvent->GetOriginalDOMEventTarget(); } NS_IMETHODIMP diff --git a/dom/fetch/InternalRequest.cpp b/dom/fetch/InternalRequest.cpp index 85feabde3..b2631da6a 100644 --- a/dom/fetch/InternalRequest.cpp +++ b/dom/fetch/InternalRequest.cpp @@ -320,6 +320,9 @@ InternalRequest::MapContentPolicyTypeToRequestContext(nsContentPolicyType aConte case nsIContentPolicy::TYPE_WEB_MANIFEST: context = RequestContext::Manifest; break; + case nsIContentPolicy::TYPE_SAVEAS_DOWNLOAD: + context = RequestContext::Internal; + break; default: MOZ_ASSERT(false, "Unhandled nsContentPolicyType value"); break; diff --git a/dom/fetch/InternalRequest.h b/dom/fetch/InternalRequest.h index 84ee0bf69..966490675 100644 --- a/dom/fetch/InternalRequest.h +++ b/dom/fetch/InternalRequest.h @@ -53,7 +53,7 @@ namespace dom { * image | TYPE_INTERNAL_IMAGE, TYPE_INTERNAL_IMAGE_PRELOAD, TYPE_INTERNAL_IMAGE_FAVICON * imageset | TYPE_IMAGESET * import | Not supported by Gecko - * internal | TYPE_DOCUMENT, TYPE_XBL, TYPE_OTHER + * internal | TYPE_DOCUMENT, TYPE_XBL, TYPE_OTHER, TYPE_SAVEAS_DOWNLOAD * location | * manifest | TYPE_WEB_MANIFEST * object | TYPE_INTERNAL_OBJECT diff --git a/dom/fetch/Request.cpp b/dom/fetch/Request.cpp index c119a503e..7ca5b43c4 100644 --- a/dom/fetch/Request.cpp +++ b/dom/fetch/Request.cpp @@ -338,8 +338,7 @@ Request::Constructor(const GlobalObject& aGlobal, if (mode == RequestMode::Navigate || (aInit.IsAnyMemberPresent() && request->Mode() == RequestMode::Navigate)) { - aRv.ThrowTypeError<MSG_INVALID_REQUEST_MODE>(NS_LITERAL_STRING("navigate")); - return nullptr; + mode = RequestMode::Same_origin; } if (aInit.IsAnyMemberPresent()) { @@ -374,11 +373,7 @@ Request::Constructor(const GlobalObject& aGlobal, nsresult rv = principal->CheckMayLoad(uri, /* report */ false, /* allowIfInheritsPrincipal */ false); if (NS_FAILED(rv)) { - nsAutoCString globalOrigin; - principal->GetOrigin(globalOrigin); - aRv.ThrowTypeError<MSG_CROSS_ORIGIN_REFERRER_URL>(referrer, - NS_ConvertUTF8toUTF16(globalOrigin)); - return nullptr; + referrerURL.AssignLiteral(kFETCH_CLIENT_REFERRER_STR); } } } @@ -403,11 +398,10 @@ Request::Constructor(const GlobalObject& aGlobal, // this work in a single sync loop. RefPtr<ReferrerSameOriginChecker> checker = new ReferrerSameOriginChecker(worker, referrerURL, rv); - checker->Dispatch(aRv); - if (aRv.Failed() || NS_FAILED(rv)) { - aRv.ThrowTypeError<MSG_CROSS_ORIGIN_REFERRER_URL>(referrer, - worker->GetLocationInfo().mOrigin); - return nullptr; + IgnoredErrorResult error; + checker->Dispatch(Terminating, error); + if (error.Failed() || NS_FAILED(rv)) { + referrerURL.AssignLiteral(kFETCH_CLIENT_REFERRER_STR); } } } diff --git a/dom/filesystem/tests/test_webkitdirectory.html b/dom/filesystem/tests/test_webkitdirectory.html index 825f5e8fb..591619e45 100644 --- a/dom/filesystem/tests/test_webkitdirectory.html +++ b/dom/filesystem/tests/test_webkitdirectory.html @@ -152,6 +152,7 @@ function test_changeDataWhileWorking() { function test_setup() { SpecialPowers.pushPrefEnv({"set": [["dom.input.dirpicker", true], + ["dom.filesystem.pathcheck.disabled", true], ["dom.webkitBlink.dirPicker.enabled", true]]}, next); } diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp index d09bc3103..e9086933b 100644 --- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -3546,7 +3546,8 @@ HTMLInputElement::Focus(ErrorResult& aError) nsNumberControlFrame* numberControlFrame = do_QueryFrame(GetPrimaryFrame()); if (numberControlFrame) { - HTMLInputElement* textControl = numberControlFrame->GetAnonTextControl(); + RefPtr<HTMLInputElement> textControl = + numberControlFrame->GetAnonTextControl(); if (textControl) { textControl->Focus(aError); return; diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp index 1f1a545fa..3954e6208 100644 --- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -817,6 +817,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLMediaElement, nsGenericHTM NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mErrorSink->mError) for (uint32_t i = 0; i < tmp->mOutputStreams.Length(); ++i) { NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOutputStreams[i].mStream); + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOutputStreams[i].mPreCreatedTracks); } NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPlayed); NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTextTrackManager) @@ -1020,6 +1021,17 @@ void HTMLMediaElement::ShutdownDecoder() RemoveMediaElementFromURITable(); NS_ASSERTION(mDecoder, "Must have decoder to shut down"); mWaitingForKeyListener.DisconnectIfExists(); + for (OutputMediaStream& out : mOutputStreams) { + if (!out.mCapturingDecoder) { + continue; + } + if (!out.mStream) { + continue; + } + out.mNextAvailableTrackID = std::max<TrackID>( + mDecoder->NextAvailableTrackIDFor(out.mStream->GetInputStream()), + out.mNextAvailableTrackID); + } mDecoder->Shutdown(); mDecoder = nullptr; } @@ -2730,6 +2742,7 @@ HTMLMediaElement::CaptureStreamInternal(bool aFinishWhenEnded, if (mDecoder) { out->mCapturingDecoder = true; mDecoder->AddOutputStream(out->mStream->GetInputStream()->AsProcessedStream(), + out->mNextAvailableTrackID, aFinishWhenEnded); } else if (mSrcStream) { out->mCapturingMediaStream = true; @@ -2743,23 +2756,26 @@ HTMLMediaElement::CaptureStreamInternal(bool aFinishWhenEnded, if (mDecoder) { if (HasAudio()) { - TrackID audioTrackId = mMediaInfo.mAudio.mTrackId; + TrackID audioTrackId = out->mNextAvailableTrackID++; RefPtr<MediaStreamTrackSource> trackSource = getter->GetMediaStreamTrackSource(audioTrackId); RefPtr<MediaStreamTrack> track = - out->mStream->CreateDOMTrack(audioTrackId, MediaSegment::AUDIO, + out->mStream->CreateDOMTrack(audioTrackId, + MediaSegment::AUDIO, trackSource); + out->mPreCreatedTracks.AppendElement(track); out->mStream->AddTrackInternal(track); LOG(LogLevel::Debug, ("Created audio track %d for captured decoder", audioTrackId)); } if (IsVideo() && HasVideo() && !out->mCapturingAudioOnly) { - TrackID videoTrackId = mMediaInfo.mVideo.mTrackId; + TrackID videoTrackId = out->mNextAvailableTrackID++; RefPtr<MediaStreamTrackSource> trackSource = getter->GetMediaStreamTrackSource(videoTrackId); RefPtr<MediaStreamTrack> track = out->mStream->CreateDOMTrack(videoTrackId, MediaSegment::VIDEO, trackSource); + out->mPreCreatedTracks.AppendElement(track); out->mStream->AddTrackInternal(track); LOG(LogLevel::Debug, ("Created video track %d for captured decoder", videoTrackId)); @@ -2848,6 +2864,25 @@ NS_IMETHODIMP HTMLMediaElement::GetMozAudioCaptured(bool* aCaptured) return NS_OK; } +void +HTMLMediaElement::EndPreCreatedCapturedDecoderTracks() +{ + MOZ_ASSERT(NS_IsMainThread()); + for (OutputMediaStream& ms : mOutputStreams) { + if (!ms.mCapturingDecoder) { + continue; + } + for (RefPtr<MediaStreamTrack>& t : ms.mPreCreatedTracks) { + if (t->Ended()) { + continue; + } + NS_DispatchToMainThread(NewRunnableMethod( + t, &MediaStreamTrack::OverrideEnded)); + } + ms.mPreCreatedTracks.Clear(); + } +} + class MediaElementSetForURI : public nsURIHashKey { public: explicit MediaElementSetForURI(const nsIURI* aKey) : nsURIHashKey(aKey) {} @@ -3380,11 +3415,12 @@ HTMLMediaElement::WakeLockRelease() } HTMLMediaElement::OutputMediaStream::OutputMediaStream() - : mFinishWhenEnded(false) + : mNextAvailableTrackID(1) + , mFinishWhenEnded(false) , mCapturingAudioOnly(false) , mCapturingDecoder(false) , mCapturingMediaStream(false) - , mNextAvailableTrackID(1) {} +{} HTMLMediaElement::OutputMediaStream::~OutputMediaStream() { @@ -4008,6 +4044,7 @@ nsresult HTMLMediaElement::FinishDecoderSetup(MediaDecoder* aDecoder, ms.mCapturingDecoder = true; aDecoder->AddOutputStream(ms.mStream->GetInputStream()->AsProcessedStream(), + ms.mNextAvailableTrackID, ms.mFinishWhenEnded); } @@ -5477,7 +5514,9 @@ void HTMLMediaElement::SuspendOrResumeElement(bool aPauseElement, bool aSuspendE } mEventDeliveryPaused = aSuspendEvents; } else { +#ifdef MOZ_EME MOZ_ASSERT(!mMediaKeys); +#endif if (mDecoder) { mDecoder->Resume(); if (!mPaused && !mDecoder->IsEnded()) { diff --git a/dom/html/HTMLMediaElement.h b/dom/html/HTMLMediaElement.h index b65049206..af944a318 100644 --- a/dom/html/HTMLMediaElement.h +++ b/dom/html/HTMLMediaElement.h @@ -675,6 +675,11 @@ public: return mAudioCaptured; } + /** + * Ensures any MediaStreamTracks captured from a MediaDecoder are ended. + */ + void EndPreCreatedCapturedDecoderTracks(); + void MozGetMetadata(JSContext* aCx, JS::MutableHandle<JSObject*> aResult, ErrorResult& aRv); @@ -815,13 +820,18 @@ protected: ~OutputMediaStream(); RefPtr<DOMMediaStream> mStream; + TrackID mNextAvailableTrackID; bool mFinishWhenEnded; bool mCapturingAudioOnly; bool mCapturingDecoder; bool mCapturingMediaStream; + // The following members are keeping state for a captured MediaDecoder. + // Tracks that were created on main thread before MediaDecoder fed them + // to the MediaStreamGraph. + nsTArray<RefPtr<MediaStreamTrack>> mPreCreatedTracks; + // The following members are keeping state for a captured MediaStream. - TrackID mNextAvailableTrackID; nsTArray<Pair<nsString, RefPtr<MediaInputPort>>> mTrackPorts; }; diff --git a/dom/indexedDB/ActorsParent.cpp b/dom/indexedDB/ActorsParent.cpp index 4e1b9f7af..e6fe9e2a8 100644 --- a/dom/indexedDB/ActorsParent.cpp +++ b/dom/indexedDB/ActorsParent.cpp @@ -8440,12 +8440,12 @@ class ObjectStoreAddOrPutRequestOp::SCInputStream final : public nsIInputStream { const JSStructuredCloneData& mData; - JSStructuredCloneData::IterImpl mIter; + JSStructuredCloneData::Iterator mIter; public: explicit SCInputStream(const JSStructuredCloneData& aData) : mData(aData) - , mIter(aData.Iter()) + , mIter(aData.Start()) { } private: @@ -19687,7 +19687,7 @@ UpgradeFileIdsFunction::OnFunctionCall(mozIStorageValueArray* aArguments, return NS_ERROR_UNEXPECTED; } - StructuredCloneReadInfo cloneInfo; + StructuredCloneReadInfo cloneInfo(JS::StructuredCloneScope::DifferentProcess); DatabaseOperationBase::GetStructuredCloneReadInfoFromValueArray(aArguments, 1, 0, @@ -19892,7 +19892,7 @@ DatabaseOperationBase::GetStructuredCloneReadInfoFromBlob( return NS_ERROR_FILE_CORRUPTED; } - if (!aInfo->mData.WriteBytes(uncompressedBuffer, uncompressed.Length())) { + if (!aInfo->mData.AppendBytes(uncompressedBuffer, uncompressed.Length())) { return NS_ERROR_OUT_OF_MEMORY; } @@ -19978,7 +19978,7 @@ DatabaseOperationBase::GetStructuredCloneReadInfoFromExternalBlob( break; } - if (NS_WARN_IF(!aInfo->mData.WriteBytes(buffer, numRead))) { + if (NS_WARN_IF(!aInfo->mData.AppendBytes(buffer, numRead))) { rv = NS_ERROR_OUT_OF_MEMORY; break; } @@ -25337,7 +25337,7 @@ UpdateIndexDataValuesFunction::OnFunctionCall(mozIStorageValueArray* aValues, } #endif - StructuredCloneReadInfo cloneInfo; + StructuredCloneReadInfo cloneInfo(JS::StructuredCloneScope::DifferentProcess); nsresult rv = GetStructuredCloneReadInfoFromValueArray(aValues, /* aDataIndex */ 3, @@ -26546,18 +26546,9 @@ ObjectStoreAddOrPutRequestOp::DoDatabaseWork(DatabaseConnection* aConnection) char keyPropBuffer[keyPropSize]; LittleEndian::writeUint64(keyPropBuffer, keyPropValue); - auto iter = cloneData.Iter(); - DebugOnly<bool> result = - iter.AdvanceAcrossSegments(cloneData, cloneInfo.offsetToKeyProp()); - MOZ_ASSERT(result); - - for (uint32_t index = 0; index < keyPropSize; index++) { - char* keyPropPointer = iter.Data(); - *keyPropPointer = keyPropBuffer[index]; - - result = iter.AdvanceAcrossSegments(cloneData, 1); - MOZ_ASSERT(result); - } + auto iter = cloneData.Start(); + MOZ_ALWAYS_TRUE(cloneData.Advance(iter, cloneInfo.offsetToKeyProp())); + MOZ_ALWAYS_TRUE(cloneData.UpdateBytes(iter, keyPropBuffer, keyPropSize)); } } @@ -26583,7 +26574,7 @@ ObjectStoreAddOrPutRequestOp::DoDatabaseWork(DatabaseConnection* aConnection) } else { nsCString flatCloneData; flatCloneData.SetLength(cloneDataSize); - auto iter = cloneData.Iter(); + auto iter = cloneData.Start(); cloneData.ReadBytes(iter, flatCloneData.BeginWriting(), cloneDataSize); // Compress the bytes before adding into the database. @@ -26840,7 +26831,7 @@ SCInputStream::ReadSegments(nsWriteSegmentFun aWriter, *_retval += count; aCount -= count; - mIter.Advance(mData, count); + mData.Advance(mIter, count); } return NS_OK; @@ -28029,7 +28020,7 @@ CursorOpBase::PopulateResponseFromStatement( switch (mCursor->mType) { case OpenCursorParams::TObjectStoreOpenCursorParams: { - StructuredCloneReadInfo cloneInfo; + StructuredCloneReadInfo cloneInfo(JS::StructuredCloneScope::DifferentProcess); rv = GetStructuredCloneReadInfoFromStatement(aStmt, 2, 1, @@ -28077,7 +28068,7 @@ CursorOpBase::PopulateResponseFromStatement( return rv; } - StructuredCloneReadInfo cloneInfo; + StructuredCloneReadInfo cloneInfo(JS::StructuredCloneScope::DifferentProcess); rv = GetStructuredCloneReadInfoFromStatement(aStmt, 4, 3, diff --git a/dom/indexedDB/IDBObjectStore.cpp b/dom/indexedDB/IDBObjectStore.cpp index a6d6c5f06..8a0b292ad 100644 --- a/dom/indexedDB/IDBObjectStore.cpp +++ b/dom/indexedDB/IDBObjectStore.cpp @@ -67,7 +67,7 @@ struct IDBObjectStore::StructuredCloneWriteInfo uint64_t mOffsetToKeyProp; explicit StructuredCloneWriteInfo(IDBDatabase* aDatabase) - : mCloneBuffer(JS::StructuredCloneScope::SameProcessSameThread, nullptr, + : mCloneBuffer(JS::StructuredCloneScope::DifferentProcessForIndexedDB, nullptr, nullptr) , mDatabase(aDatabase) , mOffsetToKeyProp(0) @@ -1216,7 +1216,7 @@ IDBObjectStore::DeserializeValue(JSContext* aCx, // FIXME: Consider to use StructuredCloneHolder here and in other // deserializing methods. if (!JS_ReadStructuredClone(aCx, aCloneReadInfo.mData, JS_STRUCTURED_CLONE_VERSION, - JS::StructuredCloneScope::SameProcessSameThread, + JS::StructuredCloneScope::DifferentProcessForIndexedDB, aValue, &callbacks, &aCloneReadInfo)) { return false; } @@ -1249,7 +1249,7 @@ IDBObjectStore::DeserializeIndexValue(JSContext* aCx, }; if (!JS_ReadStructuredClone(aCx, aCloneReadInfo.mData, JS_STRUCTURED_CLONE_VERSION, - JS::StructuredCloneScope::SameProcessSameThread, + JS::StructuredCloneScope::DifferentProcessForIndexedDB, aValue, &callbacks, &aCloneReadInfo)) { return false; } @@ -1285,7 +1285,7 @@ IDBObjectStore::DeserializeUpgradeValue(JSContext* aCx, }; if (!JS_ReadStructuredClone(aCx, aCloneReadInfo.mData, JS_STRUCTURED_CLONE_VERSION, - JS::StructuredCloneScope::SameProcessSameThread, + JS::StructuredCloneScope::DifferentProcessForIndexedDB, aValue, &callbacks, &aCloneReadInfo)) { return false; } diff --git a/dom/indexedDB/IndexedDatabase.h b/dom/indexedDB/IndexedDatabase.h index b0c4cb877..b3c6ab725 100644 --- a/dom/indexedDB/IndexedDatabase.h +++ b/dom/indexedDB/IndexedDatabase.h @@ -65,6 +65,10 @@ struct StructuredCloneReadInfo bool mHasPreprocessInfo; // In IndexedDatabaseInlines.h + inline explicit + StructuredCloneReadInfo(JS::StructuredCloneScope aScope); + + // In IndexedDatabaseInlines.h inline StructuredCloneReadInfo(); diff --git a/dom/indexedDB/IndexedDatabaseInlines.h b/dom/indexedDB/IndexedDatabaseInlines.h index 830c2f110..8c34a81dd 100644 --- a/dom/indexedDB/IndexedDatabaseInlines.h +++ b/dom/indexedDB/IndexedDatabaseInlines.h @@ -45,14 +45,21 @@ StructuredCloneFile::operator==(const StructuredCloneFile& aOther) const } inline -StructuredCloneReadInfo::StructuredCloneReadInfo() - : mDatabase(nullptr) +StructuredCloneReadInfo::StructuredCloneReadInfo(JS::StructuredCloneScope aScope) + : mData(aScope) + , mDatabase(nullptr) , mHasPreprocessInfo(false) { MOZ_COUNT_CTOR(StructuredCloneReadInfo); } inline +StructuredCloneReadInfo::StructuredCloneReadInfo() + : StructuredCloneReadInfo(JS::StructuredCloneScope::DifferentProcessForIndexedDB) +{ +} + +inline StructuredCloneReadInfo::StructuredCloneReadInfo( StructuredCloneReadInfo&& aCloneReadInfo) : mData(Move(aCloneReadInfo.mData)) diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 3488e26bd..0a07147bf 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -4721,6 +4721,14 @@ ContentParent::RecvGetFilesRequest(const nsID& aUUID, { MOZ_ASSERT(!mGetFilesPendingRequests.GetWeak(aUUID)); + if (!mozilla::Preferences::GetBool("dom.filesystem.pathcheck.disabled", false)) { + RefPtr<FileSystemSecurity> fss = FileSystemSecurity::Get(); + if (NS_WARN_IF(!fss || + !fss->ContentProcessHasAccessTo(ChildID(), aDirectoryPath))) { + return false; + } + } + ErrorResult rv; RefPtr<GetFilesHelper> helper = GetFilesHelperParent::Create(aUUID, aDirectoryPath, aRecursiveFlag, this, diff --git a/dom/ipc/StructuredCloneData.cpp b/dom/ipc/StructuredCloneData.cpp index 98f56904f..2c1fff2ac 100644 --- a/dom/ipc/StructuredCloneData.cpp +++ b/dom/ipc/StructuredCloneData.cpp @@ -88,7 +88,7 @@ StructuredCloneData::Write(JSContext* aCx, return; } - JSStructuredCloneData data; + JSStructuredCloneData data(mBuffer->scope()); mBuffer->abandon(); mBuffer->steal(&data); mBuffer = nullptr; @@ -107,7 +107,7 @@ StructuredCloneData::ReadIPCParams(const IPC::Message* aMsg, PickleIterator* aIter) { MOZ_ASSERT(!mInitialized); - JSStructuredCloneData data; + JSStructuredCloneData data(JS::StructuredCloneScope::DifferentProcess); if (!ReadParam(aMsg, aIter, &data)) { return false; } diff --git a/dom/ipc/StructuredCloneData.h b/dom/ipc/StructuredCloneData.h index 9e427e938..64cfd1935 100644 --- a/dom/ipc/StructuredCloneData.h +++ b/dom/ipc/StructuredCloneData.h @@ -31,8 +31,8 @@ public: static already_AddRefed<SharedJSAllocatedData> CreateFromExternalData(const char* aData, size_t aDataLength) { - JSStructuredCloneData buf; - buf.WriteBytes(aData, aDataLength); + JSStructuredCloneData buf(JS::StructuredCloneScope::DifferentProcess); + buf.AppendBytes(aData, aDataLength); RefPtr<SharedJSAllocatedData> sharedData = new SharedJSAllocatedData(Move(buf)); return sharedData.forget(); @@ -41,12 +41,8 @@ public: static already_AddRefed<SharedJSAllocatedData> CreateFromExternalData(const JSStructuredCloneData& aData) { - JSStructuredCloneData buf; - auto iter = aData.Iter(); - while (!iter.Done()) { - buf.WriteBytes(iter.Data(), iter.RemainingInSegment()); - iter.Advance(aData, iter.RemainingInSegment()); - } + JSStructuredCloneData buf(aData.scope()); + buf.Append(aData); RefPtr<SharedJSAllocatedData> sharedData = new SharedJSAllocatedData(Move(buf)); return sharedData.forget(); @@ -70,6 +66,7 @@ public: : StructuredCloneHolder(StructuredCloneHolder::CloningSupported, StructuredCloneHolder::TransferringSupported, StructuredCloneHolder::StructuredCloneScope::DifferentProcess) + , mExternalData(StructuredCloneHolder::StructuredCloneScope::DifferentProcess) , mInitialized(false) {} @@ -113,10 +110,9 @@ public: bool UseExternalData(const JSStructuredCloneData& aData) { - auto iter = aData.Iter(); + auto iter = aData.Start(); bool success = false; - mExternalData = - aData.Borrow<js::SystemAllocPolicy>(iter, aData.Size(), &success); + mExternalData = aData.Borrow(iter, aData.Size(), &success); mInitialized = true; return success; } @@ -133,6 +129,11 @@ public: return mSharedData ? mSharedData->Data() : mExternalData; } + void InitScope(JS::StructuredCloneScope aScope) + { + Data().initScope(aScope); + } + size_t DataLength() const { return mSharedData ? mSharedData->DataLength() : mExternalData.Size(); diff --git a/dom/media/MediaDecoder.cpp b/dom/media/MediaDecoder.cpp index ab39886ba..9334d1bcb 100644 --- a/dom/media/MediaDecoder.cpp +++ b/dom/media/MediaDecoder.cpp @@ -327,11 +327,13 @@ MediaDecoder::SetVolume(double aVolume) void MediaDecoder::AddOutputStream(ProcessedMediaStream* aStream, + TrackID aNextAvailableTrackID, bool aFinishWhenEnded) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(mDecoderStateMachine, "Must be called after Load()."); - mDecoderStateMachine->AddOutputStream(aStream, aFinishWhenEnded); + mDecoderStateMachine->AddOutputStream( + aStream, aNextAvailableTrackID, aFinishWhenEnded); } void @@ -342,6 +344,14 @@ MediaDecoder::RemoveOutputStream(MediaStream* aStream) mDecoderStateMachine->RemoveOutputStream(aStream); } +TrackID +MediaDecoder::NextAvailableTrackIDFor(MediaStream* aOutputStream) const +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mDecoderStateMachine, "Must be called after Load()."); + return mDecoderStateMachine->NextAvailableTrackIDFor(aOutputStream); +} + double MediaDecoder::GetDuration() { @@ -1736,6 +1746,8 @@ MediaDecoder::RemoveMediaTracks() videoList->RemoveTracks(); } + element->EndPreCreatedCapturedDecoderTracks(); + mMediaTracksConstructed = false; } diff --git a/dom/media/MediaDecoder.h b/dom/media/MediaDecoder.h index 825bbbd2c..a4edcbe72 100644 --- a/dom/media/MediaDecoder.h +++ b/dom/media/MediaDecoder.h @@ -210,9 +210,13 @@ public: // Add an output stream. All decoder output will be sent to the stream. // The stream is initially blocked. The decoder is responsible for unblocking // it while it is playing back. - virtual void AddOutputStream(ProcessedMediaStream* aStream, bool aFinishWhenEnded); + virtual void AddOutputStream(ProcessedMediaStream* aStream, + TrackID aNextAvailableTrackID, + bool aFinishWhenEnded); // Remove an output stream added with AddOutputStream. virtual void RemoveOutputStream(MediaStream* aStream); + // The next TrackID that can be used without risk of a collision. + virtual TrackID NextAvailableTrackIDFor(MediaStream* aOutputStream) const; // Return the duration of the video in seconds. virtual double GetDuration(); diff --git a/dom/media/MediaDecoderStateMachine.cpp b/dom/media/MediaDecoderStateMachine.cpp index f13e59b6c..c586139ad 100644 --- a/dom/media/MediaDecoderStateMachine.cpp +++ b/dom/media/MediaDecoderStateMachine.cpp @@ -2835,6 +2835,10 @@ MediaDecoderStateMachine::FinishDecodeFirstFrame() RefPtr<ShutdownPromise> MediaDecoderStateMachine::BeginShutdown() { + MOZ_ASSERT(NS_IsMainThread()); + if (mOutputStreamManager) { + mOutputStreamManager->Clear(); + } return InvokeAsync(OwnerThread(), this, __func__, &MediaDecoderStateMachine::Shutdown); } @@ -3250,11 +3254,12 @@ MediaDecoderStateMachine::DumpDebugInfo() } void MediaDecoderStateMachine::AddOutputStream(ProcessedMediaStream* aStream, + TrackID aNextAvailableTrackID, bool aFinishWhenEnded) { MOZ_ASSERT(NS_IsMainThread()); DECODER_LOG("AddOutputStream aStream=%p!", aStream); - mOutputStreamManager->Add(aStream, aFinishWhenEnded); + mOutputStreamManager->Add(aStream, aNextAvailableTrackID, aFinishWhenEnded); nsCOMPtr<nsIRunnable> r = NewRunnableMethod<bool>( this, &MediaDecoderStateMachine::SetAudioCaptured, true); OwnerThread()->Dispatch(r.forget()); @@ -3269,9 +3274,17 @@ void MediaDecoderStateMachine::RemoveOutputStream(MediaStream* aStream) nsCOMPtr<nsIRunnable> r = NewRunnableMethod<bool>( this, &MediaDecoderStateMachine::SetAudioCaptured, false); OwnerThread()->Dispatch(r.forget()); + } } +TrackID +MediaDecoderStateMachine::NextAvailableTrackIDFor(MediaStream* aOutputStream) const +{ + MOZ_ASSERT(NS_IsMainThread()); + return mOutputStreamManager->NextAvailableTrackIDFor(aOutputStream); +} + size_t MediaDecoderStateMachine::SizeOfVideoQueue() const { diff --git a/dom/media/MediaDecoderStateMachine.h b/dom/media/MediaDecoderStateMachine.h index a61fe13d2..ff3258ff1 100644 --- a/dom/media/MediaDecoderStateMachine.h +++ b/dom/media/MediaDecoderStateMachine.h @@ -168,9 +168,12 @@ public: void DumpDebugInfo(); - void AddOutputStream(ProcessedMediaStream* aStream, bool aFinishWhenEnded); + void AddOutputStream(ProcessedMediaStream* aStream, + TrackID aNextAvailableTrackID, + bool aFinishWhenEnded); // Remove an output stream added with AddOutputStream. void RemoveOutputStream(MediaStream* aStream); + TrackID NextAvailableTrackIDFor(MediaStream* aOutputStream) const; // Seeks to the decoder to aTarget asynchronously. RefPtr<MediaDecoder::SeekPromise> InvokeSeek(SeekTarget aTarget); diff --git a/dom/media/PeerConnection.js b/dom/media/PeerConnection.js index 0c3021799..0569b15ae 100644 --- a/dom/media/PeerConnection.js +++ b/dom/media/PeerConnection.js @@ -516,6 +516,18 @@ RTCPeerConnection.prototype = { }; }, + // This implements the fairly common "Queue a task" logic + async _queueTaskWithClosedCheck(func) { + return new Promise(resolve => { + Services.tm.mainThread.dispatch({ run() { + if (!this._closed) { + func(); + resolve(); + } + }}, Ci.nsIThread.DISPATCH_NORMAL); + }); + }, + /** * An RTCConfiguration may look like this: * @@ -1445,7 +1457,10 @@ PeerConnectionObserver.prototype = { break; case "IceConnectionState": - this.handleIceConnectionStateChange(this._dompc._pc.iceConnectionState); + let connState = this._dompc._pc.iceConnectionState; + this._dompc._queueTaskWithClosedCheck(() => { + this.handleIceConnectionStateChange(connState); + }); break; case "IceGatheringState": diff --git a/dom/media/mediasink/DecodedStream.cpp b/dom/media/mediasink/DecodedStream.cpp index 9501a6cde..00bc5ea49 100644 --- a/dom/media/mediasink/DecodedStream.cpp +++ b/dom/media/mediasink/DecodedStream.cpp @@ -181,17 +181,22 @@ DecodedStreamData::DecodedStreamData(OutputStreamManager* aOutputStreamManager, , mOutputStreamManager(aOutputStreamManager) { mStream->AddListener(mListener); - mOutputStreamManager->Connect(mStream); + TrackID audioTrack = TRACK_NONE; + TrackID videoTrack = TRACK_NONE; // Initialize tracks. if (aInit.mInfo.HasAudio()) { - mStream->AddAudioTrack(aInit.mInfo.mAudio.mTrackId, + audioTrack = aInit.mInfo.mAudio.mTrackId; + mStream->AddAudioTrack(audioTrack, aInit.mInfo.mAudio.mRate, 0, new AudioSegment()); } if (aInit.mInfo.HasVideo()) { - mStream->AddTrack(aInit.mInfo.mVideo.mTrackId, 0, new VideoSegment()); + videoTrack = aInit.mInfo.mVideo.mTrackId; + mStream->AddTrack(videoTrack, 0, new VideoSegment()); } + + mOutputStreamManager->Connect(mStream, audioTrack, videoTrack); } DecodedStreamData::~DecodedStreamData() diff --git a/dom/media/mediasink/OutputStreamManager.cpp b/dom/media/mediasink/OutputStreamManager.cpp index d5685837a..7ecc203ed 100644 --- a/dom/media/mediasink/OutputStreamManager.cpp +++ b/dom/media/mediasink/OutputStreamManager.cpp @@ -13,29 +13,41 @@ OutputStreamData::~OutputStreamData() { MOZ_ASSERT(NS_IsMainThread()); // Break the connection to the input stream if necessary. - if (mPort) { - mPort->Destroy(); + for (RefPtr<MediaInputPort>& port : mPorts) { + port->Destroy(); } } void -OutputStreamData::Init(OutputStreamManager* aOwner, ProcessedMediaStream* aStream) +OutputStreamData::Init(OutputStreamManager* aOwner, + ProcessedMediaStream* aStream, + TrackID aNextAvailableTrackID) { mOwner = aOwner; mStream = aStream; + mNextAvailableTrackID = aNextAvailableTrackID; } bool -OutputStreamData::Connect(MediaStream* aStream) +OutputStreamData::Connect(MediaStream* aStream, + TrackID aInputAudioTrackID, + TrackID aInputVideoTrackID) { MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!mPort, "Already connected?"); + MOZ_ASSERT(mPorts.IsEmpty(), "Already connected?"); if (mStream->IsDestroyed()) { return false; } - mPort = mStream->AllocateInputPort(aStream); + for (TrackID tid : {aInputAudioTrackID, aInputVideoTrackID}) { + if (tid == TRACK_NONE) { + continue; + } + MOZ_ASSERT(IsTrackIDExplicit(tid)); + mPorts.AppendElement(mStream->AllocateInputPort( + aStream, tid, mNextAvailableTrackID++)); + } return true; } @@ -51,11 +63,11 @@ OutputStreamData::Disconnect() return false; } - // Disconnect the existing port if necessary. - if (mPort) { - mPort->Destroy(); - mPort = nullptr; + // Disconnect any existing port. + for (RefPtr<MediaInputPort>& port : mPorts) { + port->Destroy(); } + mPorts.Clear(); return true; } @@ -71,8 +83,16 @@ OutputStreamData::Graph() const return mStream->Graph(); } +TrackID +OutputStreamData::NextAvailableTrackID() const +{ + return mNextAvailableTrackID; +} + void -OutputStreamManager::Add(ProcessedMediaStream* aStream, bool aFinishWhenEnded) +OutputStreamManager::Add(ProcessedMediaStream* aStream, + TrackID aNextAvailableTrackID, + bool aFinishWhenEnded) { MOZ_ASSERT(NS_IsMainThread()); // All streams must belong to the same graph. @@ -84,12 +104,12 @@ OutputStreamManager::Add(ProcessedMediaStream* aStream, bool aFinishWhenEnded) } OutputStreamData* p = mStreams.AppendElement(); - p->Init(this, aStream); + p->Init(this, aStream, aNextAvailableTrackID); // Connect to the input stream if we have one. Otherwise the output stream // will be connected in Connect(). if (mInputStream) { - p->Connect(mInputStream); + p->Connect(mInputStream, mInputAudioTrackID, mInputVideoTrackID); } } @@ -106,12 +126,35 @@ OutputStreamManager::Remove(MediaStream* aStream) } void -OutputStreamManager::Connect(MediaStream* aStream) +OutputStreamManager::Clear() +{ + MOZ_ASSERT(NS_IsMainThread()); + mStreams.Clear(); +} + +TrackID +OutputStreamManager::NextAvailableTrackIDFor(MediaStream* aOutputStream) const +{ + MOZ_ASSERT(NS_IsMainThread()); + for (const OutputStreamData& out : mStreams) { + if (out.Equals(aOutputStream)) { + return out.NextAvailableTrackID(); + } + } + return TRACK_INVALID; +} + +void +OutputStreamManager::Connect(MediaStream* aStream, + TrackID aAudioTrackID, + TrackID aVideoTrackID) { MOZ_ASSERT(NS_IsMainThread()); mInputStream = aStream; + mInputAudioTrackID = aAudioTrackID; + mInputVideoTrackID = aVideoTrackID; for (int32_t i = mStreams.Length() - 1; i >= 0; --i) { - if (!mStreams[i].Connect(aStream)) { + if (!mStreams[i].Connect(aStream, mInputAudioTrackID, mInputVideoTrackID)) { // Probably the DOMMediaStream was GCed. Clean up. mStreams.RemoveElementAt(i); } @@ -123,6 +166,8 @@ OutputStreamManager::Disconnect() { MOZ_ASSERT(NS_IsMainThread()); mInputStream = nullptr; + mInputAudioTrackID = TRACK_INVALID; + mInputVideoTrackID = TRACK_INVALID; for (int32_t i = mStreams.Length() - 1; i >= 0; --i) { if (!mStreams[i].Disconnect()) { // Probably the DOMMediaStream was GCed. Clean up. diff --git a/dom/media/mediasink/OutputStreamManager.h b/dom/media/mediasink/OutputStreamManager.h index 7f91a60c1..941a86cf0 100644 --- a/dom/media/mediasink/OutputStreamManager.h +++ b/dom/media/mediasink/OutputStreamManager.h @@ -9,6 +9,7 @@ #include "mozilla/RefPtr.h" #include "nsTArray.h" +#include "MediaSegment.h" namespace mozilla { @@ -21,11 +22,13 @@ class ProcessedMediaStream; class OutputStreamData { public: ~OutputStreamData(); - void Init(OutputStreamManager* aOwner, ProcessedMediaStream* aStream); + void Init(OutputStreamManager* aOwner, + ProcessedMediaStream* aStream, + TrackID aNextAvailableTrackID); - // Connect mStream to the input stream. + // Connect the given input stream's audio and video tracks to mStream. // Return false is mStream is already destroyed, otherwise true. - bool Connect(MediaStream* aStream); + bool Connect(MediaStream* aStream, TrackID aAudioTrackID, TrackID aVideoTrackID); // Disconnect mStream from its input stream. // Return false is mStream is already destroyed, otherwise true. bool Disconnect(); @@ -34,12 +37,16 @@ public: bool Equals(MediaStream* aStream) const; // Return the graph mStream belongs to. MediaStreamGraph* Graph() const; + // The next TrackID that will not cause a collision in mStream. + TrackID NextAvailableTrackID() const; private: OutputStreamManager* mOwner; RefPtr<ProcessedMediaStream> mStream; - // mPort connects our mStream to an input stream. - RefPtr<MediaInputPort> mPort; + // mPort connects an input stream to our mStream. + nsTArray<RefPtr<MediaInputPort>> mPorts; + // For guaranteeing TrackID uniqueness in our mStream. + TrackID mNextAvailableTrackID = TRACK_INVALID; }; class OutputStreamManager { @@ -47,18 +54,26 @@ class OutputStreamManager { public: // Add the output stream to the collection. - void Add(ProcessedMediaStream* aStream, bool aFinishWhenEnded); + void Add(ProcessedMediaStream* aStream, + TrackID aNextAvailableTrackID, + bool aFinishWhenEnded); // Remove the output stream from the collection. void Remove(MediaStream* aStream); + // Clear all output streams from the collection. + void Clear(); + // The next TrackID that will not cause a collision in aOutputStream. + TrackID NextAvailableTrackIDFor(MediaStream* aOutputStream) const; // Return true if the collection empty. bool IsEmpty() const { MOZ_ASSERT(NS_IsMainThread()); return mStreams.IsEmpty(); } - // Connect all output streams in the collection to the input stream. - void Connect(MediaStream* aStream); - // Disconnect all output streams from the input stream. + // Connect the given input stream's tracks to all output streams. + void Connect(MediaStream* aStream, + TrackID aAudioTrackID, + TrackID aVideoTrackID); + // Disconnect the input stream to all output streams. void Disconnect(); // Return the graph these streams belong to or null if empty. MediaStreamGraph* Graph() const @@ -72,6 +87,8 @@ private: // Keep the input stream so we can connect the output streams that // are added after Connect(). RefPtr<MediaStream> mInputStream; + TrackID mInputAudioTrackID = TRACK_INVALID; + TrackID mInputVideoTrackID = TRACK_INVALID; nsTArray<OutputStreamData> mStreams; }; diff --git a/dom/notification/Notification.cpp b/dom/notification/Notification.cpp index 9c0ce2f18..1dd5724e4 100644 --- a/dom/notification/Notification.cpp +++ b/dom/notification/Notification.cpp @@ -1881,7 +1881,7 @@ Notification::GetPermission(nsIGlobalObject* aGlobal, ErrorResult& aRv) MOZ_ASSERT(worker); RefPtr<GetPermissionRunnable> r = new GetPermissionRunnable(worker); - r->Dispatch(aRv); + r->Dispatch(Terminating, aRv); if (aRv.Failed()) { return NotificationPermission::Denied; } @@ -2484,7 +2484,7 @@ NotificationWorkerHolder::Notify(Status aStatus) RefPtr<CloseNotificationRunnable> r = new CloseNotificationRunnable(kungFuDeathGrip); ErrorResult rv; - r->Dispatch(rv); + r->Dispatch(Killing, rv); // XXXbz I'm told throwing and returning false from here is pointless (and // also that doing sync stuff from here is really weird), so I guess we just // suppress the exception on rv, if any. @@ -2627,7 +2627,7 @@ Notification::ShowPersistentNotification(JSContext* aCx, worker->AssertIsOnWorkerThread(); RefPtr<CheckLoadRunnable> loadChecker = new CheckLoadRunnable(worker, NS_ConvertUTF16toUTF8(aScope)); - loadChecker->Dispatch(aRv); + loadChecker->Dispatch(Terminating, aRv); if (aRv.Failed()) { return nullptr; } diff --git a/dom/performance/PerformanceNavigationTiming.cpp b/dom/performance/PerformanceNavigationTiming.cpp index 4e00b2bb2..d7e16725a 100644 --- a/dom/performance/PerformanceNavigationTiming.cpp +++ b/dom/performance/PerformanceNavigationTiming.cpp @@ -6,6 +6,7 @@ #include "mozilla/dom/PerformanceNavigationTiming.h" #include "mozilla/dom/PerformanceNavigationTimingBinding.h" +#include "mozilla/TimerClamping.h" using namespace mozilla::dom; @@ -24,49 +25,49 @@ PerformanceNavigationTiming::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aG DOMHighResTimeStamp PerformanceNavigationTiming::UnloadEventStart() const { - return mTiming->GetDOMTiming()->GetUnloadEventStartHighRes(); + return TimerClamping::ReduceMsTimeValue(mTiming->GetDOMTiming()->GetUnloadEventStartHighRes()); } DOMHighResTimeStamp PerformanceNavigationTiming::UnloadEventEnd() const { - return mTiming->GetDOMTiming()->GetUnloadEventEndHighRes(); + return TimerClamping::ReduceMsTimeValue(mTiming->GetDOMTiming()->GetUnloadEventEndHighRes()); } DOMHighResTimeStamp PerformanceNavigationTiming::DomInteractive() const { - return mTiming->GetDOMTiming()->GetDomInteractiveHighRes(); + return TimerClamping::ReduceMsTimeValue(mTiming->GetDOMTiming()->GetDomInteractiveHighRes()); } DOMHighResTimeStamp PerformanceNavigationTiming::DomContentLoadedEventStart() const { - return mTiming->GetDOMTiming()->GetDomContentLoadedEventStartHighRes(); + return TimerClamping::ReduceMsTimeValue(mTiming->GetDOMTiming()->GetDomContentLoadedEventStartHighRes()); } DOMHighResTimeStamp PerformanceNavigationTiming::DomContentLoadedEventEnd() const { - return mTiming->GetDOMTiming()->GetDomContentLoadedEventEndHighRes(); + return TimerClamping::ReduceMsTimeValue(mTiming->GetDOMTiming()->GetDomContentLoadedEventEndHighRes()); } DOMHighResTimeStamp PerformanceNavigationTiming::DomComplete() const { - return mTiming->GetDOMTiming()->GetDomCompleteHighRes(); + return TimerClamping::ReduceMsTimeValue(mTiming->GetDOMTiming()->GetDomCompleteHighRes()); } DOMHighResTimeStamp PerformanceNavigationTiming::LoadEventStart() const { - return mTiming->GetDOMTiming()->GetLoadEventStartHighRes(); + return TimerClamping::ReduceMsTimeValue(mTiming->GetDOMTiming()->GetLoadEventStartHighRes()); } DOMHighResTimeStamp PerformanceNavigationTiming::LoadEventEnd() const { - return mTiming->GetDOMTiming()->GetLoadEventEndHighRes(); + return TimerClamping::ReduceMsTimeValue(mTiming->GetDOMTiming()->GetLoadEventEndHighRes()); } NavigationType diff --git a/dom/plugins/base/nsPluginStreamListenerPeer.cpp b/dom/plugins/base/nsPluginStreamListenerPeer.cpp index 26e0318e3..665e11ec1 100644 --- a/dom/plugins/base/nsPluginStreamListenerPeer.cpp +++ b/dom/plugins/base/nsPluginStreamListenerPeer.cpp @@ -1381,15 +1381,6 @@ nsPluginStreamListenerPeer::AsyncOnChannelRedirect(nsIChannel *oldChannel, nsICh return NS_ERROR_FAILURE; } - nsCOMPtr<nsIAsyncVerifyRedirectCallback> proxyCallback = - new ChannelRedirectProxyCallback(this, callback, oldChannel, newChannel); - - // Give NPAPI a chance to control redirects. - bool notificationHandled = mPStreamListener->HandleRedirectNotification(oldChannel, newChannel, proxyCallback); - if (notificationHandled) { - return NS_OK; - } - // Don't allow cross-origin 307 POST redirects. nsCOMPtr<nsIHttpChannel> oldHttpChannel(do_QueryInterface(oldChannel)); if (oldHttpChannel) { @@ -1413,6 +1404,15 @@ nsPluginStreamListenerPeer::AsyncOnChannelRedirect(nsIChannel *oldChannel, nsICh } } + nsCOMPtr<nsIAsyncVerifyRedirectCallback> proxyCallback = + new ChannelRedirectProxyCallback(this, callback, oldChannel, newChannel); + + // Give NPAPI a chance to control redirects. + bool notificationHandled = mPStreamListener->HandleRedirectNotification(oldChannel, newChannel, proxyCallback); + if (notificationHandled) { + return NS_OK; + } + // Fall back to channel event sink for window. nsCOMPtr<nsIChannelEventSink> channelEventSink; nsresult rv = GetInterfaceGlobal(NS_GET_IID(nsIChannelEventSink), getter_AddRefs(channelEventSink)); diff --git a/dom/plugins/test/unit/xpcshell.ini b/dom/plugins/test/unit/xpcshell.ini index 69b6731b2..2665c82b5 100644 --- a/dom/plugins/test/unit/xpcshell.ini +++ b/dom/plugins/test/unit/xpcshell.ini @@ -5,7 +5,6 @@ tail = tags = addons firefox-appdir = browser support-files = - !/toolkit/mozapps/webextensions/test/xpcshell/head_addons.js [test_allowed_types.js] skip-if = appname == "thunderbird" diff --git a/dom/quota/StorageManager.cpp b/dom/quota/StorageManager.cpp index c8455f0fe..4e9f0cf8c 100644 --- a/dom/quota/StorageManager.cpp +++ b/dom/quota/StorageManager.cpp @@ -335,7 +335,7 @@ StorageManager::Estimate(ErrorResult& aRv) new EstimateWorkerMainThreadRunnable(promiseProxy->GetWorkerPrivate(), promiseProxy); - runnnable->Dispatch(aRv); + runnnable->Dispatch(Terminating, aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } diff --git a/dom/security/nsCSPService.cpp b/dom/security/nsCSPService.cpp index 7344e19fa..4807c9aa4 100644 --- a/dom/security/nsCSPService.cpp +++ b/dom/security/nsCSPService.cpp @@ -288,6 +288,7 @@ CSPService::AsyncOnChannelRedirect(nsIChannel *oldChannel, nsContentUtils::InternalContentPolicyTypeToExternalOrWorker(policyType); int16_t aDecision = nsIContentPolicy::ACCEPT; + nsCOMPtr<nsISupports> requestContext = loadInfo->GetLoadingContext(); // 1) Apply speculative CSP for preloads if (isPreload) { nsCOMPtr<nsIContentSecurityPolicy> preloadCsp; @@ -298,7 +299,7 @@ CSPService::AsyncOnChannelRedirect(nsIChannel *oldChannel, preloadCsp->ShouldLoad(policyType, // load type per nsIContentPolicy (uint32_t) newUri, // nsIURI nullptr, // nsIURI - nullptr, // nsISupports + requestContext, // nsISupports EmptyCString(), // ACString - MIME guess originalUri, // aExtra &aDecision); @@ -321,7 +322,7 @@ CSPService::AsyncOnChannelRedirect(nsIChannel *oldChannel, csp->ShouldLoad(policyType, // load type per nsIContentPolicy (uint32_t) newUri, // nsIURI nullptr, // nsIURI - nullptr, // nsISupports + requestContext, // nsISupports EmptyCString(), // ACString - MIME guess originalUri, // aExtra &aDecision); diff --git a/dom/security/nsCSPUtils.cpp b/dom/security/nsCSPUtils.cpp index 49832f8f4..71c8e3433 100644 --- a/dom/security/nsCSPUtils.cpp +++ b/dom/security/nsCSPUtils.cpp @@ -258,6 +258,9 @@ CSP_ContentTypeToDirective(nsContentPolicyType aType) case nsIContentPolicy::TYPE_CSP_REPORT: return nsIContentSecurityPolicy::NO_DIRECTIVE; + case nsIContentPolicy::TYPE_SAVEAS_DOWNLOAD: + return nsIContentSecurityPolicy::NO_DIRECTIVE; + // Fall through to error for all other directives default: MOZ_ASSERT(false, "Can not map nsContentPolicyType to CSPDirective"); diff --git a/dom/security/nsContentSecurityManager.cpp b/dom/security/nsContentSecurityManager.cpp index f329aa723..570730312 100644 --- a/dom/security/nsContentSecurityManager.cpp +++ b/dom/security/nsContentSecurityManager.cpp @@ -303,7 +303,7 @@ DoContentSecurityChecks(nsIChannel* aChannel, nsILoadInfo* aLoadInfo) case nsIContentPolicy::TYPE_DOCUMENT: { mimeTypeGuess = EmptyCString(); - requestingContext = aLoadInfo->LoadingNode(); + requestingContext = aLoadInfo->ContextForTopLevelLoad(); break; } @@ -471,6 +471,12 @@ DoContentSecurityChecks(nsIChannel* aChannel, nsILoadInfo* aLoadInfo) break; } + case nsIContentPolicy::TYPE_SAVEAS_DOWNLOAD: { + mimeTypeGuess = EmptyCString(); + requestingContext = aLoadInfo->LoadingNode(); + break; + } + default: // nsIContentPolicy::TYPE_INVALID MOZ_ASSERT(false, "can not perform security check without a valid contentType"); diff --git a/dom/security/nsMixedContentBlocker.cpp b/dom/security/nsMixedContentBlocker.cpp index 7d50a43a3..c03628da0 100644 --- a/dom/security/nsMixedContentBlocker.cpp +++ b/dom/security/nsMixedContentBlocker.cpp @@ -468,6 +468,13 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect, *aDecision = ACCEPT; return NS_OK; + // Creating insecure connections for a save-as link download is acceptable. + // This download is completely disconnected from the docShell, but still + // using the same loading principal. + case TYPE_SAVEAS_DOWNLOAD: + *aDecision = ACCEPT; + return NS_OK; + // Static display content is considered moderate risk for mixed content so // these will be blocked according to the mixed display preference case TYPE_IMAGE: diff --git a/dom/security/test/csp/file_nonce_redirector.sjs b/dom/security/test/csp/file_nonce_redirector.sjs new file mode 100644 index 000000000..21a8f4e9c --- /dev/null +++ b/dom/security/test/csp/file_nonce_redirector.sjs @@ -0,0 +1,25 @@ +// custom *.sjs file for +// Bug 1469150:Scripts with valid nonce get blocked if URL redirects. + +const URL_PATH = "example.com/tests/dom/security/test/csp/"; + +function handleRequest(request, response) { + response.setHeader("Cache-Control", "no-cache", false); + let queryStr = request.queryString; + + if (queryStr === "redirect") { + response.setStatusLine("1.1", 302, "Found"); + response.setHeader("Location", + "https://" + URL_PATH + "file_nonce_redirector.sjs?load", false); + return; + } + + if (queryStr === "load") { + response.setHeader("Content-Type", "application/javascript", false); + response.write("console.log('script loaded');"); + return; + } + + // we should never get here - return something unexpected + response.write("d'oh"); +} diff --git a/dom/security/test/csp/file_nonce_redirects.html b/dom/security/test/csp/file_nonce_redirects.html new file mode 100644 index 000000000..e29116490 --- /dev/null +++ b/dom/security/test/csp/file_nonce_redirects.html @@ -0,0 +1,23 @@ +<!DOCTYPE HTML> +<html> + <head> + <meta charset='utf-8'> + <meta http-equiv="Content-Security-Policy" content="script-src 'nonce-abcd1234'"> + <title>Bug 1469150:Scripts with valid nonce get blocked if URL redirects</title> + </head> +<body> + +<script nonce='abcd1234' id='redirectScript'></script> + +<script nonce='abcd1234' type='application/javascript'> + var redirectScript = document.getElementById('redirectScript'); + redirectScript.onload = function(e) { + window.parent.postMessage({result: 'script-loaded'}, '*'); + }; + redirectScript.onerror = function(e) { + window.parent.postMessage({result: 'script-blocked'}, '*'); + } + redirectScript.src = 'file_nonce_redirector.sjs?redirect'; +</script> +</body> +</html> diff --git a/dom/security/test/csp/mochitest.ini b/dom/security/test/csp/mochitest.ini index 33b112020..86b7fd0cd 100644 --- a/dom/security/test/csp/mochitest.ini +++ b/dom/security/test/csp/mochitest.ini @@ -88,6 +88,8 @@ support-files = file_shouldprocess.html file_nonce_source.html file_nonce_source.html^headers^ + file_nonce_redirects.html + file_nonce_redirector.sjs file_bug941404.html file_bug941404_xhr.html file_bug941404_xhr.html^headers^ @@ -245,6 +247,7 @@ skip-if = toolkit == 'android' # Times out, not sure why (bug 1008445) [test_frame_ancestors_ro.html] [test_policyuri_regression_from_multipolicy.html] [test_nonce_source.html] +[test_nonce_redirects.html] [test_bug941404.html] [test_form-action.html] [test_hash_source.html] diff --git a/dom/security/test/csp/test_nonce_redirects.html b/dom/security/test/csp/test_nonce_redirects.html new file mode 100644 index 000000000..f84fdcc7b --- /dev/null +++ b/dom/security/test/csp/test_nonce_redirects.html @@ -0,0 +1,47 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Bug 1469150:Scripts with valid nonce get blocked if URL redirects</title> + <!-- Including SimpleTest.js so we can use waitForExplicitFinish !--> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<iframe style="width:100%;" id="testframe"></iframe> + +<script class="testbody" type="text/javascript"> + +/* Description of the test: + * We load a script with a matching nonce, which redirects + * and we make sure that script is allowed. + */ + +SimpleTest.waitForExplicitFinish(); + +function finishTest() { + window.removeEventListener("message", receiveMessage); + SimpleTest.finish(); +} + +function checkResults(aResult) { + + if (aResult === "script-loaded") { + ok(true, "expected result: script loaded"); + } + else { + ok(false, "unexpected result: script blocked"); + } + finishTest(); +} + +window.addEventListener("message", receiveMessage); +function receiveMessage(event) { + checkResults(event.data.result); +} + +document.getElementById("testframe").src = "file_nonce_redirects.html"; + +</script> +</body> +</html> diff --git a/dom/tests/mochitest/fetch/test_request.js b/dom/tests/mochitest/fetch/test_request.js index 5be361a46..405767b50 100644 --- a/dom/tests/mochitest/fetch/test_request.js +++ b/dom/tests/mochitest/fetch/test_request.js @@ -152,12 +152,9 @@ function testHeaderGuard() { } function testMode() { - try { - var req = new Request("http://example.com", {mode: "navigate"}); - ok(false, "Creating a Request with navigate RequestMode should throw a TypeError"); - } catch(e) { - is(e.name, "TypeError", "Creating a Request with navigate RequestMode should throw a TypeError"); - } + var req = new Request("http://example.com", {mode: "navigate"}); + ok(true, "Creating a Request with navigate RequestMode should not throw."); + is(req.mode, "same-origin", "Request mode becomes same-origin"); } function testMethod() { diff --git a/dom/url/URL.cpp b/dom/url/URL.cpp index c8724c359..04f5ec137 100644 --- a/dom/url/URL.cpp +++ b/dom/url/URL.cpp @@ -1113,6 +1113,12 @@ public: return true; } + void + Dispatch(ErrorResult& aRv) + { + WorkerMainThreadRunnable::Dispatch(Terminating, aRv); + } + private: nsAString& mValue; GetterType mType; @@ -1213,6 +1219,12 @@ public: return mFailed; } + void + Dispatch(ErrorResult& aRv) + { + WorkerMainThreadRunnable::Dispatch(Terminating, aRv); + } + private: const nsString mValue; SetterType mType; @@ -1224,7 +1236,7 @@ already_AddRefed<URLWorker> FinishConstructor(JSContext* aCx, WorkerPrivate* aPrivate, ConstructorRunnable* aRunnable, ErrorResult& aRv) { - aRunnable->Dispatch(aRv); + aRunnable->Dispatch(Terminating, aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } @@ -1302,7 +1314,7 @@ URLWorker::CreateObjectURL(const GlobalObject& aGlobal, Blob& aBlob, RefPtr<CreateURLRunnable> runnable = new CreateURLRunnable(workerPrivate, blobImpl, aOptions, aResult); - runnable->Dispatch(aRv); + runnable->Dispatch(Terminating, aRv); if (NS_WARN_IF(aRv.Failed())) { return; } @@ -1325,7 +1337,7 @@ URLWorker::RevokeObjectURL(const GlobalObject& aGlobal, const nsAString& aUrl, RefPtr<RevokeURLRunnable> runnable = new RevokeURLRunnable(workerPrivate, aUrl); - runnable->Dispatch(aRv); + runnable->Dispatch(Terminating, aRv); if (NS_WARN_IF(aRv.Failed())) { return; } @@ -1348,7 +1360,7 @@ URLWorker::IsValidURL(const GlobalObject& aGlobal, const nsAString& aUrl, RefPtr<IsValidURLRunnable> runnable = new IsValidURLRunnable(workerPrivate, aUrl); - runnable->Dispatch(aRv); + runnable->Dispatch(Terminating, aRv); if (NS_WARN_IF(aRv.Failed())) { return false; } diff --git a/dom/webidl/Document.webidl b/dom/webidl/Document.webidl index 0b8c278fe..904b1fb77 100644 --- a/dom/webidl/Document.webidl +++ b/dom/webidl/Document.webidl @@ -277,8 +277,11 @@ partial interface Document { // https://w3c.github.io/page-visibility/#extensions-to-the-document-interface partial interface Document { + [Pref="dom.visibilityAPI.enabled"] readonly attribute boolean hidden; + [Pref="dom.visibilityAPI.enabled"] readonly attribute VisibilityState visibilityState; + [Pref="dom.visibilityAPI.enabled"] attribute EventHandler onvisibilitychange; }; diff --git a/dom/webidl/Element.webidl b/dom/webidl/Element.webidl index ca5f1b35c..97eb4ffe0 100644 --- a/dom/webidl/Element.webidl +++ b/dom/webidl/Element.webidl @@ -41,6 +41,8 @@ interface Element : Node { [Pure] DOMString? getAttributeNS(DOMString? namespace, DOMString localName); [Throws] + boolean toggleAttribute(DOMString name, optional boolean force); + [Throws] void setAttribute(DOMString name, DOMString value); [Throws] void setAttributeNS(DOMString? namespace, DOMString name, DOMString value); diff --git a/dom/webidl/Event.webidl b/dom/webidl/Event.webidl index f87dc195c..70a0ef513 100644 --- a/dom/webidl/Event.webidl +++ b/dom/webidl/Event.webidl @@ -17,6 +17,8 @@ interface Event { readonly attribute DOMString type; [Pure] readonly attribute EventTarget? target; + [Pure, BinaryName="target"] + readonly attribute EventTarget? srcElement; [Pure] readonly attribute EventTarget? currentTarget; diff --git a/dom/workers/RuntimeService.cpp b/dom/workers/RuntimeService.cpp index ad95d4896..e1910536f 100644 --- a/dom/workers/RuntimeService.cpp +++ b/dom/workers/RuntimeService.cpp @@ -587,7 +587,7 @@ ContentSecurityPolicyAllows(JSContext* aCx) new LogViolationDetailsRunnable(worker, fileName, lineNum); ErrorResult rv; - runnable->Dispatch(rv); + runnable->Dispatch(Killing, rv); if (NS_WARN_IF(rv.Failed())) { rv.SuppressException(); } diff --git a/dom/workers/ScriptLoader.cpp b/dom/workers/ScriptLoader.cpp index 46545e737..56b18441e 100644 --- a/dom/workers/ScriptLoader.cpp +++ b/dom/workers/ScriptLoader.cpp @@ -2118,12 +2118,16 @@ LoadAllScripts(WorkerPrivate* aWorkerPrivate, aWorkerPrivate->AssertIsOnWorkerThread(); NS_ASSERTION(!aLoadInfos.IsEmpty(), "Bad arguments!"); - AutoSyncLoopHolder syncLoop(aWorkerPrivate); + AutoSyncLoopHolder syncLoop(aWorkerPrivate, Terminating); + nsCOMPtr<nsIEventTarget> syncLoopTarget = syncLoop.GetEventTarget(); + if (!syncLoopTarget) { + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); + return; + } RefPtr<ScriptLoaderRunnable> loader = - new ScriptLoaderRunnable(aWorkerPrivate, syncLoop.EventTarget(), - aLoadInfos, aIsMainScript, aWorkerScriptType, - aRv); + new ScriptLoaderRunnable(aWorkerPrivate, syncLoopTarget, aLoadInfos, + aIsMainScript, aWorkerScriptType, aRv); NS_ASSERTION(aLoadInfos.IsEmpty(), "Should have swapped!"); @@ -2184,7 +2188,7 @@ ChannelFromScriptURLWorkerThread(JSContext* aCx, new ChannelGetterRunnable(aParent, aScriptURL, aChannel); ErrorResult rv; - getter->Dispatch(rv); + getter->Dispatch(Terminating, rv); if (rv.Failed()) { NS_ERROR("Failed to dispatch!"); return rv.StealNSResult(); diff --git a/dom/workers/WorkerNavigator.cpp b/dom/workers/WorkerNavigator.cpp index 682c7a22c..f79896881 100644 --- a/dom/workers/WorkerNavigator.cpp +++ b/dom/workers/WorkerNavigator.cpp @@ -152,7 +152,7 @@ WorkerNavigator::GetUserAgent(nsString& aUserAgent, ErrorResult& aRv) const RefPtr<GetUserAgentRunnable> runnable = new GetUserAgentRunnable(workerPrivate, aUserAgent); - runnable->Dispatch(aRv); + runnable->Dispatch(Terminating, aRv); } uint64_t diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp index 8848e881a..612090027 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -5446,10 +5446,18 @@ WorkerPrivate::CancelAllTimeouts() } already_AddRefed<nsIEventTarget> -WorkerPrivate::CreateNewSyncLoop() +WorkerPrivate::CreateNewSyncLoop(Status aFailStatus) { AssertIsOnWorkerThread(); + { + MutexAutoLock lock(mMutex); + + if (mStatus >= aFailStatus) { + return nullptr; + } + } + nsCOMPtr<nsIThreadInternal> thread = do_QueryInterface(NS_GetCurrentThread()); MOZ_ASSERT(thread); diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h index 28283bed7..20a530205 100644 --- a/dom/workers/WorkerPrivate.h +++ b/dom/workers/WorkerPrivate.h @@ -1442,8 +1442,11 @@ private: memcpy(aPreferences, mPreferences, WORKERPREF_COUNT * sizeof(bool)); } + // If the worker shutdown status is equal or greater then aFailStatus, this + // operation will fail and nullptr will be returned. See WorkerHolder.h for + // more information about the correct value to use. already_AddRefed<nsIEventTarget> - CreateNewSyncLoop(); + CreateNewSyncLoop(Status aFailStatus); bool RunCurrentSyncLoop(); @@ -1518,9 +1521,11 @@ class AutoSyncLoopHolder uint32_t mIndex; public: - explicit AutoSyncLoopHolder(WorkerPrivate* aWorkerPrivate) + // See CreateNewSyncLoop() for more information about the correct value to use + // for aFailStatus. + AutoSyncLoopHolder(WorkerPrivate* aWorkerPrivate, Status aFailStatus) : mWorkerPrivate(aWorkerPrivate) - , mTarget(aWorkerPrivate->CreateNewSyncLoop()) + , mTarget(aWorkerPrivate->CreateNewSyncLoop(aFailStatus)) , mIndex(aWorkerPrivate->mSyncLoopStack.Length() - 1) { aWorkerPrivate->AssertIsOnWorkerThread(); @@ -1528,7 +1533,7 @@ public: ~AutoSyncLoopHolder() { - if (mWorkerPrivate) { + if (mWorkerPrivate && mTarget) { mWorkerPrivate->AssertIsOnWorkerThread(); mWorkerPrivate->StopSyncLoop(mTarget, false); mWorkerPrivate->DestroySyncLoop(mIndex); @@ -1547,8 +1552,9 @@ public: } nsIEventTarget* - EventTarget() const + GetEventTarget() const { + // This can be null if CreateNewSyncLoop() fails. return mTarget; } }; diff --git a/dom/workers/WorkerRunnable.cpp b/dom/workers/WorkerRunnable.cpp index 1e16d7254..6bbe40f66 100644 --- a/dom/workers/WorkerRunnable.cpp +++ b/dom/workers/WorkerRunnable.cpp @@ -568,15 +568,20 @@ WorkerMainThreadRunnable::WorkerMainThreadRunnable(WorkerPrivate* aWorkerPrivate } void -WorkerMainThreadRunnable::Dispatch(ErrorResult& aRv) +WorkerMainThreadRunnable::Dispatch(Status aFailStatus, ErrorResult& aRv) { mWorkerPrivate->AssertIsOnWorkerThread(); TimeStamp startTime = TimeStamp::NowLoRes(); - AutoSyncLoopHolder syncLoop(mWorkerPrivate); + AutoSyncLoopHolder syncLoop(mWorkerPrivate, aFailStatus); - mSyncLoopTarget = syncLoop.EventTarget(); + mSyncLoopTarget = syncLoop.GetEventTarget(); + if (!mSyncLoopTarget) { + // SyncLoop creation can fail if the worker is shutting down. + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); + return; + } DebugOnly<nsresult> rv = mWorkerPrivate->DispatchToMainThread(this); MOZ_ASSERT(NS_SUCCEEDED(rv), @@ -621,7 +626,7 @@ bool WorkerCheckAPIExposureOnMainThreadRunnable::Dispatch() { ErrorResult rv; - WorkerMainThreadRunnable::Dispatch(rv); + WorkerMainThreadRunnable::Dispatch(Terminating, rv); bool ok = !rv.Failed(); rv.SuppressException(); return ok; diff --git a/dom/workers/WorkerRunnable.h b/dom/workers/WorkerRunnable.h index c65060f44..8249a8053 100644 --- a/dom/workers/WorkerRunnable.h +++ b/dom/workers/WorkerRunnable.h @@ -401,9 +401,12 @@ protected: public: // Dispatch the runnable to the main thread. If dispatch to main thread - // fails, or if the worker is shut down while dispatching, an error will be - // reported on aRv. In that case the error MUST be propagated out to script. - void Dispatch(ErrorResult& aRv); + // fails, or if the worker is in a state equal or greater of aFailStatus, an + // error will be reported on aRv. Normally you want to use 'Terminating' for + // aFailStatus, except if you want an infallible runnable. In this case, use + // 'Killing'. + // In that case the error MUST be propagated out to script. + void Dispatch(Status aFailStatus, ErrorResult& aRv); private: NS_IMETHOD Run() override; diff --git a/dom/workers/test/serviceworkers/fetch_event_worker.js b/dom/workers/test/serviceworkers/fetch_event_worker.js index 1caef71e8..895128e2c 100644 --- a/dom/workers/test/serviceworkers/fetch_event_worker.js +++ b/dom/workers/test/serviceworkers/fetch_event_worker.js @@ -148,28 +148,21 @@ onfetch = function(ev) { } else if (ev.request.url.includes("navigate.html")) { - var navigateModeCorrectlyChecked = false; var requests = [ // should not throw new Request(ev.request), new Request(ev.request, undefined), new Request(ev.request, null), new Request(ev.request, {}), new Request(ev.request, {someUnrelatedProperty: 42}), + new Request(ev.request, {method: "GET"}), ]; - try { - var request3 = new Request(ev.request, {method: "GET"}); // should throw - } catch(e) { - navigateModeCorrectlyChecked = requests[0].mode == "navigate"; - } - if (navigateModeCorrectlyChecked) { - ev.respondWith(Promise.resolve( - new Response("<script>window.frameElement.test_result = true;</script>", { - headers : { - "Content-Type": "text/html" - } - }) - )); - } + ev.respondWith(Promise.resolve( + new Response("<script>window.frameElement.test_result = true;</script>", { + headers : { + "Content-Type": "text/html" + } + }) + )); } else if (ev.request.url.includes("nonexistent_worker_script.js")) { diff --git a/dom/xbl/nsXBLBinding.cpp b/dom/xbl/nsXBLBinding.cpp index 6ae17c4c0..d9a2aacc5 100644 --- a/dom/xbl/nsXBLBinding.cpp +++ b/dom/xbl/nsXBLBinding.cpp @@ -1014,7 +1014,17 @@ nsXBLBinding::DoInitJSClass(JSContext *cx, NS_ENSURE_TRUE(xblScope, NS_ERROR_UNEXPECTED); JS::Rooted<JSObject*> parent_proto(cx); - if (!JS_GetPrototype(cx, obj, &parent_proto)) { + { + JS::RootedObject wrapped(cx, obj); + JSAutoCompartment ac(cx, xblScope); + if (!JS_WrapObject(cx, &wrapped)) { + return NS_ERROR_FAILURE; + } + if (!JS_GetPrototype(cx, wrapped, &parent_proto)) { + return NS_ERROR_FAILURE; + } + } + if (!JS_WrapObject(cx, &parent_proto)) { return NS_ERROR_FAILURE; } diff --git a/dom/xhr/XMLHttpRequestWorker.cpp b/dom/xhr/XMLHttpRequestWorker.cpp index e7193a279..c9e892f26 100644 --- a/dom/xhr/XMLHttpRequestWorker.cpp +++ b/dom/xhr/XMLHttpRequestWorker.cpp @@ -208,9 +208,9 @@ public: } void - Dispatch(ErrorResult& aRv) + Dispatch(Status aFailStatus, ErrorResult& aRv) { - WorkerMainThreadRunnable::Dispatch(aRv); + WorkerMainThreadRunnable::Dispatch(aFailStatus, aRv); if (NS_WARN_IF(aRv.Failed())) { return; } @@ -1633,11 +1633,10 @@ XMLHttpRequestWorker::ReleaseProxy(ReleaseType aType) new SyncTeardownRunnable(mWorkerPrivate, mProxy); mProxy = nullptr; - ErrorResult forAssertionsOnly; - runnable->Dispatch(forAssertionsOnly); - if (forAssertionsOnly.Failed()) { - NS_ERROR("Failed to dispatch teardown runnable!"); - } + IgnoredErrorResult forAssertionsOnly; + // This runnable _must_ be executed. + runnable->Dispatch(Dead, forAssertionsOnly); + MOZ_DIAGNOSTIC_ASSERT(!forAssertionsOnly.Failed()); } } } @@ -1804,8 +1803,12 @@ XMLHttpRequestWorker::SendInternal(SendRunnable* aRunnable, nsCOMPtr<nsIEventTarget> syncLoopTarget; bool isSyncXHR = mProxy->mIsSyncXHR; if (isSyncXHR) { - autoSyncLoop.emplace(mWorkerPrivate); - syncLoopTarget = autoSyncLoop->EventTarget(); + autoSyncLoop.emplace(mWorkerPrivate, Terminating); + syncLoopTarget = autoSyncLoop->GetEventTarget(); + if (!syncLoopTarget) { + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); + return; + } } mProxy->mOuterChannelId++; @@ -1815,7 +1818,7 @@ XMLHttpRequestWorker::SendInternal(SendRunnable* aRunnable, mStateData.mFlagSend = true; - aRunnable->Dispatch(aRv); + aRunnable->Dispatch(Terminating, aRv); if (aRv.Failed()) { // Dispatch() may have spun the event loop and we may have already unrooted. // If so we don't want autoUnpin to try again. @@ -1889,7 +1892,7 @@ XMLHttpRequestWorker::Open(const nsACString& aMethod, mTimeout, mResponseType); ++mProxy->mOpenCount; - runnable->Dispatch(aRv); + runnable->Dispatch(Terminating, aRv); if (aRv.Failed()) { if (mProxy && !--mProxy->mOpenCount) { ReleaseProxy(); @@ -1926,7 +1929,7 @@ XMLHttpRequestWorker::SetRequestHeader(const nsACString& aHeader, RefPtr<SetRequestHeaderRunnable> runnable = new SetRequestHeaderRunnable(mWorkerPrivate, mProxy, aHeader, aValue); - runnable->Dispatch(aRv); + runnable->Dispatch(Terminating, aRv); } void @@ -1949,7 +1952,7 @@ XMLHttpRequestWorker::SetTimeout(uint32_t aTimeout, ErrorResult& aRv) RefPtr<SetTimeoutRunnable> runnable = new SetTimeoutRunnable(mWorkerPrivate, mProxy, aTimeout); - runnable->Dispatch(aRv); + runnable->Dispatch(Terminating, aRv); } void @@ -1972,7 +1975,7 @@ XMLHttpRequestWorker::SetWithCredentials(bool aWithCredentials, ErrorResult& aRv RefPtr<SetWithCredentialsRunnable> runnable = new SetWithCredentialsRunnable(mWorkerPrivate, mProxy, aWithCredentials); - runnable->Dispatch(aRv); + runnable->Dispatch(Terminating, aRv); } void @@ -1997,7 +2000,7 @@ XMLHttpRequestWorker::SetMozBackgroundRequest(bool aBackgroundRequest, RefPtr<SetBackgroundRequestRunnable> runnable = new SetBackgroundRequestRunnable(mWorkerPrivate, mProxy, aBackgroundRequest); - runnable->Dispatch(aRv); + runnable->Dispatch(Terminating, aRv); } XMLHttpRequestUpload* @@ -2265,7 +2268,7 @@ XMLHttpRequestWorker::Abort(ErrorResult& aRv) mProxy->mOuterEventStreamId++; RefPtr<AbortRunnable> runnable = new AbortRunnable(mWorkerPrivate, mProxy); - runnable->Dispatch(aRv); + runnable->Dispatch(Terminating, aRv); } void @@ -2288,7 +2291,7 @@ XMLHttpRequestWorker::GetResponseHeader(const nsACString& aHeader, RefPtr<GetResponseHeaderRunnable> runnable = new GetResponseHeaderRunnable(mWorkerPrivate, mProxy, aHeader, responseHeader); - runnable->Dispatch(aRv); + runnable->Dispatch(Terminating, aRv); if (aRv.Failed()) { return; } @@ -2314,7 +2317,7 @@ XMLHttpRequestWorker::GetAllResponseHeaders(nsACString& aResponseHeaders, nsCString responseHeaders; RefPtr<GetAllResponseHeadersRunnable> runnable = new GetAllResponseHeadersRunnable(mWorkerPrivate, mProxy, responseHeaders); - runnable->Dispatch(aRv); + runnable->Dispatch(Terminating, aRv); if (aRv.Failed()) { return; } @@ -2346,7 +2349,7 @@ XMLHttpRequestWorker::OverrideMimeType(const nsAString& aMimeType, ErrorResult& RefPtr<OverrideMimeTypeRunnable> runnable = new OverrideMimeTypeRunnable(mWorkerPrivate, mProxy, aMimeType); - runnable->Dispatch(aRv); + runnable->Dispatch(Terminating, aRv); } void @@ -2382,7 +2385,7 @@ XMLHttpRequestWorker::SetResponseType(XMLHttpRequestResponseType aResponseType, RefPtr<SetResponseTypeRunnable> runnable = new SetResponseTypeRunnable(mWorkerPrivate, mProxy, aResponseType); - runnable->Dispatch(aRv); + runnable->Dispatch(Terminating, aRv); if (aRv.Failed()) { return; } |