Line data Source code
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 "Location.h"
8 : #include "nsIScriptSecurityManager.h"
9 : #include "nsIScriptObjectPrincipal.h"
10 : #include "nsIScriptContext.h"
11 : #include "nsIDocShell.h"
12 : #include "nsIDocShellLoadInfo.h"
13 : #include "nsIWebNavigation.h"
14 : #include "nsCDefaultURIFixup.h"
15 : #include "nsIURIFixup.h"
16 : #include "nsIURL.h"
17 : #include "nsIJARURI.h"
18 : #include "nsNetUtil.h"
19 : #include "nsCOMPtr.h"
20 : #include "nsEscape.h"
21 : #include "nsIDOMWindow.h"
22 : #include "nsIDocument.h"
23 : #include "nsIPresShell.h"
24 : #include "nsPresContext.h"
25 : #include "nsError.h"
26 : #include "nsDOMClassInfoID.h"
27 : #include "nsReadableUtils.h"
28 : #include "nsITextToSubURI.h"
29 : #include "nsJSUtils.h"
30 : #include "nsContentUtils.h"
31 : #include "nsGlobalWindow.h"
32 : #include "mozilla/Likely.h"
33 : #include "nsCycleCollectionParticipant.h"
34 : #include "NullPrincipal.h"
35 : #include "mozilla/Unused.h"
36 : #include "mozilla/dom/LocationBinding.h"
37 : #include "mozilla/dom/ScriptSettings.h"
38 :
39 : namespace mozilla {
40 : namespace dom {
41 :
42 3 : Location::Location(nsPIDOMWindowInner* aWindow, nsIDocShell *aDocShell)
43 3 : : mInnerWindow(aWindow)
44 : {
45 3 : MOZ_ASSERT(aDocShell);
46 3 : MOZ_ASSERT(mInnerWindow->IsInnerWindow());
47 :
48 3 : mDocShell = do_GetWeakReference(aDocShell);
49 3 : }
50 :
51 0 : Location::~Location()
52 : {
53 0 : }
54 :
55 : // QueryInterface implementation for Location
56 59 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Location)
57 6 : NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
58 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
59 0 : NS_INTERFACE_MAP_END
60 :
61 19 : NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Location, mInnerWindow)
62 :
63 7 : NS_IMPL_CYCLE_COLLECTING_ADDREF(Location)
64 1 : NS_IMPL_CYCLE_COLLECTING_RELEASE(Location)
65 :
66 : nsresult
67 0 : Location::CheckURL(nsIURI* aURI, nsIDocShellLoadInfo** aLoadInfo)
68 : {
69 0 : *aLoadInfo = nullptr;
70 :
71 0 : nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mDocShell));
72 0 : NS_ENSURE_TRUE(docShell, NS_ERROR_NOT_AVAILABLE);
73 :
74 0 : nsCOMPtr<nsIPrincipal> triggeringPrincipal;
75 0 : nsCOMPtr<nsIURI> sourceURI;
76 0 : net::ReferrerPolicy referrerPolicy = net::RP_Unset;
77 :
78 0 : if (JSContext *cx = nsContentUtils::GetCurrentJSContext()) {
79 : // No cx means that there's no JS running, or at least no JS that
80 : // was run through code that properly pushed a context onto the
81 : // context stack (as all code that runs JS off of web pages
82 : // does). We won't bother with security checks in this case, but
83 : // we need to create the loadinfo etc.
84 :
85 : // Get security manager.
86 0 : nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
87 0 : NS_ENSURE_STATE(ssm);
88 :
89 : // Check to see if URI is allowed.
90 0 : nsresult rv = ssm->CheckLoadURIFromScript(cx, aURI);
91 0 : NS_ENSURE_SUCCESS(rv, rv);
92 :
93 : // Make the load's referrer reflect changes to the document's URI caused by
94 : // push/replaceState, if possible. First, get the document corresponding to
95 : // fp. If the document's original URI (i.e. its URI before
96 : // push/replaceState) matches the principal's URI, use the document's
97 : // current URI as the referrer. If they don't match, use the principal's
98 : // URI.
99 : //
100 : // The triggering principal for this load should be the principal of the
101 : // incumbent document (which matches where the referrer information is
102 : // coming from) when there is an incumbent document, and the subject
103 : // principal otherwise. Note that the URI in the triggering principal
104 : // may not match the referrer URI in various cases, notably including
105 : // the cases when the incumbent document's document URI was modified
106 : // after the document was loaded.
107 :
108 : nsCOMPtr<nsPIDOMWindowInner> incumbent =
109 0 : do_QueryInterface(mozilla::dom::GetIncumbentGlobal());
110 0 : nsCOMPtr<nsIDocument> doc = incumbent ? incumbent->GetDoc() : nullptr;
111 :
112 0 : if (doc) {
113 0 : nsCOMPtr<nsIURI> docOriginalURI, docCurrentURI, principalURI;
114 0 : docOriginalURI = doc->GetOriginalURI();
115 0 : docCurrentURI = doc->GetDocumentURI();
116 0 : rv = doc->NodePrincipal()->GetURI(getter_AddRefs(principalURI));
117 0 : NS_ENSURE_SUCCESS(rv, rv);
118 :
119 0 : triggeringPrincipal = doc->NodePrincipal();
120 0 : referrerPolicy = doc->GetReferrerPolicy();
121 :
122 0 : bool urisEqual = false;
123 0 : if (docOriginalURI && docCurrentURI && principalURI) {
124 0 : principalURI->Equals(docOriginalURI, &urisEqual);
125 : }
126 0 : if (urisEqual) {
127 0 : sourceURI = docCurrentURI;
128 : }
129 : else {
130 : // Use principalURI as long as it is not an NullPrincipalURI. We
131 : // could add a method such as GetReferrerURI to principals to make this
132 : // cleaner, but given that we need to start using Source Browsing
133 : // Context for referrer (see Bug 960639) this may be wasted effort at
134 : // this stage.
135 0 : if (principalURI) {
136 : bool isNullPrincipalScheme;
137 0 : rv = principalURI->SchemeIs(NS_NULLPRINCIPAL_SCHEME,
138 0 : &isNullPrincipalScheme);
139 0 : if (NS_SUCCEEDED(rv) && !isNullPrincipalScheme) {
140 0 : sourceURI = principalURI;
141 : }
142 : }
143 : }
144 : }
145 : else {
146 : // No document; determine triggeringPrincipal by quering the
147 : // subjectPrincipal, wich is the principal of the current JS
148 : // compartment, or a null principal in case there is no
149 : // compartment yet.
150 0 : triggeringPrincipal = nsContentUtils::SubjectPrincipal();
151 : }
152 : }
153 :
154 : // Create load info
155 0 : nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
156 0 : docShell->CreateLoadInfo(getter_AddRefs(loadInfo));
157 0 : NS_ENSURE_TRUE(loadInfo, NS_ERROR_FAILURE);
158 :
159 0 : loadInfo->SetTriggeringPrincipal(triggeringPrincipal);
160 :
161 0 : if (sourceURI) {
162 0 : loadInfo->SetReferrer(sourceURI);
163 0 : loadInfo->SetReferrerPolicy(referrerPolicy);
164 : }
165 :
166 0 : loadInfo.swap(*aLoadInfo);
167 :
168 0 : return NS_OK;
169 : }
170 :
171 : nsresult
172 8 : Location::GetURI(nsIURI** aURI, bool aGetInnermostURI)
173 : {
174 8 : *aURI = nullptr;
175 :
176 16 : nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mDocShell));
177 8 : if (!mDocShell) {
178 0 : return NS_OK;
179 : }
180 :
181 : nsresult rv;
182 16 : nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(docShell, &rv));
183 8 : if (NS_FAILED(rv)) {
184 0 : return rv;
185 : }
186 :
187 16 : nsCOMPtr<nsIURI> uri;
188 8 : rv = webNav->GetCurrentURI(getter_AddRefs(uri));
189 8 : NS_ENSURE_SUCCESS(rv, rv);
190 :
191 : // It is valid for docshell to return a null URI. Don't try to fixup
192 : // if this happens.
193 8 : if (!uri) {
194 0 : return NS_OK;
195 : }
196 :
197 8 : if (aGetInnermostURI) {
198 2 : nsCOMPtr<nsIJARURI> jarURI(do_QueryInterface(uri));
199 1 : while (jarURI) {
200 0 : jarURI->GetJARFile(getter_AddRefs(uri));
201 0 : jarURI = do_QueryInterface(uri);
202 : }
203 : }
204 :
205 8 : NS_ASSERTION(uri, "nsJARURI screwed up?");
206 :
207 16 : nsCOMPtr<nsIURIFixup> urifixup(do_GetService(NS_URIFIXUP_CONTRACTID, &rv));
208 8 : NS_ENSURE_SUCCESS(rv, rv);
209 :
210 8 : return urifixup->CreateExposableURI(uri, aURI);
211 : }
212 :
213 : nsresult
214 0 : Location::GetWritableURI(nsIURI** aURI, const nsACString* aNewRef)
215 : {
216 0 : *aURI = nullptr;
217 :
218 0 : nsCOMPtr<nsIURI> uri;
219 :
220 0 : nsresult rv = GetURI(getter_AddRefs(uri));
221 0 : if (NS_FAILED(rv) || !uri) {
222 0 : return rv;
223 : }
224 :
225 0 : if (!aNewRef) {
226 0 : return uri->Clone(aURI);
227 : }
228 :
229 0 : return uri->CloneWithNewRef(*aNewRef, aURI);
230 : }
231 :
232 : nsresult
233 0 : Location::SetURI(nsIURI* aURI, bool aReplace)
234 : {
235 0 : nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mDocShell));
236 0 : if (docShell) {
237 0 : nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
238 :
239 0 : if(NS_FAILED(CheckURL(aURI, getter_AddRefs(loadInfo))))
240 0 : return NS_ERROR_FAILURE;
241 :
242 0 : if (aReplace) {
243 0 : loadInfo->SetLoadType(nsIDocShellLoadInfo::loadStopContentAndReplace);
244 : } else {
245 0 : loadInfo->SetLoadType(nsIDocShellLoadInfo::loadStopContent);
246 : }
247 :
248 : // Get the incumbent script's browsing context to set as source.
249 : nsCOMPtr<nsPIDOMWindowInner> sourceWindow =
250 0 : do_QueryInterface(mozilla::dom::GetIncumbentGlobal());
251 0 : if (sourceWindow) {
252 0 : loadInfo->SetSourceDocShell(sourceWindow->GetDocShell());
253 : }
254 :
255 0 : return docShell->LoadURI(aURI, loadInfo,
256 0 : nsIWebNavigation::LOAD_FLAGS_NONE, true);
257 : }
258 :
259 0 : return NS_OK;
260 : }
261 :
262 : void
263 0 : Location::GetHash(nsAString& aHash,
264 : nsIPrincipal& aSubjectPrincipal,
265 : ErrorResult& aRv)
266 : {
267 0 : if (!CallerSubsumes(&aSubjectPrincipal)) {
268 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
269 0 : return;
270 : }
271 :
272 0 : aHash.SetLength(0);
273 :
274 0 : nsCOMPtr<nsIURI> uri;
275 0 : aRv = GetURI(getter_AddRefs(uri));
276 0 : if (NS_WARN_IF(aRv.Failed()) || !uri) {
277 0 : return;
278 : }
279 :
280 0 : nsAutoCString ref;
281 0 : nsAutoString unicodeRef;
282 :
283 0 : aRv = uri->GetRef(ref);
284 0 : if (NS_WARN_IF(aRv.Failed())) {
285 0 : return;
286 : }
287 :
288 0 : if (!ref.IsEmpty()) {
289 0 : aHash.Assign(char16_t('#'));
290 0 : AppendUTF8toUTF16(ref, aHash);
291 : }
292 :
293 0 : if (aHash == mCachedHash) {
294 : // Work around ShareThis stupidly polling location.hash every
295 : // 5ms all the time by handing out the same exact string buffer
296 : // we handed out last time.
297 0 : aHash = mCachedHash;
298 : } else {
299 0 : mCachedHash = aHash;
300 : }
301 : }
302 :
303 : void
304 0 : Location::SetHash(const nsAString& aHash,
305 : nsIPrincipal& aSubjectPrincipal,
306 : ErrorResult& aRv)
307 : {
308 0 : if (!CallerSubsumes(&aSubjectPrincipal)) {
309 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
310 0 : return;
311 : }
312 :
313 0 : NS_ConvertUTF16toUTF8 hash(aHash);
314 0 : if (hash.IsEmpty() || hash.First() != char16_t('#')) {
315 0 : hash.Insert(char16_t('#'), 0);
316 : }
317 :
318 0 : nsCOMPtr<nsIURI> uri;
319 0 : aRv = GetWritableURI(getter_AddRefs(uri), &hash);
320 0 : if (NS_WARN_IF(aRv.Failed()) || !uri) {
321 0 : return;
322 : }
323 :
324 0 : aRv = SetURI(uri);
325 : }
326 :
327 : void
328 0 : Location::GetHost(nsAString& aHost,
329 : nsIPrincipal& aSubjectPrincipal,
330 : ErrorResult& aRv)
331 : {
332 0 : if (!CallerSubsumes(&aSubjectPrincipal)) {
333 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
334 0 : return;
335 : }
336 :
337 0 : aHost.Truncate();
338 :
339 0 : nsCOMPtr<nsIURI> uri;
340 : nsresult result;
341 :
342 0 : result = GetURI(getter_AddRefs(uri), true);
343 :
344 0 : if (uri) {
345 0 : nsAutoCString hostport;
346 :
347 0 : result = uri->GetHostPort(hostport);
348 :
349 0 : if (NS_SUCCEEDED(result)) {
350 0 : AppendUTF8toUTF16(hostport, aHost);
351 : }
352 : }
353 : }
354 :
355 : void
356 0 : Location::SetHost(const nsAString& aHost,
357 : nsIPrincipal& aSubjectPrincipal,
358 : ErrorResult& aRv)
359 : {
360 0 : if (!CallerSubsumes(&aSubjectPrincipal)) {
361 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
362 0 : return;
363 : }
364 :
365 0 : nsCOMPtr<nsIURI> uri;
366 0 : aRv = GetWritableURI(getter_AddRefs(uri));
367 0 : if (NS_WARN_IF(aRv.Failed()) || !uri) {
368 0 : return;
369 : }
370 :
371 0 : aRv = uri->SetHostPort(NS_ConvertUTF16toUTF8(aHost));
372 0 : if (NS_WARN_IF(aRv.Failed())) {
373 0 : return;
374 : }
375 :
376 0 : aRv = SetURI(uri);
377 : }
378 :
379 : void
380 1 : Location::GetHostname(nsAString& aHostname,
381 : nsIPrincipal& aSubjectPrincipal,
382 : ErrorResult& aRv)
383 : {
384 1 : if (!CallerSubsumes(&aSubjectPrincipal)) {
385 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
386 0 : return;
387 : }
388 :
389 1 : aHostname.Truncate();
390 :
391 2 : nsCOMPtr<nsIURI> uri;
392 1 : GetURI(getter_AddRefs(uri), true);
393 1 : if (uri) {
394 1 : nsContentUtils::GetHostOrIPv6WithBrackets(uri, aHostname);
395 : }
396 : }
397 :
398 : void
399 0 : Location::SetHostname(const nsAString& aHostname,
400 : nsIPrincipal& aSubjectPrincipal,
401 : ErrorResult& aRv)
402 : {
403 0 : if (!CallerSubsumes(&aSubjectPrincipal)) {
404 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
405 0 : return;
406 : }
407 :
408 0 : nsCOMPtr<nsIURI> uri;
409 0 : aRv = GetWritableURI(getter_AddRefs(uri));
410 0 : if (NS_WARN_IF(aRv.Failed()) || !uri) {
411 0 : return;
412 : }
413 :
414 0 : aRv = uri->SetHost(NS_ConvertUTF16toUTF8(aHostname));
415 0 : if (NS_WARN_IF(aRv.Failed())) {
416 0 : return;
417 : }
418 :
419 0 : aRv = SetURI(uri);
420 : }
421 :
422 : nsresult
423 5 : Location::GetHref(nsAString& aHref)
424 : {
425 5 : aHref.Truncate();
426 :
427 10 : nsCOMPtr<nsIURI> uri;
428 5 : nsresult rv = GetURI(getter_AddRefs(uri));
429 5 : if (NS_WARN_IF(NS_FAILED(rv)) || !uri) {
430 0 : return rv;
431 : }
432 :
433 10 : nsAutoCString uriString;
434 5 : rv = uri->GetSpec(uriString);
435 5 : if (NS_WARN_IF(NS_FAILED(rv))) {
436 0 : return rv;
437 : }
438 :
439 5 : AppendUTF8toUTF16(uriString, aHref);
440 5 : return NS_OK;
441 : }
442 :
443 : void
444 0 : Location::SetHref(const nsAString& aHref,
445 : nsIPrincipal& aSubjectPrincipal,
446 : ErrorResult& aRv)
447 : {
448 0 : JSContext *cx = nsContentUtils::GetCurrentJSContext();
449 0 : if (cx) {
450 0 : aRv = SetHrefWithContext(cx, aHref, false);
451 0 : return;
452 : }
453 :
454 0 : nsAutoString oldHref;
455 0 : aRv = GetHref(oldHref);
456 0 : if (NS_WARN_IF(aRv.Failed())) {
457 0 : return;
458 : }
459 :
460 0 : nsCOMPtr<nsIURI> oldUri;
461 0 : aRv = NS_NewURI(getter_AddRefs(oldUri), oldHref);
462 0 : if (NS_WARN_IF(aRv.Failed())) {
463 0 : return;
464 : }
465 :
466 0 : aRv = SetHrefWithBase(aHref, oldUri, false);
467 0 : if (NS_WARN_IF(aRv.Failed())) {
468 0 : return;
469 : }
470 : }
471 :
472 : nsresult
473 0 : Location::SetHrefWithContext(JSContext* cx, const nsAString& aHref,
474 : bool aReplace)
475 : {
476 0 : nsCOMPtr<nsIURI> base;
477 :
478 : // Get the source of the caller
479 0 : nsresult result = GetSourceBaseURL(cx, getter_AddRefs(base));
480 :
481 0 : if (NS_FAILED(result)) {
482 0 : return result;
483 : }
484 :
485 0 : return SetHrefWithBase(aHref, base, aReplace);
486 : }
487 :
488 : nsresult
489 0 : Location::SetHrefWithBase(const nsAString& aHref, nsIURI* aBase,
490 : bool aReplace)
491 : {
492 : nsresult result;
493 0 : nsCOMPtr<nsIURI> newUri;
494 :
495 0 : nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mDocShell));
496 :
497 0 : if (nsIDocument* doc = GetEntryDocument()) {
498 0 : result = NS_NewURI(getter_AddRefs(newUri), aHref,
499 0 : doc->GetDocumentCharacterSet(), aBase);
500 : } else {
501 0 : result = NS_NewURI(getter_AddRefs(newUri), aHref, nullptr, aBase);
502 : }
503 :
504 0 : if (newUri) {
505 : /* Check with the scriptContext if it is currently processing a script tag.
506 : * If so, this must be a <script> tag with a location.href in it.
507 : * we want to do a replace load, in such a situation.
508 : * In other cases, for example if a event handler or a JS timer
509 : * had a location.href in it, we want to do a normal load,
510 : * so that the new url will be appended to Session History.
511 : * This solution is tricky. Hopefully it isn't going to bite
512 : * anywhere else. This is part of solution for bug # 39938, 72197
513 : */
514 0 : bool inScriptTag = false;
515 0 : nsIScriptContext* scriptContext = nullptr;
516 0 : nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(GetEntryGlobal());
517 0 : if (win) {
518 0 : scriptContext = nsGlobalWindow::Cast(win)->GetContextInternal();
519 : }
520 :
521 0 : if (scriptContext) {
522 0 : if (scriptContext->GetProcessingScriptTag()) {
523 : // Now check to make sure that the script is running in our window,
524 : // since we only want to replace if the location is set by a
525 : // <script> tag in the same window. See bug 178729.
526 : nsCOMPtr<nsIScriptGlobalObject> ourGlobal =
527 0 : docShell ? docShell->GetScriptGlobalObject() : nullptr;
528 0 : inScriptTag = (ourGlobal == scriptContext->GetGlobalObject());
529 : }
530 : }
531 :
532 0 : return SetURI(newUri, aReplace || inScriptTag);
533 : }
534 :
535 0 : return result;
536 : }
537 :
538 : void
539 0 : Location::GetOrigin(nsAString& aOrigin,
540 : nsIPrincipal& aSubjectPrincipal,
541 : ErrorResult& aRv)
542 : {
543 0 : if (!CallerSubsumes(&aSubjectPrincipal)) {
544 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
545 0 : return;
546 : }
547 :
548 0 : aOrigin.Truncate();
549 :
550 0 : nsCOMPtr<nsIURI> uri;
551 0 : aRv = GetURI(getter_AddRefs(uri), true);
552 0 : if (NS_WARN_IF(aRv.Failed()) || !uri) {
553 0 : return;
554 : }
555 :
556 0 : nsAutoString origin;
557 0 : aRv = nsContentUtils::GetUTFOrigin(uri, origin);
558 0 : if (NS_WARN_IF(aRv.Failed())) {
559 0 : return;
560 : }
561 :
562 0 : aOrigin = origin;
563 : }
564 :
565 : void
566 0 : Location::GetPathname(nsAString& aPathname,
567 : nsIPrincipal& aSubjectPrincipal,
568 : ErrorResult& aRv)
569 : {
570 0 : if (!CallerSubsumes(&aSubjectPrincipal)) {
571 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
572 0 : return;
573 : }
574 :
575 0 : aPathname.Truncate();
576 :
577 0 : nsCOMPtr<nsIURI> uri;
578 0 : aRv = GetURI(getter_AddRefs(uri));
579 0 : if (NS_WARN_IF(aRv.Failed()) || !uri) {
580 0 : return;
581 : }
582 :
583 0 : nsAutoCString file;
584 :
585 0 : aRv = uri->GetFilePath(file);
586 0 : if (NS_WARN_IF(aRv.Failed())) {
587 0 : return;
588 : }
589 :
590 0 : AppendUTF8toUTF16(file, aPathname);
591 : }
592 :
593 : void
594 0 : Location::SetPathname(const nsAString& aPathname,
595 : nsIPrincipal& aSubjectPrincipal,
596 : ErrorResult& aRv)
597 : {
598 0 : if (!CallerSubsumes(&aSubjectPrincipal)) {
599 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
600 0 : return;
601 : }
602 :
603 0 : nsCOMPtr<nsIURI> uri;
604 0 : aRv = GetWritableURI(getter_AddRefs(uri));
605 0 : if (NS_WARN_IF(aRv.Failed()) || !uri) {
606 0 : return;
607 : }
608 :
609 0 : if (NS_SUCCEEDED(uri->SetFilePath(NS_ConvertUTF16toUTF8(aPathname)))) {
610 0 : aRv = SetURI(uri);
611 : }
612 : }
613 :
614 : void
615 0 : Location::GetPort(nsAString& aPort,
616 : nsIPrincipal& aSubjectPrincipal,
617 : ErrorResult& aRv)
618 : {
619 0 : if (!CallerSubsumes(&aSubjectPrincipal)) {
620 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
621 0 : return;
622 : }
623 :
624 0 : aPort.SetLength(0);
625 :
626 0 : nsCOMPtr<nsIURI> uri;
627 0 : aRv = GetURI(getter_AddRefs(uri), true);
628 0 : if (NS_WARN_IF(aRv.Failed()) || !uri) {
629 0 : return;
630 : }
631 :
632 : int32_t port;
633 0 : nsresult result = uri->GetPort(&port);
634 :
635 : // Don't propagate this exception to caller
636 0 : if (NS_SUCCEEDED(result) && -1 != port) {
637 0 : nsAutoString portStr;
638 0 : portStr.AppendInt(port);
639 0 : aPort.Append(portStr);
640 : }
641 : }
642 :
643 : void
644 0 : Location::SetPort(const nsAString& aPort,
645 : nsIPrincipal& aSubjectPrincipal,
646 : ErrorResult& aRv)
647 : {
648 0 : if (!CallerSubsumes(&aSubjectPrincipal)) {
649 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
650 0 : return;
651 : }
652 :
653 0 : nsCOMPtr<nsIURI> uri;
654 0 : aRv = GetWritableURI(getter_AddRefs(uri));
655 0 : if (NS_WARN_IF(aRv.Failed() || !uri)) {
656 0 : return;
657 : }
658 :
659 : // perhaps use nsReadingIterators at some point?
660 0 : NS_ConvertUTF16toUTF8 portStr(aPort);
661 0 : const char *buf = portStr.get();
662 0 : int32_t port = -1;
663 :
664 0 : if (!portStr.IsEmpty() && buf) {
665 0 : if (*buf == ':') {
666 0 : port = atol(buf+1);
667 : }
668 : else {
669 0 : port = atol(buf);
670 : }
671 : }
672 :
673 0 : aRv = uri->SetPort(port);
674 0 : if (NS_WARN_IF(aRv.Failed())) {
675 0 : return;
676 : }
677 :
678 0 : aRv = SetURI(uri);
679 : }
680 :
681 : void
682 1 : Location::GetProtocol(nsAString& aProtocol,
683 : nsIPrincipal& aSubjectPrincipal,
684 : ErrorResult& aRv)
685 : {
686 1 : if (!CallerSubsumes(&aSubjectPrincipal)) {
687 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
688 0 : return;
689 : }
690 :
691 1 : aProtocol.SetLength(0);
692 :
693 2 : nsCOMPtr<nsIURI> uri;
694 1 : aRv = GetURI(getter_AddRefs(uri));
695 1 : if (NS_WARN_IF(aRv.Failed()) || !uri) {
696 0 : return;
697 : }
698 :
699 2 : nsAutoCString protocol;
700 :
701 1 : aRv = uri->GetScheme(protocol);
702 1 : if (NS_WARN_IF(aRv.Failed())) {
703 0 : return;
704 : }
705 :
706 1 : CopyASCIItoUTF16(protocol, aProtocol);
707 1 : aProtocol.Append(char16_t(':'));
708 : }
709 :
710 : void
711 0 : Location::SetProtocol(const nsAString& aProtocol,
712 : nsIPrincipal& aSubjectPrincipal,
713 : ErrorResult& aRv)
714 : {
715 0 : if (!CallerSubsumes(&aSubjectPrincipal)) {
716 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
717 0 : return;
718 : }
719 :
720 0 : nsCOMPtr<nsIURI> uri;
721 0 : aRv = GetWritableURI(getter_AddRefs(uri));
722 0 : if (NS_WARN_IF(aRv.Failed()) || !uri) {
723 0 : return;
724 : }
725 :
726 0 : nsAString::const_iterator start, end;
727 0 : aProtocol.BeginReading(start);
728 0 : aProtocol.EndReading(end);
729 0 : nsAString::const_iterator iter(start);
730 0 : Unused << FindCharInReadable(':', iter, end);
731 :
732 0 : nsresult rv = uri->SetScheme(NS_ConvertUTF16toUTF8(Substring(start, iter)));
733 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
734 : // Oh, I wish nsStandardURL returned NS_ERROR_MALFORMED_URI for _all_ the
735 : // malformed cases, not just some of them!
736 0 : aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
737 0 : return;
738 : }
739 :
740 0 : nsAutoCString newSpec;
741 0 : aRv = uri->GetSpec(newSpec);
742 0 : if (NS_WARN_IF(aRv.Failed())) {
743 0 : return;
744 : }
745 : // We may want a new URI class for the new URI, so recreate it:
746 0 : rv = NS_NewURI(getter_AddRefs(uri), newSpec);
747 0 : if (NS_FAILED(rv)) {
748 0 : if (rv == NS_ERROR_MALFORMED_URI) {
749 0 : rv = NS_ERROR_DOM_SYNTAX_ERR;
750 : }
751 :
752 0 : aRv.Throw(rv);
753 0 : return;
754 : }
755 :
756 : bool isHttp;
757 0 : aRv = uri->SchemeIs("http", &isHttp);
758 0 : if (NS_WARN_IF(aRv.Failed())) {
759 0 : return;
760 : }
761 :
762 : bool isHttps;
763 0 : aRv = uri->SchemeIs("https", &isHttps);
764 0 : if (NS_WARN_IF(aRv.Failed())) {
765 0 : return;
766 : }
767 :
768 0 : if (!isHttp && !isHttps) {
769 : // No-op, per spec.
770 0 : return;
771 : }
772 :
773 0 : aRv = SetURI(uri);
774 : }
775 :
776 : void
777 1 : Location::GetSearch(nsAString& aSearch,
778 : nsIPrincipal& aSubjectPrincipal,
779 : ErrorResult& aRv)
780 : {
781 1 : if (!CallerSubsumes(&aSubjectPrincipal)) {
782 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
783 0 : return;
784 : }
785 :
786 1 : aSearch.SetLength(0);
787 :
788 2 : nsCOMPtr<nsIURI> uri;
789 1 : nsresult result = NS_OK;
790 :
791 1 : result = GetURI(getter_AddRefs(uri));
792 :
793 2 : nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
794 :
795 1 : if (url) {
796 2 : nsAutoCString search;
797 :
798 1 : result = url->GetQuery(search);
799 :
800 1 : if (NS_SUCCEEDED(result) && !search.IsEmpty()) {
801 0 : aSearch.Assign(char16_t('?'));
802 0 : AppendUTF8toUTF16(search, aSearch);
803 : }
804 : }
805 : }
806 :
807 : void
808 0 : Location::SetSearch(const nsAString& aSearch,
809 : nsIPrincipal& aSubjectPrincipal,
810 : ErrorResult& aRv)
811 : {
812 0 : if (!CallerSubsumes(&aSubjectPrincipal)) {
813 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
814 0 : return;
815 : }
816 :
817 0 : nsCOMPtr<nsIURI> uri;
818 0 : aRv = GetWritableURI(getter_AddRefs(uri));
819 0 : nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
820 0 : if (NS_WARN_IF(aRv.Failed()) || !url) {
821 0 : return;
822 : }
823 :
824 0 : aRv = url->SetQuery(NS_ConvertUTF16toUTF8(aSearch));
825 0 : if (NS_WARN_IF(aRv.Failed())) {
826 0 : return;
827 : }
828 :
829 0 : aRv = SetURI(uri);
830 : }
831 :
832 : nsresult
833 0 : Location::Reload(bool aForceget)
834 : {
835 0 : nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mDocShell));
836 0 : nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(docShell));
837 0 : nsCOMPtr<nsPIDOMWindowOuter> window = docShell ? docShell->GetWindow()
838 0 : : nullptr;
839 :
840 0 : if (window && window->IsHandlingResizeEvent()) {
841 : // location.reload() was called on a window that is handling a
842 : // resize event. Sites do this since Netscape 4.x needed it, but
843 : // we don't, and it's a horrible experience for nothing. In stead
844 : // of reloading the page, just clear style data and reflow the
845 : // page since some sites may use this trick to work around gecko
846 : // reflow bugs, and this should have the same effect.
847 :
848 0 : nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
849 :
850 : nsIPresShell *shell;
851 : nsPresContext *pcx;
852 0 : if (doc && (shell = doc->GetShell()) && (pcx = shell->GetPresContext())) {
853 0 : pcx->RebuildAllStyleData(NS_STYLE_HINT_REFLOW, eRestyle_Subtree);
854 : }
855 :
856 0 : return NS_OK;
857 : }
858 :
859 0 : if (!webNav) {
860 0 : return NS_ERROR_FAILURE;
861 : }
862 :
863 0 : uint32_t reloadFlags = nsIWebNavigation::LOAD_FLAGS_NONE;
864 :
865 0 : if (aForceget) {
866 0 : reloadFlags = nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE |
867 : nsIWebNavigation::LOAD_FLAGS_BYPASS_PROXY;
868 : }
869 :
870 0 : nsresult rv = webNav->Reload(reloadFlags);
871 0 : if (rv == NS_BINDING_ABORTED) {
872 : // This happens when we attempt to reload a POST result and the user says
873 : // no at the "do you want to reload?" prompt. Don't propagate this one
874 : // back to callers.
875 0 : rv = NS_OK;
876 : }
877 :
878 0 : return rv;
879 : }
880 :
881 : void
882 0 : Location::Replace(const nsAString& aUrl,
883 : nsIPrincipal& aSubjectPrincipal,
884 : ErrorResult& aRv)
885 : {
886 0 : if (JSContext *cx = nsContentUtils::GetCurrentJSContext()) {
887 0 : aRv = SetHrefWithContext(cx, aUrl, true);
888 0 : return;
889 : }
890 :
891 0 : nsAutoString oldHref;
892 0 : aRv = GetHref(oldHref);
893 0 : if (NS_WARN_IF(aRv.Failed())) {
894 0 : return;
895 : }
896 :
897 0 : nsCOMPtr<nsIURI> oldUri;
898 :
899 0 : aRv = NS_NewURI(getter_AddRefs(oldUri), oldHref);
900 0 : if (NS_WARN_IF(aRv.Failed())) {
901 0 : return;
902 : }
903 :
904 0 : aRv = SetHrefWithBase(aUrl, oldUri, true);
905 : }
906 :
907 : void
908 0 : Location::Assign(const nsAString& aUrl,
909 : nsIPrincipal& aSubjectPrincipal,
910 : ErrorResult& aRv)
911 : {
912 0 : if (!CallerSubsumes(&aSubjectPrincipal)) {
913 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
914 0 : return;
915 : }
916 :
917 0 : if (JSContext *cx = nsContentUtils::GetCurrentJSContext()) {
918 0 : aRv = SetHrefWithContext(cx, aUrl, false);
919 0 : return;
920 : }
921 :
922 0 : nsAutoString oldHref;
923 0 : aRv = GetHref(oldHref);
924 0 : if (NS_WARN_IF(aRv.Failed())) {
925 0 : return;
926 : }
927 :
928 0 : nsCOMPtr<nsIURI> oldUri;
929 0 : aRv = NS_NewURI(getter_AddRefs(oldUri), oldHref);
930 0 : if (NS_WARN_IF(aRv.Failed())) {
931 0 : return;
932 : }
933 :
934 0 : if (oldUri) {
935 0 : aRv = SetHrefWithBase(aUrl, oldUri, false);
936 : }
937 : }
938 :
939 : nsresult
940 0 : Location::GetSourceBaseURL(JSContext* cx, nsIURI** sourceURL)
941 : {
942 0 : *sourceURL = nullptr;
943 0 : nsIDocument* doc = GetEntryDocument();
944 : // If there's no entry document, we either have no Script Entry Point or one
945 : // that isn't a DOM Window. This doesn't generally happen with the DOM, but
946 : // can sometimes happen with extension code in certain IPC configurations. If
947 : // this happens, try falling back on the current document associated with the
948 : // docshell. If that fails, just return null and hope that the caller passed
949 : // an absolute URI.
950 0 : nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mDocShell));
951 0 : if (!doc && docShell) {
952 : nsCOMPtr<nsPIDOMWindowOuter> docShellWin =
953 0 : do_QueryInterface(docShell->GetScriptGlobalObject());
954 0 : if (docShellWin) {
955 0 : doc = docShellWin->GetDoc();
956 : }
957 : }
958 0 : NS_ENSURE_TRUE(doc, NS_OK);
959 0 : *sourceURL = doc->GetBaseURI().take();
960 0 : return NS_OK;
961 : }
962 :
963 : bool
964 8 : Location::CallerSubsumes(nsIPrincipal* aSubjectPrincipal)
965 : {
966 8 : MOZ_ASSERT(aSubjectPrincipal);
967 :
968 : // Get the principal associated with the location object. Note that this is
969 : // the principal of the page which will actually be navigated, not the
970 : // principal of the Location object itself. This is why we need this check
971 : // even though we only allow limited cross-origin access to Location objects
972 : // in general.
973 16 : nsCOMPtr<nsPIDOMWindowOuter> outer = mInnerWindow->GetOuterWindow();
974 8 : if (MOZ_UNLIKELY(!outer))
975 0 : return false;
976 16 : nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(outer);
977 8 : bool subsumes = false;
978 : nsresult rv =
979 8 : aSubjectPrincipal->SubsumesConsideringDomain(sop->GetPrincipal(),
980 16 : &subsumes);
981 8 : NS_ENSURE_SUCCESS(rv, false);
982 8 : return subsumes;
983 : }
984 :
985 : JSObject*
986 3 : Location::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
987 : {
988 3 : return LocationBinding::Wrap(aCx, this, aGivenProto);
989 : }
990 :
991 : } // dom namespace
992 : } // mozilla namespace
|