diff --git a/caps/nsScriptSecurityManager.cpp b/caps/nsScriptSecurityManager.cpp index cafb5e4dc9..2ddd69395d 100644 --- a/caps/nsScriptSecurityManager.cpp +++ b/caps/nsScriptSecurityManager.cpp @@ -297,6 +297,15 @@ nsScriptSecurityManager::AppStatusForPrincipal(nsIPrincipal *aPrin) : nsIPrincipal::APP_STATUS_NOT_INSTALLED; } +/* + * GetChannelResultPrincipal will return the principal that the resource + * returned by this channel will use. For example, if the resource is in + * a sandbox, it will return the nullprincipal. If the resource is forced + * to inherit principal, it will return the principal of its parent. If + * the load doesn't require sandboxing or inheriting, it will return the same + * principal as GetChannelURIPrincipal. Namely the principal of the URI + * that is being loaded. + */ NS_IMETHODIMP nsScriptSecurityManager::GetChannelResultPrincipal(nsIChannel* aChannel, nsIPrincipal** aPrincipal) @@ -331,6 +340,17 @@ nsScriptSecurityManager::GetChannelResultPrincipal(nsIChannel* aChannel, return GetChannelURIPrincipal(aChannel, aPrincipal); } +/* The principal of the URI that this channel is loading. This is never + * affected by things like sandboxed loads, or loads where we forcefully + * inherit the principal. Think of this as the principal of the server + * which this channel is loading from. Most callers should use + * GetChannelResultPrincipal instead of GetChannelURIPrincipal. Only + * call GetChannelURIPrincipal if you are sure that you want the + * principal that matches the uri, even in cases when the load is + * sandboxed or when the load could be a blob or data uri (i.e even when + * you encounter loads that may or may not be sandboxed and loads + * that may or may not inherit)." + */ NS_IMETHODIMP nsScriptSecurityManager::GetChannelURIPrincipal(nsIChannel* aChannel, nsIPrincipal** aPrincipal) diff --git a/dom/base/nsContentSink.cpp b/dom/base/nsContentSink.cpp index 804ad79d39..3f60173a89 100644 --- a/dom/base/nsContentSink.cpp +++ b/dom/base/nsContentSink.cpp @@ -450,6 +450,9 @@ nsContentSink::ProcessLinkHeader(const nsAString& aLinkData) nsAutoString type; nsAutoString media; nsAutoString anchor; + nsAutoString crossOrigin; + + crossOrigin.SetIsVoid(true); // copy to work buffer nsAutoString stringList(aLinkData); @@ -624,6 +627,12 @@ nsContentSink::ProcessLinkHeader(const nsAString& aLinkData) anchor = value; anchor.StripWhitespace(); } + } else if (attr.LowerCaseEqualsLiteral("crossorigin")) { + if (crossOrigin.IsVoid()) { + crossOrigin.SetIsVoid(false); + crossOrigin = value; + crossOrigin.StripWhitespace(); + } } } } @@ -637,7 +646,7 @@ nsContentSink::ProcessLinkHeader(const nsAString& aLinkData) rv = ProcessLink(anchor, href, rel, // prefer RFC 5987 variant over non-I18zed version titleStar.IsEmpty() ? title : titleStar, - type, media); + type, media, crossOrigin); } href.Truncate(); @@ -646,6 +655,7 @@ nsContentSink::ProcessLinkHeader(const nsAString& aLinkData) type.Truncate(); media.Truncate(); anchor.Truncate(); + crossOrigin.SetIsVoid(true); seenParameters = false; } @@ -658,7 +668,7 @@ nsContentSink::ProcessLinkHeader(const nsAString& aLinkData) rv = ProcessLink(anchor, href, rel, // prefer RFC 5987 variant over non-I18zed version titleStar.IsEmpty() ? title : titleStar, - type, media); + type, media, crossOrigin); } return rv; @@ -668,7 +678,8 @@ nsContentSink::ProcessLinkHeader(const nsAString& aLinkData) nsresult nsContentSink::ProcessLink(const nsSubstring& aAnchor, const nsSubstring& aHref, const nsSubstring& aRel, const nsSubstring& aTitle, - const nsSubstring& aType, const nsSubstring& aMedia) + const nsSubstring& aType, const nsSubstring& aMedia, + const nsSubstring& aCrossOrigin) { uint32_t linkTypes = nsStyleLinkElement::ParseLinkTypes(aRel, mDocument->NodePrincipal()); @@ -692,7 +703,7 @@ nsContentSink::ProcessLink(const nsSubstring& aAnchor, const nsSubstring& aHref, } if (!aHref.IsEmpty() && (linkTypes & nsStyleLinkElement::ePRECONNECT)) { - Preconnect(aHref); + Preconnect(aHref, aCrossOrigin); } // is it a stylesheet link? @@ -873,7 +884,7 @@ nsContentSink::PrefetchDNS(const nsAString &aHref) } void -nsContentSink::Preconnect(const nsAString &aHref) +nsContentSink::Preconnect(const nsAString& aHref, const nsAString& aCrossOrigin) { // construct URI using document charset const nsACString& charset = mDocument->GetDocumentCharacterSet(); @@ -883,7 +894,7 @@ nsContentSink::Preconnect(const nsAString &aHref) mDocument->GetDocBaseURI()); if (uri && mDocument) { - mDocument->MaybePreconnect(uri); + mDocument->MaybePreconnect(uri, dom::Element::StringToCORSMode(aCrossOrigin)); } } diff --git a/dom/base/nsContentSink.h b/dom/base/nsContentSink.h index bf7b1ae100..f66359065b 100644 --- a/dom/base/nsContentSink.h +++ b/dom/base/nsContentSink.h @@ -151,7 +151,7 @@ protected: nsresult ProcessLink(const nsSubstring& aAnchor, const nsSubstring& aHref, const nsSubstring& aRel, const nsSubstring& aTitle, const nsSubstring& aType, - const nsSubstring& aMedia); + const nsSubstring& aMedia, const nsSubstring& aCrossOrigin); virtual nsresult ProcessStyleLink(nsIContent* aElement, const nsSubstring& aHref, @@ -225,7 +225,7 @@ public: // For Preconnect() aHref can either be the usual // URI format or of the form "//www.hostname.com" without a scheme. - void Preconnect(const nsAString &aHref); + void Preconnect(const nsAString& aHref, const nsAString& aCrossOrigin); protected: // Tries to scroll to the URI's named anchor. Once we've successfully diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index 38dca17d94..620cb278dc 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -7973,4 +7973,16 @@ nsContentUtils::FirePageShowEvent(nsIDocShellTreeItem* aItem, if (doc->IsShowing() == aFireIfShowing) { doc->OnPageShow(true, aChromeEventHandler); } -} \ No newline at end of file +} +/* static */ +already_AddRefed +nsContentUtils::GetWindowRoot(nsIDocument* aDoc) +{ + if (aDoc) { + nsPIDOMWindow* win = aDoc->GetWindow(); + if (win) { + return win->GetTopWindowRoot(); + } + } + return nullptr; +} diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h index eb55d356ef..6eb4d7702d 100644 --- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -99,6 +99,7 @@ class nsViewportInfo; class nsWrapperCache; class nsAttrValue; class nsITransferable; +class nsPIWindowRoot; struct JSPropertyDescriptor; struct JSRuntime; @@ -2480,6 +2481,8 @@ public: static void FirePageHideEvent(nsIDocShellTreeItem* aItem, mozilla::dom::EventTarget* aChromeEventHandler); + static already_AddRefed GetWindowRoot(nsIDocument* aDoc); + private: static bool InitializeEventTable(); diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index 978a9f913d..d872a9b593 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -1547,6 +1547,9 @@ nsIDocument::nsIDocument() mAllowDNSPrefetch(true), mIsBeingUsedAsImage(false), mHasLinksToUpdate(false), + mFontFaceSetDirty(true), + mGetUserFontSetCalled(false), + mPostedFlushUserFontSet(false), mPartID(0), mDidFireDOMContentLoaded(true), mUserHasInteracted(false) @@ -1930,6 +1933,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsDocument) // Traverse all nsIDocument pointer members. NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSecurityInfo) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDisplayDocument) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFontFaceSet) // Traverse all nsDocument nsCOMPtrs. NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParser) @@ -2059,6 +2063,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument) NS_IMPL_CYCLE_COLLECTION_UNLINK(mMasterDocument) NS_IMPL_CYCLE_COLLECTION_UNLINK(mImportManager) NS_IMPL_CYCLE_COLLECTION_UNLINK(mSubImportLinks) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mFontFaceSet) tmp->mParentDocument = nullptr; @@ -3857,6 +3862,9 @@ nsDocument::doCreateShell(nsPresContext* aContext, MaybeRescheduleAnimationFrameNotifications(); + // Now that we have a shell, we might have @font-face rules. + RebuildUserFontSet(); + return shell.forget(); } @@ -3943,6 +3951,10 @@ nsDocument::DeleteShell() // no longer intend to paint. mImageTracker.EnumerateRead(RequestDiscardEnumerator, nullptr); + // Now that we no longer have a shell, we need to forget about any FontFace + // objects for @font-face rules that came from the style set. + RebuildUserFontSet(); + mPresShell = nullptr; } @@ -9745,8 +9757,26 @@ nsDocument::MaybePreLoadImage(nsIURI* uri, const nsAString &aCrossOriginAttr, } void -nsDocument::MaybePreconnect(nsIURI* uri) +nsDocument::MaybePreconnect(nsIURI* aOrigURI, mozilla::CORSMode aCORSMode) { + nsCOMPtr uri; + if (NS_FAILED(aOrigURI->Clone(getter_AddRefs(uri)))) { + return; + } + + // The URI created here is used in 2 contexts. One is nsISpeculativeConnect + // which ignores the path and uses only the origin. The other is for the + // document mPreloadedPreconnects de-duplication hash. Anonymous vs + // non-Anonymous preconnects create different connections on the wire and + // therefore should not be considred duplicates of each other and we + // normalize the path before putting it in the hash to accomplish that. + + if (aCORSMode == CORS_ANONYMOUS) { + uri->SetPath(NS_LITERAL_CSTRING("/anonymous")); + } else { + uri->SetPath(NS_LITERAL_CSTRING("/")); + } + if (mPreloadedPreconnects.Contains(uri)) { return; } @@ -9758,7 +9788,11 @@ nsDocument::MaybePreconnect(nsIURI* uri) return; } - speculator->SpeculativeConnect(uri, nullptr); + if (aCORSMode == CORS_ANONYMOUS) { + speculator->SpeculativeAnonymousConnect(uri, nullptr); + } else { + speculator->SpeculativeConnect(uri, nullptr); + } } void @@ -12993,22 +13027,124 @@ nsAutoSyncOperation::~nsAutoSyncOperation() nsContentUtils::SetMicroTaskLevel(mMicroTaskLevel); } -FontFaceSet* -nsIDocument::GetFonts(ErrorResult& aRv) +gfxUserFontSet* +nsIDocument::GetUserFontSet() { - nsIPresShell* shell = GetShell(); - if (!shell) { - aRv.Throw(NS_ERROR_FAILURE); + // We want to initialize the user font set lazily the first time the + // user asks for it, rather than building it too early and forcing + // rule cascade creation. Thus we try to enforce the invariant that + // we *never* build the user font set until the first call to + // GetUserFontSet. However, once it's been requested, we can't wait + // for somebody to call GetUserFontSet in order to rebuild it (see + // comments below in RebuildUserFontSet for why). +#ifdef DEBUG + bool userFontSetGottenBefore = mGetUserFontSetCalled; +#endif + // Set mGetUserFontSetCalled up front, so that FlushUserFontSet will actually + // flush. + mGetUserFontSetCalled = true; + if (mFontFaceSetDirty) { + // If this assertion fails, and there have actually been changes to + // @font-face rules, then we will call StyleChangeReflow in + // FlushUserFontSet. If we're in the middle of reflow, + // that's a bad thing to do, and the caller was responsible for + // flushing first. If we're not (e.g., in frame construction), it's + // ok. + NS_ASSERTION(!userFontSetGottenBefore || + !GetShell() || + !GetShell()->IsReflowLocked(), + "FlushUserFontSet should have been called first"); + FlushUserFontSet(); + } + + if (!mFontFaceSet) { return nullptr; } - nsPresContext* presContext = shell->GetPresContext(); - if (!presContext) { - aRv.Throw(NS_ERROR_FAILURE); - return nullptr; + return mFontFaceSet->GetUserFontSet(); +} + +void +nsIDocument::FlushUserFontSet() +{ + if (!mGetUserFontSetCalled) { + return; // No one cares about this font set yet, but we want to be careful + // to not unset our mFontFaceSetDirty bit, so when someone really + // does we'll create it. } - return presContext->Fonts(); + if (mFontFaceSetDirty) { + if (gfxPlatform::GetPlatform()->DownloadableFontsEnabled()) { + nsTArray rules; + nsIPresShell* shell = GetShell(); + if (shell && !shell->StyleSet()->AppendFontFaceRules(rules)) { + return; + } + + bool changed = false; + + if (!mFontFaceSet && !rules.IsEmpty()) { + nsCOMPtr window = do_QueryInterface(GetScopeObject()); + mFontFaceSet = new FontFaceSet(window, this); + } + if (mFontFaceSet) { + changed = mFontFaceSet->UpdateRules(rules); + } + + // We need to enqueue a style change reflow (for later) to + // reflect that we're modifying @font-face rules. (However, + // without a reflow, nothing will happen to start any downloads + // that are needed.) + if (changed && shell) { + nsPresContext* presContext = shell->GetPresContext(); + if (presContext) { + presContext->UserFontSetUpdated(); + } + } + } + + mFontFaceSetDirty = false; + } +} + +void +nsIDocument::RebuildUserFontSet() +{ + if (!mGetUserFontSetCalled) { + // We want to lazily build the user font set the first time it's + // requested (so we don't force creation of rule cascades too + // early), so don't do anything now. + return; + } + + mFontFaceSetDirty = true; + SetNeedStyleFlush(); + + // Somebody has already asked for the user font set, so we need to + // post an event to rebuild it. Setting the user font set to be dirty + // and lazily rebuilding it isn't sufficient, since it is only the act + // of rebuilding it that will trigger the style change reflow that + // calls GetUserFontSet. (This reflow causes rebuilding of text runs, + // which starts font loads, whose completion causes another style + // change reflow). + if (!mPostedFlushUserFontSet) { + nsCOMPtr ev = + NS_NewRunnableMethod(this, &nsIDocument::HandleRebuildUserFontSet); + if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev))) { + mPostedFlushUserFontSet = true; + } + } +} + +FontFaceSet* +nsIDocument::Fonts() +{ + if (!mFontFaceSet) { + nsCOMPtr window = do_QueryInterface(GetScopeObject()); + mFontFaceSet = new FontFaceSet(window, this); + GetUserFontSet(); // this will cause the user font set to be created/updated + } + return mFontFaceSet; } Selection* diff --git a/dom/base/nsDocument.h b/dom/base/nsDocument.h index 9d5cb36c22..a9e0a35726 100644 --- a/dom/base/nsDocument.h +++ b/dom/base/nsDocument.h @@ -1118,7 +1118,8 @@ public: ReferrerPolicy aReferrerPolicy) override; virtual void ForgetImagePreload(nsIURI* aURI) override; - virtual void MaybePreconnect(nsIURI* uri) override; + virtual void MaybePreconnect(nsIURI* uri, + mozilla::CORSMode aCORSMode) override; virtual void PreloadStyle(nsIURI* uri, const nsAString& charset, const nsAString& aCrossOriginAttr, diff --git a/dom/base/nsIDocument.h b/dom/base/nsIDocument.h index 7285047fed..34edc95062 100644 --- a/dom/base/nsIDocument.h +++ b/dom/base/nsIDocument.h @@ -32,8 +32,10 @@ #include "nsExpirationTracker.h" #include "nsClassHashtable.h" #include "prclist.h" +#include "mozilla/CORSMode.h" #include // for member +class gfxUserFontSet; class imgIRequest; class nsAString; class nsBindingManager; @@ -155,8 +157,8 @@ struct FullScreenOptions { } // namespace mozilla #define NS_IDOCUMENT_IID \ -{ 0x0b78eabe, 0x8b94, 0x4ea1, \ - { 0x93, 0x31, 0x5d, 0x48, 0xe8, 0x3a, 0xda, 0x95 } } +{ 0x21bbd52a, 0xc2d2, 0x4b2f, \ + { 0xbc, 0x6c, 0xc9, 0x52, 0xbe, 0x23, 0x6b, 0x19 } } // Enum for requesting a particular type of document when creating a doc enum DocumentFlavor { @@ -2078,7 +2080,8 @@ public: /** * Called by Parser for link rel=preconnect */ - virtual void MaybePreconnect(nsIURI* uri) = 0; + virtual void MaybePreconnect(nsIURI* uri, + mozilla::CORSMode aCORSMode) = 0; enum DocumentTheme { Doc_Theme_Uninitialized, // not determined yet @@ -2604,8 +2607,13 @@ public: } } + gfxUserFontSet* GetUserFontSet(); + void FlushUserFontSet(); + void RebuildUserFontSet(); // asynchronously + mozilla::dom::FontFaceSet* GetFonts() { return mFontFaceSet; } + // FontFaceSource - mozilla::dom::FontFaceSet* GetFonts(mozilla::ErrorResult& aRv); + mozilla::dom::FontFaceSet* Fonts(); bool DidFireDOMContentLoaded() const { return mDidFireDOMContentLoaded; } @@ -2664,6 +2672,11 @@ protected: mozilla::dom::XPathEvaluator* XPathEvaluator(); + void HandleRebuildUserFontSet() { + mPostedFlushUserFontSet = false; + FlushUserFontSet(); + } + nsCString mReferrer; nsString mLastModified; @@ -2720,6 +2733,9 @@ protected: // Our cached .children collection nsCOMPtr mChildrenCollection; + // container for per-context fonts (downloadable, SVG, etc.) + nsRefPtr mFontFaceSet; + // Compatibility mode nsCompatibility mCompatMode; @@ -2852,6 +2868,15 @@ protected: bool mIsLinkUpdateRegistrationsForbidden; #endif + // Is the current mFontFaceSet valid? + bool mFontFaceSetDirty; + + // Has GetUserFontSet() been called? + bool mGetUserFontSetCalled; + + // Do we currently have an event posted to call FlushUserFontSet? + bool mPostedFlushUserFontSet; + enum Type { eUnknown, // should never be used eHTML, diff --git a/dom/base/nsObjectLoadingContent.cpp b/dom/base/nsObjectLoadingContent.cpp index 3ceb0b9a4b..756a8d7de7 100644 --- a/dom/base/nsObjectLoadingContent.cpp +++ b/dom/base/nsObjectLoadingContent.cpp @@ -20,6 +20,7 @@ #include "nsIDOMHTMLObjectElement.h" #include "nsIDOMHTMLAppletElement.h" #include "nsIExternalProtocolHandler.h" +#include "nsIHttpChannelInternal.h" #include "nsIObjectFrame.h" #include "nsIPermissionManager.h" #include "nsPluginHost.h" @@ -2517,6 +2518,13 @@ nsObjectLoadingContent::OpenChannel() scriptChannel->SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL); } + nsCOMPtr internalChannel = do_QueryInterface(httpChan); + if (internalChannel) { + // Bug 1168676. object/embed tags are not allowed to be intercepted by + // ServiceWorkers. + internalChannel->ForceNoIntercept(); + } + // AsyncOpen can fail if a file does not exist. rv = chan->AsyncOpen(shim, nullptr); NS_ENSURE_SUCCESS(rv, rv); diff --git a/dom/base/nsPIWindowRoot.h b/dom/base/nsPIWindowRoot.h index e37802f67f..f040318384 100644 --- a/dom/base/nsPIWindowRoot.h +++ b/dom/base/nsPIWindowRoot.h @@ -9,14 +9,21 @@ #include "nsISupports.h" #include "mozilla/dom/EventTarget.h" +#include "nsWeakReference.h" class nsPIDOMWindow; class nsIControllers; class nsIController; +namespace mozilla { +namespace dom { +class TabParent; +} +} + #define NS_IWINDOWROOT_IID \ -{ 0x728a2682, 0x55c0, 0x4860, \ - { 0x82, 0x6b, 0x0c, 0x30, 0x0a, 0xac, 0xaa, 0x60 } } +{ 0x238edca0, 0xb30d, 0x46d3, \ + { 0xb2, 0x6a, 0x17, 0xb6, 0x21, 0x28, 0x89, 0x7e } } class nsPIWindowRoot : public mozilla::dom::EventTarget { @@ -38,6 +45,15 @@ public: virtual void SetParentTarget(mozilla::dom::EventTarget* aTarget) = 0; virtual mozilla::dom::EventTarget* GetParentTarget() = 0; + + // Stores a weak reference to the browser. + virtual void AddBrowser(mozilla::dom::TabParent* aBrowser) = 0; + virtual void RemoveBrowser(mozilla::dom::TabParent* aBrowser) = 0; + + typedef void (*BrowserEnumerator)(mozilla::dom::TabParent* aTab, void* aArg); + + // Enumerate all stored browsers that for which the weak reference is valid. + virtual void EnumerateBrowsers(BrowserEnumerator aEnumFunc, void* aArg) = 0; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsPIWindowRoot, NS_IWINDOWROOT_IID) diff --git a/dom/base/nsWindowRoot.cpp b/dom/base/nsWindowRoot.cpp index 7b85a089a7..c5d7e231b7 100644 --- a/dom/base/nsWindowRoot.cpp +++ b/dom/base/nsWindowRoot.cpp @@ -24,6 +24,7 @@ #include "nsIController.h" #include "xpcpublic.h" #include "nsCycleCollectionParticipant.h" +#include "mozilla/dom/TabParent.h" #ifdef MOZ_XUL #include "nsIDOMXULElement.h" @@ -385,6 +386,46 @@ nsWindowRoot::WrapObject(JSContext* aCx, JS::Handle aGivenProto) return mozilla::dom::WindowRootBinding::Wrap(aCx, this, aGivenProto); } +void +nsWindowRoot::AddBrowser(mozilla::dom::TabParent* aBrowser) +{ + nsWeakPtr weakBrowser = do_GetWeakReference(static_cast(aBrowser)); + mWeakBrowsers.PutEntry(weakBrowser); +} + +void +nsWindowRoot::RemoveBrowser(mozilla::dom::TabParent* aBrowser) +{ + nsWeakPtr weakBrowser = do_GetWeakReference(static_cast(aBrowser)); + mWeakBrowsers.RemoveEntry(weakBrowser); +} + +static PLDHashOperator +WeakBrowserEnumFunc(nsRefPtrHashKey* aKey, void* aArg) +{ + nsTArray>* tabParents = + static_cast>*>(aArg); + nsCOMPtr tabParent(do_QueryReferent((*aKey).GetKey())); + TabParent* tab = TabParent::GetFrom(tabParent); + if (tab) { + tabParents->AppendElement(tab); + } + return PL_DHASH_NEXT; +} + +void +nsWindowRoot::EnumerateBrowsers(BrowserEnumerator aEnumFunc, void* aArg) +{ + // Collect strong references to all browsers in a separate array in + // case aEnumFunc alters mWeakBrowsers. + nsTArray> tabParents; + mWeakBrowsers.EnumerateEntries(WeakBrowserEnumFunc, &tabParents); + + for (uint32_t i = 0; i < tabParents.Length(); ++i) { + aEnumFunc(tabParents[i], aArg); + } +} + /////////////////////////////////////////////////////////////////////////////////// already_AddRefed diff --git a/dom/base/nsWindowRoot.h b/dom/base/nsWindowRoot.h index 49686ad3ee..7113399709 100644 --- a/dom/base/nsWindowRoot.h +++ b/dom/base/nsWindowRoot.h @@ -69,6 +69,10 @@ public: NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsWindowRoot, nsIDOMEventTarget) + virtual void AddBrowser(mozilla::dom::TabParent* aBrowser); + virtual void RemoveBrowser(mozilla::dom::TabParent* aBrowser); + virtual void EnumerateBrowsers(BrowserEnumerator aEnumFunc, void *aArg); + protected: virtual ~nsWindowRoot(); @@ -84,6 +88,10 @@ protected: nsCOMPtr mPopupNode; // [OWNER] nsCOMPtr mParent; + + // The TabParents that are currently registered with this top-level window. + typedef nsTHashtable> WeakBrowserTable; + WeakBrowserTable mWeakBrowsers; }; extern already_AddRefed diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index a5113329db..f0affd086a 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -497,6 +497,10 @@ DOMInterfaces = { 'wrapperCache': False, }, +'FontFaceSet': { + 'implicitJSContext': [ 'load' ], +}, + 'FontFaceSetIterator': { 'wrapperCache': False, }, diff --git a/dom/html/HTMLLinkElement.cpp b/dom/html/HTMLLinkElement.cpp index 2b2422227d..d927b334cf 100644 --- a/dom/html/HTMLLinkElement.cpp +++ b/dom/html/HTMLLinkElement.cpp @@ -316,7 +316,7 @@ HTMLLinkElement::UpdatePreconnect() if (owner) { nsCOMPtr uri = GetHrefURI(); if (uri) { - owner->MaybePreconnect(uri); + owner->MaybePreconnect(uri, GetCORSMode()); } } } diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index d21f5c81b7..236f28e6df 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -334,9 +334,27 @@ TabParent::SetOwnerElement(Element* aElement) // If we held previous content then unregister for its events. RemoveWindowListeners(); + // If we change top-level documents then we need to change our + // registration with them. + nsRefPtr curTopLevelWin, newTopLevelWin; + if (mFrameElement) { + curTopLevelWin = nsContentUtils::GetWindowRoot(mFrameElement->OwnerDoc()); + } + if (aElement) { + newTopLevelWin = nsContentUtils::GetWindowRoot(aElement->OwnerDoc()); + } + bool isSameTopLevelWin = curTopLevelWin == newTopLevelWin; + if (curTopLevelWin && !isSameTopLevelWin) { + curTopLevelWin->RemoveBrowser(this); + } + // Update to the new content, and register to listen for events from it. mFrameElement = aElement; + if (newTopLevelWin && !isSameTopLevelWin) { + newTopLevelWin->AddBrowser(this); + } + AddWindowListeners(); TryCacheDPIAndScale(); } @@ -1003,6 +1021,7 @@ TabParent::UIResolutionChanged() // TryCacheDPIAndScale()'s cache is keyed off of // mDPI being greater than 0, so this invalidates it. mDPI = -1; + TryCacheDPIAndScale(); unused << SendUIResolutionChanged(); } } diff --git a/dom/media/MediaDecoderReader.cpp b/dom/media/MediaDecoderReader.cpp index f5ba3bdccb..e918f93f78 100644 --- a/dom/media/MediaDecoderReader.cpp +++ b/dom/media/MediaDecoderReader.cpp @@ -60,14 +60,18 @@ public: size_t mSize; }; -MediaDecoderReader::MediaDecoderReader(AbstractMediaDecoder* aDecoder) +MediaDecoderReader::MediaDecoderReader(AbstractMediaDecoder* aDecoder, + MediaTaskQueue* aBorrowedTaskQueue) : mAudioCompactor(mAudioQueue) , mDecoder(aDecoder) + , mTaskQueue(aBorrowedTaskQueue ? aBorrowedTaskQueue + : new MediaTaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK), + /* aSupportsTailDispatch = */ true)) , mIgnoreAudioOutputFormat(false) , mStartTime(-1) , mHitAudioDecodeError(false) , mShutdown(false) - , mTaskQueueIsBorrowed(false) + , mTaskQueueIsBorrowed(!!aBorrowedTaskQueue) , mAudioDiscontinuity(false) , mVideoDiscontinuity(false) { @@ -330,19 +334,6 @@ MediaDecoderReader::RequestAudioData() return p; } -MediaTaskQueue* -MediaDecoderReader::EnsureTaskQueue() -{ - if (!mTaskQueue) { - MOZ_ASSERT(!mTaskQueueIsBorrowed); - RefPtr pool(GetMediaThreadPool(MediaThreadType::PLAYBACK)); - MOZ_DIAGNOSTIC_ASSERT(pool); - mTaskQueue = new MediaTaskQueue(pool.forget()); - } - - return mTaskQueue; -} - void MediaDecoderReader::BreakCycles() { diff --git a/dom/media/MediaDecoderReader.h b/dom/media/MediaDecoderReader.h index 1defdfd4f1..ef7bd661ba 100644 --- a/dom/media/MediaDecoderReader.h +++ b/dom/media/MediaDecoderReader.h @@ -79,7 +79,7 @@ public: // The caller must ensure that Shutdown() is called before aDecoder is // destroyed. - explicit MediaDecoderReader(AbstractMediaDecoder* aDecoder); + explicit MediaDecoderReader(AbstractMediaDecoder* aDecoder, MediaTaskQueue* aBorrowedTaskQueue = nullptr); // Initializes the reader, returns NS_OK on success, or NS_ERROR_FAILURE // on failure. @@ -107,18 +107,9 @@ public: // thread. virtual nsRefPtr Shutdown(); - MediaTaskQueue* EnsureTaskQueue(); - virtual bool OnTaskQueue() { - return !GetTaskQueue() || GetTaskQueue()->IsCurrentThreadIn(); - } - - void SetBorrowedTaskQueue(MediaTaskQueue* aTaskQueue) - { - MOZ_ASSERT(!mTaskQueue && aTaskQueue); - mTaskQueue = aTaskQueue; - mTaskQueueIsBorrowed = true; + return GetTaskQueue()->IsCurrentThreadIn(); } // Resets all state related to decoding, emptying all buffers etc. @@ -323,6 +314,9 @@ protected: // Reference to the owning decoder object. AbstractMediaDecoder* mDecoder; + // Decode task queue. + nsRefPtr mTaskQueue; + // Stores presentation info required for playback. MediaInfo mInfo; @@ -356,7 +350,6 @@ private: MediaPromiseHolder mBaseAudioPromise; MediaPromiseHolder mBaseVideoPromise; - nsRefPtr mTaskQueue; bool mTaskQueueIsBorrowed; // Flags whether a the next audio/video sample comes after a "gap" or diff --git a/dom/media/MediaDecoderStateMachine.cpp b/dom/media/MediaDecoderStateMachine.cpp index bd4f3a46c5..d04ce7f840 100644 --- a/dom/media/MediaDecoderStateMachine.cpp +++ b/dom/media/MediaDecoderStateMachine.cpp @@ -180,7 +180,7 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder, bool aRealTime) : mDecoder(aDecoder), mTaskQueue(new MediaTaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK), - /* aAssertTailDispatch = */ true)), + /* aSupportsTailDispatch = */ true)), mWatchManager(this, mTaskQueue), mRealTime(aRealTime), mDispatchedStateMachine(false), @@ -284,8 +284,6 @@ MediaDecoderStateMachine::~MediaDecoderStateMachine() { MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread."); MOZ_COUNT_DTOR(MediaDecoderStateMachine); - NS_ASSERTION(!mPendingWakeDecoder.get(), - "WakeDecoder should have been revoked already"); mReader = nullptr; @@ -461,8 +459,6 @@ void MediaDecoderStateMachine::SendStreamData() AudioSegment* audio = new AudioSegment(); mediaStream->AddAudioTrack(audioTrackId, mInfo.mAudio.mRate, 0, audio, SourceMediaStream::ADDTRACK_QUEUED); - stream->mStream->DispatchWhenNotEnoughBuffered(audioTrackId, - TaskQueue(), GetWakeDecoderRunnable()); stream->mNextAudioTime = mStreamStartTime; } if (mInfo.HasVideo()) { @@ -470,9 +466,6 @@ void MediaDecoderStateMachine::SendStreamData() VideoSegment* video = new VideoSegment(); mediaStream->AddTrack(videoTrackId, 0, video, SourceMediaStream::ADDTRACK_QUEUED); - stream->mStream->DispatchWhenNotEnoughBuffered(videoTrackId, - TaskQueue(), GetWakeDecoderRunnable()); - stream->mNextVideoTime = mStreamStartTime; } mediaStream->FinishAddTracks(); @@ -601,18 +594,6 @@ void MediaDecoderStateMachine::SendStreamData() } } -MediaDecoderStateMachine::WakeDecoderRunnable* -MediaDecoderStateMachine::GetWakeDecoderRunnable() -{ - MOZ_ASSERT(OnTaskQueue()); - AssertCurrentThreadInMonitor(); - - if (!mPendingWakeDecoder.get()) { - mPendingWakeDecoder = new WakeDecoderRunnable(this); - } - return mPendingWakeDecoder.get(); -} - bool MediaDecoderStateMachine::HaveEnoughDecodedAudio(int64_t aAmpleAudioUSecs) { MOZ_ASSERT(OnTaskQueue()); @@ -634,8 +615,6 @@ bool MediaDecoderStateMachine::HaveEnoughDecodedAudio(int64_t aAmpleAudioUSecs) if (!stream->mStream->HaveEnoughBuffered(audioTrackId)) { return false; } - stream->mStream->DispatchWhenNotEnoughBuffered(audioTrackId, - TaskQueue(), GetWakeDecoderRunnable()); } return true; @@ -658,8 +637,6 @@ bool MediaDecoderStateMachine::HaveEnoughDecodedVideo() if (!stream->mStream->HaveEnoughBuffered(videoTrackId)) { return false; } - stream->mStream->DispatchWhenNotEnoughBuffered(videoTrackId, - TaskQueue(), GetWakeDecoderRunnable()); } return true; @@ -1256,10 +1233,6 @@ nsresult MediaDecoderStateMachine::Init(MediaDecoderStateMachine* aCloneDonor) { MOZ_ASSERT(NS_IsMainThread()); - if (NS_WARN_IF(!mReader->EnsureTaskQueue())) { - return NS_ERROR_FAILURE; - } - MediaDecoderReader* cloneReader = nullptr; if (aCloneDonor) { cloneReader = aCloneDonor->mReader; @@ -1554,8 +1527,6 @@ void MediaDecoderStateMachine::SetDormant(bool aDormant) // it here as well. nsCOMPtr r = NS_NewRunnableMethod(mReader, &MediaDecoderReader::ReleaseMediaResources); DecodeTaskQueue()->Dispatch(r.forget()); - // There's now no possibility of mPendingWakeDecoder being needed again. Revoke it. - mPendingWakeDecoder = nullptr; mDecoder->GetReentrantMonitor().NotifyAll(); } else if ((aDormant != true) && (mState == DECODER_STATE_DORMANT)) { mDecodingFrozenAtStateDecoding = true; @@ -1880,7 +1851,7 @@ MediaDecoderStateMachine::InitiateSeek() seekTime = std::min(seekTime, end); seekTime = std::max(mStartTime, seekTime); NS_ASSERTION(seekTime >= mStartTime && seekTime <= end, - "Can only seek in range [0,duration]"); + "Can only seek in range [0,duration]"); mCurrentSeek.mTarget.mTime = seekTime; if (mAudioCaptured) { @@ -2551,10 +2522,6 @@ MediaDecoderStateMachine::FinishShutdown() AudioQueue().ClearListeners(); VideoQueue().ClearListeners(); - // Now that those threads are stopped, there's no possibility of - // mPendingWakeDecoder being needed again. Revoke it. - mPendingWakeDecoder = nullptr; - // Disconnect canonicals and mirrors before shutting down our task queue. mEstimatedDuration.DisconnectIfConnected(); mExplicitDuration.DisconnectIfConnected(); @@ -3022,11 +2989,7 @@ void MediaDecoderStateMachine::AdvanceFrame() int64_t delta = currentFrame->mTime - clock_time; TimeStamp presTime = nowTime + TimeDuration::FromMicroseconds(delta / mPlaybackRate); NS_ASSERTION(currentFrame->mTime >= mStartTime, "Should have positive frame time"); - // Filter out invalid frames by checking the frame time. FrameTime could be - // zero if it's a initial frame. - int64_t frameTime = currentFrame->mTime - mStartTime; - if (frameTime > 0 || (frameTime == 0 && mPlayDuration == 0) || - IsRealTime()) { + { ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor()); // If we have video, we want to increment the clock in steps of the frame // duration. diff --git a/dom/media/MediaDecoderStateMachine.h b/dom/media/MediaDecoderStateMachine.h index 200190f463..d05429c251 100644 --- a/dom/media/MediaDecoderStateMachine.h +++ b/dom/media/MediaDecoderStateMachine.h @@ -422,41 +422,6 @@ protected: void LogicalPlaybackRateChanged(); void PreservesPitchChanged(); - class WakeDecoderRunnable : public nsRunnable { - public: - explicit WakeDecoderRunnable(MediaDecoderStateMachine* aSM) - : mMutex("WakeDecoderRunnable"), mStateMachine(aSM) {} - NS_IMETHOD Run() override - { - nsRefPtr stateMachine; - { - // Don't let Run() (called by media stream graph thread) race with - // Revoke() (called by decoder state machine thread) - MutexAutoLock lock(mMutex); - if (!mStateMachine) - return NS_OK; - stateMachine = mStateMachine; - } - stateMachine->ScheduleStateMachineWithLockAndWakeDecoder(); - return NS_OK; - } - void Revoke() - { - MutexAutoLock lock(mMutex); - mStateMachine = nullptr; - } - - Mutex mMutex; - // Protected by mMutex. - // We don't use an owning pointer here, because keeping mStateMachine alive - // would mean in some cases we'd have to destroy mStateMachine from this - // object, which would be problematic since MediaDecoderStateMachine can - // only be destroyed on the main thread whereas this object can be destroyed - // on the media stream graph thread. - MediaDecoderStateMachine* mStateMachine; - }; - WakeDecoderRunnable* GetWakeDecoderRunnable(); - MediaQueue& AudioQueue() { return mAudioQueue; } MediaQueue& VideoQueue() { return mVideoQueue; } @@ -1098,13 +1063,6 @@ protected: // This is created in the state machine's constructor. nsRefPtr mReader; - // Accessed only on the state machine thread. - // Not an nsRevocableEventPtr since we must Revoke() it well before - // this object is destroyed, anyway. - // Protected by decoder monitor except during the SHUTDOWN state after the - // decoder thread has been stopped. - nsRevocableEventPtr mPendingWakeDecoder; - // The time of the current frame in microseconds, corresponding to the "current // playback position" in HTML5. This is referenced from 0, which is the initial // playback position. diff --git a/dom/media/MediaFormatReader.cpp b/dom/media/MediaFormatReader.cpp index a4c1046def..bba142b990 100644 --- a/dom/media/MediaFormatReader.cpp +++ b/dom/media/MediaFormatReader.cpp @@ -64,8 +64,9 @@ TrackTypeToStr(TrackInfo::TrackType aTrack) #endif MediaFormatReader::MediaFormatReader(AbstractMediaDecoder* aDecoder, - MediaDataDemuxer* aDemuxer) - : MediaDecoderReader(aDecoder) + MediaDataDemuxer* aDemuxer, + MediaTaskQueue* aBorrowedTaskQueue) + : MediaDecoderReader(aDecoder, aBorrowedTaskQueue) , mDemuxer(aDemuxer) , mAudio(this, MediaData::AUDIO_DATA, Preferences::GetUint("media.audio-decode-ahead", 2)) , mVideo(this, MediaData::VIDEO_DATA, Preferences::GetUint("media.video-decode-ahead", 2)) diff --git a/dom/media/MediaFormatReader.h b/dom/media/MediaFormatReader.h index d54553adab..e0c0c3fc4e 100644 --- a/dom/media/MediaFormatReader.h +++ b/dom/media/MediaFormatReader.h @@ -30,7 +30,8 @@ class MediaFormatReader final : public MediaDecoderReader public: explicit MediaFormatReader(AbstractMediaDecoder* aDecoder, - MediaDataDemuxer* aDemuxer); + MediaDataDemuxer* aDemuxer, + MediaTaskQueue* aBorrowedTaskQueue = nullptr); virtual ~MediaFormatReader(); diff --git a/dom/media/fmp4/MP4Reader.cpp b/dom/media/fmp4/MP4Reader.cpp index 5a72908e46..7b7a27e448 100644 --- a/dom/media/fmp4/MP4Reader.cpp +++ b/dom/media/fmp4/MP4Reader.cpp @@ -141,8 +141,8 @@ InvokeAndRetry(ThisType* aThisVal, ReturnType(ThisType::*aMethod)(), MP4Stream* } } -MP4Reader::MP4Reader(AbstractMediaDecoder* aDecoder) - : MediaDecoderReader(aDecoder) +MP4Reader::MP4Reader(AbstractMediaDecoder* aDecoder, MediaTaskQueue* aBorrowedTaskQueue) + : MediaDecoderReader(aDecoder, aBorrowedTaskQueue) , mAudio(MediaData::AUDIO_DATA, Preferences::GetUint("media.mp4-audio-decode-ahead", 2)) , mVideo(MediaData::VIDEO_DATA, Preferences::GetUint("media.mp4-video-decode-ahead", 2)) , mLastReportedNumDecodedFrames(0) diff --git a/dom/media/fmp4/MP4Reader.h b/dom/media/fmp4/MP4Reader.h index c58ae055d9..4d1473997a 100644 --- a/dom/media/fmp4/MP4Reader.h +++ b/dom/media/fmp4/MP4Reader.h @@ -34,7 +34,7 @@ class MP4Reader final : public MediaDecoderReader typedef TrackInfo::TrackType TrackType; public: - explicit MP4Reader(AbstractMediaDecoder* aDecoder); + explicit MP4Reader(AbstractMediaDecoder* aDecoder, MediaTaskQueue* aBorrowedTaskQueue = nullptr); virtual ~MP4Reader(); diff --git a/dom/media/gtest/TestMP4Reader.cpp b/dom/media/gtest/TestMP4Reader.cpp index a76e1f8a2d..617aa00f78 100644 --- a/dom/media/gtest/TestMP4Reader.cpp +++ b/dom/media/gtest/TestMP4Reader.cpp @@ -36,7 +36,6 @@ public: decoder->SetResource(resource); reader->Init(nullptr); - reader->EnsureTaskQueue(); { // This needs to be done before invoking GetBuffered. This is normally // done by MediaDecoderStateMachine. diff --git a/dom/media/mediasource/MediaSourceReader.cpp b/dom/media/mediasource/MediaSourceReader.cpp index af9cfb50be..affae3b722 100644 --- a/dom/media/mediasource/MediaSourceReader.cpp +++ b/dom/media/mediasource/MediaSourceReader.cpp @@ -658,7 +658,8 @@ MediaSourceReader::ReleaseMediaResources() } MediaDecoderReader* -CreateReaderForType(const nsACString& aType, AbstractMediaDecoder* aDecoder) +CreateReaderForType(const nsACString& aType, AbstractMediaDecoder* aDecoder, + MediaTaskQueue* aBorrowedTaskQueue) { #ifdef MOZ_FMP4 // The MP4Reader that supports fragmented MP4 and uses @@ -671,15 +672,15 @@ CreateReaderForType(const nsACString& aType, AbstractMediaDecoder* aDecoder) bool useFormatDecoder = Preferences::GetBool("media.mediasource.format-reader.mp4", true); MediaDecoderReader* reader = useFormatDecoder ? - static_cast(new MediaFormatReader(aDecoder, new MP4Demuxer(aDecoder->GetResource()))) : - static_cast(new MP4Reader(aDecoder)); + static_cast(new MediaFormatReader(aDecoder, new MP4Demuxer(aDecoder->GetResource()), aBorrowedTaskQueue)) : + static_cast(new MP4Reader(aDecoder, aBorrowedTaskQueue)); return reader; } #endif #ifdef MOZ_WEBM if (DecoderTraits::IsWebMType(aType)) { - return new WebMReader(aDecoder); + return new WebMReader(aDecoder, aBorrowedTaskQueue); } #endif @@ -692,10 +693,17 @@ MediaSourceReader::CreateSubDecoder(const nsACString& aType, int64_t aTimestampO if (IsShutdown()) { return nullptr; } - MOZ_ASSERT(GetTaskQueue()); + + // The task queue borrowing is icky. It would be nicer to just give each subreader + // its own task queue. Unfortunately though, Request{Audio,Video}Data implementations + // currently assert that they're on "the decode thread", and so having + // separate task queues makes MediaSource stuff unnecessarily cumbersome. We + // should remove the need for these assertions (which probably involves making + // all Request*Data implementations fully async), and then get rid of the + // borrowing. nsRefPtr decoder = new SourceBufferDecoder(new SourceBufferResource(aType), mDecoder, aTimestampOffset); - nsRefPtr reader(CreateReaderForType(aType, decoder)); + nsRefPtr reader(CreateReaderForType(aType, decoder, GetTaskQueue())); if (!reader) { return nullptr; } @@ -708,15 +716,6 @@ MediaSourceReader::CreateSubDecoder(const nsACString& aType, int64_t aTimestampO reader->SetStartTime(0); } - // This part is icky. It would be nicer to just give each subreader its own - // task queue. Unfortunately though, Request{Audio,Video}Data implementations - // currently assert that they're on "the decode thread", and so having - // separate task queues makes MediaSource stuff unnecessarily cumbersome. We - // should remove the need for these assertions (which probably involves making - // all Request*Data implementations fully async), and then get rid of the - // borrowing. - reader->SetBorrowedTaskQueue(GetTaskQueue()); - #ifdef MOZ_FMP4 reader->SetSharedDecoderManager(mSharedDecoderManager); #endif diff --git a/dom/media/webaudio/MediaBufferDecoder.cpp b/dom/media/webaudio/MediaBufferDecoder.cpp index 382af0e21d..769a22f61b 100644 --- a/dom/media/webaudio/MediaBufferDecoder.cpp +++ b/dom/media/webaudio/MediaBufferDecoder.cpp @@ -211,10 +211,6 @@ MediaDecodeTask::CreateReader() return false; } - if (!mDecoderReader->EnsureTaskQueue()) { - return false; - } - return true; } diff --git a/dom/media/webaudio/test/test_mediaElementAudioSourceNodeCrossOrigin.html b/dom/media/webaudio/test/test_mediaElementAudioSourceNodeCrossOrigin.html index 3ffef389e1..7d5b4a206d 100644 --- a/dom/media/webaudio/test/test_mediaElementAudioSourceNodeCrossOrigin.html +++ b/dom/media/webaudio/test/test_mediaElementAudioSourceNodeCrossOrigin.html @@ -11,6 +11,9 @@ + + + diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 1428b0be9b..1a63dd9cbc 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -2021,6 +2021,14 @@ pref("network.automatic-ntlm-auth.allow-proxies", true); pref("network.automatic-ntlm-auth.allow-non-fqdn", false); pref("network.automatic-ntlm-auth.trusted-uris", ""); +// Sub-resources HTTP-authentication: +// 0 - don't allow sub-resources to open HTTP authentication credentials +// dialogs +// 1 - allow sub-resources to open HTTP authentication credentials dialogs, +// but don't allow it for cross-origin sub-resources +// 2 - allow the cross-origin authentication as well. +pref("network.auth.allow-subresource-auth", 1); + pref("permissions.default.image", 1); // 1-Accept, 2-Deny, 3-dontAcceptForeign pref("network.proxy.type", 5); diff --git a/netwerk/base/NetUtil.jsm b/netwerk/base/NetUtil.jsm index 06feb7a16d..c3e343e3f5 100644 --- a/netwerk/base/NetUtil.jsm +++ b/netwerk/base/NetUtil.jsm @@ -320,11 +320,12 @@ this.NetUtil = { * The loadingPrincipal of the channel. * The principal of the document where the result of this request * will be used. - * This is generally the principal of the loadingNode. However - * for loads where loadingNode is omitted this argument still - * needs to be passed. For example for loads from a WebWorker, - * pass the principal of that worker. For loads from an addon or - * from internal browser features, pass the system principal. + * This defaults to the principal of aLoadingNode, so when + * aLoadingNode is passed this can be left as null. However for + * loads where aLoadingNode is null this argument must be passed. + * For example for loads from a WebWorker, pass the principal of + * that worker. For loads from an addon or from internal browser + * features, pass the system principal. * This principal should almost always be the system principal if * loadingNode is omitted, in which case you can use the * useSystemPrincipal property. The only exception to this is diff --git a/netwerk/base/nsIIOService.idl b/netwerk/base/nsIIOService.idl index c21e082335..4e386b1b3d 100644 --- a/netwerk/base/nsIIOService.idl +++ b/netwerk/base/nsIIOService.idl @@ -88,9 +88,10 @@ interface nsIIOService : nsISupports * The loadingPrincipal of the channel. * The principal of the document where the result of this request will * be used. - * This is generally the principal of the aLoadingNode. However for - * loads where aLoadingNode is null this argument still needs to be - * passed. For example for loads from a WebWorker, pass the principal + * This defaults to the principal of aLoadingNode, so when aLoadingNode + * is passed this can be left as null. However for loads where + * aLoadingNode is null this argument must be passed. + * For example for loads from a WebWorker, pass the principal * of that worker. For loads from an addon or from internal browser * features, pass the system principal. * This principal should almost always be the system principal if diff --git a/netwerk/base/nsIIOService2.idl b/netwerk/base/nsIIOService2.idl index cde09d4876..0f66e34d0a 100644 --- a/netwerk/base/nsIIOService2.idl +++ b/netwerk/base/nsIIOService2.idl @@ -58,9 +58,10 @@ interface nsIIOService2 : nsIIOService * The loadingPrincipal of the channel. * The principal of the document where the result of this request will * be used. - * This is generally the principal of the aLoadingNode. However for - * loads where aLoadingNode is null this argument still needs to be - * passed. For example for loads from a WebWorker, pass the principal + * This defaults to the principal of aLoadingNode, so when aLoadingNode + * is passed this can be left as null. However for loads where + * aLoadingNode is null this argument must be passed. + * For example for loads from a WebWorker, pass the principal * of that worker. For loads from an addon or from internal browser * features, pass the system principal. * This principal should almost always be the system principal if diff --git a/netwerk/base/nsIOService.cpp b/netwerk/base/nsIOService.cpp index 65950ff326..745edd6dc4 100644 --- a/netwerk/base/nsIOService.cpp +++ b/netwerk/base/nsIOService.cpp @@ -582,9 +582,20 @@ nsIOService::NewChannelFromURIWithLoadInfo(nsIURI* aURI, result); } +/* ***** DEPRECATED ***** + * please use NewChannelFromURI2 providing the right arguments for: + * * aLoadingNode + * * aLoadingPrincipal + * * aTriggeringPrincipal + * * aSecurityFlags + * * aContentPolicyType + * + * See nsIIoService.idl for a detailed description of those arguments + */ NS_IMETHODIMP nsIOService::NewChannelFromURI(nsIURI *aURI, nsIChannel **result) { + NS_WARNING("Deprecated, use NewChannelFromURI2 providing loadInfo arguments!"); return NewChannelFromURI2(aURI, nullptr, // aLoadingNode nullptr, // aLoadingPrincipal @@ -738,12 +749,23 @@ nsIOService::NewChannelFromURIWithProxyFlags2(nsIURI* aURI, result); } +/* ***** DEPRECATED ***** + * please use NewChannelFromURIWithProxyFlags2 providing the right arguments for: + * * aLoadingNode + * * aLoadingPrincipal + * * aTriggeringPrincipal + * * aSecurityFlags + * * aContentPolicyType + * + * See nsIIoService.idl for a detailed description of those arguments + */ NS_IMETHODIMP nsIOService::NewChannelFromURIWithProxyFlags(nsIURI *aURI, nsIURI *aProxyURI, uint32_t aProxyFlags, nsIChannel **result) { + NS_WARNING("Deprecated, use NewChannelFromURIWithProxyFlags2 providing loadInfo arguments!"); return NewChannelFromURIWithProxyFlags2(aURI, aProxyURI, aProxyFlags, @@ -780,9 +802,20 @@ nsIOService::NewChannel2(const nsACString& aSpec, result); } +/* ***** DEPRECATED ***** + * please use NewChannel2 providing the right arguments for: + * * aLoadingNode + * * aLoadingPrincipal + * * aTriggeringPrincipal + * * aSecurityFlags + * * aContentPolicyType + * + * See nsIIoService.idl for a detailed description of those arguments + */ NS_IMETHODIMP nsIOService::NewChannel(const nsACString &aSpec, const char *aCharset, nsIURI *aBaseURI, nsIChannel **result) { + NS_WARNING("Deprecated, use NewChannel2 providing loadInfo arguments!"); return NewChannel2(aSpec, aCharset, aBaseURI, @@ -1589,14 +1622,21 @@ IOServiceProxyCallback::OnProxyAvailable(nsICancelable *request, nsIChannel *cha if (!speculativeHandler) return NS_OK; - speculativeHandler->SpeculativeConnect(uri, - mCallbacks); + nsLoadFlags loadFlags = 0; + channel->GetLoadFlags(&loadFlags); + if (loadFlags & nsIRequest::LOAD_ANONYMOUS) { + speculativeHandler->SpeculativeAnonymousConnect(uri, mCallbacks); + } else { + speculativeHandler->SpeculativeConnect(uri, mCallbacks); + } + return NS_OK; } -NS_IMETHODIMP -nsIOService::SpeculativeConnect(nsIURI *aURI, - nsIInterfaceRequestor *aCallbacks) +nsresult +nsIOService::SpeculativeConnectInternal(nsIURI *aURI, + nsIInterfaceRequestor *aCallbacks, + bool aAnonymous) { // Check for proxy information. If there is a proxy configured then a // speculative connect should not be performed because the potential @@ -1627,9 +1667,15 @@ nsIOService::SpeculativeConnect(nsIURI *aURI, nsILoadInfo::SEC_NORMAL, nsIContentPolicy::TYPE_OTHER, getter_AddRefs(channel)); - NS_ENSURE_SUCCESS(rv, rv); + if (aAnonymous) { + nsLoadFlags loadFlags = 0; + channel->GetLoadFlags(&loadFlags); + loadFlags |= nsIRequest::LOAD_ANONYMOUS; + channel->SetLoadFlags(loadFlags); + } + nsCOMPtr cancelable; nsRefPtr callback = new IOServiceProxyCallback(aCallbacks, this); @@ -1640,6 +1686,20 @@ nsIOService::SpeculativeConnect(nsIURI *aURI, return pps->AsyncResolve(channel, 0, callback, getter_AddRefs(cancelable)); } +NS_IMETHODIMP +nsIOService::SpeculativeConnect(nsIURI *aURI, + nsIInterfaceRequestor *aCallbacks) +{ + return SpeculativeConnectInternal(aURI, aCallbacks, false); +} + +NS_IMETHODIMP +nsIOService::SpeculativeAnonymousConnect(nsIURI *aURI, + nsIInterfaceRequestor *aCallbacks) +{ + return SpeculativeConnectInternal(aURI, aCallbacks, true); +} + void nsIOService::NotifyAppOfflineStatus(uint32_t appId, int32_t state) { diff --git a/netwerk/base/nsIOService.h b/netwerk/base/nsIOService.h index d29a34261b..efb0218804 100644 --- a/netwerk/base/nsIOService.h +++ b/netwerk/base/nsIOService.h @@ -125,6 +125,11 @@ private: uint32_t aProxyFlags, nsILoadInfo* aLoadInfo, nsIChannel** result); + + nsresult SpeculativeConnectInternal(nsIURI *aURI, + nsIInterfaceRequestor *aCallbacks, + bool aAnonymous); + private: bool mOffline; bool mOfflineForProfileChange; diff --git a/netwerk/base/nsISpeculativeConnect.idl b/netwerk/base/nsISpeculativeConnect.idl index 4ef559d633..15d63e1071 100644 --- a/netwerk/base/nsISpeculativeConnect.idl +++ b/netwerk/base/nsISpeculativeConnect.idl @@ -8,7 +8,7 @@ interface nsIURI; interface nsIInterfaceRequestor; -[scriptable, uuid(fa580a8d-f4a4-47c5-8ade-4f9786e8d1de)] +[scriptable, uuid(d74a17ac-5b8a-4824-a309-b1f04a3c4aed)] interface nsISpeculativeConnect : nsISupports { /** @@ -28,6 +28,8 @@ interface nsISpeculativeConnect : nsISupports void speculativeConnect(in nsIURI aURI, in nsIInterfaceRequestor aCallbacks); + void speculativeAnonymousConnect(in nsIURI aURI, + in nsIInterfaceRequestor aCallbacks); }; /** diff --git a/netwerk/base/nsNetUtil.h b/netwerk/base/nsNetUtil.h index c477d796f0..30a56f1160 100644 --- a/netwerk/base/nsNetUtil.h +++ b/netwerk/base/nsNetUtil.h @@ -225,9 +225,10 @@ NS_NewFileURI(nsIURI* *result, * The loadingPrincipal of the channel. * The principal of the document where the result of this request will * be used. -* This is generally the principal of the aLoadingNode. However for -* loads where aLoadingNode is null this argument still needs to be -* passed. For example for loads from a WebWorker, pass the principal +* This defaults to the principal of aLoadingNode, so when aLoadingNode +* is passed this can be left as null. However for loads where +* aLoadingNode is null this argument must be passed. +* For example for loads from a WebWorker, pass the principal * of that worker. For loads from an addon or from internal browser * features, pass the system principal. * This principal should almost always be the system principal if diff --git a/netwerk/ipc/NeckoParent.cpp b/netwerk/ipc/NeckoParent.cpp index e7f3a4950a..20c01e8650 100644 --- a/netwerk/ipc/NeckoParent.cpp +++ b/netwerk/ipc/NeckoParent.cpp @@ -696,12 +696,17 @@ NeckoParent::DeallocPRemoteOpenFileParent(PRemoteOpenFileParent* actor) } bool -NeckoParent::RecvSpeculativeConnect(const URIParams &aURI) +NeckoParent::RecvSpeculativeConnect(const URIParams& aURI, const bool& aAnonymous) { nsCOMPtr speculator(gIOService); nsCOMPtr uri = DeserializeURI(aURI); if (uri && speculator) { - speculator->SpeculativeConnect(uri, nullptr); + if (aAnonymous) { + speculator->SpeculativeAnonymousConnect(uri, nullptr); + } else { + speculator->SpeculativeConnect(uri, nullptr); + } + } return true; } diff --git a/netwerk/ipc/NeckoParent.h b/netwerk/ipc/NeckoParent.h index 7cac382147..7e029e14bc 100644 --- a/netwerk/ipc/NeckoParent.h +++ b/netwerk/ipc/NeckoParent.h @@ -163,7 +163,7 @@ protected: const uint32_t& flags, const nsCString& aNetworkInterface) override; virtual bool DeallocPDNSRequestParent(PDNSRequestParent*) override; - virtual bool RecvSpeculativeConnect(const URIParams& aURI) override; + virtual bool RecvSpeculativeConnect(const URIParams& aURI, const bool& aAnonymous) override; virtual bool RecvHTMLDNSPrefetch(const nsString& hostname, const uint16_t& flags) override; virtual bool RecvCancelHTMLDNSPrefetch(const nsString& hostname, diff --git a/netwerk/ipc/PNecko.ipdl b/netwerk/ipc/PNecko.ipdl index 91beece5cf..7a4ab90836 100644 --- a/netwerk/ipc/PNecko.ipdl +++ b/netwerk/ipc/PNecko.ipdl @@ -76,7 +76,7 @@ parent: URIParams fileuri, OptionalURIParams appuri); - SpeculativeConnect(URIParams uri); + SpeculativeConnect(URIParams uri, bool anonymous); HTMLDNSPrefetch(nsString hostname, uint16_t flags); CancelHTMLDNSPrefetch(nsString hostname, uint16_t flags, nsresult reason); diff --git a/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp b/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp index 6111742b85..3d6bb14c6d 100644 --- a/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp +++ b/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp @@ -7,6 +7,7 @@ // HttpLog.h should generally be included first #include "HttpLog.h" +#include "mozilla/Preferences.h" #include "nsHttpChannelAuthProvider.h" #include "nsNetUtil.h" #include "nsHttpHandler.h" @@ -27,6 +28,10 @@ namespace mozilla { namespace net { +#define SUBRESOURCE_AUTH_DIALOG_DISALLOW_ALL 0 +#define SUBRESOURCE_AUTH_DIALOG_DISALLOW_CROSS_ORIGIN 1 +#define SUBRESOURCE_AUTH_DIALOG_ALLOW_ALL 2 + #define MAX_DISPLAYED_USER_LENGTH 64 #define MAX_DISPLAYED_HOST_LENGTH 64 @@ -64,6 +69,18 @@ nsHttpChannelAuthProvider::~nsHttpChannelAuthProvider() MOZ_ASSERT(!mAuthChannel, "Disconnect wasn't called"); } +uint32_t nsHttpChannelAuthProvider::sAuthAllowPref = + SUBRESOURCE_AUTH_DIALOG_DISALLOW_CROSS_ORIGIN; + +void +nsHttpChannelAuthProvider::InitializePrefs() +{ + MOZ_ASSERT(NS_IsMainThread()); + mozilla::Preferences::AddUintVarCache(&sAuthAllowPref, + "network.auth.allow-subresource-auth", + SUBRESOURCE_AUTH_DIALOG_DISALLOW_CROSS_ORIGIN); +} + NS_IMETHODIMP nsHttpChannelAuthProvider::Init(nsIHttpAuthenticableChannel *channel) { @@ -738,6 +755,14 @@ nsHttpChannelAuthProvider::GetCredentialsForChallenge(const char *challenge, else if (authFlags & nsIHttpAuthenticator::IDENTITY_ENCRYPTED) level = nsIAuthPrompt2::LEVEL_PW_ENCRYPTED; + // Depending on the pref setting, the authentication dialog may be + // blocked for all sub-resources, blocked for cross-origin + // sub-resources, or always allowed for sub-resources. + // For more details look at the bug 647010. + if (BlockPrompt()) { + return NS_ERROR_ABORT; + } + // at this point we are forced to interact with the user to get // their username and password for this domain. rv = PromptForIdentity(level, proxyAuth, realm.get(), @@ -781,6 +806,53 @@ nsHttpChannelAuthProvider::GetCredentialsForChallenge(const char *challenge, return rv; } +bool +nsHttpChannelAuthProvider::BlockPrompt() +{ + nsCOMPtr chan = do_QueryInterface(mAuthChannel); + nsCOMPtr loadInfo; + chan->GetLoadInfo(getter_AddRefs(loadInfo)); + if (!loadInfo) { + return false; + } + + // Allow if it is the top-level document or xhr. + if ((loadInfo->InternalContentPolicyType() == nsIContentPolicy::TYPE_DOCUMENT) || + (loadInfo->InternalContentPolicyType() == nsIContentPolicy::TYPE_XMLHTTPREQUEST)) { + return false; + } + + switch (sAuthAllowPref) { + case SUBRESOURCE_AUTH_DIALOG_DISALLOW_ALL: + // Do not open the http-authentication credentials dialog for + // the sub-resources. + return true; + break; + case SUBRESOURCE_AUTH_DIALOG_DISALLOW_CROSS_ORIGIN: + // Do not open the http-authentication credentials dialog for + // the sub-resources only if they are not cross-origin. + { + nsCOMPtr loadingPrincipal = + loadInfo->LoadingPrincipal(); + if (!loadingPrincipal) { + return false; + } + + if (NS_FAILED(loadingPrincipal->CheckMayLoad(mURI, false, false))) { + return true; + } + } + break; + case SUBRESOURCE_AUTH_DIALOG_ALLOW_ALL: + // Allow the http-authentication dialog. + return false; + default: + // This is an invalid value. + MOZ_ASSERT(false, "A non valid value!"); + } + return false; +} + inline void GetAuthType(const char *challenge, nsCString &authType) { diff --git a/netwerk/protocol/http/nsHttpChannelAuthProvider.h b/netwerk/protocol/http/nsHttpChannelAuthProvider.h index f9a42d115e..05b18162dd 100644 --- a/netwerk/protocol/http/nsHttpChannelAuthProvider.h +++ b/netwerk/protocol/http/nsHttpChannelAuthProvider.h @@ -33,7 +33,7 @@ public: NS_DECL_NSIAUTHPROMPTCALLBACK nsHttpChannelAuthProvider(); - + static void InitializePrefs(); private: virtual ~nsHttpChannelAuthProvider(); @@ -111,6 +111,12 @@ private: */ nsresult ProcessSTSHeader(); + // Depending on the pref setting, the authentication dialog may be blocked + // for all sub-resources, blocked for cross-origin sub-resources, or + // always allowed for sub-resources. + // For more details look at the bug 647010. + bool BlockPrompt(); + private: nsIHttpAuthenticableChannel *mAuthChannel; // weak ref @@ -149,6 +155,11 @@ private: uint32_t mSuppressDefensiveAuth : 1; nsRefPtr mHttpHandler; // keep gHttpHandler alive + + // A variable holding the preference settings to whether to open HTTP + // authentication credentials dialogs for sub-resources and cross-origin + // sub-resources. + static uint32_t sAuthAllowPref; }; } // namespace net diff --git a/netwerk/protocol/http/nsHttpHandler.cpp b/netwerk/protocol/http/nsHttpHandler.cpp index 17d8a8171a..d4d1b457c8 100644 --- a/netwerk/protocol/http/nsHttpHandler.cpp +++ b/netwerk/protocol/http/nsHttpHandler.cpp @@ -48,6 +48,7 @@ #include "nsIMemoryReporter.h" #include "nsIParentalControlsService.h" #include "nsINetworkLinkService.h" +#include "nsHttpChannelAuthProvider.h" #include "mozilla/net/NeckoChild.h" #include "mozilla/ipc/URIUtils.h" @@ -277,6 +278,8 @@ nsHttpHandler::Init() PrefsChanged(prefBranch, nullptr); } + nsHttpChannelAuthProvider::InitializePrefs(); + if (mCompatFirefoxEnabled) { mMisc.AssignLiteral("rv:" MOZILLA_COMPATVERSION); } else { @@ -2004,14 +2007,15 @@ nsHttpHandler::Observe(nsISupports *subject, // nsISpeculativeConnect -NS_IMETHODIMP -nsHttpHandler::SpeculativeConnect(nsIURI *aURI, - nsIInterfaceRequestor *aCallbacks) +nsresult +nsHttpHandler::SpeculativeConnectInternal(nsIURI *aURI, + nsIInterfaceRequestor *aCallbacks, + bool anonymous) { if (IsNeckoChild()) { ipc::URIParams params; SerializeURI(aURI, params); - gNeckoChild->SendSpeculativeConnect(params); + gNeckoChild->SendSpeculativeConnect(params, anonymous); return NS_OK; } @@ -2074,10 +2078,25 @@ nsHttpHandler::SpeculativeConnect(nsIURI *aURI, nsHttpConnectionInfo *ci = new nsHttpConnectionInfo(host, port, EmptyCString(), username, nullptr, usingSSL); + ci->SetAnonymous(anonymous); return SpeculativeConnect(ci, aCallbacks); } +NS_IMETHODIMP +nsHttpHandler::SpeculativeConnect(nsIURI *aURI, + nsIInterfaceRequestor *aCallbacks) +{ + return SpeculativeConnectInternal(aURI, aCallbacks, false); +} + +NS_IMETHODIMP +nsHttpHandler::SpeculativeAnonymousConnect(nsIURI *aURI, + nsIInterfaceRequestor *aCallbacks) +{ + return SpeculativeConnectInternal(aURI, aCallbacks, true); +} + void nsHttpHandler::TickleWifi(nsIInterfaceRequestor *cb) { diff --git a/netwerk/protocol/http/nsHttpHandler.h b/netwerk/protocol/http/nsHttpHandler.h index c97d53431a..e2a287e9f4 100644 --- a/netwerk/protocol/http/nsHttpHandler.h +++ b/netwerk/protocol/http/nsHttpHandler.h @@ -560,6 +560,11 @@ public: private: nsRefPtr mWifiTickler; void TickleWifi(nsIInterfaceRequestor *cb); + +private: + nsresult SpeculativeConnectInternal(nsIURI *aURI, + nsIInterfaceRequestor *aCallbacks, + bool anonymous); }; extern nsHttpHandler *gHttpHandler; diff --git a/netwerk/test/mochitests/rel_preconnect.sjs b/netwerk/test/mochitests/rel_preconnect.sjs index d373f28460..7c1dc2dd90 100644 --- a/netwerk/test/mochitests/rel_preconnect.sjs +++ b/netwerk/test/mochitests/rel_preconnect.sjs @@ -6,7 +6,10 @@ function handleRequest(request, response) response.setHeader("Cache-Control", "no-cache", false); response.setHeader("Link", "<" + request.getHeader('X-Link') + - ">; rel=preconnect"); + ">; rel=preconnect" + ", " + + "<" + + request.getHeader('X-Link') + + ">; rel=preconnect; crossOrigin=anonymous"); response.write("check that header"); } diff --git a/netwerk/test/mochitests/test_rel_preconnect.html b/netwerk/test/mochitests/test_rel_preconnect.html index 490594f303..5a08ab1971 100644 --- a/netwerk/test/mochitests/test_rel_preconnect.html +++ b/netwerk/test/mochitests/test_rel_preconnect.html @@ -11,9 +11,9 @@ SimpleTest.waitForExplicitFinish(); const Cc = SpecialPowers.Cc, Ci = SpecialPowers.Ci, Cr = SpecialPowers.Cr; -var srv; +var srv1, srv2; -function TestServer(nextTest) { +function TestServer1(nextTest) { this.listener= Cc["@mozilla.org/network/server-socket;1"] .createInstance(Ci.nsIServerSocket); this.listener.init(-1, true, -1); @@ -21,7 +21,9 @@ function TestServer(nextTest) { this.nextTest = nextTest; } -TestServer.prototype = { +TestServer1.prototype = { + remainder : 2, + QueryInterface: function(iid) { iid = SpecialPowers.wrap(iid); if (iid.equals(Ci.nsIServerSocketListener) || @@ -32,8 +34,43 @@ TestServer.prototype = { onSocketAccepted: function(socket, trans) { try { socket.close(); } catch(e) {} try { trans.close(); } catch(e) {} - ok(true, "received connect"); - setTimeout(srv.nextTest, 0); + this.remainder--; + ok(true, "received connect remainder = " + this.remainder); + if (!this.remainder) { + srv1.listener.close(); + setTimeout(srv1.nextTest, 0); + } + }, + onStopListening: function(socket) {} +}; + +function TestServer2(nextTest) { + this.listener= Cc["@mozilla.org/network/server-socket;1"] + .createInstance(Ci.nsIServerSocket); + this.listener.init(-1, true, -1); + this.listener.asyncListen(SpecialPowers.wrapCallbackObject(this)); + this.nextTest = nextTest; +} + +TestServer2.prototype = { + remainder : 2, + + QueryInterface: function(iid) { + iid = SpecialPowers.wrap(iid); + if (iid.equals(Ci.nsIServerSocketListener) || + iid.equals(Ci.nsISupports)) + return this; + throw Cr.NS_ERROR_NO_INTERFACE; + }, + onSocketAccepted: function(socket, trans) { + try { socket.close(); } catch(e) {} + try { trans.close(); } catch(e) {} + this.remainder--; + ok(true, "received connect srv2 remainder = " + this.remainder); + if (!this.remainder) { + srv2.listener.close(); + setTimeout(srv2.nextTest, 0); + } }, onStopListening: function(socket) {} }; @@ -42,23 +79,29 @@ var originalLimit = SpecialPowers.getIntPref("network.http.speculative-parallel- function testElement() { - // test the link rel=preconnect element in the head - srv = new TestServer(testHeader); - SpecialPowers.setIntPref("network.http.speculative-parallel-limit", 1); + // test the link rel=preconnect element in the head for both normal + // and crossOrigin=anonymous + srv1 = new TestServer1(testHeader); + SpecialPowers.setIntPref("network.http.speculative-parallel-limit", 2); var link = document.createElement("link"); link.rel = "preconnect"; - link.href = "//localhost:" + srv.listener.port; + link.href = "//localhost:" + srv1.listener.port; + document.head.appendChild(link); + link = document.createElement("link"); + link.rel = "preconnect"; + link.href = "//localhost:" + srv1.listener.port; + link.crossOrigin = "anonymous"; document.head.appendChild(link); } function testHeader() { - // test the http link response header - srv.listener.close(); - srv = new TestServer(testDone); + // test the http link response header - the test contains both a + // normal and anonymous preconnect link header + srv2 = new TestServer2(testDone); var xhr = new XMLHttpRequest(); xhr.open("GET", 'rel_preconnect.sjs', false); - xhr.setRequestHeader("X-Link", "//localhost:" + srv.listener.port); + xhr.setRequestHeader("X-Link", "//localhost:" + srv2.listener.port); xhr.send(); is(xhr.status, 200, 'xhr cool'); } @@ -67,7 +110,6 @@ function testDone() { SpecialPowers.setIntPref("network.http.speculative-parallel-limit", originalLimit); - srv.listener.close(); SimpleTest.finish(); } diff --git a/netwerk/test/unit/test_auth_dialog_permission.js b/netwerk/test/unit/test_auth_dialog_permission.js new file mode 100644 index 0000000000..5bfe985372 --- /dev/null +++ b/netwerk/test/unit/test_auth_dialog_permission.js @@ -0,0 +1,256 @@ +// This file tests authentication prompt depending on pref +// network.auth.allow-subresource-auth: +// 0 - don't allow sub-resources to open HTTP authentication credentials +// dialogs +// 1 - allow sub-resources to open HTTP authentication credentials dialogs, +// but don't allow it for cross-origin sub-resources +// 2 - allow the cross-origin authentication as well. + +Cu.import("resource://testing-common/httpd.js"); + +var prefs = Cc["@mozilla.org/preferences-service;1"]. + getService(Ci.nsIPrefBranch); + +function authHandler(metadata, response) { + // btoa("guest:guest"), but that function is not available here + var expectedHeader = "Basic Z3Vlc3Q6Z3Vlc3Q="; + + var body; + if (metadata.hasHeader("Authorization") && + metadata.getHeader("Authorization") == expectedHeader) { + + response.setStatusLine(metadata.httpVersion, 200, "OK, authorized"); + response.setHeader("WWW-Authenticate", 'Basic realm="secret"', false); + + body = "success"; + } else { + // didn't know guest:guest, failure + response.setStatusLine(metadata.httpVersion, 401, "Unauthorized"); + response.setHeader("WWW-Authenticate", 'Basic realm="secret"', false); + + body = "failed"; + } + + response.bodyOutputStream.write(body, body.length); +} + +var httpserv = new HttpServer(); +httpserv.registerPathHandler("/auth", authHandler); +httpserv.start(-1); + +XPCOMUtils.defineLazyGetter(this, "URL", function() { + return "http://localhost:" + httpserv.identity.primaryPort; +}); + +XPCOMUtils.defineLazyGetter(this, "PORT", function() { + return httpserv.identity.primaryPort; +}); + +function AuthPrompt(promptExpected) { + this.promptExpected = promptExpected; +} + +AuthPrompt.prototype = { + user: "guest", + pass: "guest", + + QueryInterface: function authprompt_qi(iid) { + if (iid.equals(Components.interfaces.nsISupports) || + iid.equals(Components.interfaces.nsIAuthPrompt)) + return this; + throw Components.results.NS_ERROR_NO_INTERFACE; + }, + + prompt: function(title, text, realm, save, defaultText, result) { + do_throw("unexpected prompt call"); + }, + + promptUsernameAndPassword: function(title, text, realm, savePW, user, pw) { + do_check_true(this.promptExpected, + "Not expected the authentication prompt."); + + user.value = this.user; + pw.value = this.pass; + return true; + }, + + promptPassword: function(title, text, realm, save, pwd) { + do_throw("unexpected promptPassword call"); + } + +}; + +function Requestor(promptExpected) { + this.promptExpected = promptExpected; +} + +Requestor.prototype = { + QueryInterface: function(iid) { + if (iid.equals(Components.interfaces.nsISupports) || + iid.equals(Components.interfaces.nsIInterfaceRequestor)) + return this; + throw Components.results.NS_ERROR_NO_INTERFACE; + }, + + getInterface: function(iid) { + if (iid.equals(Components.interfaces.nsIAuthPrompt)) { + this.prompter = new AuthPrompt(this.promptExpected); + return this.prompter; + } + + throw Components.results.NS_ERROR_NO_INTERFACE; + }, + + prompter: null +}; + +function make_uri(url) { + var ios = Cc["@mozilla.org/network/io-service;1"]. + getService(Ci.nsIIOService); + return ios.newURI(url, null, null); +} + +function makeChan(loadingUrl, url, contentPolicy) { + var loadingUri = make_uri(loadingUrl); + var principal = Components.classes["@mozilla.org/scriptsecuritymanager;1"] + .getService(Ci.nsIScriptSecurityManager) + .getNoAppCodebasePrincipal(loadingUri); + + var ios = Components.classes["@mozilla.org/network/io-service;1"] + .getService(Components.interfaces.nsIIOService); + var chan = ios.newChannel2(url, + null, + null, + null, + principal, + null, + Ci.nsILoadInfo.SEC_NORMAL, + contentPolicy) + .QueryInterface(Components.interfaces.nsIHttpChannel); + + return chan; +} + +function Test(allow_subresource_auth_pref, loadingUri, uri, contentPolicy, + expectedCode) { + this._allow_subresource_auth_pref = allow_subresource_auth_pref; + this._loadingUri = loadingUri; + this._uri = uri; + this._contentPolicy = contentPolicy; + this._expectedCode = expectedCode; +} + +Test.prototype = { + _allow_subresource_auth_pref: 1, + _loadingUri: null, + _uri: null, + _contentPolicy: Ci.nsIContentPolicy.TYPE_OTHER, + _expectedCode: 200, + + onStartRequest: function(request, ctx) { + try { + if (!Components.isSuccessCode(request.status)) { + do_throw("Channel should have a success code!"); + } + + if (!(request instanceof Components.interfaces.nsIHttpChannel)) { + do_throw("Expecting an HTTP channel"); + } + + do_check_eq(request.responseStatus, this._expectedCode); + // The request should be succeeded iff we expect 200 + do_check_eq(request.requestSucceeded, this._expectedCode == 200); + + } catch (e) { + do_throw("Unexpected exception: " + e); + } + + throw Components.results.NS_ERROR_ABORT; + }, + + onDataAvailable: function(request, context, stream, offset, count) { + do_throw("Should not get any data!"); + }, + + onStopRequest: function(request, ctx, status) { + do_check_eq(status, Components.results.NS_ERROR_ABORT); + + // Clear the auth cache. + Components.classes["@mozilla.org/network/http-auth-manager;1"] + .getService(Components.interfaces.nsIHttpAuthManager) + .clearAll(); + + do_timeout(0, run_next_test); + }, + + run: function() { + dump("Run test: " + this._allow_subresource_auth_pref + + this._loadingUri + + this._uri + + this._contentPolicy + + this._expectedCode + " \n"); + + prefs.setIntPref("network.auth.allow-subresource-auth", + this._allow_subresource_auth_pref); + let chan = makeChan(this._loadingUri, this._uri, this._contentPolicy); + chan.notificationCallbacks = new Requestor(this._expectedCode == 200); + chan.asyncOpen(this, null); + } +}; + +var tests = [ + // For the next 3 tests the preference is set to 2 - allow the cross-origin + // authentication as well. + + // A cross-origin request. + new Test(2, "https://example.com", URL + "/auth", + Ci.nsIContentPolicy.TYPE_OTHER, 200), + // A non cross-origin sub-resource request. + new Test(2, URL + "/", URL + "/auth", + Ci.nsIContentPolicy.TYPE_OTHER, 200), + // A top level document. + new Test(2, URL + "/auth", URL + "/auth", + Ci.nsIContentPolicy.TYPE_DOCUMENT, 200), + + // For the next 3 tests the preference is set to 1 - allow sub-resources to + // open HTTP authentication credentials dialogs, but don't allow it for + // cross-origin sub-resources + + // A cross-origin request. + new Test(1, "https://example.com", URL + "/auth", + Ci.nsIContentPolicy.TYPE_OTHER, 401), + // A non cross-origin sub-resource request. + new Test(1, URL + "/", URL + "/auth", + Ci.nsIContentPolicy.TYPE_OTHER, 200), + // A top level document. + new Test(1, URL + "/auth", URL + "/auth", + Ci.nsIContentPolicy.TYPE_DOCUMENT, 200), + + // For the next 3 tests the preference is set to 0 - don't allow sub-resources + // to open HTTP authentication credentials dialogs. + + // A cross-origin request. + new Test(0, "https://example.com", URL + "/auth", + Ci.nsIContentPolicy.TYPE_OTHER, 401), + // A sub-resource request. + new Test(0, URL + "/", URL + "/auth", + Ci.nsIContentPolicy.TYPE_OTHER, 401), + // A top level request. + new Test(0, URL + "/auth", URL + "/auth", + Ci.nsIContentPolicy.TYPE_DOCUMENT, 200), +]; + +function run_next_test() { + var nextTest = tests.shift(); + if (!nextTest) { + httpserv.stop(do_test_finished); + return; + } + + nextTest.run(); +} + +function run_test() { + do_test_pending(); + run_next_test(); +} diff --git a/netwerk/test/unit/test_auth_proxy.js b/netwerk/test/unit/test_auth_proxy.js index bdaa0566f7..4b252dcf50 100644 --- a/netwerk/test/unit/test_auth_proxy.js +++ b/netwerk/test/unit/test_auth_proxy.js @@ -244,6 +244,9 @@ function run_test() { prefs.setCharPref("network.proxy.no_proxies_on", ""); prefs.setIntPref("network.proxy.type", 1); + // Turn off the authentication dialog blocking for this test. + prefs.setIntPref("network.auth.allow-subresource-auth", 2); + tests[current_test](); } diff --git a/netwerk/test/unit/test_authentication.js b/netwerk/test/unit/test_authentication.js index ea29430070..8fbbf31ebb 100644 --- a/netwerk/test/unit/test_authentication.js +++ b/netwerk/test/unit/test_authentication.js @@ -4,6 +4,11 @@ Cu.import("resource://testing-common/httpd.js"); Cu.import("resource://gre/modules/Services.jsm"); +// Turn off the authentication dialog blocking for this test. +var prefs = Cc["@mozilla.org/preferences-service;1"]. + getService(Ci.nsIPrefBranch); +prefs.setIntPref("network.auth.allow-subresource-auth", 2); + XPCOMUtils.defineLazyGetter(this, "URL", function() { return "http://localhost:" + httpserv.identity.primaryPort; }); diff --git a/netwerk/test/unit/xpcshell.ini b/netwerk/test/unit/xpcshell.ini index 3c53f58164..c516d0dabf 100644 --- a/netwerk/test/unit/xpcshell.ini +++ b/netwerk/test/unit/xpcshell.ini @@ -81,6 +81,7 @@ skip-if = true [test_auth_proxy.js] [test_authentication.js] [test_authpromptwrapper.js] +[test_auth_dialog_permission.js] [test_backgroundfilesaver.js] [test_bug203271.js] [test_bug248970_cache.js] diff --git a/parser/html/nsHtml5SpeculativeLoad.cpp b/parser/html/nsHtml5SpeculativeLoad.cpp index ee15804a95..137ca45b13 100644 --- a/parser/html/nsHtml5SpeculativeLoad.cpp +++ b/parser/html/nsHtml5SpeculativeLoad.cpp @@ -68,7 +68,7 @@ nsHtml5SpeculativeLoad::Perform(nsHtml5TreeOpExecutor* aExecutor) } break; case eSpeculativeLoadPreconnect: - aExecutor->Preconnect(mUrl); + aExecutor->Preconnect(mUrl, mCrossOrigin); break; default: NS_NOTREACHED("Bogus speculative load."); diff --git a/parser/html/nsHtml5SpeculativeLoad.h b/parser/html/nsHtml5SpeculativeLoad.h index 70ac54ebc5..db42719901 100644 --- a/parser/html/nsHtml5SpeculativeLoad.h +++ b/parser/html/nsHtml5SpeculativeLoad.h @@ -164,12 +164,14 @@ class nsHtml5SpeculativeLoad { mTypeOrCharsetSource.Assign((char16_t)aCharsetSource); } - inline void InitPreconnect(const nsAString& aUrl) + inline void InitPreconnect(const nsAString& aUrl, + const nsAString& aCrossOrigin) { NS_PRECONDITION(mOpCode == eSpeculativeLoadUninitialized, "Trying to reinitialize a speculative load!"); mOpCode = eSpeculativeLoadPreconnect; mUrl.Assign(aUrl); + mCrossOrigin.Assign(aCrossOrigin); } void Perform(nsHtml5TreeOpExecutor* aExecutor); @@ -193,9 +195,9 @@ class nsHtml5SpeculativeLoad { */ nsString mTypeOrCharsetSource; /** - * If mOpCode is eSpeculativeLoadImage or eSpeculativeLoadScript[FromHead], - * this is the value of the "crossorigin" attribute. If the - * attribute is not set, this will be a void string. + * If mOpCode is eSpeculativeLoadImage or eSpeculativeLoadScript[FromHead] + * or eSpeculativeLoadPreconnect this is the value of the "crossorigin" + * attribute. If the attribute is not set, this will be a void string. */ nsString mCrossOrigin; /** diff --git a/parser/html/nsHtml5TreeBuilderCppSupplement.h b/parser/html/nsHtml5TreeBuilderCppSupplement.h index c044b16483..b9462886e4 100644 --- a/parser/html/nsHtml5TreeBuilderCppSupplement.h +++ b/parser/html/nsHtml5TreeBuilderCppSupplement.h @@ -191,8 +191,10 @@ nsHtml5TreeBuilder::createElement(int32_t aNamespace, nsIAtom* aName, } else if (rel->LowerCaseEqualsASCII("preconnect")) { nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_HREF); if (url) { + nsString* crossOrigin = + aAttributes->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN); mSpeculativeLoadQueue.AppendElement()-> - InitPreconnect(*url); + InitPreconnect(*url, (crossOrigin) ? *crossOrigin : NullString()); } } } diff --git a/toolkit/components/passwordmgr/test/test_prompt_async.html b/toolkit/components/passwordmgr/test/test_prompt_async.html index 4afe7de579..a202861889 100644 --- a/toolkit/components/passwordmgr/test/test_prompt_async.html +++ b/toolkit/components/passwordmgr/test/test_prompt_async.html @@ -11,6 +11,9 @@ SimpleTest.waitForExplicitFinish(); SimpleTest.requestFlakyTimeout("untriaged"); + var prefs = Cc["@mozilla.org/preferences-service;1"]. + getService(Ci.nsIPrefBranch); + prefs.setIntPref("network.auth.allow-subresource-auth", 2); // Class monitoring number of open dialog windows // It checks there is always open just a single dialog per application function dialogMonitor() { diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_auth.js b/toolkit/mozapps/extensions/test/xpinstall/browser_auth.js index ee29138271..c41cb995fd 100644 --- a/toolkit/mozapps/extensions/test/xpinstall/browser_auth.js +++ b/toolkit/mozapps/extensions/test/xpinstall/browser_auth.js @@ -8,6 +8,10 @@ function test() { Harness.installsCompletedCallback = finish_test; Harness.setup(); + var prefs = Cc["@mozilla.org/preferences-service;1"]. + getService(Ci.nsIPrefBranch); + prefs.setIntPref("network.auth.allow-subresource-auth", 2); + var pm = Services.perms; pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);