Bug 1842773 - Part 5: Add ArrayBuffer.prototype.{maxByteLength,resizable} getters...
[gecko.git] / dom / base / LocationBase.cpp
blobbdf7edc1341f49e2c59466e081997627981bc22f
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/. */
7 #include "mozilla/dom/LocationBase.h"
8 #include "nsIScriptSecurityManager.h"
9 #include "nsIScriptContext.h"
10 #include "nsDocShellLoadState.h"
11 #include "nsIWebNavigation.h"
12 #include "nsNetUtil.h"
13 #include "nsCOMPtr.h"
14 #include "nsError.h"
15 #include "nsContentUtils.h"
16 #include "nsGlobalWindowInner.h"
17 #include "mozilla/NullPrincipal.h"
18 #include "mozilla/dom/Document.h"
19 #include "mozilla/dom/ReferrerInfo.h"
20 #include "mozilla/dom/WindowContext.h"
22 namespace mozilla::dom {
24 already_AddRefed<nsDocShellLoadState> LocationBase::CheckURL(
25 nsIURI* aURI, nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
26 RefPtr<BrowsingContext> bc(GetBrowsingContext());
27 if (NS_WARN_IF(!bc)) {
28 aRv.Throw(NS_ERROR_NOT_AVAILABLE);
29 return nullptr;
32 nsCOMPtr<nsIPrincipal> triggeringPrincipal;
33 nsCOMPtr<nsIURI> sourceURI;
34 ReferrerPolicy referrerPolicy = ReferrerPolicy::_empty;
35 nsCOMPtr<nsIReferrerInfo> referrerInfo;
37 // Get security manager.
38 nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
39 if (NS_WARN_IF(!ssm)) {
40 aRv.Throw(NS_ERROR_UNEXPECTED);
41 return nullptr;
44 // Check to see if URI is allowed. We're not going to worry about a
45 // window ID here because it's not 100% clear which window's id we
46 // would want, and we're throwing a content-visible exception
47 // anyway.
48 nsresult rv = ssm->CheckLoadURIWithPrincipal(
49 &aSubjectPrincipal, aURI, nsIScriptSecurityManager::STANDARD, 0);
50 if (NS_WARN_IF(NS_FAILED(rv))) {
51 nsAutoCString spec;
52 aURI->GetSpec(spec);
53 aRv.ThrowTypeError<MSG_URL_NOT_LOADABLE>(spec);
54 return nullptr;
57 // Make the load's referrer reflect changes to the document's URI caused by
58 // push/replaceState, if possible. First, get the document corresponding to
59 // fp. If the document's original URI (i.e. its URI before
60 // push/replaceState) matches the principal's URI, use the document's
61 // current URI as the referrer. If they don't match, use the principal's
62 // URI.
64 // The triggering principal for this load should be the principal of the
65 // incumbent document (which matches where the referrer information is
66 // coming from) when there is an incumbent document, and the subject
67 // principal otherwise. Note that the URI in the triggering principal
68 // may not match the referrer URI in various cases, notably including
69 // the cases when the incumbent document's document URI was modified
70 // after the document was loaded.
72 nsCOMPtr<nsPIDOMWindowInner> incumbent =
73 do_QueryInterface(mozilla::dom::GetIncumbentGlobal());
74 nsCOMPtr<Document> doc = incumbent ? incumbent->GetDoc() : nullptr;
76 // Create load info
77 RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(aURI);
79 if (!doc) {
80 // No document; just use our subject principal as the triggering principal.
81 loadState->SetTriggeringPrincipal(&aSubjectPrincipal);
82 return loadState.forget();
85 nsCOMPtr<nsIURI> docOriginalURI, docCurrentURI, principalURI;
86 docOriginalURI = doc->GetOriginalURI();
87 docCurrentURI = doc->GetDocumentURI();
88 nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
90 triggeringPrincipal = doc->NodePrincipal();
91 referrerPolicy = doc->GetReferrerPolicy();
93 bool urisEqual = false;
94 if (docOriginalURI && docCurrentURI && principal) {
95 principal->EqualsURI(docOriginalURI, &urisEqual);
97 if (urisEqual) {
98 referrerInfo = new ReferrerInfo(docCurrentURI, referrerPolicy);
99 } else {
100 principal->CreateReferrerInfo(referrerPolicy, getter_AddRefs(referrerInfo));
102 loadState->SetTriggeringPrincipal(triggeringPrincipal);
103 loadState->SetTriggeringSandboxFlags(doc->GetSandboxFlags());
104 loadState->SetCsp(doc->GetCsp());
105 if (referrerInfo) {
106 loadState->SetReferrerInfo(referrerInfo);
108 loadState->SetHasValidUserGestureActivation(
109 doc->HasValidTransientUserGestureActivation());
111 loadState->SetTriggeringWindowId(doc->InnerWindowID());
112 loadState->SetTriggeringStorageAccess(doc->UsingStorageAccess());
114 return loadState.forget();
117 void LocationBase::SetURI(nsIURI* aURI, nsIPrincipal& aSubjectPrincipal,
118 ErrorResult& aRv, bool aReplace) {
119 RefPtr<BrowsingContext> bc = GetBrowsingContext();
120 if (!bc || bc->IsDiscarded()) {
121 return;
124 CallerType callerType = aSubjectPrincipal.IsSystemPrincipal()
125 ? CallerType::System
126 : CallerType::NonSystem;
128 nsresult rv = bc->CheckLocationChangeRateLimit(callerType);
129 if (NS_FAILED(rv)) {
130 aRv.Throw(rv);
131 return;
134 RefPtr<nsDocShellLoadState> loadState =
135 CheckURL(aURI, aSubjectPrincipal, aRv);
136 if (aRv.Failed()) {
137 return;
140 if (aReplace) {
141 loadState->SetLoadType(LOAD_STOP_CONTENT_AND_REPLACE);
142 } else {
143 loadState->SetLoadType(LOAD_STOP_CONTENT);
146 // Get the incumbent script's browsing context to set as source.
147 nsCOMPtr<nsPIDOMWindowInner> sourceWindow =
148 nsContentUtils::IncumbentInnerWindow();
149 if (sourceWindow) {
150 WindowContext* context = sourceWindow->GetWindowContext();
151 loadState->SetSourceBrowsingContext(sourceWindow->GetBrowsingContext());
152 loadState->SetHasValidUserGestureActivation(
153 context && context->HasValidTransientUserGestureActivation());
156 loadState->SetLoadFlags(nsIWebNavigation::LOAD_FLAGS_NONE);
157 loadState->SetFirstParty(true);
159 rv = bc->LoadURI(loadState);
160 if (NS_WARN_IF(NS_FAILED(rv))) {
161 if (rv == NS_ERROR_DOM_BAD_CROSS_ORIGIN_URI &&
162 net::SchemeIsJavascript(loadState->URI())) {
163 // Per spec[1], attempting to load a javascript: URI into a cross-origin
164 // BrowsingContext is a no-op, and should not raise an exception.
165 // Technically, Location setters run with exceptions enabled should only
166 // throw an exception[2] when the caller is not allowed to navigate[3] the
167 // target browsing context due to sandboxing flags or not being
168 // closely-related enough, though in practice we currently throw for other
169 // reasons as well.
171 // [1]:
172 // https://html.spec.whatwg.org/multipage/browsing-the-web.html#javascript-protocol
173 // [2]:
174 // https://html.spec.whatwg.org/multipage/browsing-the-web.html#navigate
175 // [3]:
176 // https://html.spec.whatwg.org/multipage/browsers.html#allowed-to-navigate
177 return;
179 aRv.Throw(rv);
180 return;
183 Document* doc = bc->GetDocument();
184 if (doc && nsContentUtils::IsExternalProtocol(aURI)) {
185 doc->EnsureNotEnteringAndExitFullscreen();
189 void LocationBase::SetHref(const nsAString& aHref,
190 nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
191 DoSetHref(aHref, aSubjectPrincipal, false, aRv);
194 void LocationBase::DoSetHref(const nsAString& aHref,
195 nsIPrincipal& aSubjectPrincipal, bool aReplace,
196 ErrorResult& aRv) {
197 // Get the source of the caller
198 nsCOMPtr<nsIURI> base = GetSourceBaseURL();
199 SetHrefWithBase(aHref, base, aSubjectPrincipal, aReplace, aRv);
202 void LocationBase::SetHrefWithBase(const nsAString& aHref, nsIURI* aBase,
203 nsIPrincipal& aSubjectPrincipal,
204 bool aReplace, ErrorResult& aRv) {
205 nsresult result;
206 nsCOMPtr<nsIURI> newUri;
208 if (Document* doc = GetEntryDocument()) {
209 result = NS_NewURI(getter_AddRefs(newUri), aHref,
210 doc->GetDocumentCharacterSet(), aBase);
211 } else {
212 result = NS_NewURI(getter_AddRefs(newUri), aHref, nullptr, aBase);
215 if (NS_FAILED(result) || !newUri) {
216 aRv.ThrowSyntaxError("'"_ns + NS_ConvertUTF16toUTF8(aHref) +
217 "' is not a valid URL."_ns);
218 return;
221 /* Check with the scriptContext if it is currently processing a script tag.
222 * If so, this must be a <script> tag with a location.href in it.
223 * we want to do a replace load, in such a situation.
224 * In other cases, for example if a event handler or a JS timer
225 * had a location.href in it, we want to do a normal load,
226 * so that the new url will be appended to Session History.
227 * This solution is tricky. Hopefully it isn't going to bite
228 * anywhere else. This is part of solution for bug # 39938, 72197
230 bool inScriptTag = false;
231 nsIScriptContext* scriptContext = nullptr;
232 nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(GetEntryGlobal());
233 if (win) {
234 scriptContext = nsGlobalWindowInner::Cast(win)->GetContextInternal();
237 if (scriptContext) {
238 if (scriptContext->GetProcessingScriptTag()) {
239 // Now check to make sure that the script is running in our window,
240 // since we only want to replace if the location is set by a
241 // <script> tag in the same window. See bug 178729.
242 nsCOMPtr<nsIDocShell> docShell(GetDocShell());
243 nsCOMPtr<nsIScriptGlobalObject> ourGlobal =
244 docShell ? docShell->GetScriptGlobalObject() : nullptr;
245 inScriptTag = (ourGlobal == scriptContext->GetGlobalObject());
249 SetURI(newUri, aSubjectPrincipal, aRv, aReplace || inScriptTag);
252 void LocationBase::Replace(const nsAString& aUrl,
253 nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
254 DoSetHref(aUrl, aSubjectPrincipal, true, aRv);
257 nsIURI* LocationBase::GetSourceBaseURL() {
258 Document* doc = GetEntryDocument();
260 // If there's no entry document, we either have no Script Entry Point or one
261 // that isn't a DOM Window. This doesn't generally happen with the DOM, but
262 // can sometimes happen with extension code in certain IPC configurations. If
263 // this happens, try falling back on the current document associated with the
264 // docshell. If that fails, just return null and hope that the caller passed
265 // an absolute URI.
266 if (!doc) {
267 if (nsCOMPtr<nsIDocShell> docShell = GetDocShell()) {
268 nsCOMPtr<nsPIDOMWindowOuter> docShellWin =
269 do_QueryInterface(docShell->GetScriptGlobalObject());
270 if (docShellWin) {
271 doc = docShellWin->GetDoc();
275 return doc ? doc->GetBaseURI() : nullptr;
278 } // namespace mozilla::dom