LocationBase.cpp (6565B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "mozilla/dom/LocationBase.h" 8 9 #include "mozilla/NullPrincipal.h" 10 #include "mozilla/dom/Document.h" 11 #include "mozilla/dom/PolicyContainer.h" 12 #include "mozilla/dom/WindowContext.h" 13 #include "nsCOMPtr.h" 14 #include "nsContentUtils.h" 15 #include "nsDocLoader.h" 16 #include "nsDocShellLoadState.h" 17 #include "nsError.h" 18 #include "nsGlobalWindowInner.h" 19 #include "nsIClassifiedChannel.h" 20 #include "nsIScriptContext.h" 21 #include "nsIScriptSecurityManager.h" 22 #include "nsIWebNavigation.h" 23 #include "nsNetUtil.h" 24 25 namespace mozilla::dom { 26 27 static bool IncumbentGlobalHasTransientActivation() { 28 nsGlobalWindowInner* window = nsContentUtils::IncumbentInnerWindow(); 29 return window && window->GetWindowContext() && window->GetWindowContext() && 30 window->GetWindowContext()->HasValidTransientUserGestureActivation(); 31 } 32 33 // https://html.spec.whatwg.org/#location-object-navigate 34 void LocationBase::Navigate(nsIURI* aURI, nsIPrincipal& aSubjectPrincipal, 35 ErrorResult& aRv, 36 NavigationHistoryBehavior aHistoryHandling) { 37 // Step 1 38 RefPtr<BrowsingContext> navigable = GetBrowsingContext(); 39 if (!navigable || navigable->IsDiscarded()) { 40 return; 41 } 42 43 // Step 2-3, except the check for if document is completely loaded. 44 bool needsCompletelyLoadedDocument = !IncumbentGlobalHasTransientActivation(); 45 46 // Make the load's referrer reflect changes to the document's URI caused by 47 // push/replaceState, if possible. First, get the document corresponding to 48 // fp. If the document's original URI (i.e. its URI before 49 // push/replaceState) matches the principal's URI, use the document's 50 // current URI as the referrer. If they don't match, use the principal's 51 // URI. 52 // 53 // The triggering principal for this load should be the principal of the 54 // incumbent document (which matches where the referrer information is 55 // coming from) when there is an incumbent document, and the subject 56 // principal otherwise. Note that the URI in the triggering principal 57 // may not match the referrer URI in various cases, notably including 58 // the cases when the incumbent document's document URI was modified 59 // after the document was loaded. 60 61 nsCOMPtr<nsPIDOMWindowInner> incumbent = 62 do_QueryInterface(mozilla::dom::GetIncumbentGlobal()); 63 nsCOMPtr<Document> doc = incumbent ? incumbent->GetDoc() : nullptr; 64 65 // Step 4 66 navigable->Navigate(aURI, doc, aSubjectPrincipal, aRv, aHistoryHandling, 67 needsCompletelyLoadedDocument); 68 } 69 70 void LocationBase::SetHref(const nsACString& aHref, 71 nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) { 72 DoSetHref(aHref, aSubjectPrincipal, false, aRv); 73 } 74 75 void LocationBase::DoSetHref(const nsACString& aHref, 76 nsIPrincipal& aSubjectPrincipal, bool aReplace, 77 ErrorResult& aRv) { 78 // Get the source of the caller 79 nsCOMPtr<nsIURI> base = GetSourceBaseURL(); 80 SetHrefWithBase(aHref, base, aSubjectPrincipal, aReplace, aRv); 81 } 82 83 void LocationBase::SetHrefWithBase(const nsACString& aHref, nsIURI* aBase, 84 nsIPrincipal& aSubjectPrincipal, 85 bool aReplace, ErrorResult& aRv) { 86 nsresult result; 87 nsCOMPtr<nsIURI> newUri; 88 89 if (Document* doc = GetEntryDocument()) { 90 result = NS_NewURI(getter_AddRefs(newUri), aHref, 91 doc->GetDocumentCharacterSet(), aBase); 92 } else { 93 result = NS_NewURI(getter_AddRefs(newUri), aHref, nullptr, aBase); 94 } 95 96 if (NS_FAILED(result) || !newUri) { 97 aRv.ThrowSyntaxError("'"_ns + aHref + "' is not a valid URL."_ns); 98 return; 99 } 100 101 /* Check with the scriptContext if it is currently processing a script tag. 102 * If so, this must be a <script> tag with a location.href in it. 103 * we want to do a replace load, in such a situation. 104 * In other cases, for example if a event handler or a JS timer 105 * had a location.href in it, we want to do a normal load, 106 * so that the new url will be appended to Session History. 107 * This solution is tricky. Hopefully it isn't going to bite 108 * anywhere else. This is part of solution for bug # 39938, 72197 109 */ 110 bool inScriptTag = false; 111 nsIScriptContext* scriptContext = nullptr; 112 nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(GetEntryGlobal()); 113 if (win) { 114 scriptContext = nsGlobalWindowInner::Cast(win)->GetContextInternal(); 115 } 116 117 if (scriptContext) { 118 if (scriptContext->GetProcessingScriptTag()) { 119 // Now check to make sure that the script is running in our window, 120 // since we only want to replace if the location is set by a 121 // <script> tag in the same window. See bug 178729. 122 nsCOMPtr<nsIDocShell> docShell(GetDocShell()); 123 nsCOMPtr<nsIScriptGlobalObject> ourGlobal = 124 docShell ? docShell->GetScriptGlobalObject() : nullptr; 125 inScriptTag = (ourGlobal == scriptContext->GetGlobalObject()); 126 } 127 } 128 129 NavigationHistoryBehavior historyHandling = NavigationHistoryBehavior::Auto; 130 if (aReplace || inScriptTag) { 131 historyHandling = NavigationHistoryBehavior::Replace; 132 } 133 134 Navigate(newUri, aSubjectPrincipal, aRv, historyHandling); 135 } 136 137 void LocationBase::Replace(const nsACString& aUrl, 138 nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) { 139 DoSetHref(aUrl, aSubjectPrincipal, true, aRv); 140 } 141 142 nsIURI* LocationBase::GetSourceBaseURL() { 143 Document* doc = GetEntryDocument(); 144 145 // If there's no entry document, we either have no Script Entry Point or one 146 // that isn't a DOM Window. This doesn't generally happen with the DOM, but 147 // can sometimes happen with extension code in certain IPC configurations. If 148 // this happens, try falling back on the current document associated with the 149 // docshell. If that fails, just return null and hope that the caller passed 150 // an absolute URI. 151 if (!doc) { 152 if (nsCOMPtr<nsIDocShell> docShell = GetDocShell()) { 153 nsCOMPtr<nsPIDOMWindowOuter> docShellWin = 154 do_QueryInterface(docShell->GetScriptGlobalObject()); 155 if (docShellWin) { 156 doc = docShellWin->GetDoc(); 157 } 158 } 159 } 160 return doc ? doc->GetBaseURI() : nullptr; 161 } 162 163 } // namespace mozilla::dom