Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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/. */
7 #include "nsDocShell.h"
9 #include <algorithm>
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"
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
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"
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"
83 // Local Includes
84 #include "nsDocShellLoadInfo.h"
85 #include "nsCDefaultURIFixup.h"
86 #include "nsDocShellEnumerator.h"
87 #include "nsSHistory.h"
88 #include "nsDocShellEditorData.h"
90 // Helper Classes
91 #include "nsError.h"
92 #include "nsEscape.h"
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"
124 // Editor-related
125 #include "nsIEditingSession.h"
127 #include "nsPIDOMWindow.h"
128 #include "nsGlobalWindow.h"
129 #include "nsPIWindowRoot.h"
130 #include "nsICachingChannel.h"
131 #include "nsIMultiPartChannel.h"
132 #include "nsIWyciwygChannel.h"
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"
138 // used to dispatch urls to default protocol handlers
139 #include "nsCExternalHandlerService.h"
140 #include "nsIExternalProtocolService.h"
142 #include "nsFocusManager.h"
144 #include "nsITextToSubURI.h"
146 #include "nsIJARChannel.h"
148 #include "prlog.h"
150 #include "nsISelectionDisplay.h"
152 #include "nsIGlobalHistory2.h"
154 #include "nsIFrame.h"
155 #include "nsSubDocumentFrame.h"
157 // for embedding
158 #include "nsIWebBrowserChromeFocus.h"
160 #if NS_PRINT_PREVIEW
161 #include "nsIDocumentViewerPrint.h"
162 #include "nsIWebBrowserPrint.h"
163 #endif
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"
193 static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
195 #if defined(DEBUG_bryner) || defined(DEBUG_chb)
196 //#define DEBUG_DOCSHELL_FOCUS
197 #define DEBUG_PAGE_CACHE
198 #endif
200 #ifdef XP_WIN
201 #include <process.h>
202 #define getpid _getpid
203 #else
204 #include <unistd.h> // for getpid()
205 #endif
207 using namespace mozilla;
208 using namespace mozilla::dom;
210 // True means sUseErrorPages has been added to preferences var cache.
211 static bool gAddedPreferencesVarCache = false;
213 bool nsDocShell::sUseErrorPages = false;
215 // Number of documents currently loading
216 static int32_t gNumberOfDocumentsLoading = 0;
218 // Global count of existing docshells.
219 static int32_t gDocShellCount = 0;
221 // Global count of docshells with the private attribute set
222 static uint32_t gNumberOfPrivateDocShells = 0;
224 // Global reference to the URI fixup service.
225 nsIURIFixup *nsDocShell::sURIFixup = 0;
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;
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
238 #ifdef PR_LOGGING
239 #ifdef DEBUG
240 static PRLogModuleInfo* gDocShellLog;
241 #endif
242 static PRLogModuleInfo* gDocShellLeakLog;
243 #endif
245 const char kBrandBundleURL[] = "chrome://branding/locale/brand.properties";
246 const char kAppstringsBundleURL[] = "chrome://global/locale/appstrings.properties";
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 }
259 //*****************************************************************************
260 // <a ping> support
261 //*****************************************************************************
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"
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);
288 *maxPerLink = 1;
289 *requireSameHost = true;
291 if (allow) {
292 Preferences::GetInt(PREF_PINGS_MAX_PER_LINK, maxPerLink);
293 Preferences::GetBool(PREF_PINGS_REQUIRE_SAME_HOST, requireSameHost);
294 }
296 return allow;
297 }
299 static bool
300 CheckPingURI(nsIURI* uri, nsIContent* content)
301 {
302 if (!uri)
303 return false;
305 // Check with nsIScriptSecurityManager
306 nsCOMPtr<nsIScriptSecurityManager> ssmgr =
307 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
308 NS_ENSURE_TRUE(ssmgr, false);
310 nsresult rv =
311 ssmgr->CheckLoadURIWithPrincipal(content->NodePrincipal(), uri,
312 nsIScriptSecurityManager::STANDARD);
313 if (NS_FAILED(rv)) {
314 return false;
315 }
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 }
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 }
336 typedef void (* ForEachPingCallback)(void *closure, nsIContent *content,
337 nsIURI *uri, nsIIOService *ios);
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.
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;
355 nsCOMPtr<nsIAtom> pingAtom = do_GetAtom("ping");
356 if (!pingAtom)
357 return;
359 nsAutoString value;
360 content->GetAttr(kNameSpaceID_None, pingAtom, value);
361 if (value.IsEmpty())
362 return;
364 nsCOMPtr<nsIIOService> ios = do_GetIOService();
365 if (!ios)
366 return;
368 nsIDocument *doc = content->OwnerDoc();
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 }
396 //----------------------------------------------------------------------
398 // We wait this many milliseconds before killing the ping channel...
399 #define PING_TIMEOUT 10000
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 }
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 }
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
430 nsPingListener(bool requireSameHost, nsIContent* content, nsILoadGroup* loadGroup)
431 : mRequireSameHost(requireSameHost),
432 mContent(content),
433 mLoadGroup(loadGroup)
434 {}
436 ~nsPingListener();
438 nsresult StartTimeout();
440 private:
441 bool mRequireSameHost;
442 nsCOMPtr<nsIContent> mContent;
443 nsCOMPtr<nsILoadGroup> mLoadGroup;
444 nsCOMPtr<nsITimer> mTimer;
445 };
447 NS_IMPL_ISUPPORTS(nsPingListener, nsIStreamListener, nsIRequestObserver,
448 nsIInterfaceRequestor, nsIChannelEventSink)
450 nsPingListener::~nsPingListener()
451 {
452 if (mTimer) {
453 mTimer->Cancel();
454 mTimer = nullptr;
455 }
456 }
458 nsresult
459 nsPingListener::StartTimeout()
460 {
461 nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
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 }
473 return NS_ERROR_OUT_OF_MEMORY;
474 }
476 NS_IMETHODIMP
477 nsPingListener::OnStartRequest(nsIRequest *request, nsISupports *context)
478 {
479 return NS_OK;
480 }
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 }
491 NS_IMETHODIMP
492 nsPingListener::OnStopRequest(nsIRequest *request, nsISupports *context,
493 nsresult status)
494 {
495 mLoadGroup = nullptr;
497 if (mTimer) {
498 mTimer->Cancel();
499 mTimer = nullptr;
500 }
502 return NS_OK;
503 }
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 }
514 return NS_ERROR_NO_INTERFACE;
515 }
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));
525 if (!CheckPingURI(newURI, mContent))
526 return NS_ERROR_ABORT;
528 if (!mRequireSameHost) {
529 callback->OnRedirectVerifyCallback(NS_OK);
530 return NS_OK;
531 }
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);
539 if (!IsSameHost(oldURI, newURI))
540 return NS_ERROR_ABORT;
542 callback->OnRedirectVerifyCallback(NS_OK);
543 return NS_OK;
544 }
546 struct SendPingInfo {
547 int32_t numPings;
548 int32_t maxPings;
549 bool requireSameHost;
550 nsIURI *target;
551 nsIURI *referrer;
552 };
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;
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 }
568 nsIDocument *doc = content->OwnerDoc();
570 nsCOMPtr<nsIChannel> chan;
571 ios->NewChannelFromURI(uri, getter_AddRefs(chan));
572 if (!chan)
573 return;
575 // Don't bother caching the result of this URI load.
576 chan->SetLoadFlags(nsIRequest::INHIBIT_CACHING);
578 nsCOMPtr<nsIHttpChannel> httpChan = do_QueryInterface(chan);
579 if (!httpChan)
580 return;
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());
588 httpChan->SetRequestMethod(NS_LITERAL_CSTRING("POST"));
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);
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);
603 nsCOMPtr<nsIScriptSecurityManager> sm =
604 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
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);
611 // Default to sending less data if NS_URIChainHasFlags() fails.
612 referrerIsSecure = NS_FAILED(rv) || referrerIsSecure;
614 bool sameOrigin =
615 NS_SUCCEEDED(sm->CheckSameOriginURI(info->referrer, uri, false));
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 }
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 }
634 nsCOMPtr<nsIUploadChannel2> uploadChan = do_QueryInterface(httpChan);
635 if (!uploadChan)
636 return;
638 NS_NAMED_LITERAL_CSTRING(uploadData, "PING");
640 nsCOMPtr<nsIInputStream> uploadStream;
641 NS_NewPostDataStream(getter_AddRefs(uploadStream), false, uploadData);
642 if (!uploadStream)
643 return;
645 uploadChan->ExplicitSetUploadStream(uploadStream,
646 NS_LITERAL_CSTRING("text/ping"), uploadData.Length(),
647 NS_LITERAL_CSTRING("POST"), false);
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);
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;
665 nsCOMPtr<nsIStreamListener> listener(pingListener);
667 // Observe redirects as well:
668 nsCOMPtr<nsIInterfaceRequestor> callbacks = do_QueryInterface(listener);
669 NS_ASSERTION(callbacks, "oops");
670 loadGroup->SetNotificationCallbacks(callbacks);
672 chan->AsyncOpen(listener, nullptr);
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++;
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 }
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;
693 if (!PingsEnabled(&info.maxPings, &info.requireSameHost))
694 return;
695 if (info.maxPings == 0)
696 return;
698 info.numPings = 0;
699 info.target = target;
700 info.referrer = referrer;
702 ForEachPing(content, SendPing, &info);
703 }
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 }
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 }
751 return result;
752 }
754 static nsISHEntry* GetRootSHEntry(nsISHEntry *entry);
756 static void
757 IncreasePrivateDocShellCount()
758 {
759 gNumberOfPrivateDocShells++;
760 if (gNumberOfPrivateDocShells > 1 ||
761 XRE_GetProcessType() != GeckoProcessType_Content) {
762 return;
763 }
765 mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton();
766 cc->SendPrivateDocShellsExist(true);
767 }
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 }
782 nsCOMPtr<nsIObserverService> obsvc = mozilla::services::GetObserverService();
783 if (obsvc) {
784 obsvc->NotifyObservers(nullptr, "last-pb-context-exited", nullptr);
785 }
786 }
787 }
789 //*****************************************************************************
790 //*** nsDocShell: Object Management
791 //*****************************************************************************
793 static uint64_t gDocshellIDCounter = 0;
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!");
858 CallGetService(NS_URIFIXUP_CONTRACTID, &sURIFixup);
859 }
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
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 }
885 nsDocShell::~nsDocShell()
886 {
887 Destroy();
889 nsCOMPtr<nsISHistoryInternal>
890 shPrivate(do_QueryInterface(mSessionHistory));
891 if (shPrivate) {
892 shPrivate->SetRootDocShell(nullptr);
893 }
895 if (--gDocShellCount == 0) {
896 NS_IF_RELEASE(sURIFixup);
897 }
899 #ifdef PR_LOGGING
900 if (gDocShellLeakLog)
901 PR_LOG(gDocShellLeakLog, PR_LOG_DEBUG, ("DOCSHELL %p destroyed\n", this));
902 #endif
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 }
917 nsresult
918 nsDocShell::Init()
919 {
920 nsresult rv = nsDocLoader::Init();
921 NS_ENSURE_SUCCESS(rv, rv);
923 NS_ASSERTION(mLoadGroup, "Something went wrong!");
925 mContentListener = new nsDSURIContentListener(this);
926 NS_ENSURE_TRUE(mContentListener, NS_ERROR_OUT_OF_MEMORY);
928 rv = mContentListener->Init();
929 NS_ENSURE_SUCCESS(rv, rv);
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);
939 rv = nsDocLoader::AddDocLoaderAsChildOfRoot(this);
940 NS_ENSURE_SUCCESS(rv, rv);
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);
949 }
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");
960 if (shell) {
961 shell->SetTreeOwner(nullptr);
962 }
963 }
965 nsDocLoader::DestroyChildren();
966 }
968 //*****************************************************************************
969 // nsDocShell::nsISupports
970 //*****************************************************************************
972 NS_IMPL_ADDREF_INHERITED(nsDocShell, nsDocLoader)
973 NS_IMPL_RELEASE_INHERITED(nsDocShell, nsDocLoader)
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)
996 ///*****************************************************************************
997 // nsDocShell::nsIInterfaceRequestor
998 //*****************************************************************************
999 NS_IMETHODIMP nsDocShell::GetInterface(const nsIID & aIID, void **aSink)
1000 {
1001 NS_PRECONDITION(aSink, "null out param");
1003 *aSink = nullptr;
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;
1033 // Return application cache associated with this docshell, if any
1035 nsCOMPtr<nsIContentViewer> contentViewer;
1036 GetContentViewer(getter_AddRefs(contentViewer));
1037 if (!contentViewer)
1038 return NS_ERROR_NO_INTERFACE;
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;
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);
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);
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;
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 }
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 }
1153 NS_IF_ADDREF(((nsISupports *) * aSink));
1154 return *aSink ? NS_OK : NS_NOINTERFACE;
1155 }
1157 uint32_t
1158 nsDocShell::
1159 ConvertDocShellLoadInfoToLoadType(nsDocShellInfoLoadType aDocShellLoadType)
1160 {
1161 uint32_t loadType = LOAD_NORMAL;
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 }
1231 return loadType;
1232 }
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 }
1308 return docShellLoadType;
1309 }
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");
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;
1343 uint32_t loadType = MAKE_LOAD_TYPE(LOAD_NORMAL, aLoadFlags);
1345 NS_ENSURE_ARG(aURI);
1347 if (!StartupTimeline::HasRecord(StartupTimeline::FIRST_LOAD_URI) &&
1348 mItemType == typeContent && !NS_IsAboutBlank(aURI)) {
1349 StartupTimeline::RecordOnce(StartupTimeline::FIRST_LOAD_URI);
1350 }
1352 // Extract the info from the DocShellLoadInfo struct...
1353 if (aLoadInfo) {
1354 aLoadInfo->GetReferrer(getter_AddRefs(referrer));
1356 nsDocShellInfoLoadType lt = nsIDocShellLoadInfo::loadNormal;
1357 aLoadInfo->GetLoadType(<);
1358 // Get the appropriate loadType from nsIDocShellLoadInfo type
1359 loadType = ConvertDocShellLoadInfoToLoadType(lt);
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 }
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
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;
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 */
1403 // Get the parent's load type
1404 parentDS->GetLoadType(&parentLoadType);
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 }
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
1494 if (shEntry) {
1495 #ifdef DEBUG
1496 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
1497 ("nsDocShell[%p]: loading from session history", this));
1498 #endif
1500 return LoadHistoryEntry(shEntry, loadType);
1501 }
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 }
1514 // Perform the load...
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!
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 }
1563 if (aLoadFlags & LOAD_FLAGS_DISALLOW_INHERIT_OWNER) {
1564 inheritOwner = false;
1565 owner = do_CreateInstance("@mozilla.org/nullprincipal;1");
1566 }
1568 uint32_t flags = 0;
1570 if (inheritOwner)
1571 flags |= INTERNAL_LOAD_FLAGS_INHERIT_OWNER;
1573 if (!sendReferrer)
1574 flags |= INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER;
1576 if (aLoadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP)
1577 flags |= INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
1579 if (aLoadFlags & LOAD_FLAGS_FIXUP_SCHEME_TYPOS)
1580 flags |= INTERNAL_LOAD_FLAGS_FIXUP_SCHEME_TYPOS;
1582 if (aLoadFlags & LOAD_FLAGS_FIRST_LOAD)
1583 flags |= INTERNAL_LOAD_FLAGS_FIRST_LOAD;
1585 if (aLoadFlags & LOAD_FLAGS_BYPASS_CLASSIFIER)
1586 flags |= INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER;
1588 if (aLoadFlags & LOAD_FLAGS_FORCE_ALLOW_COOKIES)
1589 flags |= INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES;
1591 if (isSrcdoc)
1592 flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC;
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 }
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);
1621 mAllowKeywordFixup = false;
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 }
1640 uint32_t loadType = LOAD_NORMAL;
1641 if (aLoadInfo) {
1642 nsDocShellInfoLoadType lt = nsIDocShellLoadInfo::loadNormal;
1643 (void) aLoadInfo->GetLoadType(<);
1644 // Get the appropriate LoadType from nsIDocShellLoadInfo type
1645 loadType = ConvertDocShellLoadInfoToLoadType(lt);
1646 }
1648 NS_ENSURE_SUCCESS(Stop(nsIWebNavigation::STOP_NETWORK), NS_ERROR_FAILURE);
1650 mLoadType = loadType;
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);
1659 nsCOMPtr<nsIURILoader>
1660 uriLoader(do_GetService(NS_URI_LOADER_CONTRACTID));
1661 NS_ENSURE_TRUE(uriLoader, NS_ERROR_FAILURE);
1663 NS_ENSURE_SUCCESS(DoChannelLoad(channel, uriLoader, false),
1664 NS_ERROR_FAILURE);
1665 return NS_OK;
1666 }
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);
1675 *aLoadInfo = localRef;
1676 NS_ADDREF(*aLoadInfo);
1677 return NS_OK;
1678 }
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 }
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;
1702 if (mTiming) {
1703 mTiming->NotifyUnloadEventStart();
1704 }
1706 mContentViewer->PageHide(aIsUnload);
1708 if (mTiming) {
1709 mTiming->NotifyUnloadEventEnd();
1710 }
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 }
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 }
1730 return NS_OK;
1731 }
1733 void
1734 nsDocShell::MaybeInitTiming()
1735 {
1736 if (mTiming) {
1737 return;
1738 }
1740 mTiming = new nsDOMNavigationTiming();
1741 mTiming->NotifyNavigationStart();
1742 }
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 }
1765 // Get origin document principal
1766 nsCOMPtr<nsIDocument> originDocument(do_GetInterface(aOriginTreeItem));
1767 NS_ENSURE_TRUE(originDocument, false);
1769 // Get target principal
1770 nsCOMPtr<nsIDocument> targetDocument(do_GetInterface(aTargetTreeItem));
1771 NS_ENSURE_TRUE(targetDocument, false);
1773 bool equal;
1774 nsresult rv = originDocument->NodePrincipal()->Equals(targetDocument->NodePrincipal(),
1775 &equal);
1776 if (NS_SUCCEEDED(rv) && equal) {
1777 return true;
1778 }
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;
1788 rv = originDocument->NodePrincipal()->GetURI(getter_AddRefs(originURI));
1789 if (NS_SUCCEEDED(rv) && originURI)
1790 innerOriginURI = NS_GetInnermostURI(originURI);
1792 rv = targetDocument->NodePrincipal()->GetURI(getter_AddRefs(targetURI));
1793 if (NS_SUCCEEDED(rv) && targetURI)
1794 innerTargetURI = NS_GetInnermostURI(targetURI);
1796 return innerOriginURI && innerTargetURI &&
1797 NS_SUCCEEDED(innerOriginURI->SchemeIs("file", &originIsFile)) &&
1798 NS_SUCCEEDED(innerTargetURI->SchemeIs("file", &targetIsFile)) &&
1799 originIsFile && targetIsFile;
1800 }
1802 NS_IMETHODIMP
1803 nsDocShell::GetEldestPresContext(nsPresContext** aPresContext)
1804 {
1805 NS_ENSURE_ARG_POINTER(aPresContext);
1806 *aPresContext = nullptr;
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 }
1818 return NS_OK;
1819 }
1821 NS_IMETHODIMP
1822 nsDocShell::GetPresContext(nsPresContext ** aPresContext)
1823 {
1824 NS_ENSURE_ARG_POINTER(aPresContext);
1825 *aPresContext = nullptr;
1827 if (!mContentViewer)
1828 return NS_OK;
1830 return mContentViewer->GetPresContext(aPresContext);
1831 }
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 }
1841 NS_IMETHODIMP
1842 nsDocShell::GetEldestPresShell(nsIPresShell** aPresShell)
1843 {
1844 nsresult rv = NS_OK;
1846 NS_ENSURE_ARG_POINTER(aPresShell);
1847 *aPresShell = nullptr;
1849 nsRefPtr<nsPresContext> presContext;
1850 (void) GetEldestPresContext(getter_AddRefs(presContext));
1852 if (presContext) {
1853 NS_IF_ADDREF(*aPresShell = presContext->GetPresShell());
1854 }
1856 return rv;
1857 }
1859 NS_IMETHODIMP
1860 nsDocShell::GetContentViewer(nsIContentViewer ** aContentViewer)
1861 {
1862 NS_ENSURE_ARG_POINTER(aContentViewer);
1864 *aContentViewer = mContentViewer;
1865 NS_IF_ADDREF(*aContentViewer);
1866 return NS_OK;
1867 }
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();
1876 if (mScriptGlobal) {
1877 mScriptGlobal->SetChromeEventHandler(mChromeEventHandler);
1878 }
1880 return NS_OK;
1881 }
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 }
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 }
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
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 }
1921 mCurrentURI = NS_TryToMakeImmutable(aURI);
1923 bool isRoot = false; // Is this the root docshell
1924 bool isSubFrame = false; // Is this a subframe navigation?
1926 nsCOMPtr<nsIDocShellTreeItem> root;
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 }
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 }
1947 if (aFireOnLocationChange) {
1948 FireOnLocationChange(this, aRequest, aURI, aLocationFlags);
1949 }
1950 return !aFireOnLocationChange;
1951 }
1953 NS_IMETHODIMP
1954 nsDocShell::GetCharset(nsACString& aCharset)
1955 {
1956 aCharset.Truncate();
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 }
1966 NS_IMETHODIMP
1967 nsDocShell::GatherCharsetMenuTelemetry()
1968 {
1969 nsCOMPtr<nsIContentViewer> viewer;
1970 GetContentViewer(getter_AddRefs(viewer));
1971 if (!viewer) {
1972 return NS_OK;
1973 }
1975 nsIDocument* doc = viewer->GetDocument();
1976 if (!doc || doc->WillIgnoreCharsetOverride()) {
1977 return NS_OK;
1978 }
1980 Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_USED, true);
1982 bool isFileURL = false;
1983 nsIURI* url = doc->GetOriginalURI();
1984 if (url) {
1985 url->SchemeIs("file", &isFileURL);
1986 }
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 }
2037 NS_IMETHODIMP
2038 nsDocShell::SetCharset(const nsACString& aCharset)
2039 {
2040 // set the charset override
2041 return SetForcedCharset(aCharset);
2042 }
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 }
2063 NS_IMETHODIMP nsDocShell::GetForcedCharset(nsACString& aResult)
2064 {
2065 aResult = mForcedCharset;
2066 return NS_OK;
2067 }
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 }
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 }
2089 NS_IMETHODIMP
2090 nsDocShell::GetChannelIsUnsafe(bool *aUnsafe)
2091 {
2092 *aUnsafe = false;
2094 nsIChannel* channel = GetCurrentDocChannel();
2095 if (!channel) {
2096 return NS_OK;
2097 }
2099 nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(channel);
2100 if (!jarChannel) {
2101 return NS_OK;
2102 }
2104 return jarChannel->GetIsUnsafe(aUnsafe);
2105 }
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 }
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 }
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 }
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 }
2139 NS_IMETHODIMP
2140 nsDocShell::GetAllowPlugins(bool * aAllowPlugins)
2141 {
2142 NS_ENSURE_ARG_POINTER(aAllowPlugins);
2144 *aAllowPlugins = mAllowPlugins;
2145 if (!mAllowPlugins) {
2146 return NS_OK;
2147 }
2149 bool unsafe;
2150 *aAllowPlugins = NS_SUCCEEDED(GetChannelIsUnsafe(&unsafe)) && !unsafe;
2151 return NS_OK;
2152 }
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 }
2162 NS_IMETHODIMP
2163 nsDocShell::GetAllowJavascript(bool * aAllowJavascript)
2164 {
2165 NS_ENSURE_ARG_POINTER(aAllowJavascript);
2167 *aAllowJavascript = mAllowJavascript;
2168 return NS_OK;
2169 }
2171 NS_IMETHODIMP
2172 nsDocShell::SetAllowJavascript(bool aAllowJavascript)
2173 {
2174 mAllowJavascript = aAllowJavascript;
2175 RecomputeCanExecuteScripts();
2176 return NS_OK;
2177 }
2179 NS_IMETHODIMP
2180 nsDocShell::GetUsePrivateBrowsing(bool* aUsePrivateBrowsing)
2181 {
2182 NS_ENSURE_ARG_POINTER(aUsePrivateBrowsing);
2184 *aUsePrivateBrowsing = mInPrivateBrowsing;
2185 return NS_OK;
2186 }
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);
2197 return SetPrivateBrowsing(aUsePrivateBrowsing);
2198 }
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 }
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 }
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 }
2238 NS_IMETHODIMP
2239 nsDocShell::GetUseRemoteTabs(bool* aUseRemoteTabs)
2240 {
2241 NS_ENSURE_ARG_POINTER(aUseRemoteTabs);
2243 *aUseRemoteTabs = mUseRemoteTabs;
2244 return NS_OK;
2245 }
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
2257 mUseRemoteTabs = aUseRemoteTabs;
2258 return NS_OK;
2259 }
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;
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 }
2284 NS_IMETHODIMP
2285 nsDocShell::GetAffectPrivateSessionLifetime(bool* aAffectLifetime)
2286 {
2287 *aAffectLifetime = mAffectPrivateSessionLifetime;
2288 return NS_OK;
2289 }
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 }
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 }
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 }
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 }
2338 NS_IMETHODIMP nsDocShell::GetAllowMetaRedirects(bool * aReturn)
2339 {
2340 NS_ENSURE_ARG_POINTER(aReturn);
2342 *aReturn = mAllowMetaRedirects;
2343 if (!mAllowMetaRedirects) {
2344 return NS_OK;
2345 }
2347 bool unsafe;
2348 *aReturn = NS_SUCCEEDED(GetChannelIsUnsafe(&unsafe)) && !unsafe;
2349 return NS_OK;
2350 }
2352 NS_IMETHODIMP nsDocShell::SetAllowMetaRedirects(bool aValue)
2353 {
2354 mAllowMetaRedirects = aValue;
2355 return NS_OK;
2356 }
2358 NS_IMETHODIMP nsDocShell::GetAllowSubframes(bool * aAllowSubframes)
2359 {
2360 NS_ENSURE_ARG_POINTER(aAllowSubframes);
2362 *aAllowSubframes = mAllowSubframes;
2363 return NS_OK;
2364 }
2366 NS_IMETHODIMP nsDocShell::SetAllowSubframes(bool aAllowSubframes)
2367 {
2368 mAllowSubframes = aAllowSubframes;
2369 return NS_OK;
2370 }
2372 NS_IMETHODIMP nsDocShell::GetAllowImages(bool * aAllowImages)
2373 {
2374 NS_ENSURE_ARG_POINTER(aAllowImages);
2376 *aAllowImages = mAllowImages;
2377 return NS_OK;
2378 }
2380 NS_IMETHODIMP nsDocShell::SetAllowImages(bool aAllowImages)
2381 {
2382 mAllowImages = aAllowImages;
2383 return NS_OK;
2384 }
2386 NS_IMETHODIMP nsDocShell::GetAllowMedia(bool * aAllowMedia)
2387 {
2388 *aAllowMedia = mAllowMedia;
2389 return NS_OK;
2390 }
2392 NS_IMETHODIMP nsDocShell::SetAllowMedia(bool aAllowMedia)
2393 {
2394 mAllowMedia = aAllowMedia;
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 }
2408 return NS_OK;
2409 }
2411 NS_IMETHODIMP nsDocShell::GetAllowDNSPrefetch(bool * aAllowDNSPrefetch)
2412 {
2413 *aAllowDNSPrefetch = mAllowDNSPrefetch;
2414 return NS_OK;
2415 }
2417 NS_IMETHODIMP nsDocShell::SetAllowDNSPrefetch(bool aAllowDNSPrefetch)
2418 {
2419 mAllowDNSPrefetch = aAllowDNSPrefetch;
2420 return NS_OK;
2421 }
2423 NS_IMETHODIMP nsDocShell::GetAllowWindowControl(bool * aAllowWindowControl)
2424 {
2425 *aAllowWindowControl = mAllowWindowControl;
2426 return NS_OK;
2427 }
2429 NS_IMETHODIMP nsDocShell::SetAllowWindowControl(bool aAllowWindowControl)
2430 {
2431 mAllowWindowControl = aAllowWindowControl;
2432 return NS_OK;
2433 }
2435 NS_IMETHODIMP
2436 nsDocShell::GetAllowContentRetargeting(bool* aAllowContentRetargeting)
2437 {
2438 *aAllowContentRetargeting = mAllowContentRetargeting;
2439 return NS_OK;
2440 }
2442 NS_IMETHODIMP
2443 nsDocShell::SetAllowContentRetargeting(bool aAllowContentRetargeting)
2444 {
2445 mAllowContentRetargeting = aAllowContentRetargeting;
2446 return NS_OK;
2447 }
2449 NS_IMETHODIMP
2450 nsDocShell::GetFullscreenAllowed(bool* aFullscreenAllowed)
2451 {
2452 NS_ENSURE_ARG_POINTER(aFullscreenAllowed);
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 }
2461 // Assume false until we determine otherwise...
2462 *aFullscreenAllowed = false;
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 }
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);
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);
2497 return parent->GetFullscreenAllowed(aFullscreenAllowed);
2498 }
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 }
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 }
2531 *aMayEnableCharacterEncodingMenu = true;
2532 return NS_OK;
2533 }
2535 NS_IMETHODIMP
2536 nsDocShell::GetDocShellEnumerator(int32_t aItemType, int32_t aDirection, nsISimpleEnumerator **outEnum)
2537 {
2538 NS_ENSURE_ARG_POINTER(outEnum);
2539 *outEnum = nullptr;
2541 nsRefPtr<nsDocShellEnumerator> docShellEnum;
2542 if (aDirection == ENUMERATE_FORWARDS)
2543 docShellEnum = new nsDocShellForwardsEnumerator;
2544 else
2545 docShellEnum = new nsDocShellBackwardsEnumerator;
2547 if (!docShellEnum) return NS_ERROR_OUT_OF_MEMORY;
2549 nsresult rv = docShellEnum->SetEnumDocShellType(aItemType);
2550 if (NS_FAILED(rv)) return rv;
2552 rv = docShellEnum->SetEnumerationRootItem((nsIDocShellTreeItem *)this);
2553 if (NS_FAILED(rv)) return rv;
2555 rv = docShellEnum->First();
2556 if (NS_FAILED(rv)) return rv;
2558 rv = docShellEnum->QueryInterface(NS_GET_IID(nsISimpleEnumerator), (void **)outEnum);
2560 return rv;
2561 }
2563 NS_IMETHODIMP
2564 nsDocShell::GetAppType(uint32_t * aAppType)
2565 {
2566 *aAppType = mAppType;
2567 return NS_OK;
2568 }
2570 NS_IMETHODIMP
2571 nsDocShell::SetAppType(uint32_t aAppType)
2572 {
2573 mAppType = aAppType;
2574 return NS_OK;
2575 }
2578 NS_IMETHODIMP
2579 nsDocShell::GetAllowAuth(bool * aAllowAuth)
2580 {
2581 *aAllowAuth = mAllowAuth;
2582 return NS_OK;
2583 }
2585 NS_IMETHODIMP
2586 nsDocShell::SetAllowAuth(bool aAllowAuth)
2587 {
2588 mAllowAuth = aAllowAuth;
2589 return NS_OK;
2590 }
2592 NS_IMETHODIMP
2593 nsDocShell::GetZoom(float *zoom)
2594 {
2595 NS_ENSURE_ARG_POINTER(zoom);
2596 *zoom = 1.0f;
2597 return NS_OK;
2598 }
2600 NS_IMETHODIMP
2601 nsDocShell::SetZoom(float zoom)
2602 {
2603 return NS_ERROR_NOT_IMPLEMENTED;
2604 }
2606 NS_IMETHODIMP
2607 nsDocShell::GetMarginWidth(int32_t * aWidth)
2608 {
2609 NS_ENSURE_ARG_POINTER(aWidth);
2611 *aWidth = mMarginWidth;
2612 return NS_OK;
2613 }
2615 NS_IMETHODIMP
2616 nsDocShell::SetMarginWidth(int32_t aWidth)
2617 {
2618 mMarginWidth = aWidth;
2619 return NS_OK;
2620 }
2622 NS_IMETHODIMP
2623 nsDocShell::GetMarginHeight(int32_t * aHeight)
2624 {
2625 NS_ENSURE_ARG_POINTER(aHeight);
2627 *aHeight = mMarginHeight;
2628 return NS_OK;
2629 }
2631 NS_IMETHODIMP
2632 nsDocShell::SetMarginHeight(int32_t aHeight)
2633 {
2634 mMarginHeight = aHeight;
2635 return NS_OK;
2636 }
2638 NS_IMETHODIMP
2639 nsDocShell::GetBusyFlags(uint32_t * aBusyFlags)
2640 {
2641 NS_ENSURE_ARG_POINTER(aBusyFlags);
2643 *aBusyFlags = mBusyFlags;
2644 return NS_OK;
2645 }
2647 NS_IMETHODIMP
2648 nsDocShell::TabToTreeOwner(bool aForward, bool* aTookFocus)
2649 {
2650 NS_ENSURE_ARG_POINTER(aTookFocus);
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;
2661 return NS_OK;
2662 }
2664 NS_IMETHODIMP
2665 nsDocShell::GetSecurityUI(nsISecureBrowserUI **aSecurityUI)
2666 {
2667 NS_IF_ADDREF(*aSecurityUI = mSecurityUI);
2668 return NS_OK;
2669 }
2671 NS_IMETHODIMP
2672 nsDocShell::SetSecurityUI(nsISecureBrowserUI *aSecurityUI)
2673 {
2674 mSecurityUI = aSecurityUI;
2675 mSecurityUI->SetDocShell(this);
2676 return NS_OK;
2677 }
2679 NS_IMETHODIMP
2680 nsDocShell::GetUseErrorPages(bool *aUseErrorPages)
2681 {
2682 *aUseErrorPages = UseErrorPages();
2683 return NS_OK;
2684 }
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 }
2697 NS_IMETHODIMP
2698 nsDocShell::GetPreviousTransIndex(int32_t *aPreviousTransIndex)
2699 {
2700 *aPreviousTransIndex = mPreviousTransIndex;
2701 return NS_OK;
2702 }
2704 NS_IMETHODIMP
2705 nsDocShell::GetLoadedTransIndex(int32_t *aLoadedTransIndex)
2706 {
2707 *aLoadedTransIndex = mLoadedTransIndex;
2708 return NS_OK;
2709 }
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);
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 }
2730 return NS_OK;
2731 }
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 }
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 }
2761 return NS_OK;
2762 }
2764 nsIDOMStorageManager*
2765 nsDocShell::TopSessionStorageManager()
2766 {
2767 nsresult rv;
2769 nsCOMPtr<nsIDocShellTreeItem> topItem;
2770 rv = GetSameTypeRootTreeItem(getter_AddRefs(topItem));
2771 if (NS_FAILED(rv)) {
2772 return nullptr;
2773 }
2775 if (!topItem) {
2776 return nullptr;
2777 }
2779 nsDocShell* topDocShell = static_cast<nsDocShell*>(topItem.get());
2780 if (topDocShell != this) {
2781 return topDocShell->TopSessionStorageManager();
2782 }
2784 if (!mSessionStorageManager) {
2785 mSessionStorageManager =
2786 do_CreateInstance("@mozilla.org/dom/sessionStorage-manager;1");
2787 }
2789 return mSessionStorageManager;
2790 }
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 }
2803 nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
2804 do_GetService(THIRDPARTYUTIL_CONTRACTID);
2805 if (!thirdPartyUtil)
2806 return NS_ERROR_FAILURE;
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);
2814 if (aCreate) {
2815 return manager->CreateStorageForFirstParty(firstPartyIsolationURI,
2816 aPrincipal, aDocumentURI,
2817 mInPrivateBrowsing, aStorage);
2818 }
2820 return manager->GetStorageForFirstParty(firstPartyIsolationURI, aPrincipal,
2821 mInPrivateBrowsing, aStorage);
2822 }
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 }
2837 nsCOMPtr<nsIDOMStorageManager> manager = TopSessionStorageManager();
2838 if (!manager) {
2839 return NS_ERROR_UNEXPECTED;
2840 }
2842 return manager->CloneStorage(aStorage);
2843 }
2845 NS_IMETHODIMP
2846 nsDocShell::GetCurrentDocumentChannel(nsIChannel** aResult)
2847 {
2848 NS_IF_ADDREF(*aResult = GetCurrentDocChannel());
2849 return NS_OK;
2850 }
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 }
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 }
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 }
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 }
2897 //*****************************************************************************
2898 // nsDocShell::nsIDocShellTreeItem
2899 //*****************************************************************************
2901 NS_IMETHODIMP
2902 nsDocShell::GetName(nsAString& aName)
2903 {
2904 aName = mName;
2905 return NS_OK;
2906 }
2908 NS_IMETHODIMP
2909 nsDocShell::SetName(const nsAString& aName)
2910 {
2911 mName = aName;
2912 return NS_OK;
2913 }
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 }
2924 /* virtual */ int32_t
2925 nsDocShell::ItemType()
2926 {
2927 return mItemType;
2928 }
2930 NS_IMETHODIMP
2931 nsDocShell::GetItemType(int32_t * aItemType)
2932 {
2933 NS_ENSURE_ARG_POINTER(aItemType);
2935 *aItemType = ItemType();
2936 return NS_OK;
2937 }
2939 NS_IMETHODIMP
2940 nsDocShell::SetItemType(int32_t aItemType)
2941 {
2942 NS_ENSURE_ARG((aItemType == typeChrome) || (typeContent == aItemType));
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);
2950 NS_ENSURE_STATE(!mParent || mParent == docLoaderService);
2952 mItemType = aItemType;
2954 // disable auth prompting for anything but content
2955 mAllowAuth = mItemType == typeContent;
2957 nsRefPtr<nsPresContext> presContext = nullptr;
2958 GetPresContext(getter_AddRefs(presContext));
2959 if (presContext) {
2960 presContext->UpdateIsChrome();
2961 }
2963 return NS_OK;
2964 }
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 }
2979 already_AddRefed<nsDocShell>
2980 nsDocShell::GetParentDocshell()
2981 {
2982 nsCOMPtr<nsIDocShell> docshell = do_QueryInterface(GetAsSupports(mParent));
2983 return docshell.forget().downcast<nsDocShell>();
2984 }
2986 void
2987 nsDocShell::RecomputeCanExecuteScripts()
2988 {
2989 bool old = mCanExecuteScripts;
2990 nsRefPtr<nsDocShell> parent = GetParentDocshell();
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 }
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 }
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 }
3033 nsresult
3034 nsDocShell::SetDocLoaderParent(nsDocLoader * aParent)
3035 {
3036 bool wasFrame = IsFrame();
3038 nsDocLoader::SetDocLoaderParent(aParent);
3040 nsCOMPtr<nsISupportsPriority> priorityGroup = do_QueryInterface(mLoadGroup);
3041 if (wasFrame != IsFrame() && priorityGroup) {
3042 priorityGroup->AdjustPriority(wasFrame ? -1 : 1);
3043 }
3045 // Curse ambiguous nsISupports inheritance!
3046 nsISupports* parent = GetAsSupports(aParent);
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 }
3097 }
3099 nsCOMPtr<nsILoadContext> parentAsLoadContext(do_QueryInterface(parent));
3100 if (parentAsLoadContext &&
3101 NS_SUCCEEDED(parentAsLoadContext->GetUsePrivateBrowsing(&value)))
3102 {
3103 SetPrivateBrowsing(value);
3104 }
3106 nsCOMPtr<nsIURIContentListener> parentURIListener(do_GetInterface(parent));
3107 if (parentURIListener)
3108 mContentListener->SetParentContentListener(parentURIListener);
3110 // Our parent has changed. Recompute scriptability.
3111 RecomputeCanExecuteScripts();
3113 return NS_OK;
3114 }
3116 NS_IMETHODIMP
3117 nsDocShell::GetSameTypeParent(nsIDocShellTreeItem ** aParent)
3118 {
3119 NS_ENSURE_ARG_POINTER(aParent);
3120 *aParent = nullptr;
3122 if (nsIDocShell::GetIsBrowserOrApp()) {
3123 return NS_OK;
3124 }
3126 nsCOMPtr<nsIDocShellTreeItem> parent =
3127 do_QueryInterface(GetAsSupports(mParent));
3128 if (!parent)
3129 return NS_OK;
3131 if (parent->ItemType() == mItemType) {
3132 parent.swap(*aParent);
3133 }
3134 return NS_OK;
3135 }
3137 NS_IMETHODIMP
3138 nsDocShell::GetSameTypeParentIgnoreBrowserAndAppBoundaries(nsIDocShell** aParent)
3139 {
3140 NS_ENSURE_ARG_POINTER(aParent);
3141 *aParent = nullptr;
3143 nsCOMPtr<nsIDocShellTreeItem> parent =
3144 do_QueryInterface(GetAsSupports(mParent));
3145 if (!parent)
3146 return NS_OK;
3148 if (parent->ItemType() == mItemType) {
3149 nsCOMPtr<nsIDocShell> parentDS = do_QueryInterface(parent);
3150 parentDS.forget(aParent);
3151 }
3152 return NS_OK;
3153 }
3155 NS_IMETHODIMP
3156 nsDocShell::GetRootTreeItem(nsIDocShellTreeItem ** aRootTreeItem)
3157 {
3158 NS_ENSURE_ARG_POINTER(aRootTreeItem);
3159 *aRootTreeItem = static_cast<nsIDocShellTreeItem *>(this);
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 }
3172 NS_IMETHODIMP
3173 nsDocShell::GetSameTypeRootTreeItem(nsIDocShellTreeItem ** aRootTreeItem)
3174 {
3175 NS_ENSURE_ARG_POINTER(aRootTreeItem);
3176 *aRootTreeItem = static_cast<nsIDocShellTreeItem *>(this);
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 }
3191 /* static */
3192 bool
3193 nsDocShell::CanAccessItem(nsIDocShellTreeItem* aTargetItem,
3194 nsIDocShellTreeItem* aAccessingItem,
3195 bool aConsiderOpener)
3196 {
3197 NS_PRECONDITION(aTargetItem, "Must have target item!");
3199 if (!gValidateOrigin || !aAccessingItem) {
3200 // Good to go
3201 return true;
3202 }
3204 // XXXbz should we care if aAccessingItem or the document therein is
3205 // chrome? Should those get extra privileges?
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
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).
3226 if (aTargetItem == aAccessingItem) {
3227 // A frame is allowed to navigate itself.
3228 return true;
3229 }
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 }
3238 if (targetDS && accessingDS &&
3239 (targetDS->GetIsInBrowserElement() !=
3240 accessingDS->GetIsInBrowserElement() ||
3241 targetDS->GetAppId() != accessingDS->GetAppId())) {
3242 return false;
3243 }
3245 nsCOMPtr<nsIDocShellTreeItem> accessingRoot;
3246 aAccessingItem->GetSameTypeRootTreeItem(getter_AddRefs(accessingRoot));
3248 if (aTargetItem == accessingRoot) {
3249 // A frame can navigate its root.
3250 return true;
3251 }
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 }
3260 nsCOMPtr<nsIDocShellTreeItem> parent;
3261 target->GetSameTypeParent(getter_AddRefs(parent));
3262 parent.swap(target);
3263 } while (target);
3265 nsCOMPtr<nsIDocShellTreeItem> targetRoot;
3266 aTargetItem->GetSameTypeRootTreeItem(getter_AddRefs(targetRoot));
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 }
3275 if (!aConsiderOpener) {
3276 // All done here
3277 return false;
3278 }
3280 nsCOMPtr<nsIDOMWindow> targetWindow = do_GetInterface(aTargetItem);
3281 if (!targetWindow) {
3282 NS_ERROR("This should not happen, really");
3283 return false;
3284 }
3286 nsCOMPtr<nsIDOMWindow> targetOpener;
3287 targetWindow->GetOpener(getter_AddRefs(targetOpener));
3288 nsCOMPtr<nsIWebNavigation> openerWebNav(do_GetInterface(targetOpener));
3289 nsCOMPtr<nsIDocShellTreeItem> openerItem(do_QueryInterface(openerWebNav));
3291 if (!openerItem) {
3292 return false;
3293 }
3295 return CanAccessItem(openerItem, aAccessingItem, false);
3296 }
3298 static bool
3299 ItemIsActive(nsIDocShellTreeItem *aItem)
3300 {
3301 nsCOMPtr<nsIDOMWindow> window(do_GetInterface(aItem));
3303 if (window) {
3304 bool isClosed;
3306 if (NS_SUCCEEDED(window->GetClosed(&isClosed)) && !isClosed) {
3307 return true;
3308 }
3309 }
3311 return false;
3312 }
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);
3323 // If we don't find one, we return NS_OK and a null result
3324 *_retval = nullptr;
3326 if (!*aName)
3327 return NS_OK;
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 {
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.
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 }
3395 if (foundItem && !CanAccessItem(foundItem, aOriginalRequestor)) {
3396 foundItem = nullptr;
3397 }
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 }
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 }
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));
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;
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;
3448 if (parentAsTreeItem->ItemType() == mItemType) {
3449 return parentAsTreeItem->
3450 FindItemWithName(aName,
3451 static_cast<nsIDocShellTreeItem*>
3452 (this),
3453 aOriginalRequestor,
3454 _retval);
3455 }
3456 }
3458 // If the parent is null or not of the same type fall through and ask tree
3459 // owner.
3461 // This may fail, but comparing against null serves the same purpose
3462 nsCOMPtr<nsIDocShellTreeOwner>
3463 reqAsTreeOwner(do_QueryInterface(aRequestor));
3465 if (mTreeOwner && mTreeOwner != reqAsTreeOwner) {
3466 return mTreeOwner->
3467 FindItemWithName(aName, this, aOriginalRequestor, _retval);
3468 }
3470 return NS_OK;
3471 }
3473 bool
3474 nsDocShell::IsSandboxedFrom(nsIDocShell* aTargetDocShell)
3475 {
3476 // If no target then not sandboxed.
3477 if (!aTargetDocShell) {
3478 return false;
3479 }
3481 // We cannot be sandboxed from ourselves.
3482 if (aTargetDocShell == this) {
3483 return false;
3484 }
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 }
3496 // If no flags, we are not sandboxed at all.
3497 if (!sandboxFlags) {
3498 return false;
3499 }
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);
3515 // Otherwise, we are sandboxed from aTargetDocShell.
3516 return true;
3517 }
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 }
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 }
3538 // Otherwise, we are sandboxed from aTargetDocShell.
3539 return true;
3540 }
3542 NS_IMETHODIMP
3543 nsDocShell::GetTreeOwner(nsIDocShellTreeOwner ** aTreeOwner)
3544 {
3545 NS_ENSURE_ARG_POINTER(aTreeOwner);
3547 *aTreeOwner = mTreeOwner;
3548 NS_IF_ADDREF(*aTreeOwner);
3549 return NS_OK;
3550 }
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(" ");
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();
3567 nsCOMPtr<nsIDOMWindow> domwin(doc->GetWindow());
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();
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);
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 }
3591 static void
3592 PrintDocTree(nsIDocShellTreeItem * aParentNode)
3593 {
3594 NS_ASSERTION(aParentNode, "Pointer is null!");
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 }
3607 if (!parentItem) {
3608 parentItem = aParentNode;
3609 }
3611 PrintDocTree(parentItem, 0);
3612 }
3613 #endif
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
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));
3630 if (webProgress) {
3631 nsCOMPtr<nsIWebProgressListener>
3632 oldListener(do_QueryInterface(mTreeOwner));
3633 nsCOMPtr<nsIWebProgressListener>
3634 newListener(do_QueryInterface(aTreeOwner));
3636 if (oldListener) {
3637 webProgress->RemoveProgressListener(oldListener);
3638 }
3640 if (newListener) {
3641 webProgress->AddProgressListener(newListener,
3642 nsIWebProgress::NOTIFY_ALL);
3643 }
3644 }
3645 }
3647 mTreeOwner = aTreeOwner; // Weak reference per API
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);
3654 if (child->ItemType() == mItemType)
3655 child->SetTreeOwner(aTreeOwner);
3656 }
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();
3668 return NS_OK;
3669 }
3671 NS_IMETHODIMP
3672 nsDocShell::SetChildOffset(uint32_t aChildOffset)
3673 {
3674 mChildOffset = aChildOffset;
3675 return NS_OK;
3676 }
3678 NS_IMETHODIMP
3679 nsDocShell::GetHistoryID(uint64_t* aID)
3680 {
3681 *aID = mHistoryID;
3682 return NS_OK;
3683 }
3685 NS_IMETHODIMP
3686 nsDocShell::GetIsInUnload(bool* aIsInUnload)
3687 {
3688 *aIsInUnload = mFiredUnloadEvent;
3689 return NS_OK;
3690 }
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 }
3702 NS_IMETHODIMP
3703 nsDocShell::AddChild(nsIDocShellTreeItem * aChild)
3704 {
3705 NS_ENSURE_ARG_POINTER(aChild);
3707 nsRefPtr<nsDocLoader> childAsDocLoader = GetAsDocLoader(aChild);
3708 NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED);
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);
3719 // Make sure to remove the child from its current parent.
3720 nsDocLoader* childsParent = childAsDocLoader->GetParent();
3721 if (childsParent) {
3722 childsParent->RemoveChildLoader(childAsDocLoader);
3723 }
3725 // Make sure to clear the treeowner in case this child is a different type
3726 // from us.
3727 aChild->SetTreeOwner(nullptr);
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");
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);
3747 /* Set the child's global history if the parent has one */
3748 if (mUseGlobalHistory) {
3749 childDocShell->SetUseGlobalHistory(true);
3750 }
3752 if (aChild->ItemType() != mItemType) {
3753 return NS_OK;
3754 }
3756 aChild->SetTreeOwner(mTreeOwner);
3758 nsCOMPtr<nsIDocShell> childAsDocShell(do_QueryInterface(aChild));
3759 if (!childAsDocShell)
3760 return NS_OK;
3762 // charset, style-disabling, and zoom will be inherited in SetupNewViewer()
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.
3772 // we are NOT going to propagate the charset is this Chrome's docshell
3773 if (mItemType == nsIDocShellTreeItem::typeChrome)
3774 return NS_OK;
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;
3783 bool isWyciwyg = false;
3785 if (mCurrentURI) {
3786 // Check if the url is wyciwyg
3787 mCurrentURI->SchemeIs("wyciwyg", &isWyciwyg);
3788 }
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.
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 }
3804 // printf("### 1 >>> Adding child. Parent CS = %s. ItemType = %d.\n", NS_LossyConvertUTF16toASCII(parentCS).get(), mItemType);
3806 return NS_OK;
3807 }
3809 NS_IMETHODIMP
3810 nsDocShell::RemoveChild(nsIDocShellTreeItem * aChild)
3811 {
3812 NS_ENSURE_ARG_POINTER(aChild);
3814 nsRefPtr<nsDocLoader> childAsDocLoader = GetAsDocLoader(aChild);
3815 NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED);
3817 nsresult rv = RemoveChildLoader(childAsDocLoader);
3818 NS_ENSURE_SUCCESS(rv, rv);
3820 aChild->SetTreeOwner(nullptr);
3822 return nsDocLoader::AddDocLoaderAsChildOfRoot(childAsDocLoader);
3823 }
3825 NS_IMETHODIMP
3826 nsDocShell::GetChildAt(int32_t aIndex, nsIDocShellTreeItem ** aChild)
3827 {
3828 NS_ENSURE_ARG_POINTER(aChild);
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
3838 nsIDocumentLoader* child = ChildAt(aIndex);
3839 NS_ENSURE_TRUE(child, NS_ERROR_UNEXPECTED);
3841 return CallQueryInterface(child, aChild);
3842 }
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);
3854 *_retval = nullptr; // if we don't find one, we return NS_OK and a null result
3856 if (!*aName)
3857 return NS_OK;
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();
3866 if (aSameType && (childType != mItemType))
3867 continue;
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 }
3877 if (childType != mItemType) //Only ask it to check children if it is same type
3878 continue;
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 }
3901 NS_IMETHODIMP
3902 nsDocShell::GetChildSHEntry(int32_t aChildOffset, nsISHEntry ** aResult)
3903 {
3904 nsresult rv = NS_OK;
3906 NS_ENSURE_ARG_POINTER(aResult);
3907 *aResult = nullptr;
3910 // A nsISHEntry for a child is *only* available when the parent is in
3911 // the progress of loading a document too...
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);
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;
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 }
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 }
3955 NS_IMETHODIMP
3956 nsDocShell::AddChildSHEntry(nsISHEntry * aCloneRef, nsISHEntry * aNewEntry,
3957 int32_t aChildOffset, uint32_t loadType,
3958 bool aCloneChildren)
3959 {
3960 nsresult rv;
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;
3992 rv = mSessionHistory->GetEntryAtIndex(index, false,
3993 getter_AddRefs(currentHE));
3994 NS_ENSURE_TRUE(currentHE, NS_ERROR_FAILURE);
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));
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 }
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 */
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 }
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 }
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 }
4060 return rv;
4061 }
4063 NS_IMETHODIMP
4064 nsDocShell::SetUseGlobalHistory(bool aUseGlobalHistory)
4065 {
4066 nsresult rv;
4068 mUseGlobalHistory = aUseGlobalHistory;
4070 if (!aUseGlobalHistory) {
4071 mGlobalHistory = nullptr;
4072 return NS_OK;
4073 }
4075 // No need to initialize mGlobalHistory if IHistory is available.
4076 nsCOMPtr<IHistory> history = services::GetHistoryService();
4077 if (history) {
4078 return NS_OK;
4079 }
4081 if (mGlobalHistory) {
4082 return NS_OK;
4083 }
4085 mGlobalHistory = do_GetService(NS_GLOBALHISTORY2_CONTRACTID, &rv);
4086 return rv;
4087 }
4089 NS_IMETHODIMP
4090 nsDocShell::GetUseGlobalHistory(bool *aUseGlobalHistory)
4091 {
4092 *aUseGlobalHistory = mUseGlobalHistory;
4093 return NS_OK;
4094 }
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 }
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 }
4123 NS_IMETHODIMP
4124 nsDocShell::SetCreatedDynamically(bool aDynamic)
4125 {
4126 mDynamicallyCreated = aDynamic;
4127 return NS_OK;
4128 }
4130 NS_IMETHODIMP
4131 nsDocShell::GetCreatedDynamically(bool* aDynamic)
4132 {
4133 *aDynamic = mDynamicallyCreated;
4134 return NS_OK;
4135 }
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 }
4151 nsIScriptGlobalObject*
4152 nsDocShell::GetScriptGlobalObject()
4153 {
4154 NS_ENSURE_SUCCESS(EnsureScriptEnvironment(), nullptr);
4155 return mScriptGlobal;
4156 }
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 }
4172 NS_IMETHODIMP
4173 nsDocShell::GetDeviceSizeIsPageSize(bool* aValue)
4174 {
4175 *aValue = mDeviceSizeIsPageSize;
4176 return NS_OK;
4177 }
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 }
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 }
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 }
4217 return mIsPrintingOrPP;
4218 }
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 }
4235 //*****************************************************************************
4236 // nsDocShell::nsIWebNavigation
4237 //*****************************************************************************
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;
4254 }
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;
4271 }
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;
4287 }
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;
4303 }
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;
4318 }
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 }
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");
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;
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.
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);
4359 rv = NS_NewURI(getter_AddRefs(uri), uriString);
4360 if (uri) {
4361 aLoadFlags &= ~LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
4362 }
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
4390 if (NS_ERROR_MALFORMED_URI == rv) {
4391 DisplayLoadError(rv, uri, aURI, nullptr);
4392 }
4394 if (NS_FAILED(rv) || !uri)
4395 return NS_ERROR_FAILURE;
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);
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;
4412 nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
4413 rv = CreateLoadInfo(getter_AddRefs(loadInfo));
4414 if (NS_FAILED(rv)) return rv;
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 }
4427 loadInfo->SetLoadType(ConvertLoadTypeToDocShellLoadInfo(loadType));
4428 loadInfo->SetPostDataStream(postStream);
4429 loadInfo->SetReferrer(aReferringURI);
4430 loadInfo->SetHeadersStream(aHeaderStream);
4431 loadInfo->SetBaseURI(aBaseURI);
4433 rv = LoadURI(uri, loadInfo, extraFlags, true);
4435 // Save URI string in case it's needed later when
4436 // sending to search engine service in EndPageLoad()
4437 mOriginalUriString = uriString;
4439 return rv;
4440 }
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));
4453 NS_ENSURE_TRUE(stringBundle, NS_ERROR_FAILURE);
4454 NS_ENSURE_TRUE(prompter, NS_ERROR_FAILURE);
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;
4466 errorPage.AssignLiteral("neterror");
4468 // Turn the error code into a human readable error message.
4469 if (NS_ERROR_UNKNOWN_PROTOCOL == aError) {
4470 NS_ENSURE_ARG_POINTER(aURI);
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);
4533 uint32_t errorClass;
4534 if (!nsserr ||
4535 NS_FAILED(nsserr->GetErrorClass(aError, &errorClass))) {
4536 errorClass = nsINSSErrorsService::ERROR_CLASS_SSL_PROTOCOL;
4537 }
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");
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;
4566 bool isStsHost = false;
4567 rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS,
4568 aURI, flags, &isStsHost);
4569 NS_ENSURE_SUCCESS(rv, rv);
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 }
4582 if (Preferences::GetBool(
4583 "browser.xul.error_pages.expert_bad_cert", false)) {
4584 cssClass.AssignLiteral("expertBadCert");
4585 }
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);
4594 if (!IsFrame() && errorPage.EqualsIgnoreCase("certerror"))
4595 mozilla::Telemetry::Accumulate(mozilla::Telemetry::SECURITY_UI, bucketId);
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;
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);
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 }
4625 if (errorPage.EqualsIgnoreCase("blocked"))
4626 mozilla::Telemetry::Accumulate(mozilla::Telemetry::SECURITY_UI,
4627 bucketId);
4629 cssClass.AssignLiteral("blacklist");
4630 } else if (NS_ERROR_CONTENT_CRASHED == aError) {
4631 errorPage.AssignLiteral("tabcrashed");
4632 error.AssignLiteral("tabcrashed");
4634 nsCOMPtr<EventTarget> handler = mChromeEventHandler;
4635 if (handler) {
4636 nsCOMPtr<Element> element = do_QueryInterface(handler);
4637 element->GetAttribute(NS_LITERAL_STRING("crashedPageTitle"), messageStr);
4638 }
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 }
4715 // Test if the error should be displayed
4716 if (error.IsEmpty()) {
4717 return NS_OK;
4718 }
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 }
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);
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;
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 }
4776 // Display the error as a page or an alert prompt
4777 NS_ENSURE_FALSE(messageStr.IsEmpty(), NS_ERROR_FAILURE);
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 }
4793 // Display a message box
4794 prompter->Alert(nullptr, messageStr.get());
4795 }
4797 return NS_OK;
4798 }
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);
4814 nsAutoCString chanName;
4815 if (aFailedChannel)
4816 aFailedChannel->GetName(chanName);
4817 else
4818 chanName.AssignLiteral("<no channel>");
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;
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 }
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 }
4854 // Create a URL to pass all the error information through to the page.
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=");
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());
4889 nsAutoCString frameType(FrameTypeToString(mFrameType));
4890 errorPageUrl.AppendLiteral("&f=");
4891 errorPageUrl.AppendASCII(frameType.get());
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 }
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());
4910 nsCOMPtr<nsIURI> errorPageURI;
4911 rv = NS_NewURI(getter_AddRefs(errorPageURI), errorPageUrl);
4912 NS_ENSURE_SUCCESS(rv, rv);
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 }
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");
4934 uint32_t loadType = MAKE_LOAD_TYPE(LOAD_RELOAD_NORMAL, aReloadFlags);
4935 NS_ENSURE_TRUE(IsValidLoadType(loadType), NS_ERROR_INVALID_ARG);
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 }
4946 if (!canReload)
4947 return NS_OK;
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)));
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);
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 }
4994 return rv;
4995 }
4997 NS_IMETHODIMP
4998 nsDocShell::Stop(uint32_t aStopFlags)
4999 {
5000 // Revoke any pending event related to content viewer restoration
5001 mRestorePresentationEvent.Revoke();
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 }
5010 mFailedChannel = nullptr;
5011 mFailedURI = nullptr;
5012 }
5014 if (nsIWebNavigation::STOP_CONTENT & aStopFlags) {
5015 // Stop the document loading
5016 if (mContentViewer) {
5017 nsCOMPtr<nsIContentViewer> cv = mContentViewer;
5018 cv->Stop();
5019 }
5020 }
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 }
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 }
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 }
5044 return NS_OK;
5045 }
5047 NS_IMETHODIMP
5048 nsDocShell::GetDocument(nsIDOMDocument ** aDocument)
5049 {
5050 NS_ENSURE_ARG_POINTER(aDocument);
5051 NS_ENSURE_SUCCESS(EnsureContentViewer(), NS_ERROR_FAILURE);
5053 return mContentViewer->GetDOMDocument(aDocument);
5054 }
5056 NS_IMETHODIMP
5057 nsDocShell::GetCurrentURI(nsIURI ** aURI)
5058 {
5059 NS_ENSURE_ARG_POINTER(aURI);
5061 if (mCurrentURI) {
5062 return NS_EnsureSafeToReturn(mCurrentURI, aURI);
5063 }
5065 *aURI = nullptr;
5066 return NS_OK;
5067 }
5069 NS_IMETHODIMP
5070 nsDocShell::GetReferringURI(nsIURI ** aURI)
5071 {
5072 NS_ENSURE_ARG_POINTER(aURI);
5074 *aURI = mReferrerURI;
5075 NS_IF_ADDREF(*aURI);
5077 return NS_OK;
5078 }
5080 NS_IMETHODIMP
5081 nsDocShell::SetSessionHistory(nsISHistory * aSessionHistory)
5082 {
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.
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;
5105 }
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 }
5117 //*****************************************************************************
5118 // nsDocShell::nsIWebPageDescriptor
5119 //*****************************************************************************
5120 NS_IMETHODIMP
5121 nsDocShell::LoadPage(nsISupports *aPageDescriptor, uint32_t aDisplayType)
5122 {
5123 nsCOMPtr<nsISHEntry> shEntryIn(do_QueryInterface(aPageDescriptor));
5125 // Currently, the opaque 'page descriptor' is an nsISHEntry...
5126 if (!shEntryIn) {
5127 return NS_ERROR_INVALID_POINTER;
5128 }
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);
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);
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;
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;
5154 oldUri->GetSpec(spec);
5155 newSpec.AppendLiteral("view-source:");
5156 newSpec.Append(spec);
5158 rv = NS_NewURI(getter_AddRefs(newUri), newSpec);
5159 if (NS_FAILED(rv)) {
5160 return rv;
5161 }
5162 shEntry->SetURI(newUri);
5163 }
5165 rv = LoadHistoryEntry(shEntry, LOAD_HISTORY);
5166 return rv;
5167 }
5169 NS_IMETHODIMP
5170 nsDocShell::GetCurrentDescriptor(nsISupports **aPageDescriptor)
5171 {
5172 NS_PRECONDITION(aPageDescriptor, "Null out param?");
5174 *aPageDescriptor = nullptr;
5176 nsISHEntry* src = mOSHE ? mOSHE : mLSHE;
5177 if (src) {
5178 nsCOMPtr<nsISHEntry> dest;
5180 nsresult rv = src->Clone(getter_AddRefs(dest));
5181 if (NS_FAILED(rv)) {
5182 return rv;
5183 }
5185 // null out inappropriate cloned attributes...
5186 dest->SetParent(nullptr);
5187 dest->SetIsSubFrame(false);
5189 return CallQueryInterface(dest, aPageDescriptor);
5190 }
5192 return NS_ERROR_NOT_AVAILABLE;
5193 }
5196 //*****************************************************************************
5197 // nsDocShell::nsIBaseWindow
5198 //*****************************************************************************
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);
5208 return NS_OK;
5209 }
5211 NS_IMETHODIMP
5212 nsDocShell::Create()
5213 {
5214 if (mCreated) {
5215 // We've already been created
5216 return NS_OK;
5217 }
5219 NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome,
5220 "Unexpected item type in docshell");
5222 NS_ENSURE_TRUE(Preferences::GetRootBranch(), NS_ERROR_FAILURE);
5223 mCreated = true;
5225 mAllowSubframes =
5226 Preferences::GetBool("browser.frames.enabled", mAllowSubframes);
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 }
5234 // Should we use XUL error pages instead of alerts if possible?
5235 mUseErrorPages =
5236 Preferences::GetBool("browser.xul.error_pages.enabled", mUseErrorPages);
5238 if(!gAddedPreferencesVarCache) {
5239 Preferences::AddBoolVarCache(&sUseErrorPages,
5240 "browser.xul.error_pages.enabled",
5241 mUseErrorPages);
5242 gAddedPreferencesVarCache = true;
5243 }
5245 mDeviceSizeIsPageSize =
5246 Preferences::GetBool("docshell.device_size_is_page_size",
5247 mDeviceSizeIsPageSize);
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 }
5256 return NS_OK;
5257 }
5259 NS_IMETHODIMP
5260 nsDocShell::Destroy()
5261 {
5262 NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome,
5263 "Unexpected item type in docshell");
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 }
5274 mIsBeingDestroyed = true;
5276 // Remove our pref observers
5277 if (mObserveErrorPages) {
5278 mObserveErrorPages = false;
5279 }
5281 // Make sure to blow away our mLoadingURI just in case. No loads
5282 // from inside this pagehide.
5283 mLoadingURI = nullptr;
5285 // Fire unload event before we blow anything away.
5286 (void) FirePageHideNotification(true);
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);
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 }
5307 // Stop any URLs that are currently being loaded...
5308 Stop(nsIWebNavigation::STOP_ALL);
5310 mEditorData = nullptr;
5312 mTransferableHookData = nullptr;
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();
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);
5325 if (mContentViewer) {
5326 mContentViewer->Close(nullptr);
5327 mContentViewer->Destroy();
5328 mContentViewer = nullptr;
5329 }
5331 nsDocLoader::Destroy();
5333 mParentWidget = nullptr;
5334 mCurrentURI = nullptr;
5336 if (mScriptGlobal) {
5337 mScriptGlobal->DetachFromDocShell();
5338 mScriptGlobal = nullptr;
5339 }
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 }
5353 SetTreeOwner(nullptr);
5355 mOnePermittedSandboxedNavigator = nullptr;
5357 // required to break ref cycle
5358 mSecurityUI = nullptr;
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();
5364 if (mInPrivateBrowsing) {
5365 mInPrivateBrowsing = false;
5366 if (mAffectPrivateSessionLifetime) {
5367 DecreasePrivateDocShellCount();
5368 }
5369 }
5371 return NS_OK;
5372 }
5374 NS_IMETHODIMP
5375 nsDocShell::GetUnscaledDevicePixelsPerCSSPixel(double *aScale)
5376 {
5377 if (mParentWidget) {
5378 *aScale = mParentWidget->GetDefaultScale().scale;
5379 return NS_OK;
5380 }
5382 nsCOMPtr<nsIBaseWindow> ownerWindow(do_QueryInterface(mTreeOwner));
5383 if (ownerWindow) {
5384 return ownerWindow->GetUnscaledDevicePixelsPerCSSPixel(aScale);
5385 }
5387 *aScale = 1.0;
5388 return NS_OK;
5389 }
5391 NS_IMETHODIMP
5392 nsDocShell::SetPosition(int32_t x, int32_t y)
5393 {
5394 mBounds.x = x;
5395 mBounds.y = y;
5397 if (mContentViewer)
5398 NS_ENSURE_SUCCESS(mContentViewer->Move(x, y), NS_ERROR_FAILURE);
5400 return NS_OK;
5401 }
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 }
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 }
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 }
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;
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 }
5441 return NS_OK;
5442 }
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 }
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 }
5466 DoGetPositionAndSize(x, y, cx, cy);
5467 return NS_OK;
5468 }
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 }
5484 NS_IMETHODIMP
5485 nsDocShell::Repaint(bool aForce)
5486 {
5487 nsCOMPtr<nsIPresShell> presShell =GetPresShell();
5488 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
5490 nsViewManager* viewManager = presShell->GetViewManager();
5491 NS_ENSURE_TRUE(viewManager, NS_ERROR_FAILURE);
5493 viewManager->InvalidateAllViews();
5494 return NS_OK;
5495 }
5497 NS_IMETHODIMP
5498 nsDocShell::GetParentWidget(nsIWidget ** parentWidget)
5499 {
5500 NS_ENSURE_ARG_POINTER(parentWidget);
5502 *parentWidget = mParentWidget;
5503 NS_IF_ADDREF(*parentWidget);
5505 return NS_OK;
5506 }
5508 NS_IMETHODIMP
5509 nsDocShell::SetParentWidget(nsIWidget * aParentWidget)
5510 {
5511 mParentWidget = aParentWidget;
5513 return NS_OK;
5514 }
5516 NS_IMETHODIMP
5517 nsDocShell::GetParentNativeWindow(nativeWindow * parentNativeWindow)
5518 {
5519 NS_ENSURE_ARG_POINTER(parentNativeWindow);
5521 if (mParentWidget)
5522 *parentNativeWindow = mParentWidget->GetNativeData(NS_NATIVE_WIDGET);
5523 else
5524 *parentNativeWindow = nullptr;
5526 return NS_OK;
5527 }
5529 NS_IMETHODIMP
5530 nsDocShell::SetParentNativeWindow(nativeWindow parentNativeWindow)
5531 {
5532 return NS_ERROR_NOT_IMPLEMENTED;
5533 }
5535 NS_IMETHODIMP
5536 nsDocShell::GetNativeHandle(nsAString& aNativeHandle)
5537 {
5538 // the nativeHandle should be accessed from nsIXULWindow
5539 return NS_ERROR_NOT_IMPLEMENTED;
5540 }
5542 NS_IMETHODIMP
5543 nsDocShell::GetVisibility(bool * aVisibility)
5544 {
5545 NS_ENSURE_ARG_POINTER(aVisibility);
5547 *aVisibility = false;
5549 if (!mContentViewer)
5550 return NS_OK;
5552 nsCOMPtr<nsIPresShell> presShell = GetPresShell();
5553 if (!presShell)
5554 return NS_OK;
5556 // get the view manager
5557 nsViewManager* vm = presShell->GetViewManager();
5558 NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE);
5560 // get the root view
5561 nsView *view = vm->GetRootView(); // views are not ref counted
5562 NS_ENSURE_TRUE(view, NS_ERROR_FAILURE);
5564 // if our root view is hidden, we are not visible
5565 if (view->GetVisibility() == nsViewVisibility_kHide)
5566 return NS_OK;
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.
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();
5579 nsCOMPtr<nsIDocShell> parentDS = do_QueryInterface(parentItem);
5580 nsCOMPtr<nsIPresShell> pPresShell = parentDS->GetPresShell();
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 }
5588 nsIContent *shellContent =
5589 pPresShell->GetDocument()->FindContentForSubDocument(presShell->GetDocument());
5590 NS_ASSERTION(shellContent, "subshell not in the map");
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 }
5601 treeItem = parentItem;
5602 treeItem->GetParent(getter_AddRefs(parentItem));
5603 }
5605 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(mTreeOwner));
5606 if (!treeOwnerAsWin) {
5607 *aVisibility = true;
5608 return NS_OK;
5609 }
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 }
5616 NS_IMETHODIMP
5617 nsDocShell::SetIsOffScreenBrowser(bool aIsOffScreen)
5618 {
5619 mIsOffScreenBrowser = aIsOffScreen;
5620 return NS_OK;
5621 }
5623 NS_IMETHODIMP
5624 nsDocShell::GetIsOffScreenBrowser(bool *aIsOffScreen)
5625 {
5626 *aIsOffScreen = mIsOffScreenBrowser;
5627 return NS_OK;
5628 }
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;
5637 // Keep track ourselves.
5638 mIsActive = aIsActive;
5640 // Tell the PresShell about it.
5641 nsCOMPtr<nsIPresShell> pshell = GetPresShell();
5642 if (pshell)
5643 pshell->SetIsActive(aIsActive);
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 }
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 }
5662 if (!docshell->GetIsBrowserOrApp()) {
5663 docshell->SetIsActive(aIsActive);
5664 }
5665 }
5667 return NS_OK;
5668 }
5670 NS_IMETHODIMP
5671 nsDocShell::GetIsActive(bool *aIsActive)
5672 {
5673 *aIsActive = mIsActive;
5674 return NS_OK;
5675 }
5677 NS_IMETHODIMP
5678 nsDocShell::SetIsAppTab(bool aIsAppTab)
5679 {
5680 mIsAppTab = aIsAppTab;
5681 return NS_OK;
5682 }
5684 NS_IMETHODIMP
5685 nsDocShell::GetIsAppTab(bool *aIsAppTab)
5686 {
5687 *aIsAppTab = mIsAppTab;
5688 return NS_OK;
5689 }
5691 NS_IMETHODIMP
5692 nsDocShell::SetSandboxFlags(uint32_t aSandboxFlags)
5693 {
5694 mSandboxFlags = aSandboxFlags;
5695 return NS_OK;
5696 }
5698 NS_IMETHODIMP
5699 nsDocShell::GetSandboxFlags(uint32_t *aSandboxFlags)
5700 {
5701 *aSandboxFlags = mSandboxFlags;
5702 return NS_OK;
5703 }
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 }
5713 mOnePermittedSandboxedNavigator = do_GetWeakReference(aSandboxedNavigator);
5714 NS_ASSERTION(mOnePermittedSandboxedNavigator,
5715 "One Permitted Sandboxed Navigator must support weak references.");
5717 return NS_OK;
5718 }
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 }
5730 NS_IMETHODIMP
5731 nsDocShell::SetDefaultLoadFlags(uint32_t aDefaultLoadFlags)
5732 {
5733 mDefaultLoadFlags = aDefaultLoadFlags;
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 }
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 }
5756 NS_IMETHODIMP
5757 nsDocShell::GetDefaultLoadFlags(uint32_t *aDefaultLoadFlags)
5758 {
5759 *aDefaultLoadFlags = mDefaultLoadFlags;
5760 return NS_OK;
5761 }
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 }
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 }
5791 NS_IMETHODIMP
5792 nsDocShell::GetAllowMixedContentAndConnectionData(bool* aRootHasSecureConnection, bool* aAllowMixedContent, bool* aIsRootDocShell)
5793 {
5794 *aRootHasSecureConnection = true;
5795 *aAllowMixedContent = false;
5796 *aIsRootDocShell = false;
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);
5803 // now get the document from sameTypeRoot
5804 nsCOMPtr<nsIDocument> rootDoc = do_GetInterface(sameTypeRoot);
5805 if (rootDoc) {
5806 nsCOMPtr<nsIPrincipal> rootPrincipal = rootDoc->NodePrincipal();
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 }
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 }
5826 return NS_OK;
5827 }
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 }
5843 return NS_OK;
5844 }
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 }
5854 NS_IMETHODIMP
5855 nsDocShell::SetEnabled(bool aEnabled)
5856 {
5857 return NS_ERROR_NOT_IMPLEMENTED;
5858 }
5860 NS_IMETHODIMP
5861 nsDocShell::SetFocus()
5862 {
5863 return NS_OK;
5864 }
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 }
5873 NS_IMETHODIMP
5874 nsDocShell::GetTitle(char16_t ** aTitle)
5875 {
5876 NS_ENSURE_ARG_POINTER(aTitle);
5878 *aTitle = ToNewUnicode(mTitle);
5879 return NS_OK;
5880 }
5882 NS_IMETHODIMP
5883 nsDocShell::SetTitle(const char16_t * aTitle)
5884 {
5885 // Store local title
5886 mTitle = aTitle;
5888 nsCOMPtr<nsIDocShellTreeItem> parent;
5889 GetSameTypeParent(getter_AddRefs(parent));
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 }
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 }
5911 // Update SessionHistory with the document's title.
5912 if (mOSHE && mLoadType != LOAD_BYPASS_HISTORY &&
5913 mLoadType != LOAD_ERROR_PAGE) {
5915 mOSHE->SetTitle(mTitle);
5916 }
5918 return NS_OK;
5919 }
5921 nsresult
5922 nsDocShell::GetCurScrollPos(int32_t scrollOrientation, int32_t * curPos)
5923 {
5924 NS_ENSURE_ARG_POINTER(curPos);
5926 nsIScrollableFrame* sf = GetRootScrollFrame();
5927 NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
5929 nsPoint pt = sf->GetScrollPosition();
5931 switch (scrollOrientation) {
5932 case ScrollOrientation_X:
5933 *curPos = pt.x;
5934 return NS_OK;
5936 case ScrollOrientation_Y:
5937 *curPos = pt.y;
5938 return NS_OK;
5940 default:
5941 NS_ENSURE_TRUE(false, NS_ERROR_INVALID_ARG);
5942 }
5943 }
5945 nsresult
5946 nsDocShell::SetCurScrollPosEx(int32_t curHorizontalPos, int32_t curVerticalPos)
5947 {
5948 nsIScrollableFrame* sf = GetRootScrollFrame();
5949 NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
5951 sf->ScrollTo(nsPoint(curHorizontalPos, curVerticalPos),
5952 nsIScrollableFrame::INSTANT);
5953 return NS_OK;
5954 }
5956 //*****************************************************************************
5957 // nsDocShell::nsIScrollable
5958 //*****************************************************************************
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;
5970 case ScrollOrientation_Y:
5971 *scrollbarPref = mDefaultScrollbarPref.y;
5972 return NS_OK;
5974 default:
5975 NS_ENSURE_TRUE(false, NS_ERROR_INVALID_ARG);
5976 }
5977 return NS_ERROR_FAILURE;
5978 }
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;
5989 case ScrollOrientation_Y:
5990 mDefaultScrollbarPref.y = scrollbarPref;
5991 return NS_OK;
5993 default:
5994 NS_ENSURE_TRUE(false, NS_ERROR_INVALID_ARG);
5995 }
5996 return NS_ERROR_FAILURE;
5997 }
5999 NS_IMETHODIMP
6000 nsDocShell::GetScrollbarVisibility(bool * verticalVisible,
6001 bool * horizontalVisible)
6002 {
6003 nsIScrollableFrame* sf = GetRootScrollFrame();
6004 NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
6006 uint32_t scrollbarVisibility = sf->GetScrollbarVisibility();
6007 if (verticalVisible)
6008 *verticalVisible = (scrollbarVisibility & nsIScrollableFrame::VERTICAL) != 0;
6009 if (horizontalVisible)
6010 *horizontalVisible = (scrollbarVisibility & nsIScrollableFrame::HORIZONTAL) != 0;
6012 return NS_OK;
6013 }
6015 //*****************************************************************************
6016 // nsDocShell::nsITextScroll
6017 //*****************************************************************************
6019 NS_IMETHODIMP
6020 nsDocShell::ScrollByLines(int32_t numLines)
6021 {
6022 nsIScrollableFrame* sf = GetRootScrollFrame();
6023 NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
6025 sf->ScrollBy(nsIntPoint(0, numLines), nsIScrollableFrame::LINES,
6026 nsIScrollableFrame::SMOOTH);
6027 return NS_OK;
6028 }
6030 NS_IMETHODIMP
6031 nsDocShell::ScrollByPages(int32_t numPages)
6032 {
6033 nsIScrollableFrame* sf = GetRootScrollFrame();
6034 NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
6036 sf->ScrollBy(nsIntPoint(0, numPages), nsIScrollableFrame::PAGES,
6037 nsIScrollableFrame::SMOOTH);
6038 return NS_OK;
6039 }
6041 //*****************************************************************************
6042 // nsDocShell::nsIRefreshURI
6043 //*****************************************************************************
6045 NS_IMETHODIMP
6046 nsDocShell::RefreshURI(nsIURI * aURI, int32_t aDelay, bool aRepeat,
6047 bool aMetaRefresh)
6048 {
6049 NS_ENSURE_ARG(aURI);
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;
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;
6071 nsRefreshTimer *refreshTimer = new nsRefreshTimer();
6072 NS_ENSURE_TRUE(refreshTimer, NS_ERROR_OUT_OF_MEMORY);
6073 uint32_t busyFlags = 0;
6074 GetBusyFlags(&busyFlags);
6076 nsCOMPtr<nsISupports> dataRef = refreshTimer; // Get the ref count to 1
6078 refreshTimer->mDocShell = this;
6079 refreshTimer->mURI = aURI;
6080 refreshTimer->mDelay = aDelay;
6081 refreshTimer->mRepeat = aRepeat;
6082 refreshTimer->mMetaRefresh = aMetaRefresh;
6084 if (!mRefreshURIList) {
6085 NS_ENSURE_SUCCESS(NS_NewISupportsArray(getter_AddRefs(mRefreshURIList)),
6086 NS_ERROR_FAILURE);
6087 }
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);
6101 mRefreshURIList->AppendElement(timer); // owning timer ref
6102 timer->InitWithCallback(refreshTimer, aDelay, nsITimer::TYPE_ONE_SHOT);
6103 }
6104 return NS_OK;
6105 }
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");
6115 // Remove aTimer from mRefreshURIList if needed
6116 if (mRefreshURIList) {
6117 uint32_t n = 0;
6118 mRefreshURIList->Count(&n);
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 }
6129 return ForceRefreshURI(aURI, aDelay, aMetaRefresh);
6130 }
6132 NS_IMETHODIMP
6133 nsDocShell::ForceRefreshURI(nsIURI * aURI,
6134 int32_t aDelay,
6135 bool aMetaRefresh)
6136 {
6137 NS_ENSURE_ARG(aURI);
6139 nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
6140 CreateLoadInfo(getter_AddRefs(loadInfo));
6141 NS_ENSURE_TRUE(loadInfo, NS_ERROR_OUT_OF_MEMORY);
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);
6148 /* for most refreshes the current URI is an appropriate
6149 * internal referrer
6150 */
6151 loadInfo->SetReferrer(mCurrentURI);
6153 /* Don't ever "guess" on which owner to use to avoid picking
6154 * the current owner.
6155 */
6156 loadInfo->SetOwnerIsExplicit(true);
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) {
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);
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 }
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);
6191 return NS_OK;
6192 }
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.
6236 // when done, seconds is 0 or the given number of seconds
6237 // uriAttrib is empty or the URI specified
6238 MOZ_ASSERT(aPrincipal);
6240 nsAutoCString uriAttrib;
6241 int32_t seconds = 0;
6242 bool specifiesSeconds = false;
6244 nsACString::const_iterator iter, tokenStart, doneIterating;
6246 aHeader.BeginReading(iter);
6247 aHeader.EndReading(doneIterating);
6249 // skip leading whitespace
6250 while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter))
6251 ++iter;
6253 tokenStart = iter;
6255 // skip leading + and -
6256 if (iter != doneIterating && (*iter == '-' || *iter == '+'))
6257 ++iter;
6259 // parse number
6260 while (iter != doneIterating && (*iter >= '0' && *iter <= '9')) {
6261 seconds = seconds * 10 + (*iter - '0');
6262 specifiesSeconds = true;
6263 ++iter;
6264 }
6266 if (iter != doneIterating) {
6267 // if we started with a '-', number is negative
6268 if (*tokenStart == '-')
6269 seconds = -seconds;
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 }
6301 // skip any remaining whitespace
6302 while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter))
6303 ++iter;
6305 // skip ';' or ','
6306 if (iter != doneIterating && (*iter == ';' || *iter == ',')) {
6307 ++iter;
6308 }
6310 // skip whitespace
6311 while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter))
6312 ++iter;
6313 }
6315 // possible start of URI
6316 tokenStart = iter;
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;
6326 // skip whitespace
6327 while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter))
6328 ++iter;
6330 if (iter != doneIterating && *iter == '=') {
6331 ++iter;
6333 // skip whitespace
6334 while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter))
6335 ++iter;
6337 // found real start of URI
6338 tokenStart = iter;
6339 }
6340 }
6341 }
6342 }
6344 // skip a leading '"' or '\''.
6346 bool isQuotedURI = false;
6347 if (tokenStart != doneIterating && (*tokenStart == '"' || *tokenStart == '\''))
6348 {
6349 isQuotedURI = true;
6350 ++tokenStart;
6351 }
6353 // set iter to start of URI
6354 iter = tokenStart;
6356 // tokenStart here points to the beginning of URI
6358 // grab the rest of the URI
6359 while (iter != doneIterating)
6360 {
6361 if (isQuotedURI && (*iter == '"' || *iter == '\''))
6362 break;
6363 ++iter;
6364 }
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 }
6373 // URI is whatever's contained from tokenStart to iter.
6374 // note: if tokenStart == doneIterating, so is iter.
6376 nsresult rv = NS_OK;
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 }
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 }
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);
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);
6413 if (isjs) {
6414 return NS_ERROR_FAILURE;
6415 }
6416 }
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;
6424 rv = RefreshURI(uri, seconds * 1000, false, true);
6425 }
6426 }
6427 }
6428 return rv;
6429 }
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);
6440 if (!refreshHeader.IsEmpty()) {
6441 nsCOMPtr<nsIScriptSecurityManager> secMan =
6442 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
6443 NS_ENSURE_SUCCESS(rv, rv);
6445 nsCOMPtr<nsIPrincipal> principal;
6446 rv = secMan->GetChannelPrincipal(aChannel, getter_AddRefs(principal));
6447 NS_ENSURE_SUCCESS(rv, rv);
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 }
6459 static void
6460 DoCancelRefreshURITimers(nsISupportsArray* aTimerList)
6461 {
6462 if (!aTimerList)
6463 return;
6465 uint32_t n=0;
6466 aTimerList->Count(&n);
6468 while (n) {
6469 nsCOMPtr<nsITimer> timer(do_QueryElementAt(aTimerList, --n));
6471 aTimerList->RemoveElementAt(n); // bye bye owning timer ref
6473 if (timer)
6474 timer->Cancel();
6475 }
6476 }
6478 NS_IMETHODIMP
6479 nsDocShell::CancelRefreshURITimers()
6480 {
6481 DoCancelRefreshURITimers(mRefreshURIList);
6482 DoCancelRefreshURITimers(mSavedRefreshURIList);
6483 mRefreshURIList = nullptr;
6484 mSavedRefreshURIList = nullptr;
6486 return NS_OK;
6487 }
6489 NS_IMETHODIMP
6490 nsDocShell::GetRefreshPending(bool* _retval)
6491 {
6492 if (!mRefreshURIList) {
6493 *_retval = false;
6494 return NS_OK;
6495 }
6497 uint32_t count;
6498 nsresult rv = mRefreshURIList->Count(&count);
6499 if (NS_SUCCEEDED(rv))
6500 *_retval = (count != 0);
6501 return rv;
6502 }
6504 NS_IMETHODIMP
6505 nsDocShell::SuspendRefreshURIs()
6506 {
6507 if (mRefreshURIList) {
6508 uint32_t n = 0;
6509 mRefreshURIList->Count(&n);
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
6516 // Replace this timer object with a nsRefreshTimer object.
6517 nsCOMPtr<nsITimerCallback> callback;
6518 timer->GetCallback(getter_AddRefs(callback));
6520 timer->Cancel();
6522 nsCOMPtr<nsITimerCallback> rt = do_QueryInterface(callback);
6523 NS_ASSERTION(rt, "RefreshURIList timer callbacks should only be RefreshTimer objects");
6525 mRefreshURIList->ReplaceElementAt(rt, i);
6526 }
6527 }
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 }
6537 return NS_OK;
6538 }
6540 NS_IMETHODIMP
6541 nsDocShell::ResumeRefreshURIs()
6542 {
6543 RefreshURIFromQueue();
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 }
6553 return NS_OK;
6554 }
6556 nsresult
6557 nsDocShell::RefreshURIFromQueue()
6558 {
6559 if (!mRefreshURIList)
6560 return NS_OK;
6561 uint32_t n = 0;
6562 mRefreshURIList->Count(&n);
6564 while (n) {
6565 nsCOMPtr<nsISupports> element;
6566 mRefreshURIList->GetElementAt(--n, getter_AddRefs(element));
6567 nsCOMPtr<nsITimerCallback> refreshInfo(do_QueryInterface(element));
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
6586 return NS_OK;
6587 }
6589 //*****************************************************************************
6590 // nsDocShell::nsIContentViewerContainer
6591 //*****************************************************************************
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();
6601 nsresult rv = SetupNewViewer(aContentViewer);
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);
6626 SetHistoryEntry(&mOSHE, mLSHE);
6627 }
6629 bool updateHistory = true;
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 }
6645 if (!updateHistory)
6646 SetLayoutHistoryState(nullptr);
6648 return NS_OK;
6649 }
6651 /* void setIsPrinting (in boolean aIsPrinting); */
6652 NS_IMETHODIMP
6653 nsDocShell::SetIsPrinting(bool aIsPrinting)
6654 {
6655 mIsPrintingOrPP = aIsPrinting;
6656 return NS_OK;
6657 }
6659 //*****************************************************************************
6660 // nsDocShell::nsIWebProgressListener
6661 //*****************************************************************************
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 }
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);
6686 nsCOMPtr<nsIWyciwygChannel> wcwgChannel(do_QueryInterface(aRequest));
6687 nsCOMPtr<nsIWebProgress> webProgress =
6688 do_QueryInterface(GetAsSupports(this));
6690 // We don't update navigation timing for wyciwyg channels
6691 if (this == aProgress && !wcwgChannel){
6692 MaybeInitTiming();
6693 mTiming->NotifyFetchStart(uri, ConvertLoadTypeToNavigationType(mLoadType));
6694 }
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) {
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 }
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 }
6744 }
6745 // Page has begun to load
6746 mBusyFlags = BUSY_FLAGS_BUSY | BUSY_FLAGS_BEFORE_PAGE_LOAD;
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;
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 }
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 }
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
6810 nsCOMPtr<nsIURI> oldURI, newURI;
6811 aOldChannel->GetURI(getter_AddRefs(oldURI));
6812 aNewChannel->GetURI(getter_AddRefs(newURI));
6813 if (!oldURI || !newURI) {
6814 return;
6815 }
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 }
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 }
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!)
6847 // Get N - 1 and transition type
6848 nsCOMPtr<nsIURI> previousURI;
6849 uint32_t previousFlags = 0;
6850 ExtractLastVisit(aOldChannel, getter_AddRefs(previousURI), &previousFlags);
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));
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 }
6875 // Add visit N -1 => N
6876 AddURIVisit(oldURI, referrer, previousURI, previousFlags,
6877 responseStatus);
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 }
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);
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 }
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 }
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 }
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 }
6929 nsresult
6930 nsDocShell::EndPageLoad(nsIWebProgress * aProgress,
6931 nsIChannel * aChannel, nsresult aStatus)
6932 {
6933 if(!aChannel)
6934 return NS_ERROR_NULL_POINTER;
6936 MOZ_EVENT_TRACER_DONE(this, "docshell::pageload");
6938 nsCOMPtr<nsIURI> url;
6939 nsresult rv = aChannel->GetURI(getter_AddRefs(url));
6940 if (NS_FAILED(rv)) return rv;
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 }
6958 // Timing is picked up by the window, we don't need it anymore
6959 mTiming = nullptr;
6961 // clean up reload state for meta charset
6962 if (eCharsetReloadRequested == mCharsetReloadState)
6963 mCharsetReloadState = eCharsetReloadStopOrigional;
6964 else
6965 mCharsetReloadState = eCharsetReloadInit;
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;
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);
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;
6986 mEODForCurrentDocument = true;
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));
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 }
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);
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();
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 }
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 }
7053 if (sURIFixup) {
7054 //
7055 // Try and make an alternative URI from the old one
7056 //
7057 nsCOMPtr<nsIURI> newURI;
7058 nsCOMPtr<nsIInputStream> newPostData;
7060 nsAutoCString oldSpec;
7061 url->GetSpec(oldSpec);
7063 //
7064 // First try keyword fixup
7065 //
7066 if (aStatus == NS_ERROR_UNKNOWN_HOST && mAllowKeywordFixup) {
7067 bool keywordsEnabled =
7068 Preferences::GetBool("keyword.enabled", false);
7070 nsAutoCString host;
7071 url->GetHost(host);
7073 nsAutoCString scheme;
7074 url->GetScheme(scheme);
7076 int32_t dotLoc = host.FindChar('.');
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 }
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 }
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;
7140 // Skip fixup for anything except a normal document load
7141 // operation on the topframe.
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 }
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);
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 }
7190 // Well, fixup didn't work :-(
7191 // It is time to throw an error dialog box, and be done with it...
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 }
7229 return NS_OK;
7230 }
7233 //*****************************************************************************
7234 // nsDocShell: Content Viewer Management
7235 //*****************************************************************************
7237 NS_IMETHODIMP
7238 nsDocShell::EnsureContentViewer()
7239 {
7240 if (mContentViewer)
7241 return NS_OK;
7242 if (mIsBeingDestroyed)
7243 return NS_ERROR_FAILURE;
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 }
7260 nsresult rv = CreateAboutBlankContentViewer(principal, baseURI);
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!");
7268 doc->SetIsInitialDocument(true);
7269 }
7271 return rv;
7272 }
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;
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;
7290 mCreatingDocument = true;
7292 // mContentViewer->PermitUnload may release |this| docshell.
7293 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
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.
7305 // Unload gets fired first for
7306 // document loaded from the session history.
7307 mTiming->NotifyBeforeUnload();
7309 bool okToUnload;
7310 rv = mContentViewer->PermitUnload(false, &okToUnload);
7312 if (NS_SUCCEEDED(rv) && !okToUnload) {
7313 // The user chose not to unload the page, interrupt the load.
7314 return NS_ERROR_FAILURE;
7315 }
7317 mSavingOldViewer = aTryToSaveOldPresentation &&
7318 CanSavePresentation(LOAD_NORMAL, nullptr, nullptr);
7320 if (mTiming) {
7321 mTiming->NotifyUnloadAccepted(mCurrentURI);
7322 }
7324 // Make sure to blow away our mLoadingURI just in case. No loads
7325 // from inside this pagehide.
7326 mLoadingURI = nullptr;
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();
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 }
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;
7347 nsCOMPtr<nsIDocumentLoaderFactory> docFactory =
7348 nsContentUtils::FindInternalContentViewer("text/html");
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);
7365 blankDoc->SetContainer(this);
7367 // Copy our sandbox flags to the document. These are immutable
7368 // after being set here.
7369 blankDoc->SetSandboxFlags(mSandboxFlags);
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));
7375 // hook 'em up
7376 if (viewer) {
7377 viewer->SetContainer(this);
7378 Embed(viewer, "", 0);
7380 SetCurrentURI(blankDoc->GetDocumentURI(), nullptr, true, 0);
7381 rv = mIsBeingDestroyed ? NS_ERROR_NOT_AVAILABLE : NS_OK;
7382 }
7383 }
7384 }
7385 mCreatingDocument = false;
7387 // The transient about:blank viewer doesn't have a session history entry.
7388 SetHistoryEntry(&mOSHE, nullptr);
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 }
7396 return rv;
7397 }
7399 NS_IMETHODIMP
7400 nsDocShell::CreateAboutBlankContentViewer(nsIPrincipal *aPrincipal)
7401 {
7402 return CreateAboutBlankContentViewer(aPrincipal, nullptr);
7403 }
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
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 }
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;
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;
7438 // If the document is not done loading, don't cache it.
7439 if (!mScriptGlobal || mScriptGlobal->IsLoading())
7440 return false;
7442 if (mScriptGlobal->WouldReuseInnerWindow(aNewDocument))
7443 return false;
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;
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 }
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 }
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.");
7476 if (mEditorData || !aSHEntry)
7477 return;
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 }
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 }
7498 NS_ASSERTION(!mOSHE || !mOSHE->HasDetachedEditor(),
7499 "Detaching editor when it's already detached.");
7501 nsresult res = mEditorData->DetachFromWindow();
7502 NS_ASSERTION(NS_SUCCEEDED(res), "Failed to detach editor");
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 }
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 }
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 }
7530 if (!mScriptGlobal)
7531 return NS_ERROR_FAILURE;
7533 nsCOMPtr<nsISupports> windowState = mScriptGlobal->SaveWindowState();
7534 NS_ENSURE_TRUE(windowState, NS_ERROR_FAILURE);
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
7546 nsresult rv = mOSHE->SetWindowState(windowState);
7547 NS_ENSURE_SUCCESS(rv, rv);
7549 // Suspend refresh URIs and save off the timer queue
7550 rv = mOSHE->SetRefreshURIList(mSavedRefreshURIList);
7551 NS_ENSURE_SUCCESS(rv, rv);
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 }
7561 // Capture the docshell hierarchy.
7562 mOSHE->ClearChildShells();
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");
7569 mOSHE->AddChildShell(childShell);
7570 }
7572 return NS_OK;
7573 }
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 }
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);
7591 aContentViewer = mContentViewer;
7592 }
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.
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 }
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;
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 }
7627 return NS_OK;
7628 }
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 }
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.
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 }
7658 if (mOSHE && mOSHE->HasDetachedEditor()) {
7659 ReattachEditorToWindow(mOSHE);
7660 }
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.
7668 nsIChannel *channel = doc->GetChannel();
7669 if (channel) {
7670 mIsRestoringDocument = true;
7671 mLoadGroup->RemoveRequest(channel, nullptr, NS_OK);
7672 mIsRestoringDocument = false;
7673 }
7674 }
7676 return NS_OK;
7677 }
7679 NS_IMETHODIMP
7680 nsDocShell::GetRestoringDocument(bool *aRestoring)
7681 {
7682 *aRestoring = mIsRestoringDocument;
7683 return NS_OK;
7684 }
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");
7692 nsCOMPtr<nsIContentViewer> viewer;
7693 aSHEntry->GetContentViewer(getter_AddRefs(viewer));
7695 #ifdef DEBUG_PAGE_CACHE
7696 nsCOMPtr<nsIURI> uri;
7697 aSHEntry->GetURI(getter_AddRefs(uri));
7699 nsAutoCString spec;
7700 if (uri)
7701 uri->GetSpec(spec);
7702 #endif
7704 *aRestoring = false;
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 }
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.
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 }
7729 NS_ASSERTION(mContentViewer != viewer, "Restoring existing presentation");
7731 #ifdef DEBUG_PAGE_CACHE
7732 printf("restoring presentation from session history: %s\n", spec.get());
7733 #endif
7735 SetHistoryEntry(&mLSHE, aSHEntry);
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.
7743 BeginRestore(viewer, true);
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.
7749 // Revoke any pending restore (just in case)
7750 NS_ASSERTION(!mRestorePresentationEvent.IsPending(),
7751 "should only have one RestorePresentationEvent");
7752 mRestorePresentationEvent.Revoke();
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 }
7763 return rv;
7764 }
7766 nsresult
7767 nsDocShell::RestoreFromHistory()
7768 {
7769 mRestorePresentationEvent.Forget();
7771 // This section of code follows the same ordering as CreateContentViewer.
7772 if (!mLSHE)
7773 return NS_ERROR_FAILURE;
7775 nsCOMPtr<nsIContentViewer> viewer;
7776 mLSHE->GetContentViewer(getter_AddRefs(viewer));
7777 if (!viewer)
7778 return NS_ERROR_FAILURE;
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 }
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 }
7809 // Protect against mLSHE going away via a load triggered from
7810 // pagehide or unload.
7811 nsCOMPtr<nsISHEntry> origLSHE = mLSHE;
7813 // Make sure to blow away our mLoadingURI just in case. No loads
7814 // from inside this pagehide.
7815 mLoadingURI = nullptr;
7817 // Notify the old content viewer that it's being hidden.
7818 FirePageHideNotification(!mSavingOldViewer);
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;
7825 // Set mFiredUnloadEvent = false so that the unload handler for the
7826 // *new* document will fire.
7827 mFiredUnloadEvent = false;
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 }
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 }
7857 mSavedRefreshURIList = nullptr;
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.
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 }
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.
7878 nsView *rootViewSibling = nullptr, *rootViewParent = nullptr;
7879 nsIntRect newBounds(0, 0, 0, 0);
7881 nsCOMPtr<nsIPresShell> oldPresShell = GetPresShell();
7882 if (oldPresShell) {
7883 nsViewManager *vm = oldPresShell->GetViewManager();
7884 if (vm) {
7885 nsView *oldRootView = vm->GetRootView();
7887 if (oldRootView) {
7888 rootViewSibling = oldRootView->GetNextSibling();
7889 rootViewParent = oldRootView->GetParent();
7891 mContentViewer->GetBounds(newBounds);
7892 }
7893 }
7894 }
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 }
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.
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 }
7922 // Order the mContentViewer setup just like Embed does.
7923 mContentViewer = nullptr;
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();
7929 mContentViewer.swap(viewer);
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);
7937 bool sticky;
7938 mLSHE->GetSticky(&sticky);
7940 nsCOMPtr<nsIDOMDocument> domDoc;
7941 mContentViewer->GetDOMDocument(getter_AddRefs(domDoc));
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 }
7951 // get the previous content viewer size
7952 nsIntRect oldBounds(0, 0, 0, 0);
7953 mLSHE->GetViewerBounds(oldBounds);
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));
7960 // Reattach to the window object.
7961 mIsRestoringDocument = true; // for MediaDocument::BecomeInteractive
7962 rv = mContentViewer->Open(windowState, mLSHE);
7963 mIsRestoringDocument = false;
7965 // Hack to keep nsDocShellEditorData alive across the
7966 // SetContentViewer(nullptr) call below.
7967 nsAutoPtr<nsDocShellEditorData> data(mLSHE->ForgetEditorData());
7969 // Now remove it from the cached presentation.
7970 mLSHE->SetContentViewer(nullptr);
7971 mEODForCurrentDocument = false;
7973 mLSHE->SetEditorData(data.forget());
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
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);
7991 NS_ENSURE_SUCCESS(rv, rv);
7993 // mLSHE is now our currently-loaded document.
7994 SetHistoryEntry(&mOSHE, mLSHE);
7996 // XXX special wyciwyg handling in Embed()?
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);
8002 // This is the end of our Embed() replacement
8004 mSavingOldViewer = false;
8005 mEODForCurrentDocument = false;
8007 // Tell the event loop to favor plevents over user events, see comments
8008 // in CreateContentViewer.
8009 if (++gNumberOfDocumentsLoading == 1)
8010 FavorPerformanceHint(true);
8013 if (oldMUDV && newMUDV) {
8014 newMUDV->SetMinFontSize(minFontSize);
8015 newMUDV->SetTextZoom(textZoom);
8016 newMUDV->SetFullZoom(pageZoom);
8017 newMUDV->SetAuthorStyleDisabled(styleDisabled);
8018 }
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 }
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 }
8038 nsCOMPtr<nsPIDOMWindow> parentWindow = d->GetWindow();
8039 if (parentWindow) {
8040 parentSuspendCount = parentWindow->TimeoutSuspendCount();
8041 }
8042 }
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 }
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");
8061 rv = privWin->RestoreWindowState(windowState);
8062 NS_ENSURE_SUCCESS(rv, rv);
8064 // Now, dispatch a title change event which would happen as the
8065 // <head> is parsed.
8066 document->NotifyPossibleTitleChange(false);
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);
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);
8078 bool allowJavascript;
8079 childShell->GetAllowJavascript(&allowJavascript);
8081 bool allowRedirects;
8082 childShell->GetAllowMetaRedirects(&allowRedirects);
8084 bool allowSubframes;
8085 childShell->GetAllowSubframes(&allowSubframes);
8087 bool allowImages;
8088 childShell->GetAllowImages(&allowImages);
8090 bool allowMedia = childShell->GetAllowMedia();
8092 bool allowDNSPrefetch;
8093 childShell->GetAllowDNSPrefetch(&allowDNSPrefetch);
8095 bool allowContentRetargeting = childShell->GetAllowContentRetargeting();
8097 uint32_t defaultLoadFlags;
8098 childShell->GetDefaultLoadFlags(&defaultLoadFlags);
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);
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);
8116 rv = childShell->BeginRestore(nullptr, false);
8117 NS_ENSURE_SUCCESS(rv, rv);
8118 }
8120 nsCOMPtr<nsIPresShell> shell = GetPresShell();
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.
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().
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 }
8138 nsViewManager *newVM = shell ? shell->GetViewManager() : nullptr;
8139 nsView *newRootView = newVM ? newVM->GetRootView() : nullptr;
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);
8167 NS_ASSERTION(newRootView->GetNextSibling() == rootViewSibling,
8168 "error in InsertChild");
8169 }
8170 }
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 }
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();
8183 // Restore the refresh URI list. The refresh timers will be restarted
8184 // when EndPageLoad() is called.
8185 mRefreshURIList = refreshURIList;
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 }
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.
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).
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 }
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;
8230 // Simulate the completion of the load.
8231 nsDocShell::FinishRestore();
8233 // Restart plugins, and paint the content.
8234 if (shell) {
8235 shell->Thaw();
8236 }
8238 return privWin->FireDelayedDOMEvents();
8239 }
8241 NS_IMETHODIMP
8242 nsDocShell::CreateContentViewer(const char *aContentType,
8243 nsIRequest * request,
8244 nsIStreamListener ** aContentHandler)
8245 {
8246 *aContentHandler = nullptr;
8248 // Can we check the content type of the current content viewer
8249 // and reuse it without destroying it and re-creating it?
8251 NS_ASSERTION(mLoadGroup, "Someone ignored return from Init()?");
8253 // Instantiate the content viewer object
8254 nsCOMPtr<nsIContentViewer> viewer;
8255 nsresult rv = NewContentViewerObj(aContentType, request, mLoadGroup,
8256 aContentHandler, getter_AddRefs(viewer));
8258 if (NS_FAILED(rv))
8259 return rv;
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 //
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 }
8279 NS_ASSERTION(!mLoadingURI, "Re-entering unload?");
8281 nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(request);
8282 if (aOpenedChannel) {
8283 aOpenedChannel->GetURI(getter_AddRefs(mLoadingURI));
8284 }
8285 FirePageHideNotification(!mSavingOldViewer);
8286 mLoadingURI = nullptr;
8288 // Set mFiredUnloadEvent = false so that the unload handler for the
8289 // *new* document will fire.
8290 mFiredUnloadEvent = false;
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;
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.
8302 // Revert mLoadType to load type to state the page load failed,
8303 // following function calls need it.
8304 mLoadType = mFailedLoadType;
8306 nsCOMPtr<nsIChannel> failedChannel = mFailedChannel;
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 }
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 }
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.");
8326 mFailedChannel = nullptr;
8327 mFailedURI = nullptr;
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);
8335 if (errorOnLocationChangeNeeded) {
8336 FireOnLocationChange(this, failedChannel, failedURI,
8337 LOCATION_CHANGE_ERROR_PAGE);
8338 }
8339 }
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 }
8352 mLoadType = LOAD_ERROR_PAGE;
8353 }
8355 bool onLocationChangeNeeded = OnLoadingSite(aOpenedChannel, false);
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);
8363 if (currentLoadGroup != mLoadGroup) {
8364 nsLoadFlags loadFlags = 0;
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);
8379 // Mark the channel as being a document URI...
8380 aOpenedChannel->GetLoadFlags(&loadFlags);
8381 loadFlags |= nsIChannel::LOAD_DOCUMENT_URI;
8383 aOpenedChannel->SetLoadFlags(loadFlags);
8385 mLoadGroup->AddRequest(request, nullptr);
8386 if (currentLoadGroup)
8387 currentLoadGroup->RemoveRequest(request, nullptr,
8388 NS_BINDING_RETARGETED);
8390 // Update the notification callbacks, so that progress and
8391 // status information are sent to the right docshell...
8392 aOpenedChannel->SetNotificationCallbacks(this);
8393 }
8395 NS_ENSURE_SUCCESS(Embed(viewer, "", (nsISupports *) nullptr),
8396 NS_ERROR_FAILURE);
8398 mSavedRefreshURIList = nullptr;
8399 mSavingOldViewer = false;
8400 mEODForCurrentDocument = false;
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 }
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 }
8427 if (onLocationChangeNeeded) {
8428 FireOnLocationChange(this, request, mCurrentURI, 0);
8429 }
8431 return NS_OK;
8432 }
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);
8442 nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
8443 nsContentUtils::FindInternalContentViewer(aContentType);
8444 if (!docLoaderFactory) {
8445 return NS_ERROR_FAILURE;
8446 }
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);
8459 (*aViewer)->SetContainer(this);
8460 return NS_OK;
8461 }
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 //
8480 int32_t x = 0;
8481 int32_t y = 0;
8482 int32_t cx = 0;
8483 int32_t cy = 0;
8485 // This will get the size from the current content viewer or from the
8486 // Init settings
8487 DoGetPositionAndSize(&x, &y, &cx, &cy);
8489 nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
8490 NS_ENSURE_SUCCESS(GetSameTypeParent(getter_AddRefs(parentAsItem)),
8491 NS_ERROR_FAILURE);
8492 nsCOMPtr<nsIDocShell> parent(do_QueryInterface(parentAsItem));
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;
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);
8512 // Tell the old content viewer to hibernate in session history when
8513 // it is destroyed.
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 }
8529 if (oldMUDV) {
8530 nsresult rv;
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 }
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();
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));
8572 if (shell) {
8573 bgcolor = shell->GetCanvasBackground();
8574 }
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 }
8585 mContentViewer = nullptr;
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();
8591 mContentViewer = aNewViewer;
8593 nsCOMPtr<nsIWidget> widget;
8594 NS_ENSURE_SUCCESS(GetMainWidget(getter_AddRefs(widget)), NS_ERROR_FAILURE);
8596 nsIntRect bounds(x, y, cx, cy);
8598 mContentViewer->SetNavigationTiming(mTiming);
8600 if (NS_FAILED(mContentViewer->Init(widget, bounds))) {
8601 mContentViewer = nullptr;
8602 NS_ERROR("ContentViewer Initialization failed");
8603 return NS_ERROR_FAILURE;
8604 }
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 }
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));
8631 if (shell) {
8632 shell->SetCanvasBackground(bgcolor);
8633 }
8635 // XXX: It looks like the LayoutState gets restored again in Embed()
8636 // right after the call to SetupNewViewer(...)
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.
8642 return NS_OK;
8643 }
8645 nsresult
8646 nsDocShell::SetDocCurrentStateObj(nsISHEntry *shEntry)
8647 {
8648 nsCOMPtr<nsIDocument> document = do_GetInterface(GetAsSupports(this));
8649 NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
8651 nsCOMPtr<nsIStructuredCloneContainer> scContainer;
8652 if (shEntry) {
8653 nsresult rv = shEntry->GetStateData(getter_AddRefs(scContainer));
8654 NS_ENSURE_SUCCESS(rv, rv);
8656 // If shEntry is null, just set the document's state object to null.
8657 }
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);
8663 return NS_OK;
8664 }
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;
8678 if (!gValidateOrigin || !IsFrame()) {
8679 // Origin validation was turned off, or we're not a frame.
8680 // Permit all loads.
8682 return rv;
8683 }
8685 nsCOMPtr<nsIScriptSecurityManager> securityManager =
8686 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
8687 NS_ENSURE_SUCCESS(rv, rv);
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);
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));
8703 nsIPrincipal *p;
8704 if (!sop || !(p = sop->GetPrincipal())) {
8705 return NS_ERROR_UNEXPECTED;
8706 }
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
8715 return sameOrigin;
8716 }
8718 sameOrigin = NS_ERROR_DOM_PROP_ACCESS_DENIED;
8719 }
8721 nsCOMPtr<nsIDocShellTreeItem> tmp;
8722 item->GetSameTypeParent(getter_AddRefs(tmp));
8723 item.swap(tmp);
8724 } while (item);
8726 return sameOrigin;
8727 }
8729 //*****************************************************************************
8730 // nsDocShell: Site Loading
8731 //*****************************************************************************
8732 namespace
8733 {
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
8743 nsCopyFaviconCallback(nsIURI *aNewURI, bool aInPrivateBrowsing)
8744 : mNewURI(aNewURI)
8745 , mInPrivateBrowsing(aInPrivateBrowsing)
8746 {
8747 }
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 }
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);
8764 return favSvc->SetAndFetchFaviconForPage(mNewURI, aFaviconURI,
8765 false,
8766 mInPrivateBrowsing ?
8767 nsIFaviconService::FAVICON_LOAD_PRIVATE :
8768 nsIFaviconService::FAVICON_LOAD_NON_PRIVATE,
8769 nullptr);
8770 }
8772 private:
8773 nsCOMPtr<nsIURI> mNewURI;
8774 bool mInPrivateBrowsing;
8775 };
8777 NS_IMPL_ISUPPORTS(nsCopyFaviconCallback, nsIFaviconDataCallback)
8778 #endif
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 }
8794 } // anonymous namespace
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 }
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 }
8835 private:
8837 // Use IDL strings so .get() returns null by default
8838 nsXPIDLString mWindowTarget;
8839 nsXPIDLCString mTypeHint;
8840 nsString mSrcdoc;
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 };
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 }
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();
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 }
8911 if (!aURI) {
8912 return NS_ERROR_NULL_POINTER;
8913 }
8915 NS_ENSURE_TRUE(IsValidLoadType(aLoadType), NS_ERROR_INVALID_ARG);
8917 NS_ENSURE_TRUE(!mIsBeingDestroyed, NS_ERROR_NOT_AVAILABLE);
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 }
8928 bool bIsJavascript = false;
8929 if (NS_FAILED(aURI->SchemeIs("javascript", &bIsJavascript))) {
8930 bIsJavascript = false;
8931 }
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();
8942 nsRefPtr<nsGlobalWindow> MMADeathGrip = mScriptGlobal;
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);
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;
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.");
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 }
8983 nsISupports* context = requestingElement;
8984 if (!context) {
8985 context = ToSupports(mScriptGlobal);
8986 }
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);
8995 rv = secMan->GetSimpleCodebasePrincipal(aReferrer,
8996 getter_AddRefs(loadingPrincipal));
8997 }
8999 rv = NS_CheckContentLoadPolicy(contentType,
9000 aURI,
9001 loadingPrincipal,
9002 context,
9003 EmptyCString(), //mime guess
9004 nullptr, //extra
9005 &shouldLoad);
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 }
9012 return NS_ERROR_CONTENT_BLOCKED;
9013 }
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) {
9035 owner = GetInheritedPrincipal(true);
9036 }
9037 }
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 }
9061 nsCOMPtr<nsIDocShellTreeItem> parent;
9062 treeItem->GetSameTypeParent(getter_AddRefs(parent));
9063 parent.swap(treeItem);
9064 } while (treeItem);
9065 }
9066 }
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;
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;
9088 if (doc) {
9089 sandboxFlags = doc->GetSandboxFlags();
9090 if (sandboxFlags & SANDBOXED_AUXILIARY_NAVIGATION) {
9091 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
9092 }
9093 }
9095 nsCOMPtr<nsPIDOMWindow> win =
9096 do_GetInterface(GetAsSupports(this));
9097 NS_ENSURE_TRUE(win, NS_ERROR_NOT_AVAILABLE);
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));
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 }
9121 nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(newWin);
9122 targetDocShell = do_QueryInterface(webNav);
9123 }
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 }
9178 // Else we ran out of memory, or were a popup and got blocked,
9179 // or something.
9181 return rv;
9182 }
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 }
9192 NS_ENSURE_STATE(!HasUnloadedParent());
9194 rv = CheckLoadingPermissions();
9195 if (NS_FAILED(rv)) {
9196 return rv;
9197 }
9199 if (mFiredUnloadEvent) {
9200 if (IsOKToLoadURI(aURI)) {
9201 NS_PRECONDITION(!aWindowTarget || !*aWindowTarget,
9202 "Shouldn't have a window target here!");
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 }
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 }
9220 // Just ignore this load attempt
9221 return NS_OK;
9222 }
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 }
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 }
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 }
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;
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 }
9260 mAllowKeywordFixup =
9261 (aFlags & INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) != 0;
9262 mURIResultedInDocument = false; // reset the clock...
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) {
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);
9282 bool sameExceptHashes = NS_SUCCEEDED(splitRv1) &&
9283 NS_SUCCEEDED(splitRv2) &&
9284 curBeforeHash.Equals(newBeforeHash);
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 }
9299 bool historyNavBetweenSameDoc = false;
9300 if (mOSHE && aSHEntry) {
9301 // We're doing a history load.
9303 mOSHE->SharesDocumentWith(aSHEntry, &historyNavBetweenSameDoc);
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 }
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());
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);
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);
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);
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 }
9364 mURIResultedInDocument = true;
9366 nsCOMPtr<nsISHEntry> oldLSHE = mLSHE;
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);
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);
9393 nsCOMPtr<nsIInputStream> postData;
9394 nsCOMPtr<nsISupports> cacheKey;
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));
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 }
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);
9429 // Make sure we won't just repost without hitting the
9430 // cache first
9431 if (cacheKey)
9432 mOSHE->SetCacheKey(cacheKey);
9433 }
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 }
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 }
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 }
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);
9480 SetDocCurrentStateObj(mOSHE);
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);
9492 if (historyNavBetweenSameDoc || doHashchange) {
9493 win->DispatchSyncPopState();
9494 }
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 }
9503 // Inform the favicon service that the favicon for oldURI also
9504 // applies to aURI.
9505 CopyFavicon(currentURI, aURI, mInPrivateBrowsing);
9507 return NS_OK;
9508 }
9509 }
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);
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.
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);
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 }
9546 if (mTiming && timeBeforeUnload) {
9547 mTiming->NotifyUnloadAccepted(mCurrentURI);
9548 }
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);
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...
9572 nsCOMPtr<nsIContentViewer> zombieViewer;
9573 if (mContentViewer) {
9574 mContentViewer->GetPreviousViewer(getter_AddRefs(zombieViewer));
9575 }
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 }
9584 if (NS_FAILED(rv))
9585 return rv;
9586 }
9588 mLoadType = aLoadType;
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);
9596 mSavingOldViewer = savePresentation;
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);
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;
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();
9639 aSHEntry->SyncPresentationState();
9640 }
9641 }
9643 nsAutoString srcdoc;
9644 if (aFlags & INTERNAL_LOAD_FLAGS_IS_SRCDOC)
9645 srcdoc = aSrcdoc;
9646 else
9647 srcdoc = NullString();
9649 mozilla::net::SeerPredict(aURI, nullptr, nsINetworkSeer::PREDICT_LOAD,
9650 this, nullptr);
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);
9664 if (NS_FAILED(rv)) {
9665 nsCOMPtr<nsIChannel> chan(do_QueryInterface(req));
9666 DisplayLoadError(rv, aURI, nullptr, chan);
9667 }
9669 return rv;
9670 }
9672 nsIPrincipal*
9673 nsDocShell::GetInheritedPrincipal(bool aConsiderCurrentDocument)
9674 {
9675 nsCOMPtr<nsIDocument> document;
9676 bool inheritedFromCurrent = false;
9678 if (aConsiderCurrentDocument && mContentViewer) {
9679 document = mContentViewer->GetDocument();
9680 inheritedFromCurrent = true;
9681 }
9683 if (!document) {
9684 nsCOMPtr<nsIDocShellTreeItem> parentItem;
9685 GetSameTypeParent(getter_AddRefs(parentItem));
9686 if (parentItem) {
9687 document = do_GetInterface(parentItem);
9688 }
9689 }
9691 if (!document) {
9692 if (!aConsiderCurrentDocument) {
9693 return nullptr;
9694 }
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.
9701 if (!mContentViewer)
9702 return nullptr;
9703 document = mContentViewer->GetDocument();
9704 }
9706 //-- Get the document's principal
9707 if (document) {
9708 nsIPrincipal *docPrincipal = document->NodePrincipal();
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 }
9718 return docPrincipal;
9719 }
9721 return nullptr;
9722 }
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
9749 nsresult rv;
9750 nsCOMPtr<nsIURILoader> uriLoader;
9752 uriLoader = do_GetService(NS_URI_LOADER_CONTRACTID, &rv);
9753 if (NS_FAILED(rv)) return rv;
9755 nsLoadFlags loadFlags = mDefaultLoadFlags;
9756 if (aFirstParty) {
9757 // tag first party URL loads
9758 loadFlags |= nsIChannel::LOAD_INITIAL_DOCUMENT_URI;
9759 }
9761 if (mLoadType == LOAD_ERROR_PAGE) {
9762 // Error pages are LOAD_BACKGROUND
9763 loadFlags |= nsIChannel::LOAD_BACKGROUND;
9764 }
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 }
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 }
9805 // open a channel for the url
9806 nsCOMPtr<nsIChannel> channel;
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);
9846 if (isViewSource) {
9847 nsViewSourceHandler *vsh = nsViewSourceHandler::GetInstance();
9848 NS_ENSURE_TRUE(vsh,NS_ERROR_FAILURE);
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 }
9866 nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
9867 do_QueryInterface(channel);
9868 if (appCacheChannel) {
9869 // Any document load should not inherit application cache.
9870 appCacheChannel->SetInheritApplicationCache(false);
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);
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 }
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);
9895 channel->SetOriginalURI(aURI);
9896 if (aTypeHint && *aTypeHint) {
9897 channel->SetContentType(nsDependentCString(aTypeHint));
9898 mContentTypeHint = aTypeHint;
9899 } else {
9900 mContentTypeHint.Truncate();
9901 }
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 }
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 }
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 }
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 }
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));
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 }
9982 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
9983 NS_ASSERTION(uploadChannel, "http must support nsIUploadChannel");
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 }
10026 nsCOMPtr<nsIPrincipal> ownerPrincipal;
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 }
10039 nsContentUtils::SetUpChannelOwner(ownerPrincipal, channel, aURI, true,
10040 (mSandboxFlags & SANDBOXED_ORIGIN) ||
10041 isSrcdoc);
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 }
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 }
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 }
10067 rv = DoChannelLoad(channel, uriLoader, aBypassClassifier);
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 }
10080 return rv;
10081 }
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.
10093 nsAutoCString *buf = static_cast<nsAutoCString *>(closure);
10094 buf->Append(fromRawSegment, count);
10096 // Indicate that we have consumed all of aFromSegment
10097 *writeCount = count;
10098 return NS_OK;
10099 }
10101 NS_IMETHODIMP
10102 nsDocShell::AddHeadersToChannel(nsIInputStream *aHeadersData,
10103 nsIChannel *aGenericChannel)
10104 {
10105 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aGenericChannel);
10106 NS_ENSURE_STATE(httpChannel);
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);
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;
10122 //
10123 // Iterate over the headersString: for each "\r\n" delimited chunk,
10124 // add the value as a header to the nsIHttpChannel
10125 //
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;
10133 const nsCSubstring &oneHeader = StringHead(headersString, crlf);
10135 colon = oneHeader.FindChar(':');
10136 if (colon == kNotFound)
10137 return NS_ERROR_UNEXPECTED;
10139 headerName = StringHead(oneHeader, colon);
10140 headerValue = Substring(oneHeader, colon + 1);
10142 headerName.Trim(kWhitespace);
10143 headerValue.Trim(kWhitespace);
10145 headersString.Cut(0, crlf + 2);
10147 //
10148 // FINALLY: we can set the header!
10149 //
10151 rv = httpChannel->SetRequestHeader(headerName, headerValue, true);
10152 NS_ENSURE_SUCCESS(rv, rv);
10153 }
10155 NS_NOTREACHED("oops");
10156 return NS_ERROR_UNEXPECTED;
10157 }
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;
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 }
10181 if (!uriModified)
10182 loadFlags |= nsIRequest::VALIDATE_NEVER;
10183 }
10184 break;
10186 case LOAD_RELOAD_CHARSET_CHANGE:
10187 loadFlags |= nsIRequest::LOAD_FROM_CACHE;
10188 break;
10190 case LOAD_RELOAD_NORMAL:
10191 case LOAD_REFRESH:
10192 loadFlags |= nsIRequest::VALIDATE_ALWAYS;
10193 break;
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;
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 }
10225 if (!aBypassClassifier) {
10226 loadFlags |= nsIChannel::LOAD_CLASSIFY_URI;
10227 }
10229 (void) aChannel->SetLoadFlags(loadFlags);
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);
10241 return NS_OK;
10242 }
10244 nsresult
10245 nsDocShell::ScrollToAnchor(nsACString & aCurHash, nsACString & aNewHash,
10246 uint32_t aLoadType)
10247 {
10248 if (!mCurrentURI) {
10249 return NS_OK;
10250 }
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 }
10259 nsIScrollableFrame* rootScroll = shell->GetRootScrollFrameAsScrollable();
10260 if (rootScroll) {
10261 rootScroll->ClearDidHistoryRestore();
10262 }
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 }
10273 // Take the '#' off aNewHash to get the ref name. (aNewHash might be empty,
10274 // but that's fine.)
10275 nsDependentCSubstring newHashName(aNewHash, 1);
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.
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;
10286 char *str = ToNewCString(newHashName);
10287 if (!str) {
10288 return NS_ERROR_OUT_OF_MEMORY;
10289 }
10291 // nsUnescape modifies the string that is passed into it.
10292 nsUnescape(str);
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
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);
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)) {
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();
10320 nsCOMPtr<nsITextToSubURI> textToSubURI =
10321 do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
10322 NS_ENSURE_SUCCESS(rv, rv);
10324 // Unescape and convert to unicode
10325 nsXPIDLString uStr;
10327 rv = textToSubURI->UnEscapeAndConvert(PromiseFlatCString(aCharset).get(),
10328 PromiseFlatCString(newHashName).get(),
10329 getter_Copies(uStr));
10330 NS_ENSURE_SUCCESS(rv, rv);
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 {
10345 // Tell the shell it's at an anchor, without scrolling.
10346 shell->GoToAnchor(EmptyString(), false);
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 }
10360 return NS_OK;
10361 }
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 }
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");
10384 #if defined(PR_LOGGING) && defined(DEBUG)
10385 if (PR_LOG_TEST(gDocShellLog, PR_LOG_DEBUG)) {
10386 nsAutoCString spec;
10387 aURI->GetSpec(spec);
10389 nsAutoCString chanName;
10390 if (aChannel)
10391 aChannel->GetName(chanName);
10392 else
10393 chanName.AssignLiteral("<no channel>");
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
10401 bool equalUri = false;
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));
10409 // Check if the HTTPChannel is hiding under a multiPartChannel
10410 if (!httpChannel) {
10411 GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
10412 }
10414 if (httpChannel) {
10415 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
10416 if (uploadChannel) {
10417 uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
10418 }
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 }
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);
10434 // We don't update session history on reload.
10435 bool updateSHistory = updateGHistory && (!(aLoadType & LOAD_CMD_RELOAD));
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
10450 // Check if the url to be loaded is the same as the one already loaded.
10451 if (mCurrentURI)
10452 aURI->Equals(mCurrentURI, &equalUri);
10454 #ifdef DEBUG
10455 bool shAvailable = (rootSH != nullptr);
10457 // XXX This log message is almost useless because |updateSHistory|
10458 // and |updateGHistory| are not correct at this point.
10460 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
10461 (" shAvailable=%i updateSHistory=%i updateGHistory=%i"
10462 " equalURI=%i\n",
10463 shAvailable, updateSHistory, updateGHistory, equalUri));
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
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 }
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 }
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!");
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);
10529 // Since we're force-reloading, clear all the sub frame history.
10530 ClearFrameHistory(mLSHE);
10531 ClearFrameHistory(mOSHE);
10532 }
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 }
10547 if (aLoadType == LOAD_REFRESH) {
10548 ClearFrameHistory(mLSHE);
10549 ClearFrameHistory(mOSHE);
10550 }
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 }
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;
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 }
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));
10586 AddURIVisit(aURI, referrer, previousURI, previousFlags, responseStatus);
10587 }
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 }
10604 // aCloneSHChildren exactly means "we are not loading a new document".
10605 uint32_t locationFlags = aCloneSHChildren?
10606 uint32_t(LOCATION_CHANGE_SAME_DOCUMENT) : 0;
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 }
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);
10628 // Pass false for aCloneSHChildren, since we're loading a new page here.
10629 return OnNewURI(uri, aChannel, nullptr, mLoadType, aFireOnLocationChange,
10630 aAddToGlobalHistory, false);
10632 }
10634 void
10635 nsDocShell::SetReferrerURI(nsIURI * aURI)
10636 {
10637 mReferrerURI = aURI; // This assigment addrefs
10638 }
10640 //*****************************************************************************
10641 // nsDocShell: Session History
10642 //*****************************************************************************
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
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.
10683 nsresult rv;
10685 // Don't clobber the load type of an existing network load.
10686 AutoRestore<uint32_t> loadTypeResetter(mLoadType);
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 }
10695 nsCOMPtr<nsIDocument> document = do_GetInterface(GetAsSupports(this));
10696 NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
10698 // Step 1: Serialize aData using structured clone.
10699 nsCOMPtr<nsIStructuredCloneContainer> scContainer;
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();
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);
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);
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();
10734 bool principalsEqual = false;
10735 origPrincipal->Equals(newPrincipal, &principalsEqual);
10736 NS_ENSURE_TRUE(principalsEqual, NS_ERROR_DOM_SECURITY_ERR);
10737 }
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 }
10747 uint64_t scSize;
10748 rv = scContainer->GetSerializedNBytes(&scSize);
10749 NS_ENSURE_SUCCESS(rv, rv);
10751 NS_ENSURE_TRUE(scSize <= (uint32_t)maxStateObjSize,
10752 NS_ERROR_ILLEGAL_VALUE);
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
10772 nsIURI* docBaseURI = document->GetDocBaseURI();
10773 if (!docBaseURI)
10774 return NS_ERROR_FAILURE;
10776 nsAutoCString spec;
10777 docBaseURI->GetSpec(spec);
10779 nsAutoCString charset;
10780 rv = docBaseURI->GetOriginCharset(charset);
10781 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
10783 rv = NS_NewURI(getter_AddRefs(newURI), aURL,
10784 charset.get(), docBaseURI);
10786 // 2b: If 2a fails, raise a SECURITY_ERR
10787 if (NS_FAILED(rv)) {
10788 return NS_ERROR_DOM_SECURITY_ERR;
10789 }
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.
10802 nsCOMPtr<nsIScriptSecurityManager> secMan =
10803 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
10804 NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE);
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)) {
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);
10826 if (!docScriptObj) {
10827 return NS_ERROR_DOM_SECURITY_ERR;
10828 }
10830 nsCOMPtr<nsIPrincipal> principal = docScriptObj->GetPrincipal();
10832 if (!principal ||
10833 NS_FAILED(principal->CheckMayLoad(newURI, true, false))) {
10835 return NS_ERROR_DOM_SECURITY_ERR;
10836 }
10837 }
10839 if (currentURI) {
10840 currentURI->Equals(newURI, &equalURIs);
10841 }
10842 else {
10843 equalURIs = false;
10844 }
10846 } // end of same-origin check
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;
10855 mLoadType = LOAD_PUSHSTATE;
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);
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);
10871 NS_ENSURE_TRUE(newSHEntry, NS_ERROR_FAILURE);
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);
10878 // Set the new SHEntry's title (bug 655273).
10879 nsString title;
10880 mOSHE->GetTitle(getter_Copies(title));
10881 newSHEntry->SetTitle(title);
10883 // AddToSessionHistory may not modify mOSHE. In case it doesn't,
10884 // we'll just set mOSHE here.
10885 mOSHE = newSHEntry;
10887 } else {
10888 newSHEntry = mOSHE;
10889 newSHEntry->SetURI(newURI);
10890 }
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);
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);
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);
10915 nsCOMPtr<nsISHistoryInternal> internalSH =
10916 do_QueryInterface(rootSH);
10917 NS_ENSURE_TRUE(internalSH, NS_ERROR_UNEXPECTED);
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);
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 }
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);
10952 AddURIVisit(newURI, oldURI, oldURI, 0);
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 }
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);
10975 return NS_OK;
10976 }
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;
10988 rv = aURI->GetScheme(buf);
10989 if (NS_FAILED(rv))
10990 return false;
10992 if (buf.Equals("about")) {
10993 rv = aURI->GetPath(buf);
10994 if (NS_FAILED(rv))
10995 return false;
10997 if (buf.Equals("blank")) {
10998 return false;
10999 }
11000 }
11002 rv = Preferences::GetDefaultCString("browser.newtab.url", &pref);
11004 if (NS_FAILED(rv)) {
11005 return true;
11006 }
11008 rv = aURI->GetSpec(buf);
11009 NS_ENSURE_SUCCESS(rv, true);
11011 return !buf.Equals(pref);
11012 }
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");
11022 #if defined(PR_LOGGING) && defined(DEBUG)
11023 if (PR_LOG_TEST(gDocShellLog, PR_LOG_DEBUG)) {
11024 nsAutoCString spec;
11025 aURI->GetSpec(spec);
11027 nsAutoCString chanName;
11028 if (aChannel)
11029 aChannel->GetName(chanName);
11030 else
11031 chanName.AssignLiteral("<no channel>");
11033 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
11034 ("nsDocShell[%p]::AddToSessionHistory(\"%s\", [%s])\n", this, spec.get(),
11035 chanName.get()));
11036 }
11037 #endif
11039 nsresult rv = NS_OK;
11040 nsCOMPtr<nsISHEntry> entry;
11041 bool shouldPersist;
11043 shouldPersist = ShouldAddToSessionHistory(aURI);
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 }
11071 // Create a new entry if necessary.
11072 if (!entry) {
11073 entry = do_CreateInstance(NS_SHENTRY_CONTRACTID);
11075 if (!entry) {
11076 return NS_ERROR_OUT_OF_MEMORY;
11077 }
11078 }
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);
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));
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));
11110 discardLayoutState = ShouldDiscardLayoutState(httpChannel);
11111 }
11112 aChannel->GetOwner(getter_AddRefs(owner));
11113 }
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);
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 }
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 }
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 }
11209 return rv;
11210 }
11213 NS_IMETHODIMP
11214 nsDocShell::LoadHistoryEntry(nsISHEntry * aEntry, uint32_t aLoadType)
11215 {
11216 if (!IsNavigationAllowed()) {
11217 return NS_OK;
11218 }
11220 nsCOMPtr<nsIURI> uri;
11221 nsCOMPtr<nsIInputStream> postData;
11222 nsCOMPtr<nsIURI> referrerURI;
11223 nsAutoCString contentType;
11224 nsCOMPtr<nsISupports> owner;
11226 NS_ENSURE_TRUE(aEntry, NS_ERROR_FAILURE);
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);
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);
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 }
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 }
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;
11280 // If the user pressed cancel in the dialog, return. We're done here.
11281 if (!repost)
11282 return NS_BINDING_ABORTED;
11283 }
11285 // Do not inherit owner from document (security-critical!);
11286 uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
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 }
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 }
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 }
11334 return NS_OK;
11335 }
11337 NS_IMETHODIMP nsDocShell::PersistLayoutHistoryState()
11338 {
11339 nsresult rv = NS_OK;
11341 if (mOSHE) {
11342 nsCOMPtr<nsIPresShell> shell = GetPresShell();
11343 if (shell) {
11344 nsCOMPtr<nsILayoutHistoryState> layoutState;
11345 rv = shell->CaptureHistoryState(getter_AddRefs(layoutState));
11346 }
11347 }
11349 return rv;
11350 }
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);
11360 nsCOMPtr<nsISHContainer> container(do_QueryInterface(aRootEntry));
11361 if (!container)
11362 return NS_ERROR_FAILURE;
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 }
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.
11382 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(aRootShell->mChildList);
11383 while (iter.HasMore()) {
11384 nsDocShell *child = static_cast<nsDocShell*>(iter.GetNext());
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 }
11396 return NS_OK;
11397 }
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) { }
11409 uint32_t cloneID;
11410 bool cloneChildren;
11411 nsISHEntry *replaceEntry;
11412 nsISHEntry *destTreeParent;
11413 nsCOMPtr<nsISHEntry> resultEntry;
11414 };
11416 /* static */ nsresult
11417 nsDocShell::CloneAndReplaceChild(nsISHEntry *aEntry, nsDocShell *aShell,
11418 int32_t aEntryIndex, void *aData)
11419 {
11420 nsCOMPtr<nsISHEntry> dest;
11422 CloneAndReplaceData *data = static_cast<CloneAndReplaceData*>(aData);
11423 uint32_t cloneID = data->cloneID;
11424 nsISHEntry *replaceEntry = data->replaceEntry;
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 }
11435 uint32_t srcID;
11436 aEntry->GetID(&srcID);
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);
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 }
11458 if (srcID != cloneID && aShell) {
11459 aShell->SwapHistoryEntries(aEntry, dest);
11460 }
11462 if (container)
11463 container->AddChild(dest, aEntryIndex);
11465 data->resultEntry = dest;
11466 return rv;
11467 }
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);
11480 CloneAndReplaceData data(aCloneID, aReplaceEntry, aCloneChildren, nullptr);
11481 nsresult rv = CloneAndReplaceChild(aSrcEntry, aSrcShell, 0, &data);
11483 data.resultEntry.swap(*aResultEntry);
11484 return rv;
11485 }
11487 void
11488 nsDocShell::SwapHistoryEntries(nsISHEntry *aOldEntry, nsISHEntry *aNewEntry)
11489 {
11490 if (aOldEntry == mOSHE)
11491 mOSHE = aNewEntry;
11493 if (aOldEntry == mLSHE)
11494 mLSHE = aNewEntry;
11495 }
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 };
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;
11514 if (!aShell || aShell == ignoreShell)
11515 return NS_OK;
11517 nsISHEntry *destTreeRoot = data->destTreeRoot;
11519 nsCOMPtr<nsISHEntry> destEntry;
11520 nsCOMPtr<nsISHContainer> container =
11521 do_QueryInterface(data->destTreeParent);
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.
11529 uint32_t targetID, id;
11530 aEntry->GetID(&targetID);
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;
11545 entry->GetID(&id);
11546 if (id == targetID) {
11547 destEntry.swap(entry);
11548 break;
11549 }
11550 }
11551 }
11552 } else {
11553 destEntry = destTreeRoot;
11554 }
11556 aShell->SwapHistoryEntries(aEntry, destEntry);
11558 // Now handle the children of aEntry.
11559 SwapEntriesData childData = { ignoreShell, destTreeRoot, destEntry };
11560 return WalkHistoryEntries(aEntry, aShell,
11561 SetChildHistoryEntry, &childData);
11562 }
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 }
11575 return result;
11576 }
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.
11589 nsISHEntry *newRootEntry = GetRootSHEntry(aEntry);
11590 if (newRootEntry) {
11591 // newRootEntry is now the new root entry.
11592 // Find the old root entry as well.
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);
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 }
11618 *aPtr = aEntry;
11619 }
11622 nsresult
11623 nsDocShell::GetRootSessionHistory(nsISHistory ** aReturn)
11624 {
11625 nsresult rv;
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 }
11639 nsresult
11640 nsDocShell::GetHttpChannel(nsIChannel * aChannel, nsIHttpChannel ** aReturn)
11641 {
11642 NS_ENSURE_ARG_POINTER(aReturn);
11643 if (!aChannel)
11644 return NS_ERROR_FAILURE;
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 }
11657 bool
11658 nsDocShell::ShouldDiscardLayoutState(nsIHttpChannel * aChannel)
11659 {
11660 // By default layout State will be saved.
11661 if (!aChannel)
11662 return false;
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);
11671 return (noStore || (noCache && securityInfo));
11672 }
11674 NS_IMETHODIMP nsDocShell::GetEditor(nsIEditor * *aEditor)
11675 {
11676 NS_ENSURE_ARG_POINTER(aEditor);
11678 if (!mEditorData) {
11679 *aEditor = nullptr;
11680 return NS_OK;
11681 }
11683 return mEditorData->GetEditor(aEditor);
11684 }
11686 NS_IMETHODIMP nsDocShell::SetEditor(nsIEditor * aEditor)
11687 {
11688 nsresult rv = EnsureEditorData();
11689 if (NS_FAILED(rv)) return rv;
11691 return mEditorData->SetEditor(aEditor);
11692 }
11695 NS_IMETHODIMP nsDocShell::GetEditable(bool *aEditable)
11696 {
11697 NS_ENSURE_ARG_POINTER(aEditable);
11698 *aEditable = mEditorData && mEditorData->GetEditable();
11699 return NS_OK;
11700 }
11703 NS_IMETHODIMP nsDocShell::GetHasEditingSession(bool *aHasEditingSession)
11704 {
11705 NS_ENSURE_ARG_POINTER(aHasEditingSession);
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 }
11718 return NS_OK;
11719 }
11721 NS_IMETHODIMP nsDocShell::MakeEditable(bool inWaitForUriLoad)
11722 {
11723 nsresult rv = EnsureEditorData();
11724 if (NS_FAILED(rv)) return rv;
11726 return mEditorData->MakeEditable(inWaitForUriLoad);
11727 }
11729 bool
11730 nsDocShell::ChannelIsPost(nsIChannel* aChannel)
11731 {
11732 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
11733 if (!httpChannel) {
11734 return false;
11735 }
11737 nsAutoCString method;
11738 httpChannel->GetRequestMethod(method);
11739 return method.Equals("POST");
11740 }
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 }
11752 nsresult rv = props->GetPropertyAsInterface(
11753 NS_LITERAL_STRING("docshell.previousURI"),
11754 NS_GET_IID(nsIURI),
11755 reinterpret_cast<void**>(aURI)
11756 );
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 );
11770 NS_WARN_IF_FALSE(
11771 NS_SUCCEEDED(rv),
11772 "Could not fetch previous flags, URI will be treated like referrer"
11773 );
11774 }
11775 }
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 }
11787 props->SetPropertyAsInterface(NS_LITERAL_STRING("docshell.previousURI"),
11788 aURI);
11789 props->SetPropertyAsUint32(NS_LITERAL_STRING("docshell.previousFlags"),
11790 aChannelRedirectFlags);
11791 }
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");
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 }
11811 nsCOMPtr<IHistory> history = services::GetHistoryService();
11813 if (history) {
11814 uint32_t visitURIFlags = 0;
11816 if (!IsFrame()) {
11817 visitURIFlags |= IHistory::TOP_LEVEL;
11818 }
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 }
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 }
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 }
11852 //*****************************************************************************
11853 // nsDocShell: Helper Routines
11854 //*****************************************************************************
11856 NS_IMETHODIMP
11857 nsDocShell::SetLoadType(uint32_t aLoadType)
11858 {
11859 mLoadType = aLoadType;
11860 return NS_OK;
11861 }
11863 NS_IMETHODIMP
11864 nsDocShell::GetLoadType(uint32_t * aLoadType)
11865 {
11866 *aLoadType = mLoadType;
11867 return NS_OK;
11868 }
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 }
11879 nsCOMPtr<nsIStringBundleService> stringBundleService =
11880 mozilla::services::GetStringBundleService();
11881 if (!stringBundleService)
11882 return NS_ERROR_FAILURE;
11884 nsCOMPtr<nsIStringBundle> appBundle;
11885 nsresult rv = stringBundleService->CreateBundle(kAppstringsBundleURL,
11886 getter_AddRefs(appBundle));
11887 NS_ENSURE_SUCCESS(rv, rv);
11889 nsCOMPtr<nsIStringBundle> brandBundle;
11890 rv = stringBundleService->CreateBundle(kBrandBundleURL, getter_AddRefs(brandBundle));
11891 NS_ENSURE_SUCCESS(rv, rv);
11893 NS_ASSERTION(prompter && brandBundle && appBundle,
11894 "Unable to set up repost prompter.");
11896 nsXPIDLString brandName;
11897 rv = brandBundle->GetStringFromName(MOZ_UTF16("brandShortName"),
11898 getter_Copies(brandName));
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;
11915 rv = appBundle->GetStringFromName(MOZ_UTF16("resendButton.label"),
11916 getter_Copies(button0Title));
11917 if (NS_FAILED(rv)) return rv;
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;
11930 *aRepost = (buttonPressed == 0);
11931 return NS_OK;
11932 }
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);
11941 nsCOMPtr<nsIStringBundleService> stringBundleService =
11942 mozilla::services::GetStringBundleService();
11943 NS_ENSURE_TRUE(stringBundleService, NS_ERROR_FAILURE);
11945 NS_ENSURE_SUCCESS(stringBundleService->
11946 CreateBundle(kAppstringsBundleURL,
11947 aStringBundle),
11948 NS_ERROR_FAILURE);
11950 return NS_OK;
11951 }
11953 NS_IMETHODIMP
11954 nsDocShell::GetChildOffset(nsIDOMNode * aChild, nsIDOMNode * aParent,
11955 int32_t * aOffset)
11956 {
11957 NS_ENSURE_ARG_POINTER(aChild || aParent);
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);
11964 int32_t i = 0;
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);
11972 if (childNode.get() == aChild) {
11973 *aOffset = i;
11974 return NS_OK;
11975 }
11976 }
11978 return NS_ERROR_FAILURE;
11979 }
11981 nsIScrollableFrame *
11982 nsDocShell::GetRootScrollFrame()
11983 {
11984 nsCOMPtr<nsIPresShell> shell = GetPresShell();
11985 NS_ENSURE_TRUE(shell, nullptr);
11987 return shell->GetRootScrollFrameAsScrollableExternal();
11988 }
11990 NS_IMETHODIMP
11991 nsDocShell::EnsureScriptEnvironment()
11992 {
11993 if (mScriptGlobal)
11994 return NS_OK;
11996 if (mIsBeingDestroyed) {
11997 return NS_ERROR_NOT_AVAILABLE;
11998 }
12000 #ifdef DEBUG
12001 NS_ASSERTION(!mInEnsureScriptEnv,
12002 "Infinite loop! Calling EnsureScriptEnvironment() from "
12003 "within EnsureScriptEnvironment()!");
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
12011 nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
12012 NS_ENSURE_TRUE(browserChrome, NS_ERROR_NOT_AVAILABLE);
12014 uint32_t chromeFlags;
12015 browserChrome->GetChromeFlags(&chromeFlags);
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 }
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);
12035 mScriptGlobal->SetDocShell(this);
12037 // Ensure the script object is set up to run script.
12038 return mScriptGlobal->EnsureScriptEnvironment();
12039 }
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 }
12054 return mEditorData ? NS_OK : NS_ERROR_NOT_AVAILABLE;
12055 }
12057 nsresult
12058 nsDocShell::EnsureTransferableHookData()
12059 {
12060 if (!mTransferableHookData) {
12061 mTransferableHookData = new nsTransferableHookData();
12062 if (!mTransferableHookData) return NS_ERROR_OUT_OF_MEMORY;
12063 }
12065 return NS_OK;
12066 }
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 }
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.
12082 nsIScriptGlobalObject* scriptGO = GetScriptGlobalObject();
12083 NS_ENSURE_TRUE(scriptGO, NS_ERROR_UNEXPECTED);
12085 // default to our window
12086 nsCOMPtr<nsPIDOMWindow> ourWindow = do_QueryInterface(scriptGO);
12087 nsCOMPtr<nsPIDOMWindow> windowToSearch;
12088 nsFocusManager::GetFocusedDescendant(ourWindow, true, getter_AddRefs(windowToSearch));
12090 nsCOMPtr<nsIWebBrowserFindInFrames> findInFrames = do_QueryInterface(mFind);
12091 if (!findInFrames) return NS_ERROR_NO_INTERFACE;
12093 rv = findInFrames->SetRootSearchFrame(ourWindow);
12094 if (NS_FAILED(rv)) return rv;
12095 rv = findInFrames->SetCurrentSearchFrame(windowToSearch);
12096 if (NS_FAILED(rv)) return rv;
12098 return NS_OK;
12099 }
12101 bool
12102 nsDocShell::IsFrame()
12103 {
12104 nsCOMPtr<nsIDocShellTreeItem> parent;
12105 GetSameTypeParent(getter_AddRefs(parent));
12106 return !!parent;
12107 }
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 }
12119 NS_IMETHODIMP
12120 nsDocShell::GetIsExecutingOnLoadHandler(bool *aResult)
12121 {
12122 NS_ENSURE_ARG(aResult);
12123 *aResult = mIsExecutingOnLoadHandler;
12124 return NS_OK;
12125 }
12127 NS_IMETHODIMP
12128 nsDocShell::GetLayoutHistoryState(nsILayoutHistoryState **aLayoutHistoryState)
12129 {
12130 if (mOSHE)
12131 mOSHE->GetLayoutHistoryState(aLayoutHistoryState);
12132 return NS_OK;
12133 }
12135 NS_IMETHODIMP
12136 nsDocShell::SetLayoutHistoryState(nsILayoutHistoryState *aLayoutHistoryState)
12137 {
12138 if (mOSHE)
12139 mOSHE->SetLayoutHistoryState(aLayoutHistoryState);
12140 return NS_OK;
12141 }
12143 //*****************************************************************************
12144 //*** nsRefreshTimer: Object Management
12145 //*****************************************************************************
12147 nsRefreshTimer::nsRefreshTimer()
12148 : mDelay(0), mRepeat(false), mMetaRefresh(false)
12149 {
12150 }
12152 nsRefreshTimer::~nsRefreshTimer()
12153 {
12154 }
12156 //*****************************************************************************
12157 // nsRefreshTimer::nsISupports
12158 //*****************************************************************************
12160 NS_IMPL_ADDREF(nsRefreshTimer)
12161 NS_IMPL_RELEASE(nsRefreshTimer)
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
12168 ///*****************************************************************************
12169 // nsRefreshTimer::nsITimerCallback
12170 //******************************************************************************
12171 NS_IMETHODIMP
12172 nsRefreshTimer::Notify(nsITimer * aTimer)
12173 {
12174 NS_ASSERTION(mDocShell, "DocShell is somehow null");
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 }
12185 //*****************************************************************************
12186 // nsDocShell::InterfaceRequestorProxy
12187 //*****************************************************************************
12188 nsDocShell::InterfaceRequestorProxy::InterfaceRequestorProxy(nsIInterfaceRequestor* p)
12189 {
12190 if (p) {
12191 mWeakPtr = do_GetWeakReference(p);
12192 }
12193 }
12195 nsDocShell::InterfaceRequestorProxy::~InterfaceRequestorProxy()
12196 {
12197 mWeakPtr = nullptr;
12198 }
12200 NS_IMPL_ISUPPORTS(nsDocShell::InterfaceRequestorProxy, nsIInterfaceRequestor)
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 }
12214 nsresult
12215 nsDocShell::SetBaseUrlForWyciwyg(nsIContentViewer * aContentViewer)
12216 {
12217 if (!aContentViewer)
12218 return NS_ERROR_FAILURE;
12220 nsCOMPtr<nsIURI> baseURI;
12221 nsresult rv = NS_ERROR_NOT_AVAILABLE;
12223 if (sURIFixup)
12224 rv = sURIFixup->CreateExposableURI(mCurrentURI,
12225 getter_AddRefs(baseURI));
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 }
12237 //*****************************************************************************
12238 // nsDocShell::nsIAuthPromptProvider
12239 //*****************************************************************************
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);
12248 if (!mAllowAuth && !priorityPrompt)
12249 return NS_ERROR_NOT_AVAILABLE;
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);
12257 rv = EnsureScriptEnvironment();
12258 NS_ENSURE_SUCCESS(rv, rv);
12260 // Get the an auth prompter for our window so that the parenting
12261 // of the dialogs works as it should when using tabs.
12263 return wwatch->GetPrompt(mScriptGlobal, iid,
12264 reinterpret_cast<void**>(aResult));
12265 }
12267 //*****************************************************************************
12268 // nsDocShell::nsILoadContext
12269 //*****************************************************************************
12270 NS_IMETHODIMP
12271 nsDocShell::GetAssociatedWindow(nsIDOMWindow** aWindow)
12272 {
12273 CallGetInterface(this, aWindow);
12274 return NS_OK;
12275 }
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 }
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 }
12296 nsCOMPtr<nsIDOMWindow> top;
12297 win->GetScriptableTop(getter_AddRefs(top));
12298 NS_ENSURE_TRUE(top, NS_ERROR_FAILURE);
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 }
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 }
12322 *aIsOfType = false;
12323 return NS_OK;
12324 }
12326 NS_IMETHODIMP
12327 nsDocShell::GetIsContent(bool *aIsContent)
12328 {
12329 *aIsContent = (mItemType == typeContent);
12330 return NS_OK;
12331 }
12333 bool
12334 nsDocShell::IsOKToLoadURI(nsIURI* aURI)
12335 {
12336 NS_PRECONDITION(aURI, "Must have a URI!");
12338 if (!mFiredUnloadEvent) {
12339 return true;
12340 }
12342 if (!mLoadingURI) {
12343 return false;
12344 }
12346 nsCOMPtr<nsIScriptSecurityManager> secMan =
12347 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
12348 return
12349 secMan &&
12350 NS_SUCCEEDED(secMan->CheckSameOriginURI(aURI, mLoadingURI, false));
12351 }
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;
12363 NS_ENSURE_TRUE(mScriptGlobal, NS_ERROR_FAILURE);
12365 nsCOMPtr<nsPIWindowRoot> root = mScriptGlobal->GetTopWindowRoot();
12366 NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
12368 return root->GetControllerForCommand(inCommand, outController);
12369 }
12371 NS_IMETHODIMP
12372 nsDocShell::IsCommandEnabled(const char * inCommand, bool* outEnabled)
12373 {
12374 NS_ENSURE_ARG_POINTER(outEnabled);
12375 *outEnabled = false;
12377 nsresult rv = NS_ERROR_FAILURE;
12379 nsCOMPtr<nsIController> controller;
12380 rv = GetControllerForCommand (inCommand, getter_AddRefs(controller));
12381 if (controller)
12382 rv = controller->IsCommandEnabled(inCommand, outEnabled);
12384 return rv;
12385 }
12387 NS_IMETHODIMP
12388 nsDocShell::DoCommand(const char * inCommand)
12389 {
12390 nsresult rv = NS_ERROR_FAILURE;
12392 nsCOMPtr<nsIController> controller;
12393 rv = GetControllerForCommand(inCommand, getter_AddRefs(controller));
12394 if (controller)
12395 rv = controller->DoCommand(inCommand);
12397 return rv;
12398 }
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;
12409 nsCOMPtr<nsIDOMWindow> domWindow =
12410 do_GetInterface(static_cast<nsIInterfaceRequestor *>(this));
12412 nsresult rv = commandUpdater->Init(domWindow);
12413 if (NS_SUCCEEDED(rv))
12414 mCommandManager = do_QueryInterface(commandUpdater);
12415 }
12417 return mCommandManager ? NS_OK : NS_ERROR_FAILURE;
12418 }
12420 NS_IMETHODIMP
12421 nsDocShell::CanCutSelection(bool* aResult)
12422 {
12423 return IsCommandEnabled("cmd_cut", aResult);
12424 }
12426 NS_IMETHODIMP
12427 nsDocShell::CanCopySelection(bool* aResult)
12428 {
12429 return IsCommandEnabled("cmd_copy", aResult);
12430 }
12432 NS_IMETHODIMP
12433 nsDocShell::CanCopyLinkLocation(bool* aResult)
12434 {
12435 return IsCommandEnabled("cmd_copyLink", aResult);
12436 }
12438 NS_IMETHODIMP
12439 nsDocShell::CanCopyImageLocation(bool* aResult)
12440 {
12441 return IsCommandEnabled("cmd_copyImageLocation",
12442 aResult);
12443 }
12445 NS_IMETHODIMP
12446 nsDocShell::CanCopyImageContents(bool* aResult)
12447 {
12448 return IsCommandEnabled("cmd_copyImageContents",
12449 aResult);
12450 }
12452 NS_IMETHODIMP
12453 nsDocShell::CanPaste(bool* aResult)
12454 {
12455 return IsCommandEnabled("cmd_paste", aResult);
12456 }
12458 NS_IMETHODIMP
12459 nsDocShell::CutSelection(void)
12460 {
12461 return DoCommand ( "cmd_cut" );
12462 }
12464 NS_IMETHODIMP
12465 nsDocShell::CopySelection(void)
12466 {
12467 return DoCommand ( "cmd_copy" );
12468 }
12470 NS_IMETHODIMP
12471 nsDocShell::CopyLinkLocation(void)
12472 {
12473 return DoCommand ( "cmd_copyLink" );
12474 }
12476 NS_IMETHODIMP
12477 nsDocShell::CopyImageLocation(void)
12478 {
12479 return DoCommand ( "cmd_copyImageLocation" );
12480 }
12482 NS_IMETHODIMP
12483 nsDocShell::CopyImageContents(void)
12484 {
12485 return DoCommand ( "cmd_copyImageContents" );
12486 }
12488 NS_IMETHODIMP
12489 nsDocShell::Paste(void)
12490 {
12491 return DoCommand ( "cmd_paste" );
12492 }
12494 NS_IMETHODIMP
12495 nsDocShell::SelectAll(void)
12496 {
12497 return DoCommand ( "cmd_selectAll" );
12498 }
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 }
12512 //----------------------------------------------------------------------
12514 // link handling
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);
12526 NS_IMETHOD Run() {
12527 nsAutoPopupStatePusher popupStatePusher(mPopupState);
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 }
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 };
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 }
12571 //----------------------------------------
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");
12584 if (!IsOKToLoadURI(aURI)) {
12585 return NS_OK;
12586 }
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 }
12598 if (aContent->IsEditable()) {
12599 return NS_OK;
12600 }
12602 nsresult rv = NS_ERROR_FAILURE;
12603 nsAutoString target;
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 }
12613 if (NS_FAILED(rv))
12614 target = aTargetSpec;
12616 nsCOMPtr<nsIRunnable> ev =
12617 new OnLinkClickEvent(this, aContent, aURI, target.get(), aFileName,
12618 aPostDataStream, aHeadersDataStream, aIsTrusted);
12619 return NS_DispatchToCurrentThread(ev);
12620 }
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 }
12640 if (!IsOKToLoadURI(aURI)) {
12641 return NS_OK;
12642 }
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 }
12651 if (aContent->IsEditable()) {
12652 return NS_OK;
12653 }
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 }
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);
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 }
12693 nsCOMPtr<nsIURI> referer = refererDoc->GetDocumentURI();
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.
12698 nsAutoString target(aTargetSpec);
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 }
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 }
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 }
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 }
12753 nsCOMPtr<nsIWebBrowserChrome2> browserChrome2 = do_GetInterface(mTreeOwner);
12754 nsresult rv = NS_ERROR_FAILURE;
12756 nsCOMPtr<nsIWebBrowserChrome> browserChrome;
12757 if (!browserChrome2) {
12758 browserChrome = do_GetInterface(mTreeOwner);
12759 if (!browserChrome)
12760 return rv;
12761 }
12763 nsCOMPtr<nsITextToSubURI> textToSubURI =
12764 do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
12765 if (NS_FAILED(rv))
12766 return rv;
12768 // use url origin charset to unescape the URL
12769 nsAutoCString charset;
12770 rv = aURI->GetOriginCharset(charset);
12771 NS_ENSURE_SUCCESS(rv, rv);
12773 nsAutoCString spec;
12774 rv = aURI->GetSpec(spec);
12775 NS_ENSURE_SUCCESS(rv, rv);
12777 nsAutoString uStr;
12778 rv = textToSubURI->UnEscapeURIForUI(charset, spec, uStr);
12779 NS_ENSURE_SUCCESS(rv, rv);
12781 mozilla::net::SeerPredict(aURI, mCurrentURI, nsINetworkSeer::PREDICT_LINK,
12782 this, nullptr);
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 }
12794 NS_IMETHODIMP
12795 nsDocShell::OnLeaveLink()
12796 {
12797 nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
12798 nsresult rv = NS_ERROR_FAILURE;
12800 if (browserChrome) {
12801 rv = browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_LINK,
12802 EmptyString().get());
12803 }
12804 return rv;
12805 }
12807 bool
12808 nsDocShell::ShouldBlockLoadingForBackButton()
12809 {
12810 if (!(mLoadType & LOAD_CMD_HISTORY) ||
12811 EventStateManager::IsHandlingUserInput() ||
12812 !Preferences::GetBool("accessibility.blockjsredirection")) {
12813 return false;
12814 }
12816 bool canGoForward = false;
12817 GetCanGoForward(&canGoForward);
12818 return canGoForward;
12819 }
12821 bool
12822 nsDocShell::PluginsAllowedInCurrentDoc()
12823 {
12824 bool pluginsAllowed = false;
12826 if (!mContentViewer) {
12827 return false;
12828 }
12830 nsIDocument* doc = mContentViewer->GetDocument();
12831 if (!doc) {
12832 return false;
12833 }
12835 doc->GetAllowPlugins(&pluginsAllowed);
12836 return pluginsAllowed;
12837 }
12839 //----------------------------------------------------------------------
12840 // Web Shell Services API
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 {
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 }
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 }
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 }
12915 #ifdef DEBUG
12916 unsigned long nsDocShell::gNumberOfDocShells = 0;
12917 #endif
12919 NS_IMETHODIMP
12920 nsDocShell::GetCanExecuteScripts(bool *aResult)
12921 {
12922 *aResult = mCanExecuteScripts;
12923 return NS_OK;
12924 }
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 }
12937 return NS_OK;
12938 }
12940 NS_IMETHODIMP
12941 nsDocShell::SetIsBrowserInsideApp(uint32_t aContainingAppId)
12942 {
12943 mOwnOrContainingAppId = aContainingAppId;
12944 mFrameType = eFrameTypeBrowser;
12945 return NS_OK;
12946 }
12948 /* [infallible] */ NS_IMETHODIMP
12949 nsDocShell::GetIsBrowserElement(bool* aIsBrowser)
12950 {
12951 *aIsBrowser = (mFrameType == eFrameTypeBrowser);
12952 return NS_OK;
12953 }
12955 /* [infallible] */ NS_IMETHODIMP
12956 nsDocShell::GetIsApp(bool* aIsApp)
12957 {
12958 *aIsApp = (mFrameType == eFrameTypeApp);
12959 return NS_OK;
12960 }
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 }
12975 return NS_OK;
12976 }
12978 nsDocShell::FrameType
12979 nsDocShell::GetInheritedFrameType()
12980 {
12981 if (mFrameType != eFrameTypeRegular) {
12982 return mFrameType;
12983 }
12985 nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
12986 GetSameTypeParent(getter_AddRefs(parentAsItem));
12988 nsCOMPtr<nsIDocShell> parent = do_QueryInterface(parentAsItem);
12989 if (!parent) {
12990 return eFrameTypeRegular;
12991 }
12993 return static_cast<nsDocShell*>(parent.get())->GetInheritedFrameType();
12994 }
12996 /* [infallible] */ NS_IMETHODIMP
12997 nsDocShell::GetIsInBrowserElement(bool* aIsInBrowserElement)
12998 {
12999 *aIsInBrowserElement = (GetInheritedFrameType() == eFrameTypeBrowser);
13000 return NS_OK;
13001 }
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 }
13016 return NS_OK;
13017 }
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 }
13027 nsCOMPtr<nsIDocShell> parent;
13028 GetSameTypeParentIgnoreBrowserAndAppBoundaries(getter_AddRefs(parent));
13030 if (!parent) {
13031 *aAppId = nsIScriptSecurityManager::NO_APP_ID;
13032 return NS_OK;
13033 }
13035 return parent->GetAppId(aAppId);
13036 }
13038 NS_IMETHODIMP
13039 nsDocShell::GetAppManifestURL(nsAString& aAppManifestURL)
13040 {
13041 uint32_t appId;
13042 GetAppId(&appId);
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 }
13054 return NS_OK;
13055 }
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 }
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 }
13088 bool
13089 nsDocShell::IsInvisible()
13090 {
13091 return mInvisible;
13092 }
13094 void
13095 nsDocShell::SetInvisible(bool aInvisible)
13096 {
13097 mInvisible = aInvisible;
13098 }