diff --git a/dom/base/Location.cpp b/dom/base/Location.cpp index d58cfb92d4fa..d18782ab652b 100644 --- a/dom/base/Location.cpp +++ b/dom/base/Location.cpp @@ -61,92 +61,94 @@ NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Location, mInnerWindow) NS_IMPL_CYCLE_COLLECTING_ADDREF(Location) NS_IMPL_CYCLE_COLLECTING_RELEASE(Location) -nsresult -Location::CheckURL(nsIURI* aURI, nsDocShellLoadInfo** aLoadInfo) +already_AddRefed +Location::CheckURL(nsIURI* aURI, nsIPrincipal& aSubjectPrincipal, + ErrorResult& aRv) { - *aLoadInfo = nullptr; - nsCOMPtr docShell(do_QueryReferent(mDocShell)); - NS_ENSURE_TRUE(docShell, NS_ERROR_NOT_AVAILABLE); + if (NS_WARN_IF(!docShell)) { + aRv.Throw(NS_ERROR_NOT_AVAILABLE); + return nullptr; + } nsCOMPtr triggeringPrincipal; nsCOMPtr sourceURI; net::ReferrerPolicy referrerPolicy = net::RP_Unset; - if (JSContext *cx = nsContentUtils::GetCurrentJSContext()) { - // No cx means that there's no JS running, or at least no JS that - // was run through code that properly pushed a context onto the - // context stack (as all code that runs JS off of web pages - // does). We won't bother with security checks in this case, but - // we need to create the loadinfo etc. + // Get security manager. + nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); + if (NS_WARN_IF(!ssm)) { + aRv.Throw(NS_ERROR_UNEXPECTED); + return nullptr; + } - // Get security manager. - nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); - NS_ENSURE_STATE(ssm); + // Check to see if URI is allowed. + nsresult rv = ssm->CheckLoadURIWithPrincipal(&aSubjectPrincipal, aURI, + nsIScriptSecurityManager::STANDARD); + if (NS_WARN_IF(NS_FAILED(rv))) { + nsAutoCString spec; + aURI->GetSpec(spec); + aRv.ThrowTypeError(NS_ConvertUTF8toUTF16(spec)); + return nullptr; + } - // Check to see if URI is allowed. - nsresult rv = ssm->CheckLoadURIFromScript(cx, aURI); - NS_ENSURE_SUCCESS(rv, rv); + // Make the load's referrer reflect changes to the document's URI caused by + // push/replaceState, if possible. First, get the document corresponding to + // fp. If the document's original URI (i.e. its URI before + // push/replaceState) matches the principal's URI, use the document's + // current URI as the referrer. If they don't match, use the principal's + // URI. + // + // The triggering principal for this load should be the principal of the + // incumbent document (which matches where the referrer information is + // coming from) when there is an incumbent document, and the subject + // principal otherwise. Note that the URI in the triggering principal + // may not match the referrer URI in various cases, notably including + // the cases when the incumbent document's document URI was modified + // after the document was loaded. - // Make the load's referrer reflect changes to the document's URI caused by - // push/replaceState, if possible. First, get the document corresponding to - // fp. If the document's original URI (i.e. its URI before - // push/replaceState) matches the principal's URI, use the document's - // current URI as the referrer. If they don't match, use the principal's - // URI. - // - // The triggering principal for this load should be the principal of the - // incumbent document (which matches where the referrer information is - // coming from) when there is an incumbent document, and the subject - // principal otherwise. Note that the URI in the triggering principal - // may not match the referrer URI in various cases, notably including - // the cases when the incumbent document's document URI was modified - // after the document was loaded. + nsCOMPtr incumbent = + do_QueryInterface(mozilla::dom::GetIncumbentGlobal()); + nsCOMPtr doc = incumbent ? incumbent->GetDoc() : nullptr; - nsCOMPtr incumbent = - do_QueryInterface(mozilla::dom::GetIncumbentGlobal()); - nsCOMPtr doc = incumbent ? incumbent->GetDoc() : nullptr; + if (doc) { + nsCOMPtr docOriginalURI, docCurrentURI, principalURI; + docOriginalURI = doc->GetOriginalURI(); + docCurrentURI = doc->GetDocumentURI(); + rv = doc->NodePrincipal()->GetURI(getter_AddRefs(principalURI)); + if (NS_WARN_IF(NS_FAILED(rv))) { + aRv.Throw(rv); + return nullptr; + } - if (doc) { - nsCOMPtr docOriginalURI, docCurrentURI, principalURI; - docOriginalURI = doc->GetOriginalURI(); - docCurrentURI = doc->GetDocumentURI(); - rv = doc->NodePrincipal()->GetURI(getter_AddRefs(principalURI)); - NS_ENSURE_SUCCESS(rv, rv); + triggeringPrincipal = doc->NodePrincipal(); + referrerPolicy = doc->GetReferrerPolicy(); - triggeringPrincipal = doc->NodePrincipal(); - referrerPolicy = doc->GetReferrerPolicy(); - - bool urisEqual = false; - if (docOriginalURI && docCurrentURI && principalURI) { - principalURI->Equals(docOriginalURI, &urisEqual); - } - if (urisEqual) { - sourceURI = docCurrentURI; - } - else { - // Use principalURI as long as it is not an NullPrincipalURI. We - // could add a method such as GetReferrerURI to principals to make this - // cleaner, but given that we need to start using Source Browsing - // Context for referrer (see Bug 960639) this may be wasted effort at - // this stage. - if (principalURI) { - bool isNullPrincipalScheme; - rv = principalURI->SchemeIs(NS_NULLPRINCIPAL_SCHEME, - &isNullPrincipalScheme); - if (NS_SUCCEEDED(rv) && !isNullPrincipalScheme) { - sourceURI = principalURI; - } + bool urisEqual = false; + if (docOriginalURI && docCurrentURI && principalURI) { + principalURI->Equals(docOriginalURI, &urisEqual); + } + if (urisEqual) { + sourceURI = docCurrentURI; + } + else { + // Use principalURI as long as it is not an NullPrincipalURI. We + // could add a method such as GetReferrerURI to principals to make this + // cleaner, but given that we need to start using Source Browsing + // Context for referrer (see Bug 960639) this may be wasted effort at + // this stage. + if (principalURI) { + bool isNullPrincipalScheme; + rv = principalURI->SchemeIs(NS_NULLPRINCIPAL_SCHEME, + &isNullPrincipalScheme); + if (NS_SUCCEEDED(rv) && !isNullPrincipalScheme) { + sourceURI = principalURI; } } } - else { - // No document; determine triggeringPrincipal by quering the - // subjectPrincipal, wich is the principal of the current JS - // compartment, or a null principal in case there is no - // compartment yet. - triggeringPrincipal = nsContentUtils::SubjectPrincipal(); - } + } else { + // No document; just use our subject principal as the triggering principal. + triggeringPrincipal = &aSubjectPrincipal; } // Create load info @@ -159,9 +161,7 @@ Location::CheckURL(nsIURI* aURI, nsDocShellLoadInfo** aLoadInfo) loadInfo->SetReferrerPolicy(referrerPolicy); } - loadInfo.swap(*aLoadInfo); - - return NS_OK; + return loadInfo.forget(); } nsresult @@ -207,14 +207,14 @@ Location::GetURI(nsIURI** aURI, bool aGetInnermostURI) } void -Location::SetURI(nsIURI* aURI, ErrorResult& aRv, bool aReplace) +Location::SetURI(nsIURI* aURI, nsIPrincipal& aSubjectPrincipal, + ErrorResult& aRv, bool aReplace) { nsCOMPtr docShell(do_QueryReferent(mDocShell)); if (docShell) { - RefPtr loadInfo; - - if (NS_FAILED(CheckURL(aURI, getter_AddRefs(loadInfo)))) { - aRv.Throw(NS_ERROR_FAILURE); + RefPtr loadInfo = + CheckURL(aURI, aSubjectPrincipal, aRv); + if (aRv.Failed()) { return; } @@ -308,7 +308,7 @@ Location::SetHash(const nsAString& aHash, return; } - SetURI(uri, aRv); + SetURI(uri, aSubjectPrincipal, aRv); } void @@ -362,7 +362,7 @@ Location::SetHost(const nsAString& aHost, return; } - SetURI(uri, aRv); + SetURI(uri, aSubjectPrincipal, aRv); } void @@ -407,7 +407,7 @@ Location::SetHostname(const nsAString& aHostname, return; } - SetURI(uri, aRv); + SetURI(uri, aSubjectPrincipal, aRv); } nsresult @@ -433,21 +433,24 @@ Location::GetHref(nsAString& aHref) void Location::SetHref(const nsAString& aHref, + nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) { - DoSetHref(aHref, false, aRv); + DoSetHref(aHref, aSubjectPrincipal, false, aRv); } void -Location::DoSetHref(const nsAString& aHref, bool aReplace, ErrorResult& aRv) +Location::DoSetHref(const nsAString& aHref, nsIPrincipal& aSubjectPrincipal, + bool aReplace, ErrorResult& aRv) { // Get the source of the caller nsCOMPtr base = GetSourceBaseURL(); - SetHrefWithBase(aHref, base, aReplace, aRv); + SetHrefWithBase(aHref, base, aSubjectPrincipal, aReplace, aRv); } void Location::SetHrefWithBase(const nsAString& aHref, nsIURI* aBase, + nsIPrincipal& aSubjectPrincipal, bool aReplace, ErrorResult& aRv) { nsresult result; @@ -490,7 +493,7 @@ Location::SetHrefWithBase(const nsAString& aHref, nsIURI* aBase, } } - SetURI(newUri, aRv, aReplace || inScriptTag); + SetURI(newUri, aSubjectPrincipal, aRv, aReplace || inScriptTag); return; } @@ -575,7 +578,7 @@ Location::SetPathname(const nsAString& aPathname, return; } - SetURI(uri, aRv); + SetURI(uri, aSubjectPrincipal, aRv); } void @@ -644,7 +647,7 @@ Location::SetPort(const nsAString& aPort, return; } - SetURI(uri, aRv); + SetURI(uri, aSubjectPrincipal, aRv); } void @@ -741,7 +744,7 @@ Location::SetProtocol(const nsAString& aProtocol, return; } - SetURI(uri, aRv); + SetURI(uri, aSubjectPrincipal, aRv); } void @@ -806,7 +809,7 @@ Location::SetSearch(const nsAString& aSearch, return; } - SetURI(uri, aRv); + SetURI(uri, aSubjectPrincipal, aRv); } nsresult @@ -862,7 +865,7 @@ Location::Replace(const nsAString& aUrl, nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) { - DoSetHref(aUrl, true, aRv); + DoSetHref(aUrl, aSubjectPrincipal, true, aRv); } void @@ -875,7 +878,7 @@ Location::Assign(const nsAString& aUrl, return; } - DoSetHref(aUrl, false, aRv); + DoSetHref(aUrl, aSubjectPrincipal, false, aRv); } already_AddRefed diff --git a/dom/base/Location.h b/dom/base/Location.h index ce0319243d56..6382778b53d7 100644 --- a/dom/base/Location.h +++ b/dom/base/Location.h @@ -68,6 +68,7 @@ public: } void SetHref(const nsAString& aHref, + nsIPrincipal& aSubjectPrincipal, ErrorResult& aError); void GetOrigin(nsAString& aOrigin, @@ -166,17 +167,27 @@ protected: // Note, this method can return NS_OK with a null value for aURL. This happens // if the docShell is null. nsresult GetURI(nsIURI** aURL, bool aGetInnermostURI = false); - void SetURI(nsIURI* aURL, ErrorResult& aRv, bool aReplace = false); + void SetURI(nsIURI* aURL, nsIPrincipal& aSubjectPrincipal, + ErrorResult& aRv, bool aReplace = false); void SetHrefWithBase(const nsAString& aHref, nsIURI* aBase, + nsIPrincipal& aSubjectPrincipal, bool aReplace, ErrorResult& aRv); // Helper for Assign/SetHref/Replace - void DoSetHref(const nsAString& aHref, bool aReplace, ErrorResult& aRv); + void DoSetHref(const nsAString& aHref, nsIPrincipal& aSubjectPrincipal, + bool aReplace, ErrorResult& aRv); // Get the base URL we should be using for our relative URL // resolution for SetHref/Assign/Replace. already_AddRefed GetSourceBaseURL(); - nsresult CheckURL(nsIURI *url, nsDocShellLoadInfo** aLoadInfo); + + // Check whether it's OK to load the given url with the given subject + // principal, and if so construct the right nsDocShellLoadInfo for the load + // and return it. + already_AddRefed CheckURL(nsIURI *url, + nsIPrincipal& aSubjectPrincipal, + ErrorResult& aRv); + bool CallerSubsumes(nsIPrincipal* aSubjectPrincipal); nsString mCachedHash; diff --git a/dom/bindings/Errors.msg b/dom/bindings/Errors.msg index 26dee8593cdf..1d7e5a07b88c 100644 --- a/dom/bindings/Errors.msg +++ b/dom/bindings/Errors.msg @@ -116,3 +116,4 @@ MSG_DEF(MSG_INVALID_PANNERNODE_REFDISTANCE_ERROR, 0, JSEXN_RANGEERR, "The refDis MSG_DEF(MSG_INVALID_PANNERNODE_MAXDISTANCE_ERROR, 0, JSEXN_RANGEERR, "The maxDistance value passed to PannerNode must be positive.") MSG_DEF(MSG_INVALID_PANNERNODE_ROLLOFF_ERROR, 0, JSEXN_RANGEERR, "The rolloffFactor value passed to PannerNode must not be negative.") MSG_DEF(MSG_NOT_ARRAY_NOR_UNDEFINED, 1, JSEXN_TYPEERR, "{0} is neither an array nor undefined.") +MSG_DEF(MSG_URL_NOT_LOADABLE, 1, JSEXN_TYPEERR, "Access to '{0}' from script denied.") diff --git a/dom/webidl/Location.webidl b/dom/webidl/Location.webidl index 7381f6d1c434..b802d32b5cc3 100644 --- a/dom/webidl/Location.webidl +++ b/dom/webidl/Location.webidl @@ -20,7 +20,7 @@ interface Location { [Throws, NeedsSubjectPrincipal] stringifier; - [Throws, CrossOriginWritable, GetterNeedsSubjectPrincipal] + [Throws, CrossOriginWritable, NeedsSubjectPrincipal] attribute USVString href; [Throws, NeedsSubjectPrincipal] readonly attribute USVString origin; diff --git a/testing/web-platform/meta/url/failure.html.ini b/testing/web-platform/meta/url/failure.html.ini index 90b87f1265a5..d72cadfc7a1b 100644 --- a/testing/web-platform/meta/url/failure.html.ini +++ b/testing/web-platform/meta/url/failure.html.ini @@ -5,36 +5,24 @@ [XHR: file://example:1/ should throw] expected: FAIL - [Location's href: file://example:1/ should throw] - expected: FAIL - [URL's href: file://example:test/ should throw] expected: FAIL [XHR: file://example:test/ should throw] expected: FAIL - [Location's href: file://example:test/ should throw] - expected: FAIL - [URL's href: file://example%/ should throw] expected: FAIL [XHR: file://example%/ should throw] expected: FAIL - [Location's href: file://example%/ should throw] - expected: FAIL - [URL's href: file://[example\]/ should throw] expected: FAIL [XHR: file://[example\]/ should throw] expected: FAIL - [Location's href: file://[example\]/ should throw] - expected: FAIL - [Location's href: http://user:pass@/ should throw] expected: FAIL