docshell/base/nsDocShell.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:b97ce4dc66be
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim: set ts=4 sw=4 tw=80 et: */
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 "nsDocShell.h"
8
9 #include <algorithm>
10
11 #include "mozilla/ArrayUtils.h"
12 #include "mozilla/Attributes.h"
13 #include "mozilla/AutoRestore.h"
14 #include "mozilla/Casting.h"
15 #include "mozilla/dom/ContentChild.h"
16 #include "mozilla/dom/Element.h"
17 #include "mozilla/dom/TabChild.h"
18 #include "mozilla/EventStateManager.h"
19 #include "mozilla/Preferences.h"
20 #include "mozilla/Services.h"
21 #include "mozilla/StartupTimeline.h"
22 #include "mozilla/Telemetry.h"
23 #include "mozilla/unused.h"
24 #include "mozilla/VisualEventTracer.h"
25
26 #ifdef MOZ_LOGGING
27 // so we can get logging even in release builds (but only for some things)
28 #define FORCE_PR_LOG 1
29 #endif
30
31 #include "nsIContent.h"
32 #include "nsIDocument.h"
33 #include "nsIDOMDocument.h"
34 #include "nsIDOMElement.h"
35 #include "nsIDOMStorage.h"
36 #include "nsPIDOMStorage.h"
37 #include "nsIContentViewer.h"
38 #include "nsIDocumentLoaderFactory.h"
39 #include "nsCURILoader.h"
40 #include "nsDocShellCID.h"
41 #include "nsDOMCID.h"
42 #include "nsNetUtil.h"
43 #include "nsRect.h"
44 #include "prenv.h"
45 #include "nsIMarkupDocumentViewer.h"
46 #include "nsIDOMWindow.h"
47 #include "nsIWebBrowserChrome.h"
48 #include "nsPoint.h"
49 #include "nsIObserverService.h"
50 #include "nsIPrompt.h"
51 #include "nsIAuthPrompt.h"
52 #include "nsIAuthPrompt2.h"
53 #include "nsIChannelEventSink.h"
54 #include "nsIAsyncVerifyRedirectCallback.h"
55 #include "nsIScriptSecurityManager.h"
56 #include "nsIScriptObjectPrincipal.h"
57 #include "nsIScrollableFrame.h"
58 #include "nsContentPolicyUtils.h" // NS_CheckContentLoadPolicy(...)
59 #include "nsISeekableStream.h"
60 #include "nsAutoPtr.h"
61 #include "nsIWritablePropertyBag2.h"
62 #include "nsIAppShell.h"
63 #include "nsWidgetsCID.h"
64 #include "nsIInterfaceRequestorUtils.h"
65 #include "nsView.h"
66 #include "nsViewManager.h"
67 #include "nsIScriptChannel.h"
68 #include "nsITimedChannel.h"
69 #include "nsIPrivacyTransitionObserver.h"
70 #include "nsIReflowObserver.h"
71 #include "nsIScrollObserver.h"
72 #include "nsIDocShellTreeItem.h"
73 #include "nsIChannel.h"
74 #include "IHistory.h"
75 #include "nsViewSourceHandler.h"
76
77 // we want to explore making the document own the load group
78 // so we can associate the document URI with the load group.
79 // until this point, we have an evil hack:
80 #include "nsIHttpChannelInternal.h"
81 #include "nsPILoadGroupInternal.h"
82
83 // Local Includes
84 #include "nsDocShellLoadInfo.h"
85 #include "nsCDefaultURIFixup.h"
86 #include "nsDocShellEnumerator.h"
87 #include "nsSHistory.h"
88 #include "nsDocShellEditorData.h"
89
90 // Helper Classes
91 #include "nsError.h"
92 #include "nsEscape.h"
93
94 // Interfaces Needed
95 #include "nsIUploadChannel.h"
96 #include "nsIUploadChannel2.h"
97 #include "nsIWebProgress.h"
98 #include "nsILayoutHistoryState.h"
99 #include "nsITimer.h"
100 #include "nsISHistoryInternal.h"
101 #include "nsIPrincipal.h"
102 #include "nsISHEntry.h"
103 #include "nsIWindowWatcher.h"
104 #include "nsIPromptFactory.h"
105 #include "nsITransportSecurityInfo.h"
106 #include "nsINSSErrorsService.h"
107 #include "nsIApplicationCacheChannel.h"
108 #include "nsIApplicationCacheContainer.h"
109 #include "nsStreamUtils.h"
110 #include "nsIController.h"
111 #include "nsPICommandUpdater.h"
112 #include "nsIDOMHTMLAnchorElement.h"
113 #include "nsIWebBrowserChrome3.h"
114 #include "nsITabChild.h"
115 #include "nsISiteSecurityService.h"
116 #include "nsStructuredCloneContainer.h"
117 #include "nsIStructuredCloneContainer.h"
118 #ifdef MOZ_PLACES
119 #include "nsIFaviconService.h"
120 #include "mozIAsyncFavicons.h"
121 #endif
122 #include "nsINetworkSeer.h"
123
124 // Editor-related
125 #include "nsIEditingSession.h"
126
127 #include "nsPIDOMWindow.h"
128 #include "nsGlobalWindow.h"
129 #include "nsPIWindowRoot.h"
130 #include "nsICachingChannel.h"
131 #include "nsIMultiPartChannel.h"
132 #include "nsIWyciwygChannel.h"
133
134 // For reporting errors with the console service.
135 // These can go away if error reporting is propagated up past nsDocShell.
136 #include "nsIScriptError.h"
137
138 // used to dispatch urls to default protocol handlers
139 #include "nsCExternalHandlerService.h"
140 #include "nsIExternalProtocolService.h"
141
142 #include "nsFocusManager.h"
143
144 #include "nsITextToSubURI.h"
145
146 #include "nsIJARChannel.h"
147
148 #include "prlog.h"
149
150 #include "nsISelectionDisplay.h"
151
152 #include "nsIGlobalHistory2.h"
153
154 #include "nsIFrame.h"
155 #include "nsSubDocumentFrame.h"
156
157 // for embedding
158 #include "nsIWebBrowserChromeFocus.h"
159
160 #if NS_PRINT_PREVIEW
161 #include "nsIDocumentViewerPrint.h"
162 #include "nsIWebBrowserPrint.h"
163 #endif
164
165 #include "nsContentUtils.h"
166 #include "nsCxPusher.h"
167 #include "nsIChannelPolicy.h"
168 #include "nsIContentSecurityPolicy.h"
169 #include "nsSandboxFlags.h"
170 #include "mozIThirdPartyUtil.h"
171 #include "nsXULAppAPI.h"
172 #include "nsDOMNavigationTiming.h"
173 #include "nsISecurityUITelemetry.h"
174 #include "nsIAppsService.h"
175 #include "nsDSURIContentListener.h"
176 #include "nsDocShellLoadTypes.h"
177 #include "nsDocShellTransferableHooks.h"
178 #include "nsICommandManager.h"
179 #include "nsIDOMNode.h"
180 #include "nsIDocShellTreeOwner.h"
181 #include "nsIHttpChannel.h"
182 #include "nsISHContainer.h"
183 #include "nsISHistory.h"
184 #include "nsISecureBrowserUI.h"
185 #include "nsIStringBundle.h"
186 #include "nsISupportsArray.h"
187 #include "nsIURIFixup.h"
188 #include "nsIURILoader.h"
189 #include "nsIWebBrowserFind.h"
190 #include "nsIWidget.h"
191 #include "mozilla/dom/EncodingUtils.h"
192
193 static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
194
195 #if defined(DEBUG_bryner) || defined(DEBUG_chb)
196 //#define DEBUG_DOCSHELL_FOCUS
197 #define DEBUG_PAGE_CACHE
198 #endif
199
200 #ifdef XP_WIN
201 #include <process.h>
202 #define getpid _getpid
203 #else
204 #include <unistd.h> // for getpid()
205 #endif
206
207 using namespace mozilla;
208 using namespace mozilla::dom;
209
210 // True means sUseErrorPages has been added to preferences var cache.
211 static bool gAddedPreferencesVarCache = false;
212
213 bool nsDocShell::sUseErrorPages = false;
214
215 // Number of documents currently loading
216 static int32_t gNumberOfDocumentsLoading = 0;
217
218 // Global count of existing docshells.
219 static int32_t gDocShellCount = 0;
220
221 // Global count of docshells with the private attribute set
222 static uint32_t gNumberOfPrivateDocShells = 0;
223
224 // Global reference to the URI fixup service.
225 nsIURIFixup *nsDocShell::sURIFixup = 0;
226
227 // True means we validate window targets to prevent frameset
228 // spoofing. Initialize this to a non-bolean value so we know to check
229 // the pref on the creation of the first docshell.
230 static uint32_t gValidateOrigin = 0xffffffff;
231
232 // Hint for native dispatch of events on how long to delay after
233 // all documents have loaded in milliseconds before favoring normal
234 // native event dispatch priorites over performance
235 // Can be overridden with docshell.event_starvation_delay_hint pref.
236 #define NS_EVENT_STARVATION_DELAY_HINT 2000
237
238 #ifdef PR_LOGGING
239 #ifdef DEBUG
240 static PRLogModuleInfo* gDocShellLog;
241 #endif
242 static PRLogModuleInfo* gDocShellLeakLog;
243 #endif
244
245 const char kBrandBundleURL[] = "chrome://branding/locale/brand.properties";
246 const char kAppstringsBundleURL[] = "chrome://global/locale/appstrings.properties";
247
248 static void
249 FavorPerformanceHint(bool perfOverStarvation)
250 {
251 nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
252 if (appShell) {
253 appShell->FavorPerformanceHint(perfOverStarvation,
254 Preferences::GetUint("docshell.event_starvation_delay_hint",
255 NS_EVENT_STARVATION_DELAY_HINT));
256 }
257 }
258
259 //*****************************************************************************
260 // <a ping> support
261 //*****************************************************************************
262
263 #define PREF_PINGS_ENABLED "browser.send_pings"
264 #define PREF_PINGS_MAX_PER_LINK "browser.send_pings.max_per_link"
265 #define PREF_PINGS_REQUIRE_SAME_HOST "browser.send_pings.require_same_host"
266
267 // Check prefs to see if pings are enabled and if so what restrictions might
268 // be applied.
269 //
270 // @param maxPerLink
271 // This parameter returns the number of pings that are allowed per link click
272 //
273 // @param requireSameHost
274 // This parameter returns true if pings are restricted to the same host as
275 // the document in which the click occurs. If the same host restriction is
276 // imposed, then we still allow for pings to cross over to different
277 // protocols and ports for flexibility and because it is not possible to send
278 // a ping via FTP.
279 //
280 // @returns
281 // true if pings are enabled and false otherwise.
282 //
283 static bool
284 PingsEnabled(int32_t *maxPerLink, bool *requireSameHost)
285 {
286 bool allow = Preferences::GetBool(PREF_PINGS_ENABLED, false);
287
288 *maxPerLink = 1;
289 *requireSameHost = true;
290
291 if (allow) {
292 Preferences::GetInt(PREF_PINGS_MAX_PER_LINK, maxPerLink);
293 Preferences::GetBool(PREF_PINGS_REQUIRE_SAME_HOST, requireSameHost);
294 }
295
296 return allow;
297 }
298
299 static bool
300 CheckPingURI(nsIURI* uri, nsIContent* content)
301 {
302 if (!uri)
303 return false;
304
305 // Check with nsIScriptSecurityManager
306 nsCOMPtr<nsIScriptSecurityManager> ssmgr =
307 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
308 NS_ENSURE_TRUE(ssmgr, false);
309
310 nsresult rv =
311 ssmgr->CheckLoadURIWithPrincipal(content->NodePrincipal(), uri,
312 nsIScriptSecurityManager::STANDARD);
313 if (NS_FAILED(rv)) {
314 return false;
315 }
316
317 // Ignore non-HTTP(S)
318 bool match;
319 if ((NS_FAILED(uri->SchemeIs("http", &match)) || !match) &&
320 (NS_FAILED(uri->SchemeIs("https", &match)) || !match)) {
321 return false;
322 }
323
324 // Check with contentpolicy
325 int16_t shouldLoad = nsIContentPolicy::ACCEPT;
326 rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_PING,
327 uri,
328 content->NodePrincipal(),
329 content,
330 EmptyCString(), // mime hint
331 nullptr, //extra
332 &shouldLoad);
333 return NS_SUCCEEDED(rv) && NS_CP_ACCEPTED(shouldLoad);
334 }
335
336 typedef void (* ForEachPingCallback)(void *closure, nsIContent *content,
337 nsIURI *uri, nsIIOService *ios);
338
339 static void
340 ForEachPing(nsIContent *content, ForEachPingCallback callback, void *closure)
341 {
342 // NOTE: Using nsIDOMHTMLAnchorElement::GetPing isn't really worth it here
343 // since we'd still need to parse the resulting string. Instead, we
344 // just parse the raw attribute. It might be nice if the content node
345 // implemented an interface that exposed an enumeration of nsIURIs.
346
347 // Make sure we are dealing with either an <A> or <AREA> element in the HTML
348 // or XHTML namespace.
349 if (!content->IsHTML())
350 return;
351 nsIAtom *nameAtom = content->Tag();
352 if (nameAtom != nsGkAtoms::a && nameAtom != nsGkAtoms::area)
353 return;
354
355 nsCOMPtr<nsIAtom> pingAtom = do_GetAtom("ping");
356 if (!pingAtom)
357 return;
358
359 nsAutoString value;
360 content->GetAttr(kNameSpaceID_None, pingAtom, value);
361 if (value.IsEmpty())
362 return;
363
364 nsCOMPtr<nsIIOService> ios = do_GetIOService();
365 if (!ios)
366 return;
367
368 nsIDocument *doc = content->OwnerDoc();
369
370 // value contains relative URIs split on spaces (U+0020)
371 const char16_t *start = value.BeginReading();
372 const char16_t *end = value.EndReading();
373 const char16_t *iter = start;
374 for (;;) {
375 if (iter < end && *iter != ' ') {
376 ++iter;
377 } else { // iter is pointing at either end or a space
378 while (*start == ' ' && start < iter)
379 ++start;
380 if (iter != start) {
381 nsCOMPtr<nsIURI> uri, baseURI = content->GetBaseURI();
382 ios->NewURI(NS_ConvertUTF16toUTF8(Substring(start, iter)),
383 doc->GetDocumentCharacterSet().get(),
384 baseURI, getter_AddRefs(uri));
385 if (CheckPingURI(uri, content)) {
386 callback(closure, content, uri, ios);
387 }
388 }
389 start = iter = iter + 1;
390 if (iter >= end)
391 break;
392 }
393 }
394 }
395
396 //----------------------------------------------------------------------
397
398 // We wait this many milliseconds before killing the ping channel...
399 #define PING_TIMEOUT 10000
400
401 static void
402 OnPingTimeout(nsITimer *timer, void *closure)
403 {
404 nsILoadGroup *loadGroup = static_cast<nsILoadGroup *>(closure);
405 if (loadGroup)
406 loadGroup->Cancel(NS_ERROR_ABORT);
407 }
408
409 // Check to see if two URIs have the same host or not
410 static bool
411 IsSameHost(nsIURI *uri1, nsIURI *uri2)
412 {
413 nsAutoCString host1, host2;
414 uri1->GetAsciiHost(host1);
415 uri2->GetAsciiHost(host2);
416 return host1.Equals(host2);
417 }
418
419 class nsPingListener MOZ_FINAL : public nsIStreamListener
420 , public nsIInterfaceRequestor
421 , public nsIChannelEventSink
422 {
423 public:
424 NS_DECL_ISUPPORTS
425 NS_DECL_NSIREQUESTOBSERVER
426 NS_DECL_NSISTREAMLISTENER
427 NS_DECL_NSIINTERFACEREQUESTOR
428 NS_DECL_NSICHANNELEVENTSINK
429
430 nsPingListener(bool requireSameHost, nsIContent* content, nsILoadGroup* loadGroup)
431 : mRequireSameHost(requireSameHost),
432 mContent(content),
433 mLoadGroup(loadGroup)
434 {}
435
436 ~nsPingListener();
437
438 nsresult StartTimeout();
439
440 private:
441 bool mRequireSameHost;
442 nsCOMPtr<nsIContent> mContent;
443 nsCOMPtr<nsILoadGroup> mLoadGroup;
444 nsCOMPtr<nsITimer> mTimer;
445 };
446
447 NS_IMPL_ISUPPORTS(nsPingListener, nsIStreamListener, nsIRequestObserver,
448 nsIInterfaceRequestor, nsIChannelEventSink)
449
450 nsPingListener::~nsPingListener()
451 {
452 if (mTimer) {
453 mTimer->Cancel();
454 mTimer = nullptr;
455 }
456 }
457
458 nsresult
459 nsPingListener::StartTimeout()
460 {
461 nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
462
463 if (timer) {
464 nsresult rv = timer->InitWithFuncCallback(OnPingTimeout, mLoadGroup,
465 PING_TIMEOUT,
466 nsITimer::TYPE_ONE_SHOT);
467 if (NS_SUCCEEDED(rv)) {
468 mTimer = timer;
469 return NS_OK;
470 }
471 }
472
473 return NS_ERROR_OUT_OF_MEMORY;
474 }
475
476 NS_IMETHODIMP
477 nsPingListener::OnStartRequest(nsIRequest *request, nsISupports *context)
478 {
479 return NS_OK;
480 }
481
482 NS_IMETHODIMP
483 nsPingListener::OnDataAvailable(nsIRequest *request, nsISupports *context,
484 nsIInputStream *stream, uint64_t offset,
485 uint32_t count)
486 {
487 uint32_t result;
488 return stream->ReadSegments(NS_DiscardSegment, nullptr, count, &result);
489 }
490
491 NS_IMETHODIMP
492 nsPingListener::OnStopRequest(nsIRequest *request, nsISupports *context,
493 nsresult status)
494 {
495 mLoadGroup = nullptr;
496
497 if (mTimer) {
498 mTimer->Cancel();
499 mTimer = nullptr;
500 }
501
502 return NS_OK;
503 }
504
505 NS_IMETHODIMP
506 nsPingListener::GetInterface(const nsIID &iid, void **result)
507 {
508 if (iid.Equals(NS_GET_IID(nsIChannelEventSink))) {
509 NS_ADDREF_THIS();
510 *result = (nsIChannelEventSink *) this;
511 return NS_OK;
512 }
513
514 return NS_ERROR_NO_INTERFACE;
515 }
516
517 NS_IMETHODIMP
518 nsPingListener::AsyncOnChannelRedirect(nsIChannel *oldChan, nsIChannel *newChan,
519 uint32_t flags,
520 nsIAsyncVerifyRedirectCallback *callback)
521 {
522 nsCOMPtr<nsIURI> newURI;
523 newChan->GetURI(getter_AddRefs(newURI));
524
525 if (!CheckPingURI(newURI, mContent))
526 return NS_ERROR_ABORT;
527
528 if (!mRequireSameHost) {
529 callback->OnRedirectVerifyCallback(NS_OK);
530 return NS_OK;
531 }
532
533 // XXXbz should this be using something more like the nsContentUtils
534 // same-origin checker?
535 nsCOMPtr<nsIURI> oldURI;
536 oldChan->GetURI(getter_AddRefs(oldURI));
537 NS_ENSURE_STATE(oldURI && newURI);
538
539 if (!IsSameHost(oldURI, newURI))
540 return NS_ERROR_ABORT;
541
542 callback->OnRedirectVerifyCallback(NS_OK);
543 return NS_OK;
544 }
545
546 struct SendPingInfo {
547 int32_t numPings;
548 int32_t maxPings;
549 bool requireSameHost;
550 nsIURI *target;
551 nsIURI *referrer;
552 };
553
554 static void
555 SendPing(void *closure, nsIContent *content, nsIURI *uri, nsIIOService *ios)
556 {
557 SendPingInfo *info = static_cast<SendPingInfo *>(closure);
558 if (info->numPings >= info->maxPings)
559 return;
560
561 if (info->requireSameHost) {
562 // Make sure the referrer and the given uri share the same origin. We
563 // only require the same hostname. The scheme and port may differ.
564 if (!IsSameHost(uri, info->referrer))
565 return;
566 }
567
568 nsIDocument *doc = content->OwnerDoc();
569
570 nsCOMPtr<nsIChannel> chan;
571 ios->NewChannelFromURI(uri, getter_AddRefs(chan));
572 if (!chan)
573 return;
574
575 // Don't bother caching the result of this URI load.
576 chan->SetLoadFlags(nsIRequest::INHIBIT_CACHING);
577
578 nsCOMPtr<nsIHttpChannel> httpChan = do_QueryInterface(chan);
579 if (!httpChan)
580 return;
581
582 // This is needed in order for 3rd-party cookie blocking to work.
583 nsCOMPtr<nsIHttpChannelInternal> httpInternal = do_QueryInterface(httpChan);
584 if (httpInternal)
585 httpInternal->SetDocumentURI(doc->GetDocumentURI());
586
587
588 httpChan->SetRequestMethod(NS_LITERAL_CSTRING("POST"));
589
590 // Remove extraneous request headers (to reduce request size)
591 httpChan->SetRequestHeader(NS_LITERAL_CSTRING("accept"),
592 EmptyCString(), false);
593 httpChan->SetRequestHeader(NS_LITERAL_CSTRING("accept-language"),
594 EmptyCString(), false);
595 httpChan->SetRequestHeader(NS_LITERAL_CSTRING("accept-encoding"),
596 EmptyCString(), false);
597
598 // Always send a Ping-To header.
599 nsAutoCString pingTo;
600 if (NS_SUCCEEDED(info->target->GetSpec(pingTo)))
601 httpChan->SetRequestHeader(NS_LITERAL_CSTRING("Ping-To"), pingTo, false);
602
603 nsCOMPtr<nsIScriptSecurityManager> sm =
604 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
605
606 if (sm && info->referrer) {
607 bool referrerIsSecure;
608 uint32_t flags = nsIProtocolHandler::URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT;
609 nsresult rv = NS_URIChainHasFlags(info->referrer, flags, &referrerIsSecure);
610
611 // Default to sending less data if NS_URIChainHasFlags() fails.
612 referrerIsSecure = NS_FAILED(rv) || referrerIsSecure;
613
614 bool sameOrigin =
615 NS_SUCCEEDED(sm->CheckSameOriginURI(info->referrer, uri, false));
616
617 // If both the address of the document containing the hyperlink being
618 // audited and "ping URL" have the same origin or the document containing
619 // the hyperlink being audited was not retrieved over an encrypted
620 // connection, send a Ping-From header.
621 if (sameOrigin || !referrerIsSecure) {
622 nsAutoCString pingFrom;
623 if (NS_SUCCEEDED(info->referrer->GetSpec(pingFrom)))
624 httpChan->SetRequestHeader(NS_LITERAL_CSTRING("Ping-From"), pingFrom, false);
625 }
626
627 // If the document containing the hyperlink being audited was not retrieved
628 // over an encrypted connection and its address does not have the same
629 // origin as "ping URL", send a referrer.
630 if (!sameOrigin && !referrerIsSecure)
631 httpChan->SetReferrer(info->referrer);
632 }
633
634 nsCOMPtr<nsIUploadChannel2> uploadChan = do_QueryInterface(httpChan);
635 if (!uploadChan)
636 return;
637
638 NS_NAMED_LITERAL_CSTRING(uploadData, "PING");
639
640 nsCOMPtr<nsIInputStream> uploadStream;
641 NS_NewPostDataStream(getter_AddRefs(uploadStream), false, uploadData);
642 if (!uploadStream)
643 return;
644
645 uploadChan->ExplicitSetUploadStream(uploadStream,
646 NS_LITERAL_CSTRING("text/ping"), uploadData.Length(),
647 NS_LITERAL_CSTRING("POST"), false);
648
649 // The channel needs to have a loadgroup associated with it, so that we can
650 // cancel the channel and any redirected channels it may create.
651 nsCOMPtr<nsILoadGroup> loadGroup =
652 do_CreateInstance(NS_LOADGROUP_CONTRACTID);
653 if (!loadGroup)
654 return;
655 chan->SetLoadGroup(loadGroup);
656
657 // Construct a listener that merely discards any response. If successful at
658 // opening the channel, then it is not necessary to hold a reference to the
659 // channel. The networking subsystem will take care of that for us.
660 nsPingListener *pingListener =
661 new nsPingListener(info->requireSameHost, content, loadGroup);
662 if (!pingListener)
663 return;
664
665 nsCOMPtr<nsIStreamListener> listener(pingListener);
666
667 // Observe redirects as well:
668 nsCOMPtr<nsIInterfaceRequestor> callbacks = do_QueryInterface(listener);
669 NS_ASSERTION(callbacks, "oops");
670 loadGroup->SetNotificationCallbacks(callbacks);
671
672 chan->AsyncOpen(listener, nullptr);
673
674 // Even if AsyncOpen failed, we still count this as a successful ping. It's
675 // possible that AsyncOpen may have failed after triggering some background
676 // process that may have written something to the network.
677 info->numPings++;
678
679 // Prevent ping requests from stalling and never being garbage collected...
680 if (NS_FAILED(pingListener->StartTimeout())) {
681 // If we failed to setup the timer, then we should just cancel the channel
682 // because we won't be able to ensure that it goes away in a timely manner.
683 chan->Cancel(NS_ERROR_ABORT);
684 }
685 }
686
687 // Spec: http://whatwg.org/specs/web-apps/current-work/#ping
688 static void
689 DispatchPings(nsIContent *content, nsIURI *target, nsIURI *referrer)
690 {
691 SendPingInfo info;
692
693 if (!PingsEnabled(&info.maxPings, &info.requireSameHost))
694 return;
695 if (info.maxPings == 0)
696 return;
697
698 info.numPings = 0;
699 info.target = target;
700 info.referrer = referrer;
701
702 ForEachPing(content, SendPing, &info);
703 }
704
705 static nsDOMPerformanceNavigationType
706 ConvertLoadTypeToNavigationType(uint32_t aLoadType)
707 {
708 // Not initialized, assume it's normal load.
709 if (aLoadType == 0) {
710 aLoadType = LOAD_NORMAL;
711 }
712
713 nsDOMPerformanceNavigationType result = dom::PerformanceNavigation::TYPE_RESERVED;
714 switch (aLoadType) {
715 case LOAD_NORMAL:
716 case LOAD_NORMAL_EXTERNAL:
717 case LOAD_NORMAL_BYPASS_CACHE:
718 case LOAD_NORMAL_BYPASS_PROXY:
719 case LOAD_NORMAL_BYPASS_PROXY_AND_CACHE:
720 case LOAD_NORMAL_REPLACE:
721 case LOAD_NORMAL_ALLOW_MIXED_CONTENT:
722 case LOAD_LINK:
723 case LOAD_STOP_CONTENT:
724 case LOAD_REPLACE_BYPASS_CACHE:
725 result = dom::PerformanceNavigation::TYPE_NAVIGATE;
726 break;
727 case LOAD_HISTORY:
728 result = dom::PerformanceNavigation::TYPE_BACK_FORWARD;
729 break;
730 case LOAD_RELOAD_NORMAL:
731 case LOAD_RELOAD_CHARSET_CHANGE:
732 case LOAD_RELOAD_BYPASS_CACHE:
733 case LOAD_RELOAD_BYPASS_PROXY:
734 case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
735 case LOAD_RELOAD_ALLOW_MIXED_CONTENT:
736 result = dom::PerformanceNavigation::TYPE_RELOAD;
737 break;
738 case LOAD_STOP_CONTENT_AND_REPLACE:
739 case LOAD_REFRESH:
740 case LOAD_BYPASS_HISTORY:
741 case LOAD_ERROR_PAGE:
742 case LOAD_PUSHSTATE:
743 result = dom::PerformanceNavigation::TYPE_RESERVED;
744 break;
745 default:
746 // NS_NOTREACHED("Unexpected load type value");
747 result = dom::PerformanceNavigation::TYPE_RESERVED;
748 break;
749 }
750
751 return result;
752 }
753
754 static nsISHEntry* GetRootSHEntry(nsISHEntry *entry);
755
756 static void
757 IncreasePrivateDocShellCount()
758 {
759 gNumberOfPrivateDocShells++;
760 if (gNumberOfPrivateDocShells > 1 ||
761 XRE_GetProcessType() != GeckoProcessType_Content) {
762 return;
763 }
764
765 mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton();
766 cc->SendPrivateDocShellsExist(true);
767 }
768
769 static void
770 DecreasePrivateDocShellCount()
771 {
772 MOZ_ASSERT(gNumberOfPrivateDocShells > 0);
773 gNumberOfPrivateDocShells--;
774 if (!gNumberOfPrivateDocShells)
775 {
776 if (XRE_GetProcessType() == GeckoProcessType_Content) {
777 mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton();
778 cc->SendPrivateDocShellsExist(false);
779 return;
780 }
781
782 nsCOMPtr<nsIObserverService> obsvc = mozilla::services::GetObserverService();
783 if (obsvc) {
784 obsvc->NotifyObservers(nullptr, "last-pb-context-exited", nullptr);
785 }
786 }
787 }
788
789 //*****************************************************************************
790 //*** nsDocShell: Object Management
791 //*****************************************************************************
792
793 static uint64_t gDocshellIDCounter = 0;
794
795 // Note: operator new zeros our memory
796 nsDocShell::nsDocShell():
797 nsDocLoader(),
798 mDefaultScrollbarPref(Scrollbar_Auto, Scrollbar_Auto),
799 mTreeOwner(nullptr),
800 mChromeEventHandler(nullptr),
801 mCharsetReloadState(eCharsetReloadInit),
802 mChildOffset(0),
803 mBusyFlags(BUSY_FLAGS_NONE),
804 mAppType(nsIDocShell::APP_TYPE_UNKNOWN),
805 mLoadType(0),
806 mMarginWidth(-1),
807 mMarginHeight(-1),
808 mItemType(typeContent),
809 mPreviousTransIndex(-1),
810 mLoadedTransIndex(-1),
811 mSandboxFlags(0),
812 mFullscreenAllowed(CHECK_ATTRIBUTES),
813 mCreated(false),
814 mAllowSubframes(true),
815 mAllowPlugins(true),
816 mAllowJavascript(true),
817 mAllowMetaRedirects(true),
818 mAllowImages(true),
819 mAllowMedia(true),
820 mAllowDNSPrefetch(true),
821 mAllowWindowControl(true),
822 mAllowContentRetargeting(true),
823 mCreatingDocument(false),
824 mUseErrorPages(false),
825 mObserveErrorPages(true),
826 mAllowAuth(true),
827 mAllowKeywordFixup(false),
828 mIsOffScreenBrowser(false),
829 mIsActive(true),
830 mIsAppTab(false),
831 mUseGlobalHistory(false),
832 mInPrivateBrowsing(false),
833 mUseRemoteTabs(false),
834 mDeviceSizeIsPageSize(false),
835 mCanExecuteScripts(false),
836 mFiredUnloadEvent(false),
837 mEODForCurrentDocument(false),
838 mURIResultedInDocument(false),
839 mIsBeingDestroyed(false),
840 mIsExecutingOnLoadHandler(false),
841 mIsPrintingOrPP(false),
842 mSavingOldViewer(false),
843 #ifdef DEBUG
844 mInEnsureScriptEnv(false),
845 #endif
846 mAffectPrivateSessionLifetime(true),
847 mInvisible(false),
848 mDefaultLoadFlags(nsIRequest::LOAD_NORMAL),
849 mFrameType(eFrameTypeRegular),
850 mOwnOrContainingAppId(nsIScriptSecurityManager::UNKNOWN_APP_ID),
851 mParentCharsetSource(0)
852 {
853 mHistoryID = ++gDocshellIDCounter;
854 if (gDocShellCount++ == 0) {
855 NS_ASSERTION(sURIFixup == nullptr,
856 "Huh, sURIFixup not null in first nsDocShell ctor!");
857
858 CallGetService(NS_URIFIXUP_CONTRACTID, &sURIFixup);
859 }
860
861 #ifdef PR_LOGGING
862 #ifdef DEBUG
863 if (! gDocShellLog)
864 gDocShellLog = PR_NewLogModule("nsDocShell");
865 #endif
866 if (nullptr == gDocShellLeakLog)
867 gDocShellLeakLog = PR_NewLogModule("nsDocShellLeak");
868 if (gDocShellLeakLog)
869 PR_LOG(gDocShellLeakLog, PR_LOG_DEBUG, ("DOCSHELL %p created\n", this));
870 #endif
871
872 #ifdef DEBUG
873 // We're counting the number of |nsDocShells| to help find leaks
874 ++gNumberOfDocShells;
875 if (!PR_GetEnv("MOZ_QUIET")) {
876 printf_stderr("++DOCSHELL %p == %ld [pid = %d] [id = %llu]\n",
877 (void*) this,
878 gNumberOfDocShells,
879 getpid(),
880 SafeCast<unsigned long long>(mHistoryID));
881 }
882 #endif
883 }
884
885 nsDocShell::~nsDocShell()
886 {
887 Destroy();
888
889 nsCOMPtr<nsISHistoryInternal>
890 shPrivate(do_QueryInterface(mSessionHistory));
891 if (shPrivate) {
892 shPrivate->SetRootDocShell(nullptr);
893 }
894
895 if (--gDocShellCount == 0) {
896 NS_IF_RELEASE(sURIFixup);
897 }
898
899 #ifdef PR_LOGGING
900 if (gDocShellLeakLog)
901 PR_LOG(gDocShellLeakLog, PR_LOG_DEBUG, ("DOCSHELL %p destroyed\n", this));
902 #endif
903
904 #ifdef DEBUG
905 // We're counting the number of |nsDocShells| to help find leaks
906 --gNumberOfDocShells;
907 if (!PR_GetEnv("MOZ_QUIET")) {
908 printf_stderr("--DOCSHELL %p == %ld [pid = %d] [id = %llu]\n",
909 (void*) this,
910 gNumberOfDocShells,
911 getpid(),
912 SafeCast<unsigned long long>(mHistoryID));
913 }
914 #endif
915 }
916
917 nsresult
918 nsDocShell::Init()
919 {
920 nsresult rv = nsDocLoader::Init();
921 NS_ENSURE_SUCCESS(rv, rv);
922
923 NS_ASSERTION(mLoadGroup, "Something went wrong!");
924
925 mContentListener = new nsDSURIContentListener(this);
926 NS_ENSURE_TRUE(mContentListener, NS_ERROR_OUT_OF_MEMORY);
927
928 rv = mContentListener->Init();
929 NS_ENSURE_SUCCESS(rv, rv);
930
931 // We want to hold a strong ref to the loadgroup, so it better hold a weak
932 // ref to us... use an InterfaceRequestorProxy to do this.
933 nsCOMPtr<nsIInterfaceRequestor> proxy =
934 new InterfaceRequestorProxy(static_cast<nsIInterfaceRequestor*>
935 (this));
936 NS_ENSURE_TRUE(proxy, NS_ERROR_OUT_OF_MEMORY);
937 mLoadGroup->SetNotificationCallbacks(proxy);
938
939 rv = nsDocLoader::AddDocLoaderAsChildOfRoot(this);
940 NS_ENSURE_SUCCESS(rv, rv);
941
942 // Add as |this| a progress listener to itself. A little weird, but
943 // simpler than reproducing all the listener-notification logic in
944 // overrides of the various methods via which nsDocLoader can be
945 // notified. Note that this holds an nsWeakPtr to ourselves, so it's ok.
946 return AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_DOCUMENT |
947 nsIWebProgress::NOTIFY_STATE_NETWORK);
948
949 }
950
951 void
952 nsDocShell::DestroyChildren()
953 {
954 nsCOMPtr<nsIDocShellTreeItem> shell;
955 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
956 while (iter.HasMore()) {
957 shell = do_QueryObject(iter.GetNext());
958 NS_ASSERTION(shell, "docshell has null child");
959
960 if (shell) {
961 shell->SetTreeOwner(nullptr);
962 }
963 }
964
965 nsDocLoader::DestroyChildren();
966 }
967
968 //*****************************************************************************
969 // nsDocShell::nsISupports
970 //*****************************************************************************
971
972 NS_IMPL_ADDREF_INHERITED(nsDocShell, nsDocLoader)
973 NS_IMPL_RELEASE_INHERITED(nsDocShell, nsDocLoader)
974
975 NS_INTERFACE_MAP_BEGIN(nsDocShell)
976 NS_INTERFACE_MAP_ENTRY(nsIDocShell)
977 NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeItem)
978 NS_INTERFACE_MAP_ENTRY(nsIWebNavigation)
979 NS_INTERFACE_MAP_ENTRY(nsIBaseWindow)
980 NS_INTERFACE_MAP_ENTRY(nsIScrollable)
981 NS_INTERFACE_MAP_ENTRY(nsITextScroll)
982 NS_INTERFACE_MAP_ENTRY(nsIDocCharset)
983 NS_INTERFACE_MAP_ENTRY(nsIRefreshURI)
984 NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
985 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
986 NS_INTERFACE_MAP_ENTRY(nsIContentViewerContainer)
987 NS_INTERFACE_MAP_ENTRY(nsIWebPageDescriptor)
988 NS_INTERFACE_MAP_ENTRY(nsIAuthPromptProvider)
989 NS_INTERFACE_MAP_ENTRY(nsILoadContext)
990 NS_INTERFACE_MAP_ENTRY(nsIWebShellServices)
991 NS_INTERFACE_MAP_ENTRY(nsILinkHandler)
992 NS_INTERFACE_MAP_ENTRY(nsIClipboardCommands)
993 NS_INTERFACE_MAP_ENTRY(nsIDOMStorageManager)
994 NS_INTERFACE_MAP_END_INHERITING(nsDocLoader)
995
996 ///*****************************************************************************
997 // nsDocShell::nsIInterfaceRequestor
998 //*****************************************************************************
999 NS_IMETHODIMP nsDocShell::GetInterface(const nsIID & aIID, void **aSink)
1000 {
1001 NS_PRECONDITION(aSink, "null out param");
1002
1003 *aSink = nullptr;
1004
1005 if (aIID.Equals(NS_GET_IID(nsICommandManager))) {
1006 NS_ENSURE_SUCCESS(EnsureCommandHandler(), NS_ERROR_FAILURE);
1007 *aSink = mCommandManager;
1008 }
1009 else if (aIID.Equals(NS_GET_IID(nsIURIContentListener))) {
1010 *aSink = mContentListener;
1011 }
1012 else if ((aIID.Equals(NS_GET_IID(nsIScriptGlobalObject)) ||
1013 aIID.Equals(NS_GET_IID(nsPIDOMWindow)) ||
1014 aIID.Equals(NS_GET_IID(nsIDOMWindow)) ||
1015 aIID.Equals(NS_GET_IID(nsIDOMWindowInternal))) &&
1016 NS_SUCCEEDED(EnsureScriptEnvironment())) {
1017 return mScriptGlobal->QueryInterface(aIID, aSink);
1018 }
1019 else if (aIID.Equals(NS_GET_IID(nsIDOMDocument)) &&
1020 NS_SUCCEEDED(EnsureContentViewer())) {
1021 mContentViewer->GetDOMDocument((nsIDOMDocument **) aSink);
1022 return *aSink ? NS_OK : NS_NOINTERFACE;
1023 }
1024 else if (aIID.Equals(NS_GET_IID(nsIDocument)) &&
1025 NS_SUCCEEDED(EnsureContentViewer())) {
1026 nsCOMPtr<nsIDocument> doc = mContentViewer->GetDocument();
1027 doc.forget(aSink);
1028 return *aSink ? NS_OK : NS_NOINTERFACE;
1029 }
1030 else if (aIID.Equals(NS_GET_IID(nsIApplicationCacheContainer))) {
1031 *aSink = nullptr;
1032
1033 // Return application cache associated with this docshell, if any
1034
1035 nsCOMPtr<nsIContentViewer> contentViewer;
1036 GetContentViewer(getter_AddRefs(contentViewer));
1037 if (!contentViewer)
1038 return NS_ERROR_NO_INTERFACE;
1039
1040 nsCOMPtr<nsIDOMDocument> domDoc;
1041 contentViewer->GetDOMDocument(getter_AddRefs(domDoc));
1042 NS_ASSERTION(domDoc, "Should have a document.");
1043 if (!domDoc)
1044 return NS_ERROR_NO_INTERFACE;
1045
1046 #if defined(PR_LOGGING) && defined(DEBUG)
1047 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
1048 ("nsDocShell[%p]: returning app cache container %p",
1049 this, domDoc.get()));
1050 #endif
1051 return domDoc->QueryInterface(aIID, aSink);
1052 }
1053 else if (aIID.Equals(NS_GET_IID(nsIPrompt)) &&
1054 NS_SUCCEEDED(EnsureScriptEnvironment())) {
1055 nsresult rv;
1056 nsCOMPtr<nsIWindowWatcher> wwatch =
1057 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
1058 NS_ENSURE_SUCCESS(rv, rv);
1059
1060 // Get the an auth prompter for our window so that the parenting
1061 // of the dialogs works as it should when using tabs.
1062 nsIPrompt *prompt;
1063 rv = wwatch->GetNewPrompter(mScriptGlobal, &prompt);
1064 NS_ENSURE_SUCCESS(rv, rv);
1065
1066 *aSink = prompt;
1067 return NS_OK;
1068 }
1069 else if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
1070 aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
1071 return NS_SUCCEEDED(
1072 GetAuthPrompt(PROMPT_NORMAL, aIID, aSink)) ?
1073 NS_OK : NS_NOINTERFACE;
1074 }
1075 else if (aIID.Equals(NS_GET_IID(nsISHistory))) {
1076 nsCOMPtr<nsISHistory> shistory;
1077 nsresult
1078 rv =
1079 GetSessionHistory(getter_AddRefs(shistory));
1080 if (NS_SUCCEEDED(rv) && shistory) {
1081 *aSink = shistory;
1082 NS_ADDREF((nsISupports *) * aSink);
1083 return NS_OK;
1084 }
1085 return NS_NOINTERFACE;
1086 }
1087 else if (aIID.Equals(NS_GET_IID(nsIWebBrowserFind))) {
1088 nsresult rv = EnsureFind();
1089 if (NS_FAILED(rv)) return rv;
1090
1091 *aSink = mFind;
1092 NS_ADDREF((nsISupports*)*aSink);
1093 return NS_OK;
1094 }
1095 else if (aIID.Equals(NS_GET_IID(nsIEditingSession)) && NS_SUCCEEDED(EnsureEditorData())) {
1096 nsCOMPtr<nsIEditingSession> editingSession;
1097 mEditorData->GetEditingSession(getter_AddRefs(editingSession));
1098 if (editingSession)
1099 {
1100 *aSink = editingSession;
1101 NS_ADDREF((nsISupports *)*aSink);
1102 return NS_OK;
1103 }
1104
1105 return NS_NOINTERFACE;
1106 }
1107 else if (aIID.Equals(NS_GET_IID(nsIClipboardDragDropHookList))
1108 && NS_SUCCEEDED(EnsureTransferableHookData())) {
1109 *aSink = mTransferableHookData;
1110 NS_ADDREF((nsISupports *)*aSink);
1111 return NS_OK;
1112 }
1113 else if (aIID.Equals(NS_GET_IID(nsISelectionDisplay))) {
1114 nsIPresShell* shell = GetPresShell();
1115 if (shell)
1116 return shell->QueryInterface(aIID,aSink);
1117 }
1118 else if (aIID.Equals(NS_GET_IID(nsIDocShellTreeOwner))) {
1119 nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
1120 nsresult rv = GetTreeOwner(getter_AddRefs(treeOwner));
1121 if (NS_SUCCEEDED(rv) && treeOwner)
1122 return treeOwner->QueryInterface(aIID, aSink);
1123 }
1124 else if (aIID.Equals(NS_GET_IID(nsITabChild))) {
1125 nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
1126 nsresult rv = GetTreeOwner(getter_AddRefs(treeOwner));
1127 if (NS_SUCCEEDED(rv) && treeOwner) {
1128 nsCOMPtr<nsIInterfaceRequestor> ir = do_QueryInterface(treeOwner);
1129 if (ir)
1130 return ir->GetInterface(aIID, aSink);
1131 }
1132 }
1133 else if (aIID.Equals(NS_GET_IID(nsIContentFrameMessageManager))) {
1134 nsCOMPtr<nsITabChild> tabChild =
1135 do_GetInterface(static_cast<nsIDocShell*>(this));
1136 nsCOMPtr<nsIContentFrameMessageManager> mm;
1137 if (tabChild) {
1138 tabChild->
1139 GetMessageManager(getter_AddRefs(mm));
1140 } else {
1141 nsCOMPtr<nsPIDOMWindow> win =
1142 do_GetInterface(static_cast<nsIDocShell*>(this));
1143 if (win) {
1144 mm = do_QueryInterface(win->GetParentTarget());
1145 }
1146 }
1147 *aSink = mm.get();
1148 }
1149 else {
1150 return nsDocLoader::GetInterface(aIID, aSink);
1151 }
1152
1153 NS_IF_ADDREF(((nsISupports *) * aSink));
1154 return *aSink ? NS_OK : NS_NOINTERFACE;
1155 }
1156
1157 uint32_t
1158 nsDocShell::
1159 ConvertDocShellLoadInfoToLoadType(nsDocShellInfoLoadType aDocShellLoadType)
1160 {
1161 uint32_t loadType = LOAD_NORMAL;
1162
1163 switch (aDocShellLoadType) {
1164 case nsIDocShellLoadInfo::loadNormal:
1165 loadType = LOAD_NORMAL;
1166 break;
1167 case nsIDocShellLoadInfo::loadNormalReplace:
1168 loadType = LOAD_NORMAL_REPLACE;
1169 break;
1170 case nsIDocShellLoadInfo::loadNormalExternal:
1171 loadType = LOAD_NORMAL_EXTERNAL;
1172 break;
1173 case nsIDocShellLoadInfo::loadHistory:
1174 loadType = LOAD_HISTORY;
1175 break;
1176 case nsIDocShellLoadInfo::loadNormalBypassCache:
1177 loadType = LOAD_NORMAL_BYPASS_CACHE;
1178 break;
1179 case nsIDocShellLoadInfo::loadNormalBypassProxy:
1180 loadType = LOAD_NORMAL_BYPASS_PROXY;
1181 break;
1182 case nsIDocShellLoadInfo::loadNormalBypassProxyAndCache:
1183 loadType = LOAD_NORMAL_BYPASS_PROXY_AND_CACHE;
1184 break;
1185 case nsIDocShellLoadInfo::loadNormalAllowMixedContent:
1186 loadType = LOAD_NORMAL_ALLOW_MIXED_CONTENT;
1187 break;
1188 case nsIDocShellLoadInfo::loadReloadNormal:
1189 loadType = LOAD_RELOAD_NORMAL;
1190 break;
1191 case nsIDocShellLoadInfo::loadReloadCharsetChange:
1192 loadType = LOAD_RELOAD_CHARSET_CHANGE;
1193 break;
1194 case nsIDocShellLoadInfo::loadReloadBypassCache:
1195 loadType = LOAD_RELOAD_BYPASS_CACHE;
1196 break;
1197 case nsIDocShellLoadInfo::loadReloadBypassProxy:
1198 loadType = LOAD_RELOAD_BYPASS_PROXY;
1199 break;
1200 case nsIDocShellLoadInfo::loadReloadBypassProxyAndCache:
1201 loadType = LOAD_RELOAD_BYPASS_PROXY_AND_CACHE;
1202 break;
1203 case nsIDocShellLoadInfo::loadLink:
1204 loadType = LOAD_LINK;
1205 break;
1206 case nsIDocShellLoadInfo::loadRefresh:
1207 loadType = LOAD_REFRESH;
1208 break;
1209 case nsIDocShellLoadInfo::loadBypassHistory:
1210 loadType = LOAD_BYPASS_HISTORY;
1211 break;
1212 case nsIDocShellLoadInfo::loadStopContent:
1213 loadType = LOAD_STOP_CONTENT;
1214 break;
1215 case nsIDocShellLoadInfo::loadStopContentAndReplace:
1216 loadType = LOAD_STOP_CONTENT_AND_REPLACE;
1217 break;
1218 case nsIDocShellLoadInfo::loadPushState:
1219 loadType = LOAD_PUSHSTATE;
1220 break;
1221 case nsIDocShellLoadInfo::loadReplaceBypassCache:
1222 loadType = LOAD_REPLACE_BYPASS_CACHE;
1223 break;
1224 case nsIDocShellLoadInfo::loadReloadMixedContent:
1225 loadType = LOAD_RELOAD_ALLOW_MIXED_CONTENT;
1226 break;
1227 default:
1228 NS_NOTREACHED("Unexpected nsDocShellInfoLoadType value");
1229 }
1230
1231 return loadType;
1232 }
1233
1234
1235 nsDocShellInfoLoadType
1236 nsDocShell::ConvertLoadTypeToDocShellLoadInfo(uint32_t aLoadType)
1237 {
1238 nsDocShellInfoLoadType docShellLoadType = nsIDocShellLoadInfo::loadNormal;
1239 switch (aLoadType) {
1240 case LOAD_NORMAL:
1241 docShellLoadType = nsIDocShellLoadInfo::loadNormal;
1242 break;
1243 case LOAD_NORMAL_REPLACE:
1244 docShellLoadType = nsIDocShellLoadInfo::loadNormalReplace;
1245 break;
1246 case LOAD_NORMAL_EXTERNAL:
1247 docShellLoadType = nsIDocShellLoadInfo::loadNormalExternal;
1248 break;
1249 case LOAD_NORMAL_BYPASS_CACHE:
1250 docShellLoadType = nsIDocShellLoadInfo::loadNormalBypassCache;
1251 break;
1252 case LOAD_NORMAL_BYPASS_PROXY:
1253 docShellLoadType = nsIDocShellLoadInfo::loadNormalBypassProxy;
1254 break;
1255 case LOAD_NORMAL_BYPASS_PROXY_AND_CACHE:
1256 docShellLoadType = nsIDocShellLoadInfo::loadNormalBypassProxyAndCache;
1257 break;
1258 case LOAD_NORMAL_ALLOW_MIXED_CONTENT:
1259 docShellLoadType = nsIDocShellLoadInfo::loadNormalAllowMixedContent;
1260 break;
1261 case LOAD_HISTORY:
1262 docShellLoadType = nsIDocShellLoadInfo::loadHistory;
1263 break;
1264 case LOAD_RELOAD_NORMAL:
1265 docShellLoadType = nsIDocShellLoadInfo::loadReloadNormal;
1266 break;
1267 case LOAD_RELOAD_CHARSET_CHANGE:
1268 docShellLoadType = nsIDocShellLoadInfo::loadReloadCharsetChange;
1269 break;
1270 case LOAD_RELOAD_BYPASS_CACHE:
1271 docShellLoadType = nsIDocShellLoadInfo::loadReloadBypassCache;
1272 break;
1273 case LOAD_RELOAD_BYPASS_PROXY:
1274 docShellLoadType = nsIDocShellLoadInfo::loadReloadBypassProxy;
1275 break;
1276 case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
1277 docShellLoadType = nsIDocShellLoadInfo::loadReloadBypassProxyAndCache;
1278 break;
1279 case LOAD_LINK:
1280 docShellLoadType = nsIDocShellLoadInfo::loadLink;
1281 break;
1282 case LOAD_REFRESH:
1283 docShellLoadType = nsIDocShellLoadInfo::loadRefresh;
1284 break;
1285 case LOAD_BYPASS_HISTORY:
1286 case LOAD_ERROR_PAGE:
1287 docShellLoadType = nsIDocShellLoadInfo::loadBypassHistory;
1288 break;
1289 case LOAD_STOP_CONTENT:
1290 docShellLoadType = nsIDocShellLoadInfo::loadStopContent;
1291 break;
1292 case LOAD_STOP_CONTENT_AND_REPLACE:
1293 docShellLoadType = nsIDocShellLoadInfo::loadStopContentAndReplace;
1294 break;
1295 case LOAD_PUSHSTATE:
1296 docShellLoadType = nsIDocShellLoadInfo::loadPushState;
1297 break;
1298 case LOAD_REPLACE_BYPASS_CACHE:
1299 docShellLoadType = nsIDocShellLoadInfo::loadReplaceBypassCache;
1300 break;
1301 case LOAD_RELOAD_ALLOW_MIXED_CONTENT:
1302 docShellLoadType = nsIDocShellLoadInfo::loadReloadMixedContent;
1303 break;
1304 default:
1305 NS_NOTREACHED("Unexpected load type value");
1306 }
1307
1308 return docShellLoadType;
1309 }
1310
1311 //*****************************************************************************
1312 // nsDocShell::nsIDocShell
1313 //*****************************************************************************
1314 NS_IMETHODIMP
1315 nsDocShell::LoadURI(nsIURI * aURI,
1316 nsIDocShellLoadInfo * aLoadInfo,
1317 uint32_t aLoadFlags,
1318 bool aFirstParty)
1319 {
1320 NS_PRECONDITION(aLoadInfo || (aLoadFlags & EXTRA_LOAD_FLAGS) == 0,
1321 "Unexpected flags");
1322 NS_PRECONDITION((aLoadFlags & 0xf) == 0, "Should not have these flags set");
1323
1324 // Note: we allow loads to get through here even if mFiredUnloadEvent is
1325 // true; that case will get handled in LoadInternal or LoadHistoryEntry.
1326 if (IsPrintingOrPP()) {
1327 return NS_OK; // JS may not handle returning of an error code
1328 }
1329 nsCOMPtr<nsIURI> referrer;
1330 nsCOMPtr<nsIInputStream> postStream;
1331 nsCOMPtr<nsIInputStream> headersStream;
1332 nsCOMPtr<nsISupports> owner;
1333 bool inheritOwner = false;
1334 bool ownerIsExplicit = false;
1335 bool sendReferrer = true;
1336 bool isSrcdoc = false;
1337 nsCOMPtr<nsISHEntry> shEntry;
1338 nsXPIDLString target;
1339 nsAutoString srcdoc;
1340 nsCOMPtr<nsIDocShell> sourceDocShell;
1341 nsCOMPtr<nsIURI> baseURI;
1342
1343 uint32_t loadType = MAKE_LOAD_TYPE(LOAD_NORMAL, aLoadFlags);
1344
1345 NS_ENSURE_ARG(aURI);
1346
1347 if (!StartupTimeline::HasRecord(StartupTimeline::FIRST_LOAD_URI) &&
1348 mItemType == typeContent && !NS_IsAboutBlank(aURI)) {
1349 StartupTimeline::RecordOnce(StartupTimeline::FIRST_LOAD_URI);
1350 }
1351
1352 // Extract the info from the DocShellLoadInfo struct...
1353 if (aLoadInfo) {
1354 aLoadInfo->GetReferrer(getter_AddRefs(referrer));
1355
1356 nsDocShellInfoLoadType lt = nsIDocShellLoadInfo::loadNormal;
1357 aLoadInfo->GetLoadType(&lt);
1358 // Get the appropriate loadType from nsIDocShellLoadInfo type
1359 loadType = ConvertDocShellLoadInfoToLoadType(lt);
1360
1361 aLoadInfo->GetOwner(getter_AddRefs(owner));
1362 aLoadInfo->GetInheritOwner(&inheritOwner);
1363 aLoadInfo->GetOwnerIsExplicit(&ownerIsExplicit);
1364 aLoadInfo->GetSHEntry(getter_AddRefs(shEntry));
1365 aLoadInfo->GetTarget(getter_Copies(target));
1366 aLoadInfo->GetPostDataStream(getter_AddRefs(postStream));
1367 aLoadInfo->GetHeadersStream(getter_AddRefs(headersStream));
1368 aLoadInfo->GetSendReferrer(&sendReferrer);
1369 aLoadInfo->GetIsSrcdocLoad(&isSrcdoc);
1370 aLoadInfo->GetSrcdocData(srcdoc);
1371 aLoadInfo->GetSourceDocShell(getter_AddRefs(sourceDocShell));
1372 aLoadInfo->GetBaseURI(getter_AddRefs(baseURI));
1373 }
1374
1375 #if defined(PR_LOGGING) && defined(DEBUG)
1376 if (PR_LOG_TEST(gDocShellLog, PR_LOG_DEBUG)) {
1377 nsAutoCString uristr;
1378 aURI->GetAsciiSpec(uristr);
1379 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
1380 ("nsDocShell[%p]: loading %s with flags 0x%08x",
1381 this, uristr.get(), aLoadFlags));
1382 }
1383 #endif
1384
1385 if (!shEntry &&
1386 !LOAD_TYPE_HAS_FLAGS(loadType, LOAD_FLAGS_REPLACE_HISTORY)) {
1387 // First verify if this is a subframe.
1388 nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
1389 GetSameTypeParent(getter_AddRefs(parentAsItem));
1390 nsCOMPtr<nsIDocShell> parentDS(do_QueryInterface(parentAsItem));
1391 uint32_t parentLoadType;
1392
1393 if (parentDS && parentDS != static_cast<nsIDocShell *>(this)) {
1394 /* OK. It is a subframe. Checkout the
1395 * parent's loadtype. If the parent was loaded thro' a history
1396 * mechanism, then get the SH entry for the child from the parent.
1397 * This is done to restore frameset navigation while going back/forward.
1398 * If the parent was loaded through any other loadType, set the
1399 * child's loadType too accordingly, so that session history does not
1400 * get confused.
1401 */
1402
1403 // Get the parent's load type
1404 parentDS->GetLoadType(&parentLoadType);
1405
1406 // Get the ShEntry for the child from the parent
1407 nsCOMPtr<nsISHEntry> currentSH;
1408 bool oshe = false;
1409 parentDS->GetCurrentSHEntry(getter_AddRefs(currentSH), &oshe);
1410 bool dynamicallyAddedChild = mDynamicallyCreated;
1411 if (!dynamicallyAddedChild && !oshe && currentSH) {
1412 currentSH->HasDynamicallyAddedChild(&dynamicallyAddedChild);
1413 }
1414 if (!dynamicallyAddedChild) {
1415 // Only use the old SHEntry, if we're sure enough that
1416 // it wasn't originally for some other frame.
1417 parentDS->GetChildSHEntry(mChildOffset, getter_AddRefs(shEntry));
1418 }
1419
1420 // Make some decisions on the child frame's loadType based on the
1421 // parent's loadType.
1422 if (mCurrentURI == nullptr) {
1423 // This is a newly created frame. Check for exception cases first.
1424 // By default the subframe will inherit the parent's loadType.
1425 if (shEntry && (parentLoadType == LOAD_NORMAL ||
1426 parentLoadType == LOAD_LINK ||
1427 parentLoadType == LOAD_NORMAL_EXTERNAL)) {
1428 // The parent was loaded normally. In this case, this *brand new* child really shouldn't
1429 // have a SHEntry. If it does, it could be because the parent is replacing an
1430 // existing frame with a new frame, in the onLoadHandler. We don't want this
1431 // url to get into session history. Clear off shEntry, and set load type to
1432 // LOAD_BYPASS_HISTORY.
1433 bool inOnLoadHandler=false;
1434 parentDS->GetIsExecutingOnLoadHandler(&inOnLoadHandler);
1435 if (inOnLoadHandler) {
1436 loadType = LOAD_NORMAL_REPLACE;
1437 shEntry = nullptr;
1438 }
1439 } else if (parentLoadType == LOAD_REFRESH) {
1440 // Clear shEntry. For refresh loads, we have to load
1441 // what comes thro' the pipe, not what's in history.
1442 shEntry = nullptr;
1443 } else if ((parentLoadType == LOAD_BYPASS_HISTORY) ||
1444 (shEntry &&
1445 ((parentLoadType & LOAD_CMD_HISTORY) ||
1446 (parentLoadType == LOAD_RELOAD_NORMAL) ||
1447 (parentLoadType == LOAD_RELOAD_CHARSET_CHANGE)))) {
1448 // If the parent url, bypassed history or was loaded from
1449 // history, pass on the parent's loadType to the new child
1450 // frame too, so that the child frame will also
1451 // avoid getting into history.
1452 loadType = parentLoadType;
1453 } else if (parentLoadType == LOAD_ERROR_PAGE) {
1454 // If the parent document is an error page, we don't
1455 // want to update global/session history. However,
1456 // this child frame is not an error page.
1457 loadType = LOAD_BYPASS_HISTORY;
1458 } else if ((parentLoadType == LOAD_RELOAD_BYPASS_CACHE) ||
1459 (parentLoadType == LOAD_RELOAD_BYPASS_PROXY) ||
1460 (parentLoadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE)) {
1461 // the new frame should inherit the parent's load type so that it also
1462 // bypasses the cache and/or proxy
1463 loadType = parentLoadType;
1464 }
1465 } else {
1466 // This is a pre-existing subframe. If the load was not originally initiated
1467 // by session history, (if (!shEntry) condition succeeded) and mCurrentURI is not null,
1468 // it is possible that a parent's onLoadHandler or even self's onLoadHandler is loading
1469 // a new page in this child. Check parent's and self's busy flag and if it is set,
1470 // we don't want this onLoadHandler load to get in to session history.
1471 uint32_t parentBusy = BUSY_FLAGS_NONE;
1472 uint32_t selfBusy = BUSY_FLAGS_NONE;
1473 parentDS->GetBusyFlags(&parentBusy);
1474 GetBusyFlags(&selfBusy);
1475 if (parentBusy & BUSY_FLAGS_BUSY ||
1476 selfBusy & BUSY_FLAGS_BUSY) {
1477 loadType = LOAD_NORMAL_REPLACE;
1478 shEntry = nullptr;
1479 }
1480 }
1481 } //parentDS
1482 else {
1483 // This is the root docshell. If we got here while
1484 // executing an onLoad Handler,this load will not go
1485 // into session history.
1486 bool inOnLoadHandler=false;
1487 GetIsExecutingOnLoadHandler(&inOnLoadHandler);
1488 if (inOnLoadHandler) {
1489 loadType = LOAD_NORMAL_REPLACE;
1490 }
1491 }
1492 } // !shEntry
1493
1494 if (shEntry) {
1495 #ifdef DEBUG
1496 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
1497 ("nsDocShell[%p]: loading from session history", this));
1498 #endif
1499
1500 return LoadHistoryEntry(shEntry, loadType);
1501 }
1502
1503 // On history navigation via Back/Forward buttons, don't execute
1504 // automatic JavaScript redirection such as |location.href = ...| or
1505 // |window.open()|
1506 //
1507 // LOAD_NORMAL: window.open(...) etc.
1508 // LOAD_STOP_CONTENT: location.href = ..., location.assign(...)
1509 if ((loadType == LOAD_NORMAL || loadType == LOAD_STOP_CONTENT) &&
1510 ShouldBlockLoadingForBackButton()) {
1511 return NS_OK;
1512 }
1513
1514 // Perform the load...
1515
1516 // We need an owner (a referring principal).
1517 //
1518 // If ownerIsExplicit is not set there are 4 possibilities:
1519 // (1) If the system principal or an expanded principal was passed
1520 // in and we're a typeContent docshell, inherit the principal
1521 // from the current document instead.
1522 // (2) In all other cases when the principal passed in is not null,
1523 // use that principal.
1524 // (3) If the caller has allowed inheriting from the current document,
1525 // or if we're being called from system code (eg chrome JS or pure
1526 // C++) then inheritOwner should be true and InternalLoad will get
1527 // an owner from the current document. If none of these things are
1528 // true, then
1529 // (4) we pass a null owner into the channel, and an owner will be
1530 // created later from the channel's internal data.
1531 //
1532 // If ownerIsExplicit *is* set, there are 4 possibilities
1533 // (1) If the system principal or an expanded principal was passed in
1534 // and we're a typeContent docshell, return an error.
1535 // (2) In all other cases when the principal passed in is not null,
1536 // use that principal.
1537 // (3) If the caller has allowed inheriting from the current document,
1538 // then inheritOwner should be true and InternalLoad will get an owner
1539 // from the current document. If none of these things are true, then
1540 // (4) we pass a null owner into the channel, and an owner will be
1541 // created later from the channel's internal data.
1542 //
1543 // NOTE: This all only works because the only thing the owner is used
1544 // for in InternalLoad is data:, javascript:, and about:blank
1545 // URIs. For other URIs this would all be dead wrong!
1546
1547 if (owner && mItemType != typeChrome) {
1548 nsCOMPtr<nsIPrincipal> ownerPrincipal = do_QueryInterface(owner);
1549 if (nsContentUtils::IsSystemOrExpandedPrincipal(ownerPrincipal)) {
1550 if (ownerIsExplicit) {
1551 return NS_ERROR_DOM_SECURITY_ERR;
1552 }
1553 owner = nullptr;
1554 inheritOwner = true;
1555 }
1556 }
1557 if (!owner && !inheritOwner && !ownerIsExplicit) {
1558 // See if there's system or chrome JS code running
1559 inheritOwner = nsContentUtils::IsSystemPrincipal(
1560 nsContentUtils::GetSubjectPrincipal());
1561 }
1562
1563 if (aLoadFlags & LOAD_FLAGS_DISALLOW_INHERIT_OWNER) {
1564 inheritOwner = false;
1565 owner = do_CreateInstance("@mozilla.org/nullprincipal;1");
1566 }
1567
1568 uint32_t flags = 0;
1569
1570 if (inheritOwner)
1571 flags |= INTERNAL_LOAD_FLAGS_INHERIT_OWNER;
1572
1573 if (!sendReferrer)
1574 flags |= INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER;
1575
1576 if (aLoadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP)
1577 flags |= INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
1578
1579 if (aLoadFlags & LOAD_FLAGS_FIXUP_SCHEME_TYPOS)
1580 flags |= INTERNAL_LOAD_FLAGS_FIXUP_SCHEME_TYPOS;
1581
1582 if (aLoadFlags & LOAD_FLAGS_FIRST_LOAD)
1583 flags |= INTERNAL_LOAD_FLAGS_FIRST_LOAD;
1584
1585 if (aLoadFlags & LOAD_FLAGS_BYPASS_CLASSIFIER)
1586 flags |= INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER;
1587
1588 if (aLoadFlags & LOAD_FLAGS_FORCE_ALLOW_COOKIES)
1589 flags |= INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES;
1590
1591 if (isSrcdoc)
1592 flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC;
1593
1594 return InternalLoad(aURI,
1595 referrer,
1596 owner,
1597 flags,
1598 target.get(),
1599 nullptr, // No type hint
1600 NullString(), // No forced download
1601 postStream,
1602 headersStream,
1603 loadType,
1604 nullptr, // No SHEntry
1605 aFirstParty,
1606 srcdoc,
1607 sourceDocShell,
1608 baseURI,
1609 nullptr, // No nsIDocShell
1610 nullptr); // No nsIRequest
1611 }
1612
1613 NS_IMETHODIMP
1614 nsDocShell::LoadStream(nsIInputStream *aStream, nsIURI * aURI,
1615 const nsACString &aContentType,
1616 const nsACString &aContentCharset,
1617 nsIDocShellLoadInfo * aLoadInfo)
1618 {
1619 NS_ENSURE_ARG(aStream);
1620
1621 mAllowKeywordFixup = false;
1622
1623 // if the caller doesn't pass in a URI we need to create a dummy URI. necko
1624 // currently requires a URI in various places during the load. Some consumers
1625 // do as well.
1626 nsCOMPtr<nsIURI> uri = aURI;
1627 if (!uri) {
1628 // HACK ALERT
1629 nsresult rv = NS_OK;
1630 uri = do_CreateInstance(NS_SIMPLEURI_CONTRACTID, &rv);
1631 if (NS_FAILED(rv))
1632 return rv;
1633 // Make sure that the URI spec "looks" like a protocol and path...
1634 // For now, just use a bogus protocol called "internal"
1635 rv = uri->SetSpec(NS_LITERAL_CSTRING("internal:load-stream"));
1636 if (NS_FAILED(rv))
1637 return rv;
1638 }
1639
1640 uint32_t loadType = LOAD_NORMAL;
1641 if (aLoadInfo) {
1642 nsDocShellInfoLoadType lt = nsIDocShellLoadInfo::loadNormal;
1643 (void) aLoadInfo->GetLoadType(&lt);
1644 // Get the appropriate LoadType from nsIDocShellLoadInfo type
1645 loadType = ConvertDocShellLoadInfoToLoadType(lt);
1646 }
1647
1648 NS_ENSURE_SUCCESS(Stop(nsIWebNavigation::STOP_NETWORK), NS_ERROR_FAILURE);
1649
1650 mLoadType = loadType;
1651
1652 // build up a channel for this stream.
1653 nsCOMPtr<nsIChannel> channel;
1654 NS_ENSURE_SUCCESS(NS_NewInputStreamChannel
1655 (getter_AddRefs(channel), uri, aStream,
1656 aContentType, aContentCharset),
1657 NS_ERROR_FAILURE);
1658
1659 nsCOMPtr<nsIURILoader>
1660 uriLoader(do_GetService(NS_URI_LOADER_CONTRACTID));
1661 NS_ENSURE_TRUE(uriLoader, NS_ERROR_FAILURE);
1662
1663 NS_ENSURE_SUCCESS(DoChannelLoad(channel, uriLoader, false),
1664 NS_ERROR_FAILURE);
1665 return NS_OK;
1666 }
1667
1668 NS_IMETHODIMP
1669 nsDocShell::CreateLoadInfo(nsIDocShellLoadInfo ** aLoadInfo)
1670 {
1671 nsDocShellLoadInfo *loadInfo = new nsDocShellLoadInfo();
1672 NS_ENSURE_TRUE(loadInfo, NS_ERROR_OUT_OF_MEMORY);
1673 nsCOMPtr<nsIDocShellLoadInfo> localRef(loadInfo);
1674
1675 *aLoadInfo = localRef;
1676 NS_ADDREF(*aLoadInfo);
1677 return NS_OK;
1678 }
1679
1680
1681 /*
1682 * Reset state to a new content model within the current document and the document
1683 * viewer. Called by the document before initiating an out of band document.write().
1684 */
1685 NS_IMETHODIMP
1686 nsDocShell::PrepareForNewContentModel()
1687 {
1688 mEODForCurrentDocument = false;
1689 return NS_OK;
1690 }
1691
1692
1693 NS_IMETHODIMP
1694 nsDocShell::FirePageHideNotification(bool aIsUnload)
1695 {
1696 if (mContentViewer && !mFiredUnloadEvent) {
1697 // Keep an explicit reference since calling PageHide could release
1698 // mContentViewer
1699 nsCOMPtr<nsIContentViewer> kungFuDeathGrip(mContentViewer);
1700 mFiredUnloadEvent = true;
1701
1702 if (mTiming) {
1703 mTiming->NotifyUnloadEventStart();
1704 }
1705
1706 mContentViewer->PageHide(aIsUnload);
1707
1708 if (mTiming) {
1709 mTiming->NotifyUnloadEventEnd();
1710 }
1711
1712 nsAutoTArray<nsCOMPtr<nsIDocShell>, 8> kids;
1713 uint32_t n = mChildList.Length();
1714 kids.SetCapacity(n);
1715 for (uint32_t i = 0; i < n; i++) {
1716 kids.AppendElement(do_QueryInterface(ChildAt(i)));
1717 }
1718
1719 n = kids.Length();
1720 for (uint32_t i = 0; i < n; ++i) {
1721 if (kids[i]) {
1722 kids[i]->FirePageHideNotification(aIsUnload);
1723 }
1724 }
1725 // Now make sure our editor, if any, is detached before we go
1726 // any farther.
1727 DetachEditorFromWindow();
1728 }
1729
1730 return NS_OK;
1731 }
1732
1733 void
1734 nsDocShell::MaybeInitTiming()
1735 {
1736 if (mTiming) {
1737 return;
1738 }
1739
1740 mTiming = new nsDOMNavigationTiming();
1741 mTiming->NotifyNavigationStart();
1742 }
1743
1744
1745 //
1746 // Bug 13871: Prevent frameset spoofing
1747 //
1748 // This routine answers: 'Is origin's document from same domain as
1749 // target's document?'
1750 //
1751 // file: uris are considered the same domain for the purpose of
1752 // frame navigation regardless of script accessibility (bug 420425)
1753 //
1754 /* static */
1755 bool
1756 nsDocShell::ValidateOrigin(nsIDocShellTreeItem* aOriginTreeItem,
1757 nsIDocShellTreeItem* aTargetTreeItem)
1758 {
1759 // We want to bypass this check for chrome callers, but only if there's
1760 // JS on the stack. System callers still need to do it.
1761 if (nsContentUtils::GetCurrentJSContext() && nsContentUtils::IsCallerChrome()) {
1762 return true;
1763 }
1764
1765 // Get origin document principal
1766 nsCOMPtr<nsIDocument> originDocument(do_GetInterface(aOriginTreeItem));
1767 NS_ENSURE_TRUE(originDocument, false);
1768
1769 // Get target principal
1770 nsCOMPtr<nsIDocument> targetDocument(do_GetInterface(aTargetTreeItem));
1771 NS_ENSURE_TRUE(targetDocument, false);
1772
1773 bool equal;
1774 nsresult rv = originDocument->NodePrincipal()->Equals(targetDocument->NodePrincipal(),
1775 &equal);
1776 if (NS_SUCCEEDED(rv) && equal) {
1777 return true;
1778 }
1779
1780 // Not strictly equal, special case if both are file: uris
1781 bool originIsFile = false;
1782 bool targetIsFile = false;
1783 nsCOMPtr<nsIURI> originURI;
1784 nsCOMPtr<nsIURI> targetURI;
1785 nsCOMPtr<nsIURI> innerOriginURI;
1786 nsCOMPtr<nsIURI> innerTargetURI;
1787
1788 rv = originDocument->NodePrincipal()->GetURI(getter_AddRefs(originURI));
1789 if (NS_SUCCEEDED(rv) && originURI)
1790 innerOriginURI = NS_GetInnermostURI(originURI);
1791
1792 rv = targetDocument->NodePrincipal()->GetURI(getter_AddRefs(targetURI));
1793 if (NS_SUCCEEDED(rv) && targetURI)
1794 innerTargetURI = NS_GetInnermostURI(targetURI);
1795
1796 return innerOriginURI && innerTargetURI &&
1797 NS_SUCCEEDED(innerOriginURI->SchemeIs("file", &originIsFile)) &&
1798 NS_SUCCEEDED(innerTargetURI->SchemeIs("file", &targetIsFile)) &&
1799 originIsFile && targetIsFile;
1800 }
1801
1802 NS_IMETHODIMP
1803 nsDocShell::GetEldestPresContext(nsPresContext** aPresContext)
1804 {
1805 NS_ENSURE_ARG_POINTER(aPresContext);
1806 *aPresContext = nullptr;
1807
1808 nsCOMPtr<nsIContentViewer> viewer = mContentViewer;
1809 while (viewer) {
1810 nsCOMPtr<nsIContentViewer> prevViewer;
1811 viewer->GetPreviousViewer(getter_AddRefs(prevViewer));
1812 if (!prevViewer) {
1813 return viewer->GetPresContext(aPresContext);
1814 }
1815 viewer = prevViewer;
1816 }
1817
1818 return NS_OK;
1819 }
1820
1821 NS_IMETHODIMP
1822 nsDocShell::GetPresContext(nsPresContext ** aPresContext)
1823 {
1824 NS_ENSURE_ARG_POINTER(aPresContext);
1825 *aPresContext = nullptr;
1826
1827 if (!mContentViewer)
1828 return NS_OK;
1829
1830 return mContentViewer->GetPresContext(aPresContext);
1831 }
1832
1833 NS_IMETHODIMP_(nsIPresShell*)
1834 nsDocShell::GetPresShell()
1835 {
1836 nsRefPtr<nsPresContext> presContext;
1837 (void) GetPresContext(getter_AddRefs(presContext));
1838 return presContext ? presContext->GetPresShell() : nullptr;
1839 }
1840
1841 NS_IMETHODIMP
1842 nsDocShell::GetEldestPresShell(nsIPresShell** aPresShell)
1843 {
1844 nsresult rv = NS_OK;
1845
1846 NS_ENSURE_ARG_POINTER(aPresShell);
1847 *aPresShell = nullptr;
1848
1849 nsRefPtr<nsPresContext> presContext;
1850 (void) GetEldestPresContext(getter_AddRefs(presContext));
1851
1852 if (presContext) {
1853 NS_IF_ADDREF(*aPresShell = presContext->GetPresShell());
1854 }
1855
1856 return rv;
1857 }
1858
1859 NS_IMETHODIMP
1860 nsDocShell::GetContentViewer(nsIContentViewer ** aContentViewer)
1861 {
1862 NS_ENSURE_ARG_POINTER(aContentViewer);
1863
1864 *aContentViewer = mContentViewer;
1865 NS_IF_ADDREF(*aContentViewer);
1866 return NS_OK;
1867 }
1868
1869 NS_IMETHODIMP
1870 nsDocShell::SetChromeEventHandler(nsIDOMEventTarget* aChromeEventHandler)
1871 {
1872 // Weak reference. Don't addref.
1873 nsCOMPtr<EventTarget> handler = do_QueryInterface(aChromeEventHandler);
1874 mChromeEventHandler = handler.get();
1875
1876 if (mScriptGlobal) {
1877 mScriptGlobal->SetChromeEventHandler(mChromeEventHandler);
1878 }
1879
1880 return NS_OK;
1881 }
1882
1883 NS_IMETHODIMP
1884 nsDocShell::GetChromeEventHandler(nsIDOMEventTarget** aChromeEventHandler)
1885 {
1886 NS_ENSURE_ARG_POINTER(aChromeEventHandler);
1887 nsCOMPtr<EventTarget> handler = mChromeEventHandler;
1888 handler.forget(aChromeEventHandler);
1889 return NS_OK;
1890 }
1891
1892 /* void setCurrentURI (in nsIURI uri); */
1893 NS_IMETHODIMP
1894 nsDocShell::SetCurrentURI(nsIURI *aURI)
1895 {
1896 // Note that securityUI will set STATE_IS_INSECURE, even if
1897 // the scheme of |aURI| is "https".
1898 SetCurrentURI(aURI, nullptr, true, 0);
1899 return NS_OK;
1900 }
1901
1902 bool
1903 nsDocShell::SetCurrentURI(nsIURI *aURI, nsIRequest *aRequest,
1904 bool aFireOnLocationChange, uint32_t aLocationFlags)
1905 {
1906 #ifdef PR_LOGGING
1907 if (gDocShellLeakLog && PR_LOG_TEST(gDocShellLeakLog, PR_LOG_DEBUG)) {
1908 nsAutoCString spec;
1909 if (aURI)
1910 aURI->GetSpec(spec);
1911 PR_LogPrint("DOCSHELL %p SetCurrentURI %s\n", this, spec.get());
1912 }
1913 #endif
1914
1915 // We don't want to send a location change when we're displaying an error
1916 // page, and we don't want to change our idea of "current URI" either
1917 if (mLoadType == LOAD_ERROR_PAGE) {
1918 return false;
1919 }
1920
1921 mCurrentURI = NS_TryToMakeImmutable(aURI);
1922
1923 bool isRoot = false; // Is this the root docshell
1924 bool isSubFrame = false; // Is this a subframe navigation?
1925
1926 nsCOMPtr<nsIDocShellTreeItem> root;
1927
1928 GetSameTypeRootTreeItem(getter_AddRefs(root));
1929 if (root.get() == static_cast<nsIDocShellTreeItem *>(this))
1930 {
1931 // This is the root docshell
1932 isRoot = true;
1933 }
1934 if (mLSHE) {
1935 mLSHE->GetIsSubFrame(&isSubFrame);
1936 }
1937
1938 if (!isSubFrame && !isRoot) {
1939 /*
1940 * We don't want to send OnLocationChange notifications when
1941 * a subframe is being loaded for the first time, while
1942 * visiting a frameset page
1943 */
1944 return false;
1945 }
1946
1947 if (aFireOnLocationChange) {
1948 FireOnLocationChange(this, aRequest, aURI, aLocationFlags);
1949 }
1950 return !aFireOnLocationChange;
1951 }
1952
1953 NS_IMETHODIMP
1954 nsDocShell::GetCharset(nsACString& aCharset)
1955 {
1956 aCharset.Truncate();
1957
1958 nsIPresShell* presShell = GetPresShell();
1959 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
1960 nsIDocument *doc = presShell->GetDocument();
1961 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
1962 aCharset = doc->GetDocumentCharacterSet();
1963 return NS_OK;
1964 }
1965
1966 NS_IMETHODIMP
1967 nsDocShell::GatherCharsetMenuTelemetry()
1968 {
1969 nsCOMPtr<nsIContentViewer> viewer;
1970 GetContentViewer(getter_AddRefs(viewer));
1971 if (!viewer) {
1972 return NS_OK;
1973 }
1974
1975 nsIDocument* doc = viewer->GetDocument();
1976 if (!doc || doc->WillIgnoreCharsetOverride()) {
1977 return NS_OK;
1978 }
1979
1980 Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_USED, true);
1981
1982 bool isFileURL = false;
1983 nsIURI* url = doc->GetOriginalURI();
1984 if (url) {
1985 url->SchemeIs("file", &isFileURL);
1986 }
1987
1988 int32_t charsetSource = doc->GetDocumentCharacterSetSource();
1989 switch (charsetSource) {
1990 case kCharsetFromTopLevelDomain:
1991 // Unlabeled doc on a domain that we map to a fallback encoding
1992 Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 7);
1993 break;
1994 case kCharsetFromFallback:
1995 case kCharsetFromDocTypeDefault:
1996 case kCharsetFromCache:
1997 case kCharsetFromParentFrame:
1998 case kCharsetFromHintPrevDoc:
1999 // Changing charset on an unlabeled doc.
2000 if (isFileURL) {
2001 Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 0);
2002 } else {
2003 Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 1);
2004 }
2005 break;
2006 case kCharsetFromAutoDetection:
2007 // Changing charset on unlabeled doc where chardet fired
2008 if (isFileURL) {
2009 Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 2);
2010 } else {
2011 Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 3);
2012 }
2013 break;
2014 case kCharsetFromMetaPrescan:
2015 case kCharsetFromMetaTag:
2016 case kCharsetFromChannel:
2017 // Changing charset on a doc that had a charset label.
2018 Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 4);
2019 break;
2020 case kCharsetFromParentForced:
2021 case kCharsetFromUserForced:
2022 // Changing charset on a document that already had an override.
2023 Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 5);
2024 break;
2025 case kCharsetFromIrreversibleAutoDetection:
2026 case kCharsetFromOtherComponent:
2027 case kCharsetFromByteOrderMark:
2028 case kCharsetUninitialized:
2029 default:
2030 // Bug. This isn't supposed to happen.
2031 Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 6);
2032 break;
2033 }
2034 return NS_OK;
2035 }
2036
2037 NS_IMETHODIMP
2038 nsDocShell::SetCharset(const nsACString& aCharset)
2039 {
2040 // set the charset override
2041 return SetForcedCharset(aCharset);
2042 }
2043
2044 NS_IMETHODIMP nsDocShell::SetForcedCharset(const nsACString& aCharset)
2045 {
2046 if (aCharset.IsEmpty()) {
2047 mForcedCharset.Truncate();
2048 return NS_OK;
2049 }
2050 nsAutoCString encoding;
2051 if (!EncodingUtils::FindEncodingForLabel(aCharset, encoding)) {
2052 // Reject unknown labels
2053 return NS_ERROR_INVALID_ARG;
2054 }
2055 if (!EncodingUtils::IsAsciiCompatible(encoding)) {
2056 // Reject XSS hazards
2057 return NS_ERROR_INVALID_ARG;
2058 }
2059 mForcedCharset = encoding;
2060 return NS_OK;
2061 }
2062
2063 NS_IMETHODIMP nsDocShell::GetForcedCharset(nsACString& aResult)
2064 {
2065 aResult = mForcedCharset;
2066 return NS_OK;
2067 }
2068
2069 void
2070 nsDocShell::SetParentCharset(const nsACString& aCharset,
2071 int32_t aCharsetSource,
2072 nsIPrincipal* aPrincipal)
2073 {
2074 mParentCharset = aCharset;
2075 mParentCharsetSource = aCharsetSource;
2076 mParentCharsetPrincipal = aPrincipal;
2077 }
2078
2079 void
2080 nsDocShell::GetParentCharset(nsACString& aCharset,
2081 int32_t* aCharsetSource,
2082 nsIPrincipal** aPrincipal)
2083 {
2084 aCharset = mParentCharset;
2085 *aCharsetSource = mParentCharsetSource;
2086 NS_IF_ADDREF(*aPrincipal = mParentCharsetPrincipal);
2087 }
2088
2089 NS_IMETHODIMP
2090 nsDocShell::GetChannelIsUnsafe(bool *aUnsafe)
2091 {
2092 *aUnsafe = false;
2093
2094 nsIChannel* channel = GetCurrentDocChannel();
2095 if (!channel) {
2096 return NS_OK;
2097 }
2098
2099 nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(channel);
2100 if (!jarChannel) {
2101 return NS_OK;
2102 }
2103
2104 return jarChannel->GetIsUnsafe(aUnsafe);
2105 }
2106
2107 NS_IMETHODIMP
2108 nsDocShell::GetHasMixedActiveContentLoaded(bool* aHasMixedActiveContentLoaded)
2109 {
2110 nsCOMPtr<nsIDocument> doc(do_GetInterface(GetAsSupports(this)));
2111 *aHasMixedActiveContentLoaded = doc && doc->GetHasMixedActiveContentLoaded();
2112 return NS_OK;
2113 }
2114
2115 NS_IMETHODIMP
2116 nsDocShell::GetHasMixedActiveContentBlocked(bool* aHasMixedActiveContentBlocked)
2117 {
2118 nsCOMPtr<nsIDocument> doc(do_GetInterface(GetAsSupports(this)));
2119 *aHasMixedActiveContentBlocked = doc && doc->GetHasMixedActiveContentBlocked();
2120 return NS_OK;
2121 }
2122
2123 NS_IMETHODIMP
2124 nsDocShell::GetHasMixedDisplayContentLoaded(bool* aHasMixedDisplayContentLoaded)
2125 {
2126 nsCOMPtr<nsIDocument> doc(do_GetInterface(GetAsSupports(this)));
2127 *aHasMixedDisplayContentLoaded = doc && doc->GetHasMixedDisplayContentLoaded();
2128 return NS_OK;
2129 }
2130
2131 NS_IMETHODIMP
2132 nsDocShell::GetHasMixedDisplayContentBlocked(bool* aHasMixedDisplayContentBlocked)
2133 {
2134 nsCOMPtr<nsIDocument> doc(do_GetInterface(GetAsSupports(this)));
2135 *aHasMixedDisplayContentBlocked = doc && doc->GetHasMixedDisplayContentBlocked();
2136 return NS_OK;
2137 }
2138
2139 NS_IMETHODIMP
2140 nsDocShell::GetAllowPlugins(bool * aAllowPlugins)
2141 {
2142 NS_ENSURE_ARG_POINTER(aAllowPlugins);
2143
2144 *aAllowPlugins = mAllowPlugins;
2145 if (!mAllowPlugins) {
2146 return NS_OK;
2147 }
2148
2149 bool unsafe;
2150 *aAllowPlugins = NS_SUCCEEDED(GetChannelIsUnsafe(&unsafe)) && !unsafe;
2151 return NS_OK;
2152 }
2153
2154 NS_IMETHODIMP
2155 nsDocShell::SetAllowPlugins(bool aAllowPlugins)
2156 {
2157 mAllowPlugins = aAllowPlugins;
2158 //XXX should enable or disable a plugin host
2159 return NS_OK;
2160 }
2161
2162 NS_IMETHODIMP
2163 nsDocShell::GetAllowJavascript(bool * aAllowJavascript)
2164 {
2165 NS_ENSURE_ARG_POINTER(aAllowJavascript);
2166
2167 *aAllowJavascript = mAllowJavascript;
2168 return NS_OK;
2169 }
2170
2171 NS_IMETHODIMP
2172 nsDocShell::SetAllowJavascript(bool aAllowJavascript)
2173 {
2174 mAllowJavascript = aAllowJavascript;
2175 RecomputeCanExecuteScripts();
2176 return NS_OK;
2177 }
2178
2179 NS_IMETHODIMP
2180 nsDocShell::GetUsePrivateBrowsing(bool* aUsePrivateBrowsing)
2181 {
2182 NS_ENSURE_ARG_POINTER(aUsePrivateBrowsing);
2183
2184 *aUsePrivateBrowsing = mInPrivateBrowsing;
2185 return NS_OK;
2186 }
2187
2188 NS_IMETHODIMP
2189 nsDocShell::SetUsePrivateBrowsing(bool aUsePrivateBrowsing)
2190 {
2191 nsContentUtils::ReportToConsoleNonLocalized(
2192 NS_LITERAL_STRING("Only internal code is allowed to set the usePrivateBrowsing attribute"),
2193 nsIScriptError::warningFlag,
2194 NS_LITERAL_CSTRING("Internal API Used"),
2195 mContentViewer ? mContentViewer->GetDocument() : nullptr);
2196
2197 return SetPrivateBrowsing(aUsePrivateBrowsing);
2198 }
2199
2200 NS_IMETHODIMP
2201 nsDocShell::SetPrivateBrowsing(bool aUsePrivateBrowsing)
2202 {
2203 bool changed = aUsePrivateBrowsing != mInPrivateBrowsing;
2204 if (changed) {
2205 mInPrivateBrowsing = aUsePrivateBrowsing;
2206 if (mAffectPrivateSessionLifetime) {
2207 if (aUsePrivateBrowsing) {
2208 IncreasePrivateDocShellCount();
2209 } else {
2210 DecreasePrivateDocShellCount();
2211 }
2212 }
2213 }
2214
2215 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
2216 while (iter.HasMore()) {
2217 nsCOMPtr<nsILoadContext> shell = do_QueryObject(iter.GetNext());
2218 if (shell) {
2219 shell->SetPrivateBrowsing(aUsePrivateBrowsing);
2220 }
2221 }
2222
2223 if (changed) {
2224 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mPrivacyObservers);
2225 while (iter.HasMore()) {
2226 nsWeakPtr ref = iter.GetNext();
2227 nsCOMPtr<nsIPrivacyTransitionObserver> obs = do_QueryReferent(ref);
2228 if (!obs) {
2229 mPrivacyObservers.RemoveElement(ref);
2230 } else {
2231 obs->PrivateModeChanged(aUsePrivateBrowsing);
2232 }
2233 }
2234 }
2235 return NS_OK;
2236 }
2237
2238 NS_IMETHODIMP
2239 nsDocShell::GetUseRemoteTabs(bool* aUseRemoteTabs)
2240 {
2241 NS_ENSURE_ARG_POINTER(aUseRemoteTabs);
2242
2243 *aUseRemoteTabs = mUseRemoteTabs;
2244 return NS_OK;
2245 }
2246
2247 NS_IMETHODIMP
2248 nsDocShell::SetRemoteTabs(bool aUseRemoteTabs)
2249 {
2250 #ifdef MOZ_CRASHREPORTER
2251 if (aUseRemoteTabs) {
2252 CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("DOMIPCEnabled"),
2253 NS_LITERAL_CSTRING("1"));
2254 }
2255 #endif
2256
2257 mUseRemoteTabs = aUseRemoteTabs;
2258 return NS_OK;
2259 }
2260
2261 NS_IMETHODIMP
2262 nsDocShell::SetAffectPrivateSessionLifetime(bool aAffectLifetime)
2263 {
2264 bool change = aAffectLifetime != mAffectPrivateSessionLifetime;
2265 if (change && mInPrivateBrowsing) {
2266 if (aAffectLifetime) {
2267 IncreasePrivateDocShellCount();
2268 } else {
2269 DecreasePrivateDocShellCount();
2270 }
2271 }
2272 mAffectPrivateSessionLifetime = aAffectLifetime;
2273
2274 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
2275 while (iter.HasMore()) {
2276 nsCOMPtr<nsIDocShell> shell = do_QueryObject(iter.GetNext());
2277 if (shell) {
2278 shell->SetAffectPrivateSessionLifetime(aAffectLifetime);
2279 }
2280 }
2281 return NS_OK;
2282 }
2283
2284 NS_IMETHODIMP
2285 nsDocShell::GetAffectPrivateSessionLifetime(bool* aAffectLifetime)
2286 {
2287 *aAffectLifetime = mAffectPrivateSessionLifetime;
2288 return NS_OK;
2289 }
2290
2291 NS_IMETHODIMP
2292 nsDocShell::AddWeakPrivacyTransitionObserver(nsIPrivacyTransitionObserver* aObserver)
2293 {
2294 nsWeakPtr weakObs = do_GetWeakReference(aObserver);
2295 if (!weakObs) {
2296 return NS_ERROR_NOT_AVAILABLE;
2297 }
2298 return mPrivacyObservers.AppendElement(weakObs) ? NS_OK : NS_ERROR_FAILURE;
2299 }
2300
2301 NS_IMETHODIMP
2302 nsDocShell::AddWeakReflowObserver(nsIReflowObserver* aObserver)
2303 {
2304 nsWeakPtr weakObs = do_GetWeakReference(aObserver);
2305 if (!weakObs) {
2306 return NS_ERROR_FAILURE;
2307 }
2308 return mReflowObservers.AppendElement(weakObs) ? NS_OK : NS_ERROR_FAILURE;
2309 }
2310
2311 NS_IMETHODIMP
2312 nsDocShell::RemoveWeakReflowObserver(nsIReflowObserver* aObserver)
2313 {
2314 nsWeakPtr obs = do_GetWeakReference(aObserver);
2315 return mReflowObservers.RemoveElement(obs) ? NS_OK : NS_ERROR_FAILURE;
2316 }
2317
2318 NS_IMETHODIMP
2319 nsDocShell::NotifyReflowObservers(bool aInterruptible,
2320 DOMHighResTimeStamp aStart,
2321 DOMHighResTimeStamp aEnd)
2322 {
2323 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mReflowObservers);
2324 while (iter.HasMore()) {
2325 nsWeakPtr ref = iter.GetNext();
2326 nsCOMPtr<nsIReflowObserver> obs = do_QueryReferent(ref);
2327 if (!obs) {
2328 mReflowObservers.RemoveElement(ref);
2329 } else if (aInterruptible) {
2330 obs->ReflowInterruptible(aStart, aEnd);
2331 } else {
2332 obs->Reflow(aStart, aEnd);
2333 }
2334 }
2335 return NS_OK;
2336 }
2337
2338 NS_IMETHODIMP nsDocShell::GetAllowMetaRedirects(bool * aReturn)
2339 {
2340 NS_ENSURE_ARG_POINTER(aReturn);
2341
2342 *aReturn = mAllowMetaRedirects;
2343 if (!mAllowMetaRedirects) {
2344 return NS_OK;
2345 }
2346
2347 bool unsafe;
2348 *aReturn = NS_SUCCEEDED(GetChannelIsUnsafe(&unsafe)) && !unsafe;
2349 return NS_OK;
2350 }
2351
2352 NS_IMETHODIMP nsDocShell::SetAllowMetaRedirects(bool aValue)
2353 {
2354 mAllowMetaRedirects = aValue;
2355 return NS_OK;
2356 }
2357
2358 NS_IMETHODIMP nsDocShell::GetAllowSubframes(bool * aAllowSubframes)
2359 {
2360 NS_ENSURE_ARG_POINTER(aAllowSubframes);
2361
2362 *aAllowSubframes = mAllowSubframes;
2363 return NS_OK;
2364 }
2365
2366 NS_IMETHODIMP nsDocShell::SetAllowSubframes(bool aAllowSubframes)
2367 {
2368 mAllowSubframes = aAllowSubframes;
2369 return NS_OK;
2370 }
2371
2372 NS_IMETHODIMP nsDocShell::GetAllowImages(bool * aAllowImages)
2373 {
2374 NS_ENSURE_ARG_POINTER(aAllowImages);
2375
2376 *aAllowImages = mAllowImages;
2377 return NS_OK;
2378 }
2379
2380 NS_IMETHODIMP nsDocShell::SetAllowImages(bool aAllowImages)
2381 {
2382 mAllowImages = aAllowImages;
2383 return NS_OK;
2384 }
2385
2386 NS_IMETHODIMP nsDocShell::GetAllowMedia(bool * aAllowMedia)
2387 {
2388 *aAllowMedia = mAllowMedia;
2389 return NS_OK;
2390 }
2391
2392 NS_IMETHODIMP nsDocShell::SetAllowMedia(bool aAllowMedia)
2393 {
2394 mAllowMedia = aAllowMedia;
2395
2396 // Mute or unmute audio contexts attached to the inner window.
2397 if (mScriptGlobal) {
2398 nsPIDOMWindow* innerWin = mScriptGlobal->GetCurrentInnerWindow();
2399 if (innerWin) {
2400 if (aAllowMedia) {
2401 innerWin->UnmuteAudioContexts();
2402 } else {
2403 innerWin->MuteAudioContexts();
2404 }
2405 }
2406 }
2407
2408 return NS_OK;
2409 }
2410
2411 NS_IMETHODIMP nsDocShell::GetAllowDNSPrefetch(bool * aAllowDNSPrefetch)
2412 {
2413 *aAllowDNSPrefetch = mAllowDNSPrefetch;
2414 return NS_OK;
2415 }
2416
2417 NS_IMETHODIMP nsDocShell::SetAllowDNSPrefetch(bool aAllowDNSPrefetch)
2418 {
2419 mAllowDNSPrefetch = aAllowDNSPrefetch;
2420 return NS_OK;
2421 }
2422
2423 NS_IMETHODIMP nsDocShell::GetAllowWindowControl(bool * aAllowWindowControl)
2424 {
2425 *aAllowWindowControl = mAllowWindowControl;
2426 return NS_OK;
2427 }
2428
2429 NS_IMETHODIMP nsDocShell::SetAllowWindowControl(bool aAllowWindowControl)
2430 {
2431 mAllowWindowControl = aAllowWindowControl;
2432 return NS_OK;
2433 }
2434
2435 NS_IMETHODIMP
2436 nsDocShell::GetAllowContentRetargeting(bool* aAllowContentRetargeting)
2437 {
2438 *aAllowContentRetargeting = mAllowContentRetargeting;
2439 return NS_OK;
2440 }
2441
2442 NS_IMETHODIMP
2443 nsDocShell::SetAllowContentRetargeting(bool aAllowContentRetargeting)
2444 {
2445 mAllowContentRetargeting = aAllowContentRetargeting;
2446 return NS_OK;
2447 }
2448
2449 NS_IMETHODIMP
2450 nsDocShell::GetFullscreenAllowed(bool* aFullscreenAllowed)
2451 {
2452 NS_ENSURE_ARG_POINTER(aFullscreenAllowed);
2453
2454 // Browsers and apps have their mFullscreenAllowed retrieved from their
2455 // corresponding iframe in their parent upon creation.
2456 if (mFullscreenAllowed != CHECK_ATTRIBUTES) {
2457 *aFullscreenAllowed = (mFullscreenAllowed == PARENT_ALLOWS);
2458 return NS_OK;
2459 }
2460
2461 // Assume false until we determine otherwise...
2462 *aFullscreenAllowed = false;
2463
2464 // For non-browsers/apps, check that the enclosing iframe element
2465 // has the allowfullscreen attribute set to true. If any ancestor
2466 // iframe does not have mozallowfullscreen=true, then fullscreen is
2467 // prohibited.
2468 nsCOMPtr<nsPIDOMWindow> win = do_GetInterface(GetAsSupports(this));
2469 if (!win) {
2470 return NS_OK;
2471 }
2472 nsCOMPtr<nsIContent> frameElement = do_QueryInterface(win->GetFrameElementInternal());
2473 if (frameElement &&
2474 frameElement->IsHTML(nsGkAtoms::iframe) &&
2475 !frameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::allowfullscreen) &&
2476 !frameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::mozallowfullscreen)) {
2477 return NS_OK;
2478 }
2479
2480 // If we have no parent then we're the root docshell; no ancestor of the
2481 // original docshell doesn't have a allowfullscreen attribute, so
2482 // report fullscreen as allowed.
2483 nsCOMPtr<nsIDocShellTreeItem> dsti = do_GetInterface(GetAsSupports(this));
2484 NS_ENSURE_TRUE(dsti, NS_OK);
2485
2486 nsCOMPtr<nsIDocShellTreeItem> parentTreeItem;
2487 dsti->GetParent(getter_AddRefs(parentTreeItem));
2488 if (!parentTreeItem) {
2489 *aFullscreenAllowed = true;
2490 return NS_OK;
2491 }
2492 // Otherwise, we have a parent, continue the checking for
2493 // mozFullscreenAllowed in the parent docshell's ancestors.
2494 nsCOMPtr<nsIDocShell> parent = do_QueryInterface(parentTreeItem);
2495 NS_ENSURE_TRUE(parent, NS_OK);
2496
2497 return parent->GetFullscreenAllowed(aFullscreenAllowed);
2498 }
2499
2500 NS_IMETHODIMP
2501 nsDocShell::SetFullscreenAllowed(bool aFullscreenAllowed)
2502 {
2503 if (!nsIDocShell::GetIsBrowserOrApp()) {
2504 // Only allow setting of fullscreenAllowed on content/process boundaries.
2505 // At non-boundaries the fullscreenAllowed attribute is calculated based on
2506 // whether all enclosing frames have the "mozFullscreenAllowed" attribute
2507 // set to "true". fullscreenAllowed is set at the process boundaries to
2508 // propagate the value of the parent's "mozFullscreenAllowed" attribute
2509 // across process boundaries.
2510 return NS_ERROR_UNEXPECTED;
2511 }
2512 mFullscreenAllowed = (aFullscreenAllowed ? PARENT_ALLOWS : PARENT_PROHIBITS);
2513 return NS_OK;
2514 }
2515
2516 NS_IMETHODIMP
2517 nsDocShell::GetMayEnableCharacterEncodingMenu(bool* aMayEnableCharacterEncodingMenu)
2518 {
2519 *aMayEnableCharacterEncodingMenu = false;
2520 if (!mContentViewer) {
2521 return NS_OK;
2522 }
2523 nsIDocument* doc = mContentViewer->GetDocument();
2524 if (!doc) {
2525 return NS_OK;
2526 }
2527 if (doc->WillIgnoreCharsetOverride()) {
2528 return NS_OK;
2529 }
2530
2531 *aMayEnableCharacterEncodingMenu = true;
2532 return NS_OK;
2533 }
2534
2535 NS_IMETHODIMP
2536 nsDocShell::GetDocShellEnumerator(int32_t aItemType, int32_t aDirection, nsISimpleEnumerator **outEnum)
2537 {
2538 NS_ENSURE_ARG_POINTER(outEnum);
2539 *outEnum = nullptr;
2540
2541 nsRefPtr<nsDocShellEnumerator> docShellEnum;
2542 if (aDirection == ENUMERATE_FORWARDS)
2543 docShellEnum = new nsDocShellForwardsEnumerator;
2544 else
2545 docShellEnum = new nsDocShellBackwardsEnumerator;
2546
2547 if (!docShellEnum) return NS_ERROR_OUT_OF_MEMORY;
2548
2549 nsresult rv = docShellEnum->SetEnumDocShellType(aItemType);
2550 if (NS_FAILED(rv)) return rv;
2551
2552 rv = docShellEnum->SetEnumerationRootItem((nsIDocShellTreeItem *)this);
2553 if (NS_FAILED(rv)) return rv;
2554
2555 rv = docShellEnum->First();
2556 if (NS_FAILED(rv)) return rv;
2557
2558 rv = docShellEnum->QueryInterface(NS_GET_IID(nsISimpleEnumerator), (void **)outEnum);
2559
2560 return rv;
2561 }
2562
2563 NS_IMETHODIMP
2564 nsDocShell::GetAppType(uint32_t * aAppType)
2565 {
2566 *aAppType = mAppType;
2567 return NS_OK;
2568 }
2569
2570 NS_IMETHODIMP
2571 nsDocShell::SetAppType(uint32_t aAppType)
2572 {
2573 mAppType = aAppType;
2574 return NS_OK;
2575 }
2576
2577
2578 NS_IMETHODIMP
2579 nsDocShell::GetAllowAuth(bool * aAllowAuth)
2580 {
2581 *aAllowAuth = mAllowAuth;
2582 return NS_OK;
2583 }
2584
2585 NS_IMETHODIMP
2586 nsDocShell::SetAllowAuth(bool aAllowAuth)
2587 {
2588 mAllowAuth = aAllowAuth;
2589 return NS_OK;
2590 }
2591
2592 NS_IMETHODIMP
2593 nsDocShell::GetZoom(float *zoom)
2594 {
2595 NS_ENSURE_ARG_POINTER(zoom);
2596 *zoom = 1.0f;
2597 return NS_OK;
2598 }
2599
2600 NS_IMETHODIMP
2601 nsDocShell::SetZoom(float zoom)
2602 {
2603 return NS_ERROR_NOT_IMPLEMENTED;
2604 }
2605
2606 NS_IMETHODIMP
2607 nsDocShell::GetMarginWidth(int32_t * aWidth)
2608 {
2609 NS_ENSURE_ARG_POINTER(aWidth);
2610
2611 *aWidth = mMarginWidth;
2612 return NS_OK;
2613 }
2614
2615 NS_IMETHODIMP
2616 nsDocShell::SetMarginWidth(int32_t aWidth)
2617 {
2618 mMarginWidth = aWidth;
2619 return NS_OK;
2620 }
2621
2622 NS_IMETHODIMP
2623 nsDocShell::GetMarginHeight(int32_t * aHeight)
2624 {
2625 NS_ENSURE_ARG_POINTER(aHeight);
2626
2627 *aHeight = mMarginHeight;
2628 return NS_OK;
2629 }
2630
2631 NS_IMETHODIMP
2632 nsDocShell::SetMarginHeight(int32_t aHeight)
2633 {
2634 mMarginHeight = aHeight;
2635 return NS_OK;
2636 }
2637
2638 NS_IMETHODIMP
2639 nsDocShell::GetBusyFlags(uint32_t * aBusyFlags)
2640 {
2641 NS_ENSURE_ARG_POINTER(aBusyFlags);
2642
2643 *aBusyFlags = mBusyFlags;
2644 return NS_OK;
2645 }
2646
2647 NS_IMETHODIMP
2648 nsDocShell::TabToTreeOwner(bool aForward, bool* aTookFocus)
2649 {
2650 NS_ENSURE_ARG_POINTER(aTookFocus);
2651
2652 nsCOMPtr<nsIWebBrowserChromeFocus> chromeFocus = do_GetInterface(mTreeOwner);
2653 if (chromeFocus) {
2654 if (aForward)
2655 *aTookFocus = NS_SUCCEEDED(chromeFocus->FocusNextElement());
2656 else
2657 *aTookFocus = NS_SUCCEEDED(chromeFocus->FocusPrevElement());
2658 } else
2659 *aTookFocus = false;
2660
2661 return NS_OK;
2662 }
2663
2664 NS_IMETHODIMP
2665 nsDocShell::GetSecurityUI(nsISecureBrowserUI **aSecurityUI)
2666 {
2667 NS_IF_ADDREF(*aSecurityUI = mSecurityUI);
2668 return NS_OK;
2669 }
2670
2671 NS_IMETHODIMP
2672 nsDocShell::SetSecurityUI(nsISecureBrowserUI *aSecurityUI)
2673 {
2674 mSecurityUI = aSecurityUI;
2675 mSecurityUI->SetDocShell(this);
2676 return NS_OK;
2677 }
2678
2679 NS_IMETHODIMP
2680 nsDocShell::GetUseErrorPages(bool *aUseErrorPages)
2681 {
2682 *aUseErrorPages = UseErrorPages();
2683 return NS_OK;
2684 }
2685
2686 NS_IMETHODIMP
2687 nsDocShell::SetUseErrorPages(bool aUseErrorPages)
2688 {
2689 // If mUseErrorPages is set explicitly, stop using sUseErrorPages.
2690 if (mObserveErrorPages) {
2691 mObserveErrorPages = false;
2692 }
2693 mUseErrorPages = aUseErrorPages;
2694 return NS_OK;
2695 }
2696
2697 NS_IMETHODIMP
2698 nsDocShell::GetPreviousTransIndex(int32_t *aPreviousTransIndex)
2699 {
2700 *aPreviousTransIndex = mPreviousTransIndex;
2701 return NS_OK;
2702 }
2703
2704 NS_IMETHODIMP
2705 nsDocShell::GetLoadedTransIndex(int32_t *aLoadedTransIndex)
2706 {
2707 *aLoadedTransIndex = mLoadedTransIndex;
2708 return NS_OK;
2709 }
2710
2711 NS_IMETHODIMP
2712 nsDocShell::HistoryPurged(int32_t aNumEntries)
2713 {
2714 // These indices are used for fastback cache eviction, to determine
2715 // which session history entries are candidates for content viewer
2716 // eviction. We need to adjust by the number of entries that we
2717 // just purged from history, so that we look at the right session history
2718 // entries during eviction.
2719 mPreviousTransIndex = std::max(-1, mPreviousTransIndex - aNumEntries);
2720 mLoadedTransIndex = std::max(0, mLoadedTransIndex - aNumEntries);
2721
2722 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
2723 while (iter.HasMore()) {
2724 nsCOMPtr<nsIDocShell> shell = do_QueryObject(iter.GetNext());
2725 if (shell) {
2726 shell->HistoryPurged(aNumEntries);
2727 }
2728 }
2729
2730 return NS_OK;
2731 }
2732
2733 nsresult
2734 nsDocShell::HistoryTransactionRemoved(int32_t aIndex)
2735 {
2736 // These indices are used for fastback cache eviction, to determine
2737 // which session history entries are candidates for content viewer
2738 // eviction. We need to adjust by the number of entries that we
2739 // just purged from history, so that we look at the right session history
2740 // entries during eviction.
2741 if (aIndex == mPreviousTransIndex) {
2742 mPreviousTransIndex = -1;
2743 } else if (aIndex < mPreviousTransIndex) {
2744 --mPreviousTransIndex;
2745 }
2746 if (mLoadedTransIndex == aIndex) {
2747 mLoadedTransIndex = 0;
2748 } else if (aIndex < mLoadedTransIndex) {
2749 --mLoadedTransIndex;
2750 }
2751
2752 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
2753 while (iter.HasMore()) {
2754 nsCOMPtr<nsIDocShell> shell = do_QueryObject(iter.GetNext());
2755 if (shell) {
2756 static_cast<nsDocShell*>(shell.get())->
2757 HistoryTransactionRemoved(aIndex);
2758 }
2759 }
2760
2761 return NS_OK;
2762 }
2763
2764 nsIDOMStorageManager*
2765 nsDocShell::TopSessionStorageManager()
2766 {
2767 nsresult rv;
2768
2769 nsCOMPtr<nsIDocShellTreeItem> topItem;
2770 rv = GetSameTypeRootTreeItem(getter_AddRefs(topItem));
2771 if (NS_FAILED(rv)) {
2772 return nullptr;
2773 }
2774
2775 if (!topItem) {
2776 return nullptr;
2777 }
2778
2779 nsDocShell* topDocShell = static_cast<nsDocShell*>(topItem.get());
2780 if (topDocShell != this) {
2781 return topDocShell->TopSessionStorageManager();
2782 }
2783
2784 if (!mSessionStorageManager) {
2785 mSessionStorageManager =
2786 do_CreateInstance("@mozilla.org/dom/sessionStorage-manager;1");
2787 }
2788
2789 return mSessionStorageManager;
2790 }
2791
2792 NS_IMETHODIMP
2793 nsDocShell::GetSessionStorageForPrincipal(nsIPrincipal* aPrincipal,
2794 const nsAString& aDocumentURI,
2795 bool aCreate,
2796 nsIDOMStorage** aStorage)
2797 {
2798 nsCOMPtr<nsIDOMStorageManager> manager = TopSessionStorageManager();
2799 if (!manager) {
2800 return NS_ERROR_UNEXPECTED;
2801 }
2802
2803 nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
2804 do_GetService(THIRDPARTYUTIL_CONTRACTID);
2805 if (!thirdPartyUtil)
2806 return NS_ERROR_FAILURE;
2807
2808 nsCOMPtr<nsIDocument> doc(do_GetInterface(GetAsSupports(this)));
2809 nsCOMPtr<nsIURI> firstPartyIsolationURI;
2810 nsresult rv = thirdPartyUtil->GetFirstPartyIsolationURI(nullptr, doc,
2811 getter_AddRefs(firstPartyIsolationURI));
2812 NS_ENSURE_SUCCESS(rv, rv);
2813
2814 if (aCreate) {
2815 return manager->CreateStorageForFirstParty(firstPartyIsolationURI,
2816 aPrincipal, aDocumentURI,
2817 mInPrivateBrowsing, aStorage);
2818 }
2819
2820 return manager->GetStorageForFirstParty(firstPartyIsolationURI, aPrincipal,
2821 mInPrivateBrowsing, aStorage);
2822 }
2823
2824 // Bacause it is not called from anywhere, nsDocShell::AddSessionStorage()
2825 // does not need to be modified to isolate DOM Storage to the first party URI.
2826 nsresult
2827 nsDocShell::AddSessionStorage(nsIPrincipal* aPrincipal,
2828 nsIDOMStorage* aStorage)
2829 {
2830 nsCOMPtr<nsPIDOMStorage> pistorage = do_QueryInterface(aStorage);
2831 nsIPrincipal* storagePrincipal = pistorage->GetPrincipal();
2832 if (storagePrincipal != aPrincipal) {
2833 NS_ERROR("Wanting to add a sessionStorage for different principal");
2834 return NS_ERROR_DOM_SECURITY_ERR;
2835 }
2836
2837 nsCOMPtr<nsIDOMStorageManager> manager = TopSessionStorageManager();
2838 if (!manager) {
2839 return NS_ERROR_UNEXPECTED;
2840 }
2841
2842 return manager->CloneStorage(aStorage);
2843 }
2844
2845 NS_IMETHODIMP
2846 nsDocShell::GetCurrentDocumentChannel(nsIChannel** aResult)
2847 {
2848 NS_IF_ADDREF(*aResult = GetCurrentDocChannel());
2849 return NS_OK;
2850 }
2851
2852 nsIChannel*
2853 nsDocShell::GetCurrentDocChannel()
2854 {
2855 if (mContentViewer) {
2856 nsIDocument* doc = mContentViewer->GetDocument();
2857 if (doc) {
2858 return doc->GetChannel();
2859 }
2860 }
2861 return nullptr;
2862 }
2863
2864 NS_IMETHODIMP
2865 nsDocShell::AddWeakScrollObserver(nsIScrollObserver* aObserver)
2866 {
2867 nsWeakPtr weakObs = do_GetWeakReference(aObserver);
2868 if (!weakObs) {
2869 return NS_ERROR_FAILURE;
2870 }
2871 return mScrollObservers.AppendElement(weakObs) ? NS_OK : NS_ERROR_FAILURE;
2872 }
2873
2874 NS_IMETHODIMP
2875 nsDocShell::RemoveWeakScrollObserver(nsIScrollObserver* aObserver)
2876 {
2877 nsWeakPtr obs = do_GetWeakReference(aObserver);
2878 return mScrollObservers.RemoveElement(obs) ? NS_OK : NS_ERROR_FAILURE;
2879 }
2880
2881 NS_IMETHODIMP
2882 nsDocShell::NotifyScrollObservers()
2883 {
2884 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
2885 while (iter.HasMore()) {
2886 nsWeakPtr ref = iter.GetNext();
2887 nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
2888 if (obs) {
2889 obs->ScrollPositionChanged();
2890 } else {
2891 mScrollObservers.RemoveElement(ref);
2892 }
2893 }
2894 return NS_OK;
2895 }
2896
2897 //*****************************************************************************
2898 // nsDocShell::nsIDocShellTreeItem
2899 //*****************************************************************************
2900
2901 NS_IMETHODIMP
2902 nsDocShell::GetName(nsAString& aName)
2903 {
2904 aName = mName;
2905 return NS_OK;
2906 }
2907
2908 NS_IMETHODIMP
2909 nsDocShell::SetName(const nsAString& aName)
2910 {
2911 mName = aName;
2912 return NS_OK;
2913 }
2914
2915 NS_IMETHODIMP
2916 nsDocShell::NameEquals(const char16_t *aName, bool *_retval)
2917 {
2918 NS_ENSURE_ARG_POINTER(aName);
2919 NS_ENSURE_ARG_POINTER(_retval);
2920 *_retval = mName.Equals(aName);
2921 return NS_OK;
2922 }
2923
2924 /* virtual */ int32_t
2925 nsDocShell::ItemType()
2926 {
2927 return mItemType;
2928 }
2929
2930 NS_IMETHODIMP
2931 nsDocShell::GetItemType(int32_t * aItemType)
2932 {
2933 NS_ENSURE_ARG_POINTER(aItemType);
2934
2935 *aItemType = ItemType();
2936 return NS_OK;
2937 }
2938
2939 NS_IMETHODIMP
2940 nsDocShell::SetItemType(int32_t aItemType)
2941 {
2942 NS_ENSURE_ARG((aItemType == typeChrome) || (typeContent == aItemType));
2943
2944 // Only allow setting the type on root docshells. Those would be the ones
2945 // that have the docloader service as mParent or have no mParent at all.
2946 nsCOMPtr<nsIDocumentLoader> docLoaderService =
2947 do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID);
2948 NS_ENSURE_TRUE(docLoaderService, NS_ERROR_UNEXPECTED);
2949
2950 NS_ENSURE_STATE(!mParent || mParent == docLoaderService);
2951
2952 mItemType = aItemType;
2953
2954 // disable auth prompting for anything but content
2955 mAllowAuth = mItemType == typeContent;
2956
2957 nsRefPtr<nsPresContext> presContext = nullptr;
2958 GetPresContext(getter_AddRefs(presContext));
2959 if (presContext) {
2960 presContext->UpdateIsChrome();
2961 }
2962
2963 return NS_OK;
2964 }
2965
2966 NS_IMETHODIMP
2967 nsDocShell::GetParent(nsIDocShellTreeItem ** aParent)
2968 {
2969 if (!mParent) {
2970 *aParent = nullptr;
2971 } else {
2972 CallQueryInterface(mParent, aParent);
2973 }
2974 // Note that in the case when the parent is not an nsIDocShellTreeItem we
2975 // don't want to throw; we just want to return null.
2976 return NS_OK;
2977 }
2978
2979 already_AddRefed<nsDocShell>
2980 nsDocShell::GetParentDocshell()
2981 {
2982 nsCOMPtr<nsIDocShell> docshell = do_QueryInterface(GetAsSupports(mParent));
2983 return docshell.forget().downcast<nsDocShell>();
2984 }
2985
2986 void
2987 nsDocShell::RecomputeCanExecuteScripts()
2988 {
2989 bool old = mCanExecuteScripts;
2990 nsRefPtr<nsDocShell> parent = GetParentDocshell();
2991
2992 // If we have no tree owner, that means that we've been detached from the
2993 // docshell tree (this is distinct from having no parent dochshell, which
2994 // is the case for root docshells). It would be nice to simply disallow
2995 // script in detached docshells, but bug 986542 demonstrates that this
2996 // behavior breaks at least one website.
2997 //
2998 // So instead, we use our previous value, unless mAllowJavascript has been
2999 // explicitly set to false.
3000 if (!mTreeOwner) {
3001 mCanExecuteScripts = mCanExecuteScripts && mAllowJavascript;
3002 // If scripting has been explicitly disabled on our docshell, we're done.
3003 } else if (!mAllowJavascript) {
3004 mCanExecuteScripts = false;
3005 // If we have a parent, inherit.
3006 } else if (parent) {
3007 mCanExecuteScripts = parent->mCanExecuteScripts;
3008 // Otherwise, we're the root of the tree, and we haven't explicitly disabled
3009 // script. Allow.
3010 } else {
3011 mCanExecuteScripts = true;
3012 }
3013
3014 // Inform our active DOM window.
3015 //
3016 // This will pass the outer, which will be in the scope of the active inner.
3017 if (mScriptGlobal && mScriptGlobal->GetGlobalJSObject()) {
3018 xpc::Scriptability& scriptability =
3019 xpc::Scriptability::Get(mScriptGlobal->GetGlobalJSObject());
3020 scriptability.SetDocShellAllowsScript(mCanExecuteScripts);
3021 }
3022
3023 // If our value has changed, our children might be affected. Recompute their
3024 // value as well.
3025 if (old != mCanExecuteScripts) {
3026 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
3027 while (iter.HasMore()) {
3028 static_cast<nsDocShell*>(iter.GetNext())->RecomputeCanExecuteScripts();
3029 }
3030 }
3031 }
3032
3033 nsresult
3034 nsDocShell::SetDocLoaderParent(nsDocLoader * aParent)
3035 {
3036 bool wasFrame = IsFrame();
3037
3038 nsDocLoader::SetDocLoaderParent(aParent);
3039
3040 nsCOMPtr<nsISupportsPriority> priorityGroup = do_QueryInterface(mLoadGroup);
3041 if (wasFrame != IsFrame() && priorityGroup) {
3042 priorityGroup->AdjustPriority(wasFrame ? -1 : 1);
3043 }
3044
3045 // Curse ambiguous nsISupports inheritance!
3046 nsISupports* parent = GetAsSupports(aParent);
3047
3048 // If parent is another docshell, we inherit all their flags for
3049 // allowing plugins, scripting etc.
3050 bool value;
3051 nsCOMPtr<nsIDocShell> parentAsDocShell(do_QueryInterface(parent));
3052 if (parentAsDocShell)
3053 {
3054 if (NS_SUCCEEDED(parentAsDocShell->GetAllowPlugins(&value)))
3055 {
3056 SetAllowPlugins(value);
3057 }
3058 if (NS_SUCCEEDED(parentAsDocShell->GetAllowJavascript(&value)))
3059 {
3060 SetAllowJavascript(value);
3061 }
3062 if (NS_SUCCEEDED(parentAsDocShell->GetAllowMetaRedirects(&value)))
3063 {
3064 SetAllowMetaRedirects(value);
3065 }
3066 if (NS_SUCCEEDED(parentAsDocShell->GetAllowSubframes(&value)))
3067 {
3068 SetAllowSubframes(value);
3069 }
3070 if (NS_SUCCEEDED(parentAsDocShell->GetAllowImages(&value)))
3071 {
3072 SetAllowImages(value);
3073 }
3074 SetAllowMedia(parentAsDocShell->GetAllowMedia());
3075 if (NS_SUCCEEDED(parentAsDocShell->GetAllowWindowControl(&value)))
3076 {
3077 SetAllowWindowControl(value);
3078 }
3079 SetAllowContentRetargeting(
3080 parentAsDocShell->GetAllowContentRetargeting());
3081 if (NS_SUCCEEDED(parentAsDocShell->GetIsActive(&value)))
3082 {
3083 SetIsActive(value);
3084 }
3085 if (NS_FAILED(parentAsDocShell->GetAllowDNSPrefetch(&value))) {
3086 value = false;
3087 }
3088 SetAllowDNSPrefetch(value);
3089 value = parentAsDocShell->GetAffectPrivateSessionLifetime();
3090 SetAffectPrivateSessionLifetime(value);
3091 uint32_t flags;
3092 if (NS_SUCCEEDED(parentAsDocShell->GetDefaultLoadFlags(&flags)))
3093 {
3094 SetDefaultLoadFlags(flags);
3095 }
3096
3097 }
3098
3099 nsCOMPtr<nsILoadContext> parentAsLoadContext(do_QueryInterface(parent));
3100 if (parentAsLoadContext &&
3101 NS_SUCCEEDED(parentAsLoadContext->GetUsePrivateBrowsing(&value)))
3102 {
3103 SetPrivateBrowsing(value);
3104 }
3105
3106 nsCOMPtr<nsIURIContentListener> parentURIListener(do_GetInterface(parent));
3107 if (parentURIListener)
3108 mContentListener->SetParentContentListener(parentURIListener);
3109
3110 // Our parent has changed. Recompute scriptability.
3111 RecomputeCanExecuteScripts();
3112
3113 return NS_OK;
3114 }
3115
3116 NS_IMETHODIMP
3117 nsDocShell::GetSameTypeParent(nsIDocShellTreeItem ** aParent)
3118 {
3119 NS_ENSURE_ARG_POINTER(aParent);
3120 *aParent = nullptr;
3121
3122 if (nsIDocShell::GetIsBrowserOrApp()) {
3123 return NS_OK;
3124 }
3125
3126 nsCOMPtr<nsIDocShellTreeItem> parent =
3127 do_QueryInterface(GetAsSupports(mParent));
3128 if (!parent)
3129 return NS_OK;
3130
3131 if (parent->ItemType() == mItemType) {
3132 parent.swap(*aParent);
3133 }
3134 return NS_OK;
3135 }
3136
3137 NS_IMETHODIMP
3138 nsDocShell::GetSameTypeParentIgnoreBrowserAndAppBoundaries(nsIDocShell** aParent)
3139 {
3140 NS_ENSURE_ARG_POINTER(aParent);
3141 *aParent = nullptr;
3142
3143 nsCOMPtr<nsIDocShellTreeItem> parent =
3144 do_QueryInterface(GetAsSupports(mParent));
3145 if (!parent)
3146 return NS_OK;
3147
3148 if (parent->ItemType() == mItemType) {
3149 nsCOMPtr<nsIDocShell> parentDS = do_QueryInterface(parent);
3150 parentDS.forget(aParent);
3151 }
3152 return NS_OK;
3153 }
3154
3155 NS_IMETHODIMP
3156 nsDocShell::GetRootTreeItem(nsIDocShellTreeItem ** aRootTreeItem)
3157 {
3158 NS_ENSURE_ARG_POINTER(aRootTreeItem);
3159 *aRootTreeItem = static_cast<nsIDocShellTreeItem *>(this);
3160
3161 nsCOMPtr<nsIDocShellTreeItem> parent;
3162 NS_ENSURE_SUCCESS(GetParent(getter_AddRefs(parent)), NS_ERROR_FAILURE);
3163 while (parent) {
3164 *aRootTreeItem = parent;
3165 NS_ENSURE_SUCCESS((*aRootTreeItem)->GetParent(getter_AddRefs(parent)),
3166 NS_ERROR_FAILURE);
3167 }
3168 NS_ADDREF(*aRootTreeItem);
3169 return NS_OK;
3170 }
3171
3172 NS_IMETHODIMP
3173 nsDocShell::GetSameTypeRootTreeItem(nsIDocShellTreeItem ** aRootTreeItem)
3174 {
3175 NS_ENSURE_ARG_POINTER(aRootTreeItem);
3176 *aRootTreeItem = static_cast<nsIDocShellTreeItem *>(this);
3177
3178 nsCOMPtr<nsIDocShellTreeItem> parent;
3179 NS_ENSURE_SUCCESS(GetSameTypeParent(getter_AddRefs(parent)),
3180 NS_ERROR_FAILURE);
3181 while (parent) {
3182 *aRootTreeItem = parent;
3183 NS_ENSURE_SUCCESS((*aRootTreeItem)->
3184 GetSameTypeParent(getter_AddRefs(parent)),
3185 NS_ERROR_FAILURE);
3186 }
3187 NS_ADDREF(*aRootTreeItem);
3188 return NS_OK;
3189 }
3190
3191 /* static */
3192 bool
3193 nsDocShell::CanAccessItem(nsIDocShellTreeItem* aTargetItem,
3194 nsIDocShellTreeItem* aAccessingItem,
3195 bool aConsiderOpener)
3196 {
3197 NS_PRECONDITION(aTargetItem, "Must have target item!");
3198
3199 if (!gValidateOrigin || !aAccessingItem) {
3200 // Good to go
3201 return true;
3202 }
3203
3204 // XXXbz should we care if aAccessingItem or the document therein is
3205 // chrome? Should those get extra privileges?
3206
3207 // For historical context, see:
3208 //
3209 // Bug 13871: Prevent frameset spoofing
3210 // Bug 103638: Targets with same name in different windows open in wrong
3211 // window with javascript
3212 // Bug 408052: Adopt "ancestor" frame navigation policy
3213
3214 // Now do a security check.
3215 //
3216 // Disallow navigation if the two frames are not part of the same app, or if
3217 // they have different is-in-browser-element states.
3218 //
3219 // Allow navigation if
3220 // 1) aAccessingItem can script aTargetItem or one of its ancestors in
3221 // the frame hierarchy or
3222 // 2) aTargetItem is a top-level frame and aAccessingItem is its descendant
3223 // 3) aTargetItem is a top-level frame and aAccessingItem can target
3224 // its opener per rule (1) or (2).
3225
3226 if (aTargetItem == aAccessingItem) {
3227 // A frame is allowed to navigate itself.
3228 return true;
3229 }
3230
3231 nsCOMPtr<nsIDocShell> targetDS = do_QueryInterface(aTargetItem);
3232 nsCOMPtr<nsIDocShell> accessingDS = do_QueryInterface(aAccessingItem);
3233 if (!!targetDS != !!accessingDS) {
3234 // We must be able to convert both or neither to nsIDocShell.
3235 return false;
3236 }
3237
3238 if (targetDS && accessingDS &&
3239 (targetDS->GetIsInBrowserElement() !=
3240 accessingDS->GetIsInBrowserElement() ||
3241 targetDS->GetAppId() != accessingDS->GetAppId())) {
3242 return false;
3243 }
3244
3245 nsCOMPtr<nsIDocShellTreeItem> accessingRoot;
3246 aAccessingItem->GetSameTypeRootTreeItem(getter_AddRefs(accessingRoot));
3247
3248 if (aTargetItem == accessingRoot) {
3249 // A frame can navigate its root.
3250 return true;
3251 }
3252
3253 // Check if aAccessingItem can navigate one of aTargetItem's ancestors.
3254 nsCOMPtr<nsIDocShellTreeItem> target = aTargetItem;
3255 do {
3256 if (ValidateOrigin(aAccessingItem, target)) {
3257 return true;
3258 }
3259
3260 nsCOMPtr<nsIDocShellTreeItem> parent;
3261 target->GetSameTypeParent(getter_AddRefs(parent));
3262 parent.swap(target);
3263 } while (target);
3264
3265 nsCOMPtr<nsIDocShellTreeItem> targetRoot;
3266 aTargetItem->GetSameTypeRootTreeItem(getter_AddRefs(targetRoot));
3267
3268 if (aTargetItem != targetRoot) {
3269 // target is a subframe, not in accessor's frame hierarchy, and all its
3270 // ancestors have origins different from that of the accessor. Don't
3271 // allow access.
3272 return false;
3273 }
3274
3275 if (!aConsiderOpener) {
3276 // All done here
3277 return false;
3278 }
3279
3280 nsCOMPtr<nsIDOMWindow> targetWindow = do_GetInterface(aTargetItem);
3281 if (!targetWindow) {
3282 NS_ERROR("This should not happen, really");
3283 return false;
3284 }
3285
3286 nsCOMPtr<nsIDOMWindow> targetOpener;
3287 targetWindow->GetOpener(getter_AddRefs(targetOpener));
3288 nsCOMPtr<nsIWebNavigation> openerWebNav(do_GetInterface(targetOpener));
3289 nsCOMPtr<nsIDocShellTreeItem> openerItem(do_QueryInterface(openerWebNav));
3290
3291 if (!openerItem) {
3292 return false;
3293 }
3294
3295 return CanAccessItem(openerItem, aAccessingItem, false);
3296 }
3297
3298 static bool
3299 ItemIsActive(nsIDocShellTreeItem *aItem)
3300 {
3301 nsCOMPtr<nsIDOMWindow> window(do_GetInterface(aItem));
3302
3303 if (window) {
3304 bool isClosed;
3305
3306 if (NS_SUCCEEDED(window->GetClosed(&isClosed)) && !isClosed) {
3307 return true;
3308 }
3309 }
3310
3311 return false;
3312 }
3313
3314 NS_IMETHODIMP
3315 nsDocShell::FindItemWithName(const char16_t * aName,
3316 nsISupports * aRequestor,
3317 nsIDocShellTreeItem * aOriginalRequestor,
3318 nsIDocShellTreeItem ** _retval)
3319 {
3320 NS_ENSURE_ARG(aName);
3321 NS_ENSURE_ARG_POINTER(_retval);
3322
3323 // If we don't find one, we return NS_OK and a null result
3324 *_retval = nullptr;
3325
3326 if (!*aName)
3327 return NS_OK;
3328
3329 if (aRequestor) {
3330 // If aRequestor is not null we don't need to check special names, so
3331 // just hand straight off to the search by actual name function.
3332 return DoFindItemWithName(aName, aRequestor, aOriginalRequestor,
3333 _retval);
3334 } else {
3335
3336 // This is the entry point into the target-finding algorithm. Check
3337 // for special names. This should only be done once, hence the check
3338 // for a null aRequestor.
3339
3340 nsCOMPtr<nsIDocShellTreeItem> foundItem;
3341 nsDependentString name(aName);
3342 if (name.LowerCaseEqualsLiteral("_self")) {
3343 foundItem = this;
3344 }
3345 else if (name.LowerCaseEqualsLiteral("_blank"))
3346 {
3347 // Just return null. Caller must handle creating a new window with
3348 // a blank name himself.
3349 return NS_OK;
3350 }
3351 else if (name.LowerCaseEqualsLiteral("_parent"))
3352 {
3353 GetSameTypeParent(getter_AddRefs(foundItem));
3354 if(!foundItem)
3355 foundItem = this;
3356 }
3357 else if (name.LowerCaseEqualsLiteral("_top"))
3358 {
3359 GetSameTypeRootTreeItem(getter_AddRefs(foundItem));
3360 NS_ASSERTION(foundItem, "Must have this; worst case it's us!");
3361 }
3362 // _main is an IE target which should be case-insensitive but isn't
3363 // see bug 217886 for details
3364 else if (name.LowerCaseEqualsLiteral("_content") ||
3365 name.EqualsLiteral("_main"))
3366 {
3367 // Must pass our same type root as requestor to the
3368 // treeowner to make sure things work right.
3369 nsCOMPtr<nsIDocShellTreeItem> root;
3370 GetSameTypeRootTreeItem(getter_AddRefs(root));
3371 if (mTreeOwner) {
3372 NS_ASSERTION(root, "Must have this; worst case it's us!");
3373 mTreeOwner->FindItemWithName(aName, root, aOriginalRequestor,
3374 getter_AddRefs(foundItem));
3375 }
3376 #ifdef DEBUG
3377 else {
3378 NS_ERROR("Someone isn't setting up the tree owner. "
3379 "You might like to try that. "
3380 "Things will.....you know, work.");
3381 // Note: _content should always exist. If we don't have one
3382 // hanging off the treeowner, just create a named window....
3383 // so don't return here, in case we did that and can now find
3384 // it.
3385 // XXXbz should we be using |root| instead of creating
3386 // a new window?
3387 }
3388 #endif
3389 } else {
3390 // Do the search for item by an actual name.
3391 DoFindItemWithName(aName, aRequestor, aOriginalRequestor,
3392 getter_AddRefs(foundItem));
3393 }
3394
3395 if (foundItem && !CanAccessItem(foundItem, aOriginalRequestor)) {
3396 foundItem = nullptr;
3397 }
3398
3399 // DoFindItemWithName only returns active items and we don't check if
3400 // the item is active for the special cases.
3401 if (foundItem) {
3402 foundItem.swap(*_retval);
3403 }
3404 return NS_OK;
3405 }
3406 }
3407
3408 nsresult
3409 nsDocShell::DoFindItemWithName(const char16_t* aName,
3410 nsISupports* aRequestor,
3411 nsIDocShellTreeItem* aOriginalRequestor,
3412 nsIDocShellTreeItem** _retval)
3413 {
3414 // First we check our name.
3415 if (mName.Equals(aName) && ItemIsActive(this) &&
3416 CanAccessItem(this, aOriginalRequestor)) {
3417 NS_ADDREF(*_retval = this);
3418 return NS_OK;
3419 }
3420
3421 // This QI may fail, but the places where we want to compare, comparing
3422 // against nullptr serves the same purpose.
3423 nsCOMPtr<nsIDocShellTreeItem> reqAsTreeItem(do_QueryInterface(aRequestor));
3424
3425 // Second we check our children making sure not to ask a child if
3426 // it is the aRequestor.
3427 #ifdef DEBUG
3428 nsresult rv =
3429 #endif
3430 FindChildWithName(aName, true, true, reqAsTreeItem,
3431 aOriginalRequestor, _retval);
3432 NS_ASSERTION(NS_SUCCEEDED(rv),
3433 "FindChildWithName should not be failing here.");
3434 if (*_retval)
3435 return NS_OK;
3436
3437 // Third if we have a parent and it isn't the requestor then we
3438 // should ask it to do the search. If it is the requestor we
3439 // should just stop here and let the parent do the rest. If we
3440 // don't have a parent, then we should ask the
3441 // docShellTreeOwner to do the search.
3442 nsCOMPtr<nsIDocShellTreeItem> parentAsTreeItem =
3443 do_QueryInterface(GetAsSupports(mParent));
3444 if (parentAsTreeItem) {
3445 if (parentAsTreeItem == reqAsTreeItem)
3446 return NS_OK;
3447
3448 if (parentAsTreeItem->ItemType() == mItemType) {
3449 return parentAsTreeItem->
3450 FindItemWithName(aName,
3451 static_cast<nsIDocShellTreeItem*>
3452 (this),
3453 aOriginalRequestor,
3454 _retval);
3455 }
3456 }
3457
3458 // If the parent is null or not of the same type fall through and ask tree
3459 // owner.
3460
3461 // This may fail, but comparing against null serves the same purpose
3462 nsCOMPtr<nsIDocShellTreeOwner>
3463 reqAsTreeOwner(do_QueryInterface(aRequestor));
3464
3465 if (mTreeOwner && mTreeOwner != reqAsTreeOwner) {
3466 return mTreeOwner->
3467 FindItemWithName(aName, this, aOriginalRequestor, _retval);
3468 }
3469
3470 return NS_OK;
3471 }
3472
3473 bool
3474 nsDocShell::IsSandboxedFrom(nsIDocShell* aTargetDocShell)
3475 {
3476 // If no target then not sandboxed.
3477 if (!aTargetDocShell) {
3478 return false;
3479 }
3480
3481 // We cannot be sandboxed from ourselves.
3482 if (aTargetDocShell == this) {
3483 return false;
3484 }
3485
3486 // Default the sandbox flags to our flags, so that if we can't retrieve the
3487 // active document, we will still enforce our own.
3488 uint32_t sandboxFlags = mSandboxFlags;
3489 if (mContentViewer) {
3490 nsCOMPtr<nsIDocument> doc = mContentViewer->GetDocument();
3491 if (doc) {
3492 sandboxFlags = doc->GetSandboxFlags();
3493 }
3494 }
3495
3496 // If no flags, we are not sandboxed at all.
3497 if (!sandboxFlags) {
3498 return false;
3499 }
3500
3501 // If aTargetDocShell has an ancestor, it is not top level.
3502 nsCOMPtr<nsIDocShellTreeItem> ancestorOfTarget;
3503 aTargetDocShell->GetSameTypeParent(getter_AddRefs(ancestorOfTarget));
3504 if (ancestorOfTarget) {
3505 do {
3506 // We are not sandboxed if we are an ancestor of target.
3507 if (ancestorOfTarget == this) {
3508 return false;
3509 }
3510 nsCOMPtr<nsIDocShellTreeItem> tempTreeItem;
3511 ancestorOfTarget->GetSameTypeParent(getter_AddRefs(tempTreeItem));
3512 tempTreeItem.swap(ancestorOfTarget);
3513 } while (ancestorOfTarget);
3514
3515 // Otherwise, we are sandboxed from aTargetDocShell.
3516 return true;
3517 }
3518
3519 // aTargetDocShell is top level, are we the "one permitted sandboxed
3520 // navigator", i.e. did we open aTargetDocShell?
3521 nsCOMPtr<nsIDocShell> permittedNavigator;
3522 aTargetDocShell->
3523 GetOnePermittedSandboxedNavigator(getter_AddRefs(permittedNavigator));
3524 if (permittedNavigator == this) {
3525 return false;
3526 }
3527
3528 // If SANDBOXED_TOPLEVEL_NAVIGATION flag is not on, we are not sandboxed
3529 // from our top.
3530 if (!(sandboxFlags & SANDBOXED_TOPLEVEL_NAVIGATION)) {
3531 nsCOMPtr<nsIDocShellTreeItem> rootTreeItem;
3532 GetSameTypeRootTreeItem(getter_AddRefs(rootTreeItem));
3533 if (SameCOMIdentity(aTargetDocShell, rootTreeItem)) {
3534 return false;
3535 }
3536 }
3537
3538 // Otherwise, we are sandboxed from aTargetDocShell.
3539 return true;
3540 }
3541
3542 NS_IMETHODIMP
3543 nsDocShell::GetTreeOwner(nsIDocShellTreeOwner ** aTreeOwner)
3544 {
3545 NS_ENSURE_ARG_POINTER(aTreeOwner);
3546
3547 *aTreeOwner = mTreeOwner;
3548 NS_IF_ADDREF(*aTreeOwner);
3549 return NS_OK;
3550 }
3551
3552 #ifdef DEBUG_DOCSHELL_FOCUS
3553 static void
3554 PrintDocTree(nsIDocShellTreeItem * aParentNode, int aLevel)
3555 {
3556 for (int32_t i=0;i<aLevel;i++) printf(" ");
3557
3558 int32_t childWebshellCount;
3559 aParentNode->GetChildCount(&childWebshellCount);
3560 nsCOMPtr<nsIDocShell> parentAsDocShell(do_QueryInterface(aParentNode));
3561 int32_t type = aParentNode->ItemType();
3562 nsCOMPtr<nsIPresShell> presShell = parentAsDocShell->GetPresShell();
3563 nsRefPtr<nsPresContext> presContext;
3564 parentAsDocShell->GetPresContext(getter_AddRefs(presContext));
3565 nsIDocument *doc = presShell->GetDocument();
3566
3567 nsCOMPtr<nsIDOMWindow> domwin(doc->GetWindow());
3568
3569 nsCOMPtr<nsIWidget> widget;
3570 nsViewManager* vm = presShell->GetViewManager();
3571 if (vm) {
3572 vm->GetWidget(getter_AddRefs(widget));
3573 }
3574 dom::Element* rootElement = doc->GetRootElement();
3575
3576 printf("DS %p Ty %s Doc %p DW %p EM %p CN %p\n",
3577 (void*)parentAsDocShell.get(),
3578 type==nsIDocShellTreeItem::typeChrome?"Chr":"Con",
3579 (void*)doc, (void*)domwin.get(),
3580 (void*)presContext->EventStateManager(), (void*)rootElement);
3581
3582 if (childWebshellCount > 0) {
3583 for (int32_t i=0;i<childWebshellCount;i++) {
3584 nsCOMPtr<nsIDocShellTreeItem> child;
3585 aParentNode->GetChildAt(i, getter_AddRefs(child));
3586 PrintDocTree(child, aLevel+1);
3587 }
3588 }
3589 }
3590
3591 static void
3592 PrintDocTree(nsIDocShellTreeItem * aParentNode)
3593 {
3594 NS_ASSERTION(aParentNode, "Pointer is null!");
3595
3596 nsCOMPtr<nsIDocShellTreeItem> parentItem;
3597 aParentNode->GetParent(getter_AddRefs(parentItem));
3598 while (parentItem) {
3599 nsCOMPtr<nsIDocShellTreeItem>tmp;
3600 parentItem->GetParent(getter_AddRefs(tmp));
3601 if (!tmp) {
3602 break;
3603 }
3604 parentItem = tmp;
3605 }
3606
3607 if (!parentItem) {
3608 parentItem = aParentNode;
3609 }
3610
3611 PrintDocTree(parentItem, 0);
3612 }
3613 #endif
3614
3615 NS_IMETHODIMP
3616 nsDocShell::SetTreeOwner(nsIDocShellTreeOwner * aTreeOwner)
3617 {
3618 #ifdef DEBUG_DOCSHELL_FOCUS
3619 nsCOMPtr<nsIDocShellTreeItem> item(do_QueryInterface(aTreeOwner));
3620 if (item) {
3621 PrintDocTree(item);
3622 }
3623 #endif
3624
3625 // Don't automatically set the progress based on the tree owner for frames
3626 if (!IsFrame()) {
3627 nsCOMPtr<nsIWebProgress> webProgress =
3628 do_QueryInterface(GetAsSupports(this));
3629
3630 if (webProgress) {
3631 nsCOMPtr<nsIWebProgressListener>
3632 oldListener(do_QueryInterface(mTreeOwner));
3633 nsCOMPtr<nsIWebProgressListener>
3634 newListener(do_QueryInterface(aTreeOwner));
3635
3636 if (oldListener) {
3637 webProgress->RemoveProgressListener(oldListener);
3638 }
3639
3640 if (newListener) {
3641 webProgress->AddProgressListener(newListener,
3642 nsIWebProgress::NOTIFY_ALL);
3643 }
3644 }
3645 }
3646
3647 mTreeOwner = aTreeOwner; // Weak reference per API
3648
3649 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
3650 while (iter.HasMore()) {
3651 nsCOMPtr<nsIDocShellTreeItem> child = do_QueryObject(iter.GetNext());
3652 NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
3653
3654 if (child->ItemType() == mItemType)
3655 child->SetTreeOwner(aTreeOwner);
3656 }
3657
3658 // Our tree owner has changed. Recompute scriptability.
3659 //
3660 // Note that this is near-redundant with the recomputation in
3661 // SetDocLoaderParent(), but not so for the root DocShell, where the call to
3662 // SetTreeOwner() happens after the initial AddDocLoaderAsChildOfRoot(),
3663 // and we never set another parent. Given that this is neither expensive nor
3664 // performance-critical, let's be safe and unconditionally recompute this
3665 // state whenever dependent state changes.
3666 RecomputeCanExecuteScripts();
3667
3668 return NS_OK;
3669 }
3670
3671 NS_IMETHODIMP
3672 nsDocShell::SetChildOffset(uint32_t aChildOffset)
3673 {
3674 mChildOffset = aChildOffset;
3675 return NS_OK;
3676 }
3677
3678 NS_IMETHODIMP
3679 nsDocShell::GetHistoryID(uint64_t* aID)
3680 {
3681 *aID = mHistoryID;
3682 return NS_OK;
3683 }
3684
3685 NS_IMETHODIMP
3686 nsDocShell::GetIsInUnload(bool* aIsInUnload)
3687 {
3688 *aIsInUnload = mFiredUnloadEvent;
3689 return NS_OK;
3690 }
3691
3692 NS_IMETHODIMP
3693 nsDocShell::GetChildCount(int32_t * aChildCount)
3694 {
3695 NS_ENSURE_ARG_POINTER(aChildCount);
3696 *aChildCount = mChildList.Length();
3697 return NS_OK;
3698 }
3699
3700
3701
3702 NS_IMETHODIMP
3703 nsDocShell::AddChild(nsIDocShellTreeItem * aChild)
3704 {
3705 NS_ENSURE_ARG_POINTER(aChild);
3706
3707 nsRefPtr<nsDocLoader> childAsDocLoader = GetAsDocLoader(aChild);
3708 NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED);
3709
3710 // Make sure we're not creating a loop in the docshell tree
3711 nsDocLoader* ancestor = this;
3712 do {
3713 if (childAsDocLoader == ancestor) {
3714 return NS_ERROR_ILLEGAL_VALUE;
3715 }
3716 ancestor = ancestor->GetParent();
3717 } while (ancestor);
3718
3719 // Make sure to remove the child from its current parent.
3720 nsDocLoader* childsParent = childAsDocLoader->GetParent();
3721 if (childsParent) {
3722 childsParent->RemoveChildLoader(childAsDocLoader);
3723 }
3724
3725 // Make sure to clear the treeowner in case this child is a different type
3726 // from us.
3727 aChild->SetTreeOwner(nullptr);
3728
3729 nsresult res = AddChildLoader(childAsDocLoader);
3730 NS_ENSURE_SUCCESS(res, res);
3731 NS_ASSERTION(!mChildList.IsEmpty(),
3732 "child list must not be empty after a successful add");
3733
3734 nsCOMPtr<nsIDocShell> childDocShell = do_QueryInterface(aChild);
3735 bool dynamic = false;
3736 childDocShell->GetCreatedDynamically(&dynamic);
3737 if (!dynamic) {
3738 nsCOMPtr<nsISHEntry> currentSH;
3739 bool oshe = false;
3740 GetCurrentSHEntry(getter_AddRefs(currentSH), &oshe);
3741 if (currentSH) {
3742 currentSH->HasDynamicallyAddedChild(&dynamic);
3743 }
3744 }
3745 childDocShell->SetChildOffset(dynamic ? -1 : mChildList.Length() - 1);
3746
3747 /* Set the child's global history if the parent has one */
3748 if (mUseGlobalHistory) {
3749 childDocShell->SetUseGlobalHistory(true);
3750 }
3751
3752 if (aChild->ItemType() != mItemType) {
3753 return NS_OK;
3754 }
3755
3756 aChild->SetTreeOwner(mTreeOwner);
3757
3758 nsCOMPtr<nsIDocShell> childAsDocShell(do_QueryInterface(aChild));
3759 if (!childAsDocShell)
3760 return NS_OK;
3761
3762 // charset, style-disabling, and zoom will be inherited in SetupNewViewer()
3763
3764 // Now take this document's charset and set the child's parentCharset field
3765 // to it. We'll later use that field, in the loading process, for the
3766 // charset choosing algorithm.
3767 // If we fail, at any point, we just return NS_OK.
3768 // This code has some performance impact. But this will be reduced when
3769 // the current charset will finally be stored as an Atom, avoiding the
3770 // alias resolution extra look-up.
3771
3772 // we are NOT going to propagate the charset is this Chrome's docshell
3773 if (mItemType == nsIDocShellTreeItem::typeChrome)
3774 return NS_OK;
3775
3776 // get the parent's current charset
3777 if (!mContentViewer)
3778 return NS_OK;
3779 nsIDocument* doc = mContentViewer->GetDocument();
3780 if (!doc)
3781 return NS_OK;
3782
3783 bool isWyciwyg = false;
3784
3785 if (mCurrentURI) {
3786 // Check if the url is wyciwyg
3787 mCurrentURI->SchemeIs("wyciwyg", &isWyciwyg);
3788 }
3789
3790 if (!isWyciwyg) {
3791 // If this docshell is loaded from a wyciwyg: URI, don't
3792 // advertise our charset since it does not in any way reflect
3793 // the actual source charset, which is what we're trying to
3794 // expose here.
3795
3796 const nsACString &parentCS = doc->GetDocumentCharacterSet();
3797 int32_t charsetSource = doc->GetDocumentCharacterSetSource();
3798 // set the child's parentCharset
3799 childAsDocShell->SetParentCharset(parentCS,
3800 charsetSource,
3801 doc->NodePrincipal());
3802 }
3803
3804 // printf("### 1 >>> Adding child. Parent CS = %s. ItemType = %d.\n", NS_LossyConvertUTF16toASCII(parentCS).get(), mItemType);
3805
3806 return NS_OK;
3807 }
3808
3809 NS_IMETHODIMP
3810 nsDocShell::RemoveChild(nsIDocShellTreeItem * aChild)
3811 {
3812 NS_ENSURE_ARG_POINTER(aChild);
3813
3814 nsRefPtr<nsDocLoader> childAsDocLoader = GetAsDocLoader(aChild);
3815 NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED);
3816
3817 nsresult rv = RemoveChildLoader(childAsDocLoader);
3818 NS_ENSURE_SUCCESS(rv, rv);
3819
3820 aChild->SetTreeOwner(nullptr);
3821
3822 return nsDocLoader::AddDocLoaderAsChildOfRoot(childAsDocLoader);
3823 }
3824
3825 NS_IMETHODIMP
3826 nsDocShell::GetChildAt(int32_t aIndex, nsIDocShellTreeItem ** aChild)
3827 {
3828 NS_ENSURE_ARG_POINTER(aChild);
3829
3830 #ifdef DEBUG
3831 if (aIndex < 0) {
3832 NS_WARNING("Negative index passed to GetChildAt");
3833 } else if (static_cast<uint32_t>(aIndex) >= mChildList.Length()) {
3834 NS_WARNING("Too large an index passed to GetChildAt");
3835 }
3836 #endif
3837
3838 nsIDocumentLoader* child = ChildAt(aIndex);
3839 NS_ENSURE_TRUE(child, NS_ERROR_UNEXPECTED);
3840
3841 return CallQueryInterface(child, aChild);
3842 }
3843
3844 NS_IMETHODIMP
3845 nsDocShell::FindChildWithName(const char16_t * aName,
3846 bool aRecurse, bool aSameType,
3847 nsIDocShellTreeItem * aRequestor,
3848 nsIDocShellTreeItem * aOriginalRequestor,
3849 nsIDocShellTreeItem ** _retval)
3850 {
3851 NS_ENSURE_ARG(aName);
3852 NS_ENSURE_ARG_POINTER(_retval);
3853
3854 *_retval = nullptr; // if we don't find one, we return NS_OK and a null result
3855
3856 if (!*aName)
3857 return NS_OK;
3858
3859 nsXPIDLString childName;
3860 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
3861 while (iter.HasMore()) {
3862 nsCOMPtr<nsIDocShellTreeItem> child = do_QueryObject(iter.GetNext());
3863 NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
3864 int32_t childType = child->ItemType();
3865
3866 if (aSameType && (childType != mItemType))
3867 continue;
3868
3869 bool childNameEquals = false;
3870 child->NameEquals(aName, &childNameEquals);
3871 if (childNameEquals && ItemIsActive(child) &&
3872 CanAccessItem(child, aOriginalRequestor)) {
3873 child.swap(*_retval);
3874 break;
3875 }
3876
3877 if (childType != mItemType) //Only ask it to check children if it is same type
3878 continue;
3879
3880 if (aRecurse && (aRequestor != child)) // Only ask the child if it isn't the requestor
3881 {
3882 // See if child contains the shell with the given name
3883 #ifdef DEBUG
3884 nsresult rv =
3885 #endif
3886 child->FindChildWithName(aName, true,
3887 aSameType,
3888 static_cast<nsIDocShellTreeItem*>
3889 (this),
3890 aOriginalRequestor,
3891 _retval);
3892 NS_ASSERTION(NS_SUCCEEDED(rv),
3893 "FindChildWithName should not fail here");
3894 if (*_retval) // found it
3895 return NS_OK;
3896 }
3897 }
3898 return NS_OK;
3899 }
3900
3901 NS_IMETHODIMP
3902 nsDocShell::GetChildSHEntry(int32_t aChildOffset, nsISHEntry ** aResult)
3903 {
3904 nsresult rv = NS_OK;
3905
3906 NS_ENSURE_ARG_POINTER(aResult);
3907 *aResult = nullptr;
3908
3909
3910 // A nsISHEntry for a child is *only* available when the parent is in
3911 // the progress of loading a document too...
3912
3913 if (mLSHE) {
3914 /* Before looking for the subframe's url, check
3915 * the expiration status of the parent. If the parent
3916 * has expired from cache, then subframes will not be
3917 * loaded from history in certain situations.
3918 */
3919 bool parentExpired=false;
3920 mLSHE->GetExpirationStatus(&parentExpired);
3921
3922 /* Get the parent's Load Type so that it can be set on the child too.
3923 * By default give a loadHistory value
3924 */
3925 uint32_t loadType = nsIDocShellLoadInfo::loadHistory;
3926 mLSHE->GetLoadType(&loadType);
3927 // If the user did a shift-reload on this frameset page,
3928 // we don't want to load the subframes from history.
3929 if (loadType == nsIDocShellLoadInfo::loadReloadBypassCache ||
3930 loadType == nsIDocShellLoadInfo::loadReloadBypassProxy ||
3931 loadType == nsIDocShellLoadInfo::loadReloadBypassProxyAndCache ||
3932 loadType == nsIDocShellLoadInfo::loadRefresh)
3933 return rv;
3934
3935 /* If the user pressed reload and the parent frame has expired
3936 * from cache, we do not want to load the child frame from history.
3937 */
3938 if (parentExpired && (loadType == nsIDocShellLoadInfo::loadReloadNormal)) {
3939 // The parent has expired. Return null.
3940 *aResult = nullptr;
3941 return rv;
3942 }
3943
3944 nsCOMPtr<nsISHContainer> container(do_QueryInterface(mLSHE));
3945 if (container) {
3946 // Get the child subframe from session history.
3947 rv = container->GetChildAt(aChildOffset, aResult);
3948 if (*aResult)
3949 (*aResult)->SetLoadType(loadType);
3950 }
3951 }
3952 return rv;
3953 }
3954
3955 NS_IMETHODIMP
3956 nsDocShell::AddChildSHEntry(nsISHEntry * aCloneRef, nsISHEntry * aNewEntry,
3957 int32_t aChildOffset, uint32_t loadType,
3958 bool aCloneChildren)
3959 {
3960 nsresult rv;
3961
3962 if (mLSHE && loadType != LOAD_PUSHSTATE) {
3963 /* You get here if you are currently building a
3964 * hierarchy ie.,you just visited a frameset page
3965 */
3966 nsCOMPtr<nsISHContainer> container(do_QueryInterface(mLSHE, &rv));
3967 if (container) {
3968 rv = container->AddChild(aNewEntry, aChildOffset);
3969 }
3970 }
3971 else if (!aCloneRef) {
3972 /* This is an initial load in some subframe. Just append it if we can */
3973 nsCOMPtr<nsISHContainer> container(do_QueryInterface(mOSHE, &rv));
3974 if (container) {
3975 rv = container->AddChild(aNewEntry, aChildOffset);
3976 }
3977 }
3978 else if (mSessionHistory) {
3979 /* You are currently in the rootDocShell.
3980 * You will get here when a subframe has a new url
3981 * to load and you have walked up the tree all the
3982 * way to the top to clone the current SHEntry hierarchy
3983 * and replace the subframe where a new url was loaded with
3984 * a new entry.
3985 */
3986 int32_t index = -1;
3987 nsCOMPtr<nsISHEntry> currentHE;
3988 mSessionHistory->GetIndex(&index);
3989 if (index < 0)
3990 return NS_ERROR_FAILURE;
3991
3992 rv = mSessionHistory->GetEntryAtIndex(index, false,
3993 getter_AddRefs(currentHE));
3994 NS_ENSURE_TRUE(currentHE, NS_ERROR_FAILURE);
3995
3996 nsCOMPtr<nsISHEntry> currentEntry(do_QueryInterface(currentHE));
3997 if (currentEntry) {
3998 uint32_t cloneID = 0;
3999 nsCOMPtr<nsISHEntry> nextEntry;
4000 aCloneRef->GetID(&cloneID);
4001 rv = CloneAndReplace(currentEntry, this, cloneID, aNewEntry,
4002 aCloneChildren, getter_AddRefs(nextEntry));
4003
4004 if (NS_SUCCEEDED(rv)) {
4005 nsCOMPtr<nsISHistoryInternal>
4006 shPrivate(do_QueryInterface(mSessionHistory));
4007 NS_ENSURE_TRUE(shPrivate, NS_ERROR_FAILURE);
4008 rv = shPrivate->AddEntry(nextEntry, true);
4009 }
4010 }
4011 }
4012 else {
4013 /* Just pass this along */
4014 nsCOMPtr<nsIDocShell> parent =
4015 do_QueryInterface(GetAsSupports(mParent), &rv);
4016 if (parent) {
4017 rv = parent->AddChildSHEntry(aCloneRef, aNewEntry, aChildOffset,
4018 loadType, aCloneChildren);
4019 }
4020 }
4021 return rv;
4022 }
4023
4024 nsresult
4025 nsDocShell::DoAddChildSHEntry(nsISHEntry* aNewEntry, int32_t aChildOffset,
4026 bool aCloneChildren)
4027 {
4028 /* You will get here when you are in a subframe and
4029 * a new url has been loaded on you.
4030 * The mOSHE in this subframe will be the previous url's
4031 * mOSHE. This mOSHE will be used as the identification
4032 * for this subframe in the CloneAndReplace function.
4033 */
4034
4035 // In this case, we will end up calling AddEntry, which increases the
4036 // current index by 1
4037 nsCOMPtr<nsISHistory> rootSH;
4038 GetRootSessionHistory(getter_AddRefs(rootSH));
4039 if (rootSH) {
4040 rootSH->GetIndex(&mPreviousTransIndex);
4041 }
4042
4043 nsresult rv;
4044 nsCOMPtr<nsIDocShell> parent =
4045 do_QueryInterface(GetAsSupports(mParent), &rv);
4046 if (parent) {
4047 rv = parent->AddChildSHEntry(mOSHE, aNewEntry, aChildOffset, mLoadType,
4048 aCloneChildren);
4049 }
4050
4051
4052 if (rootSH) {
4053 rootSH->GetIndex(&mLoadedTransIndex);
4054 #ifdef DEBUG_PAGE_CACHE
4055 printf("Previous index: %d, Loaded index: %d\n\n", mPreviousTransIndex,
4056 mLoadedTransIndex);
4057 #endif
4058 }
4059
4060 return rv;
4061 }
4062
4063 NS_IMETHODIMP
4064 nsDocShell::SetUseGlobalHistory(bool aUseGlobalHistory)
4065 {
4066 nsresult rv;
4067
4068 mUseGlobalHistory = aUseGlobalHistory;
4069
4070 if (!aUseGlobalHistory) {
4071 mGlobalHistory = nullptr;
4072 return NS_OK;
4073 }
4074
4075 // No need to initialize mGlobalHistory if IHistory is available.
4076 nsCOMPtr<IHistory> history = services::GetHistoryService();
4077 if (history) {
4078 return NS_OK;
4079 }
4080
4081 if (mGlobalHistory) {
4082 return NS_OK;
4083 }
4084
4085 mGlobalHistory = do_GetService(NS_GLOBALHISTORY2_CONTRACTID, &rv);
4086 return rv;
4087 }
4088
4089 NS_IMETHODIMP
4090 nsDocShell::GetUseGlobalHistory(bool *aUseGlobalHistory)
4091 {
4092 *aUseGlobalHistory = mUseGlobalHistory;
4093 return NS_OK;
4094 }
4095
4096 NS_IMETHODIMP
4097 nsDocShell::RemoveFromSessionHistory()
4098 {
4099 nsCOMPtr<nsISHistoryInternal> internalHistory;
4100 nsCOMPtr<nsISHistory> sessionHistory;
4101 nsCOMPtr<nsIDocShellTreeItem> root;
4102 GetSameTypeRootTreeItem(getter_AddRefs(root));
4103 if (root) {
4104 nsCOMPtr<nsIWebNavigation> rootAsWebnav =
4105 do_QueryInterface(root);
4106 if (rootAsWebnav) {
4107 rootAsWebnav->GetSessionHistory(getter_AddRefs(sessionHistory));
4108 internalHistory = do_QueryInterface(sessionHistory);
4109 }
4110 }
4111 if (!internalHistory) {
4112 return NS_OK;
4113 }
4114
4115 int32_t index = 0;
4116 sessionHistory->GetIndex(&index);
4117 nsAutoTArray<uint64_t, 16> ids;
4118 ids.AppendElement(mHistoryID);
4119 internalHistory->RemoveEntries(ids, index);
4120 return NS_OK;
4121 }
4122
4123 NS_IMETHODIMP
4124 nsDocShell::SetCreatedDynamically(bool aDynamic)
4125 {
4126 mDynamicallyCreated = aDynamic;
4127 return NS_OK;
4128 }
4129
4130 NS_IMETHODIMP
4131 nsDocShell::GetCreatedDynamically(bool* aDynamic)
4132 {
4133 *aDynamic = mDynamicallyCreated;
4134 return NS_OK;
4135 }
4136
4137 NS_IMETHODIMP
4138 nsDocShell::GetCurrentSHEntry(nsISHEntry** aEntry, bool* aOSHE)
4139 {
4140 *aOSHE = false;
4141 *aEntry = nullptr;
4142 if (mLSHE) {
4143 NS_ADDREF(*aEntry = mLSHE);
4144 } else if (mOSHE) {
4145 NS_ADDREF(*aEntry = mOSHE);
4146 *aOSHE = true;
4147 }
4148 return NS_OK;
4149 }
4150
4151 nsIScriptGlobalObject*
4152 nsDocShell::GetScriptGlobalObject()
4153 {
4154 NS_ENSURE_SUCCESS(EnsureScriptEnvironment(), nullptr);
4155 return mScriptGlobal;
4156 }
4157
4158 NS_IMETHODIMP
4159 nsDocShell::SetDeviceSizeIsPageSize(bool aValue)
4160 {
4161 if (mDeviceSizeIsPageSize != aValue) {
4162 mDeviceSizeIsPageSize = aValue;
4163 nsRefPtr<nsPresContext> presContext;
4164 GetPresContext(getter_AddRefs(presContext));
4165 if (presContext) {
4166 presContext->MediaFeatureValuesChanged(presContext->eAlwaysRebuildStyle);
4167 }
4168 }
4169 return NS_OK;
4170 }
4171
4172 NS_IMETHODIMP
4173 nsDocShell::GetDeviceSizeIsPageSize(bool* aValue)
4174 {
4175 *aValue = mDeviceSizeIsPageSize;
4176 return NS_OK;
4177 }
4178
4179 void
4180 nsDocShell::ClearFrameHistory(nsISHEntry* aEntry)
4181 {
4182 nsCOMPtr<nsISHContainer> shcontainer = do_QueryInterface(aEntry);
4183 nsCOMPtr<nsISHistory> rootSH;
4184 GetRootSessionHistory(getter_AddRefs(rootSH));
4185 nsCOMPtr<nsISHistoryInternal> history = do_QueryInterface(rootSH);
4186 if (!history || !shcontainer) {
4187 return;
4188 }
4189
4190 int32_t count = 0;
4191 shcontainer->GetChildCount(&count);
4192 nsAutoTArray<uint64_t, 16> ids;
4193 for (int32_t i = 0; i < count; ++i) {
4194 nsCOMPtr<nsISHEntry> child;
4195 shcontainer->GetChildAt(i, getter_AddRefs(child));
4196 if (child) {
4197 uint64_t id = 0;
4198 child->GetDocshellID(&id);
4199 ids.AppendElement(id);
4200 }
4201 }
4202 int32_t index = 0;
4203 rootSH->GetIndex(&index);
4204 history->RemoveEntries(ids, index);
4205 }
4206
4207 //-------------------------------------
4208 //-- Helper Method for Print discovery
4209 //-------------------------------------
4210 bool
4211 nsDocShell::IsPrintingOrPP(bool aDisplayErrorDialog)
4212 {
4213 if (mIsPrintingOrPP && aDisplayErrorDialog) {
4214 DisplayLoadError(NS_ERROR_DOCUMENT_IS_PRINTMODE, nullptr, nullptr, nullptr);
4215 }
4216
4217 return mIsPrintingOrPP;
4218 }
4219
4220 bool
4221 nsDocShell::IsNavigationAllowed(bool aDisplayPrintErrorDialog)
4222 {
4223 bool isAllowed = !IsPrintingOrPP(aDisplayPrintErrorDialog) && !mFiredUnloadEvent;
4224 if (!isAllowed) {
4225 return false;
4226 }
4227 if (!mContentViewer) {
4228 return true;
4229 }
4230 bool firingBeforeUnload;
4231 mContentViewer->GetBeforeUnloadFiring(&firingBeforeUnload);
4232 return !firingBeforeUnload;
4233 }
4234
4235 //*****************************************************************************
4236 // nsDocShell::nsIWebNavigation
4237 //*****************************************************************************
4238
4239 NS_IMETHODIMP
4240 nsDocShell::GetCanGoBack(bool * aCanGoBack)
4241 {
4242 if (!IsNavigationAllowed(false)) {
4243 *aCanGoBack = false;
4244 return NS_OK; // JS may not handle returning of an error code
4245 }
4246 nsresult rv;
4247 nsCOMPtr<nsISHistory> rootSH;
4248 rv = GetRootSessionHistory(getter_AddRefs(rootSH));
4249 nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
4250 NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
4251 rv = webnav->GetCanGoBack(aCanGoBack);
4252 return rv;
4253
4254 }
4255
4256 NS_IMETHODIMP
4257 nsDocShell::GetCanGoForward(bool * aCanGoForward)
4258 {
4259 if (!IsNavigationAllowed(false)) {
4260 *aCanGoForward = false;
4261 return NS_OK; // JS may not handle returning of an error code
4262 }
4263 nsresult rv;
4264 nsCOMPtr<nsISHistory> rootSH;
4265 rv = GetRootSessionHistory(getter_AddRefs(rootSH));
4266 nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
4267 NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
4268 rv = webnav->GetCanGoForward(aCanGoForward);
4269 return rv;
4270
4271 }
4272
4273 NS_IMETHODIMP
4274 nsDocShell::GoBack()
4275 {
4276 if (!IsNavigationAllowed()) {
4277 return NS_OK; // JS may not handle returning of an error code
4278 }
4279 nsresult rv;
4280 nsCOMPtr<nsISHistory> rootSH;
4281 rv = GetRootSessionHistory(getter_AddRefs(rootSH));
4282 nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
4283 NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
4284 rv = webnav->GoBack();
4285 return rv;
4286
4287 }
4288
4289 NS_IMETHODIMP
4290 nsDocShell::GoForward()
4291 {
4292 if (!IsNavigationAllowed()) {
4293 return NS_OK; // JS may not handle returning of an error code
4294 }
4295 nsresult rv;
4296 nsCOMPtr<nsISHistory> rootSH;
4297 rv = GetRootSessionHistory(getter_AddRefs(rootSH));
4298 nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
4299 NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
4300 rv = webnav->GoForward();
4301 return rv;
4302
4303 }
4304
4305 NS_IMETHODIMP nsDocShell::GotoIndex(int32_t aIndex)
4306 {
4307 if (!IsNavigationAllowed()) {
4308 return NS_OK; // JS may not handle returning of an error code
4309 }
4310 nsresult rv;
4311 nsCOMPtr<nsISHistory> rootSH;
4312 rv = GetRootSessionHistory(getter_AddRefs(rootSH));
4313 nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
4314 NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
4315 rv = webnav->GotoIndex(aIndex);
4316 return rv;
4317
4318 }
4319
4320 NS_IMETHODIMP
4321 nsDocShell::LoadURI(const char16_t * aURI,
4322 uint32_t aLoadFlags,
4323 nsIURI * aReferringURI,
4324 nsIInputStream * aPostStream,
4325 nsIInputStream * aHeaderStream)
4326 {
4327 return LoadURIWithBase(aURI, aLoadFlags, aReferringURI, aPostStream,
4328 aHeaderStream, nullptr);
4329 }
4330
4331 NS_IMETHODIMP
4332 nsDocShell::LoadURIWithBase(const char16_t * aURI,
4333 uint32_t aLoadFlags,
4334 nsIURI * aReferringURI,
4335 nsIInputStream * aPostStream,
4336 nsIInputStream * aHeaderStream,
4337 nsIURI * aBaseURI)
4338 {
4339 NS_ASSERTION((aLoadFlags & 0xf) == 0, "Unexpected flags");
4340
4341 if (!IsNavigationAllowed()) {
4342 return NS_OK; // JS may not handle returning of an error code
4343 }
4344 nsCOMPtr<nsIURI> uri;
4345 nsCOMPtr<nsIInputStream> postStream(aPostStream);
4346 nsresult rv = NS_OK;
4347
4348 // Create a URI from our string; if that succeeds, we want to
4349 // change aLoadFlags to not include the ALLOW_THIRD_PARTY_FIXUP
4350 // flag.
4351
4352 NS_ConvertUTF16toUTF8 uriString(aURI);
4353 // Cleanup the empty spaces that might be on each end.
4354 uriString.Trim(" ");
4355 // Eliminate embedded newlines, which single-line text fields now allow:
4356 uriString.StripChars("\r\n");
4357 NS_ENSURE_TRUE(!uriString.IsEmpty(), NS_ERROR_FAILURE);
4358
4359 rv = NS_NewURI(getter_AddRefs(uri), uriString);
4360 if (uri) {
4361 aLoadFlags &= ~LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
4362 }
4363
4364 if (sURIFixup) {
4365 // Call the fixup object. This will clobber the rv from NS_NewURI
4366 // above, but that's fine with us. Note that we need to do this even
4367 // if NS_NewURI returned a URI, because fixup handles nested URIs, etc
4368 // (things like view-source:mozilla.org for example).
4369 uint32_t fixupFlags = 0;
4370 if (aLoadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) {
4371 fixupFlags |= nsIURIFixup::FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP;
4372 }
4373 if (aLoadFlags & LOAD_FLAGS_FIXUP_SCHEME_TYPOS) {
4374 fixupFlags |= nsIURIFixup::FIXUP_FLAG_FIX_SCHEME_TYPOS;
4375 }
4376 nsCOMPtr<nsIInputStream> fixupStream;
4377 rv = sURIFixup->CreateFixupURI(uriString, fixupFlags,
4378 getter_AddRefs(fixupStream),
4379 getter_AddRefs(uri));
4380 if (fixupStream) {
4381 // CreateFixupURI only returns a post data stream if it succeeded
4382 // and changed the URI, in which case we should override the
4383 // passed-in post data.
4384 postStream = fixupStream;
4385 }
4386 }
4387 // else no fixup service so just use the URI we created and see
4388 // what happens
4389
4390 if (NS_ERROR_MALFORMED_URI == rv) {
4391 DisplayLoadError(rv, uri, aURI, nullptr);
4392 }
4393
4394 if (NS_FAILED(rv) || !uri)
4395 return NS_ERROR_FAILURE;
4396
4397 PopupControlState popupState;
4398 if (aLoadFlags & LOAD_FLAGS_ALLOW_POPUPS) {
4399 popupState = openAllowed;
4400 aLoadFlags &= ~LOAD_FLAGS_ALLOW_POPUPS;
4401 } else {
4402 popupState = openOverridden;
4403 }
4404 nsAutoPopupStatePusher statePusher(popupState);
4405
4406 // Don't pass certain flags that aren't needed and end up confusing
4407 // ConvertLoadTypeToDocShellLoadInfo. We do need to ensure that they are
4408 // passed to LoadURI though, since it uses them.
4409 uint32_t extraFlags = (aLoadFlags & EXTRA_LOAD_FLAGS);
4410 aLoadFlags &= ~EXTRA_LOAD_FLAGS;
4411
4412 nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
4413 rv = CreateLoadInfo(getter_AddRefs(loadInfo));
4414 if (NS_FAILED(rv)) return rv;
4415
4416 /*
4417 * If the user "Disables Protection on This Page", we have to make sure to
4418 * remember the users decision when opening links in child tabs [Bug 906190]
4419 */
4420 uint32_t loadType;
4421 if (aLoadFlags & LOAD_FLAGS_ALLOW_MIXED_CONTENT) {
4422 loadType = MAKE_LOAD_TYPE(LOAD_NORMAL_ALLOW_MIXED_CONTENT, aLoadFlags);
4423 } else {
4424 loadType = MAKE_LOAD_TYPE(LOAD_NORMAL, aLoadFlags);
4425 }
4426
4427 loadInfo->SetLoadType(ConvertLoadTypeToDocShellLoadInfo(loadType));
4428 loadInfo->SetPostDataStream(postStream);
4429 loadInfo->SetReferrer(aReferringURI);
4430 loadInfo->SetHeadersStream(aHeaderStream);
4431 loadInfo->SetBaseURI(aBaseURI);
4432
4433 rv = LoadURI(uri, loadInfo, extraFlags, true);
4434
4435 // Save URI string in case it's needed later when
4436 // sending to search engine service in EndPageLoad()
4437 mOriginalUriString = uriString;
4438
4439 return rv;
4440 }
4441
4442 NS_IMETHODIMP
4443 nsDocShell::DisplayLoadError(nsresult aError, nsIURI *aURI,
4444 const char16_t *aURL,
4445 nsIChannel* aFailedChannel)
4446 {
4447 // Get prompt and string bundle servcies
4448 nsCOMPtr<nsIPrompt> prompter;
4449 nsCOMPtr<nsIStringBundle> stringBundle;
4450 GetPromptAndStringBundle(getter_AddRefs(prompter),
4451 getter_AddRefs(stringBundle));
4452
4453 NS_ENSURE_TRUE(stringBundle, NS_ERROR_FAILURE);
4454 NS_ENSURE_TRUE(prompter, NS_ERROR_FAILURE);
4455
4456 nsAutoString error;
4457 const uint32_t kMaxFormatStrArgs = 3;
4458 nsAutoString formatStrs[kMaxFormatStrArgs];
4459 uint32_t formatStrCount = 0;
4460 bool addHostPort = false;
4461 nsresult rv = NS_OK;
4462 nsAutoString messageStr;
4463 nsAutoCString cssClass;
4464 nsAutoCString errorPage;
4465
4466 errorPage.AssignLiteral("neterror");
4467
4468 // Turn the error code into a human readable error message.
4469 if (NS_ERROR_UNKNOWN_PROTOCOL == aError) {
4470 NS_ENSURE_ARG_POINTER(aURI);
4471
4472 // Extract the schemes into a comma delimited list.
4473 nsAutoCString scheme;
4474 aURI->GetScheme(scheme);
4475 CopyASCIItoUTF16(scheme, formatStrs[0]);
4476 nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(aURI);
4477 while (nestedURI) {
4478 nsCOMPtr<nsIURI> tempURI;
4479 nsresult rv2;
4480 rv2 = nestedURI->GetInnerURI(getter_AddRefs(tempURI));
4481 if (NS_SUCCEEDED(rv2) && tempURI) {
4482 tempURI->GetScheme(scheme);
4483 formatStrs[0].Append(NS_LITERAL_STRING(", "));
4484 AppendASCIItoUTF16(scheme, formatStrs[0]);
4485 }
4486 nestedURI = do_QueryInterface(tempURI);
4487 }
4488 formatStrCount = 1;
4489 error.AssignLiteral("unknownProtocolFound");
4490 }
4491 else if (NS_ERROR_FILE_NOT_FOUND == aError) {
4492 NS_ENSURE_ARG_POINTER(aURI);
4493 error.AssignLiteral("fileNotFound");
4494 }
4495 else if (NS_ERROR_UNKNOWN_HOST == aError) {
4496 NS_ENSURE_ARG_POINTER(aURI);
4497 // Get the host
4498 nsAutoCString host;
4499 nsCOMPtr<nsIURI> innermostURI = NS_GetInnermostURI(aURI);
4500 innermostURI->GetHost(host);
4501 CopyUTF8toUTF16(host, formatStrs[0]);
4502 formatStrCount = 1;
4503 error.AssignLiteral("dnsNotFound");
4504 }
4505 else if(NS_ERROR_CONNECTION_REFUSED == aError) {
4506 NS_ENSURE_ARG_POINTER(aURI);
4507 addHostPort = true;
4508 error.AssignLiteral("connectionFailure");
4509 }
4510 else if(NS_ERROR_NET_INTERRUPT == aError) {
4511 NS_ENSURE_ARG_POINTER(aURI);
4512 addHostPort = true;
4513 error.AssignLiteral("netInterrupt");
4514 }
4515 else if (NS_ERROR_NET_TIMEOUT == aError) {
4516 NS_ENSURE_ARG_POINTER(aURI);
4517 // Get the host
4518 nsAutoCString host;
4519 aURI->GetHost(host);
4520 CopyUTF8toUTF16(host, formatStrs[0]);
4521 formatStrCount = 1;
4522 error.AssignLiteral("netTimeout");
4523 }
4524 else if (NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION == aError) {
4525 // CSP error
4526 cssClass.AssignLiteral("neterror");
4527 error.AssignLiteral("cspFrameAncestorBlocked");
4528 }
4529 else if (NS_ERROR_GET_MODULE(aError) == NS_ERROR_MODULE_SECURITY) {
4530 nsCOMPtr<nsINSSErrorsService> nsserr =
4531 do_GetService(NS_NSS_ERRORS_SERVICE_CONTRACTID);
4532
4533 uint32_t errorClass;
4534 if (!nsserr ||
4535 NS_FAILED(nsserr->GetErrorClass(aError, &errorClass))) {
4536 errorClass = nsINSSErrorsService::ERROR_CLASS_SSL_PROTOCOL;
4537 }
4538
4539 nsCOMPtr<nsISupports> securityInfo;
4540 nsCOMPtr<nsITransportSecurityInfo> tsi;
4541 if (aFailedChannel)
4542 aFailedChannel->GetSecurityInfo(getter_AddRefs(securityInfo));
4543 tsi = do_QueryInterface(securityInfo);
4544 if (tsi) {
4545 // Usually we should have aFailedChannel and get a detailed message
4546 tsi->GetErrorMessage(getter_Copies(messageStr));
4547 }
4548 else {
4549 // No channel, let's obtain the generic error message
4550 if (nsserr) {
4551 nsserr->GetErrorMessage(aError, messageStr);
4552 }
4553 }
4554 if (!messageStr.IsEmpty()) {
4555 if (errorClass == nsINSSErrorsService::ERROR_CLASS_BAD_CERT) {
4556 error.AssignLiteral("nssBadCert");
4557
4558 // if this is a Strict-Transport-Security host and the cert
4559 // is bad, don't allow overrides (STS Spec section 7.3).
4560 nsCOMPtr<nsISiteSecurityService> sss =
4561 do_GetService(NS_SSSERVICE_CONTRACTID, &rv);
4562 NS_ENSURE_SUCCESS(rv, rv);
4563 uint32_t flags =
4564 mInPrivateBrowsing ? nsISocketProvider::NO_PERMANENT_STORAGE : 0;
4565
4566 bool isStsHost = false;
4567 rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS,
4568 aURI, flags, &isStsHost);
4569 NS_ENSURE_SUCCESS(rv, rv);
4570
4571 uint32_t bucketId;
4572 if (isStsHost) {
4573 cssClass.AssignLiteral("badStsCert");
4574 //measuring STS separately allows us to measure click through
4575 //rates easily
4576 bucketId = nsISecurityUITelemetry::WARNING_BAD_CERT_TOP_STS;
4577 } else {
4578 bucketId = nsISecurityUITelemetry::WARNING_BAD_CERT_TOP;
4579 }
4580
4581
4582 if (Preferences::GetBool(
4583 "browser.xul.error_pages.expert_bad_cert", false)) {
4584 cssClass.AssignLiteral("expertBadCert");
4585 }
4586
4587 // See if an alternate cert error page is registered
4588 nsAdoptingCString alternateErrorPage =
4589 Preferences::GetCString(
4590 "security.alternate_certificate_error_page");
4591 if (alternateErrorPage)
4592 errorPage.Assign(alternateErrorPage);
4593
4594 if (!IsFrame() && errorPage.EqualsIgnoreCase("certerror"))
4595 mozilla::Telemetry::Accumulate(mozilla::Telemetry::SECURITY_UI, bucketId);
4596
4597 } else {
4598 error.AssignLiteral("nssFailure2");
4599 }
4600 }
4601 } else if (NS_ERROR_PHISHING_URI == aError || NS_ERROR_MALWARE_URI == aError) {
4602 nsAutoCString host;
4603 aURI->GetHost(host);
4604 CopyUTF8toUTF16(host, formatStrs[0]);
4605 formatStrCount = 1;
4606
4607 // Malware and phishing detectors may want to use an alternate error
4608 // page, but if the pref's not set, we'll fall back on the standard page
4609 nsAdoptingCString alternateErrorPage =
4610 Preferences::GetCString("urlclassifier.alternate_error_page");
4611 if (alternateErrorPage)
4612 errorPage.Assign(alternateErrorPage);
4613
4614 uint32_t bucketId;
4615 if (NS_ERROR_PHISHING_URI == aError) {
4616 error.AssignLiteral("phishingBlocked");
4617 bucketId = IsFrame() ? nsISecurityUITelemetry::WARNING_PHISHING_PAGE_FRAME :
4618 nsISecurityUITelemetry::WARNING_PHISHING_PAGE_TOP ;
4619 } else {
4620 error.AssignLiteral("malwareBlocked");
4621 bucketId = IsFrame() ? nsISecurityUITelemetry::WARNING_MALWARE_PAGE_FRAME :
4622 nsISecurityUITelemetry::WARNING_MALWARE_PAGE_TOP ;
4623 }
4624
4625 if (errorPage.EqualsIgnoreCase("blocked"))
4626 mozilla::Telemetry::Accumulate(mozilla::Telemetry::SECURITY_UI,
4627 bucketId);
4628
4629 cssClass.AssignLiteral("blacklist");
4630 } else if (NS_ERROR_CONTENT_CRASHED == aError) {
4631 errorPage.AssignLiteral("tabcrashed");
4632 error.AssignLiteral("tabcrashed");
4633
4634 nsCOMPtr<EventTarget> handler = mChromeEventHandler;
4635 if (handler) {
4636 nsCOMPtr<Element> element = do_QueryInterface(handler);
4637 element->GetAttribute(NS_LITERAL_STRING("crashedPageTitle"), messageStr);
4638 }
4639
4640 // DisplayLoadError requires a non-empty messageStr to proceed and call LoadErrorPage.
4641 // If the page doesn't have a title, we will use a blank space which will be trimmed
4642 // and thus treated as empty by the front-end.
4643 if (messageStr.IsEmpty()) {
4644 messageStr.Assign(NS_LITERAL_STRING(" "));
4645 }
4646 }
4647 else {
4648 // Errors requiring simple formatting
4649 switch (aError) {
4650 case NS_ERROR_MALFORMED_URI:
4651 // URI is malformed
4652 error.AssignLiteral("malformedURI");
4653 break;
4654 case NS_ERROR_REDIRECT_LOOP:
4655 // Doc failed to load because the server generated too many redirects
4656 error.AssignLiteral("redirectLoop");
4657 break;
4658 case NS_ERROR_UNKNOWN_SOCKET_TYPE:
4659 // Doc failed to load because PSM is not installed
4660 error.AssignLiteral("unknownSocketType");
4661 break;
4662 case NS_ERROR_NET_RESET:
4663 // Doc failed to load because the server kept reseting the connection
4664 // before we could read any data from it
4665 error.AssignLiteral("netReset");
4666 break;
4667 case NS_ERROR_DOCUMENT_NOT_CACHED:
4668 // Doc failed to load because the cache does not contain a copy of
4669 // the document.
4670 error.AssignLiteral("notCached");
4671 break;
4672 case NS_ERROR_OFFLINE:
4673 // Doc failed to load because we are offline.
4674 error.AssignLiteral("netOffline");
4675 break;
4676 case NS_ERROR_DOCUMENT_IS_PRINTMODE:
4677 // Doc navigation attempted while Printing or Print Preview
4678 error.AssignLiteral("isprinting");
4679 break;
4680 case NS_ERROR_PORT_ACCESS_NOT_ALLOWED:
4681 // Port blocked for security reasons
4682 addHostPort = true;
4683 error.AssignLiteral("deniedPortAccess");
4684 break;
4685 case NS_ERROR_UNKNOWN_PROXY_HOST:
4686 // Proxy hostname could not be resolved.
4687 error.AssignLiteral("proxyResolveFailure");
4688 break;
4689 case NS_ERROR_PROXY_CONNECTION_REFUSED:
4690 // Proxy connection was refused.
4691 error.AssignLiteral("proxyConnectFailure");
4692 break;
4693 case NS_ERROR_INVALID_CONTENT_ENCODING:
4694 // Bad Content Encoding.
4695 error.AssignLiteral("contentEncodingError");
4696 break;
4697 case NS_ERROR_REMOTE_XUL:
4698 {
4699 error.AssignLiteral("remoteXUL");
4700 break;
4701 }
4702 case NS_ERROR_UNSAFE_CONTENT_TYPE:
4703 // Channel refused to load from an unrecognized content type.
4704 error.AssignLiteral("unsafeContentType");
4705 break;
4706 case NS_ERROR_CORRUPTED_CONTENT:
4707 // Broken Content Detected. e.g. Content-MD5 check failure.
4708 error.AssignLiteral("corruptedContentError");
4709 break;
4710 default:
4711 break;
4712 }
4713 }
4714
4715 // Test if the error should be displayed
4716 if (error.IsEmpty()) {
4717 return NS_OK;
4718 }
4719
4720 // Test if the error needs to be formatted
4721 if (!messageStr.IsEmpty()) {
4722 // already obtained message
4723 }
4724 else {
4725 if (addHostPort) {
4726 // Build up the host:port string.
4727 nsAutoCString hostport;
4728 if (aURI) {
4729 aURI->GetHostPort(hostport);
4730 } else {
4731 hostport.AssignLiteral("?");
4732 }
4733 CopyUTF8toUTF16(hostport, formatStrs[formatStrCount++]);
4734 }
4735
4736 nsAutoCString spec;
4737 rv = NS_ERROR_NOT_AVAILABLE;
4738 if (aURI) {
4739 // displaying "file://" is aesthetically unpleasing and could even be
4740 // confusing to the user
4741 bool isFileURI = false;
4742 rv = aURI->SchemeIs("file", &isFileURI);
4743 if (NS_SUCCEEDED(rv) && isFileURI)
4744 aURI->GetPath(spec);
4745 else
4746 aURI->GetSpec(spec);
4747
4748 nsAutoCString charset;
4749 // unescape and convert from origin charset
4750 aURI->GetOriginCharset(charset);
4751 nsCOMPtr<nsITextToSubURI> textToSubURI(
4752 do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv));
4753 if (NS_SUCCEEDED(rv)) {
4754 rv = textToSubURI->UnEscapeURIForUI(charset, spec, formatStrs[formatStrCount]);
4755 }
4756 } else {
4757 spec.AssignLiteral("?");
4758 }
4759 if (NS_FAILED(rv))
4760 CopyUTF8toUTF16(spec, formatStrs[formatStrCount]);
4761 rv = NS_OK;
4762 ++formatStrCount;
4763
4764 const char16_t *strs[kMaxFormatStrArgs];
4765 for (uint32_t i = 0; i < formatStrCount; i++) {
4766 strs[i] = formatStrs[i].get();
4767 }
4768 nsXPIDLString str;
4769 rv = stringBundle->FormatStringFromName(
4770 error.get(),
4771 strs, formatStrCount, getter_Copies(str));
4772 NS_ENSURE_SUCCESS(rv, rv);
4773 messageStr.Assign(str.get());
4774 }
4775
4776 // Display the error as a page or an alert prompt
4777 NS_ENSURE_FALSE(messageStr.IsEmpty(), NS_ERROR_FAILURE);
4778
4779 if (UseErrorPages()) {
4780 // Display an error page
4781 LoadErrorPage(aURI, aURL, errorPage.get(), error.get(),
4782 messageStr.get(), cssClass.get(), aFailedChannel);
4783 }
4784 else
4785 {
4786 // The prompter reqires that our private window has a document (or it
4787 // asserts). Satisfy that assertion now since GetDoc will force
4788 // creation of one if it hasn't already been created.
4789 if (mScriptGlobal) {
4790 unused << mScriptGlobal->GetDoc();
4791 }
4792
4793 // Display a message box
4794 prompter->Alert(nullptr, messageStr.get());
4795 }
4796
4797 return NS_OK;
4798 }
4799
4800
4801 NS_IMETHODIMP
4802 nsDocShell::LoadErrorPage(nsIURI *aURI, const char16_t *aURL,
4803 const char *aErrorPage,
4804 const char16_t *aErrorType,
4805 const char16_t *aDescription,
4806 const char *aCSSClass,
4807 nsIChannel* aFailedChannel)
4808 {
4809 #if defined(PR_LOGGING) && defined(DEBUG)
4810 if (PR_LOG_TEST(gDocShellLog, PR_LOG_DEBUG)) {
4811 nsAutoCString spec;
4812 aURI->GetSpec(spec);
4813
4814 nsAutoCString chanName;
4815 if (aFailedChannel)
4816 aFailedChannel->GetName(chanName);
4817 else
4818 chanName.AssignLiteral("<no channel>");
4819
4820 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
4821 ("nsDocShell[%p]::LoadErrorPage(\"%s\", \"%s\", {...}, [%s])\n", this,
4822 spec.get(), NS_ConvertUTF16toUTF8(aURL).get(), chanName.get()));
4823 }
4824 #endif
4825 mFailedChannel = aFailedChannel;
4826 mFailedURI = aURI;
4827 mFailedLoadType = mLoadType;
4828
4829 if (mLSHE) {
4830 // Abandon mLSHE's BFCache entry and create a new one. This way, if
4831 // we go back or forward to another SHEntry with the same doc
4832 // identifier, the error page won't persist.
4833 mLSHE->AbandonBFCacheEntry();
4834 }
4835
4836 nsAutoCString url;
4837 nsAutoCString charset;
4838 if (aURI)
4839 {
4840 nsresult rv = aURI->GetSpec(url);
4841 NS_ENSURE_SUCCESS(rv, rv);
4842 rv = aURI->GetOriginCharset(charset);
4843 NS_ENSURE_SUCCESS(rv, rv);
4844 }
4845 else if (aURL)
4846 {
4847 CopyUTF16toUTF8(aURL, url);
4848 }
4849 else
4850 {
4851 return NS_ERROR_INVALID_POINTER;
4852 }
4853
4854 // Create a URL to pass all the error information through to the page.
4855
4856 #undef SAFE_ESCAPE
4857 #define SAFE_ESCAPE(cstring, escArg1, escArg2) \
4858 { \
4859 char* s = nsEscape(escArg1, escArg2); \
4860 if (!s) \
4861 return NS_ERROR_OUT_OF_MEMORY; \
4862 cstring.Adopt(s); \
4863 }
4864 nsCString escapedUrl, escapedCharset, escapedError, escapedDescription,
4865 escapedCSSClass;
4866 SAFE_ESCAPE(escapedUrl, url.get(), url_Path);
4867 SAFE_ESCAPE(escapedCharset, charset.get(), url_Path);
4868 SAFE_ESCAPE(escapedError,
4869 NS_ConvertUTF16toUTF8(aErrorType).get(), url_Path);
4870 SAFE_ESCAPE(escapedDescription,
4871 NS_ConvertUTF16toUTF8(aDescription).get(), url_Path);
4872 if (aCSSClass) {
4873 SAFE_ESCAPE(escapedCSSClass, aCSSClass, url_Path);
4874 }
4875 nsCString errorPageUrl("about:");
4876 errorPageUrl.AppendASCII(aErrorPage);
4877 errorPageUrl.AppendLiteral("?e=");
4878
4879 errorPageUrl.AppendASCII(escapedError.get());
4880 errorPageUrl.AppendLiteral("&u=");
4881 errorPageUrl.AppendASCII(escapedUrl.get());
4882 if (!escapedCSSClass.IsEmpty()) {
4883 errorPageUrl.AppendLiteral("&s=");
4884 errorPageUrl.AppendASCII(escapedCSSClass.get());
4885 }
4886 errorPageUrl.AppendLiteral("&c=");
4887 errorPageUrl.AppendASCII(escapedCharset.get());
4888
4889 nsAutoCString frameType(FrameTypeToString(mFrameType));
4890 errorPageUrl.AppendLiteral("&f=");
4891 errorPageUrl.AppendASCII(frameType.get());
4892
4893 // Append the manifest URL if the error comes from an app.
4894 nsString manifestURL;
4895 nsresult rv = GetAppManifestURL(manifestURL);
4896 if (manifestURL.Length() > 0) {
4897 nsCString manifestParam;
4898 SAFE_ESCAPE(manifestParam,
4899 NS_ConvertUTF16toUTF8(manifestURL).get(),
4900 url_Path);
4901 errorPageUrl.AppendLiteral("&m=");
4902 errorPageUrl.AppendASCII(manifestParam.get());
4903 }
4904
4905 // netError.xhtml's getDescription only handles the "d" parameter at the
4906 // end of the URL, so append it last.
4907 errorPageUrl.AppendLiteral("&d=");
4908 errorPageUrl.AppendASCII(escapedDescription.get());
4909
4910 nsCOMPtr<nsIURI> errorPageURI;
4911 rv = NS_NewURI(getter_AddRefs(errorPageURI), errorPageUrl);
4912 NS_ENSURE_SUCCESS(rv, rv);
4913
4914 return InternalLoad(errorPageURI, nullptr, nullptr,
4915 INTERNAL_LOAD_FLAGS_INHERIT_OWNER, nullptr, nullptr,
4916 NullString(), nullptr, nullptr, LOAD_ERROR_PAGE,
4917 nullptr, true, NullString(), this, nullptr, nullptr,
4918 nullptr);
4919 }
4920
4921
4922 NS_IMETHODIMP
4923 nsDocShell::Reload(uint32_t aReloadFlags)
4924 {
4925 if (!IsNavigationAllowed()) {
4926 return NS_OK; // JS may not handle returning of an error code
4927 }
4928 nsresult rv;
4929 NS_ASSERTION(((aReloadFlags & 0xf) == 0),
4930 "Reload command not updated to use load flags!");
4931 NS_ASSERTION((aReloadFlags & EXTRA_LOAD_FLAGS) == 0,
4932 "Don't pass these flags to Reload");
4933
4934 uint32_t loadType = MAKE_LOAD_TYPE(LOAD_RELOAD_NORMAL, aReloadFlags);
4935 NS_ENSURE_TRUE(IsValidLoadType(loadType), NS_ERROR_INVALID_ARG);
4936
4937 // Send notifications to the HistoryListener if any, about the impending reload
4938 nsCOMPtr<nsISHistory> rootSH;
4939 rv = GetRootSessionHistory(getter_AddRefs(rootSH));
4940 nsCOMPtr<nsISHistoryInternal> shistInt(do_QueryInterface(rootSH));
4941 bool canReload = true;
4942 if (rootSH) {
4943 shistInt->NotifyOnHistoryReload(mCurrentURI, aReloadFlags, &canReload);
4944 }
4945
4946 if (!canReload)
4947 return NS_OK;
4948
4949 /* If you change this part of code, make sure bug 45297 does not re-occur */
4950 if (mOSHE) {
4951 rv = LoadHistoryEntry(mOSHE, loadType);
4952 }
4953 else if (mLSHE) { // In case a reload happened before the current load is done
4954 rv = LoadHistoryEntry(mLSHE, loadType);
4955 }
4956 else {
4957 nsCOMPtr<nsIDocument> doc(do_GetInterface(GetAsSupports(this)));
4958
4959 // Do not inherit owner from document
4960 uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
4961 nsAutoString srcdoc;
4962 nsIPrincipal* principal = nullptr;
4963 nsAutoString contentTypeHint;
4964 nsCOMPtr<nsIURI> baseURI;
4965 if (doc) {
4966 principal = doc->NodePrincipal();
4967 doc->GetContentType(contentTypeHint);
4968
4969 if (doc->IsSrcdocDocument()) {
4970 doc->GetSrcdocData(srcdoc);
4971 flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC;
4972 baseURI = doc->GetBaseURI();
4973 }
4974 }
4975 rv = InternalLoad(mCurrentURI,
4976 mReferrerURI,
4977 principal,
4978 flags,
4979 nullptr, // No window target
4980 NS_LossyConvertUTF16toASCII(contentTypeHint).get(),
4981 NullString(), // No forced download
4982 nullptr, // No post data
4983 nullptr, // No headers data
4984 loadType, // Load type
4985 nullptr, // No SHEntry
4986 true,
4987 srcdoc, // srcdoc argument for iframe
4988 this, // For reloads we are the source
4989 baseURI,
4990 nullptr, // No nsIDocShell
4991 nullptr); // No nsIRequest
4992 }
4993
4994 return rv;
4995 }
4996
4997 NS_IMETHODIMP
4998 nsDocShell::Stop(uint32_t aStopFlags)
4999 {
5000 // Revoke any pending event related to content viewer restoration
5001 mRestorePresentationEvent.Revoke();
5002
5003 if (mLoadType == LOAD_ERROR_PAGE) {
5004 if (mLSHE) {
5005 // Since error page loads never unset mLSHE, do so now
5006 SetHistoryEntry(&mOSHE, mLSHE);
5007 SetHistoryEntry(&mLSHE, nullptr);
5008 }
5009
5010 mFailedChannel = nullptr;
5011 mFailedURI = nullptr;
5012 }
5013
5014 if (nsIWebNavigation::STOP_CONTENT & aStopFlags) {
5015 // Stop the document loading
5016 if (mContentViewer) {
5017 nsCOMPtr<nsIContentViewer> cv = mContentViewer;
5018 cv->Stop();
5019 }
5020 }
5021
5022 if (nsIWebNavigation::STOP_NETWORK & aStopFlags) {
5023 // Suspend any timers that were set for this loader. We'll clear
5024 // them out for good in CreateContentViewer.
5025 if (mRefreshURIList) {
5026 SuspendRefreshURIs();
5027 mSavedRefreshURIList.swap(mRefreshURIList);
5028 mRefreshURIList = nullptr;
5029 }
5030
5031 // XXXbz We could also pass |this| to nsIURILoader::Stop. That will
5032 // just call Stop() on us as an nsIDocumentLoader... We need fewer
5033 // redundant apis!
5034 Stop();
5035 }
5036
5037 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
5038 while (iter.HasMore()) {
5039 nsCOMPtr<nsIWebNavigation> shellAsNav(do_QueryObject(iter.GetNext()));
5040 if (shellAsNav)
5041 shellAsNav->Stop(aStopFlags);
5042 }
5043
5044 return NS_OK;
5045 }
5046
5047 NS_IMETHODIMP
5048 nsDocShell::GetDocument(nsIDOMDocument ** aDocument)
5049 {
5050 NS_ENSURE_ARG_POINTER(aDocument);
5051 NS_ENSURE_SUCCESS(EnsureContentViewer(), NS_ERROR_FAILURE);
5052
5053 return mContentViewer->GetDOMDocument(aDocument);
5054 }
5055
5056 NS_IMETHODIMP
5057 nsDocShell::GetCurrentURI(nsIURI ** aURI)
5058 {
5059 NS_ENSURE_ARG_POINTER(aURI);
5060
5061 if (mCurrentURI) {
5062 return NS_EnsureSafeToReturn(mCurrentURI, aURI);
5063 }
5064
5065 *aURI = nullptr;
5066 return NS_OK;
5067 }
5068
5069 NS_IMETHODIMP
5070 nsDocShell::GetReferringURI(nsIURI ** aURI)
5071 {
5072 NS_ENSURE_ARG_POINTER(aURI);
5073
5074 *aURI = mReferrerURI;
5075 NS_IF_ADDREF(*aURI);
5076
5077 return NS_OK;
5078 }
5079
5080 NS_IMETHODIMP
5081 nsDocShell::SetSessionHistory(nsISHistory * aSessionHistory)
5082 {
5083
5084 NS_ENSURE_TRUE(aSessionHistory, NS_ERROR_FAILURE);
5085 // make sure that we are the root docshell and
5086 // set a handle to root docshell in SH.
5087
5088 nsCOMPtr<nsIDocShellTreeItem> root;
5089 /* Get the root docshell. If *this* is the root docshell
5090 * then save a handle to *this* in SH. SH needs it to do
5091 * traversions thro' its entries
5092 */
5093 GetSameTypeRootTreeItem(getter_AddRefs(root));
5094 NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
5095 if (root.get() == static_cast<nsIDocShellTreeItem *>(this)) {
5096 mSessionHistory = aSessionHistory;
5097 nsCOMPtr<nsISHistoryInternal>
5098 shPrivate(do_QueryInterface(mSessionHistory));
5099 NS_ENSURE_TRUE(shPrivate, NS_ERROR_FAILURE);
5100 shPrivate->SetRootDocShell(this);
5101 return NS_OK;
5102 }
5103 return NS_ERROR_FAILURE;
5104
5105 }
5106
5107
5108 NS_IMETHODIMP
5109 nsDocShell::GetSessionHistory(nsISHistory ** aSessionHistory)
5110 {
5111 NS_ENSURE_ARG_POINTER(aSessionHistory);
5112 *aSessionHistory = mSessionHistory;
5113 NS_IF_ADDREF(*aSessionHistory);
5114 return NS_OK;
5115 }
5116
5117 //*****************************************************************************
5118 // nsDocShell::nsIWebPageDescriptor
5119 //*****************************************************************************
5120 NS_IMETHODIMP
5121 nsDocShell::LoadPage(nsISupports *aPageDescriptor, uint32_t aDisplayType)
5122 {
5123 nsCOMPtr<nsISHEntry> shEntryIn(do_QueryInterface(aPageDescriptor));
5124
5125 // Currently, the opaque 'page descriptor' is an nsISHEntry...
5126 if (!shEntryIn) {
5127 return NS_ERROR_INVALID_POINTER;
5128 }
5129
5130 // Now clone shEntryIn, since we might end up modifying it later on, and we
5131 // want a page descriptor to be reusable.
5132 nsCOMPtr<nsISHEntry> shEntry;
5133 nsresult rv = shEntryIn->Clone(getter_AddRefs(shEntry));
5134 NS_ENSURE_SUCCESS(rv, rv);
5135
5136 // Give our cloned shEntry a new bfcache entry so this load is independent
5137 // of all other loads. (This is important, in particular, for bugs 582795
5138 // and 585298.)
5139 rv = shEntry->AbandonBFCacheEntry();
5140 NS_ENSURE_SUCCESS(rv, rv);
5141
5142 //
5143 // load the page as view-source
5144 //
5145 if (nsIWebPageDescriptor::DISPLAY_AS_SOURCE == aDisplayType) {
5146 nsCOMPtr<nsIURI> oldUri, newUri;
5147 nsCString spec, newSpec;
5148
5149 // Create a new view-source URI and replace the original.
5150 rv = shEntry->GetURI(getter_AddRefs(oldUri));
5151 if (NS_FAILED(rv))
5152 return rv;
5153
5154 oldUri->GetSpec(spec);
5155 newSpec.AppendLiteral("view-source:");
5156 newSpec.Append(spec);
5157
5158 rv = NS_NewURI(getter_AddRefs(newUri), newSpec);
5159 if (NS_FAILED(rv)) {
5160 return rv;
5161 }
5162 shEntry->SetURI(newUri);
5163 }
5164
5165 rv = LoadHistoryEntry(shEntry, LOAD_HISTORY);
5166 return rv;
5167 }
5168
5169 NS_IMETHODIMP
5170 nsDocShell::GetCurrentDescriptor(nsISupports **aPageDescriptor)
5171 {
5172 NS_PRECONDITION(aPageDescriptor, "Null out param?");
5173
5174 *aPageDescriptor = nullptr;
5175
5176 nsISHEntry* src = mOSHE ? mOSHE : mLSHE;
5177 if (src) {
5178 nsCOMPtr<nsISHEntry> dest;
5179
5180 nsresult rv = src->Clone(getter_AddRefs(dest));
5181 if (NS_FAILED(rv)) {
5182 return rv;
5183 }
5184
5185 // null out inappropriate cloned attributes...
5186 dest->SetParent(nullptr);
5187 dest->SetIsSubFrame(false);
5188
5189 return CallQueryInterface(dest, aPageDescriptor);
5190 }
5191
5192 return NS_ERROR_NOT_AVAILABLE;
5193 }
5194
5195
5196 //*****************************************************************************
5197 // nsDocShell::nsIBaseWindow
5198 //*****************************************************************************
5199
5200 NS_IMETHODIMP
5201 nsDocShell::InitWindow(nativeWindow parentNativeWindow,
5202 nsIWidget * parentWidget, int32_t x, int32_t y,
5203 int32_t cx, int32_t cy)
5204 {
5205 SetParentWidget(parentWidget);
5206 SetPositionAndSize(x, y, cx, cy, false);
5207
5208 return NS_OK;
5209 }
5210
5211 NS_IMETHODIMP
5212 nsDocShell::Create()
5213 {
5214 if (mCreated) {
5215 // We've already been created
5216 return NS_OK;
5217 }
5218
5219 NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome,
5220 "Unexpected item type in docshell");
5221
5222 NS_ENSURE_TRUE(Preferences::GetRootBranch(), NS_ERROR_FAILURE);
5223 mCreated = true;
5224
5225 mAllowSubframes =
5226 Preferences::GetBool("browser.frames.enabled", mAllowSubframes);
5227
5228 if (gValidateOrigin == 0xffffffff) {
5229 // Check pref to see if we should prevent frameset spoofing
5230 gValidateOrigin =
5231 Preferences::GetBool("browser.frame.validate_origin", true);
5232 }
5233
5234 // Should we use XUL error pages instead of alerts if possible?
5235 mUseErrorPages =
5236 Preferences::GetBool("browser.xul.error_pages.enabled", mUseErrorPages);
5237
5238 if(!gAddedPreferencesVarCache) {
5239 Preferences::AddBoolVarCache(&sUseErrorPages,
5240 "browser.xul.error_pages.enabled",
5241 mUseErrorPages);
5242 gAddedPreferencesVarCache = true;
5243 }
5244
5245 mDeviceSizeIsPageSize =
5246 Preferences::GetBool("docshell.device_size_is_page_size",
5247 mDeviceSizeIsPageSize);
5248
5249 nsCOMPtr<nsIObserverService> serv = services::GetObserverService();
5250 if (serv) {
5251 const char* msg = mItemType == typeContent ?
5252 NS_WEBNAVIGATION_CREATE : NS_CHROME_WEBNAVIGATION_CREATE;
5253 serv->NotifyObservers(GetAsSupports(this), msg, nullptr);
5254 }
5255
5256 return NS_OK;
5257 }
5258
5259 NS_IMETHODIMP
5260 nsDocShell::Destroy()
5261 {
5262 NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome,
5263 "Unexpected item type in docshell");
5264
5265 if (!mIsBeingDestroyed) {
5266 nsCOMPtr<nsIObserverService> serv = services::GetObserverService();
5267 if (serv) {
5268 const char* msg = mItemType == typeContent ?
5269 NS_WEBNAVIGATION_DESTROY : NS_CHROME_WEBNAVIGATION_DESTROY;
5270 serv->NotifyObservers(GetAsSupports(this), msg, nullptr);
5271 }
5272 }
5273
5274 mIsBeingDestroyed = true;
5275
5276 // Remove our pref observers
5277 if (mObserveErrorPages) {
5278 mObserveErrorPages = false;
5279 }
5280
5281 // Make sure to blow away our mLoadingURI just in case. No loads
5282 // from inside this pagehide.
5283 mLoadingURI = nullptr;
5284
5285 // Fire unload event before we blow anything away.
5286 (void) FirePageHideNotification(true);
5287
5288 // Clear pointers to any detached nsEditorData that's lying
5289 // around in shistory entries. Breaks cycle. See bug 430921.
5290 if (mOSHE)
5291 mOSHE->SetEditorData(nullptr);
5292 if (mLSHE)
5293 mLSHE->SetEditorData(nullptr);
5294
5295 // Note: mContentListener can be null if Init() failed and we're being
5296 // called from the destructor.
5297 if (mContentListener) {
5298 mContentListener->DropDocShellreference();
5299 mContentListener->SetParentContentListener(nullptr);
5300 // Note that we do NOT set mContentListener to null here; that
5301 // way if someone tries to do a load in us after this point
5302 // the nsDSURIContentListener will block it. All of which
5303 // means that we should do this before calling Stop(), of
5304 // course.
5305 }
5306
5307 // Stop any URLs that are currently being loaded...
5308 Stop(nsIWebNavigation::STOP_ALL);
5309
5310 mEditorData = nullptr;
5311
5312 mTransferableHookData = nullptr;
5313
5314 // Save the state of the current document, before destroying the window.
5315 // This is needed to capture the state of a frameset when the new document
5316 // causes the frameset to be destroyed...
5317 PersistLayoutHistoryState();
5318
5319 // Remove this docshell from its parent's child list
5320 nsCOMPtr<nsIDocShellTreeItem> docShellParentAsItem =
5321 do_QueryInterface(GetAsSupports(mParent));
5322 if (docShellParentAsItem)
5323 docShellParentAsItem->RemoveChild(this);
5324
5325 if (mContentViewer) {
5326 mContentViewer->Close(nullptr);
5327 mContentViewer->Destroy();
5328 mContentViewer = nullptr;
5329 }
5330
5331 nsDocLoader::Destroy();
5332
5333 mParentWidget = nullptr;
5334 mCurrentURI = nullptr;
5335
5336 if (mScriptGlobal) {
5337 mScriptGlobal->DetachFromDocShell();
5338 mScriptGlobal = nullptr;
5339 }
5340
5341 if (mSessionHistory) {
5342 // We want to destroy these content viewers now rather than
5343 // letting their destruction wait for the session history
5344 // entries to get garbage collected. (Bug 488394)
5345 nsCOMPtr<nsISHistoryInternal> shPrivate =
5346 do_QueryInterface(mSessionHistory);
5347 if (shPrivate) {
5348 shPrivate->EvictAllContentViewers();
5349 }
5350 mSessionHistory = nullptr;
5351 }
5352
5353 SetTreeOwner(nullptr);
5354
5355 mOnePermittedSandboxedNavigator = nullptr;
5356
5357 // required to break ref cycle
5358 mSecurityUI = nullptr;
5359
5360 // Cancel any timers that were set for this docshell; this is needed
5361 // to break the cycle between us and the timers.
5362 CancelRefreshURITimers();
5363
5364 if (mInPrivateBrowsing) {
5365 mInPrivateBrowsing = false;
5366 if (mAffectPrivateSessionLifetime) {
5367 DecreasePrivateDocShellCount();
5368 }
5369 }
5370
5371 return NS_OK;
5372 }
5373
5374 NS_IMETHODIMP
5375 nsDocShell::GetUnscaledDevicePixelsPerCSSPixel(double *aScale)
5376 {
5377 if (mParentWidget) {
5378 *aScale = mParentWidget->GetDefaultScale().scale;
5379 return NS_OK;
5380 }
5381
5382 nsCOMPtr<nsIBaseWindow> ownerWindow(do_QueryInterface(mTreeOwner));
5383 if (ownerWindow) {
5384 return ownerWindow->GetUnscaledDevicePixelsPerCSSPixel(aScale);
5385 }
5386
5387 *aScale = 1.0;
5388 return NS_OK;
5389 }
5390
5391 NS_IMETHODIMP
5392 nsDocShell::SetPosition(int32_t x, int32_t y)
5393 {
5394 mBounds.x = x;
5395 mBounds.y = y;
5396
5397 if (mContentViewer)
5398 NS_ENSURE_SUCCESS(mContentViewer->Move(x, y), NS_ERROR_FAILURE);
5399
5400 return NS_OK;
5401 }
5402
5403 NS_IMETHODIMP
5404 nsDocShell::GetPosition(int32_t * aX, int32_t * aY)
5405 {
5406 int32_t dummyHolder;
5407 return GetPositionAndSize(aX, aY, &dummyHolder, &dummyHolder);
5408 }
5409
5410 NS_IMETHODIMP
5411 nsDocShell::SetSize(int32_t aCX, int32_t aCY, bool aRepaint)
5412 {
5413 int32_t x = 0, y = 0;
5414 GetPosition(&x, &y);
5415 return SetPositionAndSize(x, y, aCX, aCY, aRepaint);
5416 }
5417
5418 NS_IMETHODIMP
5419 nsDocShell::GetSize(int32_t * aCX, int32_t * aCY)
5420 {
5421 int32_t dummyHolder;
5422 return GetPositionAndSize(&dummyHolder, &dummyHolder, aCX, aCY);
5423 }
5424
5425 NS_IMETHODIMP
5426 nsDocShell::SetPositionAndSize(int32_t x, int32_t y, int32_t cx,
5427 int32_t cy, bool fRepaint)
5428 {
5429 mBounds.x = x;
5430 mBounds.y = y;
5431 mBounds.width = cx;
5432 mBounds.height = cy;
5433
5434 // Hold strong ref, since SetBounds can make us null out mContentViewer
5435 nsCOMPtr<nsIContentViewer> viewer = mContentViewer;
5436 if (viewer) {
5437 //XXX Border figured in here or is that handled elsewhere?
5438 NS_ENSURE_SUCCESS(viewer->SetBounds(mBounds), NS_ERROR_FAILURE);
5439 }
5440
5441 return NS_OK;
5442 }
5443
5444 NS_IMETHODIMP
5445 nsDocShell::GetPositionAndSize(int32_t * x, int32_t * y, int32_t * cx,
5446 int32_t * cy)
5447 {
5448 if (mParentWidget) {
5449 // ensure size is up-to-date if window has changed resolution
5450 nsIntRect r;
5451 mParentWidget->GetClientBounds(r);
5452 SetPositionAndSize(mBounds.x, mBounds.y, r.width, r.height, false);
5453 }
5454
5455 // We should really consider just getting this information from
5456 // our window instead of duplicating the storage and code...
5457 if (cx || cy) {
5458 // Caller wants to know our size; make sure to give them up to
5459 // date information.
5460 nsCOMPtr<nsIDocument> doc(do_GetInterface(GetAsSupports(mParent)));
5461 if (doc) {
5462 doc->FlushPendingNotifications(Flush_Layout);
5463 }
5464 }
5465
5466 DoGetPositionAndSize(x, y, cx, cy);
5467 return NS_OK;
5468 }
5469
5470 void
5471 nsDocShell::DoGetPositionAndSize(int32_t * x, int32_t * y, int32_t * cx,
5472 int32_t * cy)
5473 {
5474 if (x)
5475 *x = mBounds.x;
5476 if (y)
5477 *y = mBounds.y;
5478 if (cx)
5479 *cx = mBounds.width;
5480 if (cy)
5481 *cy = mBounds.height;
5482 }
5483
5484 NS_IMETHODIMP
5485 nsDocShell::Repaint(bool aForce)
5486 {
5487 nsCOMPtr<nsIPresShell> presShell =GetPresShell();
5488 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
5489
5490 nsViewManager* viewManager = presShell->GetViewManager();
5491 NS_ENSURE_TRUE(viewManager, NS_ERROR_FAILURE);
5492
5493 viewManager->InvalidateAllViews();
5494 return NS_OK;
5495 }
5496
5497 NS_IMETHODIMP
5498 nsDocShell::GetParentWidget(nsIWidget ** parentWidget)
5499 {
5500 NS_ENSURE_ARG_POINTER(parentWidget);
5501
5502 *parentWidget = mParentWidget;
5503 NS_IF_ADDREF(*parentWidget);
5504
5505 return NS_OK;
5506 }
5507
5508 NS_IMETHODIMP
5509 nsDocShell::SetParentWidget(nsIWidget * aParentWidget)
5510 {
5511 mParentWidget = aParentWidget;
5512
5513 return NS_OK;
5514 }
5515
5516 NS_IMETHODIMP
5517 nsDocShell::GetParentNativeWindow(nativeWindow * parentNativeWindow)
5518 {
5519 NS_ENSURE_ARG_POINTER(parentNativeWindow);
5520
5521 if (mParentWidget)
5522 *parentNativeWindow = mParentWidget->GetNativeData(NS_NATIVE_WIDGET);
5523 else
5524 *parentNativeWindow = nullptr;
5525
5526 return NS_OK;
5527 }
5528
5529 NS_IMETHODIMP
5530 nsDocShell::SetParentNativeWindow(nativeWindow parentNativeWindow)
5531 {
5532 return NS_ERROR_NOT_IMPLEMENTED;
5533 }
5534
5535 NS_IMETHODIMP
5536 nsDocShell::GetNativeHandle(nsAString& aNativeHandle)
5537 {
5538 // the nativeHandle should be accessed from nsIXULWindow
5539 return NS_ERROR_NOT_IMPLEMENTED;
5540 }
5541
5542 NS_IMETHODIMP
5543 nsDocShell::GetVisibility(bool * aVisibility)
5544 {
5545 NS_ENSURE_ARG_POINTER(aVisibility);
5546
5547 *aVisibility = false;
5548
5549 if (!mContentViewer)
5550 return NS_OK;
5551
5552 nsCOMPtr<nsIPresShell> presShell = GetPresShell();
5553 if (!presShell)
5554 return NS_OK;
5555
5556 // get the view manager
5557 nsViewManager* vm = presShell->GetViewManager();
5558 NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE);
5559
5560 // get the root view
5561 nsView *view = vm->GetRootView(); // views are not ref counted
5562 NS_ENSURE_TRUE(view, NS_ERROR_FAILURE);
5563
5564 // if our root view is hidden, we are not visible
5565 if (view->GetVisibility() == nsViewVisibility_kHide)
5566 return NS_OK;
5567
5568 // otherwise, we must walk up the document and view trees checking
5569 // for a hidden view, unless we're an off screen browser, which
5570 // would make this test meaningless.
5571
5572 nsCOMPtr<nsIDocShellTreeItem> treeItem = this;
5573 nsCOMPtr<nsIDocShellTreeItem> parentItem;
5574 treeItem->GetParent(getter_AddRefs(parentItem));
5575 while (parentItem) {
5576 nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(treeItem));
5577 presShell = docShell->GetPresShell();
5578
5579 nsCOMPtr<nsIDocShell> parentDS = do_QueryInterface(parentItem);
5580 nsCOMPtr<nsIPresShell> pPresShell = parentDS->GetPresShell();
5581
5582 // Null-check for crash in bug 267804
5583 if (!pPresShell) {
5584 NS_NOTREACHED("parent docshell has null pres shell");
5585 return NS_OK;
5586 }
5587
5588 nsIContent *shellContent =
5589 pPresShell->GetDocument()->FindContentForSubDocument(presShell->GetDocument());
5590 NS_ASSERTION(shellContent, "subshell not in the map");
5591
5592 nsIFrame* frame = shellContent ? shellContent->GetPrimaryFrame() : nullptr;
5593 bool isDocShellOffScreen = false;
5594 docShell->GetIsOffScreenBrowser(&isDocShellOffScreen);
5595 if (frame &&
5596 !frame->IsVisibleConsideringAncestors(nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY) &&
5597 !isDocShellOffScreen) {
5598 return NS_OK;
5599 }
5600
5601 treeItem = parentItem;
5602 treeItem->GetParent(getter_AddRefs(parentItem));
5603 }
5604
5605 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(mTreeOwner));
5606 if (!treeOwnerAsWin) {
5607 *aVisibility = true;
5608 return NS_OK;
5609 }
5610
5611 // Check with the tree owner as well to give embedders a chance to
5612 // expose visibility as well.
5613 return treeOwnerAsWin->GetVisibility(aVisibility);
5614 }
5615
5616 NS_IMETHODIMP
5617 nsDocShell::SetIsOffScreenBrowser(bool aIsOffScreen)
5618 {
5619 mIsOffScreenBrowser = aIsOffScreen;
5620 return NS_OK;
5621 }
5622
5623 NS_IMETHODIMP
5624 nsDocShell::GetIsOffScreenBrowser(bool *aIsOffScreen)
5625 {
5626 *aIsOffScreen = mIsOffScreenBrowser;
5627 return NS_OK;
5628 }
5629
5630 NS_IMETHODIMP
5631 nsDocShell::SetIsActive(bool aIsActive)
5632 {
5633 // We disallow setting active on chrome docshells.
5634 if (mItemType == nsIDocShellTreeItem::typeChrome)
5635 return NS_ERROR_INVALID_ARG;
5636
5637 // Keep track ourselves.
5638 mIsActive = aIsActive;
5639
5640 // Tell the PresShell about it.
5641 nsCOMPtr<nsIPresShell> pshell = GetPresShell();
5642 if (pshell)
5643 pshell->SetIsActive(aIsActive);
5644
5645 // Tell the window about it
5646 if (mScriptGlobal) {
5647 mScriptGlobal->SetIsBackground(!aIsActive);
5648 if (nsCOMPtr<nsIDocument> doc = mScriptGlobal->GetExtantDoc()) {
5649 doc->PostVisibilityUpdateEvent();
5650 }
5651 }
5652
5653 // Recursively tell all of our children, but don't tell <iframe mozbrowser>
5654 // children; they handle their state separately.
5655 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
5656 while (iter.HasMore()) {
5657 nsCOMPtr<nsIDocShell> docshell = do_QueryObject(iter.GetNext());
5658 if (!docshell) {
5659 continue;
5660 }
5661
5662 if (!docshell->GetIsBrowserOrApp()) {
5663 docshell->SetIsActive(aIsActive);
5664 }
5665 }
5666
5667 return NS_OK;
5668 }
5669
5670 NS_IMETHODIMP
5671 nsDocShell::GetIsActive(bool *aIsActive)
5672 {
5673 *aIsActive = mIsActive;
5674 return NS_OK;
5675 }
5676
5677 NS_IMETHODIMP
5678 nsDocShell::SetIsAppTab(bool aIsAppTab)
5679 {
5680 mIsAppTab = aIsAppTab;
5681 return NS_OK;
5682 }
5683
5684 NS_IMETHODIMP
5685 nsDocShell::GetIsAppTab(bool *aIsAppTab)
5686 {
5687 *aIsAppTab = mIsAppTab;
5688 return NS_OK;
5689 }
5690
5691 NS_IMETHODIMP
5692 nsDocShell::SetSandboxFlags(uint32_t aSandboxFlags)
5693 {
5694 mSandboxFlags = aSandboxFlags;
5695 return NS_OK;
5696 }
5697
5698 NS_IMETHODIMP
5699 nsDocShell::GetSandboxFlags(uint32_t *aSandboxFlags)
5700 {
5701 *aSandboxFlags = mSandboxFlags;
5702 return NS_OK;
5703 }
5704
5705 NS_IMETHODIMP
5706 nsDocShell::SetOnePermittedSandboxedNavigator(nsIDocShell* aSandboxedNavigator)
5707 {
5708 if (mOnePermittedSandboxedNavigator) {
5709 NS_ERROR("One Permitted Sandboxed Navigator should only be set once.");
5710 return NS_OK;
5711 }
5712
5713 mOnePermittedSandboxedNavigator = do_GetWeakReference(aSandboxedNavigator);
5714 NS_ASSERTION(mOnePermittedSandboxedNavigator,
5715 "One Permitted Sandboxed Navigator must support weak references.");
5716
5717 return NS_OK;
5718 }
5719
5720 NS_IMETHODIMP
5721 nsDocShell::GetOnePermittedSandboxedNavigator(nsIDocShell** aSandboxedNavigator)
5722 {
5723 NS_ENSURE_ARG_POINTER(aSandboxedNavigator);
5724 nsCOMPtr<nsIDocShell> permittedNavigator =
5725 do_QueryReferent(mOnePermittedSandboxedNavigator);
5726 NS_IF_ADDREF(*aSandboxedNavigator = permittedNavigator);
5727 return NS_OK;
5728 }
5729
5730 NS_IMETHODIMP
5731 nsDocShell::SetDefaultLoadFlags(uint32_t aDefaultLoadFlags)
5732 {
5733 mDefaultLoadFlags = aDefaultLoadFlags;
5734
5735 // Tell the load group to set these flags all requests in the group
5736 if (mLoadGroup) {
5737 mLoadGroup->SetDefaultLoadFlags(aDefaultLoadFlags);
5738 } else {
5739 NS_WARNING("nsDocShell::SetDefaultLoadFlags has no loadGroup to propagate the flags to");
5740 }
5741
5742 // Recursively tell all of our children. We *do not* skip
5743 // <iframe mozbrowser> children - if someone sticks custom flags in this
5744 // docShell then they too get the same flags.
5745 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
5746 while (iter.HasMore()) {
5747 nsCOMPtr<nsIDocShell> docshell = do_QueryObject(iter.GetNext());
5748 if (!docshell) {
5749 continue;
5750 }
5751 docshell->SetDefaultLoadFlags(aDefaultLoadFlags);
5752 }
5753 return NS_OK;
5754 }
5755
5756 NS_IMETHODIMP
5757 nsDocShell::GetDefaultLoadFlags(uint32_t *aDefaultLoadFlags)
5758 {
5759 *aDefaultLoadFlags = mDefaultLoadFlags;
5760 return NS_OK;
5761 }
5762
5763
5764 NS_IMETHODIMP
5765 nsDocShell::SetMixedContentChannel(nsIChannel* aMixedContentChannel)
5766 {
5767 #ifdef DEBUG
5768 // if the channel is non-null
5769 if (aMixedContentChannel) {
5770 // Get the root docshell.
5771 nsCOMPtr<nsIDocShellTreeItem> root;
5772 GetSameTypeRootTreeItem(getter_AddRefs(root));
5773 NS_WARN_IF_FALSE(
5774 root.get() == static_cast<nsIDocShellTreeItem *>(this),
5775 "Setting mMixedContentChannel on a docshell that is not the root docshell"
5776 );
5777 }
5778 #endif
5779 mMixedContentChannel = aMixedContentChannel;
5780 return NS_OK;
5781 }
5782
5783 NS_IMETHODIMP
5784 nsDocShell::GetMixedContentChannel(nsIChannel **aMixedContentChannel)
5785 {
5786 NS_ENSURE_ARG_POINTER(aMixedContentChannel);
5787 NS_IF_ADDREF(*aMixedContentChannel = mMixedContentChannel);
5788 return NS_OK;
5789 }
5790
5791 NS_IMETHODIMP
5792 nsDocShell::GetAllowMixedContentAndConnectionData(bool* aRootHasSecureConnection, bool* aAllowMixedContent, bool* aIsRootDocShell)
5793 {
5794 *aRootHasSecureConnection = true;
5795 *aAllowMixedContent = false;
5796 *aIsRootDocShell = false;
5797
5798 nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
5799 GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
5800 NS_ASSERTION(sameTypeRoot, "No document shell root tree item from document shell tree item!");
5801 *aIsRootDocShell = sameTypeRoot.get() == static_cast<nsIDocShellTreeItem *>(this);
5802
5803 // now get the document from sameTypeRoot
5804 nsCOMPtr<nsIDocument> rootDoc = do_GetInterface(sameTypeRoot);
5805 if (rootDoc) {
5806 nsCOMPtr<nsIPrincipal> rootPrincipal = rootDoc->NodePrincipal();
5807
5808 // For things with system principal (e.g. scratchpad) there is no uri
5809 // aRootHasSecureConnection should be false.
5810 nsCOMPtr<nsIURI> rootUri;
5811 if (nsContentUtils::IsSystemPrincipal(rootPrincipal) ||
5812 NS_FAILED(rootPrincipal->GetURI(getter_AddRefs(rootUri))) || !rootUri ||
5813 NS_FAILED(rootUri->SchemeIs("https", aRootHasSecureConnection))) {
5814 *aRootHasSecureConnection = false;
5815 }
5816
5817 // Check the root doc's channel against the root docShell's mMixedContentChannel to see
5818 // if they are the same. If they are the same, the user has overriden
5819 // the block.
5820 nsCOMPtr<nsIDocShell> rootDocShell = do_QueryInterface(sameTypeRoot);
5821 nsCOMPtr<nsIChannel> mixedChannel;
5822 rootDocShell->GetMixedContentChannel(getter_AddRefs(mixedChannel));
5823 *aAllowMixedContent = mixedChannel && (mixedChannel == rootDoc->GetChannel());
5824 }
5825
5826 return NS_OK;
5827 }
5828
5829 NS_IMETHODIMP
5830 nsDocShell::SetVisibility(bool aVisibility)
5831 {
5832 // Show()/Hide() may change mContentViewer.
5833 nsCOMPtr<nsIContentViewer> cv = mContentViewer;
5834 if (!cv)
5835 return NS_OK;
5836 if (aVisibility) {
5837 cv->Show();
5838 }
5839 else {
5840 cv->Hide();
5841 }
5842
5843 return NS_OK;
5844 }
5845
5846 NS_IMETHODIMP
5847 nsDocShell::GetEnabled(bool *aEnabled)
5848 {
5849 NS_ENSURE_ARG_POINTER(aEnabled);
5850 *aEnabled = true;
5851 return NS_ERROR_NOT_IMPLEMENTED;
5852 }
5853
5854 NS_IMETHODIMP
5855 nsDocShell::SetEnabled(bool aEnabled)
5856 {
5857 return NS_ERROR_NOT_IMPLEMENTED;
5858 }
5859
5860 NS_IMETHODIMP
5861 nsDocShell::SetFocus()
5862 {
5863 return NS_OK;
5864 }
5865
5866 NS_IMETHODIMP
5867 nsDocShell::GetMainWidget(nsIWidget ** aMainWidget)
5868 {
5869 // We don't create our own widget, so simply return the parent one.
5870 return GetParentWidget(aMainWidget);
5871 }
5872
5873 NS_IMETHODIMP
5874 nsDocShell::GetTitle(char16_t ** aTitle)
5875 {
5876 NS_ENSURE_ARG_POINTER(aTitle);
5877
5878 *aTitle = ToNewUnicode(mTitle);
5879 return NS_OK;
5880 }
5881
5882 NS_IMETHODIMP
5883 nsDocShell::SetTitle(const char16_t * aTitle)
5884 {
5885 // Store local title
5886 mTitle = aTitle;
5887
5888 nsCOMPtr<nsIDocShellTreeItem> parent;
5889 GetSameTypeParent(getter_AddRefs(parent));
5890
5891 // When title is set on the top object it should then be passed to the
5892 // tree owner.
5893 if (!parent) {
5894 nsCOMPtr<nsIBaseWindow>
5895 treeOwnerAsWin(do_QueryInterface(mTreeOwner));
5896 if (treeOwnerAsWin)
5897 treeOwnerAsWin->SetTitle(aTitle);
5898 }
5899
5900 if (mCurrentURI && mLoadType != LOAD_ERROR_PAGE && mUseGlobalHistory &&
5901 !mInPrivateBrowsing) {
5902 nsCOMPtr<IHistory> history = services::GetHistoryService();
5903 if (history) {
5904 history->SetURITitle(mCurrentURI, mTitle);
5905 }
5906 else if (mGlobalHistory) {
5907 mGlobalHistory->SetPageTitle(mCurrentURI, nsString(mTitle));
5908 }
5909 }
5910
5911 // Update SessionHistory with the document's title.
5912 if (mOSHE && mLoadType != LOAD_BYPASS_HISTORY &&
5913 mLoadType != LOAD_ERROR_PAGE) {
5914
5915 mOSHE->SetTitle(mTitle);
5916 }
5917
5918 return NS_OK;
5919 }
5920
5921 nsresult
5922 nsDocShell::GetCurScrollPos(int32_t scrollOrientation, int32_t * curPos)
5923 {
5924 NS_ENSURE_ARG_POINTER(curPos);
5925
5926 nsIScrollableFrame* sf = GetRootScrollFrame();
5927 NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
5928
5929 nsPoint pt = sf->GetScrollPosition();
5930
5931 switch (scrollOrientation) {
5932 case ScrollOrientation_X:
5933 *curPos = pt.x;
5934 return NS_OK;
5935
5936 case ScrollOrientation_Y:
5937 *curPos = pt.y;
5938 return NS_OK;
5939
5940 default:
5941 NS_ENSURE_TRUE(false, NS_ERROR_INVALID_ARG);
5942 }
5943 }
5944
5945 nsresult
5946 nsDocShell::SetCurScrollPosEx(int32_t curHorizontalPos, int32_t curVerticalPos)
5947 {
5948 nsIScrollableFrame* sf = GetRootScrollFrame();
5949 NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
5950
5951 sf->ScrollTo(nsPoint(curHorizontalPos, curVerticalPos),
5952 nsIScrollableFrame::INSTANT);
5953 return NS_OK;
5954 }
5955
5956 //*****************************************************************************
5957 // nsDocShell::nsIScrollable
5958 //*****************************************************************************
5959
5960 NS_IMETHODIMP
5961 nsDocShell::GetDefaultScrollbarPreferences(int32_t scrollOrientation,
5962 int32_t * scrollbarPref)
5963 {
5964 NS_ENSURE_ARG_POINTER(scrollbarPref);
5965 switch (scrollOrientation) {
5966 case ScrollOrientation_X:
5967 *scrollbarPref = mDefaultScrollbarPref.x;
5968 return NS_OK;
5969
5970 case ScrollOrientation_Y:
5971 *scrollbarPref = mDefaultScrollbarPref.y;
5972 return NS_OK;
5973
5974 default:
5975 NS_ENSURE_TRUE(false, NS_ERROR_INVALID_ARG);
5976 }
5977 return NS_ERROR_FAILURE;
5978 }
5979
5980 NS_IMETHODIMP
5981 nsDocShell::SetDefaultScrollbarPreferences(int32_t scrollOrientation,
5982 int32_t scrollbarPref)
5983 {
5984 switch (scrollOrientation) {
5985 case ScrollOrientation_X:
5986 mDefaultScrollbarPref.x = scrollbarPref;
5987 return NS_OK;
5988
5989 case ScrollOrientation_Y:
5990 mDefaultScrollbarPref.y = scrollbarPref;
5991 return NS_OK;
5992
5993 default:
5994 NS_ENSURE_TRUE(false, NS_ERROR_INVALID_ARG);
5995 }
5996 return NS_ERROR_FAILURE;
5997 }
5998
5999 NS_IMETHODIMP
6000 nsDocShell::GetScrollbarVisibility(bool * verticalVisible,
6001 bool * horizontalVisible)
6002 {
6003 nsIScrollableFrame* sf = GetRootScrollFrame();
6004 NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
6005
6006 uint32_t scrollbarVisibility = sf->GetScrollbarVisibility();
6007 if (verticalVisible)
6008 *verticalVisible = (scrollbarVisibility & nsIScrollableFrame::VERTICAL) != 0;
6009 if (horizontalVisible)
6010 *horizontalVisible = (scrollbarVisibility & nsIScrollableFrame::HORIZONTAL) != 0;
6011
6012 return NS_OK;
6013 }
6014
6015 //*****************************************************************************
6016 // nsDocShell::nsITextScroll
6017 //*****************************************************************************
6018
6019 NS_IMETHODIMP
6020 nsDocShell::ScrollByLines(int32_t numLines)
6021 {
6022 nsIScrollableFrame* sf = GetRootScrollFrame();
6023 NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
6024
6025 sf->ScrollBy(nsIntPoint(0, numLines), nsIScrollableFrame::LINES,
6026 nsIScrollableFrame::SMOOTH);
6027 return NS_OK;
6028 }
6029
6030 NS_IMETHODIMP
6031 nsDocShell::ScrollByPages(int32_t numPages)
6032 {
6033 nsIScrollableFrame* sf = GetRootScrollFrame();
6034 NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
6035
6036 sf->ScrollBy(nsIntPoint(0, numPages), nsIScrollableFrame::PAGES,
6037 nsIScrollableFrame::SMOOTH);
6038 return NS_OK;
6039 }
6040
6041 //*****************************************************************************
6042 // nsDocShell::nsIRefreshURI
6043 //*****************************************************************************
6044
6045 NS_IMETHODIMP
6046 nsDocShell::RefreshURI(nsIURI * aURI, int32_t aDelay, bool aRepeat,
6047 bool aMetaRefresh)
6048 {
6049 NS_ENSURE_ARG(aURI);
6050
6051 /* Check if Meta refresh/redirects are permitted. Some
6052 * embedded applications may not want to do this.
6053 * Must do this before sending out NOTIFY_REFRESH events
6054 * because listeners may have side effects (e.g. displaying a
6055 * button to manually trigger the refresh later).
6056 */
6057 bool allowRedirects = true;
6058 GetAllowMetaRedirects(&allowRedirects);
6059 if (!allowRedirects)
6060 return NS_OK;
6061
6062 // If any web progress listeners are listening for NOTIFY_REFRESH events,
6063 // give them a chance to block this refresh.
6064 bool sameURI;
6065 nsresult rv = aURI->Equals(mCurrentURI, &sameURI);
6066 if (NS_FAILED(rv))
6067 sameURI = false;
6068 if (!RefreshAttempted(this, aURI, aDelay, sameURI))
6069 return NS_OK;
6070
6071 nsRefreshTimer *refreshTimer = new nsRefreshTimer();
6072 NS_ENSURE_TRUE(refreshTimer, NS_ERROR_OUT_OF_MEMORY);
6073 uint32_t busyFlags = 0;
6074 GetBusyFlags(&busyFlags);
6075
6076 nsCOMPtr<nsISupports> dataRef = refreshTimer; // Get the ref count to 1
6077
6078 refreshTimer->mDocShell = this;
6079 refreshTimer->mURI = aURI;
6080 refreshTimer->mDelay = aDelay;
6081 refreshTimer->mRepeat = aRepeat;
6082 refreshTimer->mMetaRefresh = aMetaRefresh;
6083
6084 if (!mRefreshURIList) {
6085 NS_ENSURE_SUCCESS(NS_NewISupportsArray(getter_AddRefs(mRefreshURIList)),
6086 NS_ERROR_FAILURE);
6087 }
6088
6089 if (busyFlags & BUSY_FLAGS_BUSY) {
6090 // We are busy loading another page. Don't create the
6091 // timer right now. Instead queue up the request and trigger the
6092 // timer in EndPageLoad().
6093 mRefreshURIList->AppendElement(refreshTimer);
6094 }
6095 else {
6096 // There is no page loading going on right now. Create the
6097 // timer and fire it right away.
6098 nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1");
6099 NS_ENSURE_TRUE(timer, NS_ERROR_FAILURE);
6100
6101 mRefreshURIList->AppendElement(timer); // owning timer ref
6102 timer->InitWithCallback(refreshTimer, aDelay, nsITimer::TYPE_ONE_SHOT);
6103 }
6104 return NS_OK;
6105 }
6106
6107 nsresult
6108 nsDocShell::ForceRefreshURIFromTimer(nsIURI * aURI,
6109 int32_t aDelay,
6110 bool aMetaRefresh,
6111 nsITimer* aTimer)
6112 {
6113 NS_PRECONDITION(aTimer, "Must have a timer here");
6114
6115 // Remove aTimer from mRefreshURIList if needed
6116 if (mRefreshURIList) {
6117 uint32_t n = 0;
6118 mRefreshURIList->Count(&n);
6119
6120 for (uint32_t i = 0; i < n; ++i) {
6121 nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i);
6122 if (timer == aTimer) {
6123 mRefreshURIList->RemoveElementAt(i);
6124 break;
6125 }
6126 }
6127 }
6128
6129 return ForceRefreshURI(aURI, aDelay, aMetaRefresh);
6130 }
6131
6132 NS_IMETHODIMP
6133 nsDocShell::ForceRefreshURI(nsIURI * aURI,
6134 int32_t aDelay,
6135 bool aMetaRefresh)
6136 {
6137 NS_ENSURE_ARG(aURI);
6138
6139 nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
6140 CreateLoadInfo(getter_AddRefs(loadInfo));
6141 NS_ENSURE_TRUE(loadInfo, NS_ERROR_OUT_OF_MEMORY);
6142
6143 /* We do need to pass in a referrer, but we don't want it to
6144 * be sent to the server.
6145 */
6146 loadInfo->SetSendReferrer(false);
6147
6148 /* for most refreshes the current URI is an appropriate
6149 * internal referrer
6150 */
6151 loadInfo->SetReferrer(mCurrentURI);
6152
6153 /* Don't ever "guess" on which owner to use to avoid picking
6154 * the current owner.
6155 */
6156 loadInfo->SetOwnerIsExplicit(true);
6157
6158 /* Check if this META refresh causes a redirection
6159 * to another site.
6160 */
6161 bool equalUri = false;
6162 nsresult rv = aURI->Equals(mCurrentURI, &equalUri);
6163 if (NS_SUCCEEDED(rv) && (!equalUri) && aMetaRefresh &&
6164 aDelay <= REFRESH_REDIRECT_TIMER) {
6165
6166 /* It is a META refresh based redirection within the threshold time
6167 * we have in mind (15000 ms as defined by REFRESH_REDIRECT_TIMER).
6168 * Pass a REPLACE flag to LoadURI().
6169 */
6170 loadInfo->SetLoadType(nsIDocShellLoadInfo::loadNormalReplace);
6171
6172 /* for redirects we mimic HTTP, which passes the
6173 * original referrer
6174 */
6175 nsCOMPtr<nsIURI> internalReferrer;
6176 GetReferringURI(getter_AddRefs(internalReferrer));
6177 if (internalReferrer) {
6178 loadInfo->SetReferrer(internalReferrer);
6179 }
6180 }
6181 else {
6182 loadInfo->SetLoadType(nsIDocShellLoadInfo::loadRefresh);
6183 }
6184
6185 /*
6186 * LoadURI(...) will cancel all refresh timers... This causes the
6187 * Timer and its refreshData instance to be released...
6188 */
6189 LoadURI(aURI, loadInfo, nsIWebNavigation::LOAD_FLAGS_NONE, true);
6190
6191 return NS_OK;
6192 }
6193
6194 nsresult
6195 nsDocShell::SetupRefreshURIFromHeader(nsIURI * aBaseURI,
6196 nsIPrincipal* aPrincipal,
6197 const nsACString & aHeader)
6198 {
6199 // Refresh headers are parsed with the following format in mind
6200 // <META HTTP-EQUIV=REFRESH CONTENT="5; URL=http://uri">
6201 // By the time we are here, the following is true:
6202 // header = "REFRESH"
6203 // content = "5; URL=http://uri" // note the URL attribute is
6204 // optional, if it is absent, the currently loaded url is used.
6205 // Also note that the seconds and URL separator can be either
6206 // a ';' or a ','. The ',' separator should be illegal but CNN
6207 // is using it.
6208 //
6209 // We need to handle the following strings, where
6210 // - X is a set of digits
6211 // - URI is either a relative or absolute URI
6212 //
6213 // Note that URI should start with "url=" but we allow omission
6214 //
6215 // "" || ";" || ","
6216 // empty string. use the currently loaded URI
6217 // and refresh immediately.
6218 // "X" || "X;" || "X,"
6219 // Refresh the currently loaded URI in X seconds.
6220 // "X; URI" || "X, URI"
6221 // Refresh using URI as the destination in X seconds.
6222 // "URI" || "; URI" || ", URI"
6223 // Refresh immediately using URI as the destination.
6224 //
6225 // Currently, anything immediately following the URI, if
6226 // separated by any char in the set "'\"\t\r\n " will be
6227 // ignored. So "10; url=go.html ; foo=bar" will work,
6228 // and so will "10; url='go.html'; foo=bar". However,
6229 // "10; url=go.html; foo=bar" will result in the uri
6230 // "go.html;" since ';' and ',' are valid uri characters.
6231 //
6232 // Note that we need to remove any tokens wrapping the URI.
6233 // These tokens currently include spaces, double and single
6234 // quotes.
6235
6236 // when done, seconds is 0 or the given number of seconds
6237 // uriAttrib is empty or the URI specified
6238 MOZ_ASSERT(aPrincipal);
6239
6240 nsAutoCString uriAttrib;
6241 int32_t seconds = 0;
6242 bool specifiesSeconds = false;
6243
6244 nsACString::const_iterator iter, tokenStart, doneIterating;
6245
6246 aHeader.BeginReading(iter);
6247 aHeader.EndReading(doneIterating);
6248
6249 // skip leading whitespace
6250 while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter))
6251 ++iter;
6252
6253 tokenStart = iter;
6254
6255 // skip leading + and -
6256 if (iter != doneIterating && (*iter == '-' || *iter == '+'))
6257 ++iter;
6258
6259 // parse number
6260 while (iter != doneIterating && (*iter >= '0' && *iter <= '9')) {
6261 seconds = seconds * 10 + (*iter - '0');
6262 specifiesSeconds = true;
6263 ++iter;
6264 }
6265
6266 if (iter != doneIterating) {
6267 // if we started with a '-', number is negative
6268 if (*tokenStart == '-')
6269 seconds = -seconds;
6270
6271 // skip to next ';' or ','
6272 nsACString::const_iterator iterAfterDigit = iter;
6273 while (iter != doneIterating && !(*iter == ';' || *iter == ','))
6274 {
6275 if (specifiesSeconds)
6276 {
6277 // Non-whitespace characters here mean that the string is
6278 // malformed but tolerate sites that specify a decimal point,
6279 // even though meta refresh only works on whole seconds.
6280 if (iter == iterAfterDigit &&
6281 !nsCRT::IsAsciiSpace(*iter) && *iter != '.')
6282 {
6283 // The characters between the seconds and the next
6284 // section are just garbage!
6285 // e.g. content="2a0z+,URL=http://www.mozilla.org/"
6286 // Just ignore this redirect.
6287 return NS_ERROR_FAILURE;
6288 }
6289 else if (nsCRT::IsAsciiSpace(*iter))
6290 {
6291 // We've had at least one whitespace so tolerate the mistake
6292 // and drop through.
6293 // e.g. content="10 foo"
6294 ++iter;
6295 break;
6296 }
6297 }
6298 ++iter;
6299 }
6300
6301 // skip any remaining whitespace
6302 while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter))
6303 ++iter;
6304
6305 // skip ';' or ','
6306 if (iter != doneIterating && (*iter == ';' || *iter == ',')) {
6307 ++iter;
6308 }
6309
6310 // skip whitespace
6311 while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter))
6312 ++iter;
6313 }
6314
6315 // possible start of URI
6316 tokenStart = iter;
6317
6318 // skip "url = " to real start of URI
6319 if (iter != doneIterating && (*iter == 'u' || *iter == 'U')) {
6320 ++iter;
6321 if (iter != doneIterating && (*iter == 'r' || *iter == 'R')) {
6322 ++iter;
6323 if (iter != doneIterating && (*iter == 'l' || *iter == 'L')) {
6324 ++iter;
6325
6326 // skip whitespace
6327 while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter))
6328 ++iter;
6329
6330 if (iter != doneIterating && *iter == '=') {
6331 ++iter;
6332
6333 // skip whitespace
6334 while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter))
6335 ++iter;
6336
6337 // found real start of URI
6338 tokenStart = iter;
6339 }
6340 }
6341 }
6342 }
6343
6344 // skip a leading '"' or '\''.
6345
6346 bool isQuotedURI = false;
6347 if (tokenStart != doneIterating && (*tokenStart == '"' || *tokenStart == '\''))
6348 {
6349 isQuotedURI = true;
6350 ++tokenStart;
6351 }
6352
6353 // set iter to start of URI
6354 iter = tokenStart;
6355
6356 // tokenStart here points to the beginning of URI
6357
6358 // grab the rest of the URI
6359 while (iter != doneIterating)
6360 {
6361 if (isQuotedURI && (*iter == '"' || *iter == '\''))
6362 break;
6363 ++iter;
6364 }
6365
6366 // move iter one back if the last character is a '"' or '\''
6367 if (iter != tokenStart && isQuotedURI) {
6368 --iter;
6369 if (!(*iter == '"' || *iter == '\''))
6370 ++iter;
6371 }
6372
6373 // URI is whatever's contained from tokenStart to iter.
6374 // note: if tokenStart == doneIterating, so is iter.
6375
6376 nsresult rv = NS_OK;
6377
6378 nsCOMPtr<nsIURI> uri;
6379 bool specifiesURI = false;
6380 if (tokenStart == iter) {
6381 uri = aBaseURI;
6382 }
6383 else {
6384 uriAttrib = Substring(tokenStart, iter);
6385 // NS_NewURI takes care of any whitespace surrounding the URL
6386 rv = NS_NewURI(getter_AddRefs(uri), uriAttrib, nullptr, aBaseURI);
6387 specifiesURI = true;
6388 }
6389
6390 // No URI or seconds were specified
6391 if (!specifiesSeconds && !specifiesURI)
6392 {
6393 // Do nothing because the alternative is to spin around in a refresh
6394 // loop forever!
6395 return NS_ERROR_FAILURE;
6396 }
6397
6398 if (NS_SUCCEEDED(rv)) {
6399 nsCOMPtr<nsIScriptSecurityManager>
6400 securityManager(do_GetService
6401 (NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv));
6402 if (NS_SUCCEEDED(rv)) {
6403 rv = securityManager->
6404 CheckLoadURIWithPrincipal(aPrincipal, uri,
6405 nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT);
6406
6407 if (NS_SUCCEEDED(rv)) {
6408 bool isjs = true;
6409 rv = NS_URIChainHasFlags(uri,
6410 nsIProtocolHandler::URI_OPENING_EXECUTES_SCRIPT, &isjs);
6411 NS_ENSURE_SUCCESS(rv, rv);
6412
6413 if (isjs) {
6414 return NS_ERROR_FAILURE;
6415 }
6416 }
6417
6418 if (NS_SUCCEEDED(rv)) {
6419 // Since we can't travel back in time yet, just pretend
6420 // negative numbers do nothing at all.
6421 if (seconds < 0)
6422 return NS_ERROR_FAILURE;
6423
6424 rv = RefreshURI(uri, seconds * 1000, false, true);
6425 }
6426 }
6427 }
6428 return rv;
6429 }
6430
6431 NS_IMETHODIMP nsDocShell::SetupRefreshURI(nsIChannel * aChannel)
6432 {
6433 nsresult rv;
6434 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel, &rv));
6435 if (NS_SUCCEEDED(rv)) {
6436 nsAutoCString refreshHeader;
6437 rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("refresh"),
6438 refreshHeader);
6439
6440 if (!refreshHeader.IsEmpty()) {
6441 nsCOMPtr<nsIScriptSecurityManager> secMan =
6442 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
6443 NS_ENSURE_SUCCESS(rv, rv);
6444
6445 nsCOMPtr<nsIPrincipal> principal;
6446 rv = secMan->GetChannelPrincipal(aChannel, getter_AddRefs(principal));
6447 NS_ENSURE_SUCCESS(rv, rv);
6448
6449 SetupReferrerFromChannel(aChannel);
6450 rv = SetupRefreshURIFromHeader(mCurrentURI, principal, refreshHeader);
6451 if (NS_SUCCEEDED(rv)) {
6452 return NS_REFRESHURI_HEADER_FOUND;
6453 }
6454 }
6455 }
6456 return rv;
6457 }
6458
6459 static void
6460 DoCancelRefreshURITimers(nsISupportsArray* aTimerList)
6461 {
6462 if (!aTimerList)
6463 return;
6464
6465 uint32_t n=0;
6466 aTimerList->Count(&n);
6467
6468 while (n) {
6469 nsCOMPtr<nsITimer> timer(do_QueryElementAt(aTimerList, --n));
6470
6471 aTimerList->RemoveElementAt(n); // bye bye owning timer ref
6472
6473 if (timer)
6474 timer->Cancel();
6475 }
6476 }
6477
6478 NS_IMETHODIMP
6479 nsDocShell::CancelRefreshURITimers()
6480 {
6481 DoCancelRefreshURITimers(mRefreshURIList);
6482 DoCancelRefreshURITimers(mSavedRefreshURIList);
6483 mRefreshURIList = nullptr;
6484 mSavedRefreshURIList = nullptr;
6485
6486 return NS_OK;
6487 }
6488
6489 NS_IMETHODIMP
6490 nsDocShell::GetRefreshPending(bool* _retval)
6491 {
6492 if (!mRefreshURIList) {
6493 *_retval = false;
6494 return NS_OK;
6495 }
6496
6497 uint32_t count;
6498 nsresult rv = mRefreshURIList->Count(&count);
6499 if (NS_SUCCEEDED(rv))
6500 *_retval = (count != 0);
6501 return rv;
6502 }
6503
6504 NS_IMETHODIMP
6505 nsDocShell::SuspendRefreshURIs()
6506 {
6507 if (mRefreshURIList) {
6508 uint32_t n = 0;
6509 mRefreshURIList->Count(&n);
6510
6511 for (uint32_t i = 0; i < n; ++i) {
6512 nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i);
6513 if (!timer)
6514 continue; // this must be a nsRefreshURI already
6515
6516 // Replace this timer object with a nsRefreshTimer object.
6517 nsCOMPtr<nsITimerCallback> callback;
6518 timer->GetCallback(getter_AddRefs(callback));
6519
6520 timer->Cancel();
6521
6522 nsCOMPtr<nsITimerCallback> rt = do_QueryInterface(callback);
6523 NS_ASSERTION(rt, "RefreshURIList timer callbacks should only be RefreshTimer objects");
6524
6525 mRefreshURIList->ReplaceElementAt(rt, i);
6526 }
6527 }
6528
6529 // Suspend refresh URIs for our child shells as well.
6530 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
6531 while (iter.HasMore()) {
6532 nsCOMPtr<nsIDocShell> shell = do_QueryObject(iter.GetNext());
6533 if (shell)
6534 shell->SuspendRefreshURIs();
6535 }
6536
6537 return NS_OK;
6538 }
6539
6540 NS_IMETHODIMP
6541 nsDocShell::ResumeRefreshURIs()
6542 {
6543 RefreshURIFromQueue();
6544
6545 // Resume refresh URIs for our child shells as well.
6546 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
6547 while (iter.HasMore()) {
6548 nsCOMPtr<nsIDocShell> shell = do_QueryObject(iter.GetNext());
6549 if (shell)
6550 shell->ResumeRefreshURIs();
6551 }
6552
6553 return NS_OK;
6554 }
6555
6556 nsresult
6557 nsDocShell::RefreshURIFromQueue()
6558 {
6559 if (!mRefreshURIList)
6560 return NS_OK;
6561 uint32_t n = 0;
6562 mRefreshURIList->Count(&n);
6563
6564 while (n) {
6565 nsCOMPtr<nsISupports> element;
6566 mRefreshURIList->GetElementAt(--n, getter_AddRefs(element));
6567 nsCOMPtr<nsITimerCallback> refreshInfo(do_QueryInterface(element));
6568
6569 if (refreshInfo) {
6570 // This is the nsRefreshTimer object, waiting to be
6571 // setup in a timer object and fired.
6572 // Create the timer and trigger it.
6573 uint32_t delay = static_cast<nsRefreshTimer*>(static_cast<nsITimerCallback*>(refreshInfo))->GetDelay();
6574 nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1");
6575 if (timer) {
6576 // Replace the nsRefreshTimer element in the queue with
6577 // its corresponding timer object, so that in case another
6578 // load comes through before the timer can go off, the timer will
6579 // get cancelled in CancelRefreshURITimer()
6580 mRefreshURIList->ReplaceElementAt(timer, n);
6581 timer->InitWithCallback(refreshInfo, delay, nsITimer::TYPE_ONE_SHOT);
6582 }
6583 }
6584 } // while
6585
6586 return NS_OK;
6587 }
6588
6589 //*****************************************************************************
6590 // nsDocShell::nsIContentViewerContainer
6591 //*****************************************************************************
6592
6593 NS_IMETHODIMP
6594 nsDocShell::Embed(nsIContentViewer * aContentViewer,
6595 const char *aCommand, nsISupports * aExtraInfo)
6596 {
6597 // Save the LayoutHistoryState of the previous document, before
6598 // setting up new document
6599 PersistLayoutHistoryState();
6600
6601 nsresult rv = SetupNewViewer(aContentViewer);
6602
6603 // If we are loading a wyciwyg url from history, change the base URI for
6604 // the document to the original http url that created the document.write().
6605 // This makes sure that all relative urls in a document.written page loaded
6606 // via history work properly.
6607 if (mCurrentURI &&
6608 (mLoadType & LOAD_CMD_HISTORY ||
6609 mLoadType == LOAD_RELOAD_NORMAL ||
6610 mLoadType == LOAD_RELOAD_CHARSET_CHANGE)){
6611 bool isWyciwyg = false;
6612 // Check if the url is wyciwyg
6613 rv = mCurrentURI->SchemeIs("wyciwyg", &isWyciwyg);
6614 if (isWyciwyg && NS_SUCCEEDED(rv))
6615 SetBaseUrlForWyciwyg(aContentViewer);
6616 }
6617 // XXX What if SetupNewViewer fails?
6618 if (mLSHE) {
6619 // Restore the editing state, if it's stored in session history.
6620 if (mLSHE->HasDetachedEditor()) {
6621 ReattachEditorToWindow(mLSHE);
6622 }
6623 // Set history.state
6624 SetDocCurrentStateObj(mLSHE);
6625
6626 SetHistoryEntry(&mOSHE, mLSHE);
6627 }
6628
6629 bool updateHistory = true;
6630
6631 // Determine if this type of load should update history
6632 switch (mLoadType) {
6633 case LOAD_NORMAL_REPLACE:
6634 case LOAD_STOP_CONTENT_AND_REPLACE:
6635 case LOAD_RELOAD_BYPASS_CACHE:
6636 case LOAD_RELOAD_BYPASS_PROXY:
6637 case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
6638 case LOAD_REPLACE_BYPASS_CACHE:
6639 updateHistory = false;
6640 break;
6641 default:
6642 break;
6643 }
6644
6645 if (!updateHistory)
6646 SetLayoutHistoryState(nullptr);
6647
6648 return NS_OK;
6649 }
6650
6651 /* void setIsPrinting (in boolean aIsPrinting); */
6652 NS_IMETHODIMP
6653 nsDocShell::SetIsPrinting(bool aIsPrinting)
6654 {
6655 mIsPrintingOrPP = aIsPrinting;
6656 return NS_OK;
6657 }
6658
6659 //*****************************************************************************
6660 // nsDocShell::nsIWebProgressListener
6661 //*****************************************************************************
6662
6663 NS_IMETHODIMP
6664 nsDocShell::OnProgressChange(nsIWebProgress * aProgress,
6665 nsIRequest * aRequest,
6666 int32_t aCurSelfProgress,
6667 int32_t aMaxSelfProgress,
6668 int32_t aCurTotalProgress,
6669 int32_t aMaxTotalProgress)
6670 {
6671 return NS_OK;
6672 }
6673
6674 NS_IMETHODIMP
6675 nsDocShell::OnStateChange(nsIWebProgress * aProgress, nsIRequest * aRequest,
6676 uint32_t aStateFlags, nsresult aStatus)
6677 {
6678 if ((~aStateFlags & (STATE_START | STATE_IS_NETWORK)) == 0) {
6679 // Save timing statistics.
6680 nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
6681 nsCOMPtr<nsIURI> uri;
6682 channel->GetURI(getter_AddRefs(uri));
6683 nsAutoCString aURI;
6684 uri->GetAsciiSpec(aURI);
6685
6686 nsCOMPtr<nsIWyciwygChannel> wcwgChannel(do_QueryInterface(aRequest));
6687 nsCOMPtr<nsIWebProgress> webProgress =
6688 do_QueryInterface(GetAsSupports(this));
6689
6690 // We don't update navigation timing for wyciwyg channels
6691 if (this == aProgress && !wcwgChannel){
6692 MaybeInitTiming();
6693 mTiming->NotifyFetchStart(uri, ConvertLoadTypeToNavigationType(mLoadType));
6694 }
6695
6696 // Was the wyciwyg document loaded on this docshell?
6697 if (wcwgChannel && !mLSHE && (mItemType == typeContent) && aProgress == webProgress.get()) {
6698 bool equalUri = true;
6699 // Store the wyciwyg url in session history, only if it is
6700 // being loaded fresh for the first time. We don't want
6701 // multiple entries for successive loads
6702 if (mCurrentURI &&
6703 NS_SUCCEEDED(uri->Equals(mCurrentURI, &equalUri)) &&
6704 !equalUri) {
6705
6706 nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
6707 GetSameTypeParent(getter_AddRefs(parentAsItem));
6708 nsCOMPtr<nsIDocShell> parentDS(do_QueryInterface(parentAsItem));
6709 bool inOnLoadHandler = false;
6710 if (parentDS) {
6711 parentDS->GetIsExecutingOnLoadHandler(&inOnLoadHandler);
6712 }
6713 if (inOnLoadHandler) {
6714 // We're handling parent's load event listener, which causes
6715 // document.write in a subdocument.
6716 // Need to clear the session history for all child
6717 // docshells so that we can handle them like they would
6718 // all be added dynamically.
6719 nsCOMPtr<nsIDocShell> parent =
6720 do_QueryInterface(parentAsItem);
6721 if (parent) {
6722 bool oshe = false;
6723 nsCOMPtr<nsISHEntry> entry;
6724 parent->GetCurrentSHEntry(getter_AddRefs(entry), &oshe);
6725 static_cast<nsDocShell*>(parent.get())->
6726 ClearFrameHistory(entry);
6727 }
6728 }
6729
6730 // This is a document.write(). Get the made-up url
6731 // from the channel and store it in session history.
6732 // Pass false for aCloneChildren, since we're creating
6733 // a new DOM here.
6734 AddToSessionHistory(uri, wcwgChannel, nullptr, false,
6735 getter_AddRefs(mLSHE));
6736 SetCurrentURI(uri, aRequest, true, 0);
6737 // Save history state of the previous page
6738 PersistLayoutHistoryState();
6739 // We'll never get an Embed() for this load, so just go ahead
6740 // and SetHistoryEntry now.
6741 SetHistoryEntry(&mOSHE, mLSHE);
6742 }
6743
6744 }
6745 // Page has begun to load
6746 mBusyFlags = BUSY_FLAGS_BUSY | BUSY_FLAGS_BEFORE_PAGE_LOAD;
6747
6748 if ((aStateFlags & STATE_RESTORING) == 0) {
6749 // Show the progress cursor if the pref is set
6750 if (Preferences::GetBool("ui.use_activity_cursor", false)) {
6751 nsCOMPtr<nsIWidget> mainWidget;
6752 GetMainWidget(getter_AddRefs(mainWidget));
6753 if (mainWidget) {
6754 mainWidget->SetCursor(eCursor_spinning);
6755 }
6756 }
6757 }
6758 }
6759 else if ((~aStateFlags & (STATE_TRANSFERRING | STATE_IS_DOCUMENT)) == 0) {
6760 // Page is loading
6761 mBusyFlags = BUSY_FLAGS_BUSY | BUSY_FLAGS_PAGE_LOADING;
6762 }
6763 else if ((aStateFlags & STATE_STOP) && (aStateFlags & STATE_IS_NETWORK)) {
6764 // Page has finished loading
6765 mBusyFlags = BUSY_FLAGS_NONE;
6766
6767 // Hide the progress cursor if the pref is set
6768 if (Preferences::GetBool("ui.use_activity_cursor", false)) {
6769 nsCOMPtr<nsIWidget> mainWidget;
6770 GetMainWidget(getter_AddRefs(mainWidget));
6771 if (mainWidget) {
6772 mainWidget->SetCursor(eCursor_standard);
6773 }
6774 }
6775 }
6776 if ((~aStateFlags & (STATE_IS_DOCUMENT | STATE_STOP)) == 0) {
6777 nsCOMPtr<nsIWebProgress> webProgress =
6778 do_QueryInterface(GetAsSupports(this));
6779 // Is the document stop notification for this document?
6780 if (aProgress == webProgress.get()) {
6781 nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
6782 EndPageLoad(aProgress, channel, aStatus);
6783 }
6784 }
6785 // note that redirect state changes will go through here as well, but it
6786 // is better to handle those in OnRedirectStateChange where more
6787 // information is available.
6788 return NS_OK;
6789 }
6790
6791 NS_IMETHODIMP
6792 nsDocShell::OnLocationChange(nsIWebProgress * aProgress, nsIRequest * aRequest,
6793 nsIURI * aURI, uint32_t aFlags)
6794 {
6795 NS_NOTREACHED("notification excluded in AddProgressListener(...)");
6796 return NS_OK;
6797 }
6798
6799 void
6800 nsDocShell::OnRedirectStateChange(nsIChannel* aOldChannel,
6801 nsIChannel* aNewChannel,
6802 uint32_t aRedirectFlags,
6803 uint32_t aStateFlags)
6804 {
6805 NS_ASSERTION(aStateFlags & STATE_REDIRECTING,
6806 "Calling OnRedirectStateChange when there is no redirect");
6807 if (!(aStateFlags & STATE_IS_DOCUMENT))
6808 return; // not a toplevel document
6809
6810 nsCOMPtr<nsIURI> oldURI, newURI;
6811 aOldChannel->GetURI(getter_AddRefs(oldURI));
6812 aNewChannel->GetURI(getter_AddRefs(newURI));
6813 if (!oldURI || !newURI) {
6814 return;
6815 }
6816
6817 // Check if we have a redirect registered for this url.
6818 uint32_t appId;
6819 nsresult rv = GetAppId(&appId);
6820 if (NS_FAILED(rv)) {
6821 return;
6822 }
6823
6824 if (appId != nsIScriptSecurityManager::NO_APP_ID &&
6825 appId != nsIScriptSecurityManager::UNKNOWN_APP_ID) {
6826 nsCOMPtr<nsIAppsService> appsService =
6827 do_GetService(APPS_SERVICE_CONTRACTID);
6828 NS_ASSERTION(appsService, "No AppsService available");
6829 nsCOMPtr<nsIURI> redirect;
6830 rv = appsService->GetRedirect(appId, newURI, getter_AddRefs(redirect));
6831 if (NS_SUCCEEDED(rv) && redirect) {
6832 aNewChannel->Cancel(NS_BINDING_ABORTED);
6833 rv = LoadURI(redirect, nullptr, 0, false);
6834 if (NS_SUCCEEDED(rv)) {
6835 return;
6836 }
6837 }
6838 }
6839
6840 // Below a URI visit is saved (see AddURIVisit method doc).
6841 // The visit chain looks something like:
6842 // ...
6843 // Site N - 1
6844 // => Site N
6845 // (redirect to =>) Site N + 1 (we are here!)
6846
6847 // Get N - 1 and transition type
6848 nsCOMPtr<nsIURI> previousURI;
6849 uint32_t previousFlags = 0;
6850 ExtractLastVisit(aOldChannel, getter_AddRefs(previousURI), &previousFlags);
6851
6852 if (aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL ||
6853 ChannelIsPost(aOldChannel)) {
6854 // 1. Internal redirects are ignored because they are specific to the
6855 // channel implementation.
6856 // 2. POSTs are not saved by global history.
6857 //
6858 // Regardless, we need to propagate the previous visit to the new
6859 // channel.
6860 SaveLastVisit(aNewChannel, previousURI, previousFlags);
6861 }
6862 else {
6863 nsCOMPtr<nsIURI> referrer;
6864 // Treat referrer as null if there is an error getting it.
6865 (void)NS_GetReferrerFromChannel(aOldChannel,
6866 getter_AddRefs(referrer));
6867
6868 // Get the HTTP response code, if available.
6869 uint32_t responseStatus = 0;
6870 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aOldChannel);
6871 if (httpChannel) {
6872 (void)httpChannel->GetResponseStatus(&responseStatus);
6873 }
6874
6875 // Add visit N -1 => N
6876 AddURIVisit(oldURI, referrer, previousURI, previousFlags,
6877 responseStatus);
6878
6879 // Since N + 1 could be the final destination, we will not save N => N + 1
6880 // here. OnNewURI will do that, so we will cache it.
6881 SaveLastVisit(aNewChannel, oldURI, aRedirectFlags);
6882 }
6883
6884 // check if the new load should go through the application cache.
6885 nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
6886 do_QueryInterface(aNewChannel);
6887 if (appCacheChannel) {
6888 if (GeckoProcessType_Default != XRE_GetProcessType()) {
6889 // Permission will be checked in the parent process.
6890 appCacheChannel->SetChooseApplicationCache(true);
6891 } else {
6892 nsCOMPtr<nsIScriptSecurityManager> secMan =
6893 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
6894
6895 if (secMan) {
6896 nsCOMPtr<nsIPrincipal> principal;
6897 secMan->GetDocShellCodebasePrincipal(newURI, this, getter_AddRefs(principal));
6898 appCacheChannel->SetChooseApplicationCache(NS_ShouldCheckAppCache(principal,
6899 mInPrivateBrowsing));
6900 }
6901 }
6902 }
6903
6904 if (!(aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL) &&
6905 mLoadType & (LOAD_CMD_RELOAD | LOAD_CMD_HISTORY)) {
6906 mLoadType = LOAD_NORMAL_REPLACE;
6907 SetHistoryEntry(&mLSHE, nullptr);
6908 }
6909 }
6910
6911 NS_IMETHODIMP
6912 nsDocShell::OnStatusChange(nsIWebProgress * aWebProgress,
6913 nsIRequest * aRequest,
6914 nsresult aStatus, const char16_t * aMessage)
6915 {
6916 NS_NOTREACHED("notification excluded in AddProgressListener(...)");
6917 return NS_OK;
6918 }
6919
6920 NS_IMETHODIMP
6921 nsDocShell::OnSecurityChange(nsIWebProgress * aWebProgress,
6922 nsIRequest * aRequest, uint32_t state)
6923 {
6924 NS_NOTREACHED("notification excluded in AddProgressListener(...)");
6925 return NS_OK;
6926 }
6927
6928
6929 nsresult
6930 nsDocShell::EndPageLoad(nsIWebProgress * aProgress,
6931 nsIChannel * aChannel, nsresult aStatus)
6932 {
6933 if(!aChannel)
6934 return NS_ERROR_NULL_POINTER;
6935
6936 MOZ_EVENT_TRACER_DONE(this, "docshell::pageload");
6937
6938 nsCOMPtr<nsIURI> url;
6939 nsresult rv = aChannel->GetURI(getter_AddRefs(url));
6940 if (NS_FAILED(rv)) return rv;
6941
6942 nsCOMPtr<nsITimedChannel> timingChannel =
6943 do_QueryInterface(aChannel);
6944 if (timingChannel) {
6945 TimeStamp channelCreationTime;
6946 rv = timingChannel->GetChannelCreation(&channelCreationTime);
6947 if (NS_SUCCEEDED(rv) && !channelCreationTime.IsNull()) {
6948 Telemetry::AccumulateTimeDelta(
6949 Telemetry::TOTAL_CONTENT_PAGE_LOAD_TIME,
6950 channelCreationTime);
6951 nsCOMPtr<nsPILoadGroupInternal> internalLoadGroup =
6952 do_QueryInterface(mLoadGroup);
6953 if (internalLoadGroup)
6954 internalLoadGroup->OnEndPageLoad(aChannel);
6955 }
6956 }
6957
6958 // Timing is picked up by the window, we don't need it anymore
6959 mTiming = nullptr;
6960
6961 // clean up reload state for meta charset
6962 if (eCharsetReloadRequested == mCharsetReloadState)
6963 mCharsetReloadState = eCharsetReloadStopOrigional;
6964 else
6965 mCharsetReloadState = eCharsetReloadInit;
6966
6967 // Save a pointer to the currently-loading history entry.
6968 // nsDocShell::EndPageLoad will clear mLSHE, but we may need this history
6969 // entry further down in this method.
6970 nsCOMPtr<nsISHEntry> loadingSHE = mLSHE;
6971
6972 //
6973 // one of many safeguards that prevent death and destruction if
6974 // someone is so very very rude as to bring this window down
6975 // during this load handler.
6976 //
6977 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
6978
6979 // Notify the ContentViewer that the Document has finished loading. This
6980 // will cause any OnLoad(...) and PopState(...) handlers to fire.
6981 if (!mEODForCurrentDocument && mContentViewer) {
6982 mIsExecutingOnLoadHandler = true;
6983 mContentViewer->LoadComplete(aStatus);
6984 mIsExecutingOnLoadHandler = false;
6985
6986 mEODForCurrentDocument = true;
6987
6988 // If all documents have completed their loading
6989 // favor native event dispatch priorities
6990 // over performance
6991 if (--gNumberOfDocumentsLoading == 0) {
6992 // Hint to use normal native event dispatch priorities
6993 FavorPerformanceHint(false);
6994 }
6995 }
6996 /* Check if the httpChannel has any cache-control related response headers,
6997 * like no-store, no-cache. If so, update SHEntry so that
6998 * when a user goes back/forward to this page, we appropriately do
6999 * form value restoration or load from server.
7000 */
7001 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
7002 if (!httpChannel) // HttpChannel could be hiding underneath a Multipart channel.
7003 GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
7004
7005 if (httpChannel) {
7006 // figure out if SH should be saving layout state.
7007 bool discardLayoutState = ShouldDiscardLayoutState(httpChannel);
7008 if (mLSHE && discardLayoutState && (mLoadType & LOAD_CMD_NORMAL) &&
7009 (mLoadType != LOAD_BYPASS_HISTORY) && (mLoadType != LOAD_ERROR_PAGE))
7010 mLSHE->SetSaveLayoutStateFlag(false);
7011 }
7012
7013 // Clear mLSHE after calling the onLoadHandlers. This way, if the
7014 // onLoadHandler tries to load something different in
7015 // itself or one of its children, we can deal with it appropriately.
7016 if (mLSHE) {
7017 mLSHE->SetLoadType(nsIDocShellLoadInfo::loadHistory);
7018
7019 // Clear the mLSHE reference to indicate document loading is done one
7020 // way or another.
7021 SetHistoryEntry(&mLSHE, nullptr);
7022 }
7023 // if there's a refresh header in the channel, this method
7024 // will set it up for us.
7025 RefreshURIFromQueue();
7026
7027 // Test whether this is the top frame or a subframe
7028 bool isTopFrame = true;
7029 nsCOMPtr<nsIDocShellTreeItem> targetParentTreeItem;
7030 rv = GetSameTypeParent(getter_AddRefs(targetParentTreeItem));
7031 if (NS_SUCCEEDED(rv) && targetParentTreeItem) {
7032 isTopFrame = false;
7033 }
7034
7035 //
7036 // If the page load failed, then deal with the error condition...
7037 // Errors are handled as follows:
7038 // 1. Check to see if it's a file not found error or bad content
7039 // encoding error.
7040 // 2. Send the URI to a keyword server (if enabled)
7041 // 3. If the error was DNS failure, then add www and .com to the URI
7042 // (if appropriate).
7043 // 4. Throw an error dialog box...
7044 //
7045 if (url && NS_FAILED(aStatus)) {
7046 if (aStatus == NS_ERROR_FILE_NOT_FOUND ||
7047 aStatus == NS_ERROR_CORRUPTED_CONTENT ||
7048 aStatus == NS_ERROR_INVALID_CONTENT_ENCODING) {
7049 DisplayLoadError(aStatus, url, nullptr, aChannel);
7050 return NS_OK;
7051 }
7052
7053 if (sURIFixup) {
7054 //
7055 // Try and make an alternative URI from the old one
7056 //
7057 nsCOMPtr<nsIURI> newURI;
7058 nsCOMPtr<nsIInputStream> newPostData;
7059
7060 nsAutoCString oldSpec;
7061 url->GetSpec(oldSpec);
7062
7063 //
7064 // First try keyword fixup
7065 //
7066 if (aStatus == NS_ERROR_UNKNOWN_HOST && mAllowKeywordFixup) {
7067 bool keywordsEnabled =
7068 Preferences::GetBool("keyword.enabled", false);
7069
7070 nsAutoCString host;
7071 url->GetHost(host);
7072
7073 nsAutoCString scheme;
7074 url->GetScheme(scheme);
7075
7076 int32_t dotLoc = host.FindChar('.');
7077
7078 // we should only perform a keyword search under the following
7079 // conditions:
7080 // (0) Pref keyword.enabled is true
7081 // (1) the url scheme is http (or https)
7082 // (2) the url does not have a protocol scheme
7083 // If we don't enforce such a policy, then we end up doing
7084 // keyword searchs on urls we don't intend like imap, file,
7085 // mailbox, etc. This could lead to a security problem where we
7086 // send data to the keyword server that we shouldn't be.
7087 // Someone needs to clean up keywords in general so we can
7088 // determine on a per url basis if we want keywords
7089 // enabled...this is just a bandaid...
7090 if (keywordsEnabled && !scheme.IsEmpty() &&
7091 (scheme.Find("http") != 0)) {
7092 keywordsEnabled = false;
7093 }
7094
7095 if (keywordsEnabled && (kNotFound == dotLoc)) {
7096 // only send non-qualified hosts to the keyword server
7097 if (!mOriginalUriString.IsEmpty()) {
7098 sURIFixup->KeywordToURI(mOriginalUriString,
7099 getter_AddRefs(newPostData),
7100 getter_AddRefs(newURI));
7101 }
7102 else {
7103 //
7104 // If this string was passed through nsStandardURL by
7105 // chance, then it may have been converted from UTF-8 to
7106 // ACE, which would result in a completely bogus keyword
7107 // query. Here we try to recover the original Unicode
7108 // value, but this is not 100% correct since the value may
7109 // have been normalized per the IDN normalization rules.
7110 //
7111 // Since we don't have access to the exact original string
7112 // that was entered by the user, this will just have to do.
7113 bool isACE;
7114 nsAutoCString utf8Host;
7115 nsCOMPtr<nsIIDNService> idnSrv =
7116 do_GetService(NS_IDNSERVICE_CONTRACTID);
7117 if (idnSrv &&
7118 NS_SUCCEEDED(idnSrv->IsACE(host, &isACE)) && isACE &&
7119 NS_SUCCEEDED(idnSrv->ConvertACEtoUTF8(host, utf8Host))) {
7120 sURIFixup->KeywordToURI(utf8Host,
7121 getter_AddRefs(newPostData),
7122 getter_AddRefs(newURI));
7123 } else {
7124 sURIFixup->KeywordToURI(host,
7125 getter_AddRefs(newPostData),
7126 getter_AddRefs(newURI));
7127 }
7128 }
7129 } // end keywordsEnabled
7130 }
7131
7132 //
7133 // Now try change the address, e.g. turn http://foo into
7134 // http://www.foo.com
7135 //
7136 if (aStatus == NS_ERROR_UNKNOWN_HOST ||
7137 aStatus == NS_ERROR_NET_RESET) {
7138 bool doCreateAlternate = true;
7139
7140 // Skip fixup for anything except a normal document load
7141 // operation on the topframe.
7142
7143 if (mLoadType != LOAD_NORMAL || !isTopFrame) {
7144 doCreateAlternate = false;
7145 }
7146 else {
7147 // Test if keyword lookup produced a new URI or not
7148 if (newURI) {
7149 bool sameURI = false;
7150 url->Equals(newURI, &sameURI);
7151 if (!sameURI) {
7152 // Keyword lookup made a new URI so no need to try
7153 // an alternate one.
7154 doCreateAlternate = false;
7155 }
7156 }
7157 }
7158 if (doCreateAlternate) {
7159 newURI = nullptr;
7160 newPostData = nullptr;
7161 sURIFixup->CreateFixupURI(oldSpec,
7162 nsIURIFixup::FIXUP_FLAGS_MAKE_ALTERNATE_URI,
7163 getter_AddRefs(newPostData),
7164 getter_AddRefs(newURI));
7165 }
7166 }
7167
7168 // Did we make a new URI that is different to the old one? If so
7169 // load it.
7170 //
7171 if (newURI) {
7172 // Make sure the new URI is different from the old one,
7173 // otherwise there's little point trying to load it again.
7174 bool sameURI = false;
7175 url->Equals(newURI, &sameURI);
7176 if (!sameURI) {
7177 nsAutoCString newSpec;
7178 newURI->GetSpec(newSpec);
7179 NS_ConvertUTF8toUTF16 newSpecW(newSpec);
7180
7181 return LoadURI(newSpecW.get(), // URI string
7182 LOAD_FLAGS_NONE, // Load flags
7183 nullptr, // Referring URI
7184 newPostData, // Post data stream
7185 nullptr); // Headers stream
7186 }
7187 }
7188 }
7189
7190 // Well, fixup didn't work :-(
7191 // It is time to throw an error dialog box, and be done with it...
7192
7193 // Errors to be shown only on top-level frames
7194 if ((aStatus == NS_ERROR_UNKNOWN_HOST ||
7195 aStatus == NS_ERROR_CONNECTION_REFUSED ||
7196 aStatus == NS_ERROR_UNKNOWN_PROXY_HOST ||
7197 aStatus == NS_ERROR_PROXY_CONNECTION_REFUSED) &&
7198 (isTopFrame || UseErrorPages())) {
7199 DisplayLoadError(aStatus, url, nullptr, aChannel);
7200 }
7201 // Errors to be shown for any frame
7202 else if (aStatus == NS_ERROR_NET_TIMEOUT ||
7203 aStatus == NS_ERROR_REDIRECT_LOOP ||
7204 aStatus == NS_ERROR_UNKNOWN_SOCKET_TYPE ||
7205 aStatus == NS_ERROR_NET_INTERRUPT ||
7206 aStatus == NS_ERROR_NET_RESET ||
7207 aStatus == NS_ERROR_OFFLINE ||
7208 aStatus == NS_ERROR_MALWARE_URI ||
7209 aStatus == NS_ERROR_PHISHING_URI ||
7210 aStatus == NS_ERROR_UNSAFE_CONTENT_TYPE ||
7211 aStatus == NS_ERROR_REMOTE_XUL ||
7212 aStatus == NS_ERROR_OFFLINE ||
7213 NS_ERROR_GET_MODULE(aStatus) == NS_ERROR_MODULE_SECURITY) {
7214 DisplayLoadError(aStatus, url, nullptr, aChannel);
7215 }
7216 else if (aStatus == NS_ERROR_DOCUMENT_NOT_CACHED) {
7217 // Non-caching channels will simply return NS_ERROR_OFFLINE.
7218 // Caching channels would have to look at their flags to work
7219 // out which error to return. Or we can fix up the error here.
7220 if (!(mLoadType & LOAD_CMD_HISTORY))
7221 aStatus = NS_ERROR_OFFLINE;
7222 DisplayLoadError(aStatus, url, nullptr, aChannel);
7223 }
7224 } // if we have a host
7225 else if (url && NS_SUCCEEDED(aStatus)) {
7226 mozilla::net::SeerLearnRedirect(url, aChannel, this);
7227 }
7228
7229 return NS_OK;
7230 }
7231
7232
7233 //*****************************************************************************
7234 // nsDocShell: Content Viewer Management
7235 //*****************************************************************************
7236
7237 NS_IMETHODIMP
7238 nsDocShell::EnsureContentViewer()
7239 {
7240 if (mContentViewer)
7241 return NS_OK;
7242 if (mIsBeingDestroyed)
7243 return NS_ERROR_FAILURE;
7244
7245 nsCOMPtr<nsIURI> baseURI;
7246 nsIPrincipal* principal = GetInheritedPrincipal(false);
7247 nsCOMPtr<nsIDocShellTreeItem> parentItem;
7248 GetSameTypeParent(getter_AddRefs(parentItem));
7249 if (parentItem) {
7250 nsCOMPtr<nsPIDOMWindow> domWin = do_GetInterface(GetAsSupports(this));
7251 if (domWin) {
7252 nsCOMPtr<nsIContent> parentContent =
7253 do_QueryInterface(domWin->GetFrameElementInternal());
7254 if (parentContent) {
7255 baseURI = parentContent->GetBaseURI();
7256 }
7257 }
7258 }
7259
7260 nsresult rv = CreateAboutBlankContentViewer(principal, baseURI);
7261
7262 if (NS_SUCCEEDED(rv)) {
7263 nsCOMPtr<nsIDocument> doc(do_GetInterface(GetAsSupports(this)));
7264 NS_ASSERTION(doc,
7265 "Should have doc if CreateAboutBlankContentViewer "
7266 "succeeded!");
7267
7268 doc->SetIsInitialDocument(true);
7269 }
7270
7271 return rv;
7272 }
7273
7274 nsresult
7275 nsDocShell::CreateAboutBlankContentViewer(nsIPrincipal* aPrincipal,
7276 nsIURI* aBaseURI,
7277 bool aTryToSaveOldPresentation)
7278 {
7279 nsCOMPtr<nsIDocument> blankDoc;
7280 nsCOMPtr<nsIContentViewer> viewer;
7281 nsresult rv = NS_ERROR_FAILURE;
7282
7283 /* mCreatingDocument should never be true at this point. However, it's
7284 a theoretical possibility. We want to know about it and make it stop,
7285 and this sounds like a job for an assertion. */
7286 NS_ASSERTION(!mCreatingDocument, "infinite(?) loop creating document averted");
7287 if (mCreatingDocument)
7288 return NS_ERROR_FAILURE;
7289
7290 mCreatingDocument = true;
7291
7292 // mContentViewer->PermitUnload may release |this| docshell.
7293 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
7294
7295 // Make sure timing is created. But first record whether we had it
7296 // already, so we don't clobber the timing for an in-progress load.
7297 bool hadTiming = mTiming;
7298 MaybeInitTiming();
7299 if (mContentViewer) {
7300 // We've got a content viewer already. Make sure the user
7301 // permits us to discard the current document and replace it
7302 // with about:blank. And also ensure we fire the unload events
7303 // in the current document.
7304
7305 // Unload gets fired first for
7306 // document loaded from the session history.
7307 mTiming->NotifyBeforeUnload();
7308
7309 bool okToUnload;
7310 rv = mContentViewer->PermitUnload(false, &okToUnload);
7311
7312 if (NS_SUCCEEDED(rv) && !okToUnload) {
7313 // The user chose not to unload the page, interrupt the load.
7314 return NS_ERROR_FAILURE;
7315 }
7316
7317 mSavingOldViewer = aTryToSaveOldPresentation &&
7318 CanSavePresentation(LOAD_NORMAL, nullptr, nullptr);
7319
7320 if (mTiming) {
7321 mTiming->NotifyUnloadAccepted(mCurrentURI);
7322 }
7323
7324 // Make sure to blow away our mLoadingURI just in case. No loads
7325 // from inside this pagehide.
7326 mLoadingURI = nullptr;
7327
7328 // Stop any in-progress loading, so that we don't accidentally trigger any
7329 // PageShow notifications from Embed() interrupting our loading below.
7330 Stop();
7331
7332 // Notify the current document that it is about to be unloaded!!
7333 //
7334 // It is important to fire the unload() notification *before* any state
7335 // is changed within the DocShell - otherwise, javascript will get the
7336 // wrong information :-(
7337 //
7338 (void) FirePageHideNotification(!mSavingOldViewer);
7339 }
7340
7341 // Now make sure we don't think we're in the middle of firing unload after
7342 // this point. This will make us fire unload when the about:blank document
7343 // unloads... but that's ok, more or less. Would be nice if it fired load
7344 // too, of course.
7345 mFiredUnloadEvent = false;
7346
7347 nsCOMPtr<nsIDocumentLoaderFactory> docFactory =
7348 nsContentUtils::FindInternalContentViewer("text/html");
7349
7350 if (docFactory) {
7351 nsCOMPtr<nsIPrincipal> principal;
7352 if (mSandboxFlags & SANDBOXED_ORIGIN) {
7353 principal = do_CreateInstance("@mozilla.org/nullprincipal;1");
7354 } else {
7355 principal = aPrincipal;
7356 }
7357 // generate (about:blank) document to load
7358 docFactory->CreateBlankDocument(mLoadGroup, principal,
7359 getter_AddRefs(blankDoc));
7360 if (blankDoc) {
7361 // Hack: set the base URI manually, since this document never
7362 // got Reset() with a channel.
7363 blankDoc->SetBaseURI(aBaseURI);
7364
7365 blankDoc->SetContainer(this);
7366
7367 // Copy our sandbox flags to the document. These are immutable
7368 // after being set here.
7369 blankDoc->SetSandboxFlags(mSandboxFlags);
7370
7371 // create a content viewer for us and the new document
7372 docFactory->CreateInstanceForDocument(NS_ISUPPORTS_CAST(nsIDocShell *, this),
7373 blankDoc, "view", getter_AddRefs(viewer));
7374
7375 // hook 'em up
7376 if (viewer) {
7377 viewer->SetContainer(this);
7378 Embed(viewer, "", 0);
7379
7380 SetCurrentURI(blankDoc->GetDocumentURI(), nullptr, true, 0);
7381 rv = mIsBeingDestroyed ? NS_ERROR_NOT_AVAILABLE : NS_OK;
7382 }
7383 }
7384 }
7385 mCreatingDocument = false;
7386
7387 // The transient about:blank viewer doesn't have a session history entry.
7388 SetHistoryEntry(&mOSHE, nullptr);
7389
7390 // Clear out our mTiming like we would in EndPageLoad, if we didn't
7391 // have one before entering this function.
7392 if (!hadTiming) {
7393 mTiming = nullptr;
7394 }
7395
7396 return rv;
7397 }
7398
7399 NS_IMETHODIMP
7400 nsDocShell::CreateAboutBlankContentViewer(nsIPrincipal *aPrincipal)
7401 {
7402 return CreateAboutBlankContentViewer(aPrincipal, nullptr);
7403 }
7404
7405 bool
7406 nsDocShell::CanSavePresentation(uint32_t aLoadType,
7407 nsIRequest *aNewRequest,
7408 nsIDocument *aNewDocument)
7409 {
7410 if (!mOSHE)
7411 return false; // no entry to save into
7412
7413 nsCOMPtr<nsIContentViewer> viewer;
7414 mOSHE->GetContentViewer(getter_AddRefs(viewer));
7415 if (viewer) {
7416 NS_WARNING("mOSHE already has a content viewer!");
7417 return false;
7418 }
7419
7420 // Only save presentation for "normal" loads and link loads. Anything else
7421 // probably wants to refetch the page, so caching the old presentation
7422 // would be incorrect.
7423 if (aLoadType != LOAD_NORMAL &&
7424 aLoadType != LOAD_HISTORY &&
7425 aLoadType != LOAD_LINK &&
7426 aLoadType != LOAD_STOP_CONTENT &&
7427 aLoadType != LOAD_STOP_CONTENT_AND_REPLACE &&
7428 aLoadType != LOAD_ERROR_PAGE)
7429 return false;
7430
7431 // If the session history entry has the saveLayoutState flag set to false,
7432 // then we should not cache the presentation.
7433 bool canSaveState;
7434 mOSHE->GetSaveLayoutStateFlag(&canSaveState);
7435 if (!canSaveState)
7436 return false;
7437
7438 // If the document is not done loading, don't cache it.
7439 if (!mScriptGlobal || mScriptGlobal->IsLoading())
7440 return false;
7441
7442 if (mScriptGlobal->WouldReuseInnerWindow(aNewDocument))
7443 return false;
7444
7445 // Avoid doing the work of saving the presentation state in the case where
7446 // the content viewer cache is disabled.
7447 if (nsSHistory::GetMaxTotalViewers() == 0)
7448 return false;
7449
7450 // Don't cache the content viewer if we're in a subframe and the subframe
7451 // pref is disabled.
7452 bool cacheFrames =
7453 Preferences::GetBool("browser.sessionhistory.cache_subframes",
7454 false);
7455 if (!cacheFrames) {
7456 nsCOMPtr<nsIDocShellTreeItem> root;
7457 GetSameTypeParent(getter_AddRefs(root));
7458 if (root && root != this) {
7459 return false; // this is a subframe load
7460 }
7461 }
7462
7463 // If the document does not want its presentation cached, then don't.
7464 nsCOMPtr<nsIDocument> doc = mScriptGlobal->GetExtantDoc();
7465 return doc && doc->CanSavePresentation(aNewRequest);
7466 }
7467
7468 void
7469 nsDocShell::ReattachEditorToWindow(nsISHEntry *aSHEntry)
7470 {
7471 NS_ASSERTION(!mEditorData,
7472 "Why reattach an editor when we already have one?");
7473 NS_ASSERTION(aSHEntry && aSHEntry->HasDetachedEditor(),
7474 "Reattaching when there's not a detached editor.");
7475
7476 if (mEditorData || !aSHEntry)
7477 return;
7478
7479 mEditorData = aSHEntry->ForgetEditorData();
7480 if (mEditorData) {
7481 #ifdef DEBUG
7482 nsresult rv =
7483 #endif
7484 mEditorData->ReattachToWindow(this);
7485 NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to reattach editing session");
7486 }
7487 }
7488
7489 void
7490 nsDocShell::DetachEditorFromWindow()
7491 {
7492 if (!mEditorData || mEditorData->WaitingForLoad()) {
7493 // If there's nothing to detach, or if the editor data is actually set
7494 // up for the _new_ page that's coming in, don't detach.
7495 return;
7496 }
7497
7498 NS_ASSERTION(!mOSHE || !mOSHE->HasDetachedEditor(),
7499 "Detaching editor when it's already detached.");
7500
7501 nsresult res = mEditorData->DetachFromWindow();
7502 NS_ASSERTION(NS_SUCCEEDED(res), "Failed to detach editor");
7503
7504 if (NS_SUCCEEDED(res)) {
7505 // Make mOSHE hold the owning ref to the editor data.
7506 if (mOSHE)
7507 mOSHE->SetEditorData(mEditorData.forget());
7508 else
7509 mEditorData = nullptr;
7510 }
7511
7512 #ifdef DEBUG
7513 {
7514 bool isEditable;
7515 GetEditable(&isEditable);
7516 NS_ASSERTION(!isEditable,
7517 "Window is still editable after detaching editor.");
7518 }
7519 #endif // DEBUG
7520 }
7521
7522 nsresult
7523 nsDocShell::CaptureState()
7524 {
7525 if (!mOSHE || mOSHE == mLSHE) {
7526 // No entry to save into, or we're replacing the existing entry.
7527 return NS_ERROR_FAILURE;
7528 }
7529
7530 if (!mScriptGlobal)
7531 return NS_ERROR_FAILURE;
7532
7533 nsCOMPtr<nsISupports> windowState = mScriptGlobal->SaveWindowState();
7534 NS_ENSURE_TRUE(windowState, NS_ERROR_FAILURE);
7535
7536 #ifdef DEBUG_PAGE_CACHE
7537 nsCOMPtr<nsIURI> uri;
7538 mOSHE->GetURI(getter_AddRefs(uri));
7539 nsAutoCString spec;
7540 if (uri)
7541 uri->GetSpec(spec);
7542 printf("Saving presentation into session history\n");
7543 printf(" SH URI: %s\n", spec.get());
7544 #endif
7545
7546 nsresult rv = mOSHE->SetWindowState(windowState);
7547 NS_ENSURE_SUCCESS(rv, rv);
7548
7549 // Suspend refresh URIs and save off the timer queue
7550 rv = mOSHE->SetRefreshURIList(mSavedRefreshURIList);
7551 NS_ENSURE_SUCCESS(rv, rv);
7552
7553 // Capture the current content viewer bounds.
7554 if (mContentViewer) {
7555 nsIntRect bounds;
7556 mContentViewer->GetBounds(bounds);
7557 rv = mOSHE->SetViewerBounds(bounds);
7558 NS_ENSURE_SUCCESS(rv, rv);
7559 }
7560
7561 // Capture the docshell hierarchy.
7562 mOSHE->ClearChildShells();
7563
7564 uint32_t childCount = mChildList.Length();
7565 for (uint32_t i = 0; i < childCount; ++i) {
7566 nsCOMPtr<nsIDocShellTreeItem> childShell = do_QueryInterface(ChildAt(i));
7567 NS_ASSERTION(childShell, "null child shell");
7568
7569 mOSHE->AddChildShell(childShell);
7570 }
7571
7572 return NS_OK;
7573 }
7574
7575 NS_IMETHODIMP
7576 nsDocShell::RestorePresentationEvent::Run()
7577 {
7578 if (mDocShell && NS_FAILED(mDocShell->RestoreFromHistory()))
7579 NS_WARNING("RestoreFromHistory failed");
7580 return NS_OK;
7581 }
7582
7583 NS_IMETHODIMP
7584 nsDocShell::BeginRestore(nsIContentViewer *aContentViewer, bool aTop)
7585 {
7586 nsresult rv;
7587 if (!aContentViewer) {
7588 rv = EnsureContentViewer();
7589 NS_ENSURE_SUCCESS(rv, rv);
7590
7591 aContentViewer = mContentViewer;
7592 }
7593
7594 // Dispatch events for restoring the presentation. We try to simulate
7595 // the progress notifications loading the document would cause, so we add
7596 // the document's channel to the loadgroup to initiate stateChange
7597 // notifications.
7598
7599 nsCOMPtr<nsIDOMDocument> domDoc;
7600 aContentViewer->GetDOMDocument(getter_AddRefs(domDoc));
7601 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
7602 if (doc) {
7603 nsIChannel *channel = doc->GetChannel();
7604 if (channel) {
7605 mEODForCurrentDocument = false;
7606 mIsRestoringDocument = true;
7607 mLoadGroup->AddRequest(channel, nullptr);
7608 mIsRestoringDocument = false;
7609 }
7610 }
7611
7612 if (!aTop) {
7613 // This point corresponds to us having gotten OnStartRequest or
7614 // STATE_START, so do the same thing that CreateContentViewer does at
7615 // this point to ensure that unload/pagehide events for this document
7616 // will fire when it's unloaded again.
7617 mFiredUnloadEvent = false;
7618
7619 // For non-top frames, there is no notion of making sure that the
7620 // previous document is in the domwindow when STATE_START notifications
7621 // happen. We can just call BeginRestore for all of the child shells
7622 // now.
7623 rv = BeginRestoreChildren();
7624 NS_ENSURE_SUCCESS(rv, rv);
7625 }
7626
7627 return NS_OK;
7628 }
7629
7630 nsresult
7631 nsDocShell::BeginRestoreChildren()
7632 {
7633 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
7634 while (iter.HasMore()) {
7635 nsCOMPtr<nsIDocShell> child = do_QueryObject(iter.GetNext());
7636 if (child) {
7637 nsresult rv = child->BeginRestore(nullptr, false);
7638 NS_ENSURE_SUCCESS(rv, rv);
7639 }
7640 }
7641 return NS_OK;
7642 }
7643
7644 NS_IMETHODIMP
7645 nsDocShell::FinishRestore()
7646 {
7647 // First we call finishRestore() on our children. In the simulated load,
7648 // all of the child frames finish loading before the main document.
7649
7650 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
7651 while (iter.HasMore()) {
7652 nsCOMPtr<nsIDocShell> child = do_QueryObject(iter.GetNext());
7653 if (child) {
7654 child->FinishRestore();
7655 }
7656 }
7657
7658 if (mOSHE && mOSHE->HasDetachedEditor()) {
7659 ReattachEditorToWindow(mOSHE);
7660 }
7661
7662 nsCOMPtr<nsIDocument> doc = do_GetInterface(GetAsSupports(this));
7663 if (doc) {
7664 // Finally, we remove the request from the loadgroup. This will
7665 // cause onStateChange(STATE_STOP) to fire, which will fire the
7666 // pageshow event to the chrome.
7667
7668 nsIChannel *channel = doc->GetChannel();
7669 if (channel) {
7670 mIsRestoringDocument = true;
7671 mLoadGroup->RemoveRequest(channel, nullptr, NS_OK);
7672 mIsRestoringDocument = false;
7673 }
7674 }
7675
7676 return NS_OK;
7677 }
7678
7679 NS_IMETHODIMP
7680 nsDocShell::GetRestoringDocument(bool *aRestoring)
7681 {
7682 *aRestoring = mIsRestoringDocument;
7683 return NS_OK;
7684 }
7685
7686 nsresult
7687 nsDocShell::RestorePresentation(nsISHEntry *aSHEntry, bool *aRestoring)
7688 {
7689 NS_ASSERTION(mLoadType & LOAD_CMD_HISTORY,
7690 "RestorePresentation should only be called for history loads");
7691
7692 nsCOMPtr<nsIContentViewer> viewer;
7693 aSHEntry->GetContentViewer(getter_AddRefs(viewer));
7694
7695 #ifdef DEBUG_PAGE_CACHE
7696 nsCOMPtr<nsIURI> uri;
7697 aSHEntry->GetURI(getter_AddRefs(uri));
7698
7699 nsAutoCString spec;
7700 if (uri)
7701 uri->GetSpec(spec);
7702 #endif
7703
7704 *aRestoring = false;
7705
7706 if (!viewer) {
7707 #ifdef DEBUG_PAGE_CACHE
7708 printf("no saved presentation for uri: %s\n", spec.get());
7709 #endif
7710 return NS_OK;
7711 }
7712
7713 // We need to make sure the content viewer's container is this docshell.
7714 // In subframe navigation, it's possible for the docshell that the
7715 // content viewer was originally loaded into to be replaced with a
7716 // different one. We don't currently support restoring the presentation
7717 // in that case.
7718
7719 nsCOMPtr<nsIDocShell> container;
7720 viewer->GetContainer(getter_AddRefs(container));
7721 if (!::SameCOMIdentity(container, GetAsSupports(this))) {
7722 #ifdef DEBUG_PAGE_CACHE
7723 printf("No valid container, clearing presentation\n");
7724 #endif
7725 aSHEntry->SetContentViewer(nullptr);
7726 return NS_ERROR_FAILURE;
7727 }
7728
7729 NS_ASSERTION(mContentViewer != viewer, "Restoring existing presentation");
7730
7731 #ifdef DEBUG_PAGE_CACHE
7732 printf("restoring presentation from session history: %s\n", spec.get());
7733 #endif
7734
7735 SetHistoryEntry(&mLSHE, aSHEntry);
7736
7737 // Add the request to our load group. We do this before swapping out
7738 // the content viewers so that consumers of STATE_START can access
7739 // the old document. We only deal with the toplevel load at this time --
7740 // to be consistent with normal document loading, subframes cannot start
7741 // loading until after data arrives, which is after STATE_START completes.
7742
7743 BeginRestore(viewer, true);
7744
7745 // Post an event that will remove the request after we've returned
7746 // to the event loop. This mimics the way it is called by nsIChannel
7747 // implementations.
7748
7749 // Revoke any pending restore (just in case)
7750 NS_ASSERTION(!mRestorePresentationEvent.IsPending(),
7751 "should only have one RestorePresentationEvent");
7752 mRestorePresentationEvent.Revoke();
7753
7754 nsRefPtr<RestorePresentationEvent> evt = new RestorePresentationEvent(this);
7755 nsresult rv = NS_DispatchToCurrentThread(evt);
7756 if (NS_SUCCEEDED(rv)) {
7757 mRestorePresentationEvent = evt.get();
7758 // The rest of the restore processing will happen on our event
7759 // callback.
7760 *aRestoring = true;
7761 }
7762
7763 return rv;
7764 }
7765
7766 nsresult
7767 nsDocShell::RestoreFromHistory()
7768 {
7769 mRestorePresentationEvent.Forget();
7770
7771 // This section of code follows the same ordering as CreateContentViewer.
7772 if (!mLSHE)
7773 return NS_ERROR_FAILURE;
7774
7775 nsCOMPtr<nsIContentViewer> viewer;
7776 mLSHE->GetContentViewer(getter_AddRefs(viewer));
7777 if (!viewer)
7778 return NS_ERROR_FAILURE;
7779
7780 if (mSavingOldViewer) {
7781 // We determined that it was safe to cache the document presentation
7782 // at the time we initiated the new load. We need to check whether
7783 // it's still safe to do so, since there may have been DOM mutations
7784 // or new requests initiated.
7785 nsCOMPtr<nsIDOMDocument> domDoc;
7786 viewer->GetDOMDocument(getter_AddRefs(domDoc));
7787 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
7788 nsIRequest *request = nullptr;
7789 if (doc)
7790 request = doc->GetChannel();
7791 mSavingOldViewer = CanSavePresentation(mLoadType, request, doc);
7792 }
7793
7794 nsCOMPtr<nsIMarkupDocumentViewer> oldMUDV(
7795 do_QueryInterface(mContentViewer));
7796 nsCOMPtr<nsIMarkupDocumentViewer> newMUDV(
7797 do_QueryInterface(viewer));
7798 int32_t minFontSize = 0;
7799 float textZoom = 1.0f;
7800 float pageZoom = 1.0f;
7801 bool styleDisabled = false;
7802 if (oldMUDV && newMUDV) {
7803 oldMUDV->GetMinFontSize(&minFontSize);
7804 oldMUDV->GetTextZoom(&textZoom);
7805 oldMUDV->GetFullZoom(&pageZoom);
7806 oldMUDV->GetAuthorStyleDisabled(&styleDisabled);
7807 }
7808
7809 // Protect against mLSHE going away via a load triggered from
7810 // pagehide or unload.
7811 nsCOMPtr<nsISHEntry> origLSHE = mLSHE;
7812
7813 // Make sure to blow away our mLoadingURI just in case. No loads
7814 // from inside this pagehide.
7815 mLoadingURI = nullptr;
7816
7817 // Notify the old content viewer that it's being hidden.
7818 FirePageHideNotification(!mSavingOldViewer);
7819
7820 // If mLSHE was changed as a result of the pagehide event, then
7821 // something else was loaded. Don't finish restoring.
7822 if (mLSHE != origLSHE)
7823 return NS_OK;
7824
7825 // Set mFiredUnloadEvent = false so that the unload handler for the
7826 // *new* document will fire.
7827 mFiredUnloadEvent = false;
7828
7829 mURIResultedInDocument = true;
7830 nsCOMPtr<nsISHistory> rootSH;
7831 GetRootSessionHistory(getter_AddRefs(rootSH));
7832 if (rootSH) {
7833 nsCOMPtr<nsISHistoryInternal> hist = do_QueryInterface(rootSH);
7834 rootSH->GetIndex(&mPreviousTransIndex);
7835 hist->UpdateIndex();
7836 rootSH->GetIndex(&mLoadedTransIndex);
7837 #ifdef DEBUG_PAGE_CACHE
7838 printf("Previous index: %d, Loaded index: %d\n\n", mPreviousTransIndex,
7839 mLoadedTransIndex);
7840 #endif
7841 }
7842
7843 // Rather than call Embed(), we will retrieve the viewer from the session
7844 // history entry and swap it in.
7845 // XXX can we refactor this so that we can just call Embed()?
7846 PersistLayoutHistoryState();
7847 nsresult rv;
7848 if (mContentViewer) {
7849 if (mSavingOldViewer && NS_FAILED(CaptureState())) {
7850 if (mOSHE) {
7851 mOSHE->SyncPresentationState();
7852 }
7853 mSavingOldViewer = false;
7854 }
7855 }
7856
7857 mSavedRefreshURIList = nullptr;
7858
7859 // In cases where we use a transient about:blank viewer between loads,
7860 // we never show the transient viewer, so _its_ previous viewer is never
7861 // unhooked from the view hierarchy. Destroy any such previous viewer now,
7862 // before we grab the root view sibling, so that we don't grab a view
7863 // that's about to go away.
7864
7865 if (mContentViewer) {
7866 nsCOMPtr<nsIContentViewer> previousViewer;
7867 mContentViewer->GetPreviousViewer(getter_AddRefs(previousViewer));
7868 if (previousViewer) {
7869 mContentViewer->SetPreviousViewer(nullptr);
7870 previousViewer->Destroy();
7871 }
7872 }
7873
7874 // Save off the root view's parent and sibling so that we can insert the
7875 // new content viewer's root view at the same position. Also save the
7876 // bounds of the root view's widget.
7877
7878 nsView *rootViewSibling = nullptr, *rootViewParent = nullptr;
7879 nsIntRect newBounds(0, 0, 0, 0);
7880
7881 nsCOMPtr<nsIPresShell> oldPresShell = GetPresShell();
7882 if (oldPresShell) {
7883 nsViewManager *vm = oldPresShell->GetViewManager();
7884 if (vm) {
7885 nsView *oldRootView = vm->GetRootView();
7886
7887 if (oldRootView) {
7888 rootViewSibling = oldRootView->GetNextSibling();
7889 rootViewParent = oldRootView->GetParent();
7890
7891 mContentViewer->GetBounds(newBounds);
7892 }
7893 }
7894 }
7895
7896 nsCOMPtr<nsIContent> container;
7897 nsCOMPtr<nsIDocument> sibling;
7898 if (rootViewParent && rootViewParent->GetParent()) {
7899 nsIFrame* frame = rootViewParent->GetParent()->GetFrame();
7900 container = frame ? frame->GetContent() : nullptr;
7901 }
7902 if (rootViewSibling) {
7903 nsIFrame *frame = rootViewSibling->GetFrame();
7904 sibling = frame ? frame->PresContext()->PresShell()->GetDocument() : nullptr;
7905 }
7906
7907 // Transfer ownership to mContentViewer. By ensuring that either the
7908 // docshell or the session history, but not both, have references to the
7909 // content viewer, we prevent the viewer from being torn down after
7910 // Destroy() is called.
7911
7912 if (mContentViewer) {
7913 mContentViewer->Close(mSavingOldViewer ? mOSHE.get() : nullptr);
7914 viewer->SetPreviousViewer(mContentViewer);
7915 }
7916 if (mOSHE && (!mContentViewer || !mSavingOldViewer)) {
7917 // We don't plan to save a viewer in mOSHE; tell it to drop
7918 // any other state it's holding.
7919 mOSHE->SyncPresentationState();
7920 }
7921
7922 // Order the mContentViewer setup just like Embed does.
7923 mContentViewer = nullptr;
7924
7925 // Now that we're about to switch documents, forget all of our children.
7926 // Note that we cached them as needed up in CaptureState above.
7927 DestroyChildren();
7928
7929 mContentViewer.swap(viewer);
7930
7931 // Grab all of the related presentation from the SHEntry now.
7932 // Clearing the viewer from the SHEntry will clear all of this state.
7933 nsCOMPtr<nsISupports> windowState;
7934 mLSHE->GetWindowState(getter_AddRefs(windowState));
7935 mLSHE->SetWindowState(nullptr);
7936
7937 bool sticky;
7938 mLSHE->GetSticky(&sticky);
7939
7940 nsCOMPtr<nsIDOMDocument> domDoc;
7941 mContentViewer->GetDOMDocument(getter_AddRefs(domDoc));
7942
7943 nsCOMArray<nsIDocShellTreeItem> childShells;
7944 int32_t i = 0;
7945 nsCOMPtr<nsIDocShellTreeItem> child;
7946 while (NS_SUCCEEDED(mLSHE->ChildShellAt(i++, getter_AddRefs(child))) &&
7947 child) {
7948 childShells.AppendObject(child);
7949 }
7950
7951 // get the previous content viewer size
7952 nsIntRect oldBounds(0, 0, 0, 0);
7953 mLSHE->GetViewerBounds(oldBounds);
7954
7955 // Restore the refresh URI list. The refresh timers will be restarted
7956 // when EndPageLoad() is called.
7957 nsCOMPtr<nsISupportsArray> refreshURIList;
7958 mLSHE->GetRefreshURIList(getter_AddRefs(refreshURIList));
7959
7960 // Reattach to the window object.
7961 mIsRestoringDocument = true; // for MediaDocument::BecomeInteractive
7962 rv = mContentViewer->Open(windowState, mLSHE);
7963 mIsRestoringDocument = false;
7964
7965 // Hack to keep nsDocShellEditorData alive across the
7966 // SetContentViewer(nullptr) call below.
7967 nsAutoPtr<nsDocShellEditorData> data(mLSHE->ForgetEditorData());
7968
7969 // Now remove it from the cached presentation.
7970 mLSHE->SetContentViewer(nullptr);
7971 mEODForCurrentDocument = false;
7972
7973 mLSHE->SetEditorData(data.forget());
7974
7975 #ifdef DEBUG
7976 {
7977 nsCOMPtr<nsISupportsArray> refreshURIs;
7978 mLSHE->GetRefreshURIList(getter_AddRefs(refreshURIs));
7979 nsCOMPtr<nsIDocShellTreeItem> childShell;
7980 mLSHE->ChildShellAt(0, getter_AddRefs(childShell));
7981 NS_ASSERTION(!refreshURIs && !childShell,
7982 "SHEntry should have cleared presentation state");
7983 }
7984 #endif
7985
7986 // Restore the sticky state of the viewer. The viewer has set this state
7987 // on the history entry in Destroy() just before marking itself non-sticky,
7988 // to avoid teardown of the presentation.
7989 mContentViewer->SetSticky(sticky);
7990
7991 NS_ENSURE_SUCCESS(rv, rv);
7992
7993 // mLSHE is now our currently-loaded document.
7994 SetHistoryEntry(&mOSHE, mLSHE);
7995
7996 // XXX special wyciwyg handling in Embed()?
7997
7998 // We aren't going to restore any items from the LayoutHistoryState,
7999 // but we don't want them to stay around in case the page is reloaded.
8000 SetLayoutHistoryState(nullptr);
8001
8002 // This is the end of our Embed() replacement
8003
8004 mSavingOldViewer = false;
8005 mEODForCurrentDocument = false;
8006
8007 // Tell the event loop to favor plevents over user events, see comments
8008 // in CreateContentViewer.
8009 if (++gNumberOfDocumentsLoading == 1)
8010 FavorPerformanceHint(true);
8011
8012
8013 if (oldMUDV && newMUDV) {
8014 newMUDV->SetMinFontSize(minFontSize);
8015 newMUDV->SetTextZoom(textZoom);
8016 newMUDV->SetFullZoom(pageZoom);
8017 newMUDV->SetAuthorStyleDisabled(styleDisabled);
8018 }
8019
8020 nsCOMPtr<nsIDocument> document = do_QueryInterface(domDoc);
8021 uint32_t parentSuspendCount = 0;
8022 if (document) {
8023 nsCOMPtr<nsIDocShellTreeItem> parent;
8024 GetParent(getter_AddRefs(parent));
8025 nsCOMPtr<nsIDocument> d = do_GetInterface(parent);
8026 if (d) {
8027 if (d->EventHandlingSuppressed()) {
8028 document->SuppressEventHandling(nsIDocument::eEvents,
8029 d->EventHandlingSuppressed());
8030 }
8031
8032 // Ick, it'd be nicer to not rewalk all of the subdocs here.
8033 if (d->AnimationsPaused()) {
8034 document->SuppressEventHandling(nsIDocument::eAnimationsOnly,
8035 d->AnimationsPaused());
8036 }
8037
8038 nsCOMPtr<nsPIDOMWindow> parentWindow = d->GetWindow();
8039 if (parentWindow) {
8040 parentSuspendCount = parentWindow->TimeoutSuspendCount();
8041 }
8042 }
8043
8044 // Use the uri from the mLSHE we had when we entered this function
8045 // (which need not match the document's URI if anchors are involved),
8046 // since that's the history entry we're loading. Note that if we use
8047 // origLSHE we don't have to worry about whether the entry in question
8048 // is still mLSHE or whether it's now mOSHE.
8049 nsCOMPtr<nsIURI> uri;
8050 origLSHE->GetURI(getter_AddRefs(uri));
8051 SetCurrentURI(uri, document->GetChannel(), true, 0);
8052 }
8053
8054 // This is the end of our CreateContentViewer() replacement.
8055 // Now we simulate a load. First, we restore the state of the javascript
8056 // window object.
8057 nsCOMPtr<nsPIDOMWindow> privWin =
8058 do_GetInterface(static_cast<nsIInterfaceRequestor*>(this));
8059 NS_ASSERTION(privWin, "could not get nsPIDOMWindow interface");
8060
8061 rv = privWin->RestoreWindowState(windowState);
8062 NS_ENSURE_SUCCESS(rv, rv);
8063
8064 // Now, dispatch a title change event which would happen as the
8065 // <head> is parsed.
8066 document->NotifyPossibleTitleChange(false);
8067
8068 // Now we simulate appending child docshells for subframes.
8069 for (i = 0; i < childShells.Count(); ++i) {
8070 nsIDocShellTreeItem *childItem = childShells.ObjectAt(i);
8071 nsCOMPtr<nsIDocShell> childShell = do_QueryInterface(childItem);
8072
8073 // Make sure to not clobber the state of the child. Since AddChild
8074 // always clobbers it, save it off first.
8075 bool allowPlugins;
8076 childShell->GetAllowPlugins(&allowPlugins);
8077
8078 bool allowJavascript;
8079 childShell->GetAllowJavascript(&allowJavascript);
8080
8081 bool allowRedirects;
8082 childShell->GetAllowMetaRedirects(&allowRedirects);
8083
8084 bool allowSubframes;
8085 childShell->GetAllowSubframes(&allowSubframes);
8086
8087 bool allowImages;
8088 childShell->GetAllowImages(&allowImages);
8089
8090 bool allowMedia = childShell->GetAllowMedia();
8091
8092 bool allowDNSPrefetch;
8093 childShell->GetAllowDNSPrefetch(&allowDNSPrefetch);
8094
8095 bool allowContentRetargeting = childShell->GetAllowContentRetargeting();
8096
8097 uint32_t defaultLoadFlags;
8098 childShell->GetDefaultLoadFlags(&defaultLoadFlags);
8099
8100 // this.AddChild(child) calls child.SetDocLoaderParent(this), meaning
8101 // that the child inherits our state. Among other things, this means
8102 // that the child inherits our mIsActive and mInPrivateBrowsing, which
8103 // is what we want.
8104 AddChild(childItem);
8105
8106 childShell->SetAllowPlugins(allowPlugins);
8107 childShell->SetAllowJavascript(allowJavascript);
8108 childShell->SetAllowMetaRedirects(allowRedirects);
8109 childShell->SetAllowSubframes(allowSubframes);
8110 childShell->SetAllowImages(allowImages);
8111 childShell->SetAllowMedia(allowMedia);
8112 childShell->SetAllowDNSPrefetch(allowDNSPrefetch);
8113 childShell->SetAllowContentRetargeting(allowContentRetargeting);
8114 childShell->SetDefaultLoadFlags(defaultLoadFlags);
8115
8116 rv = childShell->BeginRestore(nullptr, false);
8117 NS_ENSURE_SUCCESS(rv, rv);
8118 }
8119
8120 nsCOMPtr<nsIPresShell> shell = GetPresShell();
8121
8122 // We may be displayed on a different monitor (or in a different
8123 // HiDPI mode) than when we got into the history list. So we need
8124 // to check if this has happened. See bug 838239.
8125
8126 // Because the prescontext normally handles resolution changes via
8127 // a runnable (see nsPresContext::UIResolutionChanged), its device
8128 // context won't be -immediately- updated as a result of calling
8129 // shell->BackingScaleFactorChanged().
8130
8131 // But we depend on that device context when adjusting the view size
8132 // via mContentViewer->SetBounds(newBounds) below. So we need to
8133 // explicitly tell it to check for changed resolution here.
8134 if (shell && shell->GetPresContext()->DeviceContext()->CheckDPIChange()) {
8135 shell->BackingScaleFactorChanged();
8136 }
8137
8138 nsViewManager *newVM = shell ? shell->GetViewManager() : nullptr;
8139 nsView *newRootView = newVM ? newVM->GetRootView() : nullptr;
8140
8141 // Insert the new root view at the correct location in the view tree.
8142 if (container) {
8143 nsSubDocumentFrame* subDocFrame = do_QueryFrame(container->GetPrimaryFrame());
8144 rootViewParent = subDocFrame ? subDocFrame->EnsureInnerView() : nullptr;
8145 }
8146 if (sibling &&
8147 sibling->GetShell() &&
8148 sibling->GetShell()->GetViewManager()) {
8149 rootViewSibling = sibling->GetShell()->GetViewManager()->GetRootView();
8150 } else {
8151 rootViewSibling = nullptr;
8152 }
8153 if (rootViewParent && newRootView && newRootView->GetParent() != rootViewParent) {
8154 nsViewManager *parentVM = rootViewParent->GetViewManager();
8155 if (parentVM) {
8156 // InsertChild(parent, child, sib, true) inserts the child after
8157 // sib in content order, which is before sib in view order. BUT
8158 // when sib is null it inserts at the end of the the document
8159 // order, i.e., first in view order. But when oldRootSibling is
8160 // null, the old root as at the end of the view list --- last in
8161 // content order --- and we want to call InsertChild(parent, child,
8162 // nullptr, false) in that case.
8163 parentVM->InsertChild(rootViewParent, newRootView,
8164 rootViewSibling,
8165 rootViewSibling ? true : false);
8166
8167 NS_ASSERTION(newRootView->GetNextSibling() == rootViewSibling,
8168 "error in InsertChild");
8169 }
8170 }
8171
8172 // If parent is suspended, increase suspension count.
8173 // This can't be done as early as event suppression since this
8174 // depends on docshell tree.
8175 if (parentSuspendCount) {
8176 privWin->SuspendTimeouts(parentSuspendCount, false);
8177 }
8178
8179 // Now that all of the child docshells have been put into place, we can
8180 // restart the timers for the window and all of the child frames.
8181 privWin->ResumeTimeouts();
8182
8183 // Restore the refresh URI list. The refresh timers will be restarted
8184 // when EndPageLoad() is called.
8185 mRefreshURIList = refreshURIList;
8186
8187 // Meta-refresh timers have been restarted for this shell, but not
8188 // for our children. Walk the child shells and restart their timers.
8189 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
8190 while (iter.HasMore()) {
8191 nsCOMPtr<nsIDocShell> child = do_QueryObject(iter.GetNext());
8192 if (child)
8193 child->ResumeRefreshURIs();
8194 }
8195
8196 // Make sure this presentation is the same size as the previous
8197 // presentation. If this is not the same size we showed it at last time,
8198 // then we need to resize the widget.
8199
8200 // XXXbryner This interacts poorly with Firefox's infobar. If the old
8201 // presentation had the infobar visible, then we will resize the new
8202 // presentation to that smaller size. However, firing the locationchanged
8203 // event will hide the infobar, which will immediately resize the window
8204 // back to the larger size. A future optimization might be to restore
8205 // the presentation at the "wrong" size, then fire the locationchanged
8206 // event and check whether the docshell's new size is the same as the
8207 // cached viewer size (skipping the resize if they are equal).
8208
8209 if (newRootView) {
8210 if (!newBounds.IsEmpty() && !newBounds.IsEqualEdges(oldBounds)) {
8211 #ifdef DEBUG_PAGE_CACHE
8212 printf("resize widget(%d, %d, %d, %d)\n", newBounds.x,
8213 newBounds.y, newBounds.width, newBounds.height);
8214 #endif
8215 mContentViewer->SetBounds(newBounds);
8216 } else {
8217 nsIScrollableFrame *rootScrollFrame =
8218 shell->GetRootScrollFrameAsScrollableExternal();
8219 if (rootScrollFrame) {
8220 rootScrollFrame->PostScrolledAreaEventForCurrentArea();
8221 }
8222 }
8223 }
8224
8225 // The FinishRestore call below can kill these, null them out so we don't
8226 // have invalid pointer lying around.
8227 newRootView = rootViewSibling = rootViewParent = nullptr;
8228 newVM = nullptr;
8229
8230 // Simulate the completion of the load.
8231 nsDocShell::FinishRestore();
8232
8233 // Restart plugins, and paint the content.
8234 if (shell) {
8235 shell->Thaw();
8236 }
8237
8238 return privWin->FireDelayedDOMEvents();
8239 }
8240
8241 NS_IMETHODIMP
8242 nsDocShell::CreateContentViewer(const char *aContentType,
8243 nsIRequest * request,
8244 nsIStreamListener ** aContentHandler)
8245 {
8246 *aContentHandler = nullptr;
8247
8248 // Can we check the content type of the current content viewer
8249 // and reuse it without destroying it and re-creating it?
8250
8251 NS_ASSERTION(mLoadGroup, "Someone ignored return from Init()?");
8252
8253 // Instantiate the content viewer object
8254 nsCOMPtr<nsIContentViewer> viewer;
8255 nsresult rv = NewContentViewerObj(aContentType, request, mLoadGroup,
8256 aContentHandler, getter_AddRefs(viewer));
8257
8258 if (NS_FAILED(rv))
8259 return rv;
8260
8261 // Notify the current document that it is about to be unloaded!!
8262 //
8263 // It is important to fire the unload() notification *before* any state
8264 // is changed within the DocShell - otherwise, javascript will get the
8265 // wrong information :-(
8266 //
8267
8268 if (mSavingOldViewer) {
8269 // We determined that it was safe to cache the document presentation
8270 // at the time we initiated the new load. We need to check whether
8271 // it's still safe to do so, since there may have been DOM mutations
8272 // or new requests initiated.
8273 nsCOMPtr<nsIDOMDocument> domDoc;
8274 viewer->GetDOMDocument(getter_AddRefs(domDoc));
8275 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
8276 mSavingOldViewer = CanSavePresentation(mLoadType, request, doc);
8277 }
8278
8279 NS_ASSERTION(!mLoadingURI, "Re-entering unload?");
8280
8281 nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(request);
8282 if (aOpenedChannel) {
8283 aOpenedChannel->GetURI(getter_AddRefs(mLoadingURI));
8284 }
8285 FirePageHideNotification(!mSavingOldViewer);
8286 mLoadingURI = nullptr;
8287
8288 // Set mFiredUnloadEvent = false so that the unload handler for the
8289 // *new* document will fire.
8290 mFiredUnloadEvent = false;
8291
8292 // we've created a new document so go ahead and call
8293 // OnLoadingSite(), but don't fire OnLocationChange()
8294 // notifications before we've called Embed(). See bug 284993.
8295 mURIResultedInDocument = true;
8296
8297 if (mLoadType == LOAD_ERROR_PAGE) {
8298 // We need to set the SH entry and our current URI here and not
8299 // at the moment we load the page. We want the same behavior
8300 // of Stop() as for a normal page load. See bug 514232 for details.
8301
8302 // Revert mLoadType to load type to state the page load failed,
8303 // following function calls need it.
8304 mLoadType = mFailedLoadType;
8305
8306 nsCOMPtr<nsIChannel> failedChannel = mFailedChannel;
8307
8308 // Make sure we have a URI to set currentURI.
8309 nsCOMPtr<nsIURI> failedURI;
8310 if (failedChannel) {
8311 NS_GetFinalChannelURI(failedChannel, getter_AddRefs(failedURI));
8312 }
8313
8314 if (!failedURI) {
8315 failedURI = mFailedURI;
8316 }
8317 if (!failedURI) {
8318 // We need a URI object to store a session history entry, so make up a URI
8319 NS_NewURI(getter_AddRefs(failedURI), "about:blank");
8320 }
8321
8322 // When we don't have failedURI, something wrong will happen. See
8323 // bug 291876.
8324 MOZ_ASSERT(failedURI, "We don't have a URI for history APIs.");
8325
8326 mFailedChannel = nullptr;
8327 mFailedURI = nullptr;
8328
8329 // Create an shistory entry for the old load.
8330 if (failedURI) {
8331 bool errorOnLocationChangeNeeded =
8332 OnNewURI(failedURI, failedChannel, nullptr, mLoadType, false,
8333 false, false);
8334
8335 if (errorOnLocationChangeNeeded) {
8336 FireOnLocationChange(this, failedChannel, failedURI,
8337 LOCATION_CHANGE_ERROR_PAGE);
8338 }
8339 }
8340
8341 // Be sure to have a correct mLSHE, it may have been cleared by
8342 // EndPageLoad. See bug 302115.
8343 if (mSessionHistory && !mLSHE) {
8344 int32_t idx;
8345 mSessionHistory->GetRequestedIndex(&idx);
8346 if (idx == -1)
8347 mSessionHistory->GetIndex(&idx);
8348 mSessionHistory->GetEntryAtIndex(idx, false,
8349 getter_AddRefs(mLSHE));
8350 }
8351
8352 mLoadType = LOAD_ERROR_PAGE;
8353 }
8354
8355 bool onLocationChangeNeeded = OnLoadingSite(aOpenedChannel, false);
8356
8357 // let's try resetting the load group if we need to...
8358 nsCOMPtr<nsILoadGroup> currentLoadGroup;
8359 NS_ENSURE_SUCCESS(aOpenedChannel->
8360 GetLoadGroup(getter_AddRefs(currentLoadGroup)),
8361 NS_ERROR_FAILURE);
8362
8363 if (currentLoadGroup != mLoadGroup) {
8364 nsLoadFlags loadFlags = 0;
8365
8366 //Cancel any URIs that are currently loading...
8367 /// XXX: Need to do this eventually Stop();
8368 //
8369 // Retarget the document to this loadgroup...
8370 //
8371 /* First attach the channel to the right loadgroup
8372 * and then remove from the old loadgroup. This
8373 * puts the notifications in the right order and
8374 * we don't null-out mLSHE in OnStateChange() for
8375 * all redirected urls
8376 */
8377 aOpenedChannel->SetLoadGroup(mLoadGroup);
8378
8379 // Mark the channel as being a document URI...
8380 aOpenedChannel->GetLoadFlags(&loadFlags);
8381 loadFlags |= nsIChannel::LOAD_DOCUMENT_URI;
8382
8383 aOpenedChannel->SetLoadFlags(loadFlags);
8384
8385 mLoadGroup->AddRequest(request, nullptr);
8386 if (currentLoadGroup)
8387 currentLoadGroup->RemoveRequest(request, nullptr,
8388 NS_BINDING_RETARGETED);
8389
8390 // Update the notification callbacks, so that progress and
8391 // status information are sent to the right docshell...
8392 aOpenedChannel->SetNotificationCallbacks(this);
8393 }
8394
8395 NS_ENSURE_SUCCESS(Embed(viewer, "", (nsISupports *) nullptr),
8396 NS_ERROR_FAILURE);
8397
8398 mSavedRefreshURIList = nullptr;
8399 mSavingOldViewer = false;
8400 mEODForCurrentDocument = false;
8401
8402 // if this document is part of a multipart document,
8403 // the ID can be used to distinguish it from the other parts.
8404 nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(request));
8405 if (multiPartChannel) {
8406 nsCOMPtr<nsIPresShell> shell = GetPresShell();
8407 if (NS_SUCCEEDED(rv) && shell) {
8408 nsIDocument *doc = shell->GetDocument();
8409 if (doc) {
8410 uint32_t partID;
8411 multiPartChannel->GetPartID(&partID);
8412 doc->SetPartID(partID);
8413 }
8414 }
8415 }
8416
8417 // Give hint to native plevent dispatch mechanism. If a document
8418 // is loading the native plevent dispatch mechanism should favor
8419 // performance over normal native event dispatch priorities.
8420 if (++gNumberOfDocumentsLoading == 1) {
8421 // Hint to favor performance for the plevent notification mechanism.
8422 // We want the pages to load as fast as possible even if its means
8423 // native messages might be starved.
8424 FavorPerformanceHint(true);
8425 }
8426
8427 if (onLocationChangeNeeded) {
8428 FireOnLocationChange(this, request, mCurrentURI, 0);
8429 }
8430
8431 return NS_OK;
8432 }
8433
8434 nsresult
8435 nsDocShell::NewContentViewerObj(const char *aContentType,
8436 nsIRequest * request, nsILoadGroup * aLoadGroup,
8437 nsIStreamListener ** aContentHandler,
8438 nsIContentViewer ** aViewer)
8439 {
8440 nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(request);
8441
8442 nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
8443 nsContentUtils::FindInternalContentViewer(aContentType);
8444 if (!docLoaderFactory) {
8445 return NS_ERROR_FAILURE;
8446 }
8447
8448 // Now create an instance of the content viewer
8449 // nsLayoutDLF makes the determination if it should be a "view-source" instead of "view"
8450 nsresult rv = docLoaderFactory->CreateInstance("view",
8451 aOpenedChannel,
8452 aLoadGroup, aContentType,
8453 this,
8454 nullptr,
8455 aContentHandler,
8456 aViewer);
8457 NS_ENSURE_SUCCESS(rv, rv);
8458
8459 (*aViewer)->SetContainer(this);
8460 return NS_OK;
8461 }
8462
8463 NS_IMETHODIMP
8464 nsDocShell::SetupNewViewer(nsIContentViewer * aNewViewer)
8465 {
8466 //
8467 // Copy content viewer state from previous or parent content viewer.
8468 //
8469 // The following logic is mirrored in nsHTMLDocument::StartDocumentLoad!
8470 //
8471 // Do NOT to maintain a reference to the old content viewer outside
8472 // of this "copying" block, or it will not be destroyed until the end of
8473 // this routine and all <SCRIPT>s and event handlers fail! (bug 20315)
8474 //
8475 // In this block of code, if we get an error result, we return it
8476 // but if we get a null pointer, that's perfectly legal for parent
8477 // and parentContentViewer.
8478 //
8479
8480 int32_t x = 0;
8481 int32_t y = 0;
8482 int32_t cx = 0;
8483 int32_t cy = 0;
8484
8485 // This will get the size from the current content viewer or from the
8486 // Init settings
8487 DoGetPositionAndSize(&x, &y, &cx, &cy);
8488
8489 nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
8490 NS_ENSURE_SUCCESS(GetSameTypeParent(getter_AddRefs(parentAsItem)),
8491 NS_ERROR_FAILURE);
8492 nsCOMPtr<nsIDocShell> parent(do_QueryInterface(parentAsItem));
8493
8494 nsAutoCString forceCharset;
8495 nsAutoCString hintCharset;
8496 int32_t hintCharsetSource;
8497 int32_t minFontSize;
8498 float textZoom;
8499 float pageZoom;
8500 bool styleDisabled;
8501 // |newMUDV| also serves as a flag to set the data from the above vars
8502 nsCOMPtr<nsIMarkupDocumentViewer> newMUDV;
8503
8504 if (mContentViewer || parent) {
8505 nsCOMPtr<nsIMarkupDocumentViewer> oldMUDV;
8506 if (mContentViewer) {
8507 // Get any interesting state from old content viewer
8508 // XXX: it would be far better to just reuse the document viewer ,
8509 // since we know we're just displaying the same document as before
8510 oldMUDV = do_QueryInterface(mContentViewer);
8511
8512 // Tell the old content viewer to hibernate in session history when
8513 // it is destroyed.
8514
8515 if (mSavingOldViewer && NS_FAILED(CaptureState())) {
8516 if (mOSHE) {
8517 mOSHE->SyncPresentationState();
8518 }
8519 mSavingOldViewer = false;
8520 }
8521 }
8522 else {
8523 // No old content viewer, so get state from parent's content viewer
8524 nsCOMPtr<nsIContentViewer> parentContentViewer;
8525 parent->GetContentViewer(getter_AddRefs(parentContentViewer));
8526 oldMUDV = do_QueryInterface(parentContentViewer);
8527 }
8528
8529 if (oldMUDV) {
8530 nsresult rv;
8531
8532 newMUDV = do_QueryInterface(aNewViewer,&rv);
8533 if (newMUDV) {
8534 NS_ENSURE_SUCCESS(oldMUDV->
8535 GetForceCharacterSet(forceCharset),
8536 NS_ERROR_FAILURE);
8537 NS_ENSURE_SUCCESS(oldMUDV->
8538 GetHintCharacterSet(hintCharset),
8539 NS_ERROR_FAILURE);
8540 NS_ENSURE_SUCCESS(oldMUDV->
8541 GetHintCharacterSetSource(&hintCharsetSource),
8542 NS_ERROR_FAILURE);
8543 NS_ENSURE_SUCCESS(oldMUDV->
8544 GetMinFontSize(&minFontSize),
8545 NS_ERROR_FAILURE);
8546 NS_ENSURE_SUCCESS(oldMUDV->
8547 GetTextZoom(&textZoom),
8548 NS_ERROR_FAILURE);
8549 NS_ENSURE_SUCCESS(oldMUDV->
8550 GetFullZoom(&pageZoom),
8551 NS_ERROR_FAILURE);
8552 NS_ENSURE_SUCCESS(oldMUDV->
8553 GetAuthorStyleDisabled(&styleDisabled),
8554 NS_ERROR_FAILURE);
8555 }
8556 }
8557 }
8558
8559 nscolor bgcolor = NS_RGBA(0, 0, 0, 0);
8560 // Ensure that the content viewer is destroyed *after* the GC - bug 71515
8561 nsCOMPtr<nsIContentViewer> kungfuDeathGrip = mContentViewer;
8562 if (mContentViewer) {
8563 // Stop any activity that may be happening in the old document before
8564 // releasing it...
8565 mContentViewer->Stop();
8566
8567 // Try to extract the canvas background color from the old
8568 // presentation shell, so we can use it for the next document.
8569 nsCOMPtr<nsIPresShell> shell;
8570 mContentViewer->GetPresShell(getter_AddRefs(shell));
8571
8572 if (shell) {
8573 bgcolor = shell->GetCanvasBackground();
8574 }
8575
8576 mContentViewer->Close(mSavingOldViewer ? mOSHE.get() : nullptr);
8577 aNewViewer->SetPreviousViewer(mContentViewer);
8578 }
8579 if (mOSHE && (!mContentViewer || !mSavingOldViewer)) {
8580 // We don't plan to save a viewer in mOSHE; tell it to drop
8581 // any other state it's holding.
8582 mOSHE->SyncPresentationState();
8583 }
8584
8585 mContentViewer = nullptr;
8586
8587 // Now that we're about to switch documents, forget all of our children.
8588 // Note that we cached them as needed up in CaptureState above.
8589 DestroyChildren();
8590
8591 mContentViewer = aNewViewer;
8592
8593 nsCOMPtr<nsIWidget> widget;
8594 NS_ENSURE_SUCCESS(GetMainWidget(getter_AddRefs(widget)), NS_ERROR_FAILURE);
8595
8596 nsIntRect bounds(x, y, cx, cy);
8597
8598 mContentViewer->SetNavigationTiming(mTiming);
8599
8600 if (NS_FAILED(mContentViewer->Init(widget, bounds))) {
8601 mContentViewer = nullptr;
8602 NS_ERROR("ContentViewer Initialization failed");
8603 return NS_ERROR_FAILURE;
8604 }
8605
8606 // If we have old state to copy, set the old state onto the new content
8607 // viewer
8608 if (newMUDV) {
8609 NS_ENSURE_SUCCESS(newMUDV->SetForceCharacterSet(forceCharset),
8610 NS_ERROR_FAILURE);
8611 NS_ENSURE_SUCCESS(newMUDV->SetHintCharacterSet(hintCharset),
8612 NS_ERROR_FAILURE);
8613 NS_ENSURE_SUCCESS(newMUDV->
8614 SetHintCharacterSetSource(hintCharsetSource),
8615 NS_ERROR_FAILURE);
8616 NS_ENSURE_SUCCESS(newMUDV->SetMinFontSize(minFontSize),
8617 NS_ERROR_FAILURE);
8618 NS_ENSURE_SUCCESS(newMUDV->SetTextZoom(textZoom),
8619 NS_ERROR_FAILURE);
8620 NS_ENSURE_SUCCESS(newMUDV->SetFullZoom(pageZoom),
8621 NS_ERROR_FAILURE);
8622 NS_ENSURE_SUCCESS(newMUDV->SetAuthorStyleDisabled(styleDisabled),
8623 NS_ERROR_FAILURE);
8624 }
8625
8626 // Stuff the bgcolor from the old pres shell into the new
8627 // pres shell. This improves page load continuity.
8628 nsCOMPtr<nsIPresShell> shell;
8629 mContentViewer->GetPresShell(getter_AddRefs(shell));
8630
8631 if (shell) {
8632 shell->SetCanvasBackground(bgcolor);
8633 }
8634
8635 // XXX: It looks like the LayoutState gets restored again in Embed()
8636 // right after the call to SetupNewViewer(...)
8637
8638 // We don't show the mContentViewer yet, since we want to draw the old page
8639 // until we have enough of the new page to show. Just return with the new
8640 // viewer still set to hidden.
8641
8642 return NS_OK;
8643 }
8644
8645 nsresult
8646 nsDocShell::SetDocCurrentStateObj(nsISHEntry *shEntry)
8647 {
8648 nsCOMPtr<nsIDocument> document = do_GetInterface(GetAsSupports(this));
8649 NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
8650
8651 nsCOMPtr<nsIStructuredCloneContainer> scContainer;
8652 if (shEntry) {
8653 nsresult rv = shEntry->GetStateData(getter_AddRefs(scContainer));
8654 NS_ENSURE_SUCCESS(rv, rv);
8655
8656 // If shEntry is null, just set the document's state object to null.
8657 }
8658
8659 // It's OK for scContainer too be null here; that just means there's no
8660 // state data associated with this history entry.
8661 document->SetStateObject(scContainer);
8662
8663 return NS_OK;
8664 }
8665
8666 nsresult
8667 nsDocShell::CheckLoadingPermissions()
8668 {
8669 // This method checks whether the caller may load content into
8670 // this docshell. Even though we've done our best to hide windows
8671 // from code that doesn't have the right to access them, it's
8672 // still possible for an evil site to open a window and access
8673 // frames in the new window through window.frames[] (which is
8674 // allAccess for historic reasons), so we still need to do this
8675 // check on load.
8676 nsresult rv = NS_OK, sameOrigin = NS_OK;
8677
8678 if (!gValidateOrigin || !IsFrame()) {
8679 // Origin validation was turned off, or we're not a frame.
8680 // Permit all loads.
8681
8682 return rv;
8683 }
8684
8685 nsCOMPtr<nsIScriptSecurityManager> securityManager =
8686 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
8687 NS_ENSURE_SUCCESS(rv, rv);
8688
8689 // We're a frame. Check that the caller has write permission to
8690 // the parent before allowing it to load anything into this
8691 // docshell.
8692 nsCOMPtr<nsIPrincipal> subjPrincipal;
8693 rv = securityManager->GetSubjectPrincipal(getter_AddRefs(subjPrincipal));
8694 NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && subjPrincipal, rv);
8695
8696 // Check if the caller is from the same origin as this docshell,
8697 // or any of its ancestors.
8698 nsCOMPtr<nsIDocShellTreeItem> item(this);
8699 do {
8700 nsCOMPtr<nsIScriptGlobalObject> sgo(do_GetInterface(item));
8701 nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(sgo));
8702
8703 nsIPrincipal *p;
8704 if (!sop || !(p = sop->GetPrincipal())) {
8705 return NS_ERROR_UNEXPECTED;
8706 }
8707
8708 // Compare origins
8709 bool subsumes;
8710 sameOrigin = subjPrincipal->Subsumes(p, &subsumes);
8711 if (NS_SUCCEEDED(sameOrigin)) {
8712 if (subsumes) {
8713 // Same origin, permit load
8714
8715 return sameOrigin;
8716 }
8717
8718 sameOrigin = NS_ERROR_DOM_PROP_ACCESS_DENIED;
8719 }
8720
8721 nsCOMPtr<nsIDocShellTreeItem> tmp;
8722 item->GetSameTypeParent(getter_AddRefs(tmp));
8723 item.swap(tmp);
8724 } while (item);
8725
8726 return sameOrigin;
8727 }
8728
8729 //*****************************************************************************
8730 // nsDocShell: Site Loading
8731 //*****************************************************************************
8732 namespace
8733 {
8734
8735 #ifdef MOZ_PLACES
8736 // Callback used by CopyFavicon to inform the favicon service that one URI
8737 // (mNewURI) has the same favicon URI (OnComplete's aFaviconURI) as another.
8738 class nsCopyFaviconCallback MOZ_FINAL : public nsIFaviconDataCallback
8739 {
8740 public:
8741 NS_DECL_ISUPPORTS
8742
8743 nsCopyFaviconCallback(nsIURI *aNewURI, bool aInPrivateBrowsing)
8744 : mNewURI(aNewURI)
8745 , mInPrivateBrowsing(aInPrivateBrowsing)
8746 {
8747 }
8748
8749 NS_IMETHODIMP
8750 OnComplete(nsIURI *aFaviconURI, uint32_t aDataLen,
8751 const uint8_t *aData, const nsACString &aMimeType)
8752 {
8753 // Continue only if there is an associated favicon.
8754 if (!aFaviconURI) {
8755 return NS_OK;
8756 }
8757
8758 NS_ASSERTION(aDataLen == 0,
8759 "We weren't expecting the callback to deliver data.");
8760 nsCOMPtr<mozIAsyncFavicons> favSvc =
8761 do_GetService("@mozilla.org/browser/favicon-service;1");
8762 NS_ENSURE_STATE(favSvc);
8763
8764 return favSvc->SetAndFetchFaviconForPage(mNewURI, aFaviconURI,
8765 false,
8766 mInPrivateBrowsing ?
8767 nsIFaviconService::FAVICON_LOAD_PRIVATE :
8768 nsIFaviconService::FAVICON_LOAD_NON_PRIVATE,
8769 nullptr);
8770 }
8771
8772 private:
8773 nsCOMPtr<nsIURI> mNewURI;
8774 bool mInPrivateBrowsing;
8775 };
8776
8777 NS_IMPL_ISUPPORTS(nsCopyFaviconCallback, nsIFaviconDataCallback)
8778 #endif
8779
8780 // Tell the favicon service that aNewURI has the same favicon as aOldURI.
8781 void CopyFavicon(nsIURI *aOldURI, nsIURI *aNewURI, bool inPrivateBrowsing)
8782 {
8783 #ifdef MOZ_PLACES
8784 nsCOMPtr<mozIAsyncFavicons> favSvc =
8785 do_GetService("@mozilla.org/browser/favicon-service;1");
8786 if (favSvc) {
8787 nsCOMPtr<nsIFaviconDataCallback> callback =
8788 new nsCopyFaviconCallback(aNewURI, inPrivateBrowsing);
8789 favSvc->GetFaviconURLForPage(aOldURI, callback);
8790 }
8791 #endif
8792 }
8793
8794 } // anonymous namespace
8795
8796 class InternalLoadEvent : public nsRunnable
8797 {
8798 public:
8799 InternalLoadEvent(nsDocShell* aDocShell, nsIURI * aURI, nsIURI * aReferrer,
8800 nsISupports * aOwner, uint32_t aFlags,
8801 const char* aTypeHint, nsIInputStream * aPostData,
8802 nsIInputStream * aHeadersData, uint32_t aLoadType,
8803 nsISHEntry * aSHEntry, bool aFirstParty,
8804 const nsAString &aSrcdoc, nsIDocShell* aSourceDocShell,
8805 nsIURI * aBaseURI) :
8806 mSrcdoc(aSrcdoc),
8807 mDocShell(aDocShell),
8808 mURI(aURI),
8809 mReferrer(aReferrer),
8810 mOwner(aOwner),
8811 mPostData(aPostData),
8812 mHeadersData(aHeadersData),
8813 mSHEntry(aSHEntry),
8814 mFlags(aFlags),
8815 mLoadType(aLoadType),
8816 mFirstParty(aFirstParty),
8817 mSourceDocShell(aSourceDocShell),
8818 mBaseURI(aBaseURI)
8819 {
8820 // Make sure to keep null things null as needed
8821 if (aTypeHint) {
8822 mTypeHint = aTypeHint;
8823 }
8824 }
8825
8826 NS_IMETHOD Run() {
8827 return mDocShell->InternalLoad(mURI, mReferrer, mOwner, mFlags,
8828 nullptr, mTypeHint.get(),
8829 NullString(), mPostData, mHeadersData,
8830 mLoadType, mSHEntry, mFirstParty,
8831 mSrcdoc, mSourceDocShell, mBaseURI,
8832 nullptr, nullptr);
8833 }
8834
8835 private:
8836
8837 // Use IDL strings so .get() returns null by default
8838 nsXPIDLString mWindowTarget;
8839 nsXPIDLCString mTypeHint;
8840 nsString mSrcdoc;
8841
8842 nsRefPtr<nsDocShell> mDocShell;
8843 nsCOMPtr<nsIURI> mURI;
8844 nsCOMPtr<nsIURI> mReferrer;
8845 nsCOMPtr<nsISupports> mOwner;
8846 nsCOMPtr<nsIInputStream> mPostData;
8847 nsCOMPtr<nsIInputStream> mHeadersData;
8848 nsCOMPtr<nsISHEntry> mSHEntry;
8849 uint32_t mFlags;
8850 uint32_t mLoadType;
8851 bool mFirstParty;
8852 nsCOMPtr<nsIDocShell> mSourceDocShell;
8853 nsCOMPtr<nsIURI> mBaseURI;
8854 };
8855
8856 /**
8857 * Returns true if we started an asynchronous load (i.e., from the network), but
8858 * the document we're loading there hasn't yet become this docshell's active
8859 * document.
8860 *
8861 * When JustStartedNetworkLoad is true, you should be careful about modifying
8862 * mLoadType and mLSHE. These are both set when the asynchronous load first
8863 * starts, and the load expects that, when it eventually runs InternalLoad,
8864 * mLoadType and mLSHE will have their original values.
8865 */
8866 bool
8867 nsDocShell::JustStartedNetworkLoad()
8868 {
8869 return mDocumentRequest &&
8870 mDocumentRequest != GetCurrentDocChannel();
8871 }
8872
8873 NS_IMETHODIMP
8874 nsDocShell::InternalLoad(nsIURI * aURI,
8875 nsIURI * aReferrer,
8876 nsISupports * aOwner,
8877 uint32_t aFlags,
8878 const char16_t *aWindowTarget,
8879 const char* aTypeHint,
8880 const nsAString& aFileName,
8881 nsIInputStream * aPostData,
8882 nsIInputStream * aHeadersData,
8883 uint32_t aLoadType,
8884 nsISHEntry * aSHEntry,
8885 bool aFirstParty,
8886 const nsAString &aSrcdoc,
8887 nsIDocShell* aSourceDocShell,
8888 nsIURI* aBaseURI,
8889 nsIDocShell** aDocShell,
8890 nsIRequest** aRequest)
8891 {
8892 nsresult rv = NS_OK;
8893 mOriginalUriString.Truncate();
8894
8895 #ifdef PR_LOGGING
8896 if (gDocShellLeakLog && PR_LOG_TEST(gDocShellLeakLog, PR_LOG_DEBUG)) {
8897 nsAutoCString spec;
8898 if (aURI)
8899 aURI->GetSpec(spec);
8900 PR_LogPrint("DOCSHELL %p InternalLoad %s\n", this, spec.get());
8901 }
8902 #endif
8903 // Initialize aDocShell/aRequest
8904 if (aDocShell) {
8905 *aDocShell = nullptr;
8906 }
8907 if (aRequest) {
8908 *aRequest = nullptr;
8909 }
8910
8911 if (!aURI) {
8912 return NS_ERROR_NULL_POINTER;
8913 }
8914
8915 NS_ENSURE_TRUE(IsValidLoadType(aLoadType), NS_ERROR_INVALID_ARG);
8916
8917 NS_ENSURE_TRUE(!mIsBeingDestroyed, NS_ERROR_NOT_AVAILABLE);
8918
8919 // wyciwyg urls can only be loaded through history. Any normal load of
8920 // wyciwyg through docshell is illegal. Disallow such loads.
8921 if (aLoadType & LOAD_CMD_NORMAL) {
8922 bool isWyciwyg = false;
8923 rv = aURI->SchemeIs("wyciwyg", &isWyciwyg);
8924 if ((isWyciwyg && NS_SUCCEEDED(rv)) || NS_FAILED(rv))
8925 return NS_ERROR_FAILURE;
8926 }
8927
8928 bool bIsJavascript = false;
8929 if (NS_FAILED(aURI->SchemeIs("javascript", &bIsJavascript))) {
8930 bIsJavascript = false;
8931 }
8932
8933 //
8934 // First, notify any nsIContentPolicy listeners about the document load.
8935 // Only abort the load if a content policy listener explicitly vetos it!
8936 //
8937 nsCOMPtr<Element> requestingElement;
8938 // Use nsPIDOMWindow since we _want_ to cross the chrome boundary if needed
8939 if (mScriptGlobal)
8940 requestingElement = mScriptGlobal->GetFrameElementInternal();
8941
8942 nsRefPtr<nsGlobalWindow> MMADeathGrip = mScriptGlobal;
8943
8944 int16_t shouldLoad = nsIContentPolicy::ACCEPT;
8945 uint32_t contentType;
8946 bool isNewDocShell = false;
8947 bool isTargetTopLevelDocShell = false;
8948 nsCOMPtr<nsIDocShell> targetDocShell;
8949 if (aWindowTarget && *aWindowTarget) {
8950 // Locate the target DocShell.
8951 nsCOMPtr<nsIDocShellTreeItem> targetItem;
8952 rv = FindItemWithName(aWindowTarget, nullptr, this,
8953 getter_AddRefs(targetItem));
8954 NS_ENSURE_SUCCESS(rv, rv);
8955
8956 targetDocShell = do_QueryInterface(targetItem);
8957 // If the targetDocShell doesn't exist, then this is a new docShell
8958 // and we should consider this a TYPE_DOCUMENT load
8959 isNewDocShell = !targetDocShell;
8960
8961 // If the targetDocShell and the rootDocShell are the same, then the
8962 // targetDocShell is the top level document and hence we should
8963 // consider this TYPE_DOCUMENT
8964 if (targetDocShell) {
8965 nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
8966 targetDocShell->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
8967 NS_ASSERTION(sameTypeRoot, "No document shell root tree item from targetDocShell!");
8968 nsCOMPtr<nsIDocShell> rootShell = do_QueryInterface(sameTypeRoot);
8969 NS_ASSERTION(rootShell, "No root docshell from document shell root tree item.");
8970
8971 if (targetDocShell == rootShell) {
8972 isTargetTopLevelDocShell = true;
8973 }
8974 }
8975 }
8976 if (IsFrame() && !isNewDocShell && !isTargetTopLevelDocShell) {
8977 NS_ASSERTION(requestingElement, "A frame but no DOM element!?");
8978 contentType = nsIContentPolicy::TYPE_SUBDOCUMENT;
8979 } else {
8980 contentType = nsIContentPolicy::TYPE_DOCUMENT;
8981 }
8982
8983 nsISupports* context = requestingElement;
8984 if (!context) {
8985 context = ToSupports(mScriptGlobal);
8986 }
8987
8988 // XXXbz would be nice to know the loading principal here... but we don't
8989 nsCOMPtr<nsIPrincipal> loadingPrincipal = do_QueryInterface(aOwner);
8990 if (!loadingPrincipal && aReferrer) {
8991 nsCOMPtr<nsIScriptSecurityManager> secMan =
8992 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
8993 NS_ENSURE_SUCCESS(rv, rv);
8994
8995 rv = secMan->GetSimpleCodebasePrincipal(aReferrer,
8996 getter_AddRefs(loadingPrincipal));
8997 }
8998
8999 rv = NS_CheckContentLoadPolicy(contentType,
9000 aURI,
9001 loadingPrincipal,
9002 context,
9003 EmptyCString(), //mime guess
9004 nullptr, //extra
9005 &shouldLoad);
9006
9007 if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
9008 if (NS_SUCCEEDED(rv) && shouldLoad == nsIContentPolicy::REJECT_TYPE) {
9009 return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
9010 }
9011
9012 return NS_ERROR_CONTENT_BLOCKED;
9013 }
9014
9015 nsCOMPtr<nsISupports> owner(aOwner);
9016 //
9017 // Get an owner from the current document if necessary. Note that we only
9018 // do this for URIs that inherit a security context and local file URIs;
9019 // in particular we do NOT do this for about:blank. This way, random
9020 // about:blank loads that have no owner (which basically means they were
9021 // done by someone from chrome manually messing with our nsIWebNavigation
9022 // or by C++ setting document.location) don't get a funky principal. If
9023 // callers want something interesting to happen with the about:blank
9024 // principal in this case, they should pass an owner in.
9025 //
9026 {
9027 bool inherits;
9028 // One more twist: Don't inherit the owner for external loads.
9029 if (aLoadType != LOAD_NORMAL_EXTERNAL && !owner &&
9030 (aFlags & INTERNAL_LOAD_FLAGS_INHERIT_OWNER) &&
9031 NS_SUCCEEDED(nsContentUtils::URIInheritsSecurityContext(aURI,
9032 &inherits)) &&
9033 inherits) {
9034
9035 owner = GetInheritedPrincipal(true);
9036 }
9037 }
9038
9039 // Don't allow loads that would inherit our security context
9040 // if this document came from an unsafe channel.
9041 {
9042 bool willInherit;
9043 // This condition needs to match the one in
9044 // nsContentUtils::SetUpChannelOwner.
9045 // Except we reverse the rv check to be safe in case
9046 // nsContentUtils::URIInheritsSecurityContext fails here and
9047 // succeeds there.
9048 rv = nsContentUtils::URIInheritsSecurityContext(aURI, &willInherit);
9049 if (NS_FAILED(rv) || willInherit || NS_IsAboutBlank(aURI)) {
9050 nsCOMPtr<nsIDocShellTreeItem> treeItem = this;
9051 do {
9052 nsCOMPtr<nsIDocShell> itemDocShell =
9053 do_QueryInterface(treeItem);
9054 bool isUnsafe;
9055 if (itemDocShell &&
9056 NS_SUCCEEDED(itemDocShell->GetChannelIsUnsafe(&isUnsafe)) &&
9057 isUnsafe) {
9058 return NS_ERROR_DOM_SECURITY_ERR;
9059 }
9060
9061 nsCOMPtr<nsIDocShellTreeItem> parent;
9062 treeItem->GetSameTypeParent(getter_AddRefs(parent));
9063 parent.swap(treeItem);
9064 } while (treeItem);
9065 }
9066 }
9067
9068 //
9069 // Resolve the window target before going any further...
9070 // If the load has been targeted to another DocShell, then transfer the
9071 // load to it...
9072 //
9073 if (aWindowTarget && *aWindowTarget) {
9074 // We've already done our owner-inheriting. Mask out that bit, so we
9075 // don't try inheriting an owner from the target window if we came up
9076 // with a null owner above.
9077 aFlags = aFlags & ~INTERNAL_LOAD_FLAGS_INHERIT_OWNER;
9078
9079 bool isNewWindow = false;
9080 if (!targetDocShell) {
9081 // If the docshell's document is sandboxed, only open a new window
9082 // if the document's SANDBOXED_AUXILLARY_NAVIGATION flag is not set.
9083 // (i.e. if allow-popups is specified)
9084 NS_ENSURE_TRUE(mContentViewer, NS_ERROR_FAILURE);
9085 nsIDocument* doc = mContentViewer->GetDocument();
9086 uint32_t sandboxFlags = 0;
9087
9088 if (doc) {
9089 sandboxFlags = doc->GetSandboxFlags();
9090 if (sandboxFlags & SANDBOXED_AUXILIARY_NAVIGATION) {
9091 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
9092 }
9093 }
9094
9095 nsCOMPtr<nsPIDOMWindow> win =
9096 do_GetInterface(GetAsSupports(this));
9097 NS_ENSURE_TRUE(win, NS_ERROR_NOT_AVAILABLE);
9098
9099 nsDependentString name(aWindowTarget);
9100 nsCOMPtr<nsIDOMWindow> newWin;
9101 nsAutoCString spec;
9102 if (aURI)
9103 aURI->GetSpec(spec);
9104 rv = win->OpenNoNavigate(NS_ConvertUTF8toUTF16(spec),
9105 name, // window name
9106 EmptyString(), // Features
9107 getter_AddRefs(newWin));
9108
9109 // In some cases the Open call doesn't actually result in a new
9110 // window being opened. We can detect these cases by examining the
9111 // document in |newWin|, if any.
9112 nsCOMPtr<nsPIDOMWindow> piNewWin = do_QueryInterface(newWin);
9113 if (piNewWin) {
9114 nsCOMPtr<nsIDocument> newDoc = piNewWin->GetExtantDoc();
9115 if (!newDoc || newDoc->IsInitialDocument()) {
9116 isNewWindow = true;
9117 aFlags |= INTERNAL_LOAD_FLAGS_FIRST_LOAD;
9118 }
9119 }
9120
9121 nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(newWin);
9122 targetDocShell = do_QueryInterface(webNav);
9123 }
9124
9125 //
9126 // Transfer the load to the target DocShell... Pass nullptr as the
9127 // window target name from to prevent recursive retargeting!
9128 //
9129 if (NS_SUCCEEDED(rv) && targetDocShell) {
9130 rv = targetDocShell->InternalLoad(aURI,
9131 aReferrer,
9132 owner,
9133 aFlags,
9134 nullptr, // No window target
9135 aTypeHint,
9136 NullString(), // No forced download
9137 aPostData,
9138 aHeadersData,
9139 aLoadType,
9140 aSHEntry,
9141 aFirstParty,
9142 aSrcdoc,
9143 aSourceDocShell,
9144 aBaseURI,
9145 aDocShell,
9146 aRequest);
9147 if (rv == NS_ERROR_NO_CONTENT) {
9148 // XXXbz except we never reach this code!
9149 if (isNewWindow) {
9150 //
9151 // At this point, a new window has been created, but the
9152 // URI did not have any data associated with it...
9153 //
9154 // So, the best we can do, is to tear down the new window
9155 // that was just created!
9156 //
9157 nsCOMPtr<nsIDOMWindow> domWin =
9158 do_GetInterface(targetDocShell);
9159 if (domWin) {
9160 domWin->Close();
9161 }
9162 }
9163 //
9164 // NS_ERROR_NO_CONTENT should not be returned to the
9165 // caller... This is an internal error code indicating that
9166 // the URI had no data associated with it - probably a
9167 // helper-app style protocol (ie. mailto://)
9168 //
9169 rv = NS_OK;
9170 }
9171 else if (isNewWindow) {
9172 // XXX: Once new windows are created hidden, the new
9173 // window will need to be made visible... For now,
9174 // do nothing.
9175 }
9176 }
9177
9178 // Else we ran out of memory, or were a popup and got blocked,
9179 // or something.
9180
9181 return rv;
9182 }
9183
9184 //
9185 // Load is being targetted at this docshell so return an error if the
9186 // docshell is in the process of being destroyed.
9187 //
9188 if (mIsBeingDestroyed) {
9189 return NS_ERROR_FAILURE;
9190 }
9191
9192 NS_ENSURE_STATE(!HasUnloadedParent());
9193
9194 rv = CheckLoadingPermissions();
9195 if (NS_FAILED(rv)) {
9196 return rv;
9197 }
9198
9199 if (mFiredUnloadEvent) {
9200 if (IsOKToLoadURI(aURI)) {
9201 NS_PRECONDITION(!aWindowTarget || !*aWindowTarget,
9202 "Shouldn't have a window target here!");
9203
9204 // If this is a replace load, make whatever load triggered
9205 // the unload event also a replace load, so we don't
9206 // create extra history entries.
9207 if (LOAD_TYPE_HAS_FLAGS(aLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
9208 mLoadType = LOAD_NORMAL_REPLACE;
9209 }
9210
9211 // Do this asynchronously
9212 nsCOMPtr<nsIRunnable> ev =
9213 new InternalLoadEvent(this, aURI, aReferrer, aOwner, aFlags,
9214 aTypeHint, aPostData, aHeadersData,
9215 aLoadType, aSHEntry, aFirstParty, aSrcdoc,
9216 aSourceDocShell, aBaseURI);
9217 return NS_DispatchToCurrentThread(ev);
9218 }
9219
9220 // Just ignore this load attempt
9221 return NS_OK;
9222 }
9223
9224 // If a source docshell has been passed, check to see if we are sandboxed
9225 // from it as the result of an iframe or CSP sandbox.
9226 if (aSourceDocShell && aSourceDocShell->IsSandboxedFrom(this)) {
9227 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
9228 }
9229
9230 // If this docshell is owned by a frameloader, make sure to cancel
9231 // possible frameloader initialization before loading a new page.
9232 nsCOMPtr<nsIDocShellTreeItem> parent;
9233 GetParent(getter_AddRefs(parent));
9234 if (parent) {
9235 nsCOMPtr<nsIDocument> doc = do_GetInterface(parent);
9236 if (doc) {
9237 doc->TryCancelFrameLoaderInitialization(this);
9238 }
9239 }
9240
9241 // Before going any further vet loads initiated by external programs.
9242 if (aLoadType == LOAD_NORMAL_EXTERNAL) {
9243 // Disallow external chrome: loads targetted at content windows
9244 bool isChrome = false;
9245 if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome)) && isChrome) {
9246 NS_WARNING("blocked external chrome: url -- use '-chrome' option");
9247 return NS_ERROR_FAILURE;
9248 }
9249
9250 // clear the decks to prevent context bleed-through (bug 298255)
9251 rv = CreateAboutBlankContentViewer(nullptr, nullptr);
9252 if (NS_FAILED(rv))
9253 return NS_ERROR_FAILURE;
9254
9255 // reset loadType so we don't have to add lots of tests for
9256 // LOAD_NORMAL_EXTERNAL after this point
9257 aLoadType = LOAD_NORMAL;
9258 }
9259
9260 mAllowKeywordFixup =
9261 (aFlags & INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) != 0;
9262 mURIResultedInDocument = false; // reset the clock...
9263
9264 if (aLoadType == LOAD_NORMAL ||
9265 aLoadType == LOAD_STOP_CONTENT ||
9266 LOAD_TYPE_HAS_FLAGS(aLoadType, LOAD_FLAGS_REPLACE_HISTORY) ||
9267 aLoadType == LOAD_HISTORY ||
9268 aLoadType == LOAD_LINK) {
9269
9270 nsCOMPtr<nsIURI> currentURI = mCurrentURI;
9271 // Split currentURI and aURI on the '#' character. Make sure we read
9272 // the return values of SplitURIAtHash; if it fails, we don't want to
9273 // allow a short-circuited navigation.
9274 nsAutoCString curBeforeHash, curHash, newBeforeHash, newHash;
9275 nsresult splitRv1, splitRv2;
9276 splitRv1 = currentURI ?
9277 nsContentUtils::SplitURIAtHash(currentURI,
9278 curBeforeHash, curHash) :
9279 NS_ERROR_FAILURE;
9280 splitRv2 = nsContentUtils::SplitURIAtHash(aURI, newBeforeHash, newHash);
9281
9282 bool sameExceptHashes = NS_SUCCEEDED(splitRv1) &&
9283 NS_SUCCEEDED(splitRv2) &&
9284 curBeforeHash.Equals(newBeforeHash);
9285
9286 if (!sameExceptHashes && sURIFixup && currentURI &&
9287 NS_SUCCEEDED(splitRv2)) {
9288 // Maybe aURI came from the exposable form of currentURI?
9289 nsCOMPtr<nsIURI> currentExposableURI;
9290 rv = sURIFixup->CreateExposableURI(currentURI,
9291 getter_AddRefs(currentExposableURI));
9292 NS_ENSURE_SUCCESS(rv, rv);
9293 splitRv1 = nsContentUtils::SplitURIAtHash(currentExposableURI,
9294 curBeforeHash, curHash);
9295 sameExceptHashes = NS_SUCCEEDED(splitRv1) &&
9296 curBeforeHash.Equals(newBeforeHash);
9297 }
9298
9299 bool historyNavBetweenSameDoc = false;
9300 if (mOSHE && aSHEntry) {
9301 // We're doing a history load.
9302
9303 mOSHE->SharesDocumentWith(aSHEntry, &historyNavBetweenSameDoc);
9304
9305 #ifdef DEBUG
9306 if (historyNavBetweenSameDoc) {
9307 nsCOMPtr<nsIInputStream> currentPostData;
9308 mOSHE->GetPostData(getter_AddRefs(currentPostData));
9309 NS_ASSERTION(currentPostData == aPostData,
9310 "Different POST data for entries for the same page?");
9311 }
9312 #endif
9313 }
9314
9315 // A short-circuited load happens when we navigate between two SHEntries
9316 // for the same document. We do a short-circuited load under two
9317 // circumstances. Either
9318 //
9319 // a) we're navigating between two different SHEntries which share a
9320 // document, or
9321 //
9322 // b) we're navigating to a new shentry whose URI differs from the
9323 // current URI only in its hash, the new hash is non-empty, and
9324 // we're not doing a POST.
9325 //
9326 // The restriction tha the SHEntries in (a) must be different ensures
9327 // that history.go(0) and the like trigger full refreshes, rather than
9328 // short-circuited loads.
9329 bool doShortCircuitedLoad =
9330 (historyNavBetweenSameDoc && mOSHE != aSHEntry) ||
9331 (!aSHEntry && aPostData == nullptr &&
9332 sameExceptHashes && !newHash.IsEmpty());
9333
9334 if (doShortCircuitedLoad) {
9335 // Save the position of the scrollers.
9336 nscoord cx = 0, cy = 0;
9337 GetCurScrollPos(ScrollOrientation_X, &cx);
9338 GetCurScrollPos(ScrollOrientation_Y, &cy);
9339
9340 // ScrollToAnchor doesn't necessarily cause us to scroll the window;
9341 // the function decides whether a scroll is appropriate based on the
9342 // arguments it receives. But even if we don't end up scrolling,
9343 // ScrollToAnchor performs other important tasks, such as informing
9344 // the presShell that we have a new hash. See bug 680257.
9345 rv = ScrollToAnchor(curHash, newHash, aLoadType);
9346 NS_ENSURE_SUCCESS(rv, rv);
9347
9348 // Reset mLoadType to its original value once we exit this block,
9349 // because this short-circuited load might have started after a
9350 // normal, network load, and we don't want to clobber its load type.
9351 // See bug 737307.
9352 AutoRestore<uint32_t> loadTypeResetter(mLoadType);
9353
9354 // If a non-short-circuit load (i.e., a network load) is pending,
9355 // make this a replacement load, so that we don't add a SHEntry here
9356 // and the network load goes into the SHEntry it expects to.
9357 if (JustStartedNetworkLoad() && (aLoadType & LOAD_CMD_NORMAL)) {
9358 mLoadType = LOAD_NORMAL_REPLACE;
9359 }
9360 else {
9361 mLoadType = aLoadType;
9362 }
9363
9364 mURIResultedInDocument = true;
9365
9366 nsCOMPtr<nsISHEntry> oldLSHE = mLSHE;
9367
9368 /* we need to assign mLSHE to aSHEntry right here, so that on History loads,
9369 * SetCurrentURI() called from OnNewURI() will send proper
9370 * onLocationChange() notifications to the browser to update
9371 * back/forward buttons.
9372 */
9373 SetHistoryEntry(&mLSHE, aSHEntry);
9374
9375 /* This is a anchor traversal with in the same page.
9376 * call OnNewURI() so that, this traversal will be
9377 * recorded in session and global history.
9378 */
9379 nsCOMPtr<nsISupports> owner;
9380 if (mOSHE) {
9381 mOSHE->GetOwner(getter_AddRefs(owner));
9382 }
9383 // Pass true for aCloneSHChildren, since we're not
9384 // changing documents here, so all of our subframes are
9385 // still relevant to the new session history entry.
9386 //
9387 // It also makes OnNewURI(...) set LOCATION_CHANGE_SAME_DOCUMENT
9388 // flag on firing onLocationChange(...).
9389 // Anyway, aCloneSHChildren param is simply reflecting
9390 // doShortCircuitedLoad in this scope.
9391 OnNewURI(aURI, nullptr, owner, mLoadType, true, true, true);
9392
9393 nsCOMPtr<nsIInputStream> postData;
9394 nsCOMPtr<nsISupports> cacheKey;
9395
9396 if (mOSHE) {
9397 /* save current position of scroller(s) (bug 59774) */
9398 mOSHE->SetScrollPosition(cx, cy);
9399 // Get the postdata and page ident from the current page, if
9400 // the new load is being done via normal means. Note that
9401 // "normal means" can be checked for just by checking for
9402 // LOAD_CMD_NORMAL, given the loadType and allowScroll check
9403 // above -- it filters out some LOAD_CMD_NORMAL cases that we
9404 // wouldn't want here.
9405 if (aLoadType & LOAD_CMD_NORMAL) {
9406 mOSHE->GetPostData(getter_AddRefs(postData));
9407 mOSHE->GetCacheKey(getter_AddRefs(cacheKey));
9408
9409 // Link our new SHEntry to the old SHEntry's back/forward
9410 // cache data, since the two SHEntries correspond to the
9411 // same document.
9412 if (mLSHE)
9413 mLSHE->AdoptBFCacheEntry(mOSHE);
9414 }
9415 }
9416
9417 /* Assign mOSHE to mLSHE. This will either be a new entry created
9418 * by OnNewURI() for normal loads or aSHEntry for history loads.
9419 */
9420 if (mLSHE) {
9421 SetHistoryEntry(&mOSHE, mLSHE);
9422 // Save the postData obtained from the previous page
9423 // in to the session history entry created for the
9424 // anchor page, so that any history load of the anchor
9425 // page will restore the appropriate postData.
9426 if (postData)
9427 mOSHE->SetPostData(postData);
9428
9429 // Make sure we won't just repost without hitting the
9430 // cache first
9431 if (cacheKey)
9432 mOSHE->SetCacheKey(cacheKey);
9433 }
9434
9435 /* restore previous position of scroller(s), if we're moving
9436 * back in history (bug 59774)
9437 */
9438 if (mOSHE && (aLoadType == LOAD_HISTORY || aLoadType == LOAD_RELOAD_NORMAL))
9439 {
9440 nscoord bx, by;
9441 mOSHE->GetScrollPosition(&bx, &by);
9442 SetCurScrollPosEx(bx, by);
9443 }
9444
9445 /* Restore the original LSHE if we were loading something
9446 * while short-circuited load was initiated.
9447 */
9448 SetHistoryEntry(&mLSHE, oldLSHE);
9449 /* Set the title for the SH entry for this target url. so that
9450 * SH menus in go/back/forward buttons won't be empty for this.
9451 */
9452 if (mSessionHistory) {
9453 int32_t index = -1;
9454 mSessionHistory->GetIndex(&index);
9455 nsCOMPtr<nsISHEntry> shEntry;
9456 mSessionHistory->GetEntryAtIndex(index, false,
9457 getter_AddRefs(shEntry));
9458 NS_ENSURE_TRUE(shEntry, NS_ERROR_FAILURE);
9459 shEntry->SetTitle(mTitle);
9460 }
9461
9462 /* Set the title for the Global History entry for this anchor url.
9463 */
9464 if (mUseGlobalHistory && !mInPrivateBrowsing) {
9465 nsCOMPtr<IHistory> history = services::GetHistoryService();
9466 if (history) {
9467 history->SetURITitle(aURI, mTitle);
9468 }
9469 else if (mGlobalHistory) {
9470 mGlobalHistory->SetPageTitle(aURI, mTitle);
9471 }
9472 }
9473
9474 // Set the doc's URI according to the new history entry's URI.
9475 nsCOMPtr<nsIDocument> doc =
9476 do_GetInterface(GetAsSupports(this));
9477 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
9478 doc->SetDocumentURI(aURI);
9479
9480 SetDocCurrentStateObj(mOSHE);
9481
9482 // Dispatch the popstate and hashchange events, as appropriate.
9483 //
9484 // The event dispatch below can cause us to re-enter script and
9485 // destroy the docshell, nulling out mScriptGlobal. Hold a stack
9486 // reference to avoid null derefs. See bug 914521.
9487 nsRefPtr<nsGlobalWindow> win = mScriptGlobal;
9488 if (win) {
9489 // Fire a hashchange event URIs differ, and only in their hashes.
9490 bool doHashchange = sameExceptHashes && !curHash.Equals(newHash);
9491
9492 if (historyNavBetweenSameDoc || doHashchange) {
9493 win->DispatchSyncPopState();
9494 }
9495
9496 if (doHashchange) {
9497 // Note that currentURI hasn't changed because it's on the
9498 // stack, so we can just use it directly as the old URI.
9499 win->DispatchAsyncHashchange(currentURI, aURI);
9500 }
9501 }
9502
9503 // Inform the favicon service that the favicon for oldURI also
9504 // applies to aURI.
9505 CopyFavicon(currentURI, aURI, mInPrivateBrowsing);
9506
9507 return NS_OK;
9508 }
9509 }
9510
9511 // mContentViewer->PermitUnload can destroy |this| docShell, which
9512 // causes the next call of CanSavePresentation to crash.
9513 // Hold onto |this| until we return, to prevent a crash from happening.
9514 // (bug#331040)
9515 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
9516
9517 // Don't init timing for javascript:, since it generally doesn't
9518 // actually start a load or anything. If it does, we'll init
9519 // timing then, from OnStateChange.
9520
9521 // XXXbz mTiming should know what channel it's for, so we don't
9522 // need this hackery. Note that this is still broken in cases
9523 // when we're loading something that's not javascript: and the
9524 // beforeunload handler denies the load. That will screw up
9525 // timing for the next load!
9526 if (!bIsJavascript) {
9527 MaybeInitTiming();
9528 }
9529 bool timeBeforeUnload = aFileName.IsVoid();
9530 if (mTiming && timeBeforeUnload) {
9531 mTiming->NotifyBeforeUnload();
9532 }
9533 // Check if the page doesn't want to be unloaded. The javascript:
9534 // protocol handler deals with this for javascript: URLs.
9535 if (!bIsJavascript && aFileName.IsVoid() && mContentViewer) {
9536 bool okToUnload;
9537 rv = mContentViewer->PermitUnload(false, &okToUnload);
9538
9539 if (NS_SUCCEEDED(rv) && !okToUnload) {
9540 // The user chose not to unload the page, interrupt the
9541 // load.
9542 return NS_OK;
9543 }
9544 }
9545
9546 if (mTiming && timeBeforeUnload) {
9547 mTiming->NotifyUnloadAccepted(mCurrentURI);
9548 }
9549
9550 // Check for saving the presentation here, before calling Stop().
9551 // This is necessary so that we can catch any pending requests.
9552 // Since the new request has not been created yet, we pass null for the
9553 // new request parameter.
9554 // Also pass nullptr for the document, since it doesn't affect the return
9555 // value for our purposes here.
9556 bool savePresentation = CanSavePresentation(aLoadType, nullptr, nullptr);
9557
9558 // Don't stop current network activity for javascript: URL's since
9559 // they might not result in any data, and thus nothing should be
9560 // stopped in those cases. In the case where they do result in
9561 // data, the javascript: URL channel takes care of stopping
9562 // current network activity.
9563 if (!bIsJavascript && aFileName.IsVoid()) {
9564 // Stop any current network activity.
9565 // Also stop content if this is a zombie doc. otherwise
9566 // the onload will be delayed by other loads initiated in the
9567 // background by the first document that
9568 // didn't fully load before the next load was initiated.
9569 // If not a zombie, don't stop content until data
9570 // starts arriving from the new URI...
9571
9572 nsCOMPtr<nsIContentViewer> zombieViewer;
9573 if (mContentViewer) {
9574 mContentViewer->GetPreviousViewer(getter_AddRefs(zombieViewer));
9575 }
9576
9577 if (zombieViewer ||
9578 LOAD_TYPE_HAS_FLAGS(aLoadType, LOAD_FLAGS_STOP_CONTENT)) {
9579 rv = Stop(nsIWebNavigation::STOP_ALL);
9580 } else {
9581 rv = Stop(nsIWebNavigation::STOP_NETWORK);
9582 }
9583
9584 if (NS_FAILED(rv))
9585 return rv;
9586 }
9587
9588 mLoadType = aLoadType;
9589
9590 // mLSHE should be assigned to aSHEntry, only after Stop() has
9591 // been called. But when loading an error page, do not clear the
9592 // mLSHE for the real page.
9593 if (mLoadType != LOAD_ERROR_PAGE)
9594 SetHistoryEntry(&mLSHE, aSHEntry);
9595
9596 mSavingOldViewer = savePresentation;
9597
9598 // If we have a saved content viewer in history, restore and show it now.
9599 if (aSHEntry && (mLoadType & LOAD_CMD_HISTORY)) {
9600 // Make sure our history ID points to the same ID as
9601 // SHEntry's docshell ID.
9602 aSHEntry->GetDocshellID(&mHistoryID);
9603
9604 // It's possible that the previous viewer of mContentViewer is the
9605 // viewer that will end up in aSHEntry when it gets closed. If that's
9606 // the case, we need to go ahead and force it into its shentry so we
9607 // can restore it.
9608 if (mContentViewer) {
9609 nsCOMPtr<nsIContentViewer> prevViewer;
9610 mContentViewer->GetPreviousViewer(getter_AddRefs(prevViewer));
9611 if (prevViewer) {
9612 #ifdef DEBUG
9613 nsCOMPtr<nsIContentViewer> prevPrevViewer;
9614 prevViewer->GetPreviousViewer(getter_AddRefs(prevPrevViewer));
9615 NS_ASSERTION(!prevPrevViewer, "Should never have viewer chain here");
9616 #endif
9617 nsCOMPtr<nsISHEntry> viewerEntry;
9618 prevViewer->GetHistoryEntry(getter_AddRefs(viewerEntry));
9619 if (viewerEntry == aSHEntry) {
9620 // Make sure this viewer ends up in the right place
9621 mContentViewer->SetPreviousViewer(nullptr);
9622 prevViewer->Destroy();
9623 }
9624 }
9625 }
9626 nsCOMPtr<nsISHEntry> oldEntry = mOSHE;
9627 bool restoring;
9628 rv = RestorePresentation(aSHEntry, &restoring);
9629 if (restoring)
9630 return rv;
9631
9632 // We failed to restore the presentation, so clean up.
9633 // Both the old and new history entries could potentially be in
9634 // an inconsistent state.
9635 if (NS_FAILED(rv)) {
9636 if (oldEntry)
9637 oldEntry->SyncPresentationState();
9638
9639 aSHEntry->SyncPresentationState();
9640 }
9641 }
9642
9643 nsAutoString srcdoc;
9644 if (aFlags & INTERNAL_LOAD_FLAGS_IS_SRCDOC)
9645 srcdoc = aSrcdoc;
9646 else
9647 srcdoc = NullString();
9648
9649 mozilla::net::SeerPredict(aURI, nullptr, nsINetworkSeer::PREDICT_LOAD,
9650 this, nullptr);
9651
9652 nsCOMPtr<nsIRequest> req;
9653 rv = DoURILoad(aURI, aReferrer,
9654 !(aFlags & INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER),
9655 owner, aTypeHint, aFileName, aPostData, aHeadersData,
9656 aFirstParty, aDocShell, getter_AddRefs(req),
9657 (aFlags & INTERNAL_LOAD_FLAGS_FIRST_LOAD) != 0,
9658 (aFlags & INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER) != 0,
9659 (aFlags & INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES) != 0,
9660 srcdoc, aBaseURI);
9661 if (req && aRequest)
9662 NS_ADDREF(*aRequest = req);
9663
9664 if (NS_FAILED(rv)) {
9665 nsCOMPtr<nsIChannel> chan(do_QueryInterface(req));
9666 DisplayLoadError(rv, aURI, nullptr, chan);
9667 }
9668
9669 return rv;
9670 }
9671
9672 nsIPrincipal*
9673 nsDocShell::GetInheritedPrincipal(bool aConsiderCurrentDocument)
9674 {
9675 nsCOMPtr<nsIDocument> document;
9676 bool inheritedFromCurrent = false;
9677
9678 if (aConsiderCurrentDocument && mContentViewer) {
9679 document = mContentViewer->GetDocument();
9680 inheritedFromCurrent = true;
9681 }
9682
9683 if (!document) {
9684 nsCOMPtr<nsIDocShellTreeItem> parentItem;
9685 GetSameTypeParent(getter_AddRefs(parentItem));
9686 if (parentItem) {
9687 document = do_GetInterface(parentItem);
9688 }
9689 }
9690
9691 if (!document) {
9692 if (!aConsiderCurrentDocument) {
9693 return nullptr;
9694 }
9695
9696 // Make sure we end up with _something_ as the principal no matter
9697 // what.
9698 EnsureContentViewer(); // If this fails, we'll just get a null
9699 // docViewer and bail.
9700
9701 if (!mContentViewer)
9702 return nullptr;
9703 document = mContentViewer->GetDocument();
9704 }
9705
9706 //-- Get the document's principal
9707 if (document) {
9708 nsIPrincipal *docPrincipal = document->NodePrincipal();
9709
9710 // Don't allow loads in typeContent docShells to inherit the system
9711 // principal from existing documents.
9712 if (inheritedFromCurrent &&
9713 mItemType == typeContent &&
9714 nsContentUtils::IsSystemPrincipal(docPrincipal)) {
9715 return nullptr;
9716 }
9717
9718 return docPrincipal;
9719 }
9720
9721 return nullptr;
9722 }
9723
9724 nsresult
9725 nsDocShell::DoURILoad(nsIURI * aURI,
9726 nsIURI * aReferrerURI,
9727 bool aSendReferrer,
9728 nsISupports * aOwner,
9729 const char * aTypeHint,
9730 const nsAString & aFileName,
9731 nsIInputStream * aPostData,
9732 nsIInputStream * aHeadersData,
9733 bool aFirstParty,
9734 nsIDocShell ** aDocShell,
9735 nsIRequest ** aRequest,
9736 bool aIsNewWindowTarget,
9737 bool aBypassClassifier,
9738 bool aForceAllowCookies,
9739 const nsAString &aSrcdoc,
9740 nsIURI * aBaseURI)
9741 {
9742 #ifdef MOZ_VISUAL_EVENT_TRACER
9743 nsAutoCString urlSpec;
9744 aURI->GetAsciiSpec(urlSpec);
9745 MOZ_EVENT_TRACER_NAME_OBJECT(this, urlSpec.BeginReading());
9746 MOZ_EVENT_TRACER_EXEC(this, "docshell::pageload");
9747 #endif
9748
9749 nsresult rv;
9750 nsCOMPtr<nsIURILoader> uriLoader;
9751
9752 uriLoader = do_GetService(NS_URI_LOADER_CONTRACTID, &rv);
9753 if (NS_FAILED(rv)) return rv;
9754
9755 nsLoadFlags loadFlags = mDefaultLoadFlags;
9756 if (aFirstParty) {
9757 // tag first party URL loads
9758 loadFlags |= nsIChannel::LOAD_INITIAL_DOCUMENT_URI;
9759 }
9760
9761 if (mLoadType == LOAD_ERROR_PAGE) {
9762 // Error pages are LOAD_BACKGROUND
9763 loadFlags |= nsIChannel::LOAD_BACKGROUND;
9764 }
9765
9766 // check for Content Security Policy to pass along with the
9767 // new channel we are creating
9768 nsCOMPtr<nsIChannelPolicy> channelPolicy;
9769 if (IsFrame()) {
9770 // check the parent docshell for a CSP
9771 nsCOMPtr<nsIContentSecurityPolicy> csp;
9772 nsCOMPtr<nsIDocShellTreeItem> parentItem;
9773 GetSameTypeParent(getter_AddRefs(parentItem));
9774 nsCOMPtr<nsIDocument> doc = do_GetInterface(parentItem);
9775 if (doc) {
9776 rv = doc->NodePrincipal()->GetCsp(getter_AddRefs(csp));
9777 NS_ENSURE_SUCCESS(rv, rv);
9778 if (csp) {
9779 channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
9780 channelPolicy->SetContentSecurityPolicy(csp);
9781 channelPolicy->SetLoadType(nsIContentPolicy::TYPE_SUBDOCUMENT);
9782 }
9783 }
9784
9785 // Only allow view-source scheme in top-level docshells. view-source is
9786 // the only scheme to which this applies at the moment due to potential
9787 // timing attacks to read data from cross-origin iframes. If this widens
9788 // we should add a protocol flag for whether the scheme is allowed in
9789 // frames and use something like nsNetUtil::NS_URIChainHasFlags.
9790 nsCOMPtr<nsIURI> tempURI = aURI;
9791 nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(tempURI);
9792 while (nestedURI) {
9793 // view-source should always be an nsINestedURI, loop and check the
9794 // scheme on this and all inner URIs that are also nested URIs.
9795 bool isViewSource = false;
9796 rv = tempURI->SchemeIs("view-source", &isViewSource);
9797 if (NS_FAILED(rv) || isViewSource) {
9798 return NS_ERROR_UNKNOWN_PROTOCOL;
9799 }
9800 nestedURI->GetInnerURI(getter_AddRefs(tempURI));
9801 nestedURI = do_QueryInterface(tempURI);
9802 }
9803 }
9804
9805 // open a channel for the url
9806 nsCOMPtr<nsIChannel> channel;
9807
9808 bool isSrcdoc = !aSrcdoc.IsVoid();
9809 if (!isSrcdoc) {
9810 rv = NS_NewChannel(getter_AddRefs(channel),
9811 aURI,
9812 nullptr,
9813 nullptr,
9814 static_cast<nsIInterfaceRequestor *>(this),
9815 loadFlags,
9816 channelPolicy);
9817 if (NS_FAILED(rv)) {
9818 if (rv == NS_ERROR_UNKNOWN_PROTOCOL) {
9819 // This is a uri with a protocol scheme we don't know how
9820 // to handle. Embedders might still be interested in
9821 // handling the load, though, so we fire a notification
9822 // before throwing the load away.
9823 bool abort = false;
9824 nsresult rv2 = mContentListener->OnStartURIOpen(aURI, &abort);
9825 if (NS_SUCCEEDED(rv2) && abort) {
9826 // Hey, they're handling the load for us! How convenient!
9827 return NS_OK;
9828 }
9829 }
9830 return rv;
9831 }
9832 if (aBaseURI) {
9833 nsCOMPtr<nsIViewSourceChannel> vsc = do_QueryInterface(channel);
9834 if (vsc) {
9835 vsc->SetBaseURI(aBaseURI);
9836 }
9837 }
9838 }
9839 else {
9840 nsAutoCString scheme;
9841 rv = aURI->GetScheme(scheme);
9842 NS_ENSURE_SUCCESS(rv,rv);
9843 bool isViewSource;
9844 aURI->SchemeIs("view-source",&isViewSource);
9845
9846 if (isViewSource) {
9847 nsViewSourceHandler *vsh = nsViewSourceHandler::GetInstance();
9848 NS_ENSURE_TRUE(vsh,NS_ERROR_FAILURE);
9849
9850 rv = vsh->NewSrcdocChannel(aURI, aSrcdoc, aBaseURI,
9851 getter_AddRefs(channel));
9852 NS_ENSURE_SUCCESS(rv, rv);
9853 }
9854 else {
9855 rv = NS_NewInputStreamChannel(getter_AddRefs(channel),aURI,
9856 aSrcdoc,
9857 NS_LITERAL_CSTRING("text/html"),
9858 true);
9859 NS_ENSURE_SUCCESS(rv, rv);
9860 nsCOMPtr<nsIInputStreamChannel> isc = do_QueryInterface(channel);
9861 MOZ_ASSERT(isc);
9862 isc->SetBaseURI(aBaseURI);
9863 }
9864 }
9865
9866 nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
9867 do_QueryInterface(channel);
9868 if (appCacheChannel) {
9869 // Any document load should not inherit application cache.
9870 appCacheChannel->SetInheritApplicationCache(false);
9871
9872 // Loads with the correct permissions should check for a matching
9873 // application cache.
9874 if (GeckoProcessType_Default != XRE_GetProcessType()) {
9875 // Permission will be checked in the parent process
9876 appCacheChannel->SetChooseApplicationCache(true);
9877 } else {
9878 nsCOMPtr<nsIScriptSecurityManager> secMan =
9879 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
9880
9881 if (secMan) {
9882 nsCOMPtr<nsIPrincipal> principal;
9883 secMan->GetDocShellCodebasePrincipal(aURI, this, getter_AddRefs(principal));
9884 appCacheChannel->SetChooseApplicationCache(
9885 NS_ShouldCheckAppCache(principal, mInPrivateBrowsing));
9886 }
9887 }
9888 }
9889
9890 // Make sure to give the caller a channel if we managed to create one
9891 // This is important for correct error page/session history interaction
9892 if (aRequest)
9893 NS_ADDREF(*aRequest = channel);
9894
9895 channel->SetOriginalURI(aURI);
9896 if (aTypeHint && *aTypeHint) {
9897 channel->SetContentType(nsDependentCString(aTypeHint));
9898 mContentTypeHint = aTypeHint;
9899 } else {
9900 mContentTypeHint.Truncate();
9901 }
9902
9903 if (!aFileName.IsVoid()) {
9904 rv = channel->SetContentDisposition(nsIChannel::DISPOSITION_ATTACHMENT);
9905 NS_ENSURE_SUCCESS(rv, rv);
9906 if (!aFileName.IsEmpty()) {
9907 rv = channel->SetContentDispositionFilename(aFileName);
9908 NS_ENSURE_SUCCESS(rv, rv);
9909 }
9910 }
9911
9912 if (mLoadType == LOAD_NORMAL_ALLOW_MIXED_CONTENT ||
9913 mLoadType == LOAD_RELOAD_ALLOW_MIXED_CONTENT) {
9914 rv = SetMixedContentChannel(channel);
9915 NS_ENSURE_SUCCESS(rv, rv);
9916 } else if (mMixedContentChannel) {
9917 /*
9918 * If the user "Disables Protection on This Page", we call
9919 * SetMixedContentChannel for the first time, otherwise
9920 * mMixedContentChannel is still null.
9921 * Later, if the new channel passes a same orign check, we remember the
9922 * users decision by calling SetMixedContentChannel using the new channel.
9923 * This way, the user does not have to click the disable protection button
9924 * over and over for browsing the same site.
9925 */
9926 rv = nsContentUtils::CheckSameOrigin(mMixedContentChannel, channel);
9927 if (NS_FAILED(rv) || NS_FAILED(SetMixedContentChannel(channel))) {
9928 SetMixedContentChannel(nullptr);
9929 }
9930 }
9931
9932 //hack
9933 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
9934 nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal(do_QueryInterface(channel));
9935 if (httpChannelInternal) {
9936 if (aForceAllowCookies) {
9937 httpChannelInternal->SetForceAllowThirdPartyCookie(true);
9938 }
9939 if (aFirstParty) {
9940 httpChannelInternal->SetDocumentURI(aURI);
9941 } else {
9942 httpChannelInternal->SetDocumentURI(aReferrerURI);
9943 }
9944 }
9945
9946 nsCOMPtr<nsIWritablePropertyBag2> props(do_QueryInterface(channel));
9947 if (props)
9948 {
9949 // save true referrer for those who need it (e.g. xpinstall whitelisting)
9950 // Currently only http and ftp channels support this.
9951 props->SetPropertyAsInterface(NS_LITERAL_STRING("docshell.internalReferrer"),
9952 aReferrerURI);
9953 }
9954
9955 //
9956 // If this is a HTTP channel, then set up the HTTP specific information
9957 // (ie. POST data, referrer, ...)
9958 //
9959 if (httpChannel) {
9960 nsCOMPtr<nsICachingChannel> cacheChannel(do_QueryInterface(httpChannel));
9961 /* Get the cache Key from SH */
9962 nsCOMPtr<nsISupports> cacheKey;
9963 if (mLSHE) {
9964 mLSHE->GetCacheKey(getter_AddRefs(cacheKey));
9965 }
9966 else if (mOSHE) // for reload cases
9967 mOSHE->GetCacheKey(getter_AddRefs(cacheKey));
9968
9969 // figure out if we need to set the post data stream on the channel...
9970 // right now, this is only done for http channels.....
9971 if (aPostData) {
9972 // XXX it's a bit of a hack to rewind the postdata stream here but
9973 // it has to be done in case the post data is being reused multiple
9974 // times.
9975 nsCOMPtr<nsISeekableStream>
9976 postDataSeekable(do_QueryInterface(aPostData));
9977 if (postDataSeekable) {
9978 rv = postDataSeekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
9979 NS_ENSURE_SUCCESS(rv, rv);
9980 }
9981
9982 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
9983 NS_ASSERTION(uploadChannel, "http must support nsIUploadChannel");
9984
9985 // we really need to have a content type associated with this stream!!
9986 uploadChannel->SetUploadStream(aPostData, EmptyCString(), -1);
9987 /* If there is a valid postdata *and* it is a History Load,
9988 * set up the cache key on the channel, to retrieve the
9989 * data *only* from the cache. If it is a normal reload, the
9990 * cache is free to go to the server for updated postdata.
9991 */
9992 if (cacheChannel && cacheKey) {
9993 if (mLoadType == LOAD_HISTORY || mLoadType == LOAD_RELOAD_CHARSET_CHANGE) {
9994 cacheChannel->SetCacheKey(cacheKey);
9995 uint32_t loadFlags;
9996 if (NS_SUCCEEDED(channel->GetLoadFlags(&loadFlags)))
9997 channel->SetLoadFlags(loadFlags | nsICachingChannel::LOAD_ONLY_FROM_CACHE);
9998 }
9999 else if (mLoadType == LOAD_RELOAD_NORMAL)
10000 cacheChannel->SetCacheKey(cacheKey);
10001 }
10002 }
10003 else {
10004 /* If there is no postdata, set the cache key on the channel, and
10005 * do not set the LOAD_ONLY_FROM_CACHE flag, so that the channel
10006 * will be free to get it from net if it is not found in cache.
10007 * New cache may use it creatively on CGI pages with GET
10008 * method and even on those that say "no-cache"
10009 */
10010 if (mLoadType == LOAD_HISTORY || mLoadType == LOAD_RELOAD_NORMAL
10011 || mLoadType == LOAD_RELOAD_CHARSET_CHANGE) {
10012 if (cacheChannel && cacheKey)
10013 cacheChannel->SetCacheKey(cacheKey);
10014 }
10015 }
10016 if (aHeadersData) {
10017 rv = AddHeadersToChannel(aHeadersData, httpChannel);
10018 }
10019 // Set the referrer explicitly
10020 if (aReferrerURI && aSendReferrer) {
10021 // Referrer is currenly only set for link clicks here.
10022 httpChannel->SetReferrer(aReferrerURI);
10023 }
10024 }
10025
10026 nsCOMPtr<nsIPrincipal> ownerPrincipal;
10027
10028 // If the content being loaded should be sandboxed with respect to origin
10029 // we need to create a new null principal here, and then tell
10030 // nsContentUtils::SetUpChannelOwner to force it to be set as the
10031 // channel owner.
10032 if (mSandboxFlags & SANDBOXED_ORIGIN) {
10033 ownerPrincipal = do_CreateInstance("@mozilla.org/nullprincipal;1");
10034 } else {
10035 // Not sandboxed - we allow the content to assume its natural owner.
10036 ownerPrincipal = do_QueryInterface(aOwner);
10037 }
10038
10039 nsContentUtils::SetUpChannelOwner(ownerPrincipal, channel, aURI, true,
10040 (mSandboxFlags & SANDBOXED_ORIGIN) ||
10041 isSrcdoc);
10042
10043 nsCOMPtr<nsIScriptChannel> scriptChannel = do_QueryInterface(channel);
10044 if (scriptChannel) {
10045 // Allow execution against our context if the principals match
10046 scriptChannel->
10047 SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
10048 }
10049
10050 if (aIsNewWindowTarget) {
10051 nsCOMPtr<nsIWritablePropertyBag2> props = do_QueryInterface(channel);
10052 if (props) {
10053 props->SetPropertyAsBool(
10054 NS_LITERAL_STRING("docshell.newWindowTarget"),
10055 true);
10056 }
10057 }
10058
10059 nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(channel));
10060 if (timedChannel) {
10061 timedChannel->SetTimingEnabled(true);
10062 if (IsFrame()) {
10063 timedChannel->SetInitiatorType(NS_LITERAL_STRING("subdocument"));
10064 }
10065 }
10066
10067 rv = DoChannelLoad(channel, uriLoader, aBypassClassifier);
10068
10069 //
10070 // If the channel load failed, we failed and nsIWebProgress just ain't
10071 // gonna happen.
10072 //
10073 if (NS_SUCCEEDED(rv)) {
10074 if (aDocShell) {
10075 *aDocShell = this;
10076 NS_ADDREF(*aDocShell);
10077 }
10078 }
10079
10080 return rv;
10081 }
10082
10083 static NS_METHOD
10084 AppendSegmentToString(nsIInputStream *in,
10085 void *closure,
10086 const char *fromRawSegment,
10087 uint32_t toOffset,
10088 uint32_t count,
10089 uint32_t *writeCount)
10090 {
10091 // aFromSegment now contains aCount bytes of data.
10092
10093 nsAutoCString *buf = static_cast<nsAutoCString *>(closure);
10094 buf->Append(fromRawSegment, count);
10095
10096 // Indicate that we have consumed all of aFromSegment
10097 *writeCount = count;
10098 return NS_OK;
10099 }
10100
10101 NS_IMETHODIMP
10102 nsDocShell::AddHeadersToChannel(nsIInputStream *aHeadersData,
10103 nsIChannel *aGenericChannel)
10104 {
10105 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aGenericChannel);
10106 NS_ENSURE_STATE(httpChannel);
10107
10108 uint32_t numRead;
10109 nsAutoCString headersString;
10110 nsresult rv = aHeadersData->ReadSegments(AppendSegmentToString,
10111 &headersString,
10112 UINT32_MAX,
10113 &numRead);
10114 NS_ENSURE_SUCCESS(rv, rv);
10115
10116 // used during the manipulation of the String from the InputStream
10117 nsAutoCString headerName;
10118 nsAutoCString headerValue;
10119 int32_t crlf;
10120 int32_t colon;
10121
10122 //
10123 // Iterate over the headersString: for each "\r\n" delimited chunk,
10124 // add the value as a header to the nsIHttpChannel
10125 //
10126
10127 static const char kWhitespace[] = "\b\t\r\n ";
10128 while (true) {
10129 crlf = headersString.Find("\r\n");
10130 if (crlf == kNotFound)
10131 return NS_OK;
10132
10133 const nsCSubstring &oneHeader = StringHead(headersString, crlf);
10134
10135 colon = oneHeader.FindChar(':');
10136 if (colon == kNotFound)
10137 return NS_ERROR_UNEXPECTED;
10138
10139 headerName = StringHead(oneHeader, colon);
10140 headerValue = Substring(oneHeader, colon + 1);
10141
10142 headerName.Trim(kWhitespace);
10143 headerValue.Trim(kWhitespace);
10144
10145 headersString.Cut(0, crlf + 2);
10146
10147 //
10148 // FINALLY: we can set the header!
10149 //
10150
10151 rv = httpChannel->SetRequestHeader(headerName, headerValue, true);
10152 NS_ENSURE_SUCCESS(rv, rv);
10153 }
10154
10155 NS_NOTREACHED("oops");
10156 return NS_ERROR_UNEXPECTED;
10157 }
10158
10159 nsresult nsDocShell::DoChannelLoad(nsIChannel * aChannel,
10160 nsIURILoader * aURILoader,
10161 bool aBypassClassifier)
10162 {
10163 nsresult rv;
10164 // Mark the channel as being a document URI and allow content sniffing...
10165 nsLoadFlags loadFlags = 0;
10166 (void) aChannel->GetLoadFlags(&loadFlags);
10167 loadFlags |= nsIChannel::LOAD_DOCUMENT_URI |
10168 nsIChannel::LOAD_CALL_CONTENT_SNIFFERS;
10169
10170 // Load attributes depend on load type...
10171 switch (mLoadType) {
10172 case LOAD_HISTORY:
10173 {
10174 // Only send VALIDATE_NEVER if mLSHE's URI was never changed via
10175 // push/replaceState (bug 669671).
10176 bool uriModified = false;
10177 if (mLSHE) {
10178 mLSHE->GetURIWasModified(&uriModified);
10179 }
10180
10181 if (!uriModified)
10182 loadFlags |= nsIRequest::VALIDATE_NEVER;
10183 }
10184 break;
10185
10186 case LOAD_RELOAD_CHARSET_CHANGE:
10187 loadFlags |= nsIRequest::LOAD_FROM_CACHE;
10188 break;
10189
10190 case LOAD_RELOAD_NORMAL:
10191 case LOAD_REFRESH:
10192 loadFlags |= nsIRequest::VALIDATE_ALWAYS;
10193 break;
10194
10195 case LOAD_NORMAL_BYPASS_CACHE:
10196 case LOAD_NORMAL_BYPASS_PROXY:
10197 case LOAD_NORMAL_BYPASS_PROXY_AND_CACHE:
10198 case LOAD_NORMAL_ALLOW_MIXED_CONTENT:
10199 case LOAD_RELOAD_BYPASS_CACHE:
10200 case LOAD_RELOAD_BYPASS_PROXY:
10201 case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
10202 case LOAD_RELOAD_ALLOW_MIXED_CONTENT:
10203 case LOAD_REPLACE_BYPASS_CACHE:
10204 loadFlags |= nsIRequest::LOAD_BYPASS_CACHE |
10205 nsIRequest::LOAD_FRESH_CONNECTION;
10206 break;
10207
10208 case LOAD_NORMAL:
10209 case LOAD_LINK:
10210 // Set cache checking flags
10211 switch (Preferences::GetInt("browser.cache.check_doc_frequency", -1)) {
10212 case 0:
10213 loadFlags |= nsIRequest::VALIDATE_ONCE_PER_SESSION;
10214 break;
10215 case 1:
10216 loadFlags |= nsIRequest::VALIDATE_ALWAYS;
10217 break;
10218 case 2:
10219 loadFlags |= nsIRequest::VALIDATE_NEVER;
10220 break;
10221 }
10222 break;
10223 }
10224
10225 if (!aBypassClassifier) {
10226 loadFlags |= nsIChannel::LOAD_CLASSIFY_URI;
10227 }
10228
10229 (void) aChannel->SetLoadFlags(loadFlags);
10230
10231 uint32_t openFlags = 0;
10232 if (mLoadType == LOAD_LINK) {
10233 openFlags |= nsIURILoader::IS_CONTENT_PREFERRED;
10234 }
10235 if (!mAllowContentRetargeting) {
10236 openFlags |= nsIURILoader::DONT_RETARGET;
10237 }
10238 rv = aURILoader->OpenURI(aChannel, openFlags, this);
10239 NS_ENSURE_SUCCESS(rv, rv);
10240
10241 return NS_OK;
10242 }
10243
10244 nsresult
10245 nsDocShell::ScrollToAnchor(nsACString & aCurHash, nsACString & aNewHash,
10246 uint32_t aLoadType)
10247 {
10248 if (!mCurrentURI) {
10249 return NS_OK;
10250 }
10251
10252 nsCOMPtr<nsIPresShell> shell = GetPresShell();
10253 if (!shell) {
10254 // If we failed to get the shell, or if there is no shell,
10255 // nothing left to do here.
10256 return NS_OK;
10257 }
10258
10259 nsIScrollableFrame* rootScroll = shell->GetRootScrollFrameAsScrollable();
10260 if (rootScroll) {
10261 rootScroll->ClearDidHistoryRestore();
10262 }
10263
10264 // If we have no new anchor, we do not want to scroll, unless there is a
10265 // current anchor and we are doing a history load. So return if we have no
10266 // new anchor, and there is no current anchor or the load is not a history
10267 // load.
10268 if ((aCurHash.IsEmpty() || aLoadType != LOAD_HISTORY) &&
10269 aNewHash.IsEmpty()) {
10270 return NS_OK;
10271 }
10272
10273 // Take the '#' off aNewHash to get the ref name. (aNewHash might be empty,
10274 // but that's fine.)
10275 nsDependentCSubstring newHashName(aNewHash, 1);
10276
10277 // Both the new and current URIs refer to the same page. We can now
10278 // browse to the hash stored in the new URI.
10279
10280 if (!newHashName.IsEmpty()) {
10281 // anchor is there, but if it's a load from history,
10282 // we don't have any anchor jumping to do
10283 bool scroll = aLoadType != LOAD_HISTORY &&
10284 aLoadType != LOAD_RELOAD_NORMAL;
10285
10286 char *str = ToNewCString(newHashName);
10287 if (!str) {
10288 return NS_ERROR_OUT_OF_MEMORY;
10289 }
10290
10291 // nsUnescape modifies the string that is passed into it.
10292 nsUnescape(str);
10293
10294 // We assume that the bytes are in UTF-8, as it says in the
10295 // spec:
10296 // http://www.w3.org/TR/html4/appendix/notes.html#h-B.2.1
10297
10298 // We try the UTF-8 string first, and then try the document's
10299 // charset (see below). If the string is not UTF-8,
10300 // conversion will fail and give us an empty Unicode string.
10301 // In that case, we should just fall through to using the
10302 // page's charset.
10303 nsresult rv = NS_ERROR_FAILURE;
10304 NS_ConvertUTF8toUTF16 uStr(str);
10305 if (!uStr.IsEmpty()) {
10306 rv = shell->GoToAnchor(NS_ConvertUTF8toUTF16(str), scroll);
10307 }
10308 nsMemory::Free(str);
10309
10310 // Above will fail if the anchor name is not UTF-8. Need to
10311 // convert from document charset to unicode.
10312 if (NS_FAILED(rv)) {
10313
10314 // Get a document charset
10315 NS_ENSURE_TRUE(mContentViewer, NS_ERROR_FAILURE);
10316 nsIDocument* doc = mContentViewer->GetDocument();
10317 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
10318 const nsACString &aCharset = doc->GetDocumentCharacterSet();
10319
10320 nsCOMPtr<nsITextToSubURI> textToSubURI =
10321 do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
10322 NS_ENSURE_SUCCESS(rv, rv);
10323
10324 // Unescape and convert to unicode
10325 nsXPIDLString uStr;
10326
10327 rv = textToSubURI->UnEscapeAndConvert(PromiseFlatCString(aCharset).get(),
10328 PromiseFlatCString(newHashName).get(),
10329 getter_Copies(uStr));
10330 NS_ENSURE_SUCCESS(rv, rv);
10331
10332 // Ignore return value of GoToAnchor, since it will return an error
10333 // if there is no such anchor in the document, which is actually a
10334 // success condition for us (we want to update the session history
10335 // with the new URI no matter whether we actually scrolled
10336 // somewhere).
10337 //
10338 // When newHashName contains "%00", unescaped string may be empty.
10339 // And GoToAnchor asserts if we ask it to scroll to an empty ref.
10340 shell->GoToAnchor(uStr, scroll && !uStr.IsEmpty());
10341 }
10342 }
10343 else {
10344
10345 // Tell the shell it's at an anchor, without scrolling.
10346 shell->GoToAnchor(EmptyString(), false);
10347
10348 // An empty anchor was found, but if it's a load from history,
10349 // we don't have to jump to the top of the page. Scrollbar
10350 // position will be restored by the caller, based on positions
10351 // stored in session history.
10352 if (aLoadType == LOAD_HISTORY || aLoadType == LOAD_RELOAD_NORMAL)
10353 return NS_OK;
10354 // An empty anchor. Scroll to the top of the page. Ignore the
10355 // return value; failure to scroll here (e.g. if there is no
10356 // root scrollframe) is not grounds for canceling the load!
10357 SetCurScrollPosEx(0, 0);
10358 }
10359
10360 return NS_OK;
10361 }
10362
10363 void
10364 nsDocShell::SetupReferrerFromChannel(nsIChannel * aChannel)
10365 {
10366 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
10367 if (httpChannel) {
10368 nsCOMPtr<nsIURI> referrer;
10369 nsresult rv = httpChannel->GetReferrer(getter_AddRefs(referrer));
10370 if (NS_SUCCEEDED(rv)) {
10371 SetReferrerURI(referrer);
10372 }
10373 }
10374 }
10375
10376 bool
10377 nsDocShell::OnNewURI(nsIURI * aURI, nsIChannel * aChannel, nsISupports* aOwner,
10378 uint32_t aLoadType, bool aFireOnLocationChange,
10379 bool aAddToGlobalHistory, bool aCloneSHChildren)
10380 {
10381 NS_PRECONDITION(aURI, "uri is null");
10382 NS_PRECONDITION(!aChannel || !aOwner, "Shouldn't have both set");
10383
10384 #if defined(PR_LOGGING) && defined(DEBUG)
10385 if (PR_LOG_TEST(gDocShellLog, PR_LOG_DEBUG)) {
10386 nsAutoCString spec;
10387 aURI->GetSpec(spec);
10388
10389 nsAutoCString chanName;
10390 if (aChannel)
10391 aChannel->GetName(chanName);
10392 else
10393 chanName.AssignLiteral("<no channel>");
10394
10395 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
10396 ("nsDocShell[%p]::OnNewURI(\"%s\", [%s], 0x%x)\n", this, spec.get(),
10397 chanName.get(), aLoadType));
10398 }
10399 #endif
10400
10401 bool equalUri = false;
10402
10403 // Get the post data and the HTTP response code from the channel.
10404 uint32_t responseStatus = 0;
10405 nsCOMPtr<nsIInputStream> inputStream;
10406 if (aChannel) {
10407 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
10408
10409 // Check if the HTTPChannel is hiding under a multiPartChannel
10410 if (!httpChannel) {
10411 GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
10412 }
10413
10414 if (httpChannel) {
10415 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
10416 if (uploadChannel) {
10417 uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
10418 }
10419
10420 // If the response status indicates an error, unlink this session
10421 // history entry from any entries sharing its document.
10422 nsresult rv = httpChannel->GetResponseStatus(&responseStatus);
10423 if (mLSHE && NS_SUCCEEDED(rv) && responseStatus >= 400) {
10424 mLSHE->AbandonBFCacheEntry();
10425 }
10426 }
10427 }
10428
10429 // Determine if this type of load should update history.
10430 bool updateGHistory = !(aLoadType == LOAD_BYPASS_HISTORY ||
10431 aLoadType == LOAD_ERROR_PAGE ||
10432 aLoadType & LOAD_CMD_HISTORY);
10433
10434 // We don't update session history on reload.
10435 bool updateSHistory = updateGHistory && (!(aLoadType & LOAD_CMD_RELOAD));
10436
10437 /* Create SH Entry (mLSHE) only if there is a SessionHistory object (mSessionHistory) in
10438 * the current frame or in the root docshell
10439 */
10440 nsCOMPtr<nsISHistory> rootSH = mSessionHistory;
10441 if (!rootSH) {
10442 // Get the handle to SH from the root docshell
10443 GetRootSessionHistory(getter_AddRefs(rootSH));
10444 if (!rootSH) {
10445 updateSHistory = false;
10446 updateGHistory = false; // XXX Why global history too?
10447 }
10448 } // rootSH
10449
10450 // Check if the url to be loaded is the same as the one already loaded.
10451 if (mCurrentURI)
10452 aURI->Equals(mCurrentURI, &equalUri);
10453
10454 #ifdef DEBUG
10455 bool shAvailable = (rootSH != nullptr);
10456
10457 // XXX This log message is almost useless because |updateSHistory|
10458 // and |updateGHistory| are not correct at this point.
10459
10460 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
10461 (" shAvailable=%i updateSHistory=%i updateGHistory=%i"
10462 " equalURI=%i\n",
10463 shAvailable, updateSHistory, updateGHistory, equalUri));
10464
10465 if (shAvailable && mCurrentURI && !mOSHE && aLoadType != LOAD_ERROR_PAGE) {
10466 NS_ASSERTION(NS_IsAboutBlank(mCurrentURI), "no SHEntry for a non-transient viewer?");
10467 }
10468 #endif
10469
10470 /* If the url to be loaded is the same as the one already there,
10471 * and the original loadType is LOAD_NORMAL, LOAD_LINK, or
10472 * LOAD_STOP_CONTENT, set loadType to LOAD_NORMAL_REPLACE so that
10473 * AddToSessionHistory() won't mess with the current SHEntry and
10474 * if this page has any frame children, it also will be handled
10475 * properly. see bug 83684
10476 *
10477 * NB: If mOSHE is null but we have a current URI, then it means
10478 * that we must be at the transient about:blank content viewer
10479 * (asserted above) and we should let the normal load continue,
10480 * since there's nothing to replace.
10481 *
10482 * XXX Hopefully changing the loadType at this time will not hurt
10483 * anywhere. The other way to take care of sequentially repeating
10484 * frameset pages is to add new methods to nsIDocShellTreeItem.
10485 * Hopefully I don't have to do that.
10486 */
10487 if (equalUri &&
10488 mOSHE &&
10489 (mLoadType == LOAD_NORMAL ||
10490 mLoadType == LOAD_LINK ||
10491 mLoadType == LOAD_STOP_CONTENT) &&
10492 !inputStream)
10493 {
10494 mLoadType = LOAD_NORMAL_REPLACE;
10495 }
10496
10497 // If this is a refresh to the currently loaded url, we don't
10498 // have to update session or global history.
10499 if (mLoadType == LOAD_REFRESH && !inputStream && equalUri) {
10500 SetHistoryEntry(&mLSHE, mOSHE);
10501 }
10502
10503 /* If the user pressed shift-reload, cache will create a new cache key
10504 * for the page. Save the new cacheKey in Session History.
10505 * see bug 90098
10506 */
10507 if (aChannel &&
10508 (aLoadType == LOAD_RELOAD_BYPASS_CACHE ||
10509 aLoadType == LOAD_RELOAD_BYPASS_PROXY ||
10510 aLoadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE ||
10511 aLoadType == LOAD_RELOAD_ALLOW_MIXED_CONTENT)) {
10512 NS_ASSERTION(!updateSHistory,
10513 "We shouldn't be updating session history for forced"
10514 " reloads!");
10515
10516 nsCOMPtr<nsICachingChannel> cacheChannel(do_QueryInterface(aChannel));
10517 nsCOMPtr<nsISupports> cacheKey;
10518 // Get the Cache Key and store it in SH.
10519 if (cacheChannel)
10520 cacheChannel->GetCacheKey(getter_AddRefs(cacheKey));
10521 // If we already have a loading history entry, store the new cache key
10522 // in it. Otherwise, since we're doing a reload and won't be updating
10523 // our history entry, store the cache key in our current history entry.
10524 if (mLSHE)
10525 mLSHE->SetCacheKey(cacheKey);
10526 else if (mOSHE)
10527 mOSHE->SetCacheKey(cacheKey);
10528
10529 // Since we're force-reloading, clear all the sub frame history.
10530 ClearFrameHistory(mLSHE);
10531 ClearFrameHistory(mOSHE);
10532 }
10533
10534 if (aLoadType == LOAD_RELOAD_NORMAL) {
10535 nsCOMPtr<nsISHEntry> currentSH;
10536 bool oshe = false;
10537 GetCurrentSHEntry(getter_AddRefs(currentSH), &oshe);
10538 bool dynamicallyAddedChild = false;
10539 if (currentSH) {
10540 currentSH->HasDynamicallyAddedChild(&dynamicallyAddedChild);
10541 }
10542 if (dynamicallyAddedChild) {
10543 ClearFrameHistory(currentSH);
10544 }
10545 }
10546
10547 if (aLoadType == LOAD_REFRESH) {
10548 ClearFrameHistory(mLSHE);
10549 ClearFrameHistory(mOSHE);
10550 }
10551
10552 if (updateSHistory) {
10553 // Update session history if necessary...
10554 if (!mLSHE && (mItemType == typeContent) && mURIResultedInDocument) {
10555 /* This is a fresh page getting loaded for the first time
10556 *.Create a Entry for it and add it to SH, if this is the
10557 * rootDocShell
10558 */
10559 (void) AddToSessionHistory(aURI, aChannel, aOwner, aCloneSHChildren,
10560 getter_AddRefs(mLSHE));
10561 }
10562 }
10563
10564 // If this is a POST request, we do not want to include this in global
10565 // history.
10566 if (updateGHistory &&
10567 aAddToGlobalHistory &&
10568 !ChannelIsPost(aChannel)) {
10569 nsCOMPtr<nsIURI> previousURI;
10570 uint32_t previousFlags = 0;
10571
10572 if (aLoadType & LOAD_CMD_RELOAD) {
10573 // On a reload request, we don't set redirecting flags.
10574 previousURI = aURI;
10575 } else {
10576 ExtractLastVisit(aChannel, getter_AddRefs(previousURI),
10577 &previousFlags);
10578 }
10579
10580 // Note: We don't use |referrer| when our global history is
10581 // based on IHistory.
10582 nsCOMPtr<nsIURI> referrer;
10583 // Treat referrer as null if there is an error getting it.
10584 (void)NS_GetReferrerFromChannel(aChannel, getter_AddRefs(referrer));
10585
10586 AddURIVisit(aURI, referrer, previousURI, previousFlags, responseStatus);
10587 }
10588
10589 // If this was a history load or a refresh,
10590 // update the index in SH.
10591 if (rootSH && (mLoadType & (LOAD_CMD_HISTORY | LOAD_CMD_RELOAD))) {
10592 nsCOMPtr<nsISHistoryInternal> shInternal(do_QueryInterface(rootSH));
10593 if (shInternal) {
10594 rootSH->GetIndex(&mPreviousTransIndex);
10595 shInternal->UpdateIndex();
10596 rootSH->GetIndex(&mLoadedTransIndex);
10597 #ifdef DEBUG_PAGE_CACHE
10598 printf("Previous index: %d, Loaded index: %d\n\n",
10599 mPreviousTransIndex, mLoadedTransIndex);
10600 #endif
10601 }
10602 }
10603
10604 // aCloneSHChildren exactly means "we are not loading a new document".
10605 uint32_t locationFlags = aCloneSHChildren?
10606 uint32_t(LOCATION_CHANGE_SAME_DOCUMENT) : 0;
10607
10608 bool onLocationChangeNeeded = SetCurrentURI(aURI, aChannel,
10609 aFireOnLocationChange,
10610 locationFlags);
10611 // Make sure to store the referrer from the channel, if any
10612 SetupReferrerFromChannel(aChannel);
10613 return onLocationChangeNeeded;
10614 }
10615
10616 bool
10617 nsDocShell::OnLoadingSite(nsIChannel * aChannel, bool aFireOnLocationChange,
10618 bool aAddToGlobalHistory)
10619 {
10620 nsCOMPtr<nsIURI> uri;
10621 // If this a redirect, use the final url (uri)
10622 // else use the original url
10623 //
10624 // Note that this should match what documents do (see nsDocument::Reset).
10625 NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
10626 NS_ENSURE_TRUE(uri, false);
10627
10628 // Pass false for aCloneSHChildren, since we're loading a new page here.
10629 return OnNewURI(uri, aChannel, nullptr, mLoadType, aFireOnLocationChange,
10630 aAddToGlobalHistory, false);
10631
10632 }
10633
10634 void
10635 nsDocShell::SetReferrerURI(nsIURI * aURI)
10636 {
10637 mReferrerURI = aURI; // This assigment addrefs
10638 }
10639
10640 //*****************************************************************************
10641 // nsDocShell: Session History
10642 //*****************************************************************************
10643
10644 NS_IMETHODIMP
10645 nsDocShell::AddState(JS::Handle<JS::Value> aData, const nsAString& aTitle,
10646 const nsAString& aURL, bool aReplace, JSContext* aCx)
10647 {
10648 // Implements History.pushState and History.replaceState
10649
10650 // Here's what we do, roughly in the order specified by HTML5:
10651 // 1. Serialize aData using structured clone.
10652 // 2. If the third argument is present,
10653 // a. Resolve the url, relative to the first script's base URL
10654 // b. If (a) fails, raise a SECURITY_ERR
10655 // c. Compare the resulting absolute URL to the document's address. If
10656 // any part of the URLs difer other than the <path>, <query>, and
10657 // <fragment> components, raise a SECURITY_ERR and abort.
10658 // 3. If !aReplace:
10659 // Remove from the session history all entries after the current entry,
10660 // as we would after a regular navigation, and save the current
10661 // entry's scroll position (bug 590573).
10662 // 4. As apropriate, either add a state object entry to the session history
10663 // after the current entry with the following properties, or modify the
10664 // current session history entry to set
10665 // a. cloned data as the state object,
10666 // b. if the third argument was present, the absolute URL found in
10667 // step 2
10668 // Also clear the new history entry's POST data (see bug 580069).
10669 // 5. If aReplace is false (i.e. we're doing a pushState instead of a
10670 // replaceState), notify bfcache that we've navigated to a new page.
10671 // 6. If the third argument is present, set the document's current address
10672 // to the absolute URL found in step 2.
10673 //
10674 // It's important that this function not run arbitrary scripts after step 1
10675 // and before completing step 5. For example, if a script called
10676 // history.back() before we completed step 5, bfcache might destroy an
10677 // active content viewer. Since EvictOutOfRangeContentViewers at the end of
10678 // step 5 might run script, we can't just put a script blocker around the
10679 // critical section.
10680 //
10681 // Note that we completely ignore the aTitle parameter.
10682
10683 nsresult rv;
10684
10685 // Don't clobber the load type of an existing network load.
10686 AutoRestore<uint32_t> loadTypeResetter(mLoadType);
10687
10688 // pushState effectively becomes replaceState when we've started a network
10689 // load but haven't adopted its document yet. This mirrors what we do with
10690 // changes to the hash at this stage of the game.
10691 if (JustStartedNetworkLoad()) {
10692 aReplace = true;
10693 }
10694
10695 nsCOMPtr<nsIDocument> document = do_GetInterface(GetAsSupports(this));
10696 NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
10697
10698 // Step 1: Serialize aData using structured clone.
10699 nsCOMPtr<nsIStructuredCloneContainer> scContainer;
10700
10701 // scContainer->Init might cause arbitrary JS to run, and this code might
10702 // navigate the page we're on, potentially to a different origin! (bug
10703 // 634834) To protect against this, we abort if our principal changes due
10704 // to the InitFromJSVal() call.
10705 {
10706 nsCOMPtr<nsIDocument> origDocument =
10707 do_GetInterface(GetAsSupports(this));
10708 if (!origDocument)
10709 return NS_ERROR_DOM_SECURITY_ERR;
10710 nsCOMPtr<nsIPrincipal> origPrincipal = origDocument->NodePrincipal();
10711
10712 scContainer = new nsStructuredCloneContainer();
10713 JSContext *cx = aCx;
10714 nsCxPusher pusher;
10715 if (!cx) {
10716 cx = nsContentUtils::GetContextFromDocument(document);
10717 pusher.Push(cx);
10718 }
10719 rv = scContainer->InitFromJSVal(aData, cx);
10720
10721 // If we're running in the document's context and the structured clone
10722 // failed, clear the context's pending exception. See bug 637116.
10723 if (NS_FAILED(rv) && !aCx) {
10724 JS_ClearPendingException(aCx);
10725 }
10726 NS_ENSURE_SUCCESS(rv, rv);
10727
10728 nsCOMPtr<nsIDocument> newDocument =
10729 do_GetInterface(GetAsSupports(this));
10730 if (!newDocument)
10731 return NS_ERROR_DOM_SECURITY_ERR;
10732 nsCOMPtr<nsIPrincipal> newPrincipal = newDocument->NodePrincipal();
10733
10734 bool principalsEqual = false;
10735 origPrincipal->Equals(newPrincipal, &principalsEqual);
10736 NS_ENSURE_TRUE(principalsEqual, NS_ERROR_DOM_SECURITY_ERR);
10737 }
10738
10739 // Check that the state object isn't too long.
10740 // Default max length: 640k bytes.
10741 int32_t maxStateObjSize =
10742 Preferences::GetInt("browser.history.maxStateObjectSize", 0xA0000);
10743 if (maxStateObjSize < 0) {
10744 maxStateObjSize = 0;
10745 }
10746
10747 uint64_t scSize;
10748 rv = scContainer->GetSerializedNBytes(&scSize);
10749 NS_ENSURE_SUCCESS(rv, rv);
10750
10751 NS_ENSURE_TRUE(scSize <= (uint32_t)maxStateObjSize,
10752 NS_ERROR_ILLEGAL_VALUE);
10753
10754 // Step 2: Resolve aURL
10755 bool equalURIs = true;
10756 nsCOMPtr<nsIURI> currentURI;
10757 if (sURIFixup && mCurrentURI) {
10758 rv = sURIFixup->CreateExposableURI(mCurrentURI,
10759 getter_AddRefs(currentURI));
10760 NS_ENSURE_SUCCESS(rv, rv);
10761 } else {
10762 currentURI = mCurrentURI;
10763 }
10764 nsCOMPtr<nsIURI> oldURI = currentURI;
10765 nsCOMPtr<nsIURI> newURI;
10766 if (aURL.Length() == 0) {
10767 newURI = currentURI;
10768 }
10769 else {
10770 // 2a: Resolve aURL relative to mURI
10771
10772 nsIURI* docBaseURI = document->GetDocBaseURI();
10773 if (!docBaseURI)
10774 return NS_ERROR_FAILURE;
10775
10776 nsAutoCString spec;
10777 docBaseURI->GetSpec(spec);
10778
10779 nsAutoCString charset;
10780 rv = docBaseURI->GetOriginCharset(charset);
10781 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
10782
10783 rv = NS_NewURI(getter_AddRefs(newURI), aURL,
10784 charset.get(), docBaseURI);
10785
10786 // 2b: If 2a fails, raise a SECURITY_ERR
10787 if (NS_FAILED(rv)) {
10788 return NS_ERROR_DOM_SECURITY_ERR;
10789 }
10790
10791 // 2c: Same-origin check.
10792 if (!nsContentUtils::URIIsLocalFile(newURI)) {
10793 // In addition to checking that the security manager says that
10794 // the new URI has the same origin as our current URI, we also
10795 // check that the two URIs have the same userpass. (The
10796 // security manager says that |http://foo.com| and
10797 // |http://me@foo.com| have the same origin.) currentURI
10798 // won't contain the password part of the userpass, so this
10799 // means that it's never valid to specify a password in a
10800 // pushState or replaceState URI.
10801
10802 nsCOMPtr<nsIScriptSecurityManager> secMan =
10803 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
10804 NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE);
10805
10806 // It's very important that we check that newURI is of the same
10807 // origin as currentURI, not docBaseURI, because a page can
10808 // set docBaseURI arbitrarily to any domain.
10809 nsAutoCString currentUserPass, newUserPass;
10810 NS_ENSURE_SUCCESS(currentURI->GetUserPass(currentUserPass),
10811 NS_ERROR_FAILURE);
10812 NS_ENSURE_SUCCESS(newURI->GetUserPass(newUserPass),
10813 NS_ERROR_FAILURE);
10814 if (NS_FAILED(secMan->CheckSameOriginURI(currentURI,
10815 newURI, true)) ||
10816 !currentUserPass.Equals(newUserPass)) {
10817
10818 return NS_ERROR_DOM_SECURITY_ERR;
10819 }
10820 }
10821 else {
10822 // It's a file:// URI
10823 nsCOMPtr<nsIScriptObjectPrincipal> docScriptObj =
10824 do_QueryInterface(document);
10825
10826 if (!docScriptObj) {
10827 return NS_ERROR_DOM_SECURITY_ERR;
10828 }
10829
10830 nsCOMPtr<nsIPrincipal> principal = docScriptObj->GetPrincipal();
10831
10832 if (!principal ||
10833 NS_FAILED(principal->CheckMayLoad(newURI, true, false))) {
10834
10835 return NS_ERROR_DOM_SECURITY_ERR;
10836 }
10837 }
10838
10839 if (currentURI) {
10840 currentURI->Equals(newURI, &equalURIs);
10841 }
10842 else {
10843 equalURIs = false;
10844 }
10845
10846 } // end of same-origin check
10847
10848 // Step 3: Create a new entry in the session history. This will erase
10849 // all SHEntries after the new entry and make this entry the current
10850 // one. This operation may modify mOSHE, which we need later, so we
10851 // keep a reference here.
10852 NS_ENSURE_TRUE(mOSHE, NS_ERROR_FAILURE);
10853 nsCOMPtr<nsISHEntry> oldOSHE = mOSHE;
10854
10855 mLoadType = LOAD_PUSHSTATE;
10856
10857 nsCOMPtr<nsISHEntry> newSHEntry;
10858 if (!aReplace) {
10859 // Save the current scroll position (bug 590573).
10860 nscoord cx = 0, cy = 0;
10861 GetCurScrollPos(ScrollOrientation_X, &cx);
10862 GetCurScrollPos(ScrollOrientation_Y, &cy);
10863 mOSHE->SetScrollPosition(cx, cy);
10864
10865 // Since we're not changing which page we have loaded, pass
10866 // true for aCloneChildren.
10867 rv = AddToSessionHistory(newURI, nullptr, nullptr, true,
10868 getter_AddRefs(newSHEntry));
10869 NS_ENSURE_SUCCESS(rv, rv);
10870
10871 NS_ENSURE_TRUE(newSHEntry, NS_ERROR_FAILURE);
10872
10873 // Link the new SHEntry to the old SHEntry's BFCache entry, since the
10874 // two entries correspond to the same document.
10875 NS_ENSURE_SUCCESS(newSHEntry->AdoptBFCacheEntry(oldOSHE),
10876 NS_ERROR_FAILURE);
10877
10878 // Set the new SHEntry's title (bug 655273).
10879 nsString title;
10880 mOSHE->GetTitle(getter_Copies(title));
10881 newSHEntry->SetTitle(title);
10882
10883 // AddToSessionHistory may not modify mOSHE. In case it doesn't,
10884 // we'll just set mOSHE here.
10885 mOSHE = newSHEntry;
10886
10887 } else {
10888 newSHEntry = mOSHE;
10889 newSHEntry->SetURI(newURI);
10890 }
10891
10892 // Step 4: Modify new/original session history entry and clear its POST
10893 // data, if there is any.
10894 newSHEntry->SetStateData(scContainer);
10895 newSHEntry->SetPostData(nullptr);
10896
10897 // If this push/replaceState changed the document's current URI and the new
10898 // URI differs from the old URI in more than the hash, or if the old
10899 // SHEntry's URI was modified in this way by a push/replaceState call
10900 // set URIWasModified to true for the current SHEntry (bug 669671).
10901 bool sameExceptHashes = true, oldURIWasModified = false;
10902 newURI->EqualsExceptRef(currentURI, &sameExceptHashes);
10903 oldOSHE->GetURIWasModified(&oldURIWasModified);
10904 newSHEntry->SetURIWasModified(!sameExceptHashes || oldURIWasModified);
10905
10906 // Step 5: If aReplace is false, indicating that we're doing a pushState
10907 // rather than a replaceState, notify bfcache that we've added a page to
10908 // the history so it can evict content viewers if appropriate. Otherwise
10909 // call ReplaceEntry so that we notify nsIHistoryListeners that an entry
10910 // was replaced.
10911 nsCOMPtr<nsISHistory> rootSH;
10912 GetRootSessionHistory(getter_AddRefs(rootSH));
10913 NS_ENSURE_TRUE(rootSH, NS_ERROR_UNEXPECTED);
10914
10915 nsCOMPtr<nsISHistoryInternal> internalSH =
10916 do_QueryInterface(rootSH);
10917 NS_ENSURE_TRUE(internalSH, NS_ERROR_UNEXPECTED);
10918
10919 if (!aReplace) {
10920 int32_t curIndex = -1;
10921 rv = rootSH->GetIndex(&curIndex);
10922 if (NS_SUCCEEDED(rv) && curIndex > -1) {
10923 internalSH->EvictOutOfRangeContentViewers(curIndex);
10924 }
10925 } else {
10926 nsCOMPtr<nsISHEntry> rootSHEntry = GetRootSHEntry(newSHEntry);
10927
10928 int32_t index = -1;
10929 rv = rootSH->GetIndexOfEntry(rootSHEntry, &index);
10930 if (NS_SUCCEEDED(rv) && index > -1) {
10931 internalSH->ReplaceEntry(index, rootSHEntry);
10932 }
10933 }
10934
10935 // Step 6: If the document's URI changed, update document's URI and update
10936 // global history.
10937 //
10938 // We need to call FireOnLocationChange so that the browser's address bar
10939 // gets updated and the back button is enabled, but we only need to
10940 // explicitly call FireOnLocationChange if we're not calling SetCurrentURI,
10941 // since SetCurrentURI will call FireOnLocationChange for us.
10942 //
10943 // Both SetCurrentURI(...) and FireDummyOnLocationChange() pass
10944 // nullptr for aRequest param to FireOnLocationChange(...). Such an update
10945 // notification is allowed only when we know docshell is not loading a new
10946 // document and it requires LOCATION_CHANGE_SAME_DOCUMENT flag. Otherwise,
10947 // FireOnLocationChange(...) breaks security UI.
10948 if (!equalURIs) {
10949 SetCurrentURI(newURI, nullptr, true, LOCATION_CHANGE_SAME_DOCUMENT);
10950 document->SetDocumentURI(newURI);
10951
10952 AddURIVisit(newURI, oldURI, oldURI, 0);
10953
10954 // AddURIVisit doesn't set the title for the new URI in global history,
10955 // so do that here.
10956 if (mUseGlobalHistory && !mInPrivateBrowsing) {
10957 nsCOMPtr<IHistory> history = services::GetHistoryService();
10958 if (history) {
10959 history->SetURITitle(newURI, mTitle);
10960 }
10961 else if (mGlobalHistory) {
10962 mGlobalHistory->SetPageTitle(newURI, mTitle);
10963 }
10964 }
10965
10966 // Inform the favicon service that our old favicon applies to this new
10967 // URI.
10968 CopyFavicon(oldURI, newURI, mInPrivateBrowsing);
10969 }
10970 else {
10971 FireDummyOnLocationChange();
10972 }
10973 document->SetStateObject(scContainer);
10974
10975 return NS_OK;
10976 }
10977
10978 bool
10979 nsDocShell::ShouldAddToSessionHistory(nsIURI * aURI)
10980 {
10981 // I believe none of the about: urls should go in the history. But then
10982 // that could just be me... If the intent is only deny about:blank then we
10983 // should just do a spec compare, rather than two gets of the scheme and
10984 // then the path. -Gagan
10985 nsresult rv;
10986 nsAutoCString buf, pref;
10987
10988 rv = aURI->GetScheme(buf);
10989 if (NS_FAILED(rv))
10990 return false;
10991
10992 if (buf.Equals("about")) {
10993 rv = aURI->GetPath(buf);
10994 if (NS_FAILED(rv))
10995 return false;
10996
10997 if (buf.Equals("blank")) {
10998 return false;
10999 }
11000 }
11001
11002 rv = Preferences::GetDefaultCString("browser.newtab.url", &pref);
11003
11004 if (NS_FAILED(rv)) {
11005 return true;
11006 }
11007
11008 rv = aURI->GetSpec(buf);
11009 NS_ENSURE_SUCCESS(rv, true);
11010
11011 return !buf.Equals(pref);
11012 }
11013
11014 nsresult
11015 nsDocShell::AddToSessionHistory(nsIURI * aURI, nsIChannel * aChannel,
11016 nsISupports* aOwner, bool aCloneChildren,
11017 nsISHEntry ** aNewEntry)
11018 {
11019 NS_PRECONDITION(aURI, "uri is null");
11020 NS_PRECONDITION(!aChannel || !aOwner, "Shouldn't have both set");
11021
11022 #if defined(PR_LOGGING) && defined(DEBUG)
11023 if (PR_LOG_TEST(gDocShellLog, PR_LOG_DEBUG)) {
11024 nsAutoCString spec;
11025 aURI->GetSpec(spec);
11026
11027 nsAutoCString chanName;
11028 if (aChannel)
11029 aChannel->GetName(chanName);
11030 else
11031 chanName.AssignLiteral("<no channel>");
11032
11033 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
11034 ("nsDocShell[%p]::AddToSessionHistory(\"%s\", [%s])\n", this, spec.get(),
11035 chanName.get()));
11036 }
11037 #endif
11038
11039 nsresult rv = NS_OK;
11040 nsCOMPtr<nsISHEntry> entry;
11041 bool shouldPersist;
11042
11043 shouldPersist = ShouldAddToSessionHistory(aURI);
11044
11045 // Get a handle to the root docshell
11046 nsCOMPtr<nsIDocShellTreeItem> root;
11047 GetSameTypeRootTreeItem(getter_AddRefs(root));
11048 /*
11049 * If this is a LOAD_FLAGS_REPLACE_HISTORY in a subframe, we use
11050 * the existing SH entry in the page and replace the url and
11051 * other vitalities.
11052 */
11053 if (LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY) &&
11054 root != static_cast<nsIDocShellTreeItem *>(this)) {
11055 // This is a subframe
11056 entry = mOSHE;
11057 nsCOMPtr<nsISHContainer> shContainer(do_QueryInterface(entry));
11058 if (shContainer) {
11059 int32_t childCount = 0;
11060 shContainer->GetChildCount(&childCount);
11061 // Remove all children of this entry
11062 for (int32_t i = childCount - 1; i >= 0; i--) {
11063 nsCOMPtr<nsISHEntry> child;
11064 shContainer->GetChildAt(i, getter_AddRefs(child));
11065 shContainer->RemoveChild(child);
11066 } // for
11067 entry->AbandonBFCacheEntry();
11068 } // shContainer
11069 }
11070
11071 // Create a new entry if necessary.
11072 if (!entry) {
11073 entry = do_CreateInstance(NS_SHENTRY_CONTRACTID);
11074
11075 if (!entry) {
11076 return NS_ERROR_OUT_OF_MEMORY;
11077 }
11078 }
11079
11080 // Get the post data & referrer
11081 nsCOMPtr<nsIInputStream> inputStream;
11082 nsCOMPtr<nsIURI> referrerURI;
11083 nsCOMPtr<nsISupports> cacheKey;
11084 nsCOMPtr<nsISupports> owner = aOwner;
11085 bool expired = false;
11086 bool discardLayoutState = false;
11087 nsCOMPtr<nsICachingChannel> cacheChannel;
11088 if (aChannel) {
11089 cacheChannel = do_QueryInterface(aChannel);
11090
11091 /* If there is a caching channel, get the Cache Key and store it
11092 * in SH.
11093 */
11094 if (cacheChannel) {
11095 cacheChannel->GetCacheKey(getter_AddRefs(cacheKey));
11096 }
11097 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
11098
11099 // Check if the httpChannel is hiding under a multipartChannel
11100 if (!httpChannel) {
11101 GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
11102 }
11103 if (httpChannel) {
11104 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
11105 if (uploadChannel) {
11106 uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
11107 }
11108 httpChannel->GetReferrer(getter_AddRefs(referrerURI));
11109
11110 discardLayoutState = ShouldDiscardLayoutState(httpChannel);
11111 }
11112 aChannel->GetOwner(getter_AddRefs(owner));
11113 }
11114
11115 //Title is set in nsDocShell::SetTitle()
11116 entry->Create(aURI, // uri
11117 EmptyString(), // Title
11118 inputStream, // Post data stream
11119 nullptr, // LayoutHistory state
11120 cacheKey, // CacheKey
11121 mContentTypeHint, // Content-type
11122 owner, // Channel or provided owner
11123 mHistoryID,
11124 mDynamicallyCreated);
11125 entry->SetReferrerURI(referrerURI);
11126 nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(aChannel);
11127 if (inStrmChan) {
11128 bool isSrcdocChannel;
11129 inStrmChan->GetIsSrcdocChannel(&isSrcdocChannel);
11130 if (isSrcdocChannel) {
11131 nsAutoString srcdoc;
11132 inStrmChan->GetSrcdocData(srcdoc);
11133 entry->SetSrcdocData(srcdoc);
11134 nsCOMPtr<nsIURI> baseURI;
11135 inStrmChan->GetBaseURI(getter_AddRefs(baseURI));
11136 entry->SetBaseURI(baseURI);
11137 }
11138 }
11139 /* If cache got a 'no-store', ask SH not to store
11140 * HistoryLayoutState. By default, SH will set this
11141 * flag to true and save HistoryLayoutState.
11142 */
11143 if (discardLayoutState) {
11144 entry->SetSaveLayoutStateFlag(false);
11145 }
11146 if (cacheChannel) {
11147 // Check if the page has expired from cache
11148 uint32_t expTime = 0;
11149 cacheChannel->GetCacheTokenExpirationTime(&expTime);
11150 uint32_t now = PRTimeToSeconds(PR_Now());
11151 if (expTime <= now)
11152 expired = true;
11153 }
11154 if (expired)
11155 entry->SetExpirationStatus(true);
11156
11157
11158 if (root == static_cast<nsIDocShellTreeItem *>(this) && mSessionHistory) {
11159 // If we need to clone our children onto the new session
11160 // history entry, do so now.
11161 if (aCloneChildren && mOSHE) {
11162 uint32_t cloneID;
11163 mOSHE->GetID(&cloneID);
11164 nsCOMPtr<nsISHEntry> newEntry;
11165 CloneAndReplace(mOSHE, this, cloneID, entry, true, getter_AddRefs(newEntry));
11166 NS_ASSERTION(entry == newEntry, "The new session history should be in the new entry");
11167 }
11168
11169 // This is the root docshell
11170 if (LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
11171 // Replace current entry in session history.
11172 int32_t index = 0;
11173 mSessionHistory->GetIndex(&index);
11174 nsCOMPtr<nsISHistoryInternal> shPrivate(do_QueryInterface(mSessionHistory));
11175 // Replace the current entry with the new entry
11176 if (shPrivate)
11177 rv = shPrivate->ReplaceEntry(index, entry);
11178 }
11179 else {
11180 // Add to session history
11181 nsCOMPtr<nsISHistoryInternal>
11182 shPrivate(do_QueryInterface(mSessionHistory));
11183 NS_ENSURE_TRUE(shPrivate, NS_ERROR_FAILURE);
11184 mSessionHistory->GetIndex(&mPreviousTransIndex);
11185 rv = shPrivate->AddEntry(entry, shouldPersist);
11186 mSessionHistory->GetIndex(&mLoadedTransIndex);
11187 #ifdef DEBUG_PAGE_CACHE
11188 printf("Previous index: %d, Loaded index: %d\n\n",
11189 mPreviousTransIndex, mLoadedTransIndex);
11190 #endif
11191 }
11192 }
11193 else {
11194 // This is a subframe.
11195 if (!mOSHE || !LOAD_TYPE_HAS_FLAGS(mLoadType,
11196 LOAD_FLAGS_REPLACE_HISTORY))
11197 rv = DoAddChildSHEntry(entry, mChildOffset, aCloneChildren);
11198 }
11199
11200 // Return the new SH entry...
11201 if (aNewEntry) {
11202 *aNewEntry = nullptr;
11203 if (NS_SUCCEEDED(rv)) {
11204 *aNewEntry = entry;
11205 NS_ADDREF(*aNewEntry);
11206 }
11207 }
11208
11209 return rv;
11210 }
11211
11212
11213 NS_IMETHODIMP
11214 nsDocShell::LoadHistoryEntry(nsISHEntry * aEntry, uint32_t aLoadType)
11215 {
11216 if (!IsNavigationAllowed()) {
11217 return NS_OK;
11218 }
11219
11220 nsCOMPtr<nsIURI> uri;
11221 nsCOMPtr<nsIInputStream> postData;
11222 nsCOMPtr<nsIURI> referrerURI;
11223 nsAutoCString contentType;
11224 nsCOMPtr<nsISupports> owner;
11225
11226 NS_ENSURE_TRUE(aEntry, NS_ERROR_FAILURE);
11227
11228 NS_ENSURE_SUCCESS(aEntry->GetURI(getter_AddRefs(uri)), NS_ERROR_FAILURE);
11229 NS_ENSURE_SUCCESS(aEntry->GetReferrerURI(getter_AddRefs(referrerURI)),
11230 NS_ERROR_FAILURE);
11231 NS_ENSURE_SUCCESS(aEntry->GetPostData(getter_AddRefs(postData)),
11232 NS_ERROR_FAILURE);
11233 NS_ENSURE_SUCCESS(aEntry->GetContentType(contentType), NS_ERROR_FAILURE);
11234 NS_ENSURE_SUCCESS(aEntry->GetOwner(getter_AddRefs(owner)),
11235 NS_ERROR_FAILURE);
11236
11237 // Calling CreateAboutBlankContentViewer can set mOSHE to null, and if
11238 // that's the only thing holding a ref to aEntry that will cause aEntry to
11239 // die while we're loading it. So hold a strong ref to aEntry here, just
11240 // in case.
11241 nsCOMPtr<nsISHEntry> kungFuDeathGrip(aEntry);
11242 bool isJS;
11243 nsresult rv = uri->SchemeIs("javascript", &isJS);
11244 if (NS_FAILED(rv) || isJS) {
11245 // We're loading a URL that will execute script from inside asyncOpen.
11246 // Replace the current document with about:blank now to prevent
11247 // anything from the current document from leaking into any JavaScript
11248 // code in the URL.
11249 nsCOMPtr<nsIPrincipal> prin = do_QueryInterface(owner);
11250 // Don't cache the presentation if we're going to just reload the
11251 // current entry. Caching would lead to trying to save the different
11252 // content viewers in the same nsISHEntry object.
11253 rv = CreateAboutBlankContentViewer(prin, nullptr, aEntry != mOSHE);
11254
11255 if (NS_FAILED(rv)) {
11256 // The creation of the intermittent about:blank content
11257 // viewer failed for some reason (potentially because the
11258 // user prevented it). Interrupt the history load.
11259 return NS_OK;
11260 }
11261
11262 if (!owner) {
11263 // Ensure that we have an owner. Otherwise javascript: URIs will
11264 // pick it up from the about:blank page we just loaded, and we
11265 // don't really want even that in this case.
11266 owner = do_CreateInstance("@mozilla.org/nullprincipal;1");
11267 NS_ENSURE_TRUE(owner, NS_ERROR_OUT_OF_MEMORY);
11268 }
11269 }
11270
11271 /* If there is a valid postdata *and* the user pressed
11272 * reload or shift-reload, take user's permission before we
11273 * repost the data to the server.
11274 */
11275 if ((aLoadType & LOAD_CMD_RELOAD) && postData) {
11276 bool repost;
11277 rv = ConfirmRepost(&repost);
11278 if (NS_FAILED(rv)) return rv;
11279
11280 // If the user pressed cancel in the dialog, return. We're done here.
11281 if (!repost)
11282 return NS_BINDING_ABORTED;
11283 }
11284
11285 // Do not inherit owner from document (security-critical!);
11286 uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
11287
11288 nsAutoString srcdoc;
11289 bool isSrcdoc;
11290 nsCOMPtr<nsIURI> baseURI;
11291 aEntry->GetIsSrcdocEntry(&isSrcdoc);
11292 if (isSrcdoc) {
11293 aEntry->GetSrcdocData(srcdoc);
11294 aEntry->GetBaseURI(getter_AddRefs(baseURI));
11295 flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC;
11296 }
11297 else {
11298 srcdoc = NullString();
11299 }
11300
11301 // Passing nullptr as aSourceDocShell gives the same behaviour as before
11302 // aSourceDocShell was introduced. According to spec we should be passing
11303 // the source browsing context that was used when the history entry was
11304 // first created. bug 947716 has been created to address this issue.
11305 rv = InternalLoad(uri,
11306 referrerURI,
11307 owner,
11308 flags,
11309 nullptr, // No window target
11310 contentType.get(), // Type hint
11311 NullString(), // No forced file download
11312 postData, // Post data stream
11313 nullptr, // No headers stream
11314 aLoadType, // Load type
11315 aEntry, // SHEntry
11316 true,
11317 srcdoc,
11318 nullptr, // Source docshell, see comment above
11319 baseURI,
11320 nullptr, // No nsIDocShell
11321 nullptr); // No nsIRequest
11322 return rv;
11323 }
11324
11325 NS_IMETHODIMP nsDocShell::GetShouldSaveLayoutState(bool* aShould)
11326 {
11327 *aShould = false;
11328 if (mOSHE) {
11329 // Don't capture historystate and save it in history
11330 // if the page asked not to do so.
11331 mOSHE->GetSaveLayoutStateFlag(aShould);
11332 }
11333
11334 return NS_OK;
11335 }
11336
11337 NS_IMETHODIMP nsDocShell::PersistLayoutHistoryState()
11338 {
11339 nsresult rv = NS_OK;
11340
11341 if (mOSHE) {
11342 nsCOMPtr<nsIPresShell> shell = GetPresShell();
11343 if (shell) {
11344 nsCOMPtr<nsILayoutHistoryState> layoutState;
11345 rv = shell->CaptureHistoryState(getter_AddRefs(layoutState));
11346 }
11347 }
11348
11349 return rv;
11350 }
11351
11352 /* static */ nsresult
11353 nsDocShell::WalkHistoryEntries(nsISHEntry *aRootEntry,
11354 nsDocShell *aRootShell,
11355 WalkHistoryEntriesFunc aCallback,
11356 void *aData)
11357 {
11358 NS_ENSURE_TRUE(aRootEntry, NS_ERROR_FAILURE);
11359
11360 nsCOMPtr<nsISHContainer> container(do_QueryInterface(aRootEntry));
11361 if (!container)
11362 return NS_ERROR_FAILURE;
11363
11364 int32_t childCount;
11365 container->GetChildCount(&childCount);
11366 for (int32_t i = 0; i < childCount; i++) {
11367 nsCOMPtr<nsISHEntry> childEntry;
11368 container->GetChildAt(i, getter_AddRefs(childEntry));
11369 if (!childEntry) {
11370 // childEntry can be null for valid reasons, for example if the
11371 // docshell at index i never loaded anything useful.
11372 // Remember to clone also nulls in the child array (bug 464064).
11373 aCallback(nullptr, nullptr, i, aData);
11374 continue;
11375 }
11376
11377 nsDocShell *childShell = nullptr;
11378 if (aRootShell) {
11379 // Walk the children of aRootShell and see if one of them
11380 // has srcChild as a SHEntry.
11381
11382 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(aRootShell->mChildList);
11383 while (iter.HasMore()) {
11384 nsDocShell *child = static_cast<nsDocShell*>(iter.GetNext());
11385
11386 if (child->HasHistoryEntry(childEntry)) {
11387 childShell = child;
11388 break;
11389 }
11390 }
11391 }
11392 nsresult rv = aCallback(childEntry, childShell, i, aData);
11393 NS_ENSURE_SUCCESS(rv, rv);
11394 }
11395
11396 return NS_OK;
11397 }
11398
11399 // callback data for WalkHistoryEntries
11400 struct MOZ_STACK_CLASS CloneAndReplaceData
11401 {
11402 CloneAndReplaceData(uint32_t aCloneID, nsISHEntry *aReplaceEntry,
11403 bool aCloneChildren, nsISHEntry *aDestTreeParent)
11404 : cloneID(aCloneID),
11405 cloneChildren(aCloneChildren),
11406 replaceEntry(aReplaceEntry),
11407 destTreeParent(aDestTreeParent) { }
11408
11409 uint32_t cloneID;
11410 bool cloneChildren;
11411 nsISHEntry *replaceEntry;
11412 nsISHEntry *destTreeParent;
11413 nsCOMPtr<nsISHEntry> resultEntry;
11414 };
11415
11416 /* static */ nsresult
11417 nsDocShell::CloneAndReplaceChild(nsISHEntry *aEntry, nsDocShell *aShell,
11418 int32_t aEntryIndex, void *aData)
11419 {
11420 nsCOMPtr<nsISHEntry> dest;
11421
11422 CloneAndReplaceData *data = static_cast<CloneAndReplaceData*>(aData);
11423 uint32_t cloneID = data->cloneID;
11424 nsISHEntry *replaceEntry = data->replaceEntry;
11425
11426 nsCOMPtr<nsISHContainer> container =
11427 do_QueryInterface(data->destTreeParent);
11428 if (!aEntry) {
11429 if (container) {
11430 container->AddChild(nullptr, aEntryIndex);
11431 }
11432 return NS_OK;
11433 }
11434
11435 uint32_t srcID;
11436 aEntry->GetID(&srcID);
11437
11438 nsresult rv = NS_OK;
11439 if (srcID == cloneID) {
11440 // Replace the entry
11441 dest = replaceEntry;
11442 } else {
11443 // Clone the SHEntry...
11444 rv = aEntry->Clone(getter_AddRefs(dest));
11445 NS_ENSURE_SUCCESS(rv, rv);
11446 }
11447 dest->SetIsSubFrame(true);
11448
11449 if (srcID != cloneID || data->cloneChildren) {
11450 // Walk the children
11451 CloneAndReplaceData childData(cloneID, replaceEntry,
11452 data->cloneChildren, dest);
11453 rv = WalkHistoryEntries(aEntry, aShell,
11454 CloneAndReplaceChild, &childData);
11455 NS_ENSURE_SUCCESS(rv, rv);
11456 }
11457
11458 if (srcID != cloneID && aShell) {
11459 aShell->SwapHistoryEntries(aEntry, dest);
11460 }
11461
11462 if (container)
11463 container->AddChild(dest, aEntryIndex);
11464
11465 data->resultEntry = dest;
11466 return rv;
11467 }
11468
11469 /* static */ nsresult
11470 nsDocShell::CloneAndReplace(nsISHEntry *aSrcEntry,
11471 nsDocShell *aSrcShell,
11472 uint32_t aCloneID,
11473 nsISHEntry *aReplaceEntry,
11474 bool aCloneChildren,
11475 nsISHEntry **aResultEntry)
11476 {
11477 NS_ENSURE_ARG_POINTER(aResultEntry);
11478 NS_ENSURE_TRUE(aReplaceEntry, NS_ERROR_FAILURE);
11479
11480 CloneAndReplaceData data(aCloneID, aReplaceEntry, aCloneChildren, nullptr);
11481 nsresult rv = CloneAndReplaceChild(aSrcEntry, aSrcShell, 0, &data);
11482
11483 data.resultEntry.swap(*aResultEntry);
11484 return rv;
11485 }
11486
11487 void
11488 nsDocShell::SwapHistoryEntries(nsISHEntry *aOldEntry, nsISHEntry *aNewEntry)
11489 {
11490 if (aOldEntry == mOSHE)
11491 mOSHE = aNewEntry;
11492
11493 if (aOldEntry == mLSHE)
11494 mLSHE = aNewEntry;
11495 }
11496
11497
11498 struct SwapEntriesData
11499 {
11500 nsDocShell *ignoreShell; // constant; the shell to ignore
11501 nsISHEntry *destTreeRoot; // constant; the root of the dest tree
11502 nsISHEntry *destTreeParent; // constant; the node under destTreeRoot
11503 // whose children will correspond to aEntry
11504 };
11505
11506
11507 nsresult
11508 nsDocShell::SetChildHistoryEntry(nsISHEntry *aEntry, nsDocShell *aShell,
11509 int32_t aEntryIndex, void *aData)
11510 {
11511 SwapEntriesData *data = static_cast<SwapEntriesData*>(aData);
11512 nsDocShell *ignoreShell = data->ignoreShell;
11513
11514 if (!aShell || aShell == ignoreShell)
11515 return NS_OK;
11516
11517 nsISHEntry *destTreeRoot = data->destTreeRoot;
11518
11519 nsCOMPtr<nsISHEntry> destEntry;
11520 nsCOMPtr<nsISHContainer> container =
11521 do_QueryInterface(data->destTreeParent);
11522
11523 if (container) {
11524 // aEntry is a clone of some child of destTreeParent, but since the
11525 // trees aren't necessarily in sync, we'll have to locate it.
11526 // Note that we could set aShell's entry to null if we don't find a
11527 // corresponding entry under destTreeParent.
11528
11529 uint32_t targetID, id;
11530 aEntry->GetID(&targetID);
11531
11532 // First look at the given index, since this is the common case.
11533 nsCOMPtr<nsISHEntry> entry;
11534 container->GetChildAt(aEntryIndex, getter_AddRefs(entry));
11535 if (entry && NS_SUCCEEDED(entry->GetID(&id)) && id == targetID) {
11536 destEntry.swap(entry);
11537 } else {
11538 int32_t childCount;
11539 container->GetChildCount(&childCount);
11540 for (int32_t i = 0; i < childCount; ++i) {
11541 container->GetChildAt(i, getter_AddRefs(entry));
11542 if (!entry)
11543 continue;
11544
11545 entry->GetID(&id);
11546 if (id == targetID) {
11547 destEntry.swap(entry);
11548 break;
11549 }
11550 }
11551 }
11552 } else {
11553 destEntry = destTreeRoot;
11554 }
11555
11556 aShell->SwapHistoryEntries(aEntry, destEntry);
11557
11558 // Now handle the children of aEntry.
11559 SwapEntriesData childData = { ignoreShell, destTreeRoot, destEntry };
11560 return WalkHistoryEntries(aEntry, aShell,
11561 SetChildHistoryEntry, &childData);
11562 }
11563
11564
11565 static nsISHEntry*
11566 GetRootSHEntry(nsISHEntry *aEntry)
11567 {
11568 nsCOMPtr<nsISHEntry> rootEntry = aEntry;
11569 nsISHEntry *result = nullptr;
11570 while (rootEntry) {
11571 result = rootEntry;
11572 result->GetParent(getter_AddRefs(rootEntry));
11573 }
11574
11575 return result;
11576 }
11577
11578
11579 void
11580 nsDocShell::SetHistoryEntry(nsCOMPtr<nsISHEntry> *aPtr, nsISHEntry *aEntry)
11581 {
11582 // We need to sync up the docshell and session history trees for
11583 // subframe navigation. If the load was in a subframe, we forward up to
11584 // the root docshell, which will then recursively sync up all docshells
11585 // to their corresponding entries in the new session history tree.
11586 // If we don't do this, then we can cache a content viewer on the wrong
11587 // cloned entry, and subsequently restore it at the wrong time.
11588
11589 nsISHEntry *newRootEntry = GetRootSHEntry(aEntry);
11590 if (newRootEntry) {
11591 // newRootEntry is now the new root entry.
11592 // Find the old root entry as well.
11593
11594 // Need a strong ref. on |oldRootEntry| so it isn't destroyed when
11595 // SetChildHistoryEntry() does SwapHistoryEntries() (bug 304639).
11596 nsCOMPtr<nsISHEntry> oldRootEntry = GetRootSHEntry(*aPtr);
11597 if (oldRootEntry) {
11598 nsCOMPtr<nsIDocShellTreeItem> rootAsItem;
11599 GetSameTypeRootTreeItem(getter_AddRefs(rootAsItem));
11600 nsCOMPtr<nsIDocShell> rootShell = do_QueryInterface(rootAsItem);
11601 if (rootShell) { // if we're the root just set it, nothing to swap
11602 SwapEntriesData data = { this, newRootEntry };
11603 nsIDocShell *rootIDocShell =
11604 static_cast<nsIDocShell*>(rootShell);
11605 nsDocShell *rootDocShell = static_cast<nsDocShell*>
11606 (rootIDocShell);
11607
11608 #ifdef DEBUG
11609 nsresult rv =
11610 #endif
11611 SetChildHistoryEntry(oldRootEntry, rootDocShell,
11612 0, &data);
11613 NS_ASSERTION(NS_SUCCEEDED(rv), "SetChildHistoryEntry failed");
11614 }
11615 }
11616 }
11617
11618 *aPtr = aEntry;
11619 }
11620
11621
11622 nsresult
11623 nsDocShell::GetRootSessionHistory(nsISHistory ** aReturn)
11624 {
11625 nsresult rv;
11626
11627 nsCOMPtr<nsIDocShellTreeItem> root;
11628 //Get the root docshell
11629 rv = GetSameTypeRootTreeItem(getter_AddRefs(root));
11630 // QI to nsIWebNavigation
11631 nsCOMPtr<nsIWebNavigation> rootAsWebnav(do_QueryInterface(root));
11632 if (rootAsWebnav) {
11633 // Get the handle to SH from the root docshell
11634 rv = rootAsWebnav->GetSessionHistory(aReturn);
11635 }
11636 return rv;
11637 }
11638
11639 nsresult
11640 nsDocShell::GetHttpChannel(nsIChannel * aChannel, nsIHttpChannel ** aReturn)
11641 {
11642 NS_ENSURE_ARG_POINTER(aReturn);
11643 if (!aChannel)
11644 return NS_ERROR_FAILURE;
11645
11646 nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(aChannel));
11647 if (multiPartChannel) {
11648 nsCOMPtr<nsIChannel> baseChannel;
11649 multiPartChannel->GetBaseChannel(getter_AddRefs(baseChannel));
11650 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(baseChannel));
11651 *aReturn = httpChannel;
11652 NS_IF_ADDREF(*aReturn);
11653 }
11654 return NS_OK;
11655 }
11656
11657 bool
11658 nsDocShell::ShouldDiscardLayoutState(nsIHttpChannel * aChannel)
11659 {
11660 // By default layout State will be saved.
11661 if (!aChannel)
11662 return false;
11663
11664 // figure out if SH should be saving layout state
11665 nsCOMPtr<nsISupports> securityInfo;
11666 bool noStore = false, noCache = false;
11667 aChannel->GetSecurityInfo(getter_AddRefs(securityInfo));
11668 aChannel->IsNoStoreResponse(&noStore);
11669 aChannel->IsNoCacheResponse(&noCache);
11670
11671 return (noStore || (noCache && securityInfo));
11672 }
11673
11674 NS_IMETHODIMP nsDocShell::GetEditor(nsIEditor * *aEditor)
11675 {
11676 NS_ENSURE_ARG_POINTER(aEditor);
11677
11678 if (!mEditorData) {
11679 *aEditor = nullptr;
11680 return NS_OK;
11681 }
11682
11683 return mEditorData->GetEditor(aEditor);
11684 }
11685
11686 NS_IMETHODIMP nsDocShell::SetEditor(nsIEditor * aEditor)
11687 {
11688 nsresult rv = EnsureEditorData();
11689 if (NS_FAILED(rv)) return rv;
11690
11691 return mEditorData->SetEditor(aEditor);
11692 }
11693
11694
11695 NS_IMETHODIMP nsDocShell::GetEditable(bool *aEditable)
11696 {
11697 NS_ENSURE_ARG_POINTER(aEditable);
11698 *aEditable = mEditorData && mEditorData->GetEditable();
11699 return NS_OK;
11700 }
11701
11702
11703 NS_IMETHODIMP nsDocShell::GetHasEditingSession(bool *aHasEditingSession)
11704 {
11705 NS_ENSURE_ARG_POINTER(aHasEditingSession);
11706
11707 if (mEditorData)
11708 {
11709 nsCOMPtr<nsIEditingSession> editingSession;
11710 mEditorData->GetEditingSession(getter_AddRefs(editingSession));
11711 *aHasEditingSession = (editingSession.get() != nullptr);
11712 }
11713 else
11714 {
11715 *aHasEditingSession = false;
11716 }
11717
11718 return NS_OK;
11719 }
11720
11721 NS_IMETHODIMP nsDocShell::MakeEditable(bool inWaitForUriLoad)
11722 {
11723 nsresult rv = EnsureEditorData();
11724 if (NS_FAILED(rv)) return rv;
11725
11726 return mEditorData->MakeEditable(inWaitForUriLoad);
11727 }
11728
11729 bool
11730 nsDocShell::ChannelIsPost(nsIChannel* aChannel)
11731 {
11732 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
11733 if (!httpChannel) {
11734 return false;
11735 }
11736
11737 nsAutoCString method;
11738 httpChannel->GetRequestMethod(method);
11739 return method.Equals("POST");
11740 }
11741
11742 void
11743 nsDocShell::ExtractLastVisit(nsIChannel* aChannel,
11744 nsIURI** aURI,
11745 uint32_t* aChannelRedirectFlags)
11746 {
11747 nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(aChannel));
11748 if (!props) {
11749 return;
11750 }
11751
11752 nsresult rv = props->GetPropertyAsInterface(
11753 NS_LITERAL_STRING("docshell.previousURI"),
11754 NS_GET_IID(nsIURI),
11755 reinterpret_cast<void**>(aURI)
11756 );
11757
11758 if (NS_FAILED(rv)) {
11759 // There is no last visit for this channel, so this must be the first
11760 // link. Link the visit to the referrer of this request, if any.
11761 // Treat referrer as null if there is an error getting it.
11762 (void)NS_GetReferrerFromChannel(aChannel, aURI);
11763 }
11764 else {
11765 rv = props->GetPropertyAsUint32(
11766 NS_LITERAL_STRING("docshell.previousFlags"),
11767 aChannelRedirectFlags
11768 );
11769
11770 NS_WARN_IF_FALSE(
11771 NS_SUCCEEDED(rv),
11772 "Could not fetch previous flags, URI will be treated like referrer"
11773 );
11774 }
11775 }
11776
11777 void
11778 nsDocShell::SaveLastVisit(nsIChannel* aChannel,
11779 nsIURI* aURI,
11780 uint32_t aChannelRedirectFlags)
11781 {
11782 nsCOMPtr<nsIWritablePropertyBag2> props(do_QueryInterface(aChannel));
11783 if (!props || !aURI) {
11784 return;
11785 }
11786
11787 props->SetPropertyAsInterface(NS_LITERAL_STRING("docshell.previousURI"),
11788 aURI);
11789 props->SetPropertyAsUint32(NS_LITERAL_STRING("docshell.previousFlags"),
11790 aChannelRedirectFlags);
11791 }
11792
11793 void
11794 nsDocShell::AddURIVisit(nsIURI* aURI,
11795 nsIURI* aReferrerURI,
11796 nsIURI* aPreviousURI,
11797 uint32_t aChannelRedirectFlags,
11798 uint32_t aResponseStatus)
11799 {
11800 MOZ_ASSERT(aURI, "Visited URI is null!");
11801 MOZ_ASSERT(mLoadType != LOAD_ERROR_PAGE &&
11802 mLoadType != LOAD_BYPASS_HISTORY,
11803 "Do not add error or bypass pages to global history");
11804
11805 // Only content-type docshells save URI visits. Also don't do
11806 // anything here if we're not supposed to use global history.
11807 if (mItemType != typeContent || !mUseGlobalHistory || mInPrivateBrowsing) {
11808 return;
11809 }
11810
11811 nsCOMPtr<IHistory> history = services::GetHistoryService();
11812
11813 if (history) {
11814 uint32_t visitURIFlags = 0;
11815
11816 if (!IsFrame()) {
11817 visitURIFlags |= IHistory::TOP_LEVEL;
11818 }
11819
11820 if (aChannelRedirectFlags & nsIChannelEventSink::REDIRECT_TEMPORARY) {
11821 visitURIFlags |= IHistory::REDIRECT_TEMPORARY;
11822 }
11823 else if (aChannelRedirectFlags &
11824 nsIChannelEventSink::REDIRECT_PERMANENT) {
11825 visitURIFlags |= IHistory::REDIRECT_PERMANENT;
11826 }
11827
11828 if (aResponseStatus >= 300 && aResponseStatus < 400) {
11829 visitURIFlags |= IHistory::REDIRECT_SOURCE;
11830 }
11831 // Errors 400-501 and 505 are considered unrecoverable, in the sense a
11832 // simple retry attempt by the user is unlikely to solve them.
11833 // 408 is special cased, since may actually indicate a temporary
11834 // connection problem.
11835 else if (aResponseStatus != 408 &&
11836 ((aResponseStatus >= 400 && aResponseStatus <= 501) ||
11837 aResponseStatus == 505)) {
11838 visitURIFlags |= IHistory::UNRECOVERABLE_ERROR;
11839 }
11840
11841 (void)history->VisitURI(aURI, aPreviousURI, visitURIFlags);
11842 }
11843 else if (mGlobalHistory) {
11844 // Falls back to sync global history interface.
11845 (void)mGlobalHistory->AddURI(aURI,
11846 !!aChannelRedirectFlags,
11847 !IsFrame(),
11848 aReferrerURI);
11849 }
11850 }
11851
11852 //*****************************************************************************
11853 // nsDocShell: Helper Routines
11854 //*****************************************************************************
11855
11856 NS_IMETHODIMP
11857 nsDocShell::SetLoadType(uint32_t aLoadType)
11858 {
11859 mLoadType = aLoadType;
11860 return NS_OK;
11861 }
11862
11863 NS_IMETHODIMP
11864 nsDocShell::GetLoadType(uint32_t * aLoadType)
11865 {
11866 *aLoadType = mLoadType;
11867 return NS_OK;
11868 }
11869
11870 nsresult
11871 nsDocShell::ConfirmRepost(bool * aRepost)
11872 {
11873 nsCOMPtr<nsIPrompt> prompter;
11874 CallGetInterface(this, static_cast<nsIPrompt**>(getter_AddRefs(prompter)));
11875 if (!prompter) {
11876 return NS_ERROR_NOT_AVAILABLE;
11877 }
11878
11879 nsCOMPtr<nsIStringBundleService> stringBundleService =
11880 mozilla::services::GetStringBundleService();
11881 if (!stringBundleService)
11882 return NS_ERROR_FAILURE;
11883
11884 nsCOMPtr<nsIStringBundle> appBundle;
11885 nsresult rv = stringBundleService->CreateBundle(kAppstringsBundleURL,
11886 getter_AddRefs(appBundle));
11887 NS_ENSURE_SUCCESS(rv, rv);
11888
11889 nsCOMPtr<nsIStringBundle> brandBundle;
11890 rv = stringBundleService->CreateBundle(kBrandBundleURL, getter_AddRefs(brandBundle));
11891 NS_ENSURE_SUCCESS(rv, rv);
11892
11893 NS_ASSERTION(prompter && brandBundle && appBundle,
11894 "Unable to set up repost prompter.");
11895
11896 nsXPIDLString brandName;
11897 rv = brandBundle->GetStringFromName(MOZ_UTF16("brandShortName"),
11898 getter_Copies(brandName));
11899
11900 nsXPIDLString msgString, button0Title;
11901 if (NS_FAILED(rv)) { // No brand, use the generic version.
11902 rv = appBundle->GetStringFromName(MOZ_UTF16("confirmRepostPrompt"),
11903 getter_Copies(msgString));
11904 }
11905 else {
11906 // Brand available - if the app has an override file with formatting, the app name will
11907 // be included. Without an override, the prompt will look like the generic version.
11908 const char16_t *formatStrings[] = { brandName.get() };
11909 rv = appBundle->FormatStringFromName(MOZ_UTF16("confirmRepostPrompt"),
11910 formatStrings, ArrayLength(formatStrings),
11911 getter_Copies(msgString));
11912 }
11913 if (NS_FAILED(rv)) return rv;
11914
11915 rv = appBundle->GetStringFromName(MOZ_UTF16("resendButton.label"),
11916 getter_Copies(button0Title));
11917 if (NS_FAILED(rv)) return rv;
11918
11919 int32_t buttonPressed;
11920 // The actual value here is irrelevant, but we can't pass an invalid
11921 // bool through XPConnect.
11922 bool checkState = false;
11923 rv = prompter->
11924 ConfirmEx(nullptr, msgString.get(),
11925 (nsIPrompt::BUTTON_POS_0 * nsIPrompt::BUTTON_TITLE_IS_STRING) +
11926 (nsIPrompt::BUTTON_POS_1 * nsIPrompt::BUTTON_TITLE_CANCEL),
11927 button0Title.get(), nullptr, nullptr, nullptr, &checkState, &buttonPressed);
11928 if (NS_FAILED(rv)) return rv;
11929
11930 *aRepost = (buttonPressed == 0);
11931 return NS_OK;
11932 }
11933
11934 NS_IMETHODIMP
11935 nsDocShell::GetPromptAndStringBundle(nsIPrompt ** aPrompt,
11936 nsIStringBundle ** aStringBundle)
11937 {
11938 NS_ENSURE_SUCCESS(GetInterface(NS_GET_IID(nsIPrompt), (void **) aPrompt),
11939 NS_ERROR_FAILURE);
11940
11941 nsCOMPtr<nsIStringBundleService> stringBundleService =
11942 mozilla::services::GetStringBundleService();
11943 NS_ENSURE_TRUE(stringBundleService, NS_ERROR_FAILURE);
11944
11945 NS_ENSURE_SUCCESS(stringBundleService->
11946 CreateBundle(kAppstringsBundleURL,
11947 aStringBundle),
11948 NS_ERROR_FAILURE);
11949
11950 return NS_OK;
11951 }
11952
11953 NS_IMETHODIMP
11954 nsDocShell::GetChildOffset(nsIDOMNode * aChild, nsIDOMNode * aParent,
11955 int32_t * aOffset)
11956 {
11957 NS_ENSURE_ARG_POINTER(aChild || aParent);
11958
11959 nsCOMPtr<nsIDOMNodeList> childNodes;
11960 NS_ENSURE_SUCCESS(aParent->GetChildNodes(getter_AddRefs(childNodes)),
11961 NS_ERROR_FAILURE);
11962 NS_ENSURE_TRUE(childNodes, NS_ERROR_FAILURE);
11963
11964 int32_t i = 0;
11965
11966 for (; true; i++) {
11967 nsCOMPtr<nsIDOMNode> childNode;
11968 NS_ENSURE_SUCCESS(childNodes->Item(i, getter_AddRefs(childNode)),
11969 NS_ERROR_FAILURE);
11970 NS_ENSURE_TRUE(childNode, NS_ERROR_FAILURE);
11971
11972 if (childNode.get() == aChild) {
11973 *aOffset = i;
11974 return NS_OK;
11975 }
11976 }
11977
11978 return NS_ERROR_FAILURE;
11979 }
11980
11981 nsIScrollableFrame *
11982 nsDocShell::GetRootScrollFrame()
11983 {
11984 nsCOMPtr<nsIPresShell> shell = GetPresShell();
11985 NS_ENSURE_TRUE(shell, nullptr);
11986
11987 return shell->GetRootScrollFrameAsScrollableExternal();
11988 }
11989
11990 NS_IMETHODIMP
11991 nsDocShell::EnsureScriptEnvironment()
11992 {
11993 if (mScriptGlobal)
11994 return NS_OK;
11995
11996 if (mIsBeingDestroyed) {
11997 return NS_ERROR_NOT_AVAILABLE;
11998 }
11999
12000 #ifdef DEBUG
12001 NS_ASSERTION(!mInEnsureScriptEnv,
12002 "Infinite loop! Calling EnsureScriptEnvironment() from "
12003 "within EnsureScriptEnvironment()!");
12004
12005 // Yeah, this isn't re-entrant safe, but that's ok since if we
12006 // re-enter this method, we'll infinitely loop...
12007 AutoRestore<bool> boolSetter(mInEnsureScriptEnv);
12008 mInEnsureScriptEnv = true;
12009 #endif
12010
12011 nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
12012 NS_ENSURE_TRUE(browserChrome, NS_ERROR_NOT_AVAILABLE);
12013
12014 uint32_t chromeFlags;
12015 browserChrome->GetChromeFlags(&chromeFlags);
12016
12017 bool isModalContentWindow = (mItemType == typeContent) &&
12018 (chromeFlags & nsIWebBrowserChrome::CHROME_MODAL_CONTENT_WINDOW);
12019 // There can be various other content docshells associated with the
12020 // top-level window, like sidebars. Make sure that we only create an
12021 // nsGlobalModalWindow for the primary content shell.
12022 if (isModalContentWindow) {
12023 nsCOMPtr<nsIDocShellTreeItem> primaryItem;
12024 nsresult rv = mTreeOwner->GetPrimaryContentShell(getter_AddRefs(primaryItem));
12025 NS_ENSURE_SUCCESS(rv, rv);
12026 isModalContentWindow = (primaryItem == this);
12027 }
12028
12029 // If our window is modal and we're not opened as chrome, make
12030 // this window a modal content window.
12031 mScriptGlobal =
12032 NS_NewScriptGlobalObject(mItemType == typeChrome, isModalContentWindow);
12033 MOZ_ASSERT(mScriptGlobal);
12034
12035 mScriptGlobal->SetDocShell(this);
12036
12037 // Ensure the script object is set up to run script.
12038 return mScriptGlobal->EnsureScriptEnvironment();
12039 }
12040
12041
12042 NS_IMETHODIMP
12043 nsDocShell::EnsureEditorData()
12044 {
12045 bool openDocHasDetachedEditor = mOSHE && mOSHE->HasDetachedEditor();
12046 if (!mEditorData && !mIsBeingDestroyed && !openDocHasDetachedEditor) {
12047 // We shouldn't recreate the editor data if it already exists, or
12048 // we're shutting down, or we already have a detached editor data
12049 // stored in the session history. We should only have one editordata
12050 // per docshell.
12051 mEditorData = new nsDocShellEditorData(this);
12052 }
12053
12054 return mEditorData ? NS_OK : NS_ERROR_NOT_AVAILABLE;
12055 }
12056
12057 nsresult
12058 nsDocShell::EnsureTransferableHookData()
12059 {
12060 if (!mTransferableHookData) {
12061 mTransferableHookData = new nsTransferableHookData();
12062 if (!mTransferableHookData) return NS_ERROR_OUT_OF_MEMORY;
12063 }
12064
12065 return NS_OK;
12066 }
12067
12068
12069 NS_IMETHODIMP nsDocShell::EnsureFind()
12070 {
12071 nsresult rv;
12072 if (!mFind)
12073 {
12074 mFind = do_CreateInstance("@mozilla.org/embedcomp/find;1", &rv);
12075 if (NS_FAILED(rv)) return rv;
12076 }
12077
12078 // we promise that the nsIWebBrowserFind that we return has been set
12079 // up to point to the focused, or content window, so we have to
12080 // set that up each time.
12081
12082 nsIScriptGlobalObject* scriptGO = GetScriptGlobalObject();
12083 NS_ENSURE_TRUE(scriptGO, NS_ERROR_UNEXPECTED);
12084
12085 // default to our window
12086 nsCOMPtr<nsPIDOMWindow> ourWindow = do_QueryInterface(scriptGO);
12087 nsCOMPtr<nsPIDOMWindow> windowToSearch;
12088 nsFocusManager::GetFocusedDescendant(ourWindow, true, getter_AddRefs(windowToSearch));
12089
12090 nsCOMPtr<nsIWebBrowserFindInFrames> findInFrames = do_QueryInterface(mFind);
12091 if (!findInFrames) return NS_ERROR_NO_INTERFACE;
12092
12093 rv = findInFrames->SetRootSearchFrame(ourWindow);
12094 if (NS_FAILED(rv)) return rv;
12095 rv = findInFrames->SetCurrentSearchFrame(windowToSearch);
12096 if (NS_FAILED(rv)) return rv;
12097
12098 return NS_OK;
12099 }
12100
12101 bool
12102 nsDocShell::IsFrame()
12103 {
12104 nsCOMPtr<nsIDocShellTreeItem> parent;
12105 GetSameTypeParent(getter_AddRefs(parent));
12106 return !!parent;
12107 }
12108
12109 /* boolean IsBeingDestroyed (); */
12110 NS_IMETHODIMP
12111 nsDocShell::IsBeingDestroyed(bool *aDoomed)
12112 {
12113 NS_ENSURE_ARG(aDoomed);
12114 *aDoomed = mIsBeingDestroyed;
12115 return NS_OK;
12116 }
12117
12118
12119 NS_IMETHODIMP
12120 nsDocShell::GetIsExecutingOnLoadHandler(bool *aResult)
12121 {
12122 NS_ENSURE_ARG(aResult);
12123 *aResult = mIsExecutingOnLoadHandler;
12124 return NS_OK;
12125 }
12126
12127 NS_IMETHODIMP
12128 nsDocShell::GetLayoutHistoryState(nsILayoutHistoryState **aLayoutHistoryState)
12129 {
12130 if (mOSHE)
12131 mOSHE->GetLayoutHistoryState(aLayoutHistoryState);
12132 return NS_OK;
12133 }
12134
12135 NS_IMETHODIMP
12136 nsDocShell::SetLayoutHistoryState(nsILayoutHistoryState *aLayoutHistoryState)
12137 {
12138 if (mOSHE)
12139 mOSHE->SetLayoutHistoryState(aLayoutHistoryState);
12140 return NS_OK;
12141 }
12142
12143 //*****************************************************************************
12144 //*** nsRefreshTimer: Object Management
12145 //*****************************************************************************
12146
12147 nsRefreshTimer::nsRefreshTimer()
12148 : mDelay(0), mRepeat(false), mMetaRefresh(false)
12149 {
12150 }
12151
12152 nsRefreshTimer::~nsRefreshTimer()
12153 {
12154 }
12155
12156 //*****************************************************************************
12157 // nsRefreshTimer::nsISupports
12158 //*****************************************************************************
12159
12160 NS_IMPL_ADDREF(nsRefreshTimer)
12161 NS_IMPL_RELEASE(nsRefreshTimer)
12162
12163 NS_INTERFACE_MAP_BEGIN(nsRefreshTimer)
12164 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITimerCallback)
12165 NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
12166 NS_INTERFACE_MAP_END_THREADSAFE
12167
12168 ///*****************************************************************************
12169 // nsRefreshTimer::nsITimerCallback
12170 //******************************************************************************
12171 NS_IMETHODIMP
12172 nsRefreshTimer::Notify(nsITimer * aTimer)
12173 {
12174 NS_ASSERTION(mDocShell, "DocShell is somehow null");
12175
12176 if (mDocShell && aTimer) {
12177 // Get the delay count to determine load type
12178 uint32_t delay = 0;
12179 aTimer->GetDelay(&delay);
12180 mDocShell->ForceRefreshURIFromTimer(mURI, delay, mMetaRefresh, aTimer);
12181 }
12182 return NS_OK;
12183 }
12184
12185 //*****************************************************************************
12186 // nsDocShell::InterfaceRequestorProxy
12187 //*****************************************************************************
12188 nsDocShell::InterfaceRequestorProxy::InterfaceRequestorProxy(nsIInterfaceRequestor* p)
12189 {
12190 if (p) {
12191 mWeakPtr = do_GetWeakReference(p);
12192 }
12193 }
12194
12195 nsDocShell::InterfaceRequestorProxy::~InterfaceRequestorProxy()
12196 {
12197 mWeakPtr = nullptr;
12198 }
12199
12200 NS_IMPL_ISUPPORTS(nsDocShell::InterfaceRequestorProxy, nsIInterfaceRequestor)
12201
12202 NS_IMETHODIMP
12203 nsDocShell::InterfaceRequestorProxy::GetInterface(const nsIID & aIID, void **aSink)
12204 {
12205 NS_ENSURE_ARG_POINTER(aSink);
12206 nsCOMPtr<nsIInterfaceRequestor> ifReq = do_QueryReferent(mWeakPtr);
12207 if (ifReq) {
12208 return ifReq->GetInterface(aIID, aSink);
12209 }
12210 *aSink = nullptr;
12211 return NS_NOINTERFACE;
12212 }
12213
12214 nsresult
12215 nsDocShell::SetBaseUrlForWyciwyg(nsIContentViewer * aContentViewer)
12216 {
12217 if (!aContentViewer)
12218 return NS_ERROR_FAILURE;
12219
12220 nsCOMPtr<nsIURI> baseURI;
12221 nsresult rv = NS_ERROR_NOT_AVAILABLE;
12222
12223 if (sURIFixup)
12224 rv = sURIFixup->CreateExposableURI(mCurrentURI,
12225 getter_AddRefs(baseURI));
12226
12227 // Get the current document and set the base uri
12228 if (baseURI) {
12229 nsIDocument* document = aContentViewer->GetDocument();
12230 if (document) {
12231 rv = document->SetBaseURI(baseURI);
12232 }
12233 }
12234 return rv;
12235 }
12236
12237 //*****************************************************************************
12238 // nsDocShell::nsIAuthPromptProvider
12239 //*****************************************************************************
12240
12241 NS_IMETHODIMP
12242 nsDocShell::GetAuthPrompt(uint32_t aPromptReason, const nsIID& iid,
12243 void** aResult)
12244 {
12245 // a priority prompt request will override a false mAllowAuth setting
12246 bool priorityPrompt = (aPromptReason == PROMPT_PROXY);
12247
12248 if (!mAllowAuth && !priorityPrompt)
12249 return NS_ERROR_NOT_AVAILABLE;
12250
12251 // we're either allowing auth, or it's a proxy request
12252 nsresult rv;
12253 nsCOMPtr<nsIPromptFactory> wwatch =
12254 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
12255 NS_ENSURE_SUCCESS(rv, rv);
12256
12257 rv = EnsureScriptEnvironment();
12258 NS_ENSURE_SUCCESS(rv, rv);
12259
12260 // Get the an auth prompter for our window so that the parenting
12261 // of the dialogs works as it should when using tabs.
12262
12263 return wwatch->GetPrompt(mScriptGlobal, iid,
12264 reinterpret_cast<void**>(aResult));
12265 }
12266
12267 //*****************************************************************************
12268 // nsDocShell::nsILoadContext
12269 //*****************************************************************************
12270 NS_IMETHODIMP
12271 nsDocShell::GetAssociatedWindow(nsIDOMWindow** aWindow)
12272 {
12273 CallGetInterface(this, aWindow);
12274 return NS_OK;
12275 }
12276
12277 NS_IMETHODIMP
12278 nsDocShell::GetTopWindow(nsIDOMWindow** aWindow)
12279 {
12280 nsCOMPtr<nsIDOMWindow> win = do_GetInterface(GetAsSupports(this));
12281 if (win) {
12282 win->GetTop(aWindow);
12283 }
12284 return NS_OK;
12285 }
12286
12287 NS_IMETHODIMP
12288 nsDocShell::GetTopFrameElement(nsIDOMElement** aElement)
12289 {
12290 *aElement = nullptr;
12291 nsCOMPtr<nsIDOMWindow> win = do_GetInterface(GetAsSupports(this));
12292 if (!win) {
12293 return NS_OK;
12294 }
12295
12296 nsCOMPtr<nsIDOMWindow> top;
12297 win->GetScriptableTop(getter_AddRefs(top));
12298 NS_ENSURE_TRUE(top, NS_ERROR_FAILURE);
12299
12300 // GetFrameElement, /not/ GetScriptableFrameElement -- if |top| is inside
12301 // <iframe mozbrowser>, we want to return the iframe, not null.
12302 return top->GetFrameElement(aElement);
12303 }
12304
12305 NS_IMETHODIMP
12306 nsDocShell::IsAppOfType(uint32_t aAppType, bool *aIsOfType)
12307 {
12308 nsCOMPtr<nsIDocShell> shell = this;
12309 while (shell) {
12310 uint32_t type;
12311 shell->GetAppType(&type);
12312 if (type == aAppType) {
12313 *aIsOfType = true;
12314 return NS_OK;
12315 }
12316 nsCOMPtr<nsIDocShellTreeItem> item = do_QueryInterface(shell);
12317 nsCOMPtr<nsIDocShellTreeItem> parent;
12318 item->GetParent(getter_AddRefs(parent));
12319 shell = do_QueryInterface(parent);
12320 }
12321
12322 *aIsOfType = false;
12323 return NS_OK;
12324 }
12325
12326 NS_IMETHODIMP
12327 nsDocShell::GetIsContent(bool *aIsContent)
12328 {
12329 *aIsContent = (mItemType == typeContent);
12330 return NS_OK;
12331 }
12332
12333 bool
12334 nsDocShell::IsOKToLoadURI(nsIURI* aURI)
12335 {
12336 NS_PRECONDITION(aURI, "Must have a URI!");
12337
12338 if (!mFiredUnloadEvent) {
12339 return true;
12340 }
12341
12342 if (!mLoadingURI) {
12343 return false;
12344 }
12345
12346 nsCOMPtr<nsIScriptSecurityManager> secMan =
12347 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
12348 return
12349 secMan &&
12350 NS_SUCCEEDED(secMan->CheckSameOriginURI(aURI, mLoadingURI, false));
12351 }
12352
12353 //
12354 // Routines for selection and clipboard
12355 //
12356 nsresult
12357 nsDocShell::GetControllerForCommand(const char * inCommand,
12358 nsIController** outController)
12359 {
12360 NS_ENSURE_ARG_POINTER(outController);
12361 *outController = nullptr;
12362
12363 NS_ENSURE_TRUE(mScriptGlobal, NS_ERROR_FAILURE);
12364
12365 nsCOMPtr<nsPIWindowRoot> root = mScriptGlobal->GetTopWindowRoot();
12366 NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
12367
12368 return root->GetControllerForCommand(inCommand, outController);
12369 }
12370
12371 NS_IMETHODIMP
12372 nsDocShell::IsCommandEnabled(const char * inCommand, bool* outEnabled)
12373 {
12374 NS_ENSURE_ARG_POINTER(outEnabled);
12375 *outEnabled = false;
12376
12377 nsresult rv = NS_ERROR_FAILURE;
12378
12379 nsCOMPtr<nsIController> controller;
12380 rv = GetControllerForCommand (inCommand, getter_AddRefs(controller));
12381 if (controller)
12382 rv = controller->IsCommandEnabled(inCommand, outEnabled);
12383
12384 return rv;
12385 }
12386
12387 NS_IMETHODIMP
12388 nsDocShell::DoCommand(const char * inCommand)
12389 {
12390 nsresult rv = NS_ERROR_FAILURE;
12391
12392 nsCOMPtr<nsIController> controller;
12393 rv = GetControllerForCommand(inCommand, getter_AddRefs(controller));
12394 if (controller)
12395 rv = controller->DoCommand(inCommand);
12396
12397 return rv;
12398 }
12399
12400 nsresult
12401 nsDocShell::EnsureCommandHandler()
12402 {
12403 if (!mCommandManager)
12404 {
12405 nsCOMPtr<nsPICommandUpdater> commandUpdater =
12406 do_CreateInstance("@mozilla.org/embedcomp/command-manager;1");
12407 if (!commandUpdater) return NS_ERROR_OUT_OF_MEMORY;
12408
12409 nsCOMPtr<nsIDOMWindow> domWindow =
12410 do_GetInterface(static_cast<nsIInterfaceRequestor *>(this));
12411
12412 nsresult rv = commandUpdater->Init(domWindow);
12413 if (NS_SUCCEEDED(rv))
12414 mCommandManager = do_QueryInterface(commandUpdater);
12415 }
12416
12417 return mCommandManager ? NS_OK : NS_ERROR_FAILURE;
12418 }
12419
12420 NS_IMETHODIMP
12421 nsDocShell::CanCutSelection(bool* aResult)
12422 {
12423 return IsCommandEnabled("cmd_cut", aResult);
12424 }
12425
12426 NS_IMETHODIMP
12427 nsDocShell::CanCopySelection(bool* aResult)
12428 {
12429 return IsCommandEnabled("cmd_copy", aResult);
12430 }
12431
12432 NS_IMETHODIMP
12433 nsDocShell::CanCopyLinkLocation(bool* aResult)
12434 {
12435 return IsCommandEnabled("cmd_copyLink", aResult);
12436 }
12437
12438 NS_IMETHODIMP
12439 nsDocShell::CanCopyImageLocation(bool* aResult)
12440 {
12441 return IsCommandEnabled("cmd_copyImageLocation",
12442 aResult);
12443 }
12444
12445 NS_IMETHODIMP
12446 nsDocShell::CanCopyImageContents(bool* aResult)
12447 {
12448 return IsCommandEnabled("cmd_copyImageContents",
12449 aResult);
12450 }
12451
12452 NS_IMETHODIMP
12453 nsDocShell::CanPaste(bool* aResult)
12454 {
12455 return IsCommandEnabled("cmd_paste", aResult);
12456 }
12457
12458 NS_IMETHODIMP
12459 nsDocShell::CutSelection(void)
12460 {
12461 return DoCommand ( "cmd_cut" );
12462 }
12463
12464 NS_IMETHODIMP
12465 nsDocShell::CopySelection(void)
12466 {
12467 return DoCommand ( "cmd_copy" );
12468 }
12469
12470 NS_IMETHODIMP
12471 nsDocShell::CopyLinkLocation(void)
12472 {
12473 return DoCommand ( "cmd_copyLink" );
12474 }
12475
12476 NS_IMETHODIMP
12477 nsDocShell::CopyImageLocation(void)
12478 {
12479 return DoCommand ( "cmd_copyImageLocation" );
12480 }
12481
12482 NS_IMETHODIMP
12483 nsDocShell::CopyImageContents(void)
12484 {
12485 return DoCommand ( "cmd_copyImageContents" );
12486 }
12487
12488 NS_IMETHODIMP
12489 nsDocShell::Paste(void)
12490 {
12491 return DoCommand ( "cmd_paste" );
12492 }
12493
12494 NS_IMETHODIMP
12495 nsDocShell::SelectAll(void)
12496 {
12497 return DoCommand ( "cmd_selectAll" );
12498 }
12499
12500 //
12501 // SelectNone
12502 //
12503 // Collapses the current selection, insertion point ends up at beginning
12504 // of previous selection.
12505 //
12506 NS_IMETHODIMP
12507 nsDocShell::SelectNone(void)
12508 {
12509 return DoCommand ( "cmd_selectNone" );
12510 }
12511
12512 //----------------------------------------------------------------------
12513
12514 // link handling
12515
12516 class OnLinkClickEvent : public nsRunnable {
12517 public:
12518 OnLinkClickEvent(nsDocShell* aHandler, nsIContent* aContent,
12519 nsIURI* aURI,
12520 const char16_t* aTargetSpec,
12521 const nsAString& aFileName,
12522 nsIInputStream* aPostDataStream,
12523 nsIInputStream* aHeadersDataStream,
12524 bool aIsTrusted);
12525
12526 NS_IMETHOD Run() {
12527 nsAutoPopupStatePusher popupStatePusher(mPopupState);
12528
12529 nsCxPusher pusher;
12530 if (mIsTrusted || pusher.Push(mContent)) {
12531 mHandler->OnLinkClickSync(mContent, mURI,
12532 mTargetSpec.get(), mFileName,
12533 mPostDataStream, mHeadersDataStream,
12534 nullptr, nullptr);
12535 }
12536 return NS_OK;
12537 }
12538
12539 private:
12540 nsRefPtr<nsDocShell> mHandler;
12541 nsCOMPtr<nsIURI> mURI;
12542 nsString mTargetSpec;
12543 nsString mFileName;
12544 nsCOMPtr<nsIInputStream> mPostDataStream;
12545 nsCOMPtr<nsIInputStream> mHeadersDataStream;
12546 nsCOMPtr<nsIContent> mContent;
12547 PopupControlState mPopupState;
12548 bool mIsTrusted;
12549 };
12550
12551 OnLinkClickEvent::OnLinkClickEvent(nsDocShell* aHandler,
12552 nsIContent *aContent,
12553 nsIURI* aURI,
12554 const char16_t* aTargetSpec,
12555 const nsAString& aFileName,
12556 nsIInputStream* aPostDataStream,
12557 nsIInputStream* aHeadersDataStream,
12558 bool aIsTrusted)
12559 : mHandler(aHandler)
12560 , mURI(aURI)
12561 , mTargetSpec(aTargetSpec)
12562 , mFileName(aFileName)
12563 , mPostDataStream(aPostDataStream)
12564 , mHeadersDataStream(aHeadersDataStream)
12565 , mContent(aContent)
12566 , mPopupState(mHandler->mScriptGlobal->GetPopupControlState())
12567 , mIsTrusted(aIsTrusted)
12568 {
12569 }
12570
12571 //----------------------------------------
12572
12573 NS_IMETHODIMP
12574 nsDocShell::OnLinkClick(nsIContent* aContent,
12575 nsIURI* aURI,
12576 const char16_t* aTargetSpec,
12577 const nsAString& aFileName,
12578 nsIInputStream* aPostDataStream,
12579 nsIInputStream* aHeadersDataStream,
12580 bool aIsTrusted)
12581 {
12582 NS_ASSERTION(NS_IsMainThread(), "wrong thread");
12583
12584 if (!IsOKToLoadURI(aURI)) {
12585 return NS_OK;
12586 }
12587
12588 // On history navigation through Back/Forward buttons, don't execute
12589 // automatic JavaScript redirection such as |anchorElement.click()| or
12590 // |formElement.submit()|.
12591 //
12592 // XXX |formElement.submit()| bypasses this checkpoint because it calls
12593 // nsDocShell::OnLinkClickSync(...) instead.
12594 if (ShouldBlockLoadingForBackButton()) {
12595 return NS_OK;
12596 }
12597
12598 if (aContent->IsEditable()) {
12599 return NS_OK;
12600 }
12601
12602 nsresult rv = NS_ERROR_FAILURE;
12603 nsAutoString target;
12604
12605 nsCOMPtr<nsIWebBrowserChrome3> browserChrome3 = do_GetInterface(mTreeOwner);
12606 if (browserChrome3) {
12607 nsCOMPtr<nsIDOMNode> linkNode = do_QueryInterface(aContent);
12608 nsAutoString oldTarget(aTargetSpec);
12609 rv = browserChrome3->OnBeforeLinkTraversal(oldTarget, aURI,
12610 linkNode, mIsAppTab, target);
12611 }
12612
12613 if (NS_FAILED(rv))
12614 target = aTargetSpec;
12615
12616 nsCOMPtr<nsIRunnable> ev =
12617 new OnLinkClickEvent(this, aContent, aURI, target.get(), aFileName,
12618 aPostDataStream, aHeadersDataStream, aIsTrusted);
12619 return NS_DispatchToCurrentThread(ev);
12620 }
12621
12622 NS_IMETHODIMP
12623 nsDocShell::OnLinkClickSync(nsIContent *aContent,
12624 nsIURI* aURI,
12625 const char16_t* aTargetSpec,
12626 const nsAString& aFileName,
12627 nsIInputStream* aPostDataStream,
12628 nsIInputStream* aHeadersDataStream,
12629 nsIDocShell** aDocShell,
12630 nsIRequest** aRequest)
12631 {
12632 // Initialize the DocShell / Request
12633 if (aDocShell) {
12634 *aDocShell = nullptr;
12635 }
12636 if (aRequest) {
12637 *aRequest = nullptr;
12638 }
12639
12640 if (!IsOKToLoadURI(aURI)) {
12641 return NS_OK;
12642 }
12643
12644 // XXX When the linking node was HTMLFormElement, it is synchronous event.
12645 // That is, the caller of this method is not |OnLinkClickEvent::Run()|
12646 // but |HTMLFormElement::SubmitSubmission(...)|.
12647 if (nsGkAtoms::form == aContent->Tag() && ShouldBlockLoadingForBackButton()) {
12648 return NS_OK;
12649 }
12650
12651 if (aContent->IsEditable()) {
12652 return NS_OK;
12653 }
12654
12655 {
12656 // defer to an external protocol handler if necessary...
12657 nsCOMPtr<nsIExternalProtocolService> extProtService =
12658 do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID);
12659 if (extProtService) {
12660 nsAutoCString scheme;
12661 aURI->GetScheme(scheme);
12662 if (!scheme.IsEmpty()) {
12663 // if the URL scheme does not correspond to an exposed protocol, then we
12664 // need to hand this link click over to the external protocol handler.
12665 bool isExposed;
12666 nsresult rv = extProtService->IsExposedProtocol(scheme.get(), &isExposed);
12667 if (NS_SUCCEEDED(rv) && !isExposed) {
12668 return extProtService->LoadURI(aURI, this);
12669 }
12670 }
12671 }
12672 }
12673
12674 // Get the owner document of the link that was clicked, this will be
12675 // the document that the link is in, or the last document that the
12676 // link was in. From that document, we'll get the URI to use as the
12677 // referer, since the current URI in this docshell may be a
12678 // new document that we're in the process of loading.
12679 nsCOMPtr<nsIDocument> refererDoc = aContent->OwnerDoc();
12680 NS_ENSURE_TRUE(refererDoc, NS_ERROR_UNEXPECTED);
12681
12682 // Now check that the refererDoc's inner window is the current inner
12683 // window for mScriptGlobal. If it's not, then we don't want to
12684 // follow this link.
12685 nsPIDOMWindow* refererInner = refererDoc->GetInnerWindow();
12686 NS_ENSURE_TRUE(refererInner, NS_ERROR_UNEXPECTED);
12687 if (!mScriptGlobal ||
12688 mScriptGlobal->GetCurrentInnerWindow() != refererInner) {
12689 // We're no longer the current inner window
12690 return NS_OK;
12691 }
12692
12693 nsCOMPtr<nsIURI> referer = refererDoc->GetDocumentURI();
12694
12695 // referer could be null here in some odd cases, but that's ok,
12696 // we'll just load the link w/o sending a referer in those cases.
12697
12698 nsAutoString target(aTargetSpec);
12699
12700 // If this is an anchor element, grab its type property to use as a hint
12701 nsAutoString typeHint;
12702 nsCOMPtr<nsIDOMHTMLAnchorElement> anchor(do_QueryInterface(aContent));
12703 if (anchor) {
12704 anchor->GetType(typeHint);
12705 NS_ConvertUTF16toUTF8 utf8Hint(typeHint);
12706 nsAutoCString type, dummy;
12707 NS_ParseContentType(utf8Hint, type, dummy);
12708 CopyUTF8toUTF16(type, typeHint);
12709 }
12710
12711 // Clone the URI now, in case a content policy or something messes
12712 // with it under InternalLoad; we do _not_ want to change the URI
12713 // our caller passed in.
12714 nsCOMPtr<nsIURI> clonedURI;
12715 aURI->Clone(getter_AddRefs(clonedURI));
12716 if (!clonedURI) {
12717 return NS_ERROR_OUT_OF_MEMORY;
12718 }
12719
12720 nsresult rv = InternalLoad(clonedURI, // New URI
12721 referer, // Referer URI
12722 aContent->NodePrincipal(), // Owner is our node's
12723 // principal
12724 INTERNAL_LOAD_FLAGS_NONE,
12725 target.get(), // Window target
12726 NS_LossyConvertUTF16toASCII(typeHint).get(),
12727 aFileName, // Download as file
12728 aPostDataStream, // Post data stream
12729 aHeadersDataStream, // Headers stream
12730 LOAD_LINK, // Load type
12731 nullptr, // No SHEntry
12732 true, // first party site
12733 NullString(), // No srcdoc
12734 this, // We are the source
12735 nullptr, // baseURI not needed
12736 aDocShell, // DocShell out-param
12737 aRequest); // Request out-param
12738 if (NS_SUCCEEDED(rv)) {
12739 DispatchPings(aContent, aURI, referer);
12740 }
12741 return rv;
12742 }
12743
12744 NS_IMETHODIMP
12745 nsDocShell::OnOverLink(nsIContent* aContent,
12746 nsIURI* aURI,
12747 const char16_t* aTargetSpec)
12748 {
12749 if (aContent->IsEditable()) {
12750 return NS_OK;
12751 }
12752
12753 nsCOMPtr<nsIWebBrowserChrome2> browserChrome2 = do_GetInterface(mTreeOwner);
12754 nsresult rv = NS_ERROR_FAILURE;
12755
12756 nsCOMPtr<nsIWebBrowserChrome> browserChrome;
12757 if (!browserChrome2) {
12758 browserChrome = do_GetInterface(mTreeOwner);
12759 if (!browserChrome)
12760 return rv;
12761 }
12762
12763 nsCOMPtr<nsITextToSubURI> textToSubURI =
12764 do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
12765 if (NS_FAILED(rv))
12766 return rv;
12767
12768 // use url origin charset to unescape the URL
12769 nsAutoCString charset;
12770 rv = aURI->GetOriginCharset(charset);
12771 NS_ENSURE_SUCCESS(rv, rv);
12772
12773 nsAutoCString spec;
12774 rv = aURI->GetSpec(spec);
12775 NS_ENSURE_SUCCESS(rv, rv);
12776
12777 nsAutoString uStr;
12778 rv = textToSubURI->UnEscapeURIForUI(charset, spec, uStr);
12779 NS_ENSURE_SUCCESS(rv, rv);
12780
12781 mozilla::net::SeerPredict(aURI, mCurrentURI, nsINetworkSeer::PREDICT_LINK,
12782 this, nullptr);
12783
12784 if (browserChrome2) {
12785 nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aContent);
12786 rv = browserChrome2->SetStatusWithContext(nsIWebBrowserChrome::STATUS_LINK,
12787 uStr, element);
12788 } else {
12789 rv = browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_LINK, uStr.get());
12790 }
12791 return rv;
12792 }
12793
12794 NS_IMETHODIMP
12795 nsDocShell::OnLeaveLink()
12796 {
12797 nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
12798 nsresult rv = NS_ERROR_FAILURE;
12799
12800 if (browserChrome) {
12801 rv = browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_LINK,
12802 EmptyString().get());
12803 }
12804 return rv;
12805 }
12806
12807 bool
12808 nsDocShell::ShouldBlockLoadingForBackButton()
12809 {
12810 if (!(mLoadType & LOAD_CMD_HISTORY) ||
12811 EventStateManager::IsHandlingUserInput() ||
12812 !Preferences::GetBool("accessibility.blockjsredirection")) {
12813 return false;
12814 }
12815
12816 bool canGoForward = false;
12817 GetCanGoForward(&canGoForward);
12818 return canGoForward;
12819 }
12820
12821 bool
12822 nsDocShell::PluginsAllowedInCurrentDoc()
12823 {
12824 bool pluginsAllowed = false;
12825
12826 if (!mContentViewer) {
12827 return false;
12828 }
12829
12830 nsIDocument* doc = mContentViewer->GetDocument();
12831 if (!doc) {
12832 return false;
12833 }
12834
12835 doc->GetAllowPlugins(&pluginsAllowed);
12836 return pluginsAllowed;
12837 }
12838
12839 //----------------------------------------------------------------------
12840 // Web Shell Services API
12841
12842 //This functions is only called when a new charset is detected in loading a document.
12843 //Its name should be changed to "CharsetReloadDocument"
12844 NS_IMETHODIMP
12845 nsDocShell::ReloadDocument(const char* aCharset,
12846 int32_t aSource)
12847 {
12848
12849 // XXX hack. keep the aCharset and aSource wait to pick it up
12850 nsCOMPtr<nsIContentViewer> cv;
12851 NS_ENSURE_SUCCESS(GetContentViewer(getter_AddRefs(cv)), NS_ERROR_FAILURE);
12852 if (cv)
12853 {
12854 nsCOMPtr<nsIMarkupDocumentViewer> muDV = do_QueryInterface(cv);
12855 if (muDV)
12856 {
12857 int32_t hint;
12858 muDV->GetHintCharacterSetSource(&hint);
12859 if (aSource > hint)
12860 {
12861 nsCString charset(aCharset);
12862 muDV->SetHintCharacterSet(charset);
12863 muDV->SetHintCharacterSetSource(aSource);
12864 if(eCharsetReloadRequested != mCharsetReloadState)
12865 {
12866 mCharsetReloadState = eCharsetReloadRequested;
12867 return Reload(LOAD_FLAGS_CHARSET_CHANGE);
12868 }
12869 }
12870 }
12871 }
12872 //return failure if this request is not accepted due to mCharsetReloadState
12873 return NS_ERROR_DOCSHELL_REQUEST_REJECTED;
12874 }
12875
12876
12877 NS_IMETHODIMP
12878 nsDocShell::StopDocumentLoad(void)
12879 {
12880 if(eCharsetReloadRequested != mCharsetReloadState)
12881 {
12882 Stop(nsIWebNavigation::STOP_ALL);
12883 return NS_OK;
12884 }
12885 //return failer if this request is not accepted due to mCharsetReloadState
12886 return NS_ERROR_DOCSHELL_REQUEST_REJECTED;
12887 }
12888
12889 NS_IMETHODIMP
12890 nsDocShell::GetPrintPreview(nsIWebBrowserPrint** aPrintPreview)
12891 {
12892 *aPrintPreview = nullptr;
12893 #if NS_PRINT_PREVIEW
12894 nsCOMPtr<nsIDocumentViewerPrint> print = do_QueryInterface(mContentViewer);
12895 if (!print || !print->IsInitializedForPrintPreview()) {
12896 Stop(nsIWebNavigation::STOP_ALL);
12897 nsCOMPtr<nsIPrincipal> principal =
12898 do_CreateInstance("@mozilla.org/nullprincipal;1");
12899 NS_ENSURE_STATE(principal);
12900 nsresult rv = CreateAboutBlankContentViewer(principal, nullptr);
12901 NS_ENSURE_SUCCESS(rv, rv);
12902 print = do_QueryInterface(mContentViewer);
12903 NS_ENSURE_STATE(print);
12904 print->InitializeForPrintPreview();
12905 }
12906 nsCOMPtr<nsIWebBrowserPrint> result = do_QueryInterface(print);
12907 result.forget(aPrintPreview);
12908 return NS_OK;
12909 #else
12910 return NS_ERROR_NOT_IMPLEMENTED;
12911 #endif
12912 }
12913
12914
12915 #ifdef DEBUG
12916 unsigned long nsDocShell::gNumberOfDocShells = 0;
12917 #endif
12918
12919 NS_IMETHODIMP
12920 nsDocShell::GetCanExecuteScripts(bool *aResult)
12921 {
12922 *aResult = mCanExecuteScripts;
12923 return NS_OK;
12924 }
12925
12926 NS_IMETHODIMP
12927 nsDocShell::SetIsApp(uint32_t aOwnAppId)
12928 {
12929 mOwnOrContainingAppId = aOwnAppId;
12930 if (aOwnAppId != nsIScriptSecurityManager::NO_APP_ID &&
12931 aOwnAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID) {
12932 mFrameType = eFrameTypeApp;
12933 } else {
12934 mFrameType = eFrameTypeRegular;
12935 }
12936
12937 return NS_OK;
12938 }
12939
12940 NS_IMETHODIMP
12941 nsDocShell::SetIsBrowserInsideApp(uint32_t aContainingAppId)
12942 {
12943 mOwnOrContainingAppId = aContainingAppId;
12944 mFrameType = eFrameTypeBrowser;
12945 return NS_OK;
12946 }
12947
12948 /* [infallible] */ NS_IMETHODIMP
12949 nsDocShell::GetIsBrowserElement(bool* aIsBrowser)
12950 {
12951 *aIsBrowser = (mFrameType == eFrameTypeBrowser);
12952 return NS_OK;
12953 }
12954
12955 /* [infallible] */ NS_IMETHODIMP
12956 nsDocShell::GetIsApp(bool* aIsApp)
12957 {
12958 *aIsApp = (mFrameType == eFrameTypeApp);
12959 return NS_OK;
12960 }
12961
12962 /* [infallible] */ NS_IMETHODIMP
12963 nsDocShell::GetIsBrowserOrApp(bool* aIsBrowserOrApp)
12964 {
12965 switch (mFrameType) {
12966 case eFrameTypeRegular:
12967 *aIsBrowserOrApp = false;
12968 break;
12969 case eFrameTypeBrowser:
12970 case eFrameTypeApp:
12971 *aIsBrowserOrApp = true;
12972 break;
12973 }
12974
12975 return NS_OK;
12976 }
12977
12978 nsDocShell::FrameType
12979 nsDocShell::GetInheritedFrameType()
12980 {
12981 if (mFrameType != eFrameTypeRegular) {
12982 return mFrameType;
12983 }
12984
12985 nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
12986 GetSameTypeParent(getter_AddRefs(parentAsItem));
12987
12988 nsCOMPtr<nsIDocShell> parent = do_QueryInterface(parentAsItem);
12989 if (!parent) {
12990 return eFrameTypeRegular;
12991 }
12992
12993 return static_cast<nsDocShell*>(parent.get())->GetInheritedFrameType();
12994 }
12995
12996 /* [infallible] */ NS_IMETHODIMP
12997 nsDocShell::GetIsInBrowserElement(bool* aIsInBrowserElement)
12998 {
12999 *aIsInBrowserElement = (GetInheritedFrameType() == eFrameTypeBrowser);
13000 return NS_OK;
13001 }
13002
13003 /* [infallible] */ NS_IMETHODIMP
13004 nsDocShell::GetIsInBrowserOrApp(bool* aIsInBrowserOrApp)
13005 {
13006 switch (GetInheritedFrameType()) {
13007 case eFrameTypeRegular:
13008 *aIsInBrowserOrApp = false;
13009 break;
13010 case eFrameTypeBrowser:
13011 case eFrameTypeApp:
13012 *aIsInBrowserOrApp = true;
13013 break;
13014 }
13015
13016 return NS_OK;
13017 }
13018
13019 /* [infallible] */ NS_IMETHODIMP
13020 nsDocShell::GetAppId(uint32_t* aAppId)
13021 {
13022 if (mOwnOrContainingAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID) {
13023 *aAppId = mOwnOrContainingAppId;
13024 return NS_OK;
13025 }
13026
13027 nsCOMPtr<nsIDocShell> parent;
13028 GetSameTypeParentIgnoreBrowserAndAppBoundaries(getter_AddRefs(parent));
13029
13030 if (!parent) {
13031 *aAppId = nsIScriptSecurityManager::NO_APP_ID;
13032 return NS_OK;
13033 }
13034
13035 return parent->GetAppId(aAppId);
13036 }
13037
13038 NS_IMETHODIMP
13039 nsDocShell::GetAppManifestURL(nsAString& aAppManifestURL)
13040 {
13041 uint32_t appId;
13042 GetAppId(&appId);
13043
13044 if (appId != nsIScriptSecurityManager::NO_APP_ID &&
13045 appId != nsIScriptSecurityManager::UNKNOWN_APP_ID) {
13046 nsCOMPtr<nsIAppsService> appsService =
13047 do_GetService(APPS_SERVICE_CONTRACTID);
13048 NS_ASSERTION(appsService, "No AppsService available");
13049 appsService->GetManifestURLByLocalId(appId, aAppManifestURL);
13050 } else {
13051 aAppManifestURL.SetLength(0);
13052 }
13053
13054 return NS_OK;
13055 }
13056
13057 NS_IMETHODIMP
13058 nsDocShell::GetAsyncPanZoomEnabled(bool* aOut)
13059 {
13060 if (TabChild* tabChild = TabChild::GetFrom(this)) {
13061 *aOut = tabChild->IsAsyncPanZoomEnabled();
13062 return NS_OK;
13063 }
13064 *aOut = false;
13065 return NS_OK;
13066 }
13067
13068 bool
13069 nsDocShell::HasUnloadedParent()
13070 {
13071 nsCOMPtr<nsIDocShellTreeItem> currentTreeItem = this;
13072 while (currentTreeItem) {
13073 nsCOMPtr<nsIDocShellTreeItem> parentTreeItem;
13074 currentTreeItem->GetParent(getter_AddRefs(parentTreeItem));
13075 nsCOMPtr<nsIDocShell> parent = do_QueryInterface(parentTreeItem);
13076 if (parent) {
13077 bool inUnload = false;
13078 parent->GetIsInUnload(&inUnload);
13079 if (inUnload) {
13080 return true;
13081 }
13082 }
13083 currentTreeItem.swap(parentTreeItem);
13084 }
13085 return false;
13086 }
13087
13088 bool
13089 nsDocShell::IsInvisible()
13090 {
13091 return mInvisible;
13092 }
13093
13094 void
13095 nsDocShell::SetInvisible(bool aInvisible)
13096 {
13097 mInvisible = aInvisible;
13098 }

mercurial