forked from mirrors/gecko-dev
		
	 b4f9be25d4
			
		
	
	
		b4f9be25d4
		
	
	
	
	
		
			
			Updated with clang-format version 11.0.1 (taskcluster-B6bdwSKDRF-luRQWXBuzpA) # ignore-this-changeset Differential Revision: https://phabricator.services.mozilla.com/D105158
		
			
				
	
	
		
			267 lines
		
	
	
	
		
			9.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			267 lines
		
	
	
	
		
			9.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| #include "mozilla/dom/LocationBase.h"
 | |
| #include "nsIScriptSecurityManager.h"
 | |
| #include "nsIScriptContext.h"
 | |
| #include "nsDocShellLoadState.h"
 | |
| #include "nsIWebNavigation.h"
 | |
| #include "nsNetUtil.h"
 | |
| #include "nsCOMPtr.h"
 | |
| #include "nsError.h"
 | |
| #include "nsContentUtils.h"
 | |
| #include "nsGlobalWindow.h"
 | |
| #include "mozilla/NullPrincipal.h"
 | |
| #include "mozilla/dom/Document.h"
 | |
| #include "mozilla/dom/WindowContext.h"
 | |
| 
 | |
| namespace mozilla::dom {
 | |
| 
 | |
| already_AddRefed<nsDocShellLoadState> LocationBase::CheckURL(
 | |
|     nsIURI* aURI, nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
 | |
|   RefPtr<BrowsingContext> bc(GetBrowsingContext());
 | |
|   if (NS_WARN_IF(!bc)) {
 | |
|     aRv.Throw(NS_ERROR_NOT_AVAILABLE);
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<nsIPrincipal> triggeringPrincipal;
 | |
|   nsCOMPtr<nsIURI> sourceURI;
 | |
|   ReferrerPolicy referrerPolicy = ReferrerPolicy::_empty;
 | |
|   nsCOMPtr<nsIReferrerInfo> referrerInfo;
 | |
| 
 | |
|   // Get security manager.
 | |
|   nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
 | |
|   if (NS_WARN_IF(!ssm)) {
 | |
|     aRv.Throw(NS_ERROR_UNEXPECTED);
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   // Check to see if URI is allowed.  We're not going to worry about a
 | |
|   // window ID here because it's not 100% clear which window's id we
 | |
|   // would want, and we're throwing a content-visible exception
 | |
|   // anyway.
 | |
|   nsresult rv = ssm->CheckLoadURIWithPrincipal(
 | |
|       &aSubjectPrincipal, aURI, nsIScriptSecurityManager::STANDARD, 0);
 | |
|   if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|     nsAutoCString spec;
 | |
|     aURI->GetSpec(spec);
 | |
|     aRv.ThrowTypeError<MSG_URL_NOT_LOADABLE>(spec);
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   // 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<nsPIDOMWindowInner> incumbent =
 | |
|       do_QueryInterface(mozilla::dom::GetIncumbentGlobal());
 | |
|   nsCOMPtr<Document> doc = incumbent ? incumbent->GetDoc() : nullptr;
 | |
| 
 | |
|   // Create load info
 | |
|   RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(aURI);
 | |
| 
 | |
|   if (!doc) {
 | |
|     // No document; just use our subject principal as the triggering principal.
 | |
|     loadState->SetTriggeringPrincipal(&aSubjectPrincipal);
 | |
|     return loadState.forget();
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<nsIURI> docOriginalURI, docCurrentURI, principalURI;
 | |
|   docOriginalURI = doc->GetOriginalURI();
 | |
|   docCurrentURI = doc->GetDocumentURI();
 | |
|   nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
 | |
| 
 | |
|   triggeringPrincipal = doc->NodePrincipal();
 | |
|   referrerPolicy = doc->GetReferrerPolicy();
 | |
| 
 | |
|   bool urisEqual = false;
 | |
|   if (docOriginalURI && docCurrentURI && principal) {
 | |
|     principal->EqualsURI(docOriginalURI, &urisEqual);
 | |
|   }
 | |
|   if (urisEqual) {
 | |
|     referrerInfo = new ReferrerInfo(docCurrentURI, referrerPolicy);
 | |
|   } else {
 | |
|     principal->CreateReferrerInfo(referrerPolicy, getter_AddRefs(referrerInfo));
 | |
|   }
 | |
|   loadState->SetTriggeringPrincipal(triggeringPrincipal);
 | |
|   loadState->SetTriggeringSandboxFlags(doc->GetSandboxFlags());
 | |
|   loadState->SetCsp(doc->GetCsp());
 | |
|   if (referrerInfo) {
 | |
|     loadState->SetReferrerInfo(referrerInfo);
 | |
|   }
 | |
|   loadState->SetHasValidUserGestureActivation(
 | |
|       doc->HasValidTransientUserGestureActivation());
 | |
| 
 | |
|   return loadState.forget();
 | |
| }
 | |
| 
 | |
| void LocationBase::SetURI(nsIURI* aURI, nsIPrincipal& aSubjectPrincipal,
 | |
|                           ErrorResult& aRv, bool aReplace) {
 | |
|   RefPtr<BrowsingContext> bc = GetBrowsingContext();
 | |
|   if (!bc || bc->IsDiscarded()) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   CallerType callerType = aSubjectPrincipal.IsSystemPrincipal()
 | |
|                               ? CallerType::System
 | |
|                               : CallerType::NonSystem;
 | |
| 
 | |
|   nsresult rv = bc->CheckLocationChangeRateLimit(callerType);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     aRv.Throw(rv);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   RefPtr<nsDocShellLoadState> loadState =
 | |
|       CheckURL(aURI, aSubjectPrincipal, aRv);
 | |
|   if (aRv.Failed()) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (aReplace) {
 | |
|     loadState->SetLoadType(LOAD_STOP_CONTENT_AND_REPLACE);
 | |
|   } else {
 | |
|     loadState->SetLoadType(LOAD_STOP_CONTENT);
 | |
|   }
 | |
| 
 | |
|   // Get the incumbent script's browsing context to set as source.
 | |
|   nsCOMPtr<nsPIDOMWindowInner> sourceWindow =
 | |
|       nsContentUtils::CallerInnerWindow();
 | |
|   if (sourceWindow) {
 | |
|     WindowContext* context = sourceWindow->GetWindowContext();
 | |
|     loadState->SetSourceBrowsingContext(sourceWindow->GetBrowsingContext());
 | |
|     loadState->SetHasValidUserGestureActivation(
 | |
|         context && context->HasValidTransientUserGestureActivation());
 | |
|   }
 | |
| 
 | |
|   loadState->SetLoadFlags(nsIWebNavigation::LOAD_FLAGS_NONE);
 | |
|   loadState->SetFirstParty(true);
 | |
| 
 | |
|   rv = bc->LoadURI(loadState);
 | |
|   if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|     if (rv == NS_ERROR_DOM_BAD_CROSS_ORIGIN_URI &&
 | |
|         net::SchemeIsJavascript(loadState->URI())) {
 | |
|       // Per spec[1], attempting to load a javascript: URI into a cross-origin
 | |
|       // BrowsingContext is a no-op, and should not raise an exception.
 | |
|       // Technically, Location setters run with exceptions enabled should only
 | |
|       // throw an exception[2] when the caller is not allowed to navigate[3] the
 | |
|       // target browsing context due to sandboxing flags or not being
 | |
|       // closely-related enough, though in practice we currently throw for other
 | |
|       // reasons as well.
 | |
|       //
 | |
|       // [1]:
 | |
|       // https://html.spec.whatwg.org/multipage/browsing-the-web.html#javascript-protocol
 | |
|       // [2]:
 | |
|       // https://html.spec.whatwg.org/multipage/browsing-the-web.html#navigate
 | |
|       // [3]:
 | |
|       // https://html.spec.whatwg.org/multipage/browsers.html#allowed-to-navigate
 | |
|       return;
 | |
|     }
 | |
|     aRv.Throw(rv);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void LocationBase::SetHref(const nsAString& aHref,
 | |
|                            nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
 | |
|   DoSetHref(aHref, aSubjectPrincipal, false, aRv);
 | |
| }
 | |
| 
 | |
| void LocationBase::DoSetHref(const nsAString& aHref,
 | |
|                              nsIPrincipal& aSubjectPrincipal, bool aReplace,
 | |
|                              ErrorResult& aRv) {
 | |
|   // Get the source of the caller
 | |
|   nsCOMPtr<nsIURI> base = GetSourceBaseURL();
 | |
|   SetHrefWithBase(aHref, base, aSubjectPrincipal, aReplace, aRv);
 | |
| }
 | |
| 
 | |
| void LocationBase::SetHrefWithBase(const nsAString& aHref, nsIURI* aBase,
 | |
|                                    nsIPrincipal& aSubjectPrincipal,
 | |
|                                    bool aReplace, ErrorResult& aRv) {
 | |
|   nsresult result;
 | |
|   nsCOMPtr<nsIURI> newUri;
 | |
| 
 | |
|   if (Document* doc = GetEntryDocument()) {
 | |
|     result = NS_NewURI(getter_AddRefs(newUri), aHref,
 | |
|                        doc->GetDocumentCharacterSet(), aBase);
 | |
|   } else {
 | |
|     result = NS_NewURI(getter_AddRefs(newUri), aHref, nullptr, aBase);
 | |
|   }
 | |
| 
 | |
|   if (newUri) {
 | |
|     /* Check with the scriptContext if it is currently processing a script tag.
 | |
|      * If so, this must be a <script> tag with a location.href in it.
 | |
|      * we want to do a replace load, in such a situation.
 | |
|      * In other cases, for example if a event handler or a JS timer
 | |
|      * had a location.href in it, we want to do a normal load,
 | |
|      * so that the new url will be appended to Session History.
 | |
|      * This solution is tricky. Hopefully it isn't going to bite
 | |
|      * anywhere else. This is part of solution for bug # 39938, 72197
 | |
|      */
 | |
|     bool inScriptTag = false;
 | |
|     nsIScriptContext* scriptContext = nullptr;
 | |
|     nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(GetEntryGlobal());
 | |
|     if (win) {
 | |
|       scriptContext = nsGlobalWindowInner::Cast(win)->GetContextInternal();
 | |
|     }
 | |
| 
 | |
|     if (scriptContext) {
 | |
|       if (scriptContext->GetProcessingScriptTag()) {
 | |
|         // Now check to make sure that the script is running in our window,
 | |
|         // since we only want to replace if the location is set by a
 | |
|         // <script> tag in the same window.  See bug 178729.
 | |
|         nsCOMPtr<nsIDocShell> docShell(GetDocShell());
 | |
|         nsCOMPtr<nsIScriptGlobalObject> ourGlobal =
 | |
|             docShell ? docShell->GetScriptGlobalObject() : nullptr;
 | |
|         inScriptTag = (ourGlobal == scriptContext->GetGlobalObject());
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     SetURI(newUri, aSubjectPrincipal, aRv, aReplace || inScriptTag);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   aRv.Throw(result);
 | |
| }
 | |
| 
 | |
| void LocationBase::Replace(const nsAString& aUrl,
 | |
|                            nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
 | |
|   DoSetHref(aUrl, aSubjectPrincipal, true, aRv);
 | |
| }
 | |
| 
 | |
| nsIURI* LocationBase::GetSourceBaseURL() {
 | |
|   Document* doc = GetEntryDocument();
 | |
| 
 | |
|   // If there's no entry document, we either have no Script Entry Point or one
 | |
|   // that isn't a DOM Window.  This doesn't generally happen with the DOM, but
 | |
|   // can sometimes happen with extension code in certain IPC configurations.  If
 | |
|   // this happens, try falling back on the current document associated with the
 | |
|   // docshell. If that fails, just return null and hope that the caller passed
 | |
|   // an absolute URI.
 | |
|   if (!doc) {
 | |
|     if (nsCOMPtr<nsIDocShell> docShell = GetDocShell()) {
 | |
|       nsCOMPtr<nsPIDOMWindowOuter> docShellWin =
 | |
|           do_QueryInterface(docShell->GetScriptGlobalObject());
 | |
|       if (docShellWin) {
 | |
|         doc = docShellWin->GetDoc();
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return doc ? doc->GetBaseURI() : nullptr;
 | |
| }
 | |
| 
 | |
| }  // namespace mozilla::dom
 |