Backed out 9 changesets (bug 1846848) for causing multiple build bustages. CLOSED...
[gecko.git] / dom / base / Location.cpp
blobcdf25abf56e17f96b821476ba62ae5460dde109d
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 "Location.h"
8 #include "nsIScriptObjectPrincipal.h"
9 #include "nsIScriptContext.h"
10 #include "nsDocShellLoadState.h"
11 #include "nsIWebNavigation.h"
12 #include "nsIOService.h"
13 #include "nsIURL.h"
14 #include "nsIJARURI.h"
15 #include "nsIURIMutator.h"
16 #include "nsNetUtil.h"
17 #include "nsCOMPtr.h"
18 #include "nsEscape.h"
19 #include "nsPresContext.h"
20 #include "nsError.h"
21 #include "nsReadableUtils.h"
22 #include "nsJSUtils.h"
23 #include "nsContentUtils.h"
24 #include "nsDocShell.h"
25 #include "nsGlobalWindowOuter.h"
26 #include "nsPIDOMWindowInlines.h"
27 #include "mozilla/Likely.h"
28 #include "nsCycleCollectionParticipant.h"
29 #include "mozilla/BasePrincipal.h"
30 #include "mozilla/Components.h"
31 #include "mozilla/NullPrincipal.h"
32 #include "mozilla/ServoStyleConsts.h"
33 #include "mozilla/StaticPrefs_dom.h"
34 #include "mozilla/Unused.h"
35 #include "mozilla/dom/Document.h"
36 #include "mozilla/dom/DocumentInlines.h"
37 #include "mozilla/dom/LocationBinding.h"
38 #include "mozilla/dom/ScriptSettings.h"
39 #include "ReferrerInfo.h"
41 namespace mozilla::dom {
43 Location::Location(nsPIDOMWindowInner* aWindow)
44 : mCachedHash(VoidString()), mInnerWindow(aWindow) {
45 BrowsingContext* bc = GetBrowsingContext();
46 if (bc) {
47 bc->LocationCreated(this);
51 Location::~Location() {
52 if (isInList()) {
53 remove();
57 // QueryInterface implementation for Location
58 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Location)
59 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
60 NS_INTERFACE_MAP_ENTRY(nsISupports)
61 NS_INTERFACE_MAP_END
63 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Location, mInnerWindow)
65 NS_IMPL_CYCLE_COLLECTING_ADDREF(Location)
66 NS_IMPL_CYCLE_COLLECTING_RELEASE(Location)
68 BrowsingContext* Location::GetBrowsingContext() {
69 return mInnerWindow ? mInnerWindow->GetBrowsingContext() : nullptr;
72 nsIDocShell* Location::GetDocShell() {
73 if (BrowsingContext* bc = GetBrowsingContext()) {
74 return bc->GetDocShell();
76 return nullptr;
79 nsresult Location::GetURI(nsIURI** aURI, bool aGetInnermostURI) {
80 *aURI = nullptr;
82 nsIDocShell* docShell = GetDocShell();
83 if (!docShell) {
84 return NS_OK;
87 nsIWebNavigation* webNav = nsDocShell::Cast(docShell);
89 nsCOMPtr<nsIURI> uri;
90 nsresult rv = webNav->GetCurrentURI(getter_AddRefs(uri));
91 NS_ENSURE_SUCCESS(rv, rv);
93 // It is valid for docshell to return a null URI. Don't try to fixup
94 // if this happens.
95 if (!uri) {
96 return NS_OK;
99 if (aGetInnermostURI) {
100 nsCOMPtr<nsIJARURI> jarURI(do_QueryInterface(uri));
101 while (jarURI) {
102 jarURI->GetJARFile(getter_AddRefs(uri));
103 jarURI = do_QueryInterface(uri);
107 NS_ASSERTION(uri, "nsJARURI screwed up?");
108 nsCOMPtr<nsIURI> exposableURI = net::nsIOService::CreateExposableURI(uri);
109 exposableURI.forget(aURI);
110 return NS_OK;
113 void Location::GetHash(nsAString& aHash, nsIPrincipal& aSubjectPrincipal,
114 ErrorResult& aRv) {
115 if (!CallerSubsumes(&aSubjectPrincipal)) {
116 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
117 return;
120 if (!mCachedHash.IsVoid()) {
121 aHash = mCachedHash;
122 return;
125 aHash.SetLength(0);
127 nsCOMPtr<nsIURI> uri;
128 aRv = GetURI(getter_AddRefs(uri));
129 if (NS_WARN_IF(aRv.Failed()) || !uri) {
130 return;
133 nsAutoCString ref;
135 aRv = uri->GetRef(ref);
136 if (NS_WARN_IF(aRv.Failed())) {
137 return;
140 if (!ref.IsEmpty()) {
141 aHash.Assign(char16_t('#'));
142 AppendUTF8toUTF16(ref, aHash);
145 mCachedHash = aHash;
148 void Location::SetHash(const nsAString& aHash, nsIPrincipal& aSubjectPrincipal,
149 ErrorResult& aRv) {
150 if (!CallerSubsumes(&aSubjectPrincipal)) {
151 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
152 return;
155 NS_ConvertUTF16toUTF8 hash(aHash);
156 if (hash.IsEmpty() || hash.First() != char16_t('#')) {
157 hash.Insert(char16_t('#'), 0);
160 nsCOMPtr<nsIURI> uri;
161 aRv = GetURI(getter_AddRefs(uri));
162 if (NS_WARN_IF(aRv.Failed()) || !uri) {
163 return;
166 aRv = NS_MutateURI(uri).SetRef(hash).Finalize(uri);
167 if (NS_WARN_IF(aRv.Failed()) || !uri) {
168 return;
171 SetURI(uri, aSubjectPrincipal, aRv);
174 void Location::GetHost(nsAString& aHost, nsIPrincipal& aSubjectPrincipal,
175 ErrorResult& aRv) {
176 if (!CallerSubsumes(&aSubjectPrincipal)) {
177 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
178 return;
181 aHost.Truncate();
183 nsCOMPtr<nsIURI> uri;
184 nsresult result;
186 result = GetURI(getter_AddRefs(uri), true);
188 if (uri) {
189 nsAutoCString hostport;
191 result = uri->GetHostPort(hostport);
193 if (NS_SUCCEEDED(result)) {
194 AppendUTF8toUTF16(hostport, aHost);
199 void Location::SetHost(const nsAString& aHost, nsIPrincipal& aSubjectPrincipal,
200 ErrorResult& aRv) {
201 if (!CallerSubsumes(&aSubjectPrincipal)) {
202 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
203 return;
206 nsCOMPtr<nsIURI> uri;
207 aRv = GetURI(getter_AddRefs(uri));
208 if (NS_WARN_IF(aRv.Failed()) || !uri) {
209 return;
212 aRv =
213 NS_MutateURI(uri).SetHostPort(NS_ConvertUTF16toUTF8(aHost)).Finalize(uri);
214 if (NS_WARN_IF(aRv.Failed())) {
215 return;
218 SetURI(uri, aSubjectPrincipal, aRv);
221 void Location::GetHostname(nsAString& aHostname,
222 nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
223 if (!CallerSubsumes(&aSubjectPrincipal)) {
224 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
225 return;
228 aHostname.Truncate();
230 nsCOMPtr<nsIURI> uri;
231 GetURI(getter_AddRefs(uri), true);
232 if (uri) {
233 nsContentUtils::GetHostOrIPv6WithBrackets(uri, aHostname);
237 void Location::SetHostname(const nsAString& aHostname,
238 nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
239 if (!CallerSubsumes(&aSubjectPrincipal)) {
240 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
241 return;
244 nsCOMPtr<nsIURI> uri;
245 aRv = GetURI(getter_AddRefs(uri));
246 if (NS_WARN_IF(aRv.Failed()) || !uri) {
247 return;
250 aRv =
251 NS_MutateURI(uri).SetHost(NS_ConvertUTF16toUTF8(aHostname)).Finalize(uri);
252 if (NS_WARN_IF(aRv.Failed())) {
253 return;
256 SetURI(uri, aSubjectPrincipal, aRv);
259 nsresult Location::GetHref(nsAString& aHref) {
260 aHref.Truncate();
262 nsCOMPtr<nsIURI> uri;
263 nsresult rv = GetURI(getter_AddRefs(uri));
264 if (NS_WARN_IF(NS_FAILED(rv)) || !uri) {
265 return rv;
268 nsAutoCString uriString;
269 rv = uri->GetSpec(uriString);
270 if (NS_WARN_IF(NS_FAILED(rv))) {
271 return rv;
274 AppendUTF8toUTF16(uriString, aHref);
275 return NS_OK;
278 void Location::GetOrigin(nsAString& aOrigin, nsIPrincipal& aSubjectPrincipal,
279 ErrorResult& aRv) {
280 if (!CallerSubsumes(&aSubjectPrincipal)) {
281 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
282 return;
285 aOrigin.Truncate();
287 nsCOMPtr<nsIURI> uri;
288 aRv = GetURI(getter_AddRefs(uri), true);
289 if (NS_WARN_IF(aRv.Failed()) || !uri) {
290 return;
293 nsAutoString origin;
294 aRv = nsContentUtils::GetWebExposedOriginSerialization(uri, origin);
295 if (NS_WARN_IF(aRv.Failed())) {
296 return;
299 aOrigin = origin;
302 void Location::GetPathname(nsAString& aPathname,
303 nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
304 if (!CallerSubsumes(&aSubjectPrincipal)) {
305 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
306 return;
309 aPathname.Truncate();
311 nsCOMPtr<nsIURI> uri;
312 aRv = GetURI(getter_AddRefs(uri));
313 if (NS_WARN_IF(aRv.Failed()) || !uri) {
314 return;
317 nsAutoCString file;
319 aRv = uri->GetFilePath(file);
320 if (NS_WARN_IF(aRv.Failed())) {
321 return;
324 AppendUTF8toUTF16(file, aPathname);
327 void Location::SetPathname(const nsAString& aPathname,
328 nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
329 if (!CallerSubsumes(&aSubjectPrincipal)) {
330 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
331 return;
334 nsCOMPtr<nsIURI> uri;
335 aRv = GetURI(getter_AddRefs(uri));
336 if (NS_WARN_IF(aRv.Failed()) || !uri) {
337 return;
340 nsresult rv = NS_MutateURI(uri)
341 .SetFilePath(NS_ConvertUTF16toUTF8(aPathname))
342 .Finalize(uri);
343 if (NS_FAILED(rv)) {
344 return;
347 SetURI(uri, aSubjectPrincipal, aRv);
350 void Location::GetPort(nsAString& aPort, nsIPrincipal& aSubjectPrincipal,
351 ErrorResult& aRv) {
352 if (!CallerSubsumes(&aSubjectPrincipal)) {
353 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
354 return;
357 aPort.SetLength(0);
359 nsCOMPtr<nsIURI> uri;
360 aRv = GetURI(getter_AddRefs(uri), true);
361 if (NS_WARN_IF(aRv.Failed()) || !uri) {
362 return;
365 int32_t port;
366 nsresult result = uri->GetPort(&port);
368 // Don't propagate this exception to caller
369 if (NS_SUCCEEDED(result) && -1 != port) {
370 nsAutoString portStr;
371 portStr.AppendInt(port);
372 aPort.Append(portStr);
376 void Location::SetPort(const nsAString& aPort, nsIPrincipal& aSubjectPrincipal,
377 ErrorResult& aRv) {
378 if (!CallerSubsumes(&aSubjectPrincipal)) {
379 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
380 return;
383 nsCOMPtr<nsIURI> uri;
384 aRv = GetURI(getter_AddRefs(uri));
385 if (NS_WARN_IF(aRv.Failed() || !uri)) {
386 return;
389 // perhaps use nsReadingIterators at some point?
390 NS_ConvertUTF16toUTF8 portStr(aPort);
391 const char* buf = portStr.get();
392 int32_t port = -1;
394 if (!portStr.IsEmpty() && buf) {
395 if (*buf == ':') {
396 port = atol(buf + 1);
397 } else {
398 port = atol(buf);
402 aRv = NS_MutateURI(uri).SetPort(port).Finalize(uri);
403 if (NS_WARN_IF(aRv.Failed())) {
404 return;
407 SetURI(uri, aSubjectPrincipal, aRv);
410 void Location::GetProtocol(nsAString& aProtocol,
411 nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
412 if (!CallerSubsumes(&aSubjectPrincipal)) {
413 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
414 return;
417 aProtocol.SetLength(0);
419 nsCOMPtr<nsIURI> uri;
420 aRv = GetURI(getter_AddRefs(uri));
421 if (NS_WARN_IF(aRv.Failed()) || !uri) {
422 return;
425 nsAutoCString protocol;
427 aRv = uri->GetScheme(protocol);
428 if (NS_WARN_IF(aRv.Failed())) {
429 return;
432 CopyASCIItoUTF16(protocol, aProtocol);
433 aProtocol.Append(char16_t(':'));
436 void Location::SetProtocol(const nsAString& aProtocol,
437 nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
438 if (!CallerSubsumes(&aSubjectPrincipal)) {
439 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
440 return;
443 nsCOMPtr<nsIURI> uri;
444 aRv = GetURI(getter_AddRefs(uri));
445 if (NS_WARN_IF(aRv.Failed()) || !uri) {
446 return;
449 nsAString::const_iterator start, end;
450 aProtocol.BeginReading(start);
451 aProtocol.EndReading(end);
452 nsAString::const_iterator iter(start);
453 Unused << FindCharInReadable(':', iter, end);
455 nsresult rv = NS_MutateURI(uri)
456 .SetScheme(NS_ConvertUTF16toUTF8(Substring(start, iter)))
457 .Finalize(uri);
458 if (NS_WARN_IF(NS_FAILED(rv))) {
459 // Oh, I wish nsStandardURL returned NS_ERROR_MALFORMED_URI for _all_ the
460 // malformed cases, not just some of them!
461 aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
462 return;
465 nsAutoCString newSpec;
466 aRv = uri->GetSpec(newSpec);
467 if (NS_WARN_IF(aRv.Failed())) {
468 return;
470 // We may want a new URI class for the new URI, so recreate it:
471 rv = NS_NewURI(getter_AddRefs(uri), newSpec);
472 if (NS_FAILED(rv)) {
473 if (rv == NS_ERROR_MALFORMED_URI) {
474 rv = NS_ERROR_DOM_SYNTAX_ERR;
477 aRv.Throw(rv);
478 return;
481 if (!uri->SchemeIs("http") && !uri->SchemeIs("https")) {
482 // No-op, per spec.
483 return;
486 SetURI(uri, aSubjectPrincipal, aRv);
489 void Location::GetSearch(nsAString& aSearch, nsIPrincipal& aSubjectPrincipal,
490 ErrorResult& aRv) {
491 if (!CallerSubsumes(&aSubjectPrincipal)) {
492 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
493 return;
496 aSearch.SetLength(0);
498 nsCOMPtr<nsIURI> uri;
499 nsresult result = NS_OK;
501 result = GetURI(getter_AddRefs(uri));
503 nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
505 if (url) {
506 nsAutoCString search;
508 result = url->GetQuery(search);
510 if (NS_SUCCEEDED(result) && !search.IsEmpty()) {
511 aSearch.Assign(char16_t('?'));
512 AppendUTF8toUTF16(search, aSearch);
517 void Location::SetSearch(const nsAString& aSearch,
518 nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
519 if (!CallerSubsumes(&aSubjectPrincipal)) {
520 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
521 return;
524 nsCOMPtr<nsIURI> uri;
525 aRv = GetURI(getter_AddRefs(uri));
526 nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
527 if (NS_WARN_IF(aRv.Failed()) || !url) {
528 return;
531 aRv =
532 NS_MutateURI(uri).SetQuery(NS_ConvertUTF16toUTF8(aSearch)).Finalize(uri);
533 if (NS_WARN_IF(aRv.Failed())) {
534 return;
537 SetURI(uri, aSubjectPrincipal, aRv);
540 void Location::Reload(bool aForceget, nsIPrincipal& aSubjectPrincipal,
541 ErrorResult& aRv) {
542 if (!CallerSubsumes(&aSubjectPrincipal)) {
543 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
544 return;
547 RefPtr<nsDocShell> docShell(nsDocShell::Cast(GetDocShell()));
548 if (!docShell) {
549 return aRv.Throw(NS_ERROR_FAILURE);
552 if (StaticPrefs::dom_block_reload_from_resize_event_handler()) {
553 nsCOMPtr<nsPIDOMWindowOuter> window = docShell->GetWindow();
554 if (window && window->IsHandlingResizeEvent()) {
555 // location.reload() was called on a window that is handling a
556 // resize event. Sites do this since Netscape 4.x needed it, but
557 // we don't, and it's a horrible experience for nothing. In stead
558 // of reloading the page, just clear style data and reflow the
559 // page since some sites may use this trick to work around gecko
560 // reflow bugs, and this should have the same effect.
561 RefPtr<Document> doc = window->GetExtantDoc();
563 nsPresContext* pcx;
564 if (doc && (pcx = doc->GetPresContext())) {
565 pcx->RebuildAllStyleData(NS_STYLE_HINT_REFLOW,
566 RestyleHint::RestyleSubtree());
568 return;
572 RefPtr<BrowsingContext> bc = GetBrowsingContext();
573 if (!bc || bc->IsDiscarded()) {
574 return;
577 CallerType callerType = aSubjectPrincipal.IsSystemPrincipal()
578 ? CallerType::System
579 : CallerType::NonSystem;
581 nsresult rv = bc->CheckLocationChangeRateLimit(callerType);
582 if (NS_FAILED(rv)) {
583 aRv.Throw(rv);
584 return;
587 uint32_t reloadFlags = nsIWebNavigation::LOAD_FLAGS_NONE;
589 if (aForceget) {
590 reloadFlags = nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE |
591 nsIWebNavigation::LOAD_FLAGS_BYPASS_PROXY;
594 rv = docShell->Reload(reloadFlags);
595 if (NS_FAILED(rv) && rv != NS_BINDING_ABORTED) {
596 // NS_BINDING_ABORTED is returned when we attempt to reload a POST result
597 // and the user says no at the "do you want to reload?" prompt. Don't
598 // propagate this one back to callers.
599 return aRv.Throw(rv);
603 void Location::Assign(const nsAString& aUrl, nsIPrincipal& aSubjectPrincipal,
604 ErrorResult& aRv) {
605 if (!CallerSubsumes(&aSubjectPrincipal)) {
606 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
607 return;
610 DoSetHref(aUrl, aSubjectPrincipal, false, aRv);
613 bool Location::CallerSubsumes(nsIPrincipal* aSubjectPrincipal) {
614 MOZ_ASSERT(aSubjectPrincipal);
616 BrowsingContext* bc = GetBrowsingContext();
617 if (MOZ_UNLIKELY(!bc) || MOZ_UNLIKELY(bc->IsDiscarded())) {
618 // Per spec, operations on a Location object with a discarded BC are no-ops,
619 // not security errors, so we need to return true from the access check and
620 // let the caller do its own discarded docShell check.
621 return true;
623 if (MOZ_UNLIKELY(!bc->IsInProcess())) {
624 return false;
627 // Get the principal associated with the location object. Note that this is
628 // the principal of the page which will actually be navigated, not the
629 // principal of the Location object itself. This is why we need this check
630 // even though we only allow limited cross-origin access to Location objects
631 // in general.
632 nsPIDOMWindowOuter* outer = bc->GetDOMWindow();
633 MOZ_DIAGNOSTIC_ASSERT(outer);
634 if (MOZ_UNLIKELY(!outer)) return false;
636 nsIScriptObjectPrincipal* sop = nsGlobalWindowOuter::Cast(outer);
637 bool subsumes = false;
638 nsresult rv = aSubjectPrincipal->SubsumesConsideringDomain(
639 sop->GetPrincipal(), &subsumes);
640 NS_ENSURE_SUCCESS(rv, false);
641 return subsumes;
644 JSObject* Location::WrapObject(JSContext* aCx,
645 JS::Handle<JSObject*> aGivenProto) {
646 return Location_Binding::Wrap(aCx, this, aGivenProto);
649 void Location::ClearCachedValues() { mCachedHash = VoidString(); }
651 } // namespace mozilla::dom