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