Bumping manifests a=b2g-bump
[gecko.git] / docshell / base / nsDocShell.cpp
blob34fd45a6afb55f44ad673ee46eb76f248fcd79e8
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 sw=2 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"
25 #include "URIUtils.h"
27 #ifdef MOZ_LOGGING
28 // so we can get logging even in release builds (but only for some things)
29 #define FORCE_PR_LOG 1
30 #endif
32 #include "nsIContent.h"
33 #include "nsIContentInlines.h"
34 #include "nsIDocument.h"
35 #include "nsIDOMDocument.h"
36 #include "nsIDOMElement.h"
37 #include "nsIDOMStorage.h"
38 #include "nsIContentViewer.h"
39 #include "nsIDocumentLoaderFactory.h"
40 #include "nsCURILoader.h"
41 #include "nsDocShellCID.h"
42 #include "nsDOMCID.h"
43 #include "nsNetUtil.h"
44 #include "nsRect.h"
45 #include "prenv.h"
46 #include "nsIDOMWindow.h"
47 #include "nsIGlobalObject.h"
48 #include "nsIWebBrowserChrome.h"
49 #include "nsPoint.h"
50 #include "nsIObserverService.h"
51 #include "nsIPrompt.h"
52 #include "nsIAuthPrompt.h"
53 #include "nsIAuthPrompt2.h"
54 #include "nsIChannelEventSink.h"
55 #include "nsIAsyncVerifyRedirectCallback.h"
56 #include "nsIScriptSecurityManager.h"
57 #include "nsIScriptObjectPrincipal.h"
58 #include "nsIScrollableFrame.h"
59 #include "nsContentPolicyUtils.h" // NS_CheckContentLoadPolicy(...)
60 #include "nsISeekableStream.h"
61 #include "nsAutoPtr.h"
62 #include "nsIWritablePropertyBag2.h"
63 #include "nsIAppShell.h"
64 #include "nsWidgetsCID.h"
65 #include "nsIInterfaceRequestorUtils.h"
66 #include "nsView.h"
67 #include "nsViewManager.h"
68 #include "nsIScriptChannel.h"
69 #include "nsITimedChannel.h"
70 #include "nsIPrivacyTransitionObserver.h"
71 #include "nsIReflowObserver.h"
72 #include "nsIScrollObserver.h"
73 #include "nsIDocShellTreeItem.h"
74 #include "nsIChannel.h"
75 #include "IHistory.h"
76 #include "nsViewSourceHandler.h"
77 #include "nsWhitespaceTokenizer.h"
79 // we want to explore making the document own the load group
80 // so we can associate the document URI with the load group.
81 // until this point, we have an evil hack:
82 #include "nsIHttpChannelInternal.h"
83 #include "nsPILoadGroupInternal.h"
85 // Local Includes
86 #include "nsDocShellLoadInfo.h"
87 #include "nsCDefaultURIFixup.h"
88 #include "nsDocShellEnumerator.h"
89 #include "nsSHistory.h"
90 #include "nsDocShellEditorData.h"
92 // Helper Classes
93 #include "nsError.h"
94 #include "nsEscape.h"
96 // Interfaces Needed
97 #include "nsIUploadChannel.h"
98 #include "nsIUploadChannel2.h"
99 #include "nsIWebProgress.h"
100 #include "nsILayoutHistoryState.h"
101 #include "nsITimer.h"
102 #include "nsISHistoryInternal.h"
103 #include "nsIPrincipal.h"
104 #include "nsNullPrincipal.h"
105 #include "nsISHEntry.h"
106 #include "nsIWindowWatcher.h"
107 #include "nsIPromptFactory.h"
108 #include "nsITransportSecurityInfo.h"
109 #include "nsINSSErrorsService.h"
110 #include "nsIApplicationCacheChannel.h"
111 #include "nsIApplicationCacheContainer.h"
112 #include "nsStreamUtils.h"
113 #include "nsIController.h"
114 #include "nsPICommandUpdater.h"
115 #include "nsIDOMHTMLAnchorElement.h"
116 #include "nsIWebBrowserChrome3.h"
117 #include "nsITabChild.h"
118 #include "nsISiteSecurityService.h"
119 #include "nsStructuredCloneContainer.h"
120 #include "nsIStructuredCloneContainer.h"
121 #ifdef MOZ_PLACES
122 #include "nsIFaviconService.h"
123 #include "mozIAsyncFavicons.h"
124 #endif
125 #include "nsINetworkPredictor.h"
127 // Editor-related
128 #include "nsIEditingSession.h"
130 #include "nsPIDOMWindow.h"
131 #include "nsGlobalWindow.h"
132 #include "nsPIWindowRoot.h"
133 #include "nsICachingChannel.h"
134 #include "nsIMultiPartChannel.h"
135 #include "nsIWyciwygChannel.h"
137 // For reporting errors with the console service.
138 // These can go away if error reporting is propagated up past nsDocShell.
139 #include "nsIScriptError.h"
141 // used to dispatch urls to default protocol handlers
142 #include "nsCExternalHandlerService.h"
143 #include "nsIExternalProtocolService.h"
145 #include "nsFocusManager.h"
147 #include "nsITextToSubURI.h"
149 #include "nsIJARChannel.h"
151 #include "prlog.h"
153 #include "nsISelectionDisplay.h"
155 #include "nsIGlobalHistory2.h"
157 #include "nsIFrame.h"
158 #include "nsSubDocumentFrame.h"
160 // for embedding
161 #include "nsIWebBrowserChromeFocus.h"
163 #if NS_PRINT_PREVIEW
164 #include "nsIDocumentViewerPrint.h"
165 #include "nsIWebBrowserPrint.h"
166 #endif
168 #include "nsContentUtils.h"
169 #include "nsIChannelPolicy.h"
170 #include "nsIContentSecurityPolicy.h"
171 #include "nsILoadInfo.h"
172 #include "nsSandboxFlags.h"
173 #include "nsXULAppAPI.h"
174 #include "nsDOMNavigationTiming.h"
175 #include "nsISecurityUITelemetry.h"
176 #include "nsIAppsService.h"
177 #include "nsDSURIContentListener.h"
178 #include "nsDocShellLoadTypes.h"
179 #include "nsDocShellTransferableHooks.h"
180 #include "nsICommandManager.h"
181 #include "nsIDOMNode.h"
182 #include "nsIDocShellTreeOwner.h"
183 #include "nsIHttpChannel.h"
184 #include "nsISHContainer.h"
185 #include "nsISHistory.h"
186 #include "nsISecureBrowserUI.h"
187 #include "nsIStringBundle.h"
188 #include "nsISupportsArray.h"
189 #include "nsIURIFixup.h"
190 #include "nsIURILoader.h"
191 #include "nsIWebBrowserFind.h"
192 #include "nsIWidget.h"
193 #include "nsIChromeRegistry.h"
194 #include "mozilla/dom/EncodingUtils.h"
195 #include "mozilla/dom/ScriptSettings.h"
196 #include "mozilla/dom/URLSearchParams.h"
198 static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
200 #if defined(DEBUG_bryner) || defined(DEBUG_chb)
201 //#define DEBUG_DOCSHELL_FOCUS
202 #define DEBUG_PAGE_CACHE
203 #endif
205 #ifdef XP_WIN
206 #include <process.h>
207 #define getpid _getpid
208 #else
209 #include <unistd.h> // for getpid()
210 #endif
212 using namespace mozilla;
213 using namespace mozilla::dom;
215 // True means sUseErrorPages has been added to preferences var cache.
216 static bool gAddedPreferencesVarCache = false;
218 bool nsDocShell::sUseErrorPages = false;
220 // Number of documents currently loading
221 static int32_t gNumberOfDocumentsLoading = 0;
223 // Global count of existing docshells.
224 static int32_t gDocShellCount = 0;
226 // Global count of docshells with the private attribute set
227 static uint32_t gNumberOfPrivateDocShells = 0;
229 // Global reference to the URI fixup service.
230 nsIURIFixup *nsDocShell::sURIFixup = 0;
232 // True means we validate window targets to prevent frameset
233 // spoofing. Initialize this to a non-bolean value so we know to check
234 // the pref on the creation of the first docshell.
235 static uint32_t gValidateOrigin = 0xffffffff;
237 // Hint for native dispatch of events on how long to delay after
238 // all documents have loaded in milliseconds before favoring normal
239 // native event dispatch priorites over performance
240 // Can be overridden with docshell.event_starvation_delay_hint pref.
241 #define NS_EVENT_STARVATION_DELAY_HINT 2000
243 #ifdef PR_LOGGING
244 #ifdef DEBUG
245 static PRLogModuleInfo* gDocShellLog;
246 #endif
247 static PRLogModuleInfo* gDocShellLeakLog;
248 #endif
250 const char kBrandBundleURL[] = "chrome://branding/locale/brand.properties";
251 const char kAppstringsBundleURL[] = "chrome://global/locale/appstrings.properties";
253 static void
254 FavorPerformanceHint(bool perfOverStarvation)
256 nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
257 if (appShell) {
258 appShell->FavorPerformanceHint(perfOverStarvation,
259 Preferences::GetUint("docshell.event_starvation_delay_hint",
260 NS_EVENT_STARVATION_DELAY_HINT));
264 //*****************************************************************************
265 // <a ping> support
266 //*****************************************************************************
268 #define PREF_PINGS_ENABLED "browser.send_pings"
269 #define PREF_PINGS_MAX_PER_LINK "browser.send_pings.max_per_link"
270 #define PREF_PINGS_REQUIRE_SAME_HOST "browser.send_pings.require_same_host"
272 // Check prefs to see if pings are enabled and if so what restrictions might
273 // be applied.
275 // @param maxPerLink
276 // This parameter returns the number of pings that are allowed per link click
278 // @param requireSameHost
279 // This parameter returns true if pings are restricted to the same host as
280 // the document in which the click occurs. If the same host restriction is
281 // imposed, then we still allow for pings to cross over to different
282 // protocols and ports for flexibility and because it is not possible to send
283 // a ping via FTP.
285 // @returns
286 // true if pings are enabled and false otherwise.
288 static bool
289 PingsEnabled(int32_t *maxPerLink, bool *requireSameHost)
291 bool allow = Preferences::GetBool(PREF_PINGS_ENABLED, false);
293 *maxPerLink = 1;
294 *requireSameHost = true;
296 if (allow) {
297 Preferences::GetInt(PREF_PINGS_MAX_PER_LINK, maxPerLink);
298 Preferences::GetBool(PREF_PINGS_REQUIRE_SAME_HOST, requireSameHost);
301 return allow;
304 static bool
305 CheckPingURI(nsIURI* uri, nsIContent* content)
307 if (!uri)
308 return false;
310 // Check with nsIScriptSecurityManager
311 nsCOMPtr<nsIScriptSecurityManager> ssmgr =
312 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
313 NS_ENSURE_TRUE(ssmgr, false);
315 nsresult rv =
316 ssmgr->CheckLoadURIWithPrincipal(content->NodePrincipal(), uri,
317 nsIScriptSecurityManager::STANDARD);
318 if (NS_FAILED(rv)) {
319 return false;
322 // Ignore non-HTTP(S)
323 bool match;
324 if ((NS_FAILED(uri->SchemeIs("http", &match)) || !match) &&
325 (NS_FAILED(uri->SchemeIs("https", &match)) || !match)) {
326 return false;
329 // Check with contentpolicy
330 int16_t shouldLoad = nsIContentPolicy::ACCEPT;
331 rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_PING,
332 uri,
333 content->NodePrincipal(),
334 content,
335 EmptyCString(), // mime hint
336 nullptr, //extra
337 &shouldLoad);
338 return NS_SUCCEEDED(rv) && NS_CP_ACCEPTED(shouldLoad);
341 typedef void (* ForEachPingCallback)(void *closure, nsIContent *content,
342 nsIURI *uri, nsIIOService *ios);
344 static bool
345 IsElementAnchor(nsIContent* aContent)
347 // Make sure we are dealing with either an <A> or <AREA> element in the HTML
348 // or XHTML namespace.
349 if (!aContent->IsHTML()) {
350 return false;
352 nsIAtom* nameAtom = aContent->Tag();
353 return nameAtom == nsGkAtoms::a || nameAtom == nsGkAtoms::area;
356 static void
357 ForEachPing(nsIContent *content, ForEachPingCallback callback, void *closure)
359 // NOTE: Using nsIDOMHTMLAnchorElement::GetPing isn't really worth it here
360 // since we'd still need to parse the resulting string. Instead, we
361 // just parse the raw attribute. It might be nice if the content node
362 // implemented an interface that exposed an enumeration of nsIURIs.
364 // Make sure we are dealing with either an <A> or <AREA> element in the HTML
365 // or XHTML namespace.
366 if (!IsElementAnchor(content))
367 return;
369 nsCOMPtr<nsIAtom> pingAtom = do_GetAtom("ping");
370 if (!pingAtom)
371 return;
373 nsAutoString value;
374 content->GetAttr(kNameSpaceID_None, pingAtom, value);
375 if (value.IsEmpty())
376 return;
378 nsCOMPtr<nsIIOService> ios = do_GetIOService();
379 if (!ios)
380 return;
382 nsIDocument *doc = content->OwnerDoc();
384 nsWhitespaceTokenizer tokenizer(value);
386 while (tokenizer.hasMoreTokens()) {
387 nsCOMPtr<nsIURI> uri, baseURI = content->GetBaseURI();
388 ios->NewURI(NS_ConvertUTF16toUTF8(tokenizer.nextToken()),
389 doc->GetDocumentCharacterSet().get(),
390 baseURI, getter_AddRefs(uri));
391 if (CheckPingURI(uri, content)) {
392 callback(closure, content, uri, ios);
397 //----------------------------------------------------------------------
399 // We wait this many milliseconds before killing the ping channel...
400 #define PING_TIMEOUT 10000
402 static void
403 OnPingTimeout(nsITimer *timer, void *closure)
405 nsILoadGroup *loadGroup = static_cast<nsILoadGroup *>(closure);
406 if (loadGroup)
407 loadGroup->Cancel(NS_ERROR_ABORT);
410 // Check to see if two URIs have the same host or not
411 static bool
412 IsSameHost(nsIURI *uri1, nsIURI *uri2)
414 nsAutoCString host1, host2;
415 uri1->GetAsciiHost(host1);
416 uri2->GetAsciiHost(host2);
417 return host1.Equals(host2);
420 class nsPingListener MOZ_FINAL : public nsIStreamListener
421 , public nsIInterfaceRequestor
422 , public nsIChannelEventSink
424 public:
425 NS_DECL_ISUPPORTS
426 NS_DECL_NSIREQUESTOBSERVER
427 NS_DECL_NSISTREAMLISTENER
428 NS_DECL_NSIINTERFACEREQUESTOR
429 NS_DECL_NSICHANNELEVENTSINK
431 nsPingListener(bool requireSameHost, nsIContent* content, nsILoadGroup* loadGroup)
432 : mRequireSameHost(requireSameHost),
433 mContent(content),
434 mLoadGroup(loadGroup)
437 nsresult StartTimeout();
439 private:
440 ~nsPingListener();
442 bool mRequireSameHost;
443 nsCOMPtr<nsIContent> mContent;
444 nsCOMPtr<nsILoadGroup> mLoadGroup;
445 nsCOMPtr<nsITimer> mTimer;
448 NS_IMPL_ISUPPORTS(nsPingListener, nsIStreamListener, nsIRequestObserver,
449 nsIInterfaceRequestor, nsIChannelEventSink)
451 nsPingListener::~nsPingListener()
453 if (mTimer) {
454 mTimer->Cancel();
455 mTimer = nullptr;
459 nsresult
460 nsPingListener::StartTimeout()
462 nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
464 if (timer) {
465 nsresult rv = timer->InitWithFuncCallback(OnPingTimeout, mLoadGroup,
466 PING_TIMEOUT,
467 nsITimer::TYPE_ONE_SHOT);
468 if (NS_SUCCEEDED(rv)) {
469 mTimer = timer;
470 return NS_OK;
474 return NS_ERROR_OUT_OF_MEMORY;
477 NS_IMETHODIMP
478 nsPingListener::OnStartRequest(nsIRequest *request, nsISupports *context)
480 return NS_OK;
483 NS_IMETHODIMP
484 nsPingListener::OnDataAvailable(nsIRequest *request, nsISupports *context,
485 nsIInputStream *stream, uint64_t offset,
486 uint32_t count)
488 uint32_t result;
489 return stream->ReadSegments(NS_DiscardSegment, nullptr, count, &result);
492 NS_IMETHODIMP
493 nsPingListener::OnStopRequest(nsIRequest *request, nsISupports *context,
494 nsresult status)
496 mLoadGroup = nullptr;
498 if (mTimer) {
499 mTimer->Cancel();
500 mTimer = nullptr;
503 return NS_OK;
506 NS_IMETHODIMP
507 nsPingListener::GetInterface(const nsIID &iid, void **result)
509 if (iid.Equals(NS_GET_IID(nsIChannelEventSink))) {
510 NS_ADDREF_THIS();
511 *result = (nsIChannelEventSink *) this;
512 return NS_OK;
515 return NS_ERROR_NO_INTERFACE;
518 NS_IMETHODIMP
519 nsPingListener::AsyncOnChannelRedirect(nsIChannel *oldChan, nsIChannel *newChan,
520 uint32_t flags,
521 nsIAsyncVerifyRedirectCallback *callback)
523 nsCOMPtr<nsIURI> newURI;
524 newChan->GetURI(getter_AddRefs(newURI));
526 if (!CheckPingURI(newURI, mContent))
527 return NS_ERROR_ABORT;
529 if (!mRequireSameHost) {
530 callback->OnRedirectVerifyCallback(NS_OK);
531 return NS_OK;
534 // XXXbz should this be using something more like the nsContentUtils
535 // same-origin checker?
536 nsCOMPtr<nsIURI> oldURI;
537 oldChan->GetURI(getter_AddRefs(oldURI));
538 NS_ENSURE_STATE(oldURI && newURI);
540 if (!IsSameHost(oldURI, newURI))
541 return NS_ERROR_ABORT;
543 callback->OnRedirectVerifyCallback(NS_OK);
544 return NS_OK;
547 struct SendPingInfo {
548 int32_t numPings;
549 int32_t maxPings;
550 bool requireSameHost;
551 nsIURI *target;
552 nsIURI *referrer;
555 static void
556 SendPing(void *closure, nsIContent *content, nsIURI *uri, nsIIOService *ios)
558 SendPingInfo *info = static_cast<SendPingInfo *>(closure);
559 if (info->maxPings > -1 && info->numPings >= info->maxPings)
560 return;
562 if (info->requireSameHost) {
563 // Make sure the referrer and the given uri share the same origin. We
564 // only require the same hostname. The scheme and port may differ.
565 if (!IsSameHost(uri, info->referrer))
566 return;
569 nsIDocument *doc = content->OwnerDoc();
571 nsCOMPtr<nsIChannel> chan;
572 ios->NewChannelFromURI(uri, getter_AddRefs(chan));
573 if (!chan)
574 return;
576 // Don't bother caching the result of this URI load.
577 chan->SetLoadFlags(nsIRequest::INHIBIT_CACHING);
579 nsCOMPtr<nsIHttpChannel> httpChan = do_QueryInterface(chan);
580 if (!httpChan)
581 return;
583 // This is needed in order for 3rd-party cookie blocking to work.
584 nsCOMPtr<nsIHttpChannelInternal> httpInternal = do_QueryInterface(httpChan);
585 if (httpInternal)
586 httpInternal->SetDocumentURI(doc->GetDocumentURI());
589 httpChan->SetRequestMethod(NS_LITERAL_CSTRING("POST"));
591 // Remove extraneous request headers (to reduce request size)
592 httpChan->SetRequestHeader(NS_LITERAL_CSTRING("accept"),
593 EmptyCString(), false);
594 httpChan->SetRequestHeader(NS_LITERAL_CSTRING("accept-language"),
595 EmptyCString(), false);
596 httpChan->SetRequestHeader(NS_LITERAL_CSTRING("accept-encoding"),
597 EmptyCString(), false);
599 // Always send a Ping-To header.
600 nsAutoCString pingTo;
601 if (NS_SUCCEEDED(info->target->GetSpec(pingTo)))
602 httpChan->SetRequestHeader(NS_LITERAL_CSTRING("Ping-To"), pingTo, false);
604 nsCOMPtr<nsIScriptSecurityManager> sm =
605 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
607 if (sm && info->referrer) {
608 bool referrerIsSecure;
609 uint32_t flags = nsIProtocolHandler::URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT;
610 nsresult rv = NS_URIChainHasFlags(info->referrer, flags, &referrerIsSecure);
612 // Default to sending less data if NS_URIChainHasFlags() fails.
613 referrerIsSecure = NS_FAILED(rv) || referrerIsSecure;
615 bool sameOrigin =
616 NS_SUCCEEDED(sm->CheckSameOriginURI(info->referrer, uri, false));
618 // If both the address of the document containing the hyperlink being
619 // audited and "ping URL" have the same origin or the document containing
620 // the hyperlink being audited was not retrieved over an encrypted
621 // connection, send a Ping-From header.
622 if (sameOrigin || !referrerIsSecure) {
623 nsAutoCString pingFrom;
624 if (NS_SUCCEEDED(info->referrer->GetSpec(pingFrom)))
625 httpChan->SetRequestHeader(NS_LITERAL_CSTRING("Ping-From"), pingFrom, false);
628 // If the document containing the hyperlink being audited was not retrieved
629 // over an encrypted connection and its address does not have the same
630 // origin as "ping URL", send a referrer.
631 if (!sameOrigin && !referrerIsSecure)
632 httpChan->SetReferrer(info->referrer);
635 nsCOMPtr<nsIUploadChannel2> uploadChan = do_QueryInterface(httpChan);
636 if (!uploadChan)
637 return;
639 NS_NAMED_LITERAL_CSTRING(uploadData, "PING");
641 nsCOMPtr<nsIInputStream> uploadStream;
642 NS_NewPostDataStream(getter_AddRefs(uploadStream), false, uploadData);
643 if (!uploadStream)
644 return;
646 uploadChan->ExplicitSetUploadStream(uploadStream,
647 NS_LITERAL_CSTRING("text/ping"), uploadData.Length(),
648 NS_LITERAL_CSTRING("POST"), false);
650 // The channel needs to have a loadgroup associated with it, so that we can
651 // cancel the channel and any redirected channels it may create.
652 nsCOMPtr<nsILoadGroup> loadGroup =
653 do_CreateInstance(NS_LOADGROUP_CONTRACTID);
654 if (!loadGroup)
655 return;
656 chan->SetLoadGroup(loadGroup);
658 // Construct a listener that merely discards any response. If successful at
659 // opening the channel, then it is not necessary to hold a reference to the
660 // channel. The networking subsystem will take care of that for us.
661 nsPingListener *pingListener =
662 new nsPingListener(info->requireSameHost, content, loadGroup);
663 if (!pingListener)
664 return;
666 nsCOMPtr<nsIStreamListener> listener(pingListener);
668 // Observe redirects as well:
669 nsCOMPtr<nsIInterfaceRequestor> callbacks = do_QueryInterface(listener);
670 NS_ASSERTION(callbacks, "oops");
671 loadGroup->SetNotificationCallbacks(callbacks);
673 chan->AsyncOpen(listener, nullptr);
675 // Even if AsyncOpen failed, we still count this as a successful ping. It's
676 // possible that AsyncOpen may have failed after triggering some background
677 // process that may have written something to the network.
678 info->numPings++;
680 // Prevent ping requests from stalling and never being garbage collected...
681 if (NS_FAILED(pingListener->StartTimeout())) {
682 // If we failed to setup the timer, then we should just cancel the channel
683 // because we won't be able to ensure that it goes away in a timely manner.
684 chan->Cancel(NS_ERROR_ABORT);
688 // Spec: http://whatwg.org/specs/web-apps/current-work/#ping
689 static void
690 DispatchPings(nsIContent *content, nsIURI *target, nsIURI *referrer)
692 SendPingInfo info;
694 if (!PingsEnabled(&info.maxPings, &info.requireSameHost))
695 return;
696 if (info.maxPings == 0)
697 return;
699 info.numPings = 0;
700 info.target = target;
701 info.referrer = referrer;
703 ForEachPing(content, SendPing, &info);
706 static nsDOMPerformanceNavigationType
707 ConvertLoadTypeToNavigationType(uint32_t aLoadType)
709 // Not initialized, assume it's normal load.
710 if (aLoadType == 0) {
711 aLoadType = LOAD_NORMAL;
714 nsDOMPerformanceNavigationType result = dom::PerformanceNavigation::TYPE_RESERVED;
715 switch (aLoadType) {
716 case LOAD_NORMAL:
717 case LOAD_NORMAL_EXTERNAL:
718 case LOAD_NORMAL_BYPASS_CACHE:
719 case LOAD_NORMAL_BYPASS_PROXY:
720 case LOAD_NORMAL_BYPASS_PROXY_AND_CACHE:
721 case LOAD_NORMAL_REPLACE:
722 case LOAD_NORMAL_ALLOW_MIXED_CONTENT:
723 case LOAD_LINK:
724 case LOAD_STOP_CONTENT:
725 case LOAD_REPLACE_BYPASS_CACHE:
726 result = dom::PerformanceNavigation::TYPE_NAVIGATE;
727 break;
728 case LOAD_HISTORY:
729 result = dom::PerformanceNavigation::TYPE_BACK_FORWARD;
730 break;
731 case LOAD_RELOAD_NORMAL:
732 case LOAD_RELOAD_CHARSET_CHANGE:
733 case LOAD_RELOAD_BYPASS_CACHE:
734 case LOAD_RELOAD_BYPASS_PROXY:
735 case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
736 case LOAD_RELOAD_ALLOW_MIXED_CONTENT:
737 result = dom::PerformanceNavigation::TYPE_RELOAD;
738 break;
739 case LOAD_STOP_CONTENT_AND_REPLACE:
740 case LOAD_REFRESH:
741 case LOAD_BYPASS_HISTORY:
742 case LOAD_ERROR_PAGE:
743 case LOAD_PUSHSTATE:
744 result = dom::PerformanceNavigation::TYPE_RESERVED;
745 break;
746 default:
747 // NS_NOTREACHED("Unexpected load type value");
748 result = dom::PerformanceNavigation::TYPE_RESERVED;
749 break;
752 return result;
755 static nsISHEntry* GetRootSHEntry(nsISHEntry *entry);
757 static void
758 IncreasePrivateDocShellCount()
760 gNumberOfPrivateDocShells++;
761 if (gNumberOfPrivateDocShells > 1 ||
762 XRE_GetProcessType() != GeckoProcessType_Content) {
763 return;
766 mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton();
767 cc->SendPrivateDocShellsExist(true);
770 static void
771 DecreasePrivateDocShellCount()
773 MOZ_ASSERT(gNumberOfPrivateDocShells > 0);
774 gNumberOfPrivateDocShells--;
775 if (!gNumberOfPrivateDocShells)
777 if (XRE_GetProcessType() == GeckoProcessType_Content) {
778 mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton();
779 cc->SendPrivateDocShellsExist(false);
780 return;
783 nsCOMPtr<nsIObserverService> obsvc = mozilla::services::GetObserverService();
784 if (obsvc) {
785 obsvc->NotifyObservers(nullptr, "last-pb-context-exited", nullptr);
790 //*****************************************************************************
791 //*** nsDocShell: Object Management
792 //*****************************************************************************
794 static uint64_t gDocshellIDCounter = 0;
796 // Note: operator new zeros our memory
797 nsDocShell::nsDocShell():
798 nsDocLoader(),
799 mDefaultScrollbarPref(Scrollbar_Auto, Scrollbar_Auto),
800 mTreeOwner(nullptr),
801 mChromeEventHandler(nullptr),
802 mCharsetReloadState(eCharsetReloadInit),
803 mChildOffset(0),
804 mBusyFlags(BUSY_FLAGS_NONE),
805 mAppType(nsIDocShell::APP_TYPE_UNKNOWN),
806 mLoadType(0),
807 mMarginWidth(-1),
808 mMarginHeight(-1),
809 mItemType(typeContent),
810 mPreviousTransIndex(-1),
811 mLoadedTransIndex(-1),
812 mSandboxFlags(0),
813 mFullscreenAllowed(CHECK_ATTRIBUTES),
814 mCreated(false),
815 mAllowSubframes(true),
816 mAllowPlugins(true),
817 mAllowJavascript(true),
818 mAllowMetaRedirects(true),
819 mAllowImages(true),
820 mAllowMedia(true),
821 mAllowDNSPrefetch(true),
822 mAllowWindowControl(true),
823 mAllowContentRetargeting(true),
824 mCreatingDocument(false),
825 mUseErrorPages(false),
826 mObserveErrorPages(true),
827 mAllowAuth(true),
828 mAllowKeywordFixup(false),
829 mIsOffScreenBrowser(false),
830 mIsActive(true),
831 mIsAppTab(false),
832 mUseGlobalHistory(false),
833 mInPrivateBrowsing(false),
834 mUseRemoteTabs(false),
835 mDeviceSizeIsPageSize(false),
836 mCanExecuteScripts(false),
837 mFiredUnloadEvent(false),
838 mEODForCurrentDocument(false),
839 mURIResultedInDocument(false),
840 mIsBeingDestroyed(false),
841 mIsExecutingOnLoadHandler(false),
842 mIsPrintingOrPP(false),
843 mSavingOldViewer(false),
844 #ifdef DEBUG
845 mInEnsureScriptEnv(false),
846 #endif
847 mAffectPrivateSessionLifetime(true),
848 mInvisible(false),
849 mDefaultLoadFlags(nsIRequest::LOAD_NORMAL),
850 mFrameType(eFrameTypeRegular),
851 mOwnOrContainingAppId(nsIScriptSecurityManager::UNKNOWN_APP_ID),
852 mParentCharsetSource(0)
854 mHistoryID = ++gDocshellIDCounter;
855 if (gDocShellCount++ == 0) {
856 NS_ASSERTION(sURIFixup == nullptr,
857 "Huh, sURIFixup not null in first nsDocShell ctor!");
859 CallGetService(NS_URIFIXUP_CONTRACTID, &sURIFixup);
862 #ifdef PR_LOGGING
863 #ifdef DEBUG
864 if (! gDocShellLog)
865 gDocShellLog = PR_NewLogModule("nsDocShell");
866 #endif
867 if (nullptr == gDocShellLeakLog)
868 gDocShellLeakLog = PR_NewLogModule("nsDocShellLeak");
869 if (gDocShellLeakLog)
870 PR_LOG(gDocShellLeakLog, PR_LOG_DEBUG, ("DOCSHELL %p created\n", this));
871 #endif
873 #ifdef DEBUG
874 // We're counting the number of |nsDocShells| to help find leaks
875 ++gNumberOfDocShells;
876 if (!PR_GetEnv("MOZ_QUIET")) {
877 printf_stderr("++DOCSHELL %p == %ld [pid = %d] [id = %llu]\n",
878 (void*) this,
879 gNumberOfDocShells,
880 getpid(),
881 AssertedCast<unsigned long long>(mHistoryID));
883 #endif
886 nsDocShell::~nsDocShell()
888 Destroy();
890 nsCOMPtr<nsISHistoryInternal>
891 shPrivate(do_QueryInterface(mSessionHistory));
892 if (shPrivate) {
893 shPrivate->SetRootDocShell(nullptr);
896 if (--gDocShellCount == 0) {
897 NS_IF_RELEASE(sURIFixup);
900 #ifdef PR_LOGGING
901 if (gDocShellLeakLog)
902 PR_LOG(gDocShellLeakLog, PR_LOG_DEBUG, ("DOCSHELL %p destroyed\n", this));
903 #endif
905 #ifdef DEBUG
906 // We're counting the number of |nsDocShells| to help find leaks
907 --gNumberOfDocShells;
908 if (!PR_GetEnv("MOZ_QUIET")) {
909 printf_stderr("--DOCSHELL %p == %ld [pid = %d] [id = %llu]\n",
910 (void*) this,
911 gNumberOfDocShells,
912 getpid(),
913 AssertedCast<unsigned long long>(mHistoryID));
915 #endif
918 nsresult
919 nsDocShell::Init()
921 nsresult rv = nsDocLoader::Init();
922 NS_ENSURE_SUCCESS(rv, rv);
924 NS_ASSERTION(mLoadGroup, "Something went wrong!");
926 mContentListener = new nsDSURIContentListener(this);
927 NS_ENSURE_TRUE(mContentListener, NS_ERROR_OUT_OF_MEMORY);
929 rv = mContentListener->Init();
930 NS_ENSURE_SUCCESS(rv, rv);
932 // We want to hold a strong ref to the loadgroup, so it better hold a weak
933 // ref to us... use an InterfaceRequestorProxy to do this.
934 nsCOMPtr<nsIInterfaceRequestor> proxy =
935 new InterfaceRequestorProxy(static_cast<nsIInterfaceRequestor*>
936 (this));
937 NS_ENSURE_TRUE(proxy, NS_ERROR_OUT_OF_MEMORY);
938 mLoadGroup->SetNotificationCallbacks(proxy);
940 rv = nsDocLoader::AddDocLoaderAsChildOfRoot(this);
941 NS_ENSURE_SUCCESS(rv, rv);
943 // Add as |this| a progress listener to itself. A little weird, but
944 // simpler than reproducing all the listener-notification logic in
945 // overrides of the various methods via which nsDocLoader can be
946 // notified. Note that this holds an nsWeakPtr to ourselves, so it's ok.
947 return AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_DOCUMENT |
948 nsIWebProgress::NOTIFY_STATE_NETWORK);
952 void
953 nsDocShell::DestroyChildren()
955 nsCOMPtr<nsIDocShellTreeItem> shell;
956 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
957 while (iter.HasMore()) {
958 shell = do_QueryObject(iter.GetNext());
959 NS_ASSERTION(shell, "docshell has null child");
961 if (shell) {
962 shell->SetTreeOwner(nullptr);
966 nsDocLoader::DestroyChildren();
969 //*****************************************************************************
970 // nsDocShell::nsISupports
971 //*****************************************************************************
973 NS_IMPL_ADDREF_INHERITED(nsDocShell, nsDocLoader)
974 NS_IMPL_RELEASE_INHERITED(nsDocShell, nsDocLoader)
976 NS_INTERFACE_MAP_BEGIN(nsDocShell)
977 NS_INTERFACE_MAP_ENTRY(nsIDocShell)
978 NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeItem)
979 NS_INTERFACE_MAP_ENTRY(nsIWebNavigation)
980 NS_INTERFACE_MAP_ENTRY(nsIBaseWindow)
981 NS_INTERFACE_MAP_ENTRY(nsIScrollable)
982 NS_INTERFACE_MAP_ENTRY(nsITextScroll)
983 NS_INTERFACE_MAP_ENTRY(nsIDocCharset)
984 NS_INTERFACE_MAP_ENTRY(nsIRefreshURI)
985 NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
986 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
987 NS_INTERFACE_MAP_ENTRY(nsIContentViewerContainer)
988 NS_INTERFACE_MAP_ENTRY(nsIWebPageDescriptor)
989 NS_INTERFACE_MAP_ENTRY(nsIAuthPromptProvider)
990 NS_INTERFACE_MAP_ENTRY(nsILoadContext)
991 NS_INTERFACE_MAP_ENTRY(nsIWebShellServices)
992 NS_INTERFACE_MAP_ENTRY(nsILinkHandler)
993 NS_INTERFACE_MAP_ENTRY(nsIClipboardCommands)
994 NS_INTERFACE_MAP_ENTRY(nsIDOMStorageManager)
995 NS_INTERFACE_MAP_END_INHERITING(nsDocLoader)
997 ///*****************************************************************************
998 // nsDocShell::nsIInterfaceRequestor
999 //*****************************************************************************
1000 NS_IMETHODIMP nsDocShell::GetInterface(const nsIID & aIID, void **aSink)
1002 NS_PRECONDITION(aSink, "null out param");
1004 *aSink = nullptr;
1006 if (aIID.Equals(NS_GET_IID(nsICommandManager))) {
1007 NS_ENSURE_SUCCESS(EnsureCommandHandler(), NS_ERROR_FAILURE);
1008 *aSink = mCommandManager;
1010 else if (aIID.Equals(NS_GET_IID(nsIURIContentListener))) {
1011 *aSink = mContentListener;
1013 else if ((aIID.Equals(NS_GET_IID(nsIScriptGlobalObject)) ||
1014 aIID.Equals(NS_GET_IID(nsIGlobalObject)) ||
1015 aIID.Equals(NS_GET_IID(nsPIDOMWindow)) ||
1016 aIID.Equals(NS_GET_IID(nsIDOMWindow)) ||
1017 aIID.Equals(NS_GET_IID(nsIDOMWindowInternal))) &&
1018 NS_SUCCEEDED(EnsureScriptEnvironment())) {
1019 return mScriptGlobal->QueryInterface(aIID, aSink);
1021 else if (aIID.Equals(NS_GET_IID(nsIDOMDocument)) &&
1022 NS_SUCCEEDED(EnsureContentViewer())) {
1023 mContentViewer->GetDOMDocument((nsIDOMDocument **) aSink);
1024 return *aSink ? NS_OK : NS_NOINTERFACE;
1026 else if (aIID.Equals(NS_GET_IID(nsIDocument)) &&
1027 NS_SUCCEEDED(EnsureContentViewer())) {
1028 nsCOMPtr<nsIDocument> doc = mContentViewer->GetDocument();
1029 doc.forget(aSink);
1030 return *aSink ? NS_OK : NS_NOINTERFACE;
1032 else if (aIID.Equals(NS_GET_IID(nsIApplicationCacheContainer))) {
1033 *aSink = nullptr;
1035 // Return application cache associated with this docshell, if any
1037 nsCOMPtr<nsIContentViewer> contentViewer;
1038 GetContentViewer(getter_AddRefs(contentViewer));
1039 if (!contentViewer)
1040 return NS_ERROR_NO_INTERFACE;
1042 nsCOMPtr<nsIDOMDocument> domDoc;
1043 contentViewer->GetDOMDocument(getter_AddRefs(domDoc));
1044 NS_ASSERTION(domDoc, "Should have a document.");
1045 if (!domDoc)
1046 return NS_ERROR_NO_INTERFACE;
1048 #if defined(PR_LOGGING) && defined(DEBUG)
1049 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
1050 ("nsDocShell[%p]: returning app cache container %p",
1051 this, domDoc.get()));
1052 #endif
1053 return domDoc->QueryInterface(aIID, aSink);
1055 else if (aIID.Equals(NS_GET_IID(nsIPrompt)) &&
1056 NS_SUCCEEDED(EnsureScriptEnvironment())) {
1057 nsresult rv;
1058 nsCOMPtr<nsIWindowWatcher> wwatch =
1059 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
1060 NS_ENSURE_SUCCESS(rv, rv);
1062 // Get the an auth prompter for our window so that the parenting
1063 // of the dialogs works as it should when using tabs.
1064 nsIPrompt *prompt;
1065 rv = wwatch->GetNewPrompter(mScriptGlobal, &prompt);
1066 NS_ENSURE_SUCCESS(rv, rv);
1068 *aSink = prompt;
1069 return NS_OK;
1071 else if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
1072 aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
1073 return NS_SUCCEEDED(
1074 GetAuthPrompt(PROMPT_NORMAL, aIID, aSink)) ?
1075 NS_OK : NS_NOINTERFACE;
1077 else if (aIID.Equals(NS_GET_IID(nsISHistory))) {
1078 nsCOMPtr<nsISHistory> shistory;
1079 nsresult
1080 rv =
1081 GetSessionHistory(getter_AddRefs(shistory));
1082 if (NS_SUCCEEDED(rv) && shistory) {
1083 *aSink = shistory;
1084 NS_ADDREF((nsISupports *) * aSink);
1085 return NS_OK;
1087 return NS_NOINTERFACE;
1089 else if (aIID.Equals(NS_GET_IID(nsIWebBrowserFind))) {
1090 nsresult rv = EnsureFind();
1091 if (NS_FAILED(rv)) return rv;
1093 *aSink = mFind;
1094 NS_ADDREF((nsISupports*)*aSink);
1095 return NS_OK;
1097 else if (aIID.Equals(NS_GET_IID(nsIEditingSession)) && NS_SUCCEEDED(EnsureEditorData())) {
1098 nsCOMPtr<nsIEditingSession> editingSession;
1099 mEditorData->GetEditingSession(getter_AddRefs(editingSession));
1100 if (editingSession)
1102 *aSink = editingSession;
1103 NS_ADDREF((nsISupports *)*aSink);
1104 return NS_OK;
1107 return NS_NOINTERFACE;
1109 else if (aIID.Equals(NS_GET_IID(nsIClipboardDragDropHookList))
1110 && NS_SUCCEEDED(EnsureTransferableHookData())) {
1111 *aSink = mTransferableHookData;
1112 NS_ADDREF((nsISupports *)*aSink);
1113 return NS_OK;
1115 else if (aIID.Equals(NS_GET_IID(nsISelectionDisplay))) {
1116 nsIPresShell* shell = GetPresShell();
1117 if (shell)
1118 return shell->QueryInterface(aIID,aSink);
1120 else if (aIID.Equals(NS_GET_IID(nsIDocShellTreeOwner))) {
1121 nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
1122 nsresult rv = GetTreeOwner(getter_AddRefs(treeOwner));
1123 if (NS_SUCCEEDED(rv) && treeOwner)
1124 return treeOwner->QueryInterface(aIID, aSink);
1126 else if (aIID.Equals(NS_GET_IID(nsITabChild))) {
1127 nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
1128 nsresult rv = GetTreeOwner(getter_AddRefs(treeOwner));
1129 if (NS_SUCCEEDED(rv) && treeOwner) {
1130 nsCOMPtr<nsIInterfaceRequestor> ir = do_QueryInterface(treeOwner);
1131 if (ir)
1132 return ir->GetInterface(aIID, aSink);
1135 else if (aIID.Equals(NS_GET_IID(nsIContentFrameMessageManager))) {
1136 nsCOMPtr<nsITabChild> tabChild =
1137 do_GetInterface(static_cast<nsIDocShell*>(this));
1138 nsCOMPtr<nsIContentFrameMessageManager> mm;
1139 if (tabChild) {
1140 tabChild->
1141 GetMessageManager(getter_AddRefs(mm));
1142 } else {
1143 nsCOMPtr<nsPIDOMWindow> win = GetWindow();
1144 if (win) {
1145 mm = do_QueryInterface(win->GetParentTarget());
1148 *aSink = mm.get();
1150 else {
1151 return nsDocLoader::GetInterface(aIID, aSink);
1154 NS_IF_ADDREF(((nsISupports *) * aSink));
1155 return *aSink ? NS_OK : NS_NOINTERFACE;
1158 uint32_t
1159 nsDocShell::
1160 ConvertDocShellLoadInfoToLoadType(nsDocShellInfoLoadType aDocShellLoadType)
1162 uint32_t loadType = LOAD_NORMAL;
1164 switch (aDocShellLoadType) {
1165 case nsIDocShellLoadInfo::loadNormal:
1166 loadType = LOAD_NORMAL;
1167 break;
1168 case nsIDocShellLoadInfo::loadNormalReplace:
1169 loadType = LOAD_NORMAL_REPLACE;
1170 break;
1171 case nsIDocShellLoadInfo::loadNormalExternal:
1172 loadType = LOAD_NORMAL_EXTERNAL;
1173 break;
1174 case nsIDocShellLoadInfo::loadHistory:
1175 loadType = LOAD_HISTORY;
1176 break;
1177 case nsIDocShellLoadInfo::loadNormalBypassCache:
1178 loadType = LOAD_NORMAL_BYPASS_CACHE;
1179 break;
1180 case nsIDocShellLoadInfo::loadNormalBypassProxy:
1181 loadType = LOAD_NORMAL_BYPASS_PROXY;
1182 break;
1183 case nsIDocShellLoadInfo::loadNormalBypassProxyAndCache:
1184 loadType = LOAD_NORMAL_BYPASS_PROXY_AND_CACHE;
1185 break;
1186 case nsIDocShellLoadInfo::loadNormalAllowMixedContent:
1187 loadType = LOAD_NORMAL_ALLOW_MIXED_CONTENT;
1188 break;
1189 case nsIDocShellLoadInfo::loadReloadNormal:
1190 loadType = LOAD_RELOAD_NORMAL;
1191 break;
1192 case nsIDocShellLoadInfo::loadReloadCharsetChange:
1193 loadType = LOAD_RELOAD_CHARSET_CHANGE;
1194 break;
1195 case nsIDocShellLoadInfo::loadReloadBypassCache:
1196 loadType = LOAD_RELOAD_BYPASS_CACHE;
1197 break;
1198 case nsIDocShellLoadInfo::loadReloadBypassProxy:
1199 loadType = LOAD_RELOAD_BYPASS_PROXY;
1200 break;
1201 case nsIDocShellLoadInfo::loadReloadBypassProxyAndCache:
1202 loadType = LOAD_RELOAD_BYPASS_PROXY_AND_CACHE;
1203 break;
1204 case nsIDocShellLoadInfo::loadLink:
1205 loadType = LOAD_LINK;
1206 break;
1207 case nsIDocShellLoadInfo::loadRefresh:
1208 loadType = LOAD_REFRESH;
1209 break;
1210 case nsIDocShellLoadInfo::loadBypassHistory:
1211 loadType = LOAD_BYPASS_HISTORY;
1212 break;
1213 case nsIDocShellLoadInfo::loadStopContent:
1214 loadType = LOAD_STOP_CONTENT;
1215 break;
1216 case nsIDocShellLoadInfo::loadStopContentAndReplace:
1217 loadType = LOAD_STOP_CONTENT_AND_REPLACE;
1218 break;
1219 case nsIDocShellLoadInfo::loadPushState:
1220 loadType = LOAD_PUSHSTATE;
1221 break;
1222 case nsIDocShellLoadInfo::loadReplaceBypassCache:
1223 loadType = LOAD_REPLACE_BYPASS_CACHE;
1224 break;
1225 case nsIDocShellLoadInfo::loadReloadMixedContent:
1226 loadType = LOAD_RELOAD_ALLOW_MIXED_CONTENT;
1227 break;
1228 default:
1229 NS_NOTREACHED("Unexpected nsDocShellInfoLoadType value");
1232 return loadType;
1236 nsDocShellInfoLoadType
1237 nsDocShell::ConvertLoadTypeToDocShellLoadInfo(uint32_t aLoadType)
1239 nsDocShellInfoLoadType docShellLoadType = nsIDocShellLoadInfo::loadNormal;
1240 switch (aLoadType) {
1241 case LOAD_NORMAL:
1242 docShellLoadType = nsIDocShellLoadInfo::loadNormal;
1243 break;
1244 case LOAD_NORMAL_REPLACE:
1245 docShellLoadType = nsIDocShellLoadInfo::loadNormalReplace;
1246 break;
1247 case LOAD_NORMAL_EXTERNAL:
1248 docShellLoadType = nsIDocShellLoadInfo::loadNormalExternal;
1249 break;
1250 case LOAD_NORMAL_BYPASS_CACHE:
1251 docShellLoadType = nsIDocShellLoadInfo::loadNormalBypassCache;
1252 break;
1253 case LOAD_NORMAL_BYPASS_PROXY:
1254 docShellLoadType = nsIDocShellLoadInfo::loadNormalBypassProxy;
1255 break;
1256 case LOAD_NORMAL_BYPASS_PROXY_AND_CACHE:
1257 docShellLoadType = nsIDocShellLoadInfo::loadNormalBypassProxyAndCache;
1258 break;
1259 case LOAD_NORMAL_ALLOW_MIXED_CONTENT:
1260 docShellLoadType = nsIDocShellLoadInfo::loadNormalAllowMixedContent;
1261 break;
1262 case LOAD_HISTORY:
1263 docShellLoadType = nsIDocShellLoadInfo::loadHistory;
1264 break;
1265 case LOAD_RELOAD_NORMAL:
1266 docShellLoadType = nsIDocShellLoadInfo::loadReloadNormal;
1267 break;
1268 case LOAD_RELOAD_CHARSET_CHANGE:
1269 docShellLoadType = nsIDocShellLoadInfo::loadReloadCharsetChange;
1270 break;
1271 case LOAD_RELOAD_BYPASS_CACHE:
1272 docShellLoadType = nsIDocShellLoadInfo::loadReloadBypassCache;
1273 break;
1274 case LOAD_RELOAD_BYPASS_PROXY:
1275 docShellLoadType = nsIDocShellLoadInfo::loadReloadBypassProxy;
1276 break;
1277 case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
1278 docShellLoadType = nsIDocShellLoadInfo::loadReloadBypassProxyAndCache;
1279 break;
1280 case LOAD_LINK:
1281 docShellLoadType = nsIDocShellLoadInfo::loadLink;
1282 break;
1283 case LOAD_REFRESH:
1284 docShellLoadType = nsIDocShellLoadInfo::loadRefresh;
1285 break;
1286 case LOAD_BYPASS_HISTORY:
1287 case LOAD_ERROR_PAGE:
1288 docShellLoadType = nsIDocShellLoadInfo::loadBypassHistory;
1289 break;
1290 case LOAD_STOP_CONTENT:
1291 docShellLoadType = nsIDocShellLoadInfo::loadStopContent;
1292 break;
1293 case LOAD_STOP_CONTENT_AND_REPLACE:
1294 docShellLoadType = nsIDocShellLoadInfo::loadStopContentAndReplace;
1295 break;
1296 case LOAD_PUSHSTATE:
1297 docShellLoadType = nsIDocShellLoadInfo::loadPushState;
1298 break;
1299 case LOAD_REPLACE_BYPASS_CACHE:
1300 docShellLoadType = nsIDocShellLoadInfo::loadReplaceBypassCache;
1301 break;
1302 case LOAD_RELOAD_ALLOW_MIXED_CONTENT:
1303 docShellLoadType = nsIDocShellLoadInfo::loadReloadMixedContent;
1304 break;
1305 default:
1306 NS_NOTREACHED("Unexpected load type value");
1309 return docShellLoadType;
1312 //*****************************************************************************
1313 // nsDocShell::nsIDocShell
1314 //*****************************************************************************
1315 NS_IMETHODIMP
1316 nsDocShell::LoadURI(nsIURI * aURI,
1317 nsIDocShellLoadInfo * aLoadInfo,
1318 uint32_t aLoadFlags,
1319 bool aFirstParty)
1321 NS_PRECONDITION(aLoadInfo || (aLoadFlags & EXTRA_LOAD_FLAGS) == 0,
1322 "Unexpected flags");
1323 NS_PRECONDITION((aLoadFlags & 0xf) == 0, "Should not have these flags set");
1325 // Note: we allow loads to get through here even if mFiredUnloadEvent is
1326 // true; that case will get handled in LoadInternal or LoadHistoryEntry,
1327 // so we pass false as the second parameter to IsNavigationAllowed.
1328 // However, we don't allow the page to change location *in the middle of*
1329 // firing beforeunload, so we do need to check if *beforeunload* is currently
1330 // firing, so we call IsNavigationAllowed rather than just IsPrintingOrPP.
1331 if (!IsNavigationAllowed(true, false)) {
1332 return NS_OK; // JS may not handle returning of an error code
1335 if (DoAppRedirectIfNeeded(aURI, aLoadInfo, aFirstParty)) {
1336 return NS_OK;
1339 nsCOMPtr<nsIURI> referrer;
1340 nsCOMPtr<nsIInputStream> postStream;
1341 nsCOMPtr<nsIInputStream> headersStream;
1342 nsCOMPtr<nsISupports> owner;
1343 bool inheritOwner = false;
1344 bool ownerIsExplicit = false;
1345 bool sendReferrer = true;
1346 bool isSrcdoc = false;
1347 nsCOMPtr<nsISHEntry> shEntry;
1348 nsXPIDLString target;
1349 nsAutoString srcdoc;
1350 nsCOMPtr<nsIDocShell> sourceDocShell;
1351 nsCOMPtr<nsIURI> baseURI;
1353 uint32_t loadType = MAKE_LOAD_TYPE(LOAD_NORMAL, aLoadFlags);
1355 NS_ENSURE_ARG(aURI);
1357 if (!StartupTimeline::HasRecord(StartupTimeline::FIRST_LOAD_URI) &&
1358 mItemType == typeContent && !NS_IsAboutBlank(aURI)) {
1359 StartupTimeline::RecordOnce(StartupTimeline::FIRST_LOAD_URI);
1362 // Extract the info from the DocShellLoadInfo struct...
1363 if (aLoadInfo) {
1364 aLoadInfo->GetReferrer(getter_AddRefs(referrer));
1366 nsDocShellInfoLoadType lt = nsIDocShellLoadInfo::loadNormal;
1367 aLoadInfo->GetLoadType(&lt);
1368 // Get the appropriate loadType from nsIDocShellLoadInfo type
1369 loadType = ConvertDocShellLoadInfoToLoadType(lt);
1371 aLoadInfo->GetOwner(getter_AddRefs(owner));
1372 aLoadInfo->GetInheritOwner(&inheritOwner);
1373 aLoadInfo->GetOwnerIsExplicit(&ownerIsExplicit);
1374 aLoadInfo->GetSHEntry(getter_AddRefs(shEntry));
1375 aLoadInfo->GetTarget(getter_Copies(target));
1376 aLoadInfo->GetPostDataStream(getter_AddRefs(postStream));
1377 aLoadInfo->GetHeadersStream(getter_AddRefs(headersStream));
1378 aLoadInfo->GetSendReferrer(&sendReferrer);
1379 aLoadInfo->GetIsSrcdocLoad(&isSrcdoc);
1380 aLoadInfo->GetSrcdocData(srcdoc);
1381 aLoadInfo->GetSourceDocShell(getter_AddRefs(sourceDocShell));
1382 aLoadInfo->GetBaseURI(getter_AddRefs(baseURI));
1385 #if defined(PR_LOGGING) && defined(DEBUG)
1386 if (PR_LOG_TEST(gDocShellLog, PR_LOG_DEBUG)) {
1387 nsAutoCString uristr;
1388 aURI->GetAsciiSpec(uristr);
1389 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
1390 ("nsDocShell[%p]: loading %s with flags 0x%08x",
1391 this, uristr.get(), aLoadFlags));
1393 #endif
1395 if (!shEntry &&
1396 !LOAD_TYPE_HAS_FLAGS(loadType, LOAD_FLAGS_REPLACE_HISTORY)) {
1397 // First verify if this is a subframe.
1398 nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
1399 GetSameTypeParent(getter_AddRefs(parentAsItem));
1400 nsCOMPtr<nsIDocShell> parentDS(do_QueryInterface(parentAsItem));
1401 uint32_t parentLoadType;
1403 if (parentDS && parentDS != static_cast<nsIDocShell *>(this)) {
1404 /* OK. It is a subframe. Checkout the
1405 * parent's loadtype. If the parent was loaded thro' a history
1406 * mechanism, then get the SH entry for the child from the parent.
1407 * This is done to restore frameset navigation while going back/forward.
1408 * If the parent was loaded through any other loadType, set the
1409 * child's loadType too accordingly, so that session history does not
1410 * get confused.
1413 // Get the parent's load type
1414 parentDS->GetLoadType(&parentLoadType);
1416 // Get the ShEntry for the child from the parent
1417 nsCOMPtr<nsISHEntry> currentSH;
1418 bool oshe = false;
1419 parentDS->GetCurrentSHEntry(getter_AddRefs(currentSH), &oshe);
1420 bool dynamicallyAddedChild = mDynamicallyCreated;
1421 if (!dynamicallyAddedChild && !oshe && currentSH) {
1422 currentSH->HasDynamicallyAddedChild(&dynamicallyAddedChild);
1424 if (!dynamicallyAddedChild) {
1425 // Only use the old SHEntry, if we're sure enough that
1426 // it wasn't originally for some other frame.
1427 parentDS->GetChildSHEntry(mChildOffset, getter_AddRefs(shEntry));
1430 // Make some decisions on the child frame's loadType based on the
1431 // parent's loadType.
1432 if (mCurrentURI == nullptr) {
1433 // This is a newly created frame. Check for exception cases first.
1434 // By default the subframe will inherit the parent's loadType.
1435 if (shEntry && (parentLoadType == LOAD_NORMAL ||
1436 parentLoadType == LOAD_LINK ||
1437 parentLoadType == LOAD_NORMAL_EXTERNAL)) {
1438 // The parent was loaded normally. In this case, this *brand new* child really shouldn't
1439 // have a SHEntry. If it does, it could be because the parent is replacing an
1440 // existing frame with a new frame, in the onLoadHandler. We don't want this
1441 // url to get into session history. Clear off shEntry, and set load type to
1442 // LOAD_BYPASS_HISTORY.
1443 bool inOnLoadHandler=false;
1444 parentDS->GetIsExecutingOnLoadHandler(&inOnLoadHandler);
1445 if (inOnLoadHandler) {
1446 loadType = LOAD_NORMAL_REPLACE;
1447 shEntry = nullptr;
1449 } else if (parentLoadType == LOAD_REFRESH) {
1450 // Clear shEntry. For refresh loads, we have to load
1451 // what comes thro' the pipe, not what's in history.
1452 shEntry = nullptr;
1453 } else if ((parentLoadType == LOAD_BYPASS_HISTORY) ||
1454 (shEntry &&
1455 ((parentLoadType & LOAD_CMD_HISTORY) ||
1456 (parentLoadType == LOAD_RELOAD_NORMAL) ||
1457 (parentLoadType == LOAD_RELOAD_CHARSET_CHANGE)))) {
1458 // If the parent url, bypassed history or was loaded from
1459 // history, pass on the parent's loadType to the new child
1460 // frame too, so that the child frame will also
1461 // avoid getting into history.
1462 loadType = parentLoadType;
1463 } else if (parentLoadType == LOAD_ERROR_PAGE) {
1464 // If the parent document is an error page, we don't
1465 // want to update global/session history. However,
1466 // this child frame is not an error page.
1467 loadType = LOAD_BYPASS_HISTORY;
1468 } else if ((parentLoadType == LOAD_RELOAD_BYPASS_CACHE) ||
1469 (parentLoadType == LOAD_RELOAD_BYPASS_PROXY) ||
1470 (parentLoadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE)) {
1471 // the new frame should inherit the parent's load type so that it also
1472 // bypasses the cache and/or proxy
1473 loadType = parentLoadType;
1475 } else {
1476 // This is a pre-existing subframe. If the load was not originally initiated
1477 // by session history, (if (!shEntry) condition succeeded) and mCurrentURI is not null,
1478 // it is possible that a parent's onLoadHandler or even self's onLoadHandler is loading
1479 // a new page in this child. Check parent's and self's busy flag and if it is set,
1480 // we don't want this onLoadHandler load to get in to session history.
1481 uint32_t parentBusy = BUSY_FLAGS_NONE;
1482 uint32_t selfBusy = BUSY_FLAGS_NONE;
1483 parentDS->GetBusyFlags(&parentBusy);
1484 GetBusyFlags(&selfBusy);
1485 if (parentBusy & BUSY_FLAGS_BUSY ||
1486 selfBusy & BUSY_FLAGS_BUSY) {
1487 loadType = LOAD_NORMAL_REPLACE;
1488 shEntry = nullptr;
1491 } //parentDS
1492 else {
1493 // This is the root docshell. If we got here while
1494 // executing an onLoad Handler,this load will not go
1495 // into session history.
1496 bool inOnLoadHandler=false;
1497 GetIsExecutingOnLoadHandler(&inOnLoadHandler);
1498 if (inOnLoadHandler) {
1499 loadType = LOAD_NORMAL_REPLACE;
1502 } // !shEntry
1504 if (shEntry) {
1505 #ifdef DEBUG
1506 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
1507 ("nsDocShell[%p]: loading from session history", this));
1508 #endif
1510 return LoadHistoryEntry(shEntry, loadType);
1513 // On history navigation via Back/Forward buttons, don't execute
1514 // automatic JavaScript redirection such as |location.href = ...| or
1515 // |window.open()|
1517 // LOAD_NORMAL: window.open(...) etc.
1518 // LOAD_STOP_CONTENT: location.href = ..., location.assign(...)
1519 if ((loadType == LOAD_NORMAL || loadType == LOAD_STOP_CONTENT) &&
1520 ShouldBlockLoadingForBackButton()) {
1521 return NS_OK;
1524 // Perform the load...
1526 // We need an owner (a referring principal).
1528 // If ownerIsExplicit is not set there are 4 possibilities:
1529 // (1) If the system principal or an expanded principal was passed
1530 // in and we're a typeContent docshell, inherit the principal
1531 // from the current document instead.
1532 // (2) In all other cases when the principal passed in is not null,
1533 // use that principal.
1534 // (3) If the caller has allowed inheriting from the current document,
1535 // or if we're being called from system code (eg chrome JS or pure
1536 // C++) then inheritOwner should be true and InternalLoad will get
1537 // an owner from the current document. If none of these things are
1538 // true, then
1539 // (4) we pass a null owner into the channel, and an owner will be
1540 // created later from the channel's internal data.
1542 // If ownerIsExplicit *is* set, there are 4 possibilities
1543 // (1) If the system principal or an expanded principal was passed in
1544 // and we're a typeContent docshell, return an error.
1545 // (2) In all other cases when the principal passed in is not null,
1546 // use that principal.
1547 // (3) If the caller has allowed inheriting from the current document,
1548 // then inheritOwner should be true and InternalLoad will get an owner
1549 // from the current document. If none of these things are true, then
1550 // (4) we pass a null owner into the channel, and an owner will be
1551 // created later from the channel's internal data.
1553 // NOTE: This all only works because the only thing the owner is used
1554 // for in InternalLoad is data:, javascript:, and about:blank
1555 // URIs. For other URIs this would all be dead wrong!
1557 if (owner && mItemType != typeChrome) {
1558 nsCOMPtr<nsIPrincipal> ownerPrincipal = do_QueryInterface(owner);
1559 if (nsContentUtils::IsSystemOrExpandedPrincipal(ownerPrincipal)) {
1560 if (ownerIsExplicit) {
1561 return NS_ERROR_DOM_SECURITY_ERR;
1563 owner = nullptr;
1564 inheritOwner = true;
1567 if (!owner && !inheritOwner && !ownerIsExplicit) {
1568 // See if there's system or chrome JS code running
1569 inheritOwner = nsContentUtils::IsCallerChrome();
1572 if (aLoadFlags & LOAD_FLAGS_DISALLOW_INHERIT_OWNER) {
1573 inheritOwner = false;
1574 owner = do_CreateInstance("@mozilla.org/nullprincipal;1");
1577 uint32_t flags = 0;
1579 if (inheritOwner)
1580 flags |= INTERNAL_LOAD_FLAGS_INHERIT_OWNER;
1582 if (!sendReferrer)
1583 flags |= INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER;
1585 if (aLoadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP)
1586 flags |= INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
1588 if (aLoadFlags & LOAD_FLAGS_FIRST_LOAD)
1589 flags |= INTERNAL_LOAD_FLAGS_FIRST_LOAD;
1591 if (aLoadFlags & LOAD_FLAGS_BYPASS_CLASSIFIER)
1592 flags |= INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER;
1594 if (aLoadFlags & LOAD_FLAGS_FORCE_ALLOW_COOKIES)
1595 flags |= INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES;
1597 if (isSrcdoc)
1598 flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC;
1600 return InternalLoad(aURI,
1601 referrer,
1602 owner,
1603 flags,
1604 target.get(),
1605 nullptr, // No type hint
1606 NullString(), // No forced download
1607 postStream,
1608 headersStream,
1609 loadType,
1610 nullptr, // No SHEntry
1611 aFirstParty,
1612 srcdoc,
1613 sourceDocShell,
1614 baseURI,
1615 nullptr, // No nsIDocShell
1616 nullptr); // No nsIRequest
1619 NS_IMETHODIMP
1620 nsDocShell::LoadStream(nsIInputStream *aStream, nsIURI * aURI,
1621 const nsACString &aContentType,
1622 const nsACString &aContentCharset,
1623 nsIDocShellLoadInfo * aLoadInfo)
1625 NS_ENSURE_ARG(aStream);
1627 mAllowKeywordFixup = false;
1629 // if the caller doesn't pass in a URI we need to create a dummy URI. necko
1630 // currently requires a URI in various places during the load. Some consumers
1631 // do as well.
1632 nsCOMPtr<nsIURI> uri = aURI;
1633 if (!uri) {
1634 // HACK ALERT
1635 nsresult rv = NS_OK;
1636 uri = do_CreateInstance(NS_SIMPLEURI_CONTRACTID, &rv);
1637 if (NS_FAILED(rv))
1638 return rv;
1639 // Make sure that the URI spec "looks" like a protocol and path...
1640 // For now, just use a bogus protocol called "internal"
1641 rv = uri->SetSpec(NS_LITERAL_CSTRING("internal:load-stream"));
1642 if (NS_FAILED(rv))
1643 return rv;
1646 uint32_t loadType = LOAD_NORMAL;
1647 if (aLoadInfo) {
1648 nsDocShellInfoLoadType lt = nsIDocShellLoadInfo::loadNormal;
1649 (void) aLoadInfo->GetLoadType(&lt);
1650 // Get the appropriate LoadType from nsIDocShellLoadInfo type
1651 loadType = ConvertDocShellLoadInfoToLoadType(lt);
1654 NS_ENSURE_SUCCESS(Stop(nsIWebNavigation::STOP_NETWORK), NS_ERROR_FAILURE);
1656 mLoadType = loadType;
1658 // build up a channel for this stream.
1659 nsCOMPtr<nsIChannel> channel;
1660 NS_ENSURE_SUCCESS(NS_NewInputStreamChannel
1661 (getter_AddRefs(channel), uri, aStream,
1662 aContentType, aContentCharset),
1663 NS_ERROR_FAILURE);
1665 nsCOMPtr<nsIURILoader>
1666 uriLoader(do_GetService(NS_URI_LOADER_CONTRACTID));
1667 NS_ENSURE_TRUE(uriLoader, NS_ERROR_FAILURE);
1669 NS_ENSURE_SUCCESS(DoChannelLoad(channel, uriLoader, false),
1670 NS_ERROR_FAILURE);
1671 return NS_OK;
1674 NS_IMETHODIMP
1675 nsDocShell::CreateLoadInfo(nsIDocShellLoadInfo ** aLoadInfo)
1677 nsDocShellLoadInfo *loadInfo = new nsDocShellLoadInfo();
1678 NS_ENSURE_TRUE(loadInfo, NS_ERROR_OUT_OF_MEMORY);
1679 nsCOMPtr<nsIDocShellLoadInfo> localRef(loadInfo);
1681 *aLoadInfo = localRef;
1682 NS_ADDREF(*aLoadInfo);
1683 return NS_OK;
1688 * Reset state to a new content model within the current document and the document
1689 * viewer. Called by the document before initiating an out of band document.write().
1691 NS_IMETHODIMP
1692 nsDocShell::PrepareForNewContentModel()
1694 mEODForCurrentDocument = false;
1695 return NS_OK;
1699 NS_IMETHODIMP
1700 nsDocShell::FirePageHideNotification(bool aIsUnload)
1702 if (mContentViewer && !mFiredUnloadEvent) {
1703 // Keep an explicit reference since calling PageHide could release
1704 // mContentViewer
1705 nsCOMPtr<nsIContentViewer> kungFuDeathGrip(mContentViewer);
1706 mFiredUnloadEvent = true;
1708 if (mTiming) {
1709 mTiming->NotifyUnloadEventStart();
1712 mContentViewer->PageHide(aIsUnload);
1714 if (mTiming) {
1715 mTiming->NotifyUnloadEventEnd();
1718 nsAutoTArray<nsCOMPtr<nsIDocShell>, 8> kids;
1719 uint32_t n = mChildList.Length();
1720 kids.SetCapacity(n);
1721 for (uint32_t i = 0; i < n; i++) {
1722 kids.AppendElement(do_QueryInterface(ChildAt(i)));
1725 n = kids.Length();
1726 for (uint32_t i = 0; i < n; ++i) {
1727 if (kids[i]) {
1728 kids[i]->FirePageHideNotification(aIsUnload);
1731 // Now make sure our editor, if any, is detached before we go
1732 // any farther.
1733 DetachEditorFromWindow();
1736 return NS_OK;
1739 void
1740 nsDocShell::MaybeInitTiming()
1742 if (mTiming) {
1743 return;
1746 mTiming = new nsDOMNavigationTiming();
1747 mTiming->NotifyNavigationStart();
1752 // Bug 13871: Prevent frameset spoofing
1754 // This routine answers: 'Is origin's document from same domain as
1755 // target's document?'
1757 // file: uris are considered the same domain for the purpose of
1758 // frame navigation regardless of script accessibility (bug 420425)
1760 /* static */
1761 bool
1762 nsDocShell::ValidateOrigin(nsIDocShellTreeItem* aOriginTreeItem,
1763 nsIDocShellTreeItem* aTargetTreeItem)
1765 // We want to bypass this check for chrome callers, but only if there's
1766 // JS on the stack. System callers still need to do it.
1767 if (nsContentUtils::GetCurrentJSContext() && nsContentUtils::IsCallerChrome()) {
1768 return true;
1771 MOZ_ASSERT(aOriginTreeItem && aTargetTreeItem, "need two docshells");
1773 // Get origin document principal
1774 nsCOMPtr<nsIDocument> originDocument = aOriginTreeItem->GetDocument();
1775 NS_ENSURE_TRUE(originDocument, false);
1777 // Get target principal
1778 nsCOMPtr<nsIDocument> targetDocument = aTargetTreeItem->GetDocument();
1779 NS_ENSURE_TRUE(targetDocument, false);
1781 bool equal;
1782 nsresult rv = originDocument->NodePrincipal()->Equals(targetDocument->NodePrincipal(),
1783 &equal);
1784 if (NS_SUCCEEDED(rv) && equal) {
1785 return true;
1788 // Not strictly equal, special case if both are file: uris
1789 bool originIsFile = false;
1790 bool targetIsFile = false;
1791 nsCOMPtr<nsIURI> originURI;
1792 nsCOMPtr<nsIURI> targetURI;
1793 nsCOMPtr<nsIURI> innerOriginURI;
1794 nsCOMPtr<nsIURI> innerTargetURI;
1796 rv = originDocument->NodePrincipal()->GetURI(getter_AddRefs(originURI));
1797 if (NS_SUCCEEDED(rv) && originURI)
1798 innerOriginURI = NS_GetInnermostURI(originURI);
1800 rv = targetDocument->NodePrincipal()->GetURI(getter_AddRefs(targetURI));
1801 if (NS_SUCCEEDED(rv) && targetURI)
1802 innerTargetURI = NS_GetInnermostURI(targetURI);
1804 return innerOriginURI && innerTargetURI &&
1805 NS_SUCCEEDED(innerOriginURI->SchemeIs("file", &originIsFile)) &&
1806 NS_SUCCEEDED(innerTargetURI->SchemeIs("file", &targetIsFile)) &&
1807 originIsFile && targetIsFile;
1810 NS_IMETHODIMP
1811 nsDocShell::GetEldestPresContext(nsPresContext** aPresContext)
1813 NS_ENSURE_ARG_POINTER(aPresContext);
1814 *aPresContext = nullptr;
1816 nsCOMPtr<nsIContentViewer> viewer = mContentViewer;
1817 while (viewer) {
1818 nsCOMPtr<nsIContentViewer> prevViewer;
1819 viewer->GetPreviousViewer(getter_AddRefs(prevViewer));
1820 if (!prevViewer) {
1821 return viewer->GetPresContext(aPresContext);
1823 viewer = prevViewer;
1826 return NS_OK;
1829 NS_IMETHODIMP
1830 nsDocShell::GetPresContext(nsPresContext ** aPresContext)
1832 NS_ENSURE_ARG_POINTER(aPresContext);
1833 *aPresContext = nullptr;
1835 if (!mContentViewer)
1836 return NS_OK;
1838 return mContentViewer->GetPresContext(aPresContext);
1841 NS_IMETHODIMP_(nsIPresShell*)
1842 nsDocShell::GetPresShell()
1844 nsRefPtr<nsPresContext> presContext;
1845 (void) GetPresContext(getter_AddRefs(presContext));
1846 return presContext ? presContext->GetPresShell() : nullptr;
1849 NS_IMETHODIMP
1850 nsDocShell::GetEldestPresShell(nsIPresShell** aPresShell)
1852 nsresult rv = NS_OK;
1854 NS_ENSURE_ARG_POINTER(aPresShell);
1855 *aPresShell = nullptr;
1857 nsRefPtr<nsPresContext> presContext;
1858 (void) GetEldestPresContext(getter_AddRefs(presContext));
1860 if (presContext) {
1861 NS_IF_ADDREF(*aPresShell = presContext->GetPresShell());
1864 return rv;
1867 NS_IMETHODIMP
1868 nsDocShell::GetContentViewer(nsIContentViewer ** aContentViewer)
1870 NS_ENSURE_ARG_POINTER(aContentViewer);
1872 *aContentViewer = mContentViewer;
1873 NS_IF_ADDREF(*aContentViewer);
1874 return NS_OK;
1877 NS_IMETHODIMP
1878 nsDocShell::SetChromeEventHandler(nsIDOMEventTarget* aChromeEventHandler)
1880 // Weak reference. Don't addref.
1881 nsCOMPtr<EventTarget> handler = do_QueryInterface(aChromeEventHandler);
1882 mChromeEventHandler = handler.get();
1884 if (mScriptGlobal) {
1885 mScriptGlobal->SetChromeEventHandler(mChromeEventHandler);
1888 return NS_OK;
1891 NS_IMETHODIMP
1892 nsDocShell::GetChromeEventHandler(nsIDOMEventTarget** aChromeEventHandler)
1894 NS_ENSURE_ARG_POINTER(aChromeEventHandler);
1895 nsCOMPtr<EventTarget> handler = mChromeEventHandler;
1896 handler.forget(aChromeEventHandler);
1897 return NS_OK;
1900 /* void setCurrentURI (in nsIURI uri); */
1901 NS_IMETHODIMP
1902 nsDocShell::SetCurrentURI(nsIURI *aURI)
1904 // Note that securityUI will set STATE_IS_INSECURE, even if
1905 // the scheme of |aURI| is "https".
1906 SetCurrentURI(aURI, nullptr, true, 0);
1907 return NS_OK;
1910 bool
1911 nsDocShell::SetCurrentURI(nsIURI *aURI, nsIRequest *aRequest,
1912 bool aFireOnLocationChange, uint32_t aLocationFlags)
1914 #ifdef PR_LOGGING
1915 if (gDocShellLeakLog && PR_LOG_TEST(gDocShellLeakLog, PR_LOG_DEBUG)) {
1916 nsAutoCString spec;
1917 if (aURI)
1918 aURI->GetSpec(spec);
1919 PR_LogPrint("DOCSHELL %p SetCurrentURI %s\n", this, spec.get());
1921 #endif
1923 // We don't want to send a location change when we're displaying an error
1924 // page, and we don't want to change our idea of "current URI" either
1925 if (mLoadType == LOAD_ERROR_PAGE) {
1926 return false;
1929 mCurrentURI = NS_TryToMakeImmutable(aURI);
1931 bool isRoot = false; // Is this the root docshell
1932 bool isSubFrame = false; // Is this a subframe navigation?
1934 nsCOMPtr<nsIDocShellTreeItem> root;
1936 GetSameTypeRootTreeItem(getter_AddRefs(root));
1937 if (root.get() == static_cast<nsIDocShellTreeItem *>(this))
1939 // This is the root docshell
1940 isRoot = true;
1942 if (mLSHE) {
1943 mLSHE->GetIsSubFrame(&isSubFrame);
1946 // nsDocShell owns a URLSearchParams that is used by
1947 // window.location.searchParams to be in sync with the current location.
1948 if (!mURLSearchParams) {
1949 mURLSearchParams = new URLSearchParams();
1952 nsAutoCString search;
1954 nsCOMPtr<nsIURL> url(do_QueryInterface(mCurrentURI));
1955 if (url) {
1956 nsresult rv = url->GetQuery(search);
1957 if (NS_FAILED(rv)) {
1958 NS_WARNING("Failed to get the query from a nsIURL.");
1962 mURLSearchParams->ParseInput(search, nullptr);
1964 if (!isSubFrame && !isRoot) {
1966 * We don't want to send OnLocationChange notifications when
1967 * a subframe is being loaded for the first time, while
1968 * visiting a frameset page
1970 return false;
1973 if (aFireOnLocationChange) {
1974 FireOnLocationChange(this, aRequest, aURI, aLocationFlags);
1976 return !aFireOnLocationChange;
1979 NS_IMETHODIMP
1980 nsDocShell::GetCharset(nsACString& aCharset)
1982 aCharset.Truncate();
1984 nsIPresShell* presShell = GetPresShell();
1985 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
1986 nsIDocument *doc = presShell->GetDocument();
1987 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
1988 aCharset = doc->GetDocumentCharacterSet();
1989 return NS_OK;
1992 NS_IMETHODIMP
1993 nsDocShell::GatherCharsetMenuTelemetry()
1995 nsCOMPtr<nsIContentViewer> viewer;
1996 GetContentViewer(getter_AddRefs(viewer));
1997 if (!viewer) {
1998 return NS_OK;
2001 nsIDocument* doc = viewer->GetDocument();
2002 if (!doc || doc->WillIgnoreCharsetOverride()) {
2003 return NS_OK;
2006 Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_USED, true);
2008 bool isFileURL = false;
2009 nsIURI* url = doc->GetOriginalURI();
2010 if (url) {
2011 url->SchemeIs("file", &isFileURL);
2014 int32_t charsetSource = doc->GetDocumentCharacterSetSource();
2015 switch (charsetSource) {
2016 case kCharsetFromTopLevelDomain:
2017 // Unlabeled doc on a domain that we map to a fallback encoding
2018 Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 7);
2019 break;
2020 case kCharsetFromFallback:
2021 case kCharsetFromDocTypeDefault:
2022 case kCharsetFromCache:
2023 case kCharsetFromParentFrame:
2024 case kCharsetFromHintPrevDoc:
2025 // Changing charset on an unlabeled doc.
2026 if (isFileURL) {
2027 Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 0);
2028 } else {
2029 Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 1);
2031 break;
2032 case kCharsetFromAutoDetection:
2033 // Changing charset on unlabeled doc where chardet fired
2034 if (isFileURL) {
2035 Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 2);
2036 } else {
2037 Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 3);
2039 break;
2040 case kCharsetFromMetaPrescan:
2041 case kCharsetFromMetaTag:
2042 case kCharsetFromChannel:
2043 // Changing charset on a doc that had a charset label.
2044 Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 4);
2045 break;
2046 case kCharsetFromParentForced:
2047 case kCharsetFromUserForced:
2048 // Changing charset on a document that already had an override.
2049 Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 5);
2050 break;
2051 case kCharsetFromIrreversibleAutoDetection:
2052 case kCharsetFromOtherComponent:
2053 case kCharsetFromByteOrderMark:
2054 case kCharsetUninitialized:
2055 default:
2056 // Bug. This isn't supposed to happen.
2057 Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 6);
2058 break;
2060 return NS_OK;
2063 NS_IMETHODIMP
2064 nsDocShell::SetCharset(const nsACString& aCharset)
2066 // set the charset override
2067 return SetForcedCharset(aCharset);
2070 NS_IMETHODIMP nsDocShell::SetForcedCharset(const nsACString& aCharset)
2072 if (aCharset.IsEmpty()) {
2073 mForcedCharset.Truncate();
2074 return NS_OK;
2076 nsAutoCString encoding;
2077 if (!EncodingUtils::FindEncodingForLabel(aCharset, encoding)) {
2078 // Reject unknown labels
2079 return NS_ERROR_INVALID_ARG;
2081 if (!EncodingUtils::IsAsciiCompatible(encoding)) {
2082 // Reject XSS hazards
2083 return NS_ERROR_INVALID_ARG;
2085 mForcedCharset = encoding;
2086 return NS_OK;
2089 NS_IMETHODIMP nsDocShell::GetForcedCharset(nsACString& aResult)
2091 aResult = mForcedCharset;
2092 return NS_OK;
2095 void
2096 nsDocShell::SetParentCharset(const nsACString& aCharset,
2097 int32_t aCharsetSource,
2098 nsIPrincipal* aPrincipal)
2100 mParentCharset = aCharset;
2101 mParentCharsetSource = aCharsetSource;
2102 mParentCharsetPrincipal = aPrincipal;
2105 void
2106 nsDocShell::GetParentCharset(nsACString& aCharset,
2107 int32_t* aCharsetSource,
2108 nsIPrincipal** aPrincipal)
2110 aCharset = mParentCharset;
2111 *aCharsetSource = mParentCharsetSource;
2112 NS_IF_ADDREF(*aPrincipal = mParentCharsetPrincipal);
2115 NS_IMETHODIMP
2116 nsDocShell::GetChannelIsUnsafe(bool *aUnsafe)
2118 *aUnsafe = false;
2120 nsIChannel* channel = GetCurrentDocChannel();
2121 if (!channel) {
2122 return NS_OK;
2125 nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(channel);
2126 if (!jarChannel) {
2127 return NS_OK;
2130 return jarChannel->GetIsUnsafe(aUnsafe);
2133 NS_IMETHODIMP
2134 nsDocShell::GetHasMixedActiveContentLoaded(bool* aHasMixedActiveContentLoaded)
2136 nsCOMPtr<nsIDocument> doc(GetDocument());
2137 *aHasMixedActiveContentLoaded = doc && doc->GetHasMixedActiveContentLoaded();
2138 return NS_OK;
2141 NS_IMETHODIMP
2142 nsDocShell::GetHasMixedActiveContentBlocked(bool* aHasMixedActiveContentBlocked)
2144 nsCOMPtr<nsIDocument> doc(GetDocument());
2145 *aHasMixedActiveContentBlocked = doc && doc->GetHasMixedActiveContentBlocked();
2146 return NS_OK;
2149 NS_IMETHODIMP
2150 nsDocShell::GetHasMixedDisplayContentLoaded(bool* aHasMixedDisplayContentLoaded)
2152 nsCOMPtr<nsIDocument> doc(GetDocument());
2153 *aHasMixedDisplayContentLoaded = doc && doc->GetHasMixedDisplayContentLoaded();
2154 return NS_OK;
2157 NS_IMETHODIMP
2158 nsDocShell::GetHasMixedDisplayContentBlocked(bool* aHasMixedDisplayContentBlocked)
2160 nsCOMPtr<nsIDocument> doc(GetDocument());
2161 *aHasMixedDisplayContentBlocked = doc && doc->GetHasMixedDisplayContentBlocked();
2162 return NS_OK;
2165 NS_IMETHODIMP
2166 nsDocShell::GetHasTrackingContentBlocked(bool* aHasTrackingContentBlocked)
2168 nsCOMPtr<nsIDocument> doc(GetDocument());
2169 *aHasTrackingContentBlocked = doc && doc->GetHasTrackingContentBlocked();
2170 return NS_OK;
2173 NS_IMETHODIMP
2174 nsDocShell::GetHasTrackingContentLoaded(bool* aHasTrackingContentLoaded)
2176 nsCOMPtr<nsIDocument> doc(GetDocument());
2177 *aHasTrackingContentLoaded = doc && doc->GetHasTrackingContentLoaded();
2178 return NS_OK;
2181 NS_IMETHODIMP
2182 nsDocShell::GetAllowPlugins(bool * aAllowPlugins)
2184 NS_ENSURE_ARG_POINTER(aAllowPlugins);
2186 *aAllowPlugins = mAllowPlugins;
2187 if (!mAllowPlugins) {
2188 return NS_OK;
2191 bool unsafe;
2192 *aAllowPlugins = NS_SUCCEEDED(GetChannelIsUnsafe(&unsafe)) && !unsafe;
2193 return NS_OK;
2196 NS_IMETHODIMP
2197 nsDocShell::SetAllowPlugins(bool aAllowPlugins)
2199 mAllowPlugins = aAllowPlugins;
2200 //XXX should enable or disable a plugin host
2201 return NS_OK;
2204 NS_IMETHODIMP
2205 nsDocShell::GetAllowJavascript(bool * aAllowJavascript)
2207 NS_ENSURE_ARG_POINTER(aAllowJavascript);
2209 *aAllowJavascript = mAllowJavascript;
2210 return NS_OK;
2213 NS_IMETHODIMP
2214 nsDocShell::SetAllowJavascript(bool aAllowJavascript)
2216 mAllowJavascript = aAllowJavascript;
2217 RecomputeCanExecuteScripts();
2218 return NS_OK;
2221 NS_IMETHODIMP
2222 nsDocShell::GetUsePrivateBrowsing(bool* aUsePrivateBrowsing)
2224 NS_ENSURE_ARG_POINTER(aUsePrivateBrowsing);
2226 *aUsePrivateBrowsing = mInPrivateBrowsing;
2227 return NS_OK;
2230 NS_IMETHODIMP
2231 nsDocShell::SetUsePrivateBrowsing(bool aUsePrivateBrowsing)
2233 nsContentUtils::ReportToConsoleNonLocalized(
2234 NS_LITERAL_STRING("Only internal code is allowed to set the usePrivateBrowsing attribute"),
2235 nsIScriptError::warningFlag,
2236 NS_LITERAL_CSTRING("Internal API Used"),
2237 mContentViewer ? mContentViewer->GetDocument() : nullptr);
2239 return SetPrivateBrowsing(aUsePrivateBrowsing);
2242 NS_IMETHODIMP
2243 nsDocShell::SetPrivateBrowsing(bool aUsePrivateBrowsing)
2245 bool changed = aUsePrivateBrowsing != mInPrivateBrowsing;
2246 if (changed) {
2247 mInPrivateBrowsing = aUsePrivateBrowsing;
2248 if (mAffectPrivateSessionLifetime) {
2249 if (aUsePrivateBrowsing) {
2250 IncreasePrivateDocShellCount();
2251 } else {
2252 DecreasePrivateDocShellCount();
2257 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
2258 while (iter.HasMore()) {
2259 nsCOMPtr<nsILoadContext> shell = do_QueryObject(iter.GetNext());
2260 if (shell) {
2261 shell->SetPrivateBrowsing(aUsePrivateBrowsing);
2265 if (changed) {
2266 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mPrivacyObservers);
2267 while (iter.HasMore()) {
2268 nsWeakPtr ref = iter.GetNext();
2269 nsCOMPtr<nsIPrivacyTransitionObserver> obs = do_QueryReferent(ref);
2270 if (!obs) {
2271 mPrivacyObservers.RemoveElement(ref);
2272 } else {
2273 obs->PrivateModeChanged(aUsePrivateBrowsing);
2277 return NS_OK;
2280 NS_IMETHODIMP
2281 nsDocShell::GetUseRemoteTabs(bool* aUseRemoteTabs)
2283 NS_ENSURE_ARG_POINTER(aUseRemoteTabs);
2285 *aUseRemoteTabs = mUseRemoteTabs;
2286 return NS_OK;
2289 NS_IMETHODIMP
2290 nsDocShell::SetRemoteTabs(bool aUseRemoteTabs)
2292 #ifdef MOZ_CRASHREPORTER
2293 if (aUseRemoteTabs) {
2294 CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("DOMIPCEnabled"),
2295 NS_LITERAL_CSTRING("1"));
2297 #endif
2299 mUseRemoteTabs = aUseRemoteTabs;
2300 return NS_OK;
2303 NS_IMETHODIMP
2304 nsDocShell::SetAffectPrivateSessionLifetime(bool aAffectLifetime)
2306 bool change = aAffectLifetime != mAffectPrivateSessionLifetime;
2307 if (change && mInPrivateBrowsing) {
2308 if (aAffectLifetime) {
2309 IncreasePrivateDocShellCount();
2310 } else {
2311 DecreasePrivateDocShellCount();
2314 mAffectPrivateSessionLifetime = aAffectLifetime;
2316 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
2317 while (iter.HasMore()) {
2318 nsCOMPtr<nsIDocShell> shell = do_QueryObject(iter.GetNext());
2319 if (shell) {
2320 shell->SetAffectPrivateSessionLifetime(aAffectLifetime);
2323 return NS_OK;
2326 NS_IMETHODIMP
2327 nsDocShell::GetAffectPrivateSessionLifetime(bool* aAffectLifetime)
2329 *aAffectLifetime = mAffectPrivateSessionLifetime;
2330 return NS_OK;
2333 NS_IMETHODIMP
2334 nsDocShell::AddWeakPrivacyTransitionObserver(nsIPrivacyTransitionObserver* aObserver)
2336 nsWeakPtr weakObs = do_GetWeakReference(aObserver);
2337 if (!weakObs) {
2338 return NS_ERROR_NOT_AVAILABLE;
2340 return mPrivacyObservers.AppendElement(weakObs) ? NS_OK : NS_ERROR_FAILURE;
2343 NS_IMETHODIMP
2344 nsDocShell::AddWeakReflowObserver(nsIReflowObserver* aObserver)
2346 nsWeakPtr weakObs = do_GetWeakReference(aObserver);
2347 if (!weakObs) {
2348 return NS_ERROR_FAILURE;
2350 return mReflowObservers.AppendElement(weakObs) ? NS_OK : NS_ERROR_FAILURE;
2353 NS_IMETHODIMP
2354 nsDocShell::RemoveWeakReflowObserver(nsIReflowObserver* aObserver)
2356 nsWeakPtr obs = do_GetWeakReference(aObserver);
2357 return mReflowObservers.RemoveElement(obs) ? NS_OK : NS_ERROR_FAILURE;
2360 NS_IMETHODIMP
2361 nsDocShell::NotifyReflowObservers(bool aInterruptible,
2362 DOMHighResTimeStamp aStart,
2363 DOMHighResTimeStamp aEnd)
2365 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mReflowObservers);
2366 while (iter.HasMore()) {
2367 nsWeakPtr ref = iter.GetNext();
2368 nsCOMPtr<nsIReflowObserver> obs = do_QueryReferent(ref);
2369 if (!obs) {
2370 mReflowObservers.RemoveElement(ref);
2371 } else if (aInterruptible) {
2372 obs->ReflowInterruptible(aStart, aEnd);
2373 } else {
2374 obs->Reflow(aStart, aEnd);
2377 return NS_OK;
2380 NS_IMETHODIMP nsDocShell::GetAllowMetaRedirects(bool * aReturn)
2382 NS_ENSURE_ARG_POINTER(aReturn);
2384 *aReturn = mAllowMetaRedirects;
2385 if (!mAllowMetaRedirects) {
2386 return NS_OK;
2389 bool unsafe;
2390 *aReturn = NS_SUCCEEDED(GetChannelIsUnsafe(&unsafe)) && !unsafe;
2391 return NS_OK;
2394 NS_IMETHODIMP nsDocShell::SetAllowMetaRedirects(bool aValue)
2396 mAllowMetaRedirects = aValue;
2397 return NS_OK;
2400 NS_IMETHODIMP nsDocShell::GetAllowSubframes(bool * aAllowSubframes)
2402 NS_ENSURE_ARG_POINTER(aAllowSubframes);
2404 *aAllowSubframes = mAllowSubframes;
2405 return NS_OK;
2408 NS_IMETHODIMP nsDocShell::SetAllowSubframes(bool aAllowSubframes)
2410 mAllowSubframes = aAllowSubframes;
2411 return NS_OK;
2414 NS_IMETHODIMP nsDocShell::GetAllowImages(bool * aAllowImages)
2416 NS_ENSURE_ARG_POINTER(aAllowImages);
2418 *aAllowImages = mAllowImages;
2419 return NS_OK;
2422 NS_IMETHODIMP nsDocShell::SetAllowImages(bool aAllowImages)
2424 mAllowImages = aAllowImages;
2425 return NS_OK;
2428 NS_IMETHODIMP nsDocShell::GetAllowMedia(bool * aAllowMedia)
2430 *aAllowMedia = mAllowMedia;
2431 return NS_OK;
2434 NS_IMETHODIMP nsDocShell::SetAllowMedia(bool aAllowMedia)
2436 mAllowMedia = aAllowMedia;
2438 // Mute or unmute audio contexts attached to the inner window.
2439 if (mScriptGlobal) {
2440 nsPIDOMWindow* innerWin = mScriptGlobal->GetCurrentInnerWindow();
2441 if (innerWin) {
2442 if (aAllowMedia) {
2443 innerWin->UnmuteAudioContexts();
2444 } else {
2445 innerWin->MuteAudioContexts();
2450 return NS_OK;
2453 NS_IMETHODIMP nsDocShell::GetAllowDNSPrefetch(bool * aAllowDNSPrefetch)
2455 *aAllowDNSPrefetch = mAllowDNSPrefetch;
2456 return NS_OK;
2459 NS_IMETHODIMP nsDocShell::SetAllowDNSPrefetch(bool aAllowDNSPrefetch)
2461 mAllowDNSPrefetch = aAllowDNSPrefetch;
2462 return NS_OK;
2465 NS_IMETHODIMP nsDocShell::GetAllowWindowControl(bool * aAllowWindowControl)
2467 *aAllowWindowControl = mAllowWindowControl;
2468 return NS_OK;
2471 NS_IMETHODIMP nsDocShell::SetAllowWindowControl(bool aAllowWindowControl)
2473 mAllowWindowControl = aAllowWindowControl;
2474 return NS_OK;
2477 NS_IMETHODIMP
2478 nsDocShell::GetAllowContentRetargeting(bool* aAllowContentRetargeting)
2480 *aAllowContentRetargeting = mAllowContentRetargeting;
2481 return NS_OK;
2484 NS_IMETHODIMP
2485 nsDocShell::SetAllowContentRetargeting(bool aAllowContentRetargeting)
2487 mAllowContentRetargeting = aAllowContentRetargeting;
2488 return NS_OK;
2491 NS_IMETHODIMP
2492 nsDocShell::GetFullscreenAllowed(bool* aFullscreenAllowed)
2494 NS_ENSURE_ARG_POINTER(aFullscreenAllowed);
2496 // Browsers and apps have their mFullscreenAllowed retrieved from their
2497 // corresponding iframe in their parent upon creation.
2498 if (mFullscreenAllowed != CHECK_ATTRIBUTES) {
2499 *aFullscreenAllowed = (mFullscreenAllowed == PARENT_ALLOWS);
2500 return NS_OK;
2503 // Assume false until we determine otherwise...
2504 *aFullscreenAllowed = false;
2506 // For non-browsers/apps, check that the enclosing iframe element
2507 // has the allowfullscreen attribute set to true. If any ancestor
2508 // iframe does not have mozallowfullscreen=true, then fullscreen is
2509 // prohibited.
2510 nsCOMPtr<nsPIDOMWindow> win = GetWindow();
2511 if (!win) {
2512 return NS_OK;
2514 nsCOMPtr<Element> frameElement = win->GetFrameElementInternal();
2515 if (frameElement &&
2516 frameElement->IsHTML(nsGkAtoms::iframe) &&
2517 !frameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::allowfullscreen) &&
2518 !frameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::mozallowfullscreen)) {
2519 return NS_OK;
2522 // If we have no parent then we're the root docshell; no ancestor of the
2523 // original docshell doesn't have a allowfullscreen attribute, so
2524 // report fullscreen as allowed.
2525 nsRefPtr<nsDocShell> parent = GetParentDocshell();
2526 if (!parent) {
2527 *aFullscreenAllowed = true;
2528 return NS_OK;
2531 // Otherwise, we have a parent, continue the checking for
2532 // mozFullscreenAllowed in the parent docshell's ancestors.
2533 return parent->GetFullscreenAllowed(aFullscreenAllowed);
2536 NS_IMETHODIMP
2537 nsDocShell::SetFullscreenAllowed(bool aFullscreenAllowed)
2539 if (!nsIDocShell::GetIsBrowserOrApp()) {
2540 // Only allow setting of fullscreenAllowed on content/process boundaries.
2541 // At non-boundaries the fullscreenAllowed attribute is calculated based on
2542 // whether all enclosing frames have the "mozFullscreenAllowed" attribute
2543 // set to "true". fullscreenAllowed is set at the process boundaries to
2544 // propagate the value of the parent's "mozFullscreenAllowed" attribute
2545 // across process boundaries.
2546 return NS_ERROR_UNEXPECTED;
2548 mFullscreenAllowed = (aFullscreenAllowed ? PARENT_ALLOWS : PARENT_PROHIBITS);
2549 return NS_OK;
2552 NS_IMETHODIMP
2553 nsDocShell::GetMayEnableCharacterEncodingMenu(bool* aMayEnableCharacterEncodingMenu)
2555 *aMayEnableCharacterEncodingMenu = false;
2556 if (!mContentViewer) {
2557 return NS_OK;
2559 nsIDocument* doc = mContentViewer->GetDocument();
2560 if (!doc) {
2561 return NS_OK;
2563 if (doc->WillIgnoreCharsetOverride()) {
2564 return NS_OK;
2567 *aMayEnableCharacterEncodingMenu = true;
2568 return NS_OK;
2571 NS_IMETHODIMP
2572 nsDocShell::GetDocShellEnumerator(int32_t aItemType, int32_t aDirection, nsISimpleEnumerator **outEnum)
2574 NS_ENSURE_ARG_POINTER(outEnum);
2575 *outEnum = nullptr;
2577 nsRefPtr<nsDocShellEnumerator> docShellEnum;
2578 if (aDirection == ENUMERATE_FORWARDS)
2579 docShellEnum = new nsDocShellForwardsEnumerator;
2580 else
2581 docShellEnum = new nsDocShellBackwardsEnumerator;
2583 if (!docShellEnum) return NS_ERROR_OUT_OF_MEMORY;
2585 nsresult rv = docShellEnum->SetEnumDocShellType(aItemType);
2586 if (NS_FAILED(rv)) return rv;
2588 rv = docShellEnum->SetEnumerationRootItem((nsIDocShellTreeItem *)this);
2589 if (NS_FAILED(rv)) return rv;
2591 rv = docShellEnum->First();
2592 if (NS_FAILED(rv)) return rv;
2594 rv = docShellEnum->QueryInterface(NS_GET_IID(nsISimpleEnumerator), (void **)outEnum);
2596 return rv;
2599 NS_IMETHODIMP
2600 nsDocShell::GetAppType(uint32_t * aAppType)
2602 *aAppType = mAppType;
2603 return NS_OK;
2606 NS_IMETHODIMP
2607 nsDocShell::SetAppType(uint32_t aAppType)
2609 mAppType = aAppType;
2610 return NS_OK;
2614 NS_IMETHODIMP
2615 nsDocShell::GetAllowAuth(bool * aAllowAuth)
2617 *aAllowAuth = mAllowAuth;
2618 return NS_OK;
2621 NS_IMETHODIMP
2622 nsDocShell::SetAllowAuth(bool aAllowAuth)
2624 mAllowAuth = aAllowAuth;
2625 return NS_OK;
2628 NS_IMETHODIMP
2629 nsDocShell::GetZoom(float *zoom)
2631 NS_ENSURE_ARG_POINTER(zoom);
2632 *zoom = 1.0f;
2633 return NS_OK;
2636 NS_IMETHODIMP
2637 nsDocShell::SetZoom(float zoom)
2639 return NS_ERROR_NOT_IMPLEMENTED;
2642 NS_IMETHODIMP
2643 nsDocShell::GetMarginWidth(int32_t * aWidth)
2645 NS_ENSURE_ARG_POINTER(aWidth);
2647 *aWidth = mMarginWidth;
2648 return NS_OK;
2651 NS_IMETHODIMP
2652 nsDocShell::SetMarginWidth(int32_t aWidth)
2654 mMarginWidth = aWidth;
2655 return NS_OK;
2658 NS_IMETHODIMP
2659 nsDocShell::GetMarginHeight(int32_t * aHeight)
2661 NS_ENSURE_ARG_POINTER(aHeight);
2663 *aHeight = mMarginHeight;
2664 return NS_OK;
2667 NS_IMETHODIMP
2668 nsDocShell::SetMarginHeight(int32_t aHeight)
2670 mMarginHeight = aHeight;
2671 return NS_OK;
2674 NS_IMETHODIMP
2675 nsDocShell::GetBusyFlags(uint32_t * aBusyFlags)
2677 NS_ENSURE_ARG_POINTER(aBusyFlags);
2679 *aBusyFlags = mBusyFlags;
2680 return NS_OK;
2683 NS_IMETHODIMP
2684 nsDocShell::TabToTreeOwner(bool aForward, bool* aTookFocus)
2686 NS_ENSURE_ARG_POINTER(aTookFocus);
2688 nsCOMPtr<nsIWebBrowserChromeFocus> chromeFocus = do_GetInterface(mTreeOwner);
2689 if (chromeFocus) {
2690 if (aForward)
2691 *aTookFocus = NS_SUCCEEDED(chromeFocus->FocusNextElement());
2692 else
2693 *aTookFocus = NS_SUCCEEDED(chromeFocus->FocusPrevElement());
2694 } else
2695 *aTookFocus = false;
2697 return NS_OK;
2700 NS_IMETHODIMP
2701 nsDocShell::GetSecurityUI(nsISecureBrowserUI **aSecurityUI)
2703 NS_IF_ADDREF(*aSecurityUI = mSecurityUI);
2704 return NS_OK;
2707 NS_IMETHODIMP
2708 nsDocShell::SetSecurityUI(nsISecureBrowserUI *aSecurityUI)
2710 mSecurityUI = aSecurityUI;
2711 mSecurityUI->SetDocShell(this);
2712 return NS_OK;
2715 NS_IMETHODIMP
2716 nsDocShell::GetUseErrorPages(bool *aUseErrorPages)
2718 *aUseErrorPages = UseErrorPages();
2719 return NS_OK;
2722 NS_IMETHODIMP
2723 nsDocShell::SetUseErrorPages(bool aUseErrorPages)
2725 // If mUseErrorPages is set explicitly, stop using sUseErrorPages.
2726 if (mObserveErrorPages) {
2727 mObserveErrorPages = false;
2729 mUseErrorPages = aUseErrorPages;
2730 return NS_OK;
2733 NS_IMETHODIMP
2734 nsDocShell::GetPreviousTransIndex(int32_t *aPreviousTransIndex)
2736 *aPreviousTransIndex = mPreviousTransIndex;
2737 return NS_OK;
2740 NS_IMETHODIMP
2741 nsDocShell::GetLoadedTransIndex(int32_t *aLoadedTransIndex)
2743 *aLoadedTransIndex = mLoadedTransIndex;
2744 return NS_OK;
2747 NS_IMETHODIMP
2748 nsDocShell::HistoryPurged(int32_t aNumEntries)
2750 // These indices are used for fastback cache eviction, to determine
2751 // which session history entries are candidates for content viewer
2752 // eviction. We need to adjust by the number of entries that we
2753 // just purged from history, so that we look at the right session history
2754 // entries during eviction.
2755 mPreviousTransIndex = std::max(-1, mPreviousTransIndex - aNumEntries);
2756 mLoadedTransIndex = std::max(0, mLoadedTransIndex - aNumEntries);
2758 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
2759 while (iter.HasMore()) {
2760 nsCOMPtr<nsIDocShell> shell = do_QueryObject(iter.GetNext());
2761 if (shell) {
2762 shell->HistoryPurged(aNumEntries);
2766 return NS_OK;
2769 nsresult
2770 nsDocShell::HistoryTransactionRemoved(int32_t aIndex)
2772 // These indices are used for fastback cache eviction, to determine
2773 // which session history entries are candidates for content viewer
2774 // eviction. We need to adjust by the number of entries that we
2775 // just purged from history, so that we look at the right session history
2776 // entries during eviction.
2777 if (aIndex == mPreviousTransIndex) {
2778 mPreviousTransIndex = -1;
2779 } else if (aIndex < mPreviousTransIndex) {
2780 --mPreviousTransIndex;
2782 if (mLoadedTransIndex == aIndex) {
2783 mLoadedTransIndex = 0;
2784 } else if (aIndex < mLoadedTransIndex) {
2785 --mLoadedTransIndex;
2788 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
2789 while (iter.HasMore()) {
2790 nsCOMPtr<nsIDocShell> shell = do_QueryObject(iter.GetNext());
2791 if (shell) {
2792 static_cast<nsDocShell*>(shell.get())->
2793 HistoryTransactionRemoved(aIndex);
2797 return NS_OK;
2800 nsIDOMStorageManager*
2801 nsDocShell::TopSessionStorageManager()
2803 nsresult rv;
2805 nsCOMPtr<nsIDocShellTreeItem> topItem;
2806 rv = GetSameTypeRootTreeItem(getter_AddRefs(topItem));
2807 if (NS_FAILED(rv)) {
2808 return nullptr;
2811 if (!topItem) {
2812 return nullptr;
2815 nsDocShell* topDocShell = static_cast<nsDocShell*>(topItem.get());
2816 if (topDocShell != this) {
2817 return topDocShell->TopSessionStorageManager();
2820 if (!mSessionStorageManager) {
2821 mSessionStorageManager =
2822 do_CreateInstance("@mozilla.org/dom/sessionStorage-manager;1");
2825 return mSessionStorageManager;
2828 NS_IMETHODIMP
2829 nsDocShell::GetSessionStorageForPrincipal(nsIPrincipal* aPrincipal,
2830 const nsAString& aDocumentURI,
2831 bool aCreate,
2832 nsIDOMStorage** aStorage)
2834 nsCOMPtr<nsIDOMStorageManager> manager = TopSessionStorageManager();
2835 if (!manager) {
2836 return NS_ERROR_UNEXPECTED;
2839 nsCOMPtr<nsIDOMWindow> domWin = do_GetInterface(GetAsSupports(this));
2841 if (aCreate) {
2842 return manager->CreateStorage(domWin, aPrincipal, aDocumentURI,
2843 mInPrivateBrowsing, aStorage);
2846 return manager->GetStorage(domWin, aPrincipal, mInPrivateBrowsing,
2847 aStorage);
2850 nsresult
2851 nsDocShell::AddSessionStorage(nsIPrincipal* aPrincipal,
2852 nsIDOMStorage* aStorage)
2854 nsRefPtr<DOMStorage> storage = static_cast<DOMStorage*>(aStorage);
2855 if (!storage) {
2856 return NS_ERROR_UNEXPECTED;
2859 nsIPrincipal* storagePrincipal = storage->GetPrincipal();
2860 if (storagePrincipal != aPrincipal) {
2861 NS_ERROR("Wanting to add a sessionStorage for different principal");
2862 return NS_ERROR_DOM_SECURITY_ERR;
2865 nsCOMPtr<nsIDOMStorageManager> manager = TopSessionStorageManager();
2866 if (!manager) {
2867 return NS_ERROR_UNEXPECTED;
2870 return manager->CloneStorage(aStorage);
2873 NS_IMETHODIMP
2874 nsDocShell::GetCurrentDocumentChannel(nsIChannel** aResult)
2876 NS_IF_ADDREF(*aResult = GetCurrentDocChannel());
2877 return NS_OK;
2880 nsIChannel*
2881 nsDocShell::GetCurrentDocChannel()
2883 if (mContentViewer) {
2884 nsIDocument* doc = mContentViewer->GetDocument();
2885 if (doc) {
2886 return doc->GetChannel();
2889 return nullptr;
2892 NS_IMETHODIMP
2893 nsDocShell::AddWeakScrollObserver(nsIScrollObserver* aObserver)
2895 nsWeakPtr weakObs = do_GetWeakReference(aObserver);
2896 if (!weakObs) {
2897 return NS_ERROR_FAILURE;
2899 return mScrollObservers.AppendElement(weakObs) ? NS_OK : NS_ERROR_FAILURE;
2902 NS_IMETHODIMP
2903 nsDocShell::RemoveWeakScrollObserver(nsIScrollObserver* aObserver)
2905 nsWeakPtr obs = do_GetWeakReference(aObserver);
2906 return mScrollObservers.RemoveElement(obs) ? NS_OK : NS_ERROR_FAILURE;
2909 void
2910 nsDocShell::NotifyAsyncPanZoomStarted()
2912 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
2913 while (iter.HasMore()) {
2914 nsWeakPtr ref = iter.GetNext();
2915 nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
2916 if (obs) {
2917 obs->AsyncPanZoomStarted();
2918 } else {
2919 mScrollObservers.RemoveElement(ref);
2924 void
2925 nsDocShell::NotifyAsyncPanZoomStopped()
2927 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
2928 while (iter.HasMore()) {
2929 nsWeakPtr ref = iter.GetNext();
2930 nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
2931 if (obs) {
2932 obs->AsyncPanZoomStopped();
2933 } else {
2934 mScrollObservers.RemoveElement(ref);
2939 NS_IMETHODIMP
2940 nsDocShell::NotifyScrollObservers()
2942 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
2943 while (iter.HasMore()) {
2944 nsWeakPtr ref = iter.GetNext();
2945 nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
2946 if (obs) {
2947 obs->ScrollPositionChanged();
2948 } else {
2949 mScrollObservers.RemoveElement(ref);
2952 return NS_OK;
2955 //*****************************************************************************
2956 // nsDocShell::nsIDocShellTreeItem
2957 //*****************************************************************************
2959 NS_IMETHODIMP
2960 nsDocShell::GetName(nsAString& aName)
2962 aName = mName;
2963 return NS_OK;
2966 NS_IMETHODIMP
2967 nsDocShell::SetName(const nsAString& aName)
2969 mName = aName;
2970 return NS_OK;
2973 NS_IMETHODIMP
2974 nsDocShell::NameEquals(const char16_t *aName, bool *_retval)
2976 NS_ENSURE_ARG_POINTER(aName);
2977 NS_ENSURE_ARG_POINTER(_retval);
2978 *_retval = mName.Equals(aName);
2979 return NS_OK;
2982 /* virtual */ int32_t
2983 nsDocShell::ItemType()
2985 return mItemType;
2988 NS_IMETHODIMP
2989 nsDocShell::GetItemType(int32_t * aItemType)
2991 NS_ENSURE_ARG_POINTER(aItemType);
2993 *aItemType = ItemType();
2994 return NS_OK;
2997 NS_IMETHODIMP
2998 nsDocShell::SetItemType(int32_t aItemType)
3000 NS_ENSURE_ARG((aItemType == typeChrome) || (typeContent == aItemType));
3002 // Only allow setting the type on root docshells. Those would be the ones
3003 // that have the docloader service as mParent or have no mParent at all.
3004 nsCOMPtr<nsIDocumentLoader> docLoaderService =
3005 do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID);
3006 NS_ENSURE_TRUE(docLoaderService, NS_ERROR_UNEXPECTED);
3008 NS_ENSURE_STATE(!mParent || mParent == docLoaderService);
3010 mItemType = aItemType;
3012 // disable auth prompting for anything but content
3013 mAllowAuth = mItemType == typeContent;
3015 nsRefPtr<nsPresContext> presContext = nullptr;
3016 GetPresContext(getter_AddRefs(presContext));
3017 if (presContext) {
3018 presContext->UpdateIsChrome();
3021 return NS_OK;
3024 NS_IMETHODIMP
3025 nsDocShell::GetParent(nsIDocShellTreeItem ** aParent)
3027 if (!mParent) {
3028 *aParent = nullptr;
3029 } else {
3030 CallQueryInterface(mParent, aParent);
3032 // Note that in the case when the parent is not an nsIDocShellTreeItem we
3033 // don't want to throw; we just want to return null.
3034 return NS_OK;
3037 already_AddRefed<nsDocShell>
3038 nsDocShell::GetParentDocshell()
3040 nsCOMPtr<nsIDocShell> docshell = do_QueryInterface(GetAsSupports(mParent));
3041 return docshell.forget().downcast<nsDocShell>();
3044 void
3045 nsDocShell::RecomputeCanExecuteScripts()
3047 bool old = mCanExecuteScripts;
3048 nsRefPtr<nsDocShell> parent = GetParentDocshell();
3050 // If we have no tree owner, that means that we've been detached from the
3051 // docshell tree (this is distinct from having no parent dochshell, which
3052 // is the case for root docshells). It would be nice to simply disallow
3053 // script in detached docshells, but bug 986542 demonstrates that this
3054 // behavior breaks at least one website.
3056 // So instead, we use our previous value, unless mAllowJavascript has been
3057 // explicitly set to false.
3058 if (!mTreeOwner) {
3059 mCanExecuteScripts = mCanExecuteScripts && mAllowJavascript;
3060 // If scripting has been explicitly disabled on our docshell, we're done.
3061 } else if (!mAllowJavascript) {
3062 mCanExecuteScripts = false;
3063 // If we have a parent, inherit.
3064 } else if (parent) {
3065 mCanExecuteScripts = parent->mCanExecuteScripts;
3066 // Otherwise, we're the root of the tree, and we haven't explicitly disabled
3067 // script. Allow.
3068 } else {
3069 mCanExecuteScripts = true;
3072 // Inform our active DOM window.
3074 // This will pass the outer, which will be in the scope of the active inner.
3075 if (mScriptGlobal && mScriptGlobal->GetGlobalJSObject()) {
3076 xpc::Scriptability& scriptability =
3077 xpc::Scriptability::Get(mScriptGlobal->GetGlobalJSObject());
3078 scriptability.SetDocShellAllowsScript(mCanExecuteScripts);
3081 // If our value has changed, our children might be affected. Recompute their
3082 // value as well.
3083 if (old != mCanExecuteScripts) {
3084 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
3085 while (iter.HasMore()) {
3086 static_cast<nsDocShell*>(iter.GetNext())->RecomputeCanExecuteScripts();
3091 nsresult
3092 nsDocShell::SetDocLoaderParent(nsDocLoader * aParent)
3094 bool wasFrame = IsFrame();
3096 nsDocLoader::SetDocLoaderParent(aParent);
3098 nsCOMPtr<nsISupportsPriority> priorityGroup = do_QueryInterface(mLoadGroup);
3099 if (wasFrame != IsFrame() && priorityGroup) {
3100 priorityGroup->AdjustPriority(wasFrame ? -1 : 1);
3103 // Curse ambiguous nsISupports inheritance!
3104 nsISupports* parent = GetAsSupports(aParent);
3106 // If parent is another docshell, we inherit all their flags for
3107 // allowing plugins, scripting etc.
3108 bool value;
3109 nsCOMPtr<nsIDocShell> parentAsDocShell(do_QueryInterface(parent));
3110 if (parentAsDocShell)
3112 if (NS_SUCCEEDED(parentAsDocShell->GetAllowPlugins(&value)))
3114 SetAllowPlugins(value);
3116 if (NS_SUCCEEDED(parentAsDocShell->GetAllowJavascript(&value)))
3118 SetAllowJavascript(value);
3120 if (NS_SUCCEEDED(parentAsDocShell->GetAllowMetaRedirects(&value)))
3122 SetAllowMetaRedirects(value);
3124 if (NS_SUCCEEDED(parentAsDocShell->GetAllowSubframes(&value)))
3126 SetAllowSubframes(value);
3128 if (NS_SUCCEEDED(parentAsDocShell->GetAllowImages(&value)))
3130 SetAllowImages(value);
3132 SetAllowMedia(parentAsDocShell->GetAllowMedia());
3133 if (NS_SUCCEEDED(parentAsDocShell->GetAllowWindowControl(&value)))
3135 SetAllowWindowControl(value);
3137 SetAllowContentRetargeting(
3138 parentAsDocShell->GetAllowContentRetargeting());
3139 if (NS_SUCCEEDED(parentAsDocShell->GetIsActive(&value)))
3141 SetIsActive(value);
3143 if (NS_FAILED(parentAsDocShell->GetAllowDNSPrefetch(&value))) {
3144 value = false;
3146 SetAllowDNSPrefetch(value);
3147 value = parentAsDocShell->GetAffectPrivateSessionLifetime();
3148 SetAffectPrivateSessionLifetime(value);
3149 uint32_t flags;
3150 if (NS_SUCCEEDED(parentAsDocShell->GetDefaultLoadFlags(&flags)))
3152 SetDefaultLoadFlags(flags);
3157 nsCOMPtr<nsILoadContext> parentAsLoadContext(do_QueryInterface(parent));
3158 if (parentAsLoadContext &&
3159 NS_SUCCEEDED(parentAsLoadContext->GetUsePrivateBrowsing(&value)))
3161 SetPrivateBrowsing(value);
3164 nsCOMPtr<nsIURIContentListener> parentURIListener(do_GetInterface(parent));
3165 if (parentURIListener)
3166 mContentListener->SetParentContentListener(parentURIListener);
3168 // Our parent has changed. Recompute scriptability.
3169 RecomputeCanExecuteScripts();
3171 return NS_OK;
3174 NS_IMETHODIMP
3175 nsDocShell::GetSameTypeParent(nsIDocShellTreeItem ** aParent)
3177 NS_ENSURE_ARG_POINTER(aParent);
3178 *aParent = nullptr;
3180 if (nsIDocShell::GetIsBrowserOrApp()) {
3181 return NS_OK;
3184 nsCOMPtr<nsIDocShellTreeItem> parent =
3185 do_QueryInterface(GetAsSupports(mParent));
3186 if (!parent)
3187 return NS_OK;
3189 if (parent->ItemType() == mItemType) {
3190 parent.swap(*aParent);
3192 return NS_OK;
3195 NS_IMETHODIMP
3196 nsDocShell::GetSameTypeParentIgnoreBrowserAndAppBoundaries(nsIDocShell** aParent)
3198 NS_ENSURE_ARG_POINTER(aParent);
3199 *aParent = nullptr;
3201 nsCOMPtr<nsIDocShellTreeItem> parent =
3202 do_QueryInterface(GetAsSupports(mParent));
3203 if (!parent)
3204 return NS_OK;
3206 if (parent->ItemType() == mItemType) {
3207 nsCOMPtr<nsIDocShell> parentDS = do_QueryInterface(parent);
3208 parentDS.forget(aParent);
3210 return NS_OK;
3213 NS_IMETHODIMP
3214 nsDocShell::GetRootTreeItem(nsIDocShellTreeItem ** aRootTreeItem)
3216 NS_ENSURE_ARG_POINTER(aRootTreeItem);
3218 nsRefPtr<nsDocShell> root = this;
3219 nsRefPtr<nsDocShell> parent = root->GetParentDocshell();
3220 while (parent) {
3221 root = parent;
3222 parent = root->GetParentDocshell();
3225 root.forget(aRootTreeItem);
3226 return NS_OK;
3229 NS_IMETHODIMP
3230 nsDocShell::GetSameTypeRootTreeItem(nsIDocShellTreeItem ** aRootTreeItem)
3232 NS_ENSURE_ARG_POINTER(aRootTreeItem);
3233 *aRootTreeItem = static_cast<nsIDocShellTreeItem *>(this);
3235 nsCOMPtr<nsIDocShellTreeItem> parent;
3236 NS_ENSURE_SUCCESS(GetSameTypeParent(getter_AddRefs(parent)),
3237 NS_ERROR_FAILURE);
3238 while (parent) {
3239 *aRootTreeItem = parent;
3240 NS_ENSURE_SUCCESS((*aRootTreeItem)->
3241 GetSameTypeParent(getter_AddRefs(parent)),
3242 NS_ERROR_FAILURE);
3244 NS_ADDREF(*aRootTreeItem);
3245 return NS_OK;
3248 /* static */
3249 bool
3250 nsDocShell::CanAccessItem(nsIDocShellTreeItem* aTargetItem,
3251 nsIDocShellTreeItem* aAccessingItem,
3252 bool aConsiderOpener)
3254 NS_PRECONDITION(aTargetItem, "Must have target item!");
3256 if (!gValidateOrigin || !aAccessingItem) {
3257 // Good to go
3258 return true;
3261 // XXXbz should we care if aAccessingItem or the document therein is
3262 // chrome? Should those get extra privileges?
3264 // For historical context, see:
3266 // Bug 13871: Prevent frameset spoofing
3267 // Bug 103638: Targets with same name in different windows open in wrong
3268 // window with javascript
3269 // Bug 408052: Adopt "ancestor" frame navigation policy
3271 // Now do a security check.
3273 // Disallow navigation if the two frames are not part of the same app, or if
3274 // they have different is-in-browser-element states.
3276 // Allow navigation if
3277 // 1) aAccessingItem can script aTargetItem or one of its ancestors in
3278 // the frame hierarchy or
3279 // 2) aTargetItem is a top-level frame and aAccessingItem is its descendant
3280 // 3) aTargetItem is a top-level frame and aAccessingItem can target
3281 // its opener per rule (1) or (2).
3283 if (aTargetItem == aAccessingItem) {
3284 // A frame is allowed to navigate itself.
3285 return true;
3288 nsCOMPtr<nsIDocShell> targetDS = do_QueryInterface(aTargetItem);
3289 nsCOMPtr<nsIDocShell> accessingDS = do_QueryInterface(aAccessingItem);
3290 if (!!targetDS != !!accessingDS) {
3291 // We must be able to convert both or neither to nsIDocShell.
3292 return false;
3295 if (targetDS && accessingDS &&
3296 (targetDS->GetIsInBrowserElement() !=
3297 accessingDS->GetIsInBrowserElement() ||
3298 targetDS->GetAppId() != accessingDS->GetAppId())) {
3299 return false;
3302 nsCOMPtr<nsIDocShellTreeItem> accessingRoot;
3303 aAccessingItem->GetSameTypeRootTreeItem(getter_AddRefs(accessingRoot));
3305 if (aTargetItem == accessingRoot) {
3306 // A frame can navigate its root.
3307 return true;
3310 // Check if aAccessingItem can navigate one of aTargetItem's ancestors.
3311 nsCOMPtr<nsIDocShellTreeItem> target = aTargetItem;
3312 do {
3313 if (ValidateOrigin(aAccessingItem, target)) {
3314 return true;
3317 nsCOMPtr<nsIDocShellTreeItem> parent;
3318 target->GetSameTypeParent(getter_AddRefs(parent));
3319 parent.swap(target);
3320 } while (target);
3322 nsCOMPtr<nsIDocShellTreeItem> targetRoot;
3323 aTargetItem->GetSameTypeRootTreeItem(getter_AddRefs(targetRoot));
3325 if (aTargetItem != targetRoot) {
3326 // target is a subframe, not in accessor's frame hierarchy, and all its
3327 // ancestors have origins different from that of the accessor. Don't
3328 // allow access.
3329 return false;
3332 if (!aConsiderOpener) {
3333 // All done here
3334 return false;
3337 nsCOMPtr<nsIDOMWindow> targetWindow = aTargetItem->GetWindow();
3338 if (!targetWindow) {
3339 NS_ERROR("This should not happen, really");
3340 return false;
3343 nsCOMPtr<nsIDOMWindow> targetOpener;
3344 targetWindow->GetOpener(getter_AddRefs(targetOpener));
3345 nsCOMPtr<nsIWebNavigation> openerWebNav(do_GetInterface(targetOpener));
3346 nsCOMPtr<nsIDocShellTreeItem> openerItem(do_QueryInterface(openerWebNav));
3348 if (!openerItem) {
3349 return false;
3352 return CanAccessItem(openerItem, aAccessingItem, false);
3355 static bool
3356 ItemIsActive(nsIDocShellTreeItem *aItem)
3358 nsCOMPtr<nsIDOMWindow> window = aItem->GetWindow();
3360 if (window) {
3361 bool isClosed;
3363 if (NS_SUCCEEDED(window->GetClosed(&isClosed)) && !isClosed) {
3364 return true;
3368 return false;
3371 NS_IMETHODIMP
3372 nsDocShell::FindItemWithName(const char16_t * aName,
3373 nsISupports * aRequestor,
3374 nsIDocShellTreeItem * aOriginalRequestor,
3375 nsIDocShellTreeItem ** _retval)
3377 NS_ENSURE_ARG(aName);
3378 NS_ENSURE_ARG_POINTER(_retval);
3380 // If we don't find one, we return NS_OK and a null result
3381 *_retval = nullptr;
3383 if (!*aName)
3384 return NS_OK;
3386 if (aRequestor) {
3387 // If aRequestor is not null we don't need to check special names, so
3388 // just hand straight off to the search by actual name function.
3389 return DoFindItemWithName(aName, aRequestor, aOriginalRequestor,
3390 _retval);
3391 } else {
3393 // This is the entry point into the target-finding algorithm. Check
3394 // for special names. This should only be done once, hence the check
3395 // for a null aRequestor.
3397 nsCOMPtr<nsIDocShellTreeItem> foundItem;
3398 nsDependentString name(aName);
3399 if (name.LowerCaseEqualsLiteral("_self")) {
3400 foundItem = this;
3402 else if (name.LowerCaseEqualsLiteral("_blank"))
3404 // Just return null. Caller must handle creating a new window with
3405 // a blank name himself.
3406 return NS_OK;
3408 else if (name.LowerCaseEqualsLiteral("_parent"))
3410 GetSameTypeParent(getter_AddRefs(foundItem));
3411 if(!foundItem)
3412 foundItem = this;
3414 else if (name.LowerCaseEqualsLiteral("_top"))
3416 GetSameTypeRootTreeItem(getter_AddRefs(foundItem));
3417 NS_ASSERTION(foundItem, "Must have this; worst case it's us!");
3419 // _main is an IE target which should be case-insensitive but isn't
3420 // see bug 217886 for details
3421 else if (name.LowerCaseEqualsLiteral("_content") ||
3422 name.EqualsLiteral("_main"))
3424 // Must pass our same type root as requestor to the
3425 // treeowner to make sure things work right.
3426 nsCOMPtr<nsIDocShellTreeItem> root;
3427 GetSameTypeRootTreeItem(getter_AddRefs(root));
3428 if (mTreeOwner) {
3429 NS_ASSERTION(root, "Must have this; worst case it's us!");
3430 mTreeOwner->FindItemWithName(aName, root, aOriginalRequestor,
3431 getter_AddRefs(foundItem));
3433 #ifdef DEBUG
3434 else {
3435 NS_ERROR("Someone isn't setting up the tree owner. "
3436 "You might like to try that. "
3437 "Things will.....you know, work.");
3438 // Note: _content should always exist. If we don't have one
3439 // hanging off the treeowner, just create a named window....
3440 // so don't return here, in case we did that and can now find
3441 // it.
3442 // XXXbz should we be using |root| instead of creating
3443 // a new window?
3445 #endif
3446 } else {
3447 // Do the search for item by an actual name.
3448 DoFindItemWithName(aName, aRequestor, aOriginalRequestor,
3449 getter_AddRefs(foundItem));
3452 if (foundItem && !CanAccessItem(foundItem, aOriginalRequestor)) {
3453 foundItem = nullptr;
3456 // DoFindItemWithName only returns active items and we don't check if
3457 // the item is active for the special cases.
3458 if (foundItem) {
3459 foundItem.swap(*_retval);
3461 return NS_OK;
3465 nsresult
3466 nsDocShell::DoFindItemWithName(const char16_t* aName,
3467 nsISupports* aRequestor,
3468 nsIDocShellTreeItem* aOriginalRequestor,
3469 nsIDocShellTreeItem** _retval)
3471 // First we check our name.
3472 if (mName.Equals(aName) && ItemIsActive(this) &&
3473 CanAccessItem(this, aOriginalRequestor)) {
3474 NS_ADDREF(*_retval = this);
3475 return NS_OK;
3478 // This QI may fail, but the places where we want to compare, comparing
3479 // against nullptr serves the same purpose.
3480 nsCOMPtr<nsIDocShellTreeItem> reqAsTreeItem(do_QueryInterface(aRequestor));
3482 // Second we check our children making sure not to ask a child if
3483 // it is the aRequestor.
3484 #ifdef DEBUG
3485 nsresult rv =
3486 #endif
3487 FindChildWithName(aName, true, true, reqAsTreeItem,
3488 aOriginalRequestor, _retval);
3489 NS_ASSERTION(NS_SUCCEEDED(rv),
3490 "FindChildWithName should not be failing here.");
3491 if (*_retval)
3492 return NS_OK;
3494 // Third if we have a parent and it isn't the requestor then we
3495 // should ask it to do the search. If it is the requestor we
3496 // should just stop here and let the parent do the rest. If we
3497 // don't have a parent, then we should ask the
3498 // docShellTreeOwner to do the search.
3499 nsCOMPtr<nsIDocShellTreeItem> parentAsTreeItem =
3500 do_QueryInterface(GetAsSupports(mParent));
3501 if (parentAsTreeItem) {
3502 if (parentAsTreeItem == reqAsTreeItem)
3503 return NS_OK;
3505 if (parentAsTreeItem->ItemType() == mItemType) {
3506 return parentAsTreeItem->
3507 FindItemWithName(aName,
3508 static_cast<nsIDocShellTreeItem*>
3509 (this),
3510 aOriginalRequestor,
3511 _retval);
3515 // If the parent is null or not of the same type fall through and ask tree
3516 // owner.
3518 // This may fail, but comparing against null serves the same purpose
3519 nsCOMPtr<nsIDocShellTreeOwner>
3520 reqAsTreeOwner(do_QueryInterface(aRequestor));
3522 if (mTreeOwner && mTreeOwner != reqAsTreeOwner) {
3523 return mTreeOwner->
3524 FindItemWithName(aName, this, aOriginalRequestor, _retval);
3527 return NS_OK;
3530 bool
3531 nsDocShell::IsSandboxedFrom(nsIDocShell* aTargetDocShell)
3533 // If no target then not sandboxed.
3534 if (!aTargetDocShell) {
3535 return false;
3538 // We cannot be sandboxed from ourselves.
3539 if (aTargetDocShell == this) {
3540 return false;
3543 // Default the sandbox flags to our flags, so that if we can't retrieve the
3544 // active document, we will still enforce our own.
3545 uint32_t sandboxFlags = mSandboxFlags;
3546 if (mContentViewer) {
3547 nsCOMPtr<nsIDocument> doc = mContentViewer->GetDocument();
3548 if (doc) {
3549 sandboxFlags = doc->GetSandboxFlags();
3553 // If no flags, we are not sandboxed at all.
3554 if (!sandboxFlags) {
3555 return false;
3558 // If aTargetDocShell has an ancestor, it is not top level.
3559 nsCOMPtr<nsIDocShellTreeItem> ancestorOfTarget;
3560 aTargetDocShell->GetSameTypeParent(getter_AddRefs(ancestorOfTarget));
3561 if (ancestorOfTarget) {
3562 do {
3563 // We are not sandboxed if we are an ancestor of target.
3564 if (ancestorOfTarget == this) {
3565 return false;
3567 nsCOMPtr<nsIDocShellTreeItem> tempTreeItem;
3568 ancestorOfTarget->GetSameTypeParent(getter_AddRefs(tempTreeItem));
3569 tempTreeItem.swap(ancestorOfTarget);
3570 } while (ancestorOfTarget);
3572 // Otherwise, we are sandboxed from aTargetDocShell.
3573 return true;
3576 // aTargetDocShell is top level, are we the "one permitted sandboxed
3577 // navigator", i.e. did we open aTargetDocShell?
3578 nsCOMPtr<nsIDocShell> permittedNavigator;
3579 aTargetDocShell->
3580 GetOnePermittedSandboxedNavigator(getter_AddRefs(permittedNavigator));
3581 if (permittedNavigator == this) {
3582 return false;
3585 // If SANDBOXED_TOPLEVEL_NAVIGATION flag is not on, we are not sandboxed
3586 // from our top.
3587 if (!(sandboxFlags & SANDBOXED_TOPLEVEL_NAVIGATION)) {
3588 nsCOMPtr<nsIDocShellTreeItem> rootTreeItem;
3589 GetSameTypeRootTreeItem(getter_AddRefs(rootTreeItem));
3590 if (SameCOMIdentity(aTargetDocShell, rootTreeItem)) {
3591 return false;
3595 // Otherwise, we are sandboxed from aTargetDocShell.
3596 return true;
3599 NS_IMETHODIMP
3600 nsDocShell::GetTreeOwner(nsIDocShellTreeOwner ** aTreeOwner)
3602 NS_ENSURE_ARG_POINTER(aTreeOwner);
3604 *aTreeOwner = mTreeOwner;
3605 NS_IF_ADDREF(*aTreeOwner);
3606 return NS_OK;
3609 #ifdef DEBUG_DOCSHELL_FOCUS
3610 static void
3611 PrintDocTree(nsIDocShellTreeItem * aParentNode, int aLevel)
3613 for (int32_t i=0;i<aLevel;i++) printf(" ");
3615 int32_t childWebshellCount;
3616 aParentNode->GetChildCount(&childWebshellCount);
3617 nsCOMPtr<nsIDocShell> parentAsDocShell(do_QueryInterface(aParentNode));
3618 int32_t type = aParentNode->ItemType();
3619 nsCOMPtr<nsIPresShell> presShell = parentAsDocShell->GetPresShell();
3620 nsRefPtr<nsPresContext> presContext;
3621 parentAsDocShell->GetPresContext(getter_AddRefs(presContext));
3622 nsIDocument *doc = presShell->GetDocument();
3624 nsCOMPtr<nsIDOMWindow> domwin(doc->GetWindow());
3626 nsCOMPtr<nsIWidget> widget;
3627 nsViewManager* vm = presShell->GetViewManager();
3628 if (vm) {
3629 vm->GetWidget(getter_AddRefs(widget));
3631 dom::Element* rootElement = doc->GetRootElement();
3633 printf("DS %p Ty %s Doc %p DW %p EM %p CN %p\n",
3634 (void*)parentAsDocShell.get(),
3635 type==nsIDocShellTreeItem::typeChrome?"Chr":"Con",
3636 (void*)doc, (void*)domwin.get(),
3637 (void*)presContext->EventStateManager(), (void*)rootElement);
3639 if (childWebshellCount > 0) {
3640 for (int32_t i=0;i<childWebshellCount;i++) {
3641 nsCOMPtr<nsIDocShellTreeItem> child;
3642 aParentNode->GetChildAt(i, getter_AddRefs(child));
3643 PrintDocTree(child, aLevel+1);
3648 static void
3649 PrintDocTree(nsIDocShellTreeItem * aParentNode)
3651 NS_ASSERTION(aParentNode, "Pointer is null!");
3653 nsCOMPtr<nsIDocShellTreeItem> parentItem;
3654 aParentNode->GetParent(getter_AddRefs(parentItem));
3655 while (parentItem) {
3656 nsCOMPtr<nsIDocShellTreeItem>tmp;
3657 parentItem->GetParent(getter_AddRefs(tmp));
3658 if (!tmp) {
3659 break;
3661 parentItem = tmp;
3664 if (!parentItem) {
3665 parentItem = aParentNode;
3668 PrintDocTree(parentItem, 0);
3670 #endif
3672 NS_IMETHODIMP
3673 nsDocShell::SetTreeOwner(nsIDocShellTreeOwner * aTreeOwner)
3675 #ifdef DEBUG_DOCSHELL_FOCUS
3676 nsCOMPtr<nsIDocShellTreeItem> item(do_QueryInterface(aTreeOwner));
3677 if (item) {
3678 PrintDocTree(item);
3680 #endif
3682 // Don't automatically set the progress based on the tree owner for frames
3683 if (!IsFrame()) {
3684 nsCOMPtr<nsIWebProgress> webProgress =
3685 do_QueryInterface(GetAsSupports(this));
3687 if (webProgress) {
3688 nsCOMPtr<nsIWebProgressListener>
3689 oldListener(do_QueryInterface(mTreeOwner));
3690 nsCOMPtr<nsIWebProgressListener>
3691 newListener(do_QueryInterface(aTreeOwner));
3693 if (oldListener) {
3694 webProgress->RemoveProgressListener(oldListener);
3697 if (newListener) {
3698 webProgress->AddProgressListener(newListener,
3699 nsIWebProgress::NOTIFY_ALL);
3704 mTreeOwner = aTreeOwner; // Weak reference per API
3706 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
3707 while (iter.HasMore()) {
3708 nsCOMPtr<nsIDocShellTreeItem> child = do_QueryObject(iter.GetNext());
3709 NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
3711 if (child->ItemType() == mItemType)
3712 child->SetTreeOwner(aTreeOwner);
3715 // Our tree owner has changed. Recompute scriptability.
3717 // Note that this is near-redundant with the recomputation in
3718 // SetDocLoaderParent(), but not so for the root DocShell, where the call to
3719 // SetTreeOwner() happens after the initial AddDocLoaderAsChildOfRoot(),
3720 // and we never set another parent. Given that this is neither expensive nor
3721 // performance-critical, let's be safe and unconditionally recompute this
3722 // state whenever dependent state changes.
3723 RecomputeCanExecuteScripts();
3725 return NS_OK;
3728 NS_IMETHODIMP
3729 nsDocShell::SetChildOffset(uint32_t aChildOffset)
3731 mChildOffset = aChildOffset;
3732 return NS_OK;
3735 NS_IMETHODIMP
3736 nsDocShell::GetHistoryID(uint64_t* aID)
3738 *aID = mHistoryID;
3739 return NS_OK;
3742 NS_IMETHODIMP
3743 nsDocShell::GetIsInUnload(bool* aIsInUnload)
3745 *aIsInUnload = mFiredUnloadEvent;
3746 return NS_OK;
3749 NS_IMETHODIMP
3750 nsDocShell::GetChildCount(int32_t * aChildCount)
3752 NS_ENSURE_ARG_POINTER(aChildCount);
3753 *aChildCount = mChildList.Length();
3754 return NS_OK;
3759 NS_IMETHODIMP
3760 nsDocShell::AddChild(nsIDocShellTreeItem * aChild)
3762 NS_ENSURE_ARG_POINTER(aChild);
3764 nsRefPtr<nsDocLoader> childAsDocLoader = GetAsDocLoader(aChild);
3765 NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED);
3767 // Make sure we're not creating a loop in the docshell tree
3768 nsDocLoader* ancestor = this;
3769 do {
3770 if (childAsDocLoader == ancestor) {
3771 return NS_ERROR_ILLEGAL_VALUE;
3773 ancestor = ancestor->GetParent();
3774 } while (ancestor);
3776 // Make sure to remove the child from its current parent.
3777 nsDocLoader* childsParent = childAsDocLoader->GetParent();
3778 if (childsParent) {
3779 childsParent->RemoveChildLoader(childAsDocLoader);
3782 // Make sure to clear the treeowner in case this child is a different type
3783 // from us.
3784 aChild->SetTreeOwner(nullptr);
3786 nsresult res = AddChildLoader(childAsDocLoader);
3787 NS_ENSURE_SUCCESS(res, res);
3788 NS_ASSERTION(!mChildList.IsEmpty(),
3789 "child list must not be empty after a successful add");
3791 nsCOMPtr<nsIDocShell> childDocShell = do_QueryInterface(aChild);
3792 bool dynamic = false;
3793 childDocShell->GetCreatedDynamically(&dynamic);
3794 if (!dynamic) {
3795 nsCOMPtr<nsISHEntry> currentSH;
3796 bool oshe = false;
3797 GetCurrentSHEntry(getter_AddRefs(currentSH), &oshe);
3798 if (currentSH) {
3799 currentSH->HasDynamicallyAddedChild(&dynamic);
3802 childDocShell->SetChildOffset(dynamic ? -1 : mChildList.Length() - 1);
3804 /* Set the child's global history if the parent has one */
3805 if (mUseGlobalHistory) {
3806 childDocShell->SetUseGlobalHistory(true);
3809 if (aChild->ItemType() != mItemType) {
3810 return NS_OK;
3813 aChild->SetTreeOwner(mTreeOwner);
3815 nsCOMPtr<nsIDocShell> childAsDocShell(do_QueryInterface(aChild));
3816 if (!childAsDocShell)
3817 return NS_OK;
3819 // charset, style-disabling, and zoom will be inherited in SetupNewViewer()
3821 // Now take this document's charset and set the child's parentCharset field
3822 // to it. We'll later use that field, in the loading process, for the
3823 // charset choosing algorithm.
3824 // If we fail, at any point, we just return NS_OK.
3825 // This code has some performance impact. But this will be reduced when
3826 // the current charset will finally be stored as an Atom, avoiding the
3827 // alias resolution extra look-up.
3829 // we are NOT going to propagate the charset is this Chrome's docshell
3830 if (mItemType == nsIDocShellTreeItem::typeChrome)
3831 return NS_OK;
3833 // get the parent's current charset
3834 if (!mContentViewer)
3835 return NS_OK;
3836 nsIDocument* doc = mContentViewer->GetDocument();
3837 if (!doc)
3838 return NS_OK;
3840 bool isWyciwyg = false;
3842 if (mCurrentURI) {
3843 // Check if the url is wyciwyg
3844 mCurrentURI->SchemeIs("wyciwyg", &isWyciwyg);
3847 if (!isWyciwyg) {
3848 // If this docshell is loaded from a wyciwyg: URI, don't
3849 // advertise our charset since it does not in any way reflect
3850 // the actual source charset, which is what we're trying to
3851 // expose here.
3853 const nsACString &parentCS = doc->GetDocumentCharacterSet();
3854 int32_t charsetSource = doc->GetDocumentCharacterSetSource();
3855 // set the child's parentCharset
3856 childAsDocShell->SetParentCharset(parentCS,
3857 charsetSource,
3858 doc->NodePrincipal());
3861 // printf("### 1 >>> Adding child. Parent CS = %s. ItemType = %d.\n", NS_LossyConvertUTF16toASCII(parentCS).get(), mItemType);
3863 return NS_OK;
3866 NS_IMETHODIMP
3867 nsDocShell::RemoveChild(nsIDocShellTreeItem * aChild)
3869 NS_ENSURE_ARG_POINTER(aChild);
3871 nsRefPtr<nsDocLoader> childAsDocLoader = GetAsDocLoader(aChild);
3872 NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED);
3874 nsresult rv = RemoveChildLoader(childAsDocLoader);
3875 NS_ENSURE_SUCCESS(rv, rv);
3877 aChild->SetTreeOwner(nullptr);
3879 return nsDocLoader::AddDocLoaderAsChildOfRoot(childAsDocLoader);
3882 NS_IMETHODIMP
3883 nsDocShell::GetChildAt(int32_t aIndex, nsIDocShellTreeItem ** aChild)
3885 NS_ENSURE_ARG_POINTER(aChild);
3887 #ifdef DEBUG
3888 if (aIndex < 0) {
3889 NS_WARNING("Negative index passed to GetChildAt");
3890 } else if (static_cast<uint32_t>(aIndex) >= mChildList.Length()) {
3891 NS_WARNING("Too large an index passed to GetChildAt");
3893 #endif
3895 nsIDocumentLoader* child = ChildAt(aIndex);
3896 NS_ENSURE_TRUE(child, NS_ERROR_UNEXPECTED);
3898 return CallQueryInterface(child, aChild);
3901 NS_IMETHODIMP
3902 nsDocShell::FindChildWithName(const char16_t * aName,
3903 bool aRecurse, bool aSameType,
3904 nsIDocShellTreeItem * aRequestor,
3905 nsIDocShellTreeItem * aOriginalRequestor,
3906 nsIDocShellTreeItem ** _retval)
3908 NS_ENSURE_ARG(aName);
3909 NS_ENSURE_ARG_POINTER(_retval);
3911 *_retval = nullptr; // if we don't find one, we return NS_OK and a null result
3913 if (!*aName)
3914 return NS_OK;
3916 nsXPIDLString childName;
3917 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
3918 while (iter.HasMore()) {
3919 nsCOMPtr<nsIDocShellTreeItem> child = do_QueryObject(iter.GetNext());
3920 NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
3921 int32_t childType = child->ItemType();
3923 if (aSameType && (childType != mItemType))
3924 continue;
3926 bool childNameEquals = false;
3927 child->NameEquals(aName, &childNameEquals);
3928 if (childNameEquals && ItemIsActive(child) &&
3929 CanAccessItem(child, aOriginalRequestor)) {
3930 child.swap(*_retval);
3931 break;
3934 if (childType != mItemType) //Only ask it to check children if it is same type
3935 continue;
3937 if (aRecurse && (aRequestor != child)) // Only ask the child if it isn't the requestor
3939 // See if child contains the shell with the given name
3940 #ifdef DEBUG
3941 nsresult rv =
3942 #endif
3943 child->FindChildWithName(aName, true,
3944 aSameType,
3945 static_cast<nsIDocShellTreeItem*>
3946 (this),
3947 aOriginalRequestor,
3948 _retval);
3949 NS_ASSERTION(NS_SUCCEEDED(rv),
3950 "FindChildWithName should not fail here");
3951 if (*_retval) // found it
3952 return NS_OK;
3955 return NS_OK;
3958 NS_IMETHODIMP
3959 nsDocShell::GetChildSHEntry(int32_t aChildOffset, nsISHEntry ** aResult)
3961 nsresult rv = NS_OK;
3963 NS_ENSURE_ARG_POINTER(aResult);
3964 *aResult = nullptr;
3967 // A nsISHEntry for a child is *only* available when the parent is in
3968 // the progress of loading a document too...
3970 if (mLSHE) {
3971 /* Before looking for the subframe's url, check
3972 * the expiration status of the parent. If the parent
3973 * has expired from cache, then subframes will not be
3974 * loaded from history in certain situations.
3976 bool parentExpired=false;
3977 mLSHE->GetExpirationStatus(&parentExpired);
3979 /* Get the parent's Load Type so that it can be set on the child too.
3980 * By default give a loadHistory value
3982 uint32_t loadType = nsIDocShellLoadInfo::loadHistory;
3983 mLSHE->GetLoadType(&loadType);
3984 // If the user did a shift-reload on this frameset page,
3985 // we don't want to load the subframes from history.
3986 if (loadType == nsIDocShellLoadInfo::loadReloadBypassCache ||
3987 loadType == nsIDocShellLoadInfo::loadReloadBypassProxy ||
3988 loadType == nsIDocShellLoadInfo::loadReloadBypassProxyAndCache ||
3989 loadType == nsIDocShellLoadInfo::loadRefresh)
3990 return rv;
3992 /* If the user pressed reload and the parent frame has expired
3993 * from cache, we do not want to load the child frame from history.
3995 if (parentExpired && (loadType == nsIDocShellLoadInfo::loadReloadNormal)) {
3996 // The parent has expired. Return null.
3997 *aResult = nullptr;
3998 return rv;
4001 nsCOMPtr<nsISHContainer> container(do_QueryInterface(mLSHE));
4002 if (container) {
4003 // Get the child subframe from session history.
4004 rv = container->GetChildAt(aChildOffset, aResult);
4005 if (*aResult)
4006 (*aResult)->SetLoadType(loadType);
4009 return rv;
4012 NS_IMETHODIMP
4013 nsDocShell::AddChildSHEntry(nsISHEntry * aCloneRef, nsISHEntry * aNewEntry,
4014 int32_t aChildOffset, uint32_t loadType,
4015 bool aCloneChildren)
4017 nsresult rv;
4019 if (mLSHE && loadType != LOAD_PUSHSTATE) {
4020 /* You get here if you are currently building a
4021 * hierarchy ie.,you just visited a frameset page
4023 nsCOMPtr<nsISHContainer> container(do_QueryInterface(mLSHE, &rv));
4024 if (container) {
4025 rv = container->AddChild(aNewEntry, aChildOffset);
4028 else if (!aCloneRef) {
4029 /* This is an initial load in some subframe. Just append it if we can */
4030 nsCOMPtr<nsISHContainer> container(do_QueryInterface(mOSHE, &rv));
4031 if (container) {
4032 rv = container->AddChild(aNewEntry, aChildOffset);
4035 else if (mSessionHistory) {
4036 /* You are currently in the rootDocShell.
4037 * You will get here when a subframe has a new url
4038 * to load and you have walked up the tree all the
4039 * way to the top to clone the current SHEntry hierarchy
4040 * and replace the subframe where a new url was loaded with
4041 * a new entry.
4043 int32_t index = -1;
4044 nsCOMPtr<nsISHEntry> currentHE;
4045 mSessionHistory->GetIndex(&index);
4046 if (index < 0)
4047 return NS_ERROR_FAILURE;
4049 rv = mSessionHistory->GetEntryAtIndex(index, false,
4050 getter_AddRefs(currentHE));
4051 NS_ENSURE_TRUE(currentHE, NS_ERROR_FAILURE);
4053 nsCOMPtr<nsISHEntry> currentEntry(do_QueryInterface(currentHE));
4054 if (currentEntry) {
4055 uint32_t cloneID = 0;
4056 nsCOMPtr<nsISHEntry> nextEntry;
4057 aCloneRef->GetID(&cloneID);
4058 rv = CloneAndReplace(currentEntry, this, cloneID, aNewEntry,
4059 aCloneChildren, getter_AddRefs(nextEntry));
4061 if (NS_SUCCEEDED(rv)) {
4062 nsCOMPtr<nsISHistoryInternal>
4063 shPrivate(do_QueryInterface(mSessionHistory));
4064 NS_ENSURE_TRUE(shPrivate, NS_ERROR_FAILURE);
4065 rv = shPrivate->AddEntry(nextEntry, true);
4069 else {
4070 /* Just pass this along */
4071 nsCOMPtr<nsIDocShell> parent =
4072 do_QueryInterface(GetAsSupports(mParent), &rv);
4073 if (parent) {
4074 rv = parent->AddChildSHEntry(aCloneRef, aNewEntry, aChildOffset,
4075 loadType, aCloneChildren);
4078 return rv;
4081 nsresult
4082 nsDocShell::DoAddChildSHEntry(nsISHEntry* aNewEntry, int32_t aChildOffset,
4083 bool aCloneChildren)
4085 /* You will get here when you are in a subframe and
4086 * a new url has been loaded on you.
4087 * The mOSHE in this subframe will be the previous url's
4088 * mOSHE. This mOSHE will be used as the identification
4089 * for this subframe in the CloneAndReplace function.
4092 // In this case, we will end up calling AddEntry, which increases the
4093 // current index by 1
4094 nsCOMPtr<nsISHistory> rootSH;
4095 GetRootSessionHistory(getter_AddRefs(rootSH));
4096 if (rootSH) {
4097 rootSH->GetIndex(&mPreviousTransIndex);
4100 nsresult rv;
4101 nsCOMPtr<nsIDocShell> parent =
4102 do_QueryInterface(GetAsSupports(mParent), &rv);
4103 if (parent) {
4104 rv = parent->AddChildSHEntry(mOSHE, aNewEntry, aChildOffset, mLoadType,
4105 aCloneChildren);
4109 if (rootSH) {
4110 rootSH->GetIndex(&mLoadedTransIndex);
4111 #ifdef DEBUG_PAGE_CACHE
4112 printf("Previous index: %d, Loaded index: %d\n\n", mPreviousTransIndex,
4113 mLoadedTransIndex);
4114 #endif
4117 return rv;
4120 NS_IMETHODIMP
4121 nsDocShell::SetUseGlobalHistory(bool aUseGlobalHistory)
4123 nsresult rv;
4125 mUseGlobalHistory = aUseGlobalHistory;
4127 if (!aUseGlobalHistory) {
4128 mGlobalHistory = nullptr;
4129 return NS_OK;
4132 // No need to initialize mGlobalHistory if IHistory is available.
4133 nsCOMPtr<IHistory> history = services::GetHistoryService();
4134 if (history) {
4135 return NS_OK;
4138 if (mGlobalHistory) {
4139 return NS_OK;
4142 mGlobalHistory = do_GetService(NS_GLOBALHISTORY2_CONTRACTID, &rv);
4143 return rv;
4146 NS_IMETHODIMP
4147 nsDocShell::GetUseGlobalHistory(bool *aUseGlobalHistory)
4149 *aUseGlobalHistory = mUseGlobalHistory;
4150 return NS_OK;
4153 NS_IMETHODIMP
4154 nsDocShell::RemoveFromSessionHistory()
4156 nsCOMPtr<nsISHistoryInternal> internalHistory;
4157 nsCOMPtr<nsISHistory> sessionHistory;
4158 nsCOMPtr<nsIDocShellTreeItem> root;
4159 GetSameTypeRootTreeItem(getter_AddRefs(root));
4160 if (root) {
4161 nsCOMPtr<nsIWebNavigation> rootAsWebnav =
4162 do_QueryInterface(root);
4163 if (rootAsWebnav) {
4164 rootAsWebnav->GetSessionHistory(getter_AddRefs(sessionHistory));
4165 internalHistory = do_QueryInterface(sessionHistory);
4168 if (!internalHistory) {
4169 return NS_OK;
4172 int32_t index = 0;
4173 sessionHistory->GetIndex(&index);
4174 nsAutoTArray<uint64_t, 16> ids;
4175 ids.AppendElement(mHistoryID);
4176 internalHistory->RemoveEntries(ids, index);
4177 return NS_OK;
4180 NS_IMETHODIMP
4181 nsDocShell::SetCreatedDynamically(bool aDynamic)
4183 mDynamicallyCreated = aDynamic;
4184 return NS_OK;
4187 NS_IMETHODIMP
4188 nsDocShell::GetCreatedDynamically(bool* aDynamic)
4190 *aDynamic = mDynamicallyCreated;
4191 return NS_OK;
4194 NS_IMETHODIMP
4195 nsDocShell::GetCurrentSHEntry(nsISHEntry** aEntry, bool* aOSHE)
4197 *aOSHE = false;
4198 *aEntry = nullptr;
4199 if (mLSHE) {
4200 NS_ADDREF(*aEntry = mLSHE);
4201 } else if (mOSHE) {
4202 NS_ADDREF(*aEntry = mOSHE);
4203 *aOSHE = true;
4205 return NS_OK;
4208 nsIScriptGlobalObject*
4209 nsDocShell::GetScriptGlobalObject()
4211 NS_ENSURE_SUCCESS(EnsureScriptEnvironment(), nullptr);
4212 return mScriptGlobal;
4215 nsIDocument*
4216 nsDocShell::GetDocument()
4218 NS_ENSURE_SUCCESS(EnsureContentViewer(), nullptr);
4219 return mContentViewer->GetDocument();
4222 nsPIDOMWindow*
4223 nsDocShell::GetWindow()
4225 NS_ENSURE_SUCCESS(EnsureScriptEnvironment(), nullptr);
4226 return mScriptGlobal;
4229 NS_IMETHODIMP
4230 nsDocShell::SetDeviceSizeIsPageSize(bool aValue)
4232 if (mDeviceSizeIsPageSize != aValue) {
4233 mDeviceSizeIsPageSize = aValue;
4234 nsRefPtr<nsPresContext> presContext;
4235 GetPresContext(getter_AddRefs(presContext));
4236 if (presContext) {
4237 presContext->MediaFeatureValuesChanged(presContext->eAlwaysRebuildStyle);
4240 return NS_OK;
4243 NS_IMETHODIMP
4244 nsDocShell::GetDeviceSizeIsPageSize(bool* aValue)
4246 *aValue = mDeviceSizeIsPageSize;
4247 return NS_OK;
4250 void
4251 nsDocShell::ClearFrameHistory(nsISHEntry* aEntry)
4253 nsCOMPtr<nsISHContainer> shcontainer = do_QueryInterface(aEntry);
4254 nsCOMPtr<nsISHistory> rootSH;
4255 GetRootSessionHistory(getter_AddRefs(rootSH));
4256 nsCOMPtr<nsISHistoryInternal> history = do_QueryInterface(rootSH);
4257 if (!history || !shcontainer) {
4258 return;
4261 int32_t count = 0;
4262 shcontainer->GetChildCount(&count);
4263 nsAutoTArray<uint64_t, 16> ids;
4264 for (int32_t i = 0; i < count; ++i) {
4265 nsCOMPtr<nsISHEntry> child;
4266 shcontainer->GetChildAt(i, getter_AddRefs(child));
4267 if (child) {
4268 uint64_t id = 0;
4269 child->GetDocshellID(&id);
4270 ids.AppendElement(id);
4273 int32_t index = 0;
4274 rootSH->GetIndex(&index);
4275 history->RemoveEntries(ids, index);
4278 //-------------------------------------
4279 //-- Helper Method for Print discovery
4280 //-------------------------------------
4281 bool
4282 nsDocShell::IsPrintingOrPP(bool aDisplayErrorDialog)
4284 if (mIsPrintingOrPP && aDisplayErrorDialog) {
4285 DisplayLoadError(NS_ERROR_DOCUMENT_IS_PRINTMODE, nullptr, nullptr, nullptr);
4288 return mIsPrintingOrPP;
4291 bool
4292 nsDocShell::IsNavigationAllowed(bool aDisplayPrintErrorDialog,
4293 bool aCheckIfUnloadFired)
4295 bool isAllowed = !IsPrintingOrPP(aDisplayPrintErrorDialog) &&
4296 (!aCheckIfUnloadFired || !mFiredUnloadEvent);
4297 if (!isAllowed) {
4298 return false;
4300 if (!mContentViewer) {
4301 return true;
4303 bool firingBeforeUnload;
4304 mContentViewer->GetBeforeUnloadFiring(&firingBeforeUnload);
4305 return !firingBeforeUnload;
4308 //*****************************************************************************
4309 // nsDocShell::nsIWebNavigation
4310 //*****************************************************************************
4312 NS_IMETHODIMP
4313 nsDocShell::GetCanGoBack(bool * aCanGoBack)
4315 if (!IsNavigationAllowed(false)) {
4316 *aCanGoBack = false;
4317 return NS_OK; // JS may not handle returning of an error code
4319 nsresult rv;
4320 nsCOMPtr<nsISHistory> rootSH;
4321 rv = GetRootSessionHistory(getter_AddRefs(rootSH));
4322 nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
4323 NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
4324 rv = webnav->GetCanGoBack(aCanGoBack);
4325 return rv;
4329 NS_IMETHODIMP
4330 nsDocShell::GetCanGoForward(bool * aCanGoForward)
4332 if (!IsNavigationAllowed(false)) {
4333 *aCanGoForward = false;
4334 return NS_OK; // JS may not handle returning of an error code
4336 nsresult rv;
4337 nsCOMPtr<nsISHistory> rootSH;
4338 rv = GetRootSessionHistory(getter_AddRefs(rootSH));
4339 nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
4340 NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
4341 rv = webnav->GetCanGoForward(aCanGoForward);
4342 return rv;
4346 NS_IMETHODIMP
4347 nsDocShell::GoBack()
4349 if (!IsNavigationAllowed()) {
4350 return NS_OK; // JS may not handle returning of an error code
4352 nsresult rv;
4353 nsCOMPtr<nsISHistory> rootSH;
4354 rv = GetRootSessionHistory(getter_AddRefs(rootSH));
4355 nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
4356 NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
4357 rv = webnav->GoBack();
4358 return rv;
4362 NS_IMETHODIMP
4363 nsDocShell::GoForward()
4365 if (!IsNavigationAllowed()) {
4366 return NS_OK; // JS may not handle returning of an error code
4368 nsresult rv;
4369 nsCOMPtr<nsISHistory> rootSH;
4370 rv = GetRootSessionHistory(getter_AddRefs(rootSH));
4371 nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
4372 NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
4373 rv = webnav->GoForward();
4374 return rv;
4378 NS_IMETHODIMP nsDocShell::GotoIndex(int32_t aIndex)
4380 if (!IsNavigationAllowed()) {
4381 return NS_OK; // JS may not handle returning of an error code
4383 nsresult rv;
4384 nsCOMPtr<nsISHistory> rootSH;
4385 rv = GetRootSessionHistory(getter_AddRefs(rootSH));
4386 nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
4387 NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
4388 rv = webnav->GotoIndex(aIndex);
4389 return rv;
4393 NS_IMETHODIMP
4394 nsDocShell::LoadURI(const char16_t * aURI,
4395 uint32_t aLoadFlags,
4396 nsIURI * aReferringURI,
4397 nsIInputStream * aPostStream,
4398 nsIInputStream * aHeaderStream)
4400 return LoadURIWithBase(aURI, aLoadFlags, aReferringURI, aPostStream,
4401 aHeaderStream, nullptr);
4404 NS_IMETHODIMP
4405 nsDocShell::LoadURIWithBase(const char16_t * aURI,
4406 uint32_t aLoadFlags,
4407 nsIURI * aReferringURI,
4408 nsIInputStream * aPostStream,
4409 nsIInputStream * aHeaderStream,
4410 nsIURI * aBaseURI)
4412 NS_ASSERTION((aLoadFlags & 0xf) == 0, "Unexpected flags");
4414 if (!IsNavigationAllowed()) {
4415 return NS_OK; // JS may not handle returning of an error code
4417 nsCOMPtr<nsIURI> uri;
4418 nsCOMPtr<nsIInputStream> postStream(aPostStream);
4419 nsresult rv = NS_OK;
4421 // Create a URI from our string; if that succeeds, we want to
4422 // change aLoadFlags to not include the ALLOW_THIRD_PARTY_FIXUP
4423 // flag.
4425 NS_ConvertUTF16toUTF8 uriString(aURI);
4426 // Cleanup the empty spaces that might be on each end.
4427 uriString.Trim(" ");
4428 // Eliminate embedded newlines, which single-line text fields now allow:
4429 uriString.StripChars("\r\n");
4430 NS_ENSURE_TRUE(!uriString.IsEmpty(), NS_ERROR_FAILURE);
4432 rv = NS_NewURI(getter_AddRefs(uri), uriString);
4433 if (uri) {
4434 aLoadFlags &= ~LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
4437 if (sURIFixup) {
4438 // Call the fixup object. This will clobber the rv from NS_NewURI
4439 // above, but that's fine with us. Note that we need to do this even
4440 // if NS_NewURI returned a URI, because fixup handles nested URIs, etc
4441 // (things like view-source:mozilla.org for example).
4442 uint32_t fixupFlags = 0;
4443 if (aLoadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) {
4444 fixupFlags |= nsIURIFixup::FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP;
4446 if (aLoadFlags & LOAD_FLAGS_FIXUP_SCHEME_TYPOS) {
4447 fixupFlags |= nsIURIFixup::FIXUP_FLAG_FIX_SCHEME_TYPOS;
4449 nsCOMPtr<nsIInputStream> fixupStream;
4450 nsCOMPtr<nsIURIFixupInfo> fixupInfo;
4451 rv = sURIFixup->GetFixupURIInfo(uriString, fixupFlags,
4452 getter_AddRefs(fixupStream),
4453 getter_AddRefs(fixupInfo));
4455 if (NS_SUCCEEDED(rv)) {
4456 fixupInfo->GetPreferredURI(getter_AddRefs(uri));
4457 fixupInfo->SetConsumer(GetAsSupports(this));
4460 if (fixupStream) {
4461 // CreateFixupURI only returns a post data stream if it succeeded
4462 // and changed the URI, in which case we should override the
4463 // passed-in post data.
4464 postStream = fixupStream;
4467 if (aLoadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) {
4468 nsCOMPtr<nsIObserverService> serv = services::GetObserverService();
4469 if (serv) {
4470 serv->NotifyObservers(fixupInfo, "keyword-uri-fixup", aURI);
4474 // else no fixup service so just use the URI we created and see
4475 // what happens
4477 if (NS_ERROR_MALFORMED_URI == rv) {
4478 DisplayLoadError(rv, uri, aURI, nullptr);
4481 if (NS_FAILED(rv) || !uri)
4482 return NS_ERROR_FAILURE;
4484 PopupControlState popupState;
4485 if (aLoadFlags & LOAD_FLAGS_ALLOW_POPUPS) {
4486 popupState = openAllowed;
4487 aLoadFlags &= ~LOAD_FLAGS_ALLOW_POPUPS;
4488 } else {
4489 popupState = openOverridden;
4491 nsAutoPopupStatePusher statePusher(popupState);
4493 // Don't pass certain flags that aren't needed and end up confusing
4494 // ConvertLoadTypeToDocShellLoadInfo. We do need to ensure that they are
4495 // passed to LoadURI though, since it uses them.
4496 uint32_t extraFlags = (aLoadFlags & EXTRA_LOAD_FLAGS);
4497 aLoadFlags &= ~EXTRA_LOAD_FLAGS;
4499 nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
4500 rv = CreateLoadInfo(getter_AddRefs(loadInfo));
4501 if (NS_FAILED(rv)) return rv;
4504 * If the user "Disables Protection on This Page", we have to make sure to
4505 * remember the users decision when opening links in child tabs [Bug 906190]
4507 uint32_t loadType;
4508 if (aLoadFlags & LOAD_FLAGS_ALLOW_MIXED_CONTENT) {
4509 loadType = MAKE_LOAD_TYPE(LOAD_NORMAL_ALLOW_MIXED_CONTENT, aLoadFlags);
4510 } else {
4511 loadType = MAKE_LOAD_TYPE(LOAD_NORMAL, aLoadFlags);
4514 loadInfo->SetLoadType(ConvertLoadTypeToDocShellLoadInfo(loadType));
4515 loadInfo->SetPostDataStream(postStream);
4516 loadInfo->SetReferrer(aReferringURI);
4517 loadInfo->SetHeadersStream(aHeaderStream);
4518 loadInfo->SetBaseURI(aBaseURI);
4520 rv = LoadURI(uri, loadInfo, extraFlags, true);
4522 // Save URI string in case it's needed later when
4523 // sending to search engine service in EndPageLoad()
4524 mOriginalUriString = uriString;
4526 return rv;
4529 NS_IMETHODIMP
4530 nsDocShell::DisplayLoadError(nsresult aError, nsIURI *aURI,
4531 const char16_t *aURL,
4532 nsIChannel* aFailedChannel)
4534 // Get prompt and string bundle servcies
4535 nsCOMPtr<nsIPrompt> prompter;
4536 nsCOMPtr<nsIStringBundle> stringBundle;
4537 GetPromptAndStringBundle(getter_AddRefs(prompter),
4538 getter_AddRefs(stringBundle));
4540 NS_ENSURE_TRUE(stringBundle, NS_ERROR_FAILURE);
4541 NS_ENSURE_TRUE(prompter, NS_ERROR_FAILURE);
4543 nsAutoString error;
4544 const uint32_t kMaxFormatStrArgs = 3;
4545 nsAutoString formatStrs[kMaxFormatStrArgs];
4546 uint32_t formatStrCount = 0;
4547 bool addHostPort = false;
4548 nsresult rv = NS_OK;
4549 nsAutoString messageStr;
4550 nsAutoCString cssClass;
4551 nsAutoCString errorPage;
4553 errorPage.AssignLiteral("neterror");
4555 // Turn the error code into a human readable error message.
4556 if (NS_ERROR_UNKNOWN_PROTOCOL == aError) {
4557 NS_ENSURE_ARG_POINTER(aURI);
4559 // Extract the schemes into a comma delimited list.
4560 nsAutoCString scheme;
4561 aURI->GetScheme(scheme);
4562 CopyASCIItoUTF16(scheme, formatStrs[0]);
4563 nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(aURI);
4564 while (nestedURI) {
4565 nsCOMPtr<nsIURI> tempURI;
4566 nsresult rv2;
4567 rv2 = nestedURI->GetInnerURI(getter_AddRefs(tempURI));
4568 if (NS_SUCCEEDED(rv2) && tempURI) {
4569 tempURI->GetScheme(scheme);
4570 formatStrs[0].AppendLiteral(", ");
4571 AppendASCIItoUTF16(scheme, formatStrs[0]);
4573 nestedURI = do_QueryInterface(tempURI);
4575 formatStrCount = 1;
4576 error.AssignLiteral("unknownProtocolFound");
4578 else if (NS_ERROR_FILE_NOT_FOUND == aError) {
4579 NS_ENSURE_ARG_POINTER(aURI);
4580 error.AssignLiteral("fileNotFound");
4582 else if (NS_ERROR_UNKNOWN_HOST == aError) {
4583 NS_ENSURE_ARG_POINTER(aURI);
4584 // Get the host
4585 nsAutoCString host;
4586 nsCOMPtr<nsIURI> innermostURI = NS_GetInnermostURI(aURI);
4587 innermostURI->GetHost(host);
4588 CopyUTF8toUTF16(host, formatStrs[0]);
4589 formatStrCount = 1;
4590 error.AssignLiteral("dnsNotFound");
4592 else if(NS_ERROR_CONNECTION_REFUSED == aError) {
4593 NS_ENSURE_ARG_POINTER(aURI);
4594 addHostPort = true;
4595 error.AssignLiteral("connectionFailure");
4597 else if(NS_ERROR_NET_INTERRUPT == aError) {
4598 NS_ENSURE_ARG_POINTER(aURI);
4599 addHostPort = true;
4600 error.AssignLiteral("netInterrupt");
4602 else if (NS_ERROR_NET_TIMEOUT == aError) {
4603 NS_ENSURE_ARG_POINTER(aURI);
4604 // Get the host
4605 nsAutoCString host;
4606 aURI->GetHost(host);
4607 CopyUTF8toUTF16(host, formatStrs[0]);
4608 formatStrCount = 1;
4609 error.AssignLiteral("netTimeout");
4611 else if (NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION == aError) {
4612 // CSP error
4613 cssClass.AssignLiteral("neterror");
4614 error.AssignLiteral("cspFrameAncestorBlocked");
4616 else if (NS_ERROR_GET_MODULE(aError) == NS_ERROR_MODULE_SECURITY) {
4617 nsCOMPtr<nsINSSErrorsService> nsserr =
4618 do_GetService(NS_NSS_ERRORS_SERVICE_CONTRACTID);
4620 uint32_t errorClass;
4621 if (!nsserr ||
4622 NS_FAILED(nsserr->GetErrorClass(aError, &errorClass))) {
4623 errorClass = nsINSSErrorsService::ERROR_CLASS_SSL_PROTOCOL;
4626 nsCOMPtr<nsISupports> securityInfo;
4627 nsCOMPtr<nsITransportSecurityInfo> tsi;
4628 if (aFailedChannel)
4629 aFailedChannel->GetSecurityInfo(getter_AddRefs(securityInfo));
4630 tsi = do_QueryInterface(securityInfo);
4631 if (tsi) {
4632 // Usually we should have aFailedChannel and get a detailed message
4633 tsi->GetErrorMessage(getter_Copies(messageStr));
4635 else {
4636 // No channel, let's obtain the generic error message
4637 if (nsserr) {
4638 nsserr->GetErrorMessage(aError, messageStr);
4641 if (!messageStr.IsEmpty()) {
4642 if (errorClass == nsINSSErrorsService::ERROR_CLASS_BAD_CERT) {
4643 error.AssignLiteral("nssBadCert");
4645 // if this is a Strict-Transport-Security host and the cert
4646 // is bad, don't allow overrides (STS Spec section 7.3).
4647 uint32_t type = nsISiteSecurityService::HEADER_HSTS;
4648 uint32_t flags = mInPrivateBrowsing
4649 ? nsISocketProvider::NO_PERMANENT_STORAGE
4650 : 0;
4651 bool isStsHost = false;
4652 if (XRE_GetProcessType() == GeckoProcessType_Default) {
4653 nsCOMPtr<nsISiteSecurityService> sss =
4654 do_GetService(NS_SSSERVICE_CONTRACTID, &rv);
4655 NS_ENSURE_SUCCESS(rv, rv);
4656 rv = sss->IsSecureURI(type, aURI, flags, &isStsHost);
4657 NS_ENSURE_SUCCESS(rv, rv);
4658 } else {
4659 mozilla::dom::ContentChild* cc =
4660 mozilla::dom::ContentChild::GetSingleton();
4661 mozilla::ipc::URIParams uri;
4662 SerializeURI(aURI, uri);
4663 cc->SendIsSecureURI(type, uri, flags, &isStsHost);
4666 if (Preferences::GetBool(
4667 "browser.xul.error_pages.expert_bad_cert", false)) {
4668 cssClass.AssignLiteral("expertBadCert");
4671 // HSTS takes precedence over the expert bad cert pref. We
4672 // never want to show the "Add Exception" button for HSTS sites.
4673 uint32_t bucketId;
4674 if (isStsHost) {
4675 cssClass.AssignLiteral("badStsCert");
4676 //measuring STS separately allows us to measure click through
4677 //rates easily
4678 bucketId = nsISecurityUITelemetry::WARNING_BAD_CERT_TOP_STS;
4679 } else {
4680 bucketId = nsISecurityUITelemetry::WARNING_BAD_CERT_TOP;
4683 // See if an alternate cert error page is registered
4684 nsAdoptingCString alternateErrorPage =
4685 Preferences::GetCString(
4686 "security.alternate_certificate_error_page");
4687 if (alternateErrorPage)
4688 errorPage.Assign(alternateErrorPage);
4690 if (!IsFrame() && errorPage.EqualsIgnoreCase("certerror"))
4691 mozilla::Telemetry::Accumulate(mozilla::Telemetry::SECURITY_UI, bucketId);
4693 } else {
4694 error.AssignLiteral("nssFailure2");
4697 } else if (NS_ERROR_PHISHING_URI == aError || NS_ERROR_MALWARE_URI == aError) {
4698 nsAutoCString host;
4699 aURI->GetHost(host);
4700 CopyUTF8toUTF16(host, formatStrs[0]);
4701 formatStrCount = 1;
4703 // Malware and phishing detectors may want to use an alternate error
4704 // page, but if the pref's not set, we'll fall back on the standard page
4705 nsAdoptingCString alternateErrorPage =
4706 Preferences::GetCString("urlclassifier.alternate_error_page");
4707 if (alternateErrorPage)
4708 errorPage.Assign(alternateErrorPage);
4710 uint32_t bucketId;
4711 if (NS_ERROR_PHISHING_URI == aError) {
4712 error.AssignLiteral("phishingBlocked");
4713 bucketId = IsFrame() ? nsISecurityUITelemetry::WARNING_PHISHING_PAGE_FRAME :
4714 nsISecurityUITelemetry::WARNING_PHISHING_PAGE_TOP ;
4715 } else {
4716 error.AssignLiteral("malwareBlocked");
4717 bucketId = IsFrame() ? nsISecurityUITelemetry::WARNING_MALWARE_PAGE_FRAME :
4718 nsISecurityUITelemetry::WARNING_MALWARE_PAGE_TOP ;
4721 if (errorPage.EqualsIgnoreCase("blocked"))
4722 mozilla::Telemetry::Accumulate(mozilla::Telemetry::SECURITY_UI,
4723 bucketId);
4725 cssClass.AssignLiteral("blacklist");
4726 } else if (NS_ERROR_CONTENT_CRASHED == aError) {
4727 errorPage.AssignLiteral("tabcrashed");
4728 error.AssignLiteral("tabcrashed");
4730 nsCOMPtr<EventTarget> handler = mChromeEventHandler;
4731 if (handler) {
4732 nsCOMPtr<Element> element = do_QueryInterface(handler);
4733 element->GetAttribute(NS_LITERAL_STRING("crashedPageTitle"), messageStr);
4736 // DisplayLoadError requires a non-empty messageStr to proceed and call LoadErrorPage.
4737 // If the page doesn't have a title, we will use a blank space which will be trimmed
4738 // and thus treated as empty by the front-end.
4739 if (messageStr.IsEmpty()) {
4740 messageStr.AssignLiteral(MOZ_UTF16(" "));
4743 else {
4744 // Errors requiring simple formatting
4745 switch (aError) {
4746 case NS_ERROR_MALFORMED_URI:
4747 // URI is malformed
4748 error.AssignLiteral("malformedURI");
4749 break;
4750 case NS_ERROR_REDIRECT_LOOP:
4751 // Doc failed to load because the server generated too many redirects
4752 error.AssignLiteral("redirectLoop");
4753 break;
4754 case NS_ERROR_UNKNOWN_SOCKET_TYPE:
4755 // Doc failed to load because PSM is not installed
4756 error.AssignLiteral("unknownSocketType");
4757 break;
4758 case NS_ERROR_NET_RESET:
4759 // Doc failed to load because the server kept reseting the connection
4760 // before we could read any data from it
4761 error.AssignLiteral("netReset");
4762 break;
4763 case NS_ERROR_DOCUMENT_NOT_CACHED:
4764 // Doc failed to load because the cache does not contain a copy of
4765 // the document.
4766 error.AssignLiteral("notCached");
4767 break;
4768 case NS_ERROR_OFFLINE:
4769 // Doc failed to load because we are offline.
4770 error.AssignLiteral("netOffline");
4771 break;
4772 case NS_ERROR_DOCUMENT_IS_PRINTMODE:
4773 // Doc navigation attempted while Printing or Print Preview
4774 error.AssignLiteral("isprinting");
4775 break;
4776 case NS_ERROR_PORT_ACCESS_NOT_ALLOWED:
4777 // Port blocked for security reasons
4778 addHostPort = true;
4779 error.AssignLiteral("deniedPortAccess");
4780 break;
4781 case NS_ERROR_UNKNOWN_PROXY_HOST:
4782 // Proxy hostname could not be resolved.
4783 error.AssignLiteral("proxyResolveFailure");
4784 break;
4785 case NS_ERROR_PROXY_CONNECTION_REFUSED:
4786 // Proxy connection was refused.
4787 error.AssignLiteral("proxyConnectFailure");
4788 break;
4789 case NS_ERROR_INVALID_CONTENT_ENCODING:
4790 // Bad Content Encoding.
4791 error.AssignLiteral("contentEncodingError");
4792 break;
4793 case NS_ERROR_REMOTE_XUL:
4795 error.AssignLiteral("remoteXUL");
4796 break;
4798 case NS_ERROR_UNSAFE_CONTENT_TYPE:
4799 // Channel refused to load from an unrecognized content type.
4800 error.AssignLiteral("unsafeContentType");
4801 break;
4802 case NS_ERROR_CORRUPTED_CONTENT:
4803 // Broken Content Detected. e.g. Content-MD5 check failure.
4804 error.AssignLiteral("corruptedContentError");
4805 break;
4806 default:
4807 break;
4811 // Test if the error should be displayed
4812 if (error.IsEmpty()) {
4813 return NS_OK;
4816 // Test if the error needs to be formatted
4817 if (!messageStr.IsEmpty()) {
4818 // already obtained message
4820 else {
4821 if (addHostPort) {
4822 // Build up the host:port string.
4823 nsAutoCString hostport;
4824 if (aURI) {
4825 aURI->GetHostPort(hostport);
4826 } else {
4827 hostport.Assign('?');
4829 CopyUTF8toUTF16(hostport, formatStrs[formatStrCount++]);
4832 nsAutoCString spec;
4833 rv = NS_ERROR_NOT_AVAILABLE;
4834 if (aURI) {
4835 // displaying "file://" is aesthetically unpleasing and could even be
4836 // confusing to the user
4837 bool isFileURI = false;
4838 rv = aURI->SchemeIs("file", &isFileURI);
4839 if (NS_SUCCEEDED(rv) && isFileURI)
4840 aURI->GetPath(spec);
4841 else
4842 aURI->GetSpec(spec);
4844 nsAutoCString charset;
4845 // unescape and convert from origin charset
4846 aURI->GetOriginCharset(charset);
4847 nsCOMPtr<nsITextToSubURI> textToSubURI(
4848 do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv));
4849 if (NS_SUCCEEDED(rv)) {
4850 rv = textToSubURI->UnEscapeURIForUI(charset, spec, formatStrs[formatStrCount]);
4852 } else {
4853 spec.Assign('?');
4855 if (NS_FAILED(rv))
4856 CopyUTF8toUTF16(spec, formatStrs[formatStrCount]);
4857 rv = NS_OK;
4858 ++formatStrCount;
4860 const char16_t *strs[kMaxFormatStrArgs];
4861 for (uint32_t i = 0; i < formatStrCount; i++) {
4862 strs[i] = formatStrs[i].get();
4864 nsXPIDLString str;
4865 rv = stringBundle->FormatStringFromName(
4866 error.get(),
4867 strs, formatStrCount, getter_Copies(str));
4868 NS_ENSURE_SUCCESS(rv, rv);
4869 messageStr.Assign(str.get());
4872 // Display the error as a page or an alert prompt
4873 NS_ENSURE_FALSE(messageStr.IsEmpty(), NS_ERROR_FAILURE);
4875 if (UseErrorPages()) {
4876 // Display an error page
4877 LoadErrorPage(aURI, aURL, errorPage.get(), error.get(),
4878 messageStr.get(), cssClass.get(), aFailedChannel);
4880 else
4882 // The prompter reqires that our private window has a document (or it
4883 // asserts). Satisfy that assertion now since GetDoc will force
4884 // creation of one if it hasn't already been created.
4885 if (mScriptGlobal) {
4886 unused << mScriptGlobal->GetDoc();
4889 // Display a message box
4890 prompter->Alert(nullptr, messageStr.get());
4893 return NS_OK;
4897 NS_IMETHODIMP
4898 nsDocShell::LoadErrorPage(nsIURI *aURI, const char16_t *aURL,
4899 const char *aErrorPage,
4900 const char16_t *aErrorType,
4901 const char16_t *aDescription,
4902 const char *aCSSClass,
4903 nsIChannel* aFailedChannel)
4905 #if defined(PR_LOGGING) && defined(DEBUG)
4906 if (PR_LOG_TEST(gDocShellLog, PR_LOG_DEBUG)) {
4907 nsAutoCString spec;
4908 aURI->GetSpec(spec);
4910 nsAutoCString chanName;
4911 if (aFailedChannel)
4912 aFailedChannel->GetName(chanName);
4913 else
4914 chanName.AssignLiteral("<no channel>");
4916 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
4917 ("nsDocShell[%p]::LoadErrorPage(\"%s\", \"%s\", {...}, [%s])\n", this,
4918 spec.get(), NS_ConvertUTF16toUTF8(aURL).get(), chanName.get()));
4920 #endif
4921 mFailedChannel = aFailedChannel;
4922 mFailedURI = aURI;
4923 mFailedLoadType = mLoadType;
4925 if (mLSHE) {
4926 // Abandon mLSHE's BFCache entry and create a new one. This way, if
4927 // we go back or forward to another SHEntry with the same doc
4928 // identifier, the error page won't persist.
4929 mLSHE->AbandonBFCacheEntry();
4932 nsAutoCString url;
4933 nsAutoCString charset;
4934 if (aURI)
4936 nsresult rv = aURI->GetSpec(url);
4937 NS_ENSURE_SUCCESS(rv, rv);
4938 rv = aURI->GetOriginCharset(charset);
4939 NS_ENSURE_SUCCESS(rv, rv);
4941 else if (aURL)
4943 CopyUTF16toUTF8(aURL, url);
4945 else
4947 return NS_ERROR_INVALID_POINTER;
4950 // Create a URL to pass all the error information through to the page.
4952 #undef SAFE_ESCAPE
4953 #define SAFE_ESCAPE(cstring, escArg1, escArg2) \
4955 char* s = nsEscape(escArg1, escArg2); \
4956 if (!s) \
4957 return NS_ERROR_OUT_OF_MEMORY; \
4958 cstring.Adopt(s); \
4960 nsCString escapedUrl, escapedCharset, escapedError, escapedDescription,
4961 escapedCSSClass;
4962 SAFE_ESCAPE(escapedUrl, url.get(), url_Path);
4963 SAFE_ESCAPE(escapedCharset, charset.get(), url_Path);
4964 SAFE_ESCAPE(escapedError,
4965 NS_ConvertUTF16toUTF8(aErrorType).get(), url_Path);
4966 SAFE_ESCAPE(escapedDescription,
4967 NS_ConvertUTF16toUTF8(aDescription).get(), url_Path);
4968 if (aCSSClass) {
4969 SAFE_ESCAPE(escapedCSSClass, aCSSClass, url_Path);
4971 nsCString errorPageUrl("about:");
4972 errorPageUrl.AppendASCII(aErrorPage);
4973 errorPageUrl.AppendLiteral("?e=");
4975 errorPageUrl.AppendASCII(escapedError.get());
4976 errorPageUrl.AppendLiteral("&u=");
4977 errorPageUrl.AppendASCII(escapedUrl.get());
4978 if (!escapedCSSClass.IsEmpty()) {
4979 errorPageUrl.AppendLiteral("&s=");
4980 errorPageUrl.AppendASCII(escapedCSSClass.get());
4982 errorPageUrl.AppendLiteral("&c=");
4983 errorPageUrl.AppendASCII(escapedCharset.get());
4985 nsAutoCString frameType(FrameTypeToString(mFrameType));
4986 errorPageUrl.AppendLiteral("&f=");
4987 errorPageUrl.AppendASCII(frameType.get());
4989 // Append the manifest URL if the error comes from an app.
4990 nsString manifestURL;
4991 nsresult rv = GetAppManifestURL(manifestURL);
4992 if (manifestURL.Length() > 0) {
4993 nsCString manifestParam;
4994 SAFE_ESCAPE(manifestParam,
4995 NS_ConvertUTF16toUTF8(manifestURL).get(),
4996 url_Path);
4997 errorPageUrl.AppendLiteral("&m=");
4998 errorPageUrl.AppendASCII(manifestParam.get());
5001 // In some cases we need to hardcode new strings in the en-US case.
5002 // Pass the locale because about:neterror doesn't know it:
5003 nsCOMPtr<nsIXULChromeRegistry> xulreg =
5004 mozilla::services::GetXULChromeRegistryService();
5005 if (xulreg) {
5006 nsAutoCString locale;
5007 xulreg->GetSelectedLocale(NS_LITERAL_CSTRING("global"), locale);
5008 errorPageUrl.AppendLiteral("&l=");
5009 errorPageUrl.AppendASCII(locale.get());
5012 // netError.xhtml's getDescription only handles the "d" parameter at the
5013 // end of the URL, so append it last.
5014 errorPageUrl.AppendLiteral("&d=");
5015 errorPageUrl.AppendASCII(escapedDescription.get());
5017 nsCOMPtr<nsIURI> errorPageURI;
5018 rv = NS_NewURI(getter_AddRefs(errorPageURI), errorPageUrl);
5019 NS_ENSURE_SUCCESS(rv, rv);
5021 return InternalLoad(errorPageURI, nullptr, nullptr,
5022 INTERNAL_LOAD_FLAGS_INHERIT_OWNER, nullptr, nullptr,
5023 NullString(), nullptr, nullptr, LOAD_ERROR_PAGE,
5024 nullptr, true, NullString(), this, nullptr, nullptr,
5025 nullptr);
5029 NS_IMETHODIMP
5030 nsDocShell::Reload(uint32_t aReloadFlags)
5032 if (!IsNavigationAllowed()) {
5033 return NS_OK; // JS may not handle returning of an error code
5035 nsresult rv;
5036 NS_ASSERTION(((aReloadFlags & 0xf) == 0),
5037 "Reload command not updated to use load flags!");
5038 NS_ASSERTION((aReloadFlags & EXTRA_LOAD_FLAGS) == 0,
5039 "Don't pass these flags to Reload");
5041 uint32_t loadType = MAKE_LOAD_TYPE(LOAD_RELOAD_NORMAL, aReloadFlags);
5042 NS_ENSURE_TRUE(IsValidLoadType(loadType), NS_ERROR_INVALID_ARG);
5044 // Send notifications to the HistoryListener if any, about the impending reload
5045 nsCOMPtr<nsISHistory> rootSH;
5046 rv = GetRootSessionHistory(getter_AddRefs(rootSH));
5047 nsCOMPtr<nsISHistoryInternal> shistInt(do_QueryInterface(rootSH));
5048 bool canReload = true;
5049 if (rootSH) {
5050 shistInt->NotifyOnHistoryReload(mCurrentURI, aReloadFlags, &canReload);
5053 if (!canReload)
5054 return NS_OK;
5056 /* If you change this part of code, make sure bug 45297 does not re-occur */
5057 if (mOSHE) {
5058 rv = LoadHistoryEntry(mOSHE, loadType);
5060 else if (mLSHE) { // In case a reload happened before the current load is done
5061 rv = LoadHistoryEntry(mLSHE, loadType);
5063 else {
5064 nsCOMPtr<nsIDocument> doc(GetDocument());
5066 // Do not inherit owner from document
5067 uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
5068 nsAutoString srcdoc;
5069 nsIPrincipal* principal = nullptr;
5070 nsAutoString contentTypeHint;
5071 nsCOMPtr<nsIURI> baseURI;
5072 if (doc) {
5073 principal = doc->NodePrincipal();
5074 doc->GetContentType(contentTypeHint);
5076 if (doc->IsSrcdocDocument()) {
5077 doc->GetSrcdocData(srcdoc);
5078 flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC;
5079 baseURI = doc->GetBaseURI();
5082 rv = InternalLoad(mCurrentURI,
5083 mReferrerURI,
5084 principal,
5085 flags,
5086 nullptr, // No window target
5087 NS_LossyConvertUTF16toASCII(contentTypeHint).get(),
5088 NullString(), // No forced download
5089 nullptr, // No post data
5090 nullptr, // No headers data
5091 loadType, // Load type
5092 nullptr, // No SHEntry
5093 true,
5094 srcdoc, // srcdoc argument for iframe
5095 this, // For reloads we are the source
5096 baseURI,
5097 nullptr, // No nsIDocShell
5098 nullptr); // No nsIRequest
5101 return rv;
5104 NS_IMETHODIMP
5105 nsDocShell::Stop(uint32_t aStopFlags)
5107 // Revoke any pending event related to content viewer restoration
5108 mRestorePresentationEvent.Revoke();
5110 if (mLoadType == LOAD_ERROR_PAGE) {
5111 if (mLSHE) {
5112 // Since error page loads never unset mLSHE, do so now
5113 SetHistoryEntry(&mOSHE, mLSHE);
5114 SetHistoryEntry(&mLSHE, nullptr);
5117 mFailedChannel = nullptr;
5118 mFailedURI = nullptr;
5121 if (nsIWebNavigation::STOP_CONTENT & aStopFlags) {
5122 // Stop the document loading
5123 if (mContentViewer) {
5124 nsCOMPtr<nsIContentViewer> cv = mContentViewer;
5125 cv->Stop();
5129 if (nsIWebNavigation::STOP_NETWORK & aStopFlags) {
5130 // Suspend any timers that were set for this loader. We'll clear
5131 // them out for good in CreateContentViewer.
5132 if (mRefreshURIList) {
5133 SuspendRefreshURIs();
5134 mSavedRefreshURIList.swap(mRefreshURIList);
5135 mRefreshURIList = nullptr;
5138 // XXXbz We could also pass |this| to nsIURILoader::Stop. That will
5139 // just call Stop() on us as an nsIDocumentLoader... We need fewer
5140 // redundant apis!
5141 Stop();
5144 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
5145 while (iter.HasMore()) {
5146 nsCOMPtr<nsIWebNavigation> shellAsNav(do_QueryObject(iter.GetNext()));
5147 if (shellAsNav)
5148 shellAsNav->Stop(aStopFlags);
5151 return NS_OK;
5154 NS_IMETHODIMP
5155 nsDocShell::GetDocument(nsIDOMDocument ** aDocument)
5157 NS_ENSURE_ARG_POINTER(aDocument);
5158 NS_ENSURE_SUCCESS(EnsureContentViewer(), NS_ERROR_FAILURE);
5160 return mContentViewer->GetDOMDocument(aDocument);
5163 NS_IMETHODIMP
5164 nsDocShell::GetCurrentURI(nsIURI ** aURI)
5166 NS_ENSURE_ARG_POINTER(aURI);
5168 if (mCurrentURI) {
5169 return NS_EnsureSafeToReturn(mCurrentURI, aURI);
5172 *aURI = nullptr;
5173 return NS_OK;
5176 NS_IMETHODIMP
5177 nsDocShell::GetReferringURI(nsIURI ** aURI)
5179 NS_ENSURE_ARG_POINTER(aURI);
5181 *aURI = mReferrerURI;
5182 NS_IF_ADDREF(*aURI);
5184 return NS_OK;
5187 NS_IMETHODIMP
5188 nsDocShell::SetSessionHistory(nsISHistory * aSessionHistory)
5191 NS_ENSURE_TRUE(aSessionHistory, NS_ERROR_FAILURE);
5192 // make sure that we are the root docshell and
5193 // set a handle to root docshell in SH.
5195 nsCOMPtr<nsIDocShellTreeItem> root;
5196 /* Get the root docshell. If *this* is the root docshell
5197 * then save a handle to *this* in SH. SH needs it to do
5198 * traversions thro' its entries
5200 GetSameTypeRootTreeItem(getter_AddRefs(root));
5201 NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
5202 if (root.get() == static_cast<nsIDocShellTreeItem *>(this)) {
5203 mSessionHistory = aSessionHistory;
5204 nsCOMPtr<nsISHistoryInternal>
5205 shPrivate(do_QueryInterface(mSessionHistory));
5206 NS_ENSURE_TRUE(shPrivate, NS_ERROR_FAILURE);
5207 shPrivate->SetRootDocShell(this);
5208 return NS_OK;
5210 return NS_ERROR_FAILURE;
5215 NS_IMETHODIMP
5216 nsDocShell::GetSessionHistory(nsISHistory ** aSessionHistory)
5218 NS_ENSURE_ARG_POINTER(aSessionHistory);
5219 *aSessionHistory = mSessionHistory;
5220 NS_IF_ADDREF(*aSessionHistory);
5221 return NS_OK;
5224 //*****************************************************************************
5225 // nsDocShell::nsIWebPageDescriptor
5226 //*****************************************************************************
5227 NS_IMETHODIMP
5228 nsDocShell::LoadPage(nsISupports *aPageDescriptor, uint32_t aDisplayType)
5230 nsCOMPtr<nsISHEntry> shEntryIn(do_QueryInterface(aPageDescriptor));
5232 // Currently, the opaque 'page descriptor' is an nsISHEntry...
5233 if (!shEntryIn) {
5234 return NS_ERROR_INVALID_POINTER;
5237 // Now clone shEntryIn, since we might end up modifying it later on, and we
5238 // want a page descriptor to be reusable.
5239 nsCOMPtr<nsISHEntry> shEntry;
5240 nsresult rv = shEntryIn->Clone(getter_AddRefs(shEntry));
5241 NS_ENSURE_SUCCESS(rv, rv);
5243 // Give our cloned shEntry a new bfcache entry so this load is independent
5244 // of all other loads. (This is important, in particular, for bugs 582795
5245 // and 585298.)
5246 rv = shEntry->AbandonBFCacheEntry();
5247 NS_ENSURE_SUCCESS(rv, rv);
5250 // load the page as view-source
5252 if (nsIWebPageDescriptor::DISPLAY_AS_SOURCE == aDisplayType) {
5253 nsCOMPtr<nsIURI> oldUri, newUri;
5254 nsCString spec, newSpec;
5256 // Create a new view-source URI and replace the original.
5257 rv = shEntry->GetURI(getter_AddRefs(oldUri));
5258 if (NS_FAILED(rv))
5259 return rv;
5261 oldUri->GetSpec(spec);
5262 newSpec.AppendLiteral("view-source:");
5263 newSpec.Append(spec);
5265 rv = NS_NewURI(getter_AddRefs(newUri), newSpec);
5266 if (NS_FAILED(rv)) {
5267 return rv;
5269 shEntry->SetURI(newUri);
5272 rv = LoadHistoryEntry(shEntry, LOAD_HISTORY);
5273 return rv;
5276 NS_IMETHODIMP
5277 nsDocShell::GetCurrentDescriptor(nsISupports **aPageDescriptor)
5279 NS_PRECONDITION(aPageDescriptor, "Null out param?");
5281 *aPageDescriptor = nullptr;
5283 nsISHEntry* src = mOSHE ? mOSHE : mLSHE;
5284 if (src) {
5285 nsCOMPtr<nsISHEntry> dest;
5287 nsresult rv = src->Clone(getter_AddRefs(dest));
5288 if (NS_FAILED(rv)) {
5289 return rv;
5292 // null out inappropriate cloned attributes...
5293 dest->SetParent(nullptr);
5294 dest->SetIsSubFrame(false);
5296 return CallQueryInterface(dest, aPageDescriptor);
5299 return NS_ERROR_NOT_AVAILABLE;
5303 //*****************************************************************************
5304 // nsDocShell::nsIBaseWindow
5305 //*****************************************************************************
5307 NS_IMETHODIMP
5308 nsDocShell::InitWindow(nativeWindow parentNativeWindow,
5309 nsIWidget * parentWidget, int32_t x, int32_t y,
5310 int32_t cx, int32_t cy)
5312 SetParentWidget(parentWidget);
5313 SetPositionAndSize(x, y, cx, cy, false);
5315 return NS_OK;
5318 NS_IMETHODIMP
5319 nsDocShell::Create()
5321 if (mCreated) {
5322 // We've already been created
5323 return NS_OK;
5326 NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome,
5327 "Unexpected item type in docshell");
5329 NS_ENSURE_TRUE(Preferences::GetRootBranch(), NS_ERROR_FAILURE);
5330 mCreated = true;
5332 if (gValidateOrigin == 0xffffffff) {
5333 // Check pref to see if we should prevent frameset spoofing
5334 gValidateOrigin =
5335 Preferences::GetBool("browser.frame.validate_origin", true);
5338 // Should we use XUL error pages instead of alerts if possible?
5339 mUseErrorPages =
5340 Preferences::GetBool("browser.xul.error_pages.enabled", mUseErrorPages);
5342 if(!gAddedPreferencesVarCache) {
5343 Preferences::AddBoolVarCache(&sUseErrorPages,
5344 "browser.xul.error_pages.enabled",
5345 mUseErrorPages);
5346 gAddedPreferencesVarCache = true;
5349 mDeviceSizeIsPageSize =
5350 Preferences::GetBool("docshell.device_size_is_page_size",
5351 mDeviceSizeIsPageSize);
5353 nsCOMPtr<nsIObserverService> serv = services::GetObserverService();
5354 if (serv) {
5355 const char* msg = mItemType == typeContent ?
5356 NS_WEBNAVIGATION_CREATE : NS_CHROME_WEBNAVIGATION_CREATE;
5357 serv->NotifyObservers(GetAsSupports(this), msg, nullptr);
5360 return NS_OK;
5363 NS_IMETHODIMP
5364 nsDocShell::Destroy()
5366 NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome,
5367 "Unexpected item type in docshell");
5369 if (!mIsBeingDestroyed) {
5370 nsCOMPtr<nsIObserverService> serv = services::GetObserverService();
5371 if (serv) {
5372 const char* msg = mItemType == typeContent ?
5373 NS_WEBNAVIGATION_DESTROY : NS_CHROME_WEBNAVIGATION_DESTROY;
5374 serv->NotifyObservers(GetAsSupports(this), msg, nullptr);
5378 mIsBeingDestroyed = true;
5380 // Remove our pref observers
5381 if (mObserveErrorPages) {
5382 mObserveErrorPages = false;
5385 // Make sure to blow away our mLoadingURI just in case. No loads
5386 // from inside this pagehide.
5387 mLoadingURI = nullptr;
5389 // Fire unload event before we blow anything away.
5390 (void) FirePageHideNotification(true);
5392 // Clear pointers to any detached nsEditorData that's lying
5393 // around in shistory entries. Breaks cycle. See bug 430921.
5394 if (mOSHE)
5395 mOSHE->SetEditorData(nullptr);
5396 if (mLSHE)
5397 mLSHE->SetEditorData(nullptr);
5399 // Note: mContentListener can be null if Init() failed and we're being
5400 // called from the destructor.
5401 if (mContentListener) {
5402 mContentListener->DropDocShellreference();
5403 mContentListener->SetParentContentListener(nullptr);
5404 // Note that we do NOT set mContentListener to null here; that
5405 // way if someone tries to do a load in us after this point
5406 // the nsDSURIContentListener will block it. All of which
5407 // means that we should do this before calling Stop(), of
5408 // course.
5411 // Stop any URLs that are currently being loaded...
5412 Stop(nsIWebNavigation::STOP_ALL);
5414 mEditorData = nullptr;
5416 mTransferableHookData = nullptr;
5418 // Save the state of the current document, before destroying the window.
5419 // This is needed to capture the state of a frameset when the new document
5420 // causes the frameset to be destroyed...
5421 PersistLayoutHistoryState();
5423 // Remove this docshell from its parent's child list
5424 nsCOMPtr<nsIDocShellTreeItem> docShellParentAsItem =
5425 do_QueryInterface(GetAsSupports(mParent));
5426 if (docShellParentAsItem)
5427 docShellParentAsItem->RemoveChild(this);
5429 if (mContentViewer) {
5430 mContentViewer->Close(nullptr);
5431 mContentViewer->Destroy();
5432 mContentViewer = nullptr;
5435 nsDocLoader::Destroy();
5437 mParentWidget = nullptr;
5438 mCurrentURI = nullptr;
5440 if (mURLSearchParams) {
5441 mURLSearchParams->RemoveObservers();
5442 mURLSearchParams = nullptr;
5445 if (mScriptGlobal) {
5446 mScriptGlobal->DetachFromDocShell();
5447 mScriptGlobal = nullptr;
5450 if (mSessionHistory) {
5451 // We want to destroy these content viewers now rather than
5452 // letting their destruction wait for the session history
5453 // entries to get garbage collected. (Bug 488394)
5454 nsCOMPtr<nsISHistoryInternal> shPrivate =
5455 do_QueryInterface(mSessionHistory);
5456 if (shPrivate) {
5457 shPrivate->EvictAllContentViewers();
5459 mSessionHistory = nullptr;
5462 SetTreeOwner(nullptr);
5464 mOnePermittedSandboxedNavigator = nullptr;
5466 // required to break ref cycle
5467 mSecurityUI = nullptr;
5469 // Cancel any timers that were set for this docshell; this is needed
5470 // to break the cycle between us and the timers.
5471 CancelRefreshURITimers();
5473 if (mInPrivateBrowsing) {
5474 mInPrivateBrowsing = false;
5475 if (mAffectPrivateSessionLifetime) {
5476 DecreasePrivateDocShellCount();
5480 return NS_OK;
5483 NS_IMETHODIMP
5484 nsDocShell::GetUnscaledDevicePixelsPerCSSPixel(double *aScale)
5486 if (mParentWidget) {
5487 *aScale = mParentWidget->GetDefaultScale().scale;
5488 return NS_OK;
5491 nsCOMPtr<nsIBaseWindow> ownerWindow(do_QueryInterface(mTreeOwner));
5492 if (ownerWindow) {
5493 return ownerWindow->GetUnscaledDevicePixelsPerCSSPixel(aScale);
5496 *aScale = 1.0;
5497 return NS_OK;
5500 NS_IMETHODIMP
5501 nsDocShell::SetPosition(int32_t x, int32_t y)
5503 mBounds.x = x;
5504 mBounds.y = y;
5506 if (mContentViewer)
5507 NS_ENSURE_SUCCESS(mContentViewer->Move(x, y), NS_ERROR_FAILURE);
5509 return NS_OK;
5512 NS_IMETHODIMP
5513 nsDocShell::GetPosition(int32_t * aX, int32_t * aY)
5515 int32_t dummyHolder;
5516 return GetPositionAndSize(aX, aY, &dummyHolder, &dummyHolder);
5519 NS_IMETHODIMP
5520 nsDocShell::SetSize(int32_t aCX, int32_t aCY, bool aRepaint)
5522 int32_t x = 0, y = 0;
5523 GetPosition(&x, &y);
5524 return SetPositionAndSize(x, y, aCX, aCY, aRepaint);
5527 NS_IMETHODIMP
5528 nsDocShell::GetSize(int32_t * aCX, int32_t * aCY)
5530 int32_t dummyHolder;
5531 return GetPositionAndSize(&dummyHolder, &dummyHolder, aCX, aCY);
5534 NS_IMETHODIMP
5535 nsDocShell::SetPositionAndSize(int32_t x, int32_t y, int32_t cx,
5536 int32_t cy, bool fRepaint)
5538 mBounds.x = x;
5539 mBounds.y = y;
5540 mBounds.width = cx;
5541 mBounds.height = cy;
5543 // Hold strong ref, since SetBounds can make us null out mContentViewer
5544 nsCOMPtr<nsIContentViewer> viewer = mContentViewer;
5545 if (viewer) {
5546 //XXX Border figured in here or is that handled elsewhere?
5547 NS_ENSURE_SUCCESS(viewer->SetBounds(mBounds), NS_ERROR_FAILURE);
5550 return NS_OK;
5553 NS_IMETHODIMP
5554 nsDocShell::GetPositionAndSize(int32_t * x, int32_t * y, int32_t * cx,
5555 int32_t * cy)
5557 if (mParentWidget) {
5558 // ensure size is up-to-date if window has changed resolution
5559 nsIntRect r;
5560 mParentWidget->GetClientBounds(r);
5561 SetPositionAndSize(mBounds.x, mBounds.y, r.width, r.height, false);
5564 // We should really consider just getting this information from
5565 // our window instead of duplicating the storage and code...
5566 if (cx || cy) {
5567 // Caller wants to know our size; make sure to give them up to
5568 // date information.
5569 nsCOMPtr<nsIDocument> doc(do_GetInterface(GetAsSupports(mParent)));
5570 if (doc) {
5571 doc->FlushPendingNotifications(Flush_Layout);
5575 DoGetPositionAndSize(x, y, cx, cy);
5576 return NS_OK;
5579 void
5580 nsDocShell::DoGetPositionAndSize(int32_t * x, int32_t * y, int32_t * cx,
5581 int32_t * cy)
5583 if (x)
5584 *x = mBounds.x;
5585 if (y)
5586 *y = mBounds.y;
5587 if (cx)
5588 *cx = mBounds.width;
5589 if (cy)
5590 *cy = mBounds.height;
5593 NS_IMETHODIMP
5594 nsDocShell::Repaint(bool aForce)
5596 nsCOMPtr<nsIPresShell> presShell =GetPresShell();
5597 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
5599 nsViewManager* viewManager = presShell->GetViewManager();
5600 NS_ENSURE_TRUE(viewManager, NS_ERROR_FAILURE);
5602 viewManager->InvalidateAllViews();
5603 return NS_OK;
5606 NS_IMETHODIMP
5607 nsDocShell::GetParentWidget(nsIWidget ** parentWidget)
5609 NS_ENSURE_ARG_POINTER(parentWidget);
5611 *parentWidget = mParentWidget;
5612 NS_IF_ADDREF(*parentWidget);
5614 return NS_OK;
5617 NS_IMETHODIMP
5618 nsDocShell::SetParentWidget(nsIWidget * aParentWidget)
5620 mParentWidget = aParentWidget;
5622 return NS_OK;
5625 NS_IMETHODIMP
5626 nsDocShell::GetParentNativeWindow(nativeWindow * parentNativeWindow)
5628 NS_ENSURE_ARG_POINTER(parentNativeWindow);
5630 if (mParentWidget)
5631 *parentNativeWindow = mParentWidget->GetNativeData(NS_NATIVE_WIDGET);
5632 else
5633 *parentNativeWindow = nullptr;
5635 return NS_OK;
5638 NS_IMETHODIMP
5639 nsDocShell::SetParentNativeWindow(nativeWindow parentNativeWindow)
5641 return NS_ERROR_NOT_IMPLEMENTED;
5644 NS_IMETHODIMP
5645 nsDocShell::GetNativeHandle(nsAString& aNativeHandle)
5647 // the nativeHandle should be accessed from nsIXULWindow
5648 return NS_ERROR_NOT_IMPLEMENTED;
5651 NS_IMETHODIMP
5652 nsDocShell::GetVisibility(bool * aVisibility)
5654 NS_ENSURE_ARG_POINTER(aVisibility);
5656 *aVisibility = false;
5658 if (!mContentViewer)
5659 return NS_OK;
5661 nsCOMPtr<nsIPresShell> presShell = GetPresShell();
5662 if (!presShell)
5663 return NS_OK;
5665 // get the view manager
5666 nsViewManager* vm = presShell->GetViewManager();
5667 NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE);
5669 // get the root view
5670 nsView *view = vm->GetRootView(); // views are not ref counted
5671 NS_ENSURE_TRUE(view, NS_ERROR_FAILURE);
5673 // if our root view is hidden, we are not visible
5674 if (view->GetVisibility() == nsViewVisibility_kHide)
5675 return NS_OK;
5677 // otherwise, we must walk up the document and view trees checking
5678 // for a hidden view, unless we're an off screen browser, which
5679 // would make this test meaningless.
5681 nsRefPtr<nsDocShell> docShell = this;
5682 nsRefPtr<nsDocShell> parentItem = docShell->GetParentDocshell();
5683 while (parentItem) {
5684 presShell = docShell->GetPresShell();
5686 nsCOMPtr<nsIPresShell> pPresShell = parentItem->GetPresShell();
5688 // Null-check for crash in bug 267804
5689 if (!pPresShell) {
5690 NS_NOTREACHED("parent docshell has null pres shell");
5691 return NS_OK;
5694 vm = presShell->GetViewManager();
5695 if (vm) {
5696 view = vm->GetRootView();
5699 if (view) {
5700 view = view->GetParent(); // anonymous inner view
5701 if (view) {
5702 view = view->GetParent(); // subdocumentframe's view
5706 nsIFrame* frame = view ? view->GetFrame() : nullptr;
5707 bool isDocShellOffScreen = false;
5708 docShell->GetIsOffScreenBrowser(&isDocShellOffScreen);
5709 if (frame &&
5710 !frame->IsVisibleConsideringAncestors(nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY) &&
5711 !isDocShellOffScreen) {
5712 return NS_OK;
5715 docShell = parentItem;
5716 parentItem = docShell->GetParentDocshell();
5719 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(mTreeOwner));
5720 if (!treeOwnerAsWin) {
5721 *aVisibility = true;
5722 return NS_OK;
5725 // Check with the tree owner as well to give embedders a chance to
5726 // expose visibility as well.
5727 return treeOwnerAsWin->GetVisibility(aVisibility);
5730 NS_IMETHODIMP
5731 nsDocShell::SetIsOffScreenBrowser(bool aIsOffScreen)
5733 mIsOffScreenBrowser = aIsOffScreen;
5734 return NS_OK;
5737 NS_IMETHODIMP
5738 nsDocShell::GetIsOffScreenBrowser(bool *aIsOffScreen)
5740 *aIsOffScreen = mIsOffScreenBrowser;
5741 return NS_OK;
5744 NS_IMETHODIMP
5745 nsDocShell::SetIsActive(bool aIsActive)
5747 // We disallow setting active on chrome docshells.
5748 if (mItemType == nsIDocShellTreeItem::typeChrome)
5749 return NS_ERROR_INVALID_ARG;
5751 // Keep track ourselves.
5752 mIsActive = aIsActive;
5754 // Tell the PresShell about it.
5755 nsCOMPtr<nsIPresShell> pshell = GetPresShell();
5756 if (pshell)
5757 pshell->SetIsActive(aIsActive);
5759 // Tell the window about it
5760 if (mScriptGlobal) {
5761 mScriptGlobal->SetIsBackground(!aIsActive);
5762 if (nsCOMPtr<nsIDocument> doc = mScriptGlobal->GetExtantDoc()) {
5763 doc->PostVisibilityUpdateEvent();
5767 // Recursively tell all of our children, but don't tell <iframe mozbrowser>
5768 // children; they handle their state separately.
5769 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
5770 while (iter.HasMore()) {
5771 nsCOMPtr<nsIDocShell> docshell = do_QueryObject(iter.GetNext());
5772 if (!docshell) {
5773 continue;
5776 if (!docshell->GetIsBrowserOrApp()) {
5777 docshell->SetIsActive(aIsActive);
5781 return NS_OK;
5784 NS_IMETHODIMP
5785 nsDocShell::GetIsActive(bool *aIsActive)
5787 *aIsActive = mIsActive;
5788 return NS_OK;
5791 NS_IMETHODIMP
5792 nsDocShell::SetIsAppTab(bool aIsAppTab)
5794 mIsAppTab = aIsAppTab;
5795 return NS_OK;
5798 NS_IMETHODIMP
5799 nsDocShell::GetIsAppTab(bool *aIsAppTab)
5801 *aIsAppTab = mIsAppTab;
5802 return NS_OK;
5805 NS_IMETHODIMP
5806 nsDocShell::SetSandboxFlags(uint32_t aSandboxFlags)
5808 mSandboxFlags = aSandboxFlags;
5809 return NS_OK;
5812 NS_IMETHODIMP
5813 nsDocShell::GetSandboxFlags(uint32_t *aSandboxFlags)
5815 *aSandboxFlags = mSandboxFlags;
5816 return NS_OK;
5819 NS_IMETHODIMP
5820 nsDocShell::SetOnePermittedSandboxedNavigator(nsIDocShell* aSandboxedNavigator)
5822 if (mOnePermittedSandboxedNavigator) {
5823 NS_ERROR("One Permitted Sandboxed Navigator should only be set once.");
5824 return NS_OK;
5827 mOnePermittedSandboxedNavigator = do_GetWeakReference(aSandboxedNavigator);
5828 NS_ASSERTION(mOnePermittedSandboxedNavigator,
5829 "One Permitted Sandboxed Navigator must support weak references.");
5831 return NS_OK;
5834 NS_IMETHODIMP
5835 nsDocShell::GetOnePermittedSandboxedNavigator(nsIDocShell** aSandboxedNavigator)
5837 NS_ENSURE_ARG_POINTER(aSandboxedNavigator);
5838 nsCOMPtr<nsIDocShell> permittedNavigator =
5839 do_QueryReferent(mOnePermittedSandboxedNavigator);
5840 NS_IF_ADDREF(*aSandboxedNavigator = permittedNavigator);
5841 return NS_OK;
5844 NS_IMETHODIMP
5845 nsDocShell::SetDefaultLoadFlags(uint32_t aDefaultLoadFlags)
5847 mDefaultLoadFlags = aDefaultLoadFlags;
5849 // Tell the load group to set these flags all requests in the group
5850 if (mLoadGroup) {
5851 mLoadGroup->SetDefaultLoadFlags(aDefaultLoadFlags);
5852 } else {
5853 NS_WARNING("nsDocShell::SetDefaultLoadFlags has no loadGroup to propagate the flags to");
5856 // Recursively tell all of our children. We *do not* skip
5857 // <iframe mozbrowser> children - if someone sticks custom flags in this
5858 // docShell then they too get the same flags.
5859 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
5860 while (iter.HasMore()) {
5861 nsCOMPtr<nsIDocShell> docshell = do_QueryObject(iter.GetNext());
5862 if (!docshell) {
5863 continue;
5865 docshell->SetDefaultLoadFlags(aDefaultLoadFlags);
5867 return NS_OK;
5870 NS_IMETHODIMP
5871 nsDocShell::GetDefaultLoadFlags(uint32_t *aDefaultLoadFlags)
5873 *aDefaultLoadFlags = mDefaultLoadFlags;
5874 return NS_OK;
5878 NS_IMETHODIMP
5879 nsDocShell::SetMixedContentChannel(nsIChannel* aMixedContentChannel)
5881 #ifdef DEBUG
5882 // if the channel is non-null
5883 if (aMixedContentChannel) {
5884 // Get the root docshell.
5885 nsCOMPtr<nsIDocShellTreeItem> root;
5886 GetSameTypeRootTreeItem(getter_AddRefs(root));
5887 NS_WARN_IF_FALSE(
5888 root.get() == static_cast<nsIDocShellTreeItem *>(this),
5889 "Setting mMixedContentChannel on a docshell that is not the root docshell"
5892 #endif
5893 mMixedContentChannel = aMixedContentChannel;
5894 return NS_OK;
5897 NS_IMETHODIMP
5898 nsDocShell::GetFailedChannel(nsIChannel** aFailedChannel)
5900 NS_ENSURE_ARG_POINTER(aFailedChannel);
5901 nsIDocument* doc = GetDocument();
5902 if (!doc) {
5903 *aFailedChannel = nullptr;
5904 return NS_OK;
5906 NS_IF_ADDREF(*aFailedChannel = doc->GetFailedChannel());
5907 return NS_OK;
5910 NS_IMETHODIMP
5911 nsDocShell::GetMixedContentChannel(nsIChannel **aMixedContentChannel)
5913 NS_ENSURE_ARG_POINTER(aMixedContentChannel);
5914 NS_IF_ADDREF(*aMixedContentChannel = mMixedContentChannel);
5915 return NS_OK;
5918 NS_IMETHODIMP
5919 nsDocShell::GetAllowMixedContentAndConnectionData(bool* aRootHasSecureConnection, bool* aAllowMixedContent, bool* aIsRootDocShell)
5921 *aRootHasSecureConnection = true;
5922 *aAllowMixedContent = false;
5923 *aIsRootDocShell = false;
5925 nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
5926 GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
5927 NS_ASSERTION(sameTypeRoot, "No document shell root tree item from document shell tree item!");
5928 *aIsRootDocShell = sameTypeRoot.get() == static_cast<nsIDocShellTreeItem *>(this);
5930 // now get the document from sameTypeRoot
5931 nsCOMPtr<nsIDocument> rootDoc = sameTypeRoot->GetDocument();
5932 if (rootDoc) {
5933 nsCOMPtr<nsIPrincipal> rootPrincipal = rootDoc->NodePrincipal();
5935 // For things with system principal (e.g. scratchpad) there is no uri
5936 // aRootHasSecureConnection should be false.
5937 nsCOMPtr<nsIURI> rootUri;
5938 if (nsContentUtils::IsSystemPrincipal(rootPrincipal) ||
5939 NS_FAILED(rootPrincipal->GetURI(getter_AddRefs(rootUri))) || !rootUri ||
5940 NS_FAILED(rootUri->SchemeIs("https", aRootHasSecureConnection))) {
5941 *aRootHasSecureConnection = false;
5944 // Check the root doc's channel against the root docShell's mMixedContentChannel to see
5945 // if they are the same. If they are the same, the user has overriden
5946 // the block.
5947 nsCOMPtr<nsIDocShell> rootDocShell = do_QueryInterface(sameTypeRoot);
5948 nsCOMPtr<nsIChannel> mixedChannel;
5949 rootDocShell->GetMixedContentChannel(getter_AddRefs(mixedChannel));
5950 *aAllowMixedContent = mixedChannel && (mixedChannel == rootDoc->GetChannel());
5953 return NS_OK;
5956 NS_IMETHODIMP
5957 nsDocShell::SetVisibility(bool aVisibility)
5959 // Show()/Hide() may change mContentViewer.
5960 nsCOMPtr<nsIContentViewer> cv = mContentViewer;
5961 if (!cv)
5962 return NS_OK;
5963 if (aVisibility) {
5964 cv->Show();
5966 else {
5967 cv->Hide();
5970 return NS_OK;
5973 NS_IMETHODIMP
5974 nsDocShell::GetEnabled(bool *aEnabled)
5976 NS_ENSURE_ARG_POINTER(aEnabled);
5977 *aEnabled = true;
5978 return NS_ERROR_NOT_IMPLEMENTED;
5981 NS_IMETHODIMP
5982 nsDocShell::SetEnabled(bool aEnabled)
5984 return NS_ERROR_NOT_IMPLEMENTED;
5987 NS_IMETHODIMP
5988 nsDocShell::SetFocus()
5990 return NS_OK;
5993 NS_IMETHODIMP
5994 nsDocShell::GetMainWidget(nsIWidget ** aMainWidget)
5996 // We don't create our own widget, so simply return the parent one.
5997 return GetParentWidget(aMainWidget);
6000 NS_IMETHODIMP
6001 nsDocShell::GetTitle(char16_t ** aTitle)
6003 NS_ENSURE_ARG_POINTER(aTitle);
6005 *aTitle = ToNewUnicode(mTitle);
6006 return NS_OK;
6009 NS_IMETHODIMP
6010 nsDocShell::SetTitle(const char16_t * aTitle)
6012 // Store local title
6013 mTitle = aTitle;
6015 nsCOMPtr<nsIDocShellTreeItem> parent;
6016 GetSameTypeParent(getter_AddRefs(parent));
6018 // When title is set on the top object it should then be passed to the
6019 // tree owner.
6020 if (!parent) {
6021 nsCOMPtr<nsIBaseWindow>
6022 treeOwnerAsWin(do_QueryInterface(mTreeOwner));
6023 if (treeOwnerAsWin)
6024 treeOwnerAsWin->SetTitle(aTitle);
6027 if (mCurrentURI && mLoadType != LOAD_ERROR_PAGE && mUseGlobalHistory &&
6028 !mInPrivateBrowsing) {
6029 nsCOMPtr<IHistory> history = services::GetHistoryService();
6030 if (history) {
6031 history->SetURITitle(mCurrentURI, mTitle);
6033 else if (mGlobalHistory) {
6034 mGlobalHistory->SetPageTitle(mCurrentURI, nsString(mTitle));
6038 // Update SessionHistory with the document's title.
6039 if (mOSHE && mLoadType != LOAD_BYPASS_HISTORY &&
6040 mLoadType != LOAD_ERROR_PAGE) {
6042 mOSHE->SetTitle(mTitle);
6045 return NS_OK;
6048 nsresult
6049 nsDocShell::GetCurScrollPos(int32_t scrollOrientation, int32_t * curPos)
6051 NS_ENSURE_ARG_POINTER(curPos);
6053 nsIScrollableFrame* sf = GetRootScrollFrame();
6054 NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
6056 nsPoint pt = sf->GetScrollPosition();
6058 switch (scrollOrientation) {
6059 case ScrollOrientation_X:
6060 *curPos = pt.x;
6061 return NS_OK;
6063 case ScrollOrientation_Y:
6064 *curPos = pt.y;
6065 return NS_OK;
6067 default:
6068 NS_ENSURE_TRUE(false, NS_ERROR_INVALID_ARG);
6072 nsresult
6073 nsDocShell::SetCurScrollPosEx(int32_t curHorizontalPos, int32_t curVerticalPos)
6075 nsIScrollableFrame* sf = GetRootScrollFrame();
6076 NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
6078 sf->ScrollTo(nsPoint(curHorizontalPos, curVerticalPos),
6079 nsIScrollableFrame::INSTANT);
6080 return NS_OK;
6083 //*****************************************************************************
6084 // nsDocShell::nsIScrollable
6085 //*****************************************************************************
6087 NS_IMETHODIMP
6088 nsDocShell::GetDefaultScrollbarPreferences(int32_t scrollOrientation,
6089 int32_t * scrollbarPref)
6091 NS_ENSURE_ARG_POINTER(scrollbarPref);
6092 switch (scrollOrientation) {
6093 case ScrollOrientation_X:
6094 *scrollbarPref = mDefaultScrollbarPref.x;
6095 return NS_OK;
6097 case ScrollOrientation_Y:
6098 *scrollbarPref = mDefaultScrollbarPref.y;
6099 return NS_OK;
6101 default:
6102 NS_ENSURE_TRUE(false, NS_ERROR_INVALID_ARG);
6104 return NS_ERROR_FAILURE;
6107 NS_IMETHODIMP
6108 nsDocShell::SetDefaultScrollbarPreferences(int32_t scrollOrientation,
6109 int32_t scrollbarPref)
6111 switch (scrollOrientation) {
6112 case ScrollOrientation_X:
6113 mDefaultScrollbarPref.x = scrollbarPref;
6114 return NS_OK;
6116 case ScrollOrientation_Y:
6117 mDefaultScrollbarPref.y = scrollbarPref;
6118 return NS_OK;
6120 default:
6121 NS_ENSURE_TRUE(false, NS_ERROR_INVALID_ARG);
6123 return NS_ERROR_FAILURE;
6126 NS_IMETHODIMP
6127 nsDocShell::GetScrollbarVisibility(bool * verticalVisible,
6128 bool * horizontalVisible)
6130 nsIScrollableFrame* sf = GetRootScrollFrame();
6131 NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
6133 uint32_t scrollbarVisibility = sf->GetScrollbarVisibility();
6134 if (verticalVisible)
6135 *verticalVisible = (scrollbarVisibility & nsIScrollableFrame::VERTICAL) != 0;
6136 if (horizontalVisible)
6137 *horizontalVisible = (scrollbarVisibility & nsIScrollableFrame::HORIZONTAL) != 0;
6139 return NS_OK;
6142 //*****************************************************************************
6143 // nsDocShell::nsITextScroll
6144 //*****************************************************************************
6146 NS_IMETHODIMP
6147 nsDocShell::ScrollByLines(int32_t numLines)
6149 nsIScrollableFrame* sf = GetRootScrollFrame();
6150 NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
6152 sf->ScrollBy(nsIntPoint(0, numLines), nsIScrollableFrame::LINES,
6153 nsIScrollableFrame::SMOOTH);
6154 return NS_OK;
6157 NS_IMETHODIMP
6158 nsDocShell::ScrollByPages(int32_t numPages)
6160 nsIScrollableFrame* sf = GetRootScrollFrame();
6161 NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
6163 sf->ScrollBy(nsIntPoint(0, numPages), nsIScrollableFrame::PAGES,
6164 nsIScrollableFrame::SMOOTH);
6165 return NS_OK;
6168 //*****************************************************************************
6169 // nsDocShell::nsIRefreshURI
6170 //*****************************************************************************
6172 NS_IMETHODIMP
6173 nsDocShell::RefreshURI(nsIURI * aURI, int32_t aDelay, bool aRepeat,
6174 bool aMetaRefresh)
6176 NS_ENSURE_ARG(aURI);
6178 /* Check if Meta refresh/redirects are permitted. Some
6179 * embedded applications may not want to do this.
6180 * Must do this before sending out NOTIFY_REFRESH events
6181 * because listeners may have side effects (e.g. displaying a
6182 * button to manually trigger the refresh later).
6184 bool allowRedirects = true;
6185 GetAllowMetaRedirects(&allowRedirects);
6186 if (!allowRedirects)
6187 return NS_OK;
6189 // If any web progress listeners are listening for NOTIFY_REFRESH events,
6190 // give them a chance to block this refresh.
6191 bool sameURI;
6192 nsresult rv = aURI->Equals(mCurrentURI, &sameURI);
6193 if (NS_FAILED(rv))
6194 sameURI = false;
6195 if (!RefreshAttempted(this, aURI, aDelay, sameURI))
6196 return NS_OK;
6198 nsRefreshTimer *refreshTimer = new nsRefreshTimer();
6199 NS_ENSURE_TRUE(refreshTimer, NS_ERROR_OUT_OF_MEMORY);
6200 uint32_t busyFlags = 0;
6201 GetBusyFlags(&busyFlags);
6203 nsCOMPtr<nsISupports> dataRef = refreshTimer; // Get the ref count to 1
6205 refreshTimer->mDocShell = this;
6206 refreshTimer->mURI = aURI;
6207 refreshTimer->mDelay = aDelay;
6208 refreshTimer->mRepeat = aRepeat;
6209 refreshTimer->mMetaRefresh = aMetaRefresh;
6211 if (!mRefreshURIList) {
6212 NS_ENSURE_SUCCESS(NS_NewISupportsArray(getter_AddRefs(mRefreshURIList)),
6213 NS_ERROR_FAILURE);
6216 if (busyFlags & BUSY_FLAGS_BUSY) {
6217 // We are busy loading another page. Don't create the
6218 // timer right now. Instead queue up the request and trigger the
6219 // timer in EndPageLoad().
6220 mRefreshURIList->AppendElement(refreshTimer);
6222 else {
6223 // There is no page loading going on right now. Create the
6224 // timer and fire it right away.
6225 nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1");
6226 NS_ENSURE_TRUE(timer, NS_ERROR_FAILURE);
6228 mRefreshURIList->AppendElement(timer); // owning timer ref
6229 timer->InitWithCallback(refreshTimer, aDelay, nsITimer::TYPE_ONE_SHOT);
6231 return NS_OK;
6234 nsresult
6235 nsDocShell::ForceRefreshURIFromTimer(nsIURI * aURI,
6236 int32_t aDelay,
6237 bool aMetaRefresh,
6238 nsITimer* aTimer)
6240 NS_PRECONDITION(aTimer, "Must have a timer here");
6242 // Remove aTimer from mRefreshURIList if needed
6243 if (mRefreshURIList) {
6244 uint32_t n = 0;
6245 mRefreshURIList->Count(&n);
6247 for (uint32_t i = 0; i < n; ++i) {
6248 nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i);
6249 if (timer == aTimer) {
6250 mRefreshURIList->RemoveElementAt(i);
6251 break;
6256 return ForceRefreshURI(aURI, aDelay, aMetaRefresh);
6259 bool
6260 nsDocShell::DoAppRedirectIfNeeded(nsIURI * aURI,
6261 nsIDocShellLoadInfo * aLoadInfo,
6262 bool aFirstParty)
6264 uint32_t appId;
6265 nsresult rv = GetAppId(&appId);
6266 if (NS_FAILED(rv)) {
6267 return false;
6270 if (appId != nsIScriptSecurityManager::NO_APP_ID &&
6271 appId != nsIScriptSecurityManager::UNKNOWN_APP_ID) {
6272 nsCOMPtr<nsIAppsService> appsService =
6273 do_GetService(APPS_SERVICE_CONTRACTID);
6274 NS_ASSERTION(appsService, "No AppsService available");
6275 nsCOMPtr<nsIURI> redirect;
6276 rv = appsService->GetRedirect(appId, aURI, getter_AddRefs(redirect));
6277 if (NS_SUCCEEDED(rv) && redirect) {
6278 rv = LoadURI(redirect, aLoadInfo, nsIWebNavigation::LOAD_FLAGS_NONE, aFirstParty);
6279 if (NS_SUCCEEDED(rv)) {
6280 return true;
6285 return false;
6288 NS_IMETHODIMP
6289 nsDocShell::ForceRefreshURI(nsIURI * aURI,
6290 int32_t aDelay,
6291 bool aMetaRefresh)
6293 NS_ENSURE_ARG(aURI);
6295 nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
6296 CreateLoadInfo(getter_AddRefs(loadInfo));
6297 NS_ENSURE_TRUE(loadInfo, NS_ERROR_OUT_OF_MEMORY);
6299 /* We do need to pass in a referrer, but we don't want it to
6300 * be sent to the server.
6302 loadInfo->SetSendReferrer(false);
6304 /* for most refreshes the current URI is an appropriate
6305 * internal referrer
6307 loadInfo->SetReferrer(mCurrentURI);
6309 /* Don't ever "guess" on which owner to use to avoid picking
6310 * the current owner.
6312 loadInfo->SetOwnerIsExplicit(true);
6314 /* Check if this META refresh causes a redirection
6315 * to another site.
6317 bool equalUri = false;
6318 nsresult rv = aURI->Equals(mCurrentURI, &equalUri);
6319 if (NS_SUCCEEDED(rv) && (!equalUri) && aMetaRefresh &&
6320 aDelay <= REFRESH_REDIRECT_TIMER) {
6322 /* It is a META refresh based redirection within the threshold time
6323 * we have in mind (15000 ms as defined by REFRESH_REDIRECT_TIMER).
6324 * Pass a REPLACE flag to LoadURI().
6326 loadInfo->SetLoadType(nsIDocShellLoadInfo::loadNormalReplace);
6328 /* for redirects we mimic HTTP, which passes the
6329 * original referrer
6331 nsCOMPtr<nsIURI> internalReferrer;
6332 GetReferringURI(getter_AddRefs(internalReferrer));
6333 if (internalReferrer) {
6334 loadInfo->SetReferrer(internalReferrer);
6337 else {
6338 loadInfo->SetLoadType(nsIDocShellLoadInfo::loadRefresh);
6342 * LoadURI(...) will cancel all refresh timers... This causes the
6343 * Timer and its refreshData instance to be released...
6345 LoadURI(aURI, loadInfo, nsIWebNavigation::LOAD_FLAGS_NONE, true);
6347 return NS_OK;
6350 nsresult
6351 nsDocShell::SetupRefreshURIFromHeader(nsIURI * aBaseURI,
6352 nsIPrincipal* aPrincipal,
6353 const nsACString & aHeader)
6355 // Refresh headers are parsed with the following format in mind
6356 // <META HTTP-EQUIV=REFRESH CONTENT="5; URL=http://uri">
6357 // By the time we are here, the following is true:
6358 // header = "REFRESH"
6359 // content = "5; URL=http://uri" // note the URL attribute is
6360 // optional, if it is absent, the currently loaded url is used.
6361 // Also note that the seconds and URL separator can be either
6362 // a ';' or a ','. The ',' separator should be illegal but CNN
6363 // is using it.
6365 // We need to handle the following strings, where
6366 // - X is a set of digits
6367 // - URI is either a relative or absolute URI
6369 // Note that URI should start with "url=" but we allow omission
6371 // "" || ";" || ","
6372 // empty string. use the currently loaded URI
6373 // and refresh immediately.
6374 // "X" || "X;" || "X,"
6375 // Refresh the currently loaded URI in X seconds.
6376 // "X; URI" || "X, URI"
6377 // Refresh using URI as the destination in X seconds.
6378 // "URI" || "; URI" || ", URI"
6379 // Refresh immediately using URI as the destination.
6381 // Currently, anything immediately following the URI, if
6382 // separated by any char in the set "'\"\t\r\n " will be
6383 // ignored. So "10; url=go.html ; foo=bar" will work,
6384 // and so will "10; url='go.html'; foo=bar". However,
6385 // "10; url=go.html; foo=bar" will result in the uri
6386 // "go.html;" since ';' and ',' are valid uri characters.
6388 // Note that we need to remove any tokens wrapping the URI.
6389 // These tokens currently include spaces, double and single
6390 // quotes.
6392 // when done, seconds is 0 or the given number of seconds
6393 // uriAttrib is empty or the URI specified
6394 MOZ_ASSERT(aPrincipal);
6396 nsAutoCString uriAttrib;
6397 int32_t seconds = 0;
6398 bool specifiesSeconds = false;
6400 nsACString::const_iterator iter, tokenStart, doneIterating;
6402 aHeader.BeginReading(iter);
6403 aHeader.EndReading(doneIterating);
6405 // skip leading whitespace
6406 while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter))
6407 ++iter;
6409 tokenStart = iter;
6411 // skip leading + and -
6412 if (iter != doneIterating && (*iter == '-' || *iter == '+'))
6413 ++iter;
6415 // parse number
6416 while (iter != doneIterating && (*iter >= '0' && *iter <= '9')) {
6417 seconds = seconds * 10 + (*iter - '0');
6418 specifiesSeconds = true;
6419 ++iter;
6422 if (iter != doneIterating) {
6423 // if we started with a '-', number is negative
6424 if (*tokenStart == '-')
6425 seconds = -seconds;
6427 // skip to next ';' or ','
6428 nsACString::const_iterator iterAfterDigit = iter;
6429 while (iter != doneIterating && !(*iter == ';' || *iter == ','))
6431 if (specifiesSeconds)
6433 // Non-whitespace characters here mean that the string is
6434 // malformed but tolerate sites that specify a decimal point,
6435 // even though meta refresh only works on whole seconds.
6436 if (iter == iterAfterDigit &&
6437 !nsCRT::IsAsciiSpace(*iter) && *iter != '.')
6439 // The characters between the seconds and the next
6440 // section are just garbage!
6441 // e.g. content="2a0z+,URL=http://www.mozilla.org/"
6442 // Just ignore this redirect.
6443 return NS_ERROR_FAILURE;
6445 else if (nsCRT::IsAsciiSpace(*iter))
6447 // We've had at least one whitespace so tolerate the mistake
6448 // and drop through.
6449 // e.g. content="10 foo"
6450 ++iter;
6451 break;
6454 ++iter;
6457 // skip any remaining whitespace
6458 while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter))
6459 ++iter;
6461 // skip ';' or ','
6462 if (iter != doneIterating && (*iter == ';' || *iter == ',')) {
6463 ++iter;
6466 // skip whitespace
6467 while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter))
6468 ++iter;
6471 // possible start of URI
6472 tokenStart = iter;
6474 // skip "url = " to real start of URI
6475 if (iter != doneIterating && (*iter == 'u' || *iter == 'U')) {
6476 ++iter;
6477 if (iter != doneIterating && (*iter == 'r' || *iter == 'R')) {
6478 ++iter;
6479 if (iter != doneIterating && (*iter == 'l' || *iter == 'L')) {
6480 ++iter;
6482 // skip whitespace
6483 while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter))
6484 ++iter;
6486 if (iter != doneIterating && *iter == '=') {
6487 ++iter;
6489 // skip whitespace
6490 while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter))
6491 ++iter;
6493 // found real start of URI
6494 tokenStart = iter;
6500 // skip a leading '"' or '\''.
6502 bool isQuotedURI = false;
6503 if (tokenStart != doneIterating && (*tokenStart == '"' || *tokenStart == '\''))
6505 isQuotedURI = true;
6506 ++tokenStart;
6509 // set iter to start of URI
6510 iter = tokenStart;
6512 // tokenStart here points to the beginning of URI
6514 // grab the rest of the URI
6515 while (iter != doneIterating)
6517 if (isQuotedURI && (*iter == '"' || *iter == '\''))
6518 break;
6519 ++iter;
6522 // move iter one back if the last character is a '"' or '\''
6523 if (iter != tokenStart && isQuotedURI) {
6524 --iter;
6525 if (!(*iter == '"' || *iter == '\''))
6526 ++iter;
6529 // URI is whatever's contained from tokenStart to iter.
6530 // note: if tokenStart == doneIterating, so is iter.
6532 nsresult rv = NS_OK;
6534 nsCOMPtr<nsIURI> uri;
6535 bool specifiesURI = false;
6536 if (tokenStart == iter) {
6537 uri = aBaseURI;
6539 else {
6540 uriAttrib = Substring(tokenStart, iter);
6541 // NS_NewURI takes care of any whitespace surrounding the URL
6542 rv = NS_NewURI(getter_AddRefs(uri), uriAttrib, nullptr, aBaseURI);
6543 specifiesURI = true;
6546 // No URI or seconds were specified
6547 if (!specifiesSeconds && !specifiesURI)
6549 // Do nothing because the alternative is to spin around in a refresh
6550 // loop forever!
6551 return NS_ERROR_FAILURE;
6554 if (NS_SUCCEEDED(rv)) {
6555 nsCOMPtr<nsIScriptSecurityManager>
6556 securityManager(do_GetService
6557 (NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv));
6558 if (NS_SUCCEEDED(rv)) {
6559 rv = securityManager->
6560 CheckLoadURIWithPrincipal(aPrincipal, uri,
6561 nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT);
6563 if (NS_SUCCEEDED(rv)) {
6564 bool isjs = true;
6565 rv = NS_URIChainHasFlags(uri,
6566 nsIProtocolHandler::URI_OPENING_EXECUTES_SCRIPT, &isjs);
6567 NS_ENSURE_SUCCESS(rv, rv);
6569 if (isjs) {
6570 return NS_ERROR_FAILURE;
6574 if (NS_SUCCEEDED(rv)) {
6575 // Since we can't travel back in time yet, just pretend
6576 // negative numbers do nothing at all.
6577 if (seconds < 0)
6578 return NS_ERROR_FAILURE;
6580 rv = RefreshURI(uri, seconds * 1000, false, true);
6584 return rv;
6587 NS_IMETHODIMP nsDocShell::SetupRefreshURI(nsIChannel * aChannel)
6589 nsresult rv;
6590 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel, &rv));
6591 if (NS_SUCCEEDED(rv)) {
6592 nsAutoCString refreshHeader;
6593 rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("refresh"),
6594 refreshHeader);
6596 if (!refreshHeader.IsEmpty()) {
6597 nsCOMPtr<nsIScriptSecurityManager> secMan =
6598 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
6599 NS_ENSURE_SUCCESS(rv, rv);
6601 nsCOMPtr<nsIPrincipal> principal;
6602 rv = secMan->GetChannelPrincipal(aChannel, getter_AddRefs(principal));
6603 NS_ENSURE_SUCCESS(rv, rv);
6605 SetupReferrerFromChannel(aChannel);
6606 rv = SetupRefreshURIFromHeader(mCurrentURI, principal, refreshHeader);
6607 if (NS_SUCCEEDED(rv)) {
6608 return NS_REFRESHURI_HEADER_FOUND;
6612 return rv;
6615 static void
6616 DoCancelRefreshURITimers(nsISupportsArray* aTimerList)
6618 if (!aTimerList)
6619 return;
6621 uint32_t n=0;
6622 aTimerList->Count(&n);
6624 while (n) {
6625 nsCOMPtr<nsITimer> timer(do_QueryElementAt(aTimerList, --n));
6627 aTimerList->RemoveElementAt(n); // bye bye owning timer ref
6629 if (timer)
6630 timer->Cancel();
6634 NS_IMETHODIMP
6635 nsDocShell::CancelRefreshURITimers()
6637 DoCancelRefreshURITimers(mRefreshURIList);
6638 DoCancelRefreshURITimers(mSavedRefreshURIList);
6639 mRefreshURIList = nullptr;
6640 mSavedRefreshURIList = nullptr;
6642 return NS_OK;
6645 NS_IMETHODIMP
6646 nsDocShell::GetRefreshPending(bool* _retval)
6648 if (!mRefreshURIList) {
6649 *_retval = false;
6650 return NS_OK;
6653 uint32_t count;
6654 nsresult rv = mRefreshURIList->Count(&count);
6655 if (NS_SUCCEEDED(rv))
6656 *_retval = (count != 0);
6657 return rv;
6660 NS_IMETHODIMP
6661 nsDocShell::SuspendRefreshURIs()
6663 if (mRefreshURIList) {
6664 uint32_t n = 0;
6665 mRefreshURIList->Count(&n);
6667 for (uint32_t i = 0; i < n; ++i) {
6668 nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i);
6669 if (!timer)
6670 continue; // this must be a nsRefreshURI already
6672 // Replace this timer object with a nsRefreshTimer object.
6673 nsCOMPtr<nsITimerCallback> callback;
6674 timer->GetCallback(getter_AddRefs(callback));
6676 timer->Cancel();
6678 nsCOMPtr<nsITimerCallback> rt = do_QueryInterface(callback);
6679 NS_ASSERTION(rt, "RefreshURIList timer callbacks should only be RefreshTimer objects");
6681 mRefreshURIList->ReplaceElementAt(rt, i);
6685 // Suspend refresh URIs for our child shells as well.
6686 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
6687 while (iter.HasMore()) {
6688 nsCOMPtr<nsIDocShell> shell = do_QueryObject(iter.GetNext());
6689 if (shell)
6690 shell->SuspendRefreshURIs();
6693 return NS_OK;
6696 NS_IMETHODIMP
6697 nsDocShell::ResumeRefreshURIs()
6699 RefreshURIFromQueue();
6701 // Resume refresh URIs for our child shells as well.
6702 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
6703 while (iter.HasMore()) {
6704 nsCOMPtr<nsIDocShell> shell = do_QueryObject(iter.GetNext());
6705 if (shell)
6706 shell->ResumeRefreshURIs();
6709 return NS_OK;
6712 nsresult
6713 nsDocShell::RefreshURIFromQueue()
6715 if (!mRefreshURIList)
6716 return NS_OK;
6717 uint32_t n = 0;
6718 mRefreshURIList->Count(&n);
6720 while (n) {
6721 nsCOMPtr<nsISupports> element;
6722 mRefreshURIList->GetElementAt(--n, getter_AddRefs(element));
6723 nsCOMPtr<nsITimerCallback> refreshInfo(do_QueryInterface(element));
6725 if (refreshInfo) {
6726 // This is the nsRefreshTimer object, waiting to be
6727 // setup in a timer object and fired.
6728 // Create the timer and trigger it.
6729 uint32_t delay = static_cast<nsRefreshTimer*>(static_cast<nsITimerCallback*>(refreshInfo))->GetDelay();
6730 nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1");
6731 if (timer) {
6732 // Replace the nsRefreshTimer element in the queue with
6733 // its corresponding timer object, so that in case another
6734 // load comes through before the timer can go off, the timer will
6735 // get cancelled in CancelRefreshURITimer()
6736 mRefreshURIList->ReplaceElementAt(timer, n);
6737 timer->InitWithCallback(refreshInfo, delay, nsITimer::TYPE_ONE_SHOT);
6740 } // while
6742 return NS_OK;
6745 //*****************************************************************************
6746 // nsDocShell::nsIContentViewerContainer
6747 //*****************************************************************************
6749 NS_IMETHODIMP
6750 nsDocShell::Embed(nsIContentViewer * aContentViewer,
6751 const char *aCommand, nsISupports * aExtraInfo)
6753 // Save the LayoutHistoryState of the previous document, before
6754 // setting up new document
6755 PersistLayoutHistoryState();
6757 nsresult rv = SetupNewViewer(aContentViewer);
6758 NS_ENSURE_SUCCESS(rv, rv);
6760 // If we are loading a wyciwyg url from history, change the base URI for
6761 // the document to the original http url that created the document.write().
6762 // This makes sure that all relative urls in a document.written page loaded
6763 // via history work properly.
6764 if (mCurrentURI &&
6765 (mLoadType & LOAD_CMD_HISTORY ||
6766 mLoadType == LOAD_RELOAD_NORMAL ||
6767 mLoadType == LOAD_RELOAD_CHARSET_CHANGE)){
6768 bool isWyciwyg = false;
6769 // Check if the url is wyciwyg
6770 rv = mCurrentURI->SchemeIs("wyciwyg", &isWyciwyg);
6771 if (isWyciwyg && NS_SUCCEEDED(rv))
6772 SetBaseUrlForWyciwyg(aContentViewer);
6774 // XXX What if SetupNewViewer fails?
6775 if (mLSHE) {
6776 // Restore the editing state, if it's stored in session history.
6777 if (mLSHE->HasDetachedEditor()) {
6778 ReattachEditorToWindow(mLSHE);
6780 // Set history.state
6781 SetDocCurrentStateObj(mLSHE);
6783 SetHistoryEntry(&mOSHE, mLSHE);
6786 bool updateHistory = true;
6788 // Determine if this type of load should update history
6789 switch (mLoadType) {
6790 case LOAD_NORMAL_REPLACE:
6791 case LOAD_STOP_CONTENT_AND_REPLACE:
6792 case LOAD_RELOAD_BYPASS_CACHE:
6793 case LOAD_RELOAD_BYPASS_PROXY:
6794 case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
6795 case LOAD_REPLACE_BYPASS_CACHE:
6796 updateHistory = false;
6797 break;
6798 default:
6799 break;
6802 if (!updateHistory)
6803 SetLayoutHistoryState(nullptr);
6805 return NS_OK;
6808 /* void setIsPrinting (in boolean aIsPrinting); */
6809 NS_IMETHODIMP
6810 nsDocShell::SetIsPrinting(bool aIsPrinting)
6812 mIsPrintingOrPP = aIsPrinting;
6813 return NS_OK;
6816 //*****************************************************************************
6817 // nsDocShell::nsIWebProgressListener
6818 //*****************************************************************************
6820 NS_IMETHODIMP
6821 nsDocShell::OnProgressChange(nsIWebProgress * aProgress,
6822 nsIRequest * aRequest,
6823 int32_t aCurSelfProgress,
6824 int32_t aMaxSelfProgress,
6825 int32_t aCurTotalProgress,
6826 int32_t aMaxTotalProgress)
6828 return NS_OK;
6831 NS_IMETHODIMP
6832 nsDocShell::OnStateChange(nsIWebProgress * aProgress, nsIRequest * aRequest,
6833 uint32_t aStateFlags, nsresult aStatus)
6835 if ((~aStateFlags & (STATE_START | STATE_IS_NETWORK)) == 0) {
6836 // Save timing statistics.
6837 nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
6838 nsCOMPtr<nsIURI> uri;
6839 channel->GetURI(getter_AddRefs(uri));
6840 nsAutoCString aURI;
6841 uri->GetAsciiSpec(aURI);
6843 nsCOMPtr<nsIWyciwygChannel> wcwgChannel(do_QueryInterface(aRequest));
6844 nsCOMPtr<nsIWebProgress> webProgress =
6845 do_QueryInterface(GetAsSupports(this));
6847 // We don't update navigation timing for wyciwyg channels
6848 if (this == aProgress && !wcwgChannel){
6849 MaybeInitTiming();
6850 mTiming->NotifyFetchStart(uri, ConvertLoadTypeToNavigationType(mLoadType));
6853 // Was the wyciwyg document loaded on this docshell?
6854 if (wcwgChannel && !mLSHE && (mItemType == typeContent) && aProgress == webProgress.get()) {
6855 bool equalUri = true;
6856 // Store the wyciwyg url in session history, only if it is
6857 // being loaded fresh for the first time. We don't want
6858 // multiple entries for successive loads
6859 if (mCurrentURI &&
6860 NS_SUCCEEDED(uri->Equals(mCurrentURI, &equalUri)) &&
6861 !equalUri) {
6863 nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
6864 GetSameTypeParent(getter_AddRefs(parentAsItem));
6865 nsCOMPtr<nsIDocShell> parentDS(do_QueryInterface(parentAsItem));
6866 bool inOnLoadHandler = false;
6867 if (parentDS) {
6868 parentDS->GetIsExecutingOnLoadHandler(&inOnLoadHandler);
6870 if (inOnLoadHandler) {
6871 // We're handling parent's load event listener, which causes
6872 // document.write in a subdocument.
6873 // Need to clear the session history for all child
6874 // docshells so that we can handle them like they would
6875 // all be added dynamically.
6876 nsCOMPtr<nsIDocShell> parent =
6877 do_QueryInterface(parentAsItem);
6878 if (parent) {
6879 bool oshe = false;
6880 nsCOMPtr<nsISHEntry> entry;
6881 parent->GetCurrentSHEntry(getter_AddRefs(entry), &oshe);
6882 static_cast<nsDocShell*>(parent.get())->
6883 ClearFrameHistory(entry);
6887 // This is a document.write(). Get the made-up url
6888 // from the channel and store it in session history.
6889 // Pass false for aCloneChildren, since we're creating
6890 // a new DOM here.
6891 AddToSessionHistory(uri, wcwgChannel, nullptr, false,
6892 getter_AddRefs(mLSHE));
6893 SetCurrentURI(uri, aRequest, true, 0);
6894 // Save history state of the previous page
6895 PersistLayoutHistoryState();
6896 // We'll never get an Embed() for this load, so just go ahead
6897 // and SetHistoryEntry now.
6898 SetHistoryEntry(&mOSHE, mLSHE);
6902 // Page has begun to load
6903 mBusyFlags = BUSY_FLAGS_BUSY | BUSY_FLAGS_BEFORE_PAGE_LOAD;
6905 if ((aStateFlags & STATE_RESTORING) == 0) {
6906 // Show the progress cursor if the pref is set
6907 if (Preferences::GetBool("ui.use_activity_cursor", false)) {
6908 nsCOMPtr<nsIWidget> mainWidget;
6909 GetMainWidget(getter_AddRefs(mainWidget));
6910 if (mainWidget) {
6911 mainWidget->SetCursor(eCursor_spinning);
6916 else if ((~aStateFlags & (STATE_TRANSFERRING | STATE_IS_DOCUMENT)) == 0) {
6917 // Page is loading
6918 mBusyFlags = BUSY_FLAGS_BUSY | BUSY_FLAGS_PAGE_LOADING;
6920 else if ((aStateFlags & STATE_STOP) && (aStateFlags & STATE_IS_NETWORK)) {
6921 // Page has finished loading
6922 mBusyFlags = BUSY_FLAGS_NONE;
6924 // Hide the progress cursor if the pref is set
6925 if (Preferences::GetBool("ui.use_activity_cursor", false)) {
6926 nsCOMPtr<nsIWidget> mainWidget;
6927 GetMainWidget(getter_AddRefs(mainWidget));
6928 if (mainWidget) {
6929 mainWidget->SetCursor(eCursor_standard);
6933 if ((~aStateFlags & (STATE_IS_DOCUMENT | STATE_STOP)) == 0) {
6934 nsCOMPtr<nsIWebProgress> webProgress =
6935 do_QueryInterface(GetAsSupports(this));
6936 // Is the document stop notification for this document?
6937 if (aProgress == webProgress.get()) {
6938 nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
6939 EndPageLoad(aProgress, channel, aStatus);
6942 // note that redirect state changes will go through here as well, but it
6943 // is better to handle those in OnRedirectStateChange where more
6944 // information is available.
6945 return NS_OK;
6948 NS_IMETHODIMP
6949 nsDocShell::OnLocationChange(nsIWebProgress * aProgress, nsIRequest * aRequest,
6950 nsIURI * aURI, uint32_t aFlags)
6952 NS_NOTREACHED("notification excluded in AddProgressListener(...)");
6953 return NS_OK;
6956 void
6957 nsDocShell::OnRedirectStateChange(nsIChannel* aOldChannel,
6958 nsIChannel* aNewChannel,
6959 uint32_t aRedirectFlags,
6960 uint32_t aStateFlags)
6962 NS_ASSERTION(aStateFlags & STATE_REDIRECTING,
6963 "Calling OnRedirectStateChange when there is no redirect");
6964 if (!(aStateFlags & STATE_IS_DOCUMENT))
6965 return; // not a toplevel document
6967 nsCOMPtr<nsIURI> oldURI, newURI;
6968 aOldChannel->GetURI(getter_AddRefs(oldURI));
6969 aNewChannel->GetURI(getter_AddRefs(newURI));
6970 if (!oldURI || !newURI) {
6971 return;
6974 if (DoAppRedirectIfNeeded(newURI, nullptr, false)) {
6975 return;
6978 // Below a URI visit is saved (see AddURIVisit method doc).
6979 // The visit chain looks something like:
6980 // ...
6981 // Site N - 1
6982 // => Site N
6983 // (redirect to =>) Site N + 1 (we are here!)
6985 // Get N - 1 and transition type
6986 nsCOMPtr<nsIURI> previousURI;
6987 uint32_t previousFlags = 0;
6988 ExtractLastVisit(aOldChannel, getter_AddRefs(previousURI), &previousFlags);
6990 if (aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL ||
6991 ChannelIsPost(aOldChannel)) {
6992 // 1. Internal redirects are ignored because they are specific to the
6993 // channel implementation.
6994 // 2. POSTs are not saved by global history.
6996 // Regardless, we need to propagate the previous visit to the new
6997 // channel.
6998 SaveLastVisit(aNewChannel, previousURI, previousFlags);
7000 else {
7001 nsCOMPtr<nsIURI> referrer;
7002 // Treat referrer as null if there is an error getting it.
7003 (void)NS_GetReferrerFromChannel(aOldChannel,
7004 getter_AddRefs(referrer));
7006 // Get the HTTP response code, if available.
7007 uint32_t responseStatus = 0;
7008 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aOldChannel);
7009 if (httpChannel) {
7010 (void)httpChannel->GetResponseStatus(&responseStatus);
7013 // Add visit N -1 => N
7014 AddURIVisit(oldURI, referrer, previousURI, previousFlags,
7015 responseStatus);
7017 // Since N + 1 could be the final destination, we will not save N => N + 1
7018 // here. OnNewURI will do that, so we will cache it.
7019 SaveLastVisit(aNewChannel, oldURI, aRedirectFlags);
7022 // check if the new load should go through the application cache.
7023 nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
7024 do_QueryInterface(aNewChannel);
7025 if (appCacheChannel) {
7026 if (GeckoProcessType_Default != XRE_GetProcessType()) {
7027 // Permission will be checked in the parent process.
7028 appCacheChannel->SetChooseApplicationCache(true);
7029 } else {
7030 nsCOMPtr<nsIScriptSecurityManager> secMan =
7031 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
7033 if (secMan) {
7034 nsCOMPtr<nsIPrincipal> principal;
7035 secMan->GetDocShellCodebasePrincipal(newURI, this, getter_AddRefs(principal));
7036 appCacheChannel->SetChooseApplicationCache(NS_ShouldCheckAppCache(principal,
7037 mInPrivateBrowsing));
7042 if (!(aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL) &&
7043 mLoadType & (LOAD_CMD_RELOAD | LOAD_CMD_HISTORY)) {
7044 mLoadType = LOAD_NORMAL_REPLACE;
7045 SetHistoryEntry(&mLSHE, nullptr);
7049 NS_IMETHODIMP
7050 nsDocShell::OnStatusChange(nsIWebProgress * aWebProgress,
7051 nsIRequest * aRequest,
7052 nsresult aStatus, const char16_t * aMessage)
7054 NS_NOTREACHED("notification excluded in AddProgressListener(...)");
7055 return NS_OK;
7058 NS_IMETHODIMP
7059 nsDocShell::OnSecurityChange(nsIWebProgress * aWebProgress,
7060 nsIRequest * aRequest, uint32_t state)
7062 NS_NOTREACHED("notification excluded in AddProgressListener(...)");
7063 return NS_OK;
7067 nsresult
7068 nsDocShell::EndPageLoad(nsIWebProgress * aProgress,
7069 nsIChannel * aChannel, nsresult aStatus)
7071 if(!aChannel)
7072 return NS_ERROR_NULL_POINTER;
7074 MOZ_EVENT_TRACER_DONE(this, "docshell::pageload");
7076 nsCOMPtr<nsIURI> url;
7077 nsresult rv = aChannel->GetURI(getter_AddRefs(url));
7078 if (NS_FAILED(rv)) return rv;
7080 nsCOMPtr<nsITimedChannel> timingChannel =
7081 do_QueryInterface(aChannel);
7082 if (timingChannel) {
7083 TimeStamp channelCreationTime;
7084 rv = timingChannel->GetChannelCreation(&channelCreationTime);
7085 if (NS_SUCCEEDED(rv) && !channelCreationTime.IsNull()) {
7086 Telemetry::AccumulateTimeDelta(
7087 Telemetry::TOTAL_CONTENT_PAGE_LOAD_TIME,
7088 channelCreationTime);
7089 nsCOMPtr<nsPILoadGroupInternal> internalLoadGroup =
7090 do_QueryInterface(mLoadGroup);
7091 if (internalLoadGroup)
7092 internalLoadGroup->OnEndPageLoad(aChannel);
7096 // Timing is picked up by the window, we don't need it anymore
7097 mTiming = nullptr;
7099 // clean up reload state for meta charset
7100 if (eCharsetReloadRequested == mCharsetReloadState)
7101 mCharsetReloadState = eCharsetReloadStopOrigional;
7102 else
7103 mCharsetReloadState = eCharsetReloadInit;
7105 // Save a pointer to the currently-loading history entry.
7106 // nsDocShell::EndPageLoad will clear mLSHE, but we may need this history
7107 // entry further down in this method.
7108 nsCOMPtr<nsISHEntry> loadingSHE = mLSHE;
7111 // one of many safeguards that prevent death and destruction if
7112 // someone is so very very rude as to bring this window down
7113 // during this load handler.
7115 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
7117 // Notify the ContentViewer that the Document has finished loading. This
7118 // will cause any OnLoad(...) and PopState(...) handlers to fire.
7119 if (!mEODForCurrentDocument && mContentViewer) {
7120 mIsExecutingOnLoadHandler = true;
7121 mContentViewer->LoadComplete(aStatus);
7122 mIsExecutingOnLoadHandler = false;
7124 mEODForCurrentDocument = true;
7126 // If all documents have completed their loading
7127 // favor native event dispatch priorities
7128 // over performance
7129 if (--gNumberOfDocumentsLoading == 0) {
7130 // Hint to use normal native event dispatch priorities
7131 FavorPerformanceHint(false);
7134 /* Check if the httpChannel has any cache-control related response headers,
7135 * like no-store, no-cache. If so, update SHEntry so that
7136 * when a user goes back/forward to this page, we appropriately do
7137 * form value restoration or load from server.
7139 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
7140 if (!httpChannel) // HttpChannel could be hiding underneath a Multipart channel.
7141 GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
7143 if (httpChannel) {
7144 // figure out if SH should be saving layout state.
7145 bool discardLayoutState = ShouldDiscardLayoutState(httpChannel);
7146 if (mLSHE && discardLayoutState && (mLoadType & LOAD_CMD_NORMAL) &&
7147 (mLoadType != LOAD_BYPASS_HISTORY) && (mLoadType != LOAD_ERROR_PAGE))
7148 mLSHE->SetSaveLayoutStateFlag(false);
7151 // Clear mLSHE after calling the onLoadHandlers. This way, if the
7152 // onLoadHandler tries to load something different in
7153 // itself or one of its children, we can deal with it appropriately.
7154 if (mLSHE) {
7155 mLSHE->SetLoadType(nsIDocShellLoadInfo::loadHistory);
7157 // Clear the mLSHE reference to indicate document loading is done one
7158 // way or another.
7159 SetHistoryEntry(&mLSHE, nullptr);
7161 // if there's a refresh header in the channel, this method
7162 // will set it up for us.
7163 RefreshURIFromQueue();
7165 // Test whether this is the top frame or a subframe
7166 bool isTopFrame = true;
7167 nsCOMPtr<nsIDocShellTreeItem> targetParentTreeItem;
7168 rv = GetSameTypeParent(getter_AddRefs(targetParentTreeItem));
7169 if (NS_SUCCEEDED(rv) && targetParentTreeItem) {
7170 isTopFrame = false;
7174 // If the page load failed, then deal with the error condition...
7175 // Errors are handled as follows:
7176 // 1. Check to see if it's a file not found error or bad content
7177 // encoding error.
7178 // 2. Send the URI to a keyword server (if enabled)
7179 // 3. If the error was DNS failure, then add www and .com to the URI
7180 // (if appropriate).
7181 // 4. Throw an error dialog box...
7183 if (url && NS_FAILED(aStatus)) {
7184 if (aStatus == NS_ERROR_FILE_NOT_FOUND ||
7185 aStatus == NS_ERROR_CORRUPTED_CONTENT ||
7186 aStatus == NS_ERROR_INVALID_CONTENT_ENCODING) {
7187 DisplayLoadError(aStatus, url, nullptr, aChannel);
7188 return NS_OK;
7191 // Handle iframe document not loading error because source was
7192 // a tracking URL. We make a note of this iframe node by including
7193 // it in a dedicated array of blocked tracking nodes under its parent
7194 // document. (document of parent window of blocked document)
7195 if (isTopFrame == false && aStatus == NS_ERROR_TRACKING_URI) {
7196 // frameElement is our nsIContent to be annotated
7197 nsCOMPtr<nsIDOMElement> frameElement;
7198 nsPIDOMWindow* thisWindow = GetWindow();
7199 if (!thisWindow) {
7200 return NS_OK;
7203 thisWindow->GetFrameElement(getter_AddRefs(frameElement));
7204 if (!frameElement) {
7205 return NS_OK;
7208 // Parent window
7209 nsCOMPtr<nsIDocShellTreeItem> parentItem;
7210 GetSameTypeParent(getter_AddRefs(parentItem));
7211 if (!parentItem) {
7212 return NS_OK;
7215 nsCOMPtr<nsIDocument> parentDoc;
7216 parentDoc = parentItem->GetDocument();
7217 if (!parentDoc) {
7218 return NS_OK;
7221 nsCOMPtr<nsIContent> cont = do_QueryInterface(frameElement);
7222 parentDoc->AddBlockedTrackingNode(cont);
7224 return NS_OK;
7227 if (sURIFixup) {
7229 // Try and make an alternative URI from the old one
7231 nsCOMPtr<nsIURI> newURI;
7232 nsCOMPtr<nsIInputStream> newPostData;
7234 nsAutoCString oldSpec;
7235 url->GetSpec(oldSpec);
7238 // First try keyword fixup
7240 if (aStatus == NS_ERROR_UNKNOWN_HOST && mAllowKeywordFixup) {
7241 bool keywordsEnabled =
7242 Preferences::GetBool("keyword.enabled", false);
7244 nsAutoCString host;
7245 url->GetHost(host);
7247 nsAutoCString scheme;
7248 url->GetScheme(scheme);
7250 int32_t dotLoc = host.FindChar('.');
7252 // we should only perform a keyword search under the following
7253 // conditions:
7254 // (0) Pref keyword.enabled is true
7255 // (1) the url scheme is http (or https)
7256 // (2) the url does not have a protocol scheme
7257 // If we don't enforce such a policy, then we end up doing
7258 // keyword searchs on urls we don't intend like imap, file,
7259 // mailbox, etc. This could lead to a security problem where we
7260 // send data to the keyword server that we shouldn't be.
7261 // Someone needs to clean up keywords in general so we can
7262 // determine on a per url basis if we want keywords
7263 // enabled...this is just a bandaid...
7264 if (keywordsEnabled && !scheme.IsEmpty() &&
7265 (scheme.Find("http") != 0)) {
7266 keywordsEnabled = false;
7269 if (keywordsEnabled && (kNotFound == dotLoc)) {
7270 // only send non-qualified hosts to the keyword server
7271 if (!mOriginalUriString.IsEmpty()) {
7272 sURIFixup->KeywordToURI(mOriginalUriString,
7273 getter_AddRefs(newPostData),
7274 getter_AddRefs(newURI));
7276 else {
7278 // If this string was passed through nsStandardURL by
7279 // chance, then it may have been converted from UTF-8 to
7280 // ACE, which would result in a completely bogus keyword
7281 // query. Here we try to recover the original Unicode
7282 // value, but this is not 100% correct since the value may
7283 // have been normalized per the IDN normalization rules.
7285 // Since we don't have access to the exact original string
7286 // that was entered by the user, this will just have to do.
7287 bool isACE;
7288 nsAutoCString utf8Host;
7289 nsCOMPtr<nsIIDNService> idnSrv =
7290 do_GetService(NS_IDNSERVICE_CONTRACTID);
7291 if (idnSrv &&
7292 NS_SUCCEEDED(idnSrv->IsACE(host, &isACE)) && isACE &&
7293 NS_SUCCEEDED(idnSrv->ConvertACEtoUTF8(host, utf8Host))) {
7294 sURIFixup->KeywordToURI(utf8Host,
7295 getter_AddRefs(newPostData),
7296 getter_AddRefs(newURI));
7297 } else {
7298 sURIFixup->KeywordToURI(host,
7299 getter_AddRefs(newPostData),
7300 getter_AddRefs(newURI));
7303 } // end keywordsEnabled
7307 // Now try change the address, e.g. turn http://foo into
7308 // http://www.foo.com
7310 if (aStatus == NS_ERROR_UNKNOWN_HOST ||
7311 aStatus == NS_ERROR_NET_RESET) {
7312 bool doCreateAlternate = true;
7314 // Skip fixup for anything except a normal document load
7315 // operation on the topframe.
7317 if (mLoadType != LOAD_NORMAL || !isTopFrame) {
7318 doCreateAlternate = false;
7320 else {
7321 // Test if keyword lookup produced a new URI or not
7322 if (newURI) {
7323 bool sameURI = false;
7324 url->Equals(newURI, &sameURI);
7325 if (!sameURI) {
7326 // Keyword lookup made a new URI so no need to try
7327 // an alternate one.
7328 doCreateAlternate = false;
7332 if (doCreateAlternate) {
7333 newURI = nullptr;
7334 newPostData = nullptr;
7335 sURIFixup->CreateFixupURI(oldSpec,
7336 nsIURIFixup::FIXUP_FLAGS_MAKE_ALTERNATE_URI,
7337 getter_AddRefs(newPostData),
7338 getter_AddRefs(newURI));
7342 // Did we make a new URI that is different to the old one? If so
7343 // load it.
7345 if (newURI) {
7346 // Make sure the new URI is different from the old one,
7347 // otherwise there's little point trying to load it again.
7348 bool sameURI = false;
7349 url->Equals(newURI, &sameURI);
7350 if (!sameURI) {
7351 nsAutoCString newSpec;
7352 newURI->GetSpec(newSpec);
7353 NS_ConvertUTF8toUTF16 newSpecW(newSpec);
7355 return LoadURI(newSpecW.get(), // URI string
7356 LOAD_FLAGS_NONE, // Load flags
7357 nullptr, // Referring URI
7358 newPostData, // Post data stream
7359 nullptr); // Headers stream
7364 // Well, fixup didn't work :-(
7365 // It is time to throw an error dialog box, and be done with it...
7367 // Errors to be shown only on top-level frames
7368 if ((aStatus == NS_ERROR_UNKNOWN_HOST ||
7369 aStatus == NS_ERROR_CONNECTION_REFUSED ||
7370 aStatus == NS_ERROR_UNKNOWN_PROXY_HOST ||
7371 aStatus == NS_ERROR_PROXY_CONNECTION_REFUSED) &&
7372 (isTopFrame || UseErrorPages())) {
7373 DisplayLoadError(aStatus, url, nullptr, aChannel);
7375 // Errors to be shown for any frame
7376 else if (aStatus == NS_ERROR_NET_TIMEOUT ||
7377 aStatus == NS_ERROR_REDIRECT_LOOP ||
7378 aStatus == NS_ERROR_UNKNOWN_SOCKET_TYPE ||
7379 aStatus == NS_ERROR_NET_INTERRUPT ||
7380 aStatus == NS_ERROR_NET_RESET ||
7381 aStatus == NS_ERROR_OFFLINE ||
7382 aStatus == NS_ERROR_MALWARE_URI ||
7383 aStatus == NS_ERROR_PHISHING_URI ||
7384 aStatus == NS_ERROR_UNSAFE_CONTENT_TYPE ||
7385 aStatus == NS_ERROR_REMOTE_XUL ||
7386 aStatus == NS_ERROR_OFFLINE ||
7387 NS_ERROR_GET_MODULE(aStatus) == NS_ERROR_MODULE_SECURITY) {
7388 DisplayLoadError(aStatus, url, nullptr, aChannel);
7390 else if (aStatus == NS_ERROR_DOCUMENT_NOT_CACHED) {
7391 // Non-caching channels will simply return NS_ERROR_OFFLINE.
7392 // Caching channels would have to look at their flags to work
7393 // out which error to return. Or we can fix up the error here.
7394 if (!(mLoadType & LOAD_CMD_HISTORY))
7395 aStatus = NS_ERROR_OFFLINE;
7396 DisplayLoadError(aStatus, url, nullptr, aChannel);
7398 } // if we have a host
7399 else if (url && NS_SUCCEEDED(aStatus)) {
7400 mozilla::net::PredictorLearnRedirect(url, aChannel, this);
7403 return NS_OK;
7407 //*****************************************************************************
7408 // nsDocShell: Content Viewer Management
7409 //*****************************************************************************
7411 NS_IMETHODIMP
7412 nsDocShell::EnsureContentViewer()
7414 if (mContentViewer)
7415 return NS_OK;
7416 if (mIsBeingDestroyed)
7417 return NS_ERROR_FAILURE;
7419 nsCOMPtr<nsIURI> baseURI;
7420 nsIPrincipal* principal = GetInheritedPrincipal(false);
7421 nsCOMPtr<nsIDocShellTreeItem> parentItem;
7422 GetSameTypeParent(getter_AddRefs(parentItem));
7423 if (parentItem) {
7424 nsCOMPtr<nsPIDOMWindow> domWin = GetWindow();
7425 if (domWin) {
7426 nsCOMPtr<Element> parentElement =
7427 domWin->GetFrameElementInternal();
7428 if (parentElement) {
7429 baseURI = parentElement->GetBaseURI();
7434 nsresult rv = CreateAboutBlankContentViewer(principal, baseURI);
7436 NS_ENSURE_STATE(mContentViewer);
7438 if (NS_SUCCEEDED(rv)) {
7439 nsCOMPtr<nsIDocument> doc(GetDocument());
7440 NS_ASSERTION(doc,
7441 "Should have doc if CreateAboutBlankContentViewer "
7442 "succeeded!");
7444 doc->SetIsInitialDocument(true);
7447 return rv;
7450 nsresult
7451 nsDocShell::CreateAboutBlankContentViewer(nsIPrincipal* aPrincipal,
7452 nsIURI* aBaseURI,
7453 bool aTryToSaveOldPresentation)
7455 nsCOMPtr<nsIDocument> blankDoc;
7456 nsCOMPtr<nsIContentViewer> viewer;
7457 nsresult rv = NS_ERROR_FAILURE;
7459 /* mCreatingDocument should never be true at this point. However, it's
7460 a theoretical possibility. We want to know about it and make it stop,
7461 and this sounds like a job for an assertion. */
7462 NS_ASSERTION(!mCreatingDocument, "infinite(?) loop creating document averted");
7463 if (mCreatingDocument)
7464 return NS_ERROR_FAILURE;
7466 AutoRestore<bool> creatingDocument(mCreatingDocument);
7467 mCreatingDocument = true;
7469 // mContentViewer->PermitUnload may release |this| docshell.
7470 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
7472 // Make sure timing is created. But first record whether we had it
7473 // already, so we don't clobber the timing for an in-progress load.
7474 bool hadTiming = mTiming;
7475 MaybeInitTiming();
7476 if (mContentViewer) {
7477 // We've got a content viewer already. Make sure the user
7478 // permits us to discard the current document and replace it
7479 // with about:blank. And also ensure we fire the unload events
7480 // in the current document.
7482 // Unload gets fired first for
7483 // document loaded from the session history.
7484 mTiming->NotifyBeforeUnload();
7486 bool okToUnload;
7487 rv = mContentViewer->PermitUnload(false, &okToUnload);
7489 if (NS_SUCCEEDED(rv) && !okToUnload) {
7490 // The user chose not to unload the page, interrupt the load.
7491 return NS_ERROR_FAILURE;
7494 mSavingOldViewer = aTryToSaveOldPresentation &&
7495 CanSavePresentation(LOAD_NORMAL, nullptr, nullptr);
7497 if (mTiming) {
7498 mTiming->NotifyUnloadAccepted(mCurrentURI);
7501 // Make sure to blow away our mLoadingURI just in case. No loads
7502 // from inside this pagehide.
7503 mLoadingURI = nullptr;
7505 // Stop any in-progress loading, so that we don't accidentally trigger any
7506 // PageShow notifications from Embed() interrupting our loading below.
7507 Stop();
7509 // Notify the current document that it is about to be unloaded!!
7511 // It is important to fire the unload() notification *before* any state
7512 // is changed within the DocShell - otherwise, javascript will get the
7513 // wrong information :-(
7515 (void) FirePageHideNotification(!mSavingOldViewer);
7518 // Now make sure we don't think we're in the middle of firing unload after
7519 // this point. This will make us fire unload when the about:blank document
7520 // unloads... but that's ok, more or less. Would be nice if it fired load
7521 // too, of course.
7522 mFiredUnloadEvent = false;
7524 nsCOMPtr<nsIDocumentLoaderFactory> docFactory =
7525 nsContentUtils::FindInternalContentViewer("text/html");
7527 if (docFactory) {
7528 nsCOMPtr<nsIPrincipal> principal;
7529 if (mSandboxFlags & SANDBOXED_ORIGIN) {
7530 principal = nsNullPrincipal::CreateWithInheritedAttributes(aPrincipal);
7531 NS_ENSURE_TRUE(principal, NS_ERROR_FAILURE);
7532 } else {
7533 principal = aPrincipal;
7535 // generate (about:blank) document to load
7536 docFactory->CreateBlankDocument(mLoadGroup, principal,
7537 getter_AddRefs(blankDoc));
7538 if (blankDoc) {
7539 // Hack: set the base URI manually, since this document never
7540 // got Reset() with a channel.
7541 blankDoc->SetBaseURI(aBaseURI);
7543 blankDoc->SetContainer(this);
7545 // Copy our sandbox flags to the document. These are immutable
7546 // after being set here.
7547 blankDoc->SetSandboxFlags(mSandboxFlags);
7549 // create a content viewer for us and the new document
7550 docFactory->CreateInstanceForDocument(NS_ISUPPORTS_CAST(nsIDocShell *, this),
7551 blankDoc, "view", getter_AddRefs(viewer));
7553 // hook 'em up
7554 if (viewer) {
7555 viewer->SetContainer(this);
7556 rv = Embed(viewer, "", 0);
7557 NS_ENSURE_SUCCESS(rv, rv);
7559 SetCurrentURI(blankDoc->GetDocumentURI(), nullptr, true, 0);
7560 rv = mIsBeingDestroyed ? NS_ERROR_NOT_AVAILABLE : NS_OK;
7565 // The transient about:blank viewer doesn't have a session history entry.
7566 SetHistoryEntry(&mOSHE, nullptr);
7568 // Clear out our mTiming like we would in EndPageLoad, if we didn't
7569 // have one before entering this function.
7570 if (!hadTiming) {
7571 mTiming = nullptr;
7574 return rv;
7577 NS_IMETHODIMP
7578 nsDocShell::CreateAboutBlankContentViewer(nsIPrincipal *aPrincipal)
7580 return CreateAboutBlankContentViewer(aPrincipal, nullptr);
7583 bool
7584 nsDocShell::CanSavePresentation(uint32_t aLoadType,
7585 nsIRequest *aNewRequest,
7586 nsIDocument *aNewDocument)
7588 if (!mOSHE)
7589 return false; // no entry to save into
7591 nsCOMPtr<nsIContentViewer> viewer;
7592 mOSHE->GetContentViewer(getter_AddRefs(viewer));
7593 if (viewer) {
7594 NS_WARNING("mOSHE already has a content viewer!");
7595 return false;
7598 // Only save presentation for "normal" loads and link loads. Anything else
7599 // probably wants to refetch the page, so caching the old presentation
7600 // would be incorrect.
7601 if (aLoadType != LOAD_NORMAL &&
7602 aLoadType != LOAD_HISTORY &&
7603 aLoadType != LOAD_LINK &&
7604 aLoadType != LOAD_STOP_CONTENT &&
7605 aLoadType != LOAD_STOP_CONTENT_AND_REPLACE &&
7606 aLoadType != LOAD_ERROR_PAGE)
7607 return false;
7609 // If the session history entry has the saveLayoutState flag set to false,
7610 // then we should not cache the presentation.
7611 bool canSaveState;
7612 mOSHE->GetSaveLayoutStateFlag(&canSaveState);
7613 if (!canSaveState)
7614 return false;
7616 // If the document is not done loading, don't cache it.
7617 if (!mScriptGlobal || mScriptGlobal->IsLoading())
7618 return false;
7620 if (mScriptGlobal->WouldReuseInnerWindow(aNewDocument))
7621 return false;
7623 // Avoid doing the work of saving the presentation state in the case where
7624 // the content viewer cache is disabled.
7625 if (nsSHistory::GetMaxTotalViewers() == 0)
7626 return false;
7628 // Don't cache the content viewer if we're in a subframe and the subframe
7629 // pref is disabled.
7630 bool cacheFrames =
7631 Preferences::GetBool("browser.sessionhistory.cache_subframes",
7632 false);
7633 if (!cacheFrames) {
7634 nsCOMPtr<nsIDocShellTreeItem> root;
7635 GetSameTypeParent(getter_AddRefs(root));
7636 if (root && root != this) {
7637 return false; // this is a subframe load
7641 // If the document does not want its presentation cached, then don't.
7642 nsCOMPtr<nsIDocument> doc = mScriptGlobal->GetExtantDoc();
7643 return doc && doc->CanSavePresentation(aNewRequest);
7646 void
7647 nsDocShell::ReattachEditorToWindow(nsISHEntry *aSHEntry)
7649 NS_ASSERTION(!mEditorData,
7650 "Why reattach an editor when we already have one?");
7651 NS_ASSERTION(aSHEntry && aSHEntry->HasDetachedEditor(),
7652 "Reattaching when there's not a detached editor.");
7654 if (mEditorData || !aSHEntry)
7655 return;
7657 mEditorData = aSHEntry->ForgetEditorData();
7658 if (mEditorData) {
7659 #ifdef DEBUG
7660 nsresult rv =
7661 #endif
7662 mEditorData->ReattachToWindow(this);
7663 NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to reattach editing session");
7667 void
7668 nsDocShell::DetachEditorFromWindow()
7670 if (!mEditorData || mEditorData->WaitingForLoad()) {
7671 // If there's nothing to detach, or if the editor data is actually set
7672 // up for the _new_ page that's coming in, don't detach.
7673 return;
7676 NS_ASSERTION(!mOSHE || !mOSHE->HasDetachedEditor(),
7677 "Detaching editor when it's already detached.");
7679 nsresult res = mEditorData->DetachFromWindow();
7680 NS_ASSERTION(NS_SUCCEEDED(res), "Failed to detach editor");
7682 if (NS_SUCCEEDED(res)) {
7683 // Make mOSHE hold the owning ref to the editor data.
7684 if (mOSHE)
7685 mOSHE->SetEditorData(mEditorData.forget());
7686 else
7687 mEditorData = nullptr;
7690 #ifdef DEBUG
7692 bool isEditable;
7693 GetEditable(&isEditable);
7694 NS_ASSERTION(!isEditable,
7695 "Window is still editable after detaching editor.");
7697 #endif // DEBUG
7700 nsresult
7701 nsDocShell::CaptureState()
7703 if (!mOSHE || mOSHE == mLSHE) {
7704 // No entry to save into, or we're replacing the existing entry.
7705 return NS_ERROR_FAILURE;
7708 if (!mScriptGlobal)
7709 return NS_ERROR_FAILURE;
7711 nsCOMPtr<nsISupports> windowState = mScriptGlobal->SaveWindowState();
7712 NS_ENSURE_TRUE(windowState, NS_ERROR_FAILURE);
7714 #ifdef DEBUG_PAGE_CACHE
7715 nsCOMPtr<nsIURI> uri;
7716 mOSHE->GetURI(getter_AddRefs(uri));
7717 nsAutoCString spec;
7718 if (uri)
7719 uri->GetSpec(spec);
7720 printf("Saving presentation into session history\n");
7721 printf(" SH URI: %s\n", spec.get());
7722 #endif
7724 nsresult rv = mOSHE->SetWindowState(windowState);
7725 NS_ENSURE_SUCCESS(rv, rv);
7727 // Suspend refresh URIs and save off the timer queue
7728 rv = mOSHE->SetRefreshURIList(mSavedRefreshURIList);
7729 NS_ENSURE_SUCCESS(rv, rv);
7731 // Capture the current content viewer bounds.
7732 if (mContentViewer) {
7733 nsIntRect bounds;
7734 mContentViewer->GetBounds(bounds);
7735 rv = mOSHE->SetViewerBounds(bounds);
7736 NS_ENSURE_SUCCESS(rv, rv);
7739 // Capture the docshell hierarchy.
7740 mOSHE->ClearChildShells();
7742 uint32_t childCount = mChildList.Length();
7743 for (uint32_t i = 0; i < childCount; ++i) {
7744 nsCOMPtr<nsIDocShellTreeItem> childShell = do_QueryInterface(ChildAt(i));
7745 NS_ASSERTION(childShell, "null child shell");
7747 mOSHE->AddChildShell(childShell);
7750 return NS_OK;
7753 NS_IMETHODIMP
7754 nsDocShell::RestorePresentationEvent::Run()
7756 if (mDocShell && NS_FAILED(mDocShell->RestoreFromHistory()))
7757 NS_WARNING("RestoreFromHistory failed");
7758 return NS_OK;
7761 NS_IMETHODIMP
7762 nsDocShell::BeginRestore(nsIContentViewer *aContentViewer, bool aTop)
7764 nsresult rv;
7765 if (!aContentViewer) {
7766 rv = EnsureContentViewer();
7767 NS_ENSURE_SUCCESS(rv, rv);
7769 aContentViewer = mContentViewer;
7772 // Dispatch events for restoring the presentation. We try to simulate
7773 // the progress notifications loading the document would cause, so we add
7774 // the document's channel to the loadgroup to initiate stateChange
7775 // notifications.
7777 nsCOMPtr<nsIDOMDocument> domDoc;
7778 aContentViewer->GetDOMDocument(getter_AddRefs(domDoc));
7779 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
7780 if (doc) {
7781 nsIChannel *channel = doc->GetChannel();
7782 if (channel) {
7783 mEODForCurrentDocument = false;
7784 mIsRestoringDocument = true;
7785 mLoadGroup->AddRequest(channel, nullptr);
7786 mIsRestoringDocument = false;
7790 if (!aTop) {
7791 // This point corresponds to us having gotten OnStartRequest or
7792 // STATE_START, so do the same thing that CreateContentViewer does at
7793 // this point to ensure that unload/pagehide events for this document
7794 // will fire when it's unloaded again.
7795 mFiredUnloadEvent = false;
7797 // For non-top frames, there is no notion of making sure that the
7798 // previous document is in the domwindow when STATE_START notifications
7799 // happen. We can just call BeginRestore for all of the child shells
7800 // now.
7801 rv = BeginRestoreChildren();
7802 NS_ENSURE_SUCCESS(rv, rv);
7805 return NS_OK;
7808 nsresult
7809 nsDocShell::BeginRestoreChildren()
7811 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
7812 while (iter.HasMore()) {
7813 nsCOMPtr<nsIDocShell> child = do_QueryObject(iter.GetNext());
7814 if (child) {
7815 nsresult rv = child->BeginRestore(nullptr, false);
7816 NS_ENSURE_SUCCESS(rv, rv);
7819 return NS_OK;
7822 NS_IMETHODIMP
7823 nsDocShell::FinishRestore()
7825 // First we call finishRestore() on our children. In the simulated load,
7826 // all of the child frames finish loading before the main document.
7828 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
7829 while (iter.HasMore()) {
7830 nsCOMPtr<nsIDocShell> child = do_QueryObject(iter.GetNext());
7831 if (child) {
7832 child->FinishRestore();
7836 if (mOSHE && mOSHE->HasDetachedEditor()) {
7837 ReattachEditorToWindow(mOSHE);
7840 nsCOMPtr<nsIDocument> doc = GetDocument();
7841 if (doc) {
7842 // Finally, we remove the request from the loadgroup. This will
7843 // cause onStateChange(STATE_STOP) to fire, which will fire the
7844 // pageshow event to the chrome.
7846 nsIChannel *channel = doc->GetChannel();
7847 if (channel) {
7848 mIsRestoringDocument = true;
7849 mLoadGroup->RemoveRequest(channel, nullptr, NS_OK);
7850 mIsRestoringDocument = false;
7854 return NS_OK;
7857 NS_IMETHODIMP
7858 nsDocShell::GetRestoringDocument(bool *aRestoring)
7860 *aRestoring = mIsRestoringDocument;
7861 return NS_OK;
7864 nsresult
7865 nsDocShell::RestorePresentation(nsISHEntry *aSHEntry, bool *aRestoring)
7867 NS_ASSERTION(mLoadType & LOAD_CMD_HISTORY,
7868 "RestorePresentation should only be called for history loads");
7870 nsCOMPtr<nsIContentViewer> viewer;
7871 aSHEntry->GetContentViewer(getter_AddRefs(viewer));
7873 #ifdef DEBUG_PAGE_CACHE
7874 nsCOMPtr<nsIURI> uri;
7875 aSHEntry->GetURI(getter_AddRefs(uri));
7877 nsAutoCString spec;
7878 if (uri)
7879 uri->GetSpec(spec);
7880 #endif
7882 *aRestoring = false;
7884 if (!viewer) {
7885 #ifdef DEBUG_PAGE_CACHE
7886 printf("no saved presentation for uri: %s\n", spec.get());
7887 #endif
7888 return NS_OK;
7891 // We need to make sure the content viewer's container is this docshell.
7892 // In subframe navigation, it's possible for the docshell that the
7893 // content viewer was originally loaded into to be replaced with a
7894 // different one. We don't currently support restoring the presentation
7895 // in that case.
7897 nsCOMPtr<nsIDocShell> container;
7898 viewer->GetContainer(getter_AddRefs(container));
7899 if (!::SameCOMIdentity(container, GetAsSupports(this))) {
7900 #ifdef DEBUG_PAGE_CACHE
7901 printf("No valid container, clearing presentation\n");
7902 #endif
7903 aSHEntry->SetContentViewer(nullptr);
7904 return NS_ERROR_FAILURE;
7907 NS_ASSERTION(mContentViewer != viewer, "Restoring existing presentation");
7909 #ifdef DEBUG_PAGE_CACHE
7910 printf("restoring presentation from session history: %s\n", spec.get());
7911 #endif
7913 SetHistoryEntry(&mLSHE, aSHEntry);
7915 // Add the request to our load group. We do this before swapping out
7916 // the content viewers so that consumers of STATE_START can access
7917 // the old document. We only deal with the toplevel load at this time --
7918 // to be consistent with normal document loading, subframes cannot start
7919 // loading until after data arrives, which is after STATE_START completes.
7921 BeginRestore(viewer, true);
7923 // Post an event that will remove the request after we've returned
7924 // to the event loop. This mimics the way it is called by nsIChannel
7925 // implementations.
7927 // Revoke any pending restore (just in case)
7928 NS_ASSERTION(!mRestorePresentationEvent.IsPending(),
7929 "should only have one RestorePresentationEvent");
7930 mRestorePresentationEvent.Revoke();
7932 nsRefPtr<RestorePresentationEvent> evt = new RestorePresentationEvent(this);
7933 nsresult rv = NS_DispatchToCurrentThread(evt);
7934 if (NS_SUCCEEDED(rv)) {
7935 mRestorePresentationEvent = evt.get();
7936 // The rest of the restore processing will happen on our event
7937 // callback.
7938 *aRestoring = true;
7941 return rv;
7944 nsresult
7945 nsDocShell::RestoreFromHistory()
7947 mRestorePresentationEvent.Forget();
7949 // This section of code follows the same ordering as CreateContentViewer.
7950 if (!mLSHE)
7951 return NS_ERROR_FAILURE;
7953 nsCOMPtr<nsIContentViewer> viewer;
7954 mLSHE->GetContentViewer(getter_AddRefs(viewer));
7955 if (!viewer)
7956 return NS_ERROR_FAILURE;
7958 if (mSavingOldViewer) {
7959 // We determined that it was safe to cache the document presentation
7960 // at the time we initiated the new load. We need to check whether
7961 // it's still safe to do so, since there may have been DOM mutations
7962 // or new requests initiated.
7963 nsCOMPtr<nsIDOMDocument> domDoc;
7964 viewer->GetDOMDocument(getter_AddRefs(domDoc));
7965 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
7966 nsIRequest *request = nullptr;
7967 if (doc)
7968 request = doc->GetChannel();
7969 mSavingOldViewer = CanSavePresentation(mLoadType, request, doc);
7972 nsCOMPtr<nsIContentViewer> oldCv(mContentViewer);
7973 nsCOMPtr<nsIContentViewer> newCv(viewer);
7974 int32_t minFontSize = 0;
7975 float textZoom = 1.0f;
7976 float pageZoom = 1.0f;
7977 bool styleDisabled = false;
7978 if (oldCv && newCv) {
7979 oldCv->GetMinFontSize(&minFontSize);
7980 oldCv->GetTextZoom(&textZoom);
7981 oldCv->GetFullZoom(&pageZoom);
7982 oldCv->GetAuthorStyleDisabled(&styleDisabled);
7985 // Protect against mLSHE going away via a load triggered from
7986 // pagehide or unload.
7987 nsCOMPtr<nsISHEntry> origLSHE = mLSHE;
7989 // Make sure to blow away our mLoadingURI just in case. No loads
7990 // from inside this pagehide.
7991 mLoadingURI = nullptr;
7993 // Notify the old content viewer that it's being hidden.
7994 FirePageHideNotification(!mSavingOldViewer);
7996 // If mLSHE was changed as a result of the pagehide event, then
7997 // something else was loaded. Don't finish restoring.
7998 if (mLSHE != origLSHE)
7999 return NS_OK;
8001 // Set mFiredUnloadEvent = false so that the unload handler for the
8002 // *new* document will fire.
8003 mFiredUnloadEvent = false;
8005 mURIResultedInDocument = true;
8006 nsCOMPtr<nsISHistory> rootSH;
8007 GetRootSessionHistory(getter_AddRefs(rootSH));
8008 if (rootSH) {
8009 nsCOMPtr<nsISHistoryInternal> hist = do_QueryInterface(rootSH);
8010 rootSH->GetIndex(&mPreviousTransIndex);
8011 hist->UpdateIndex();
8012 rootSH->GetIndex(&mLoadedTransIndex);
8013 #ifdef DEBUG_PAGE_CACHE
8014 printf("Previous index: %d, Loaded index: %d\n\n", mPreviousTransIndex,
8015 mLoadedTransIndex);
8016 #endif
8019 // Rather than call Embed(), we will retrieve the viewer from the session
8020 // history entry and swap it in.
8021 // XXX can we refactor this so that we can just call Embed()?
8022 PersistLayoutHistoryState();
8023 nsresult rv;
8024 if (mContentViewer) {
8025 if (mSavingOldViewer && NS_FAILED(CaptureState())) {
8026 if (mOSHE) {
8027 mOSHE->SyncPresentationState();
8029 mSavingOldViewer = false;
8033 mSavedRefreshURIList = nullptr;
8035 // In cases where we use a transient about:blank viewer between loads,
8036 // we never show the transient viewer, so _its_ previous viewer is never
8037 // unhooked from the view hierarchy. Destroy any such previous viewer now,
8038 // before we grab the root view sibling, so that we don't grab a view
8039 // that's about to go away.
8041 if (mContentViewer) {
8042 nsCOMPtr<nsIContentViewer> previousViewer;
8043 mContentViewer->GetPreviousViewer(getter_AddRefs(previousViewer));
8044 if (previousViewer) {
8045 mContentViewer->SetPreviousViewer(nullptr);
8046 previousViewer->Destroy();
8050 // Save off the root view's parent and sibling so that we can insert the
8051 // new content viewer's root view at the same position. Also save the
8052 // bounds of the root view's widget.
8054 nsView *rootViewSibling = nullptr, *rootViewParent = nullptr;
8055 nsIntRect newBounds(0, 0, 0, 0);
8057 nsCOMPtr<nsIPresShell> oldPresShell = GetPresShell();
8058 if (oldPresShell) {
8059 nsViewManager *vm = oldPresShell->GetViewManager();
8060 if (vm) {
8061 nsView *oldRootView = vm->GetRootView();
8063 if (oldRootView) {
8064 rootViewSibling = oldRootView->GetNextSibling();
8065 rootViewParent = oldRootView->GetParent();
8067 mContentViewer->GetBounds(newBounds);
8072 nsCOMPtr<nsIContent> container;
8073 nsCOMPtr<nsIDocument> sibling;
8074 if (rootViewParent && rootViewParent->GetParent()) {
8075 nsIFrame* frame = rootViewParent->GetParent()->GetFrame();
8076 container = frame ? frame->GetContent() : nullptr;
8078 if (rootViewSibling) {
8079 nsIFrame *frame = rootViewSibling->GetFrame();
8080 sibling = frame ? frame->PresContext()->PresShell()->GetDocument() : nullptr;
8083 // Transfer ownership to mContentViewer. By ensuring that either the
8084 // docshell or the session history, but not both, have references to the
8085 // content viewer, we prevent the viewer from being torn down after
8086 // Destroy() is called.
8088 if (mContentViewer) {
8089 mContentViewer->Close(mSavingOldViewer ? mOSHE.get() : nullptr);
8090 viewer->SetPreviousViewer(mContentViewer);
8092 if (mOSHE && (!mContentViewer || !mSavingOldViewer)) {
8093 // We don't plan to save a viewer in mOSHE; tell it to drop
8094 // any other state it's holding.
8095 mOSHE->SyncPresentationState();
8098 // Order the mContentViewer setup just like Embed does.
8099 mContentViewer = nullptr;
8101 // Now that we're about to switch documents, forget all of our children.
8102 // Note that we cached them as needed up in CaptureState above.
8103 DestroyChildren();
8105 mContentViewer.swap(viewer);
8107 // Grab all of the related presentation from the SHEntry now.
8108 // Clearing the viewer from the SHEntry will clear all of this state.
8109 nsCOMPtr<nsISupports> windowState;
8110 mLSHE->GetWindowState(getter_AddRefs(windowState));
8111 mLSHE->SetWindowState(nullptr);
8113 bool sticky;
8114 mLSHE->GetSticky(&sticky);
8116 nsCOMPtr<nsIDOMDocument> domDoc;
8117 mContentViewer->GetDOMDocument(getter_AddRefs(domDoc));
8119 nsCOMArray<nsIDocShellTreeItem> childShells;
8120 int32_t i = 0;
8121 nsCOMPtr<nsIDocShellTreeItem> child;
8122 while (NS_SUCCEEDED(mLSHE->ChildShellAt(i++, getter_AddRefs(child))) &&
8123 child) {
8124 childShells.AppendObject(child);
8127 // get the previous content viewer size
8128 nsIntRect oldBounds(0, 0, 0, 0);
8129 mLSHE->GetViewerBounds(oldBounds);
8131 // Restore the refresh URI list. The refresh timers will be restarted
8132 // when EndPageLoad() is called.
8133 nsCOMPtr<nsISupportsArray> refreshURIList;
8134 mLSHE->GetRefreshURIList(getter_AddRefs(refreshURIList));
8136 // Reattach to the window object.
8137 mIsRestoringDocument = true; // for MediaDocument::BecomeInteractive
8138 rv = mContentViewer->Open(windowState, mLSHE);
8139 mIsRestoringDocument = false;
8141 // Hack to keep nsDocShellEditorData alive across the
8142 // SetContentViewer(nullptr) call below.
8143 nsAutoPtr<nsDocShellEditorData> data(mLSHE->ForgetEditorData());
8145 // Now remove it from the cached presentation.
8146 mLSHE->SetContentViewer(nullptr);
8147 mEODForCurrentDocument = false;
8149 mLSHE->SetEditorData(data.forget());
8151 #ifdef DEBUG
8153 nsCOMPtr<nsISupportsArray> refreshURIs;
8154 mLSHE->GetRefreshURIList(getter_AddRefs(refreshURIs));
8155 nsCOMPtr<nsIDocShellTreeItem> childShell;
8156 mLSHE->ChildShellAt(0, getter_AddRefs(childShell));
8157 NS_ASSERTION(!refreshURIs && !childShell,
8158 "SHEntry should have cleared presentation state");
8160 #endif
8162 // Restore the sticky state of the viewer. The viewer has set this state
8163 // on the history entry in Destroy() just before marking itself non-sticky,
8164 // to avoid teardown of the presentation.
8165 mContentViewer->SetSticky(sticky);
8167 NS_ENSURE_SUCCESS(rv, rv);
8169 // mLSHE is now our currently-loaded document.
8170 SetHistoryEntry(&mOSHE, mLSHE);
8172 // XXX special wyciwyg handling in Embed()?
8174 // We aren't going to restore any items from the LayoutHistoryState,
8175 // but we don't want them to stay around in case the page is reloaded.
8176 SetLayoutHistoryState(nullptr);
8178 // This is the end of our Embed() replacement
8180 mSavingOldViewer = false;
8181 mEODForCurrentDocument = false;
8183 // Tell the event loop to favor plevents over user events, see comments
8184 // in CreateContentViewer.
8185 if (++gNumberOfDocumentsLoading == 1)
8186 FavorPerformanceHint(true);
8189 if (oldCv && newCv) {
8190 newCv->SetMinFontSize(minFontSize);
8191 newCv->SetTextZoom(textZoom);
8192 newCv->SetFullZoom(pageZoom);
8193 newCv->SetAuthorStyleDisabled(styleDisabled);
8196 nsCOMPtr<nsIDocument> document = do_QueryInterface(domDoc);
8197 uint32_t parentSuspendCount = 0;
8198 if (document) {
8199 nsRefPtr<nsDocShell> parent = GetParentDocshell();
8200 if (parent) {
8201 nsCOMPtr<nsIDocument> d = parent->GetDocument();
8202 if (d) {
8203 if (d->EventHandlingSuppressed()) {
8204 document->SuppressEventHandling(nsIDocument::eEvents,
8205 d->EventHandlingSuppressed());
8208 // Ick, it'd be nicer to not rewalk all of the subdocs here.
8209 if (d->AnimationsPaused()) {
8210 document->SuppressEventHandling(nsIDocument::eAnimationsOnly,
8211 d->AnimationsPaused());
8214 nsCOMPtr<nsPIDOMWindow> parentWindow = d->GetWindow();
8215 if (parentWindow) {
8216 parentSuspendCount = parentWindow->TimeoutSuspendCount();
8221 // Use the uri from the mLSHE we had when we entered this function
8222 // (which need not match the document's URI if anchors are involved),
8223 // since that's the history entry we're loading. Note that if we use
8224 // origLSHE we don't have to worry about whether the entry in question
8225 // is still mLSHE or whether it's now mOSHE.
8226 nsCOMPtr<nsIURI> uri;
8227 origLSHE->GetURI(getter_AddRefs(uri));
8228 SetCurrentURI(uri, document->GetChannel(), true, 0);
8231 // This is the end of our CreateContentViewer() replacement.
8232 // Now we simulate a load. First, we restore the state of the javascript
8233 // window object.
8234 nsCOMPtr<nsPIDOMWindow> privWin = GetWindow();
8235 NS_ASSERTION(privWin, "could not get nsPIDOMWindow interface");
8237 rv = privWin->RestoreWindowState(windowState);
8238 NS_ENSURE_SUCCESS(rv, rv);
8240 // Now, dispatch a title change event which would happen as the
8241 // <head> is parsed.
8242 document->NotifyPossibleTitleChange(false);
8244 // Now we simulate appending child docshells for subframes.
8245 for (i = 0; i < childShells.Count(); ++i) {
8246 nsIDocShellTreeItem *childItem = childShells.ObjectAt(i);
8247 nsCOMPtr<nsIDocShell> childShell = do_QueryInterface(childItem);
8249 // Make sure to not clobber the state of the child. Since AddChild
8250 // always clobbers it, save it off first.
8251 bool allowPlugins;
8252 childShell->GetAllowPlugins(&allowPlugins);
8254 bool allowJavascript;
8255 childShell->GetAllowJavascript(&allowJavascript);
8257 bool allowRedirects;
8258 childShell->GetAllowMetaRedirects(&allowRedirects);
8260 bool allowSubframes;
8261 childShell->GetAllowSubframes(&allowSubframes);
8263 bool allowImages;
8264 childShell->GetAllowImages(&allowImages);
8266 bool allowMedia = childShell->GetAllowMedia();
8268 bool allowDNSPrefetch;
8269 childShell->GetAllowDNSPrefetch(&allowDNSPrefetch);
8271 bool allowContentRetargeting = childShell->GetAllowContentRetargeting();
8273 uint32_t defaultLoadFlags;
8274 childShell->GetDefaultLoadFlags(&defaultLoadFlags);
8276 // this.AddChild(child) calls child.SetDocLoaderParent(this), meaning
8277 // that the child inherits our state. Among other things, this means
8278 // that the child inherits our mIsActive and mInPrivateBrowsing, which
8279 // is what we want.
8280 AddChild(childItem);
8282 childShell->SetAllowPlugins(allowPlugins);
8283 childShell->SetAllowJavascript(allowJavascript);
8284 childShell->SetAllowMetaRedirects(allowRedirects);
8285 childShell->SetAllowSubframes(allowSubframes);
8286 childShell->SetAllowImages(allowImages);
8287 childShell->SetAllowMedia(allowMedia);
8288 childShell->SetAllowDNSPrefetch(allowDNSPrefetch);
8289 childShell->SetAllowContentRetargeting(allowContentRetargeting);
8290 childShell->SetDefaultLoadFlags(defaultLoadFlags);
8292 rv = childShell->BeginRestore(nullptr, false);
8293 NS_ENSURE_SUCCESS(rv, rv);
8296 nsCOMPtr<nsIPresShell> shell = GetPresShell();
8298 // We may be displayed on a different monitor (or in a different
8299 // HiDPI mode) than when we got into the history list. So we need
8300 // to check if this has happened. See bug 838239.
8302 // Because the prescontext normally handles resolution changes via
8303 // a runnable (see nsPresContext::UIResolutionChanged), its device
8304 // context won't be -immediately- updated as a result of calling
8305 // shell->BackingScaleFactorChanged().
8307 // But we depend on that device context when adjusting the view size
8308 // via mContentViewer->SetBounds(newBounds) below. So we need to
8309 // explicitly tell it to check for changed resolution here.
8310 if (shell && shell->GetPresContext()->DeviceContext()->CheckDPIChange()) {
8311 shell->BackingScaleFactorChanged();
8314 nsViewManager *newVM = shell ? shell->GetViewManager() : nullptr;
8315 nsView *newRootView = newVM ? newVM->GetRootView() : nullptr;
8317 // Insert the new root view at the correct location in the view tree.
8318 if (container) {
8319 nsSubDocumentFrame* subDocFrame = do_QueryFrame(container->GetPrimaryFrame());
8320 rootViewParent = subDocFrame ? subDocFrame->EnsureInnerView() : nullptr;
8322 if (sibling &&
8323 sibling->GetShell() &&
8324 sibling->GetShell()->GetViewManager()) {
8325 rootViewSibling = sibling->GetShell()->GetViewManager()->GetRootView();
8326 } else {
8327 rootViewSibling = nullptr;
8329 if (rootViewParent && newRootView && newRootView->GetParent() != rootViewParent) {
8330 nsViewManager *parentVM = rootViewParent->GetViewManager();
8331 if (parentVM) {
8332 // InsertChild(parent, child, sib, true) inserts the child after
8333 // sib in content order, which is before sib in view order. BUT
8334 // when sib is null it inserts at the end of the the document
8335 // order, i.e., first in view order. But when oldRootSibling is
8336 // null, the old root as at the end of the view list --- last in
8337 // content order --- and we want to call InsertChild(parent, child,
8338 // nullptr, false) in that case.
8339 parentVM->InsertChild(rootViewParent, newRootView,
8340 rootViewSibling,
8341 rootViewSibling ? true : false);
8343 NS_ASSERTION(newRootView->GetNextSibling() == rootViewSibling,
8344 "error in InsertChild");
8348 // If parent is suspended, increase suspension count.
8349 // This can't be done as early as event suppression since this
8350 // depends on docshell tree.
8351 if (parentSuspendCount) {
8352 privWin->SuspendTimeouts(parentSuspendCount, false);
8355 // Now that all of the child docshells have been put into place, we can
8356 // restart the timers for the window and all of the child frames.
8357 privWin->ResumeTimeouts();
8359 // Restore the refresh URI list. The refresh timers will be restarted
8360 // when EndPageLoad() is called.
8361 mRefreshURIList = refreshURIList;
8363 // Meta-refresh timers have been restarted for this shell, but not
8364 // for our children. Walk the child shells and restart their timers.
8365 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
8366 while (iter.HasMore()) {
8367 nsCOMPtr<nsIDocShell> child = do_QueryObject(iter.GetNext());
8368 if (child)
8369 child->ResumeRefreshURIs();
8372 // Make sure this presentation is the same size as the previous
8373 // presentation. If this is not the same size we showed it at last time,
8374 // then we need to resize the widget.
8376 // XXXbryner This interacts poorly with Firefox's infobar. If the old
8377 // presentation had the infobar visible, then we will resize the new
8378 // presentation to that smaller size. However, firing the locationchanged
8379 // event will hide the infobar, which will immediately resize the window
8380 // back to the larger size. A future optimization might be to restore
8381 // the presentation at the "wrong" size, then fire the locationchanged
8382 // event and check whether the docshell's new size is the same as the
8383 // cached viewer size (skipping the resize if they are equal).
8385 if (newRootView) {
8386 if (!newBounds.IsEmpty() && !newBounds.IsEqualEdges(oldBounds)) {
8387 #ifdef DEBUG_PAGE_CACHE
8388 printf("resize widget(%d, %d, %d, %d)\n", newBounds.x,
8389 newBounds.y, newBounds.width, newBounds.height);
8390 #endif
8391 mContentViewer->SetBounds(newBounds);
8392 } else {
8393 nsIScrollableFrame *rootScrollFrame =
8394 shell->GetRootScrollFrameAsScrollableExternal();
8395 if (rootScrollFrame) {
8396 rootScrollFrame->PostScrolledAreaEventForCurrentArea();
8401 // The FinishRestore call below can kill these, null them out so we don't
8402 // have invalid pointer lying around.
8403 newRootView = rootViewSibling = rootViewParent = nullptr;
8404 newVM = nullptr;
8406 // Simulate the completion of the load.
8407 nsDocShell::FinishRestore();
8409 // Restart plugins, and paint the content.
8410 if (shell) {
8411 shell->Thaw();
8414 return privWin->FireDelayedDOMEvents();
8417 NS_IMETHODIMP
8418 nsDocShell::CreateContentViewer(const char *aContentType,
8419 nsIRequest * request,
8420 nsIStreamListener ** aContentHandler)
8422 *aContentHandler = nullptr;
8424 // Can we check the content type of the current content viewer
8425 // and reuse it without destroying it and re-creating it?
8427 NS_ASSERTION(mLoadGroup, "Someone ignored return from Init()?");
8429 // Instantiate the content viewer object
8430 nsCOMPtr<nsIContentViewer> viewer;
8431 nsresult rv = NewContentViewerObj(aContentType, request, mLoadGroup,
8432 aContentHandler, getter_AddRefs(viewer));
8434 if (NS_FAILED(rv))
8435 return rv;
8437 // Notify the current document that it is about to be unloaded!!
8439 // It is important to fire the unload() notification *before* any state
8440 // is changed within the DocShell - otherwise, javascript will get the
8441 // wrong information :-(
8444 if (mSavingOldViewer) {
8445 // We determined that it was safe to cache the document presentation
8446 // at the time we initiated the new load. We need to check whether
8447 // it's still safe to do so, since there may have been DOM mutations
8448 // or new requests initiated.
8449 nsCOMPtr<nsIDOMDocument> domDoc;
8450 viewer->GetDOMDocument(getter_AddRefs(domDoc));
8451 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
8452 mSavingOldViewer = CanSavePresentation(mLoadType, request, doc);
8455 NS_ASSERTION(!mLoadingURI, "Re-entering unload?");
8457 nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(request);
8458 if (aOpenedChannel) {
8459 aOpenedChannel->GetURI(getter_AddRefs(mLoadingURI));
8461 FirePageHideNotification(!mSavingOldViewer);
8462 mLoadingURI = nullptr;
8464 // Set mFiredUnloadEvent = false so that the unload handler for the
8465 // *new* document will fire.
8466 mFiredUnloadEvent = false;
8468 // we've created a new document so go ahead and call
8469 // OnLoadingSite(), but don't fire OnLocationChange()
8470 // notifications before we've called Embed(). See bug 284993.
8471 mURIResultedInDocument = true;
8473 if (mLoadType == LOAD_ERROR_PAGE) {
8474 // We need to set the SH entry and our current URI here and not
8475 // at the moment we load the page. We want the same behavior
8476 // of Stop() as for a normal page load. See bug 514232 for details.
8478 // Revert mLoadType to load type to state the page load failed,
8479 // following function calls need it.
8480 mLoadType = mFailedLoadType;
8482 nsCOMPtr<nsIChannel> failedChannel = mFailedChannel;
8484 nsIDocument* doc = viewer->GetDocument();
8485 if (doc) {
8486 doc->SetFailedChannel(failedChannel);
8489 // Make sure we have a URI to set currentURI.
8490 nsCOMPtr<nsIURI> failedURI;
8491 if (failedChannel) {
8492 NS_GetFinalChannelURI(failedChannel, getter_AddRefs(failedURI));
8495 if (!failedURI) {
8496 failedURI = mFailedURI;
8498 if (!failedURI) {
8499 // We need a URI object to store a session history entry, so make up a URI
8500 NS_NewURI(getter_AddRefs(failedURI), "about:blank");
8503 // When we don't have failedURI, something wrong will happen. See
8504 // bug 291876.
8505 MOZ_ASSERT(failedURI, "We don't have a URI for history APIs.");
8507 mFailedChannel = nullptr;
8508 mFailedURI = nullptr;
8510 // Create an shistory entry for the old load.
8511 if (failedURI) {
8512 bool errorOnLocationChangeNeeded =
8513 OnNewURI(failedURI, failedChannel, nullptr, mLoadType, false,
8514 false, false);
8516 if (errorOnLocationChangeNeeded) {
8517 FireOnLocationChange(this, failedChannel, failedURI,
8518 LOCATION_CHANGE_ERROR_PAGE);
8522 // Be sure to have a correct mLSHE, it may have been cleared by
8523 // EndPageLoad. See bug 302115.
8524 if (mSessionHistory && !mLSHE) {
8525 int32_t idx;
8526 mSessionHistory->GetRequestedIndex(&idx);
8527 if (idx == -1)
8528 mSessionHistory->GetIndex(&idx);
8529 mSessionHistory->GetEntryAtIndex(idx, false,
8530 getter_AddRefs(mLSHE));
8533 mLoadType = LOAD_ERROR_PAGE;
8536 bool onLocationChangeNeeded = OnLoadingSite(aOpenedChannel, false);
8538 // let's try resetting the load group if we need to...
8539 nsCOMPtr<nsILoadGroup> currentLoadGroup;
8540 NS_ENSURE_SUCCESS(aOpenedChannel->
8541 GetLoadGroup(getter_AddRefs(currentLoadGroup)),
8542 NS_ERROR_FAILURE);
8544 if (currentLoadGroup != mLoadGroup) {
8545 nsLoadFlags loadFlags = 0;
8547 //Cancel any URIs that are currently loading...
8548 /// XXX: Need to do this eventually Stop();
8550 // Retarget the document to this loadgroup...
8552 /* First attach the channel to the right loadgroup
8553 * and then remove from the old loadgroup. This
8554 * puts the notifications in the right order and
8555 * we don't null-out mLSHE in OnStateChange() for
8556 * all redirected urls
8558 aOpenedChannel->SetLoadGroup(mLoadGroup);
8560 // Mark the channel as being a document URI...
8561 aOpenedChannel->GetLoadFlags(&loadFlags);
8562 loadFlags |= nsIChannel::LOAD_DOCUMENT_URI;
8564 aOpenedChannel->SetLoadFlags(loadFlags);
8566 mLoadGroup->AddRequest(request, nullptr);
8567 if (currentLoadGroup)
8568 currentLoadGroup->RemoveRequest(request, nullptr,
8569 NS_BINDING_RETARGETED);
8571 // Update the notification callbacks, so that progress and
8572 // status information are sent to the right docshell...
8573 aOpenedChannel->SetNotificationCallbacks(this);
8576 NS_ENSURE_SUCCESS(Embed(viewer, "", (nsISupports *) nullptr),
8577 NS_ERROR_FAILURE);
8579 mSavedRefreshURIList = nullptr;
8580 mSavingOldViewer = false;
8581 mEODForCurrentDocument = false;
8583 // if this document is part of a multipart document,
8584 // the ID can be used to distinguish it from the other parts.
8585 nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(request));
8586 if (multiPartChannel) {
8587 nsCOMPtr<nsIPresShell> shell = GetPresShell();
8588 if (NS_SUCCEEDED(rv) && shell) {
8589 nsIDocument *doc = shell->GetDocument();
8590 if (doc) {
8591 uint32_t partID;
8592 multiPartChannel->GetPartID(&partID);
8593 doc->SetPartID(partID);
8598 // Give hint to native plevent dispatch mechanism. If a document
8599 // is loading the native plevent dispatch mechanism should favor
8600 // performance over normal native event dispatch priorities.
8601 if (++gNumberOfDocumentsLoading == 1) {
8602 // Hint to favor performance for the plevent notification mechanism.
8603 // We want the pages to load as fast as possible even if its means
8604 // native messages might be starved.
8605 FavorPerformanceHint(true);
8608 if (onLocationChangeNeeded) {
8609 FireOnLocationChange(this, request, mCurrentURI, 0);
8612 return NS_OK;
8615 nsresult
8616 nsDocShell::NewContentViewerObj(const char *aContentType,
8617 nsIRequest * request, nsILoadGroup * aLoadGroup,
8618 nsIStreamListener ** aContentHandler,
8619 nsIContentViewer ** aViewer)
8621 nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(request);
8623 nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
8624 nsContentUtils::FindInternalContentViewer(aContentType);
8625 if (!docLoaderFactory) {
8626 return NS_ERROR_FAILURE;
8629 // Now create an instance of the content viewer
8630 // nsLayoutDLF makes the determination if it should be a "view-source" instead of "view"
8631 nsresult rv = docLoaderFactory->CreateInstance("view",
8632 aOpenedChannel,
8633 aLoadGroup, aContentType,
8634 this,
8635 nullptr,
8636 aContentHandler,
8637 aViewer);
8638 NS_ENSURE_SUCCESS(rv, rv);
8640 (*aViewer)->SetContainer(this);
8641 return NS_OK;
8644 NS_IMETHODIMP
8645 nsDocShell::SetupNewViewer(nsIContentViewer * aNewViewer)
8648 // Copy content viewer state from previous or parent content viewer.
8650 // The following logic is mirrored in nsHTMLDocument::StartDocumentLoad!
8652 // Do NOT to maintain a reference to the old content viewer outside
8653 // of this "copying" block, or it will not be destroyed until the end of
8654 // this routine and all <SCRIPT>s and event handlers fail! (bug 20315)
8656 // In this block of code, if we get an error result, we return it
8657 // but if we get a null pointer, that's perfectly legal for parent
8658 // and parentContentViewer.
8661 int32_t x = 0;
8662 int32_t y = 0;
8663 int32_t cx = 0;
8664 int32_t cy = 0;
8666 // This will get the size from the current content viewer or from the
8667 // Init settings
8668 DoGetPositionAndSize(&x, &y, &cx, &cy);
8670 nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
8671 NS_ENSURE_SUCCESS(GetSameTypeParent(getter_AddRefs(parentAsItem)),
8672 NS_ERROR_FAILURE);
8673 nsCOMPtr<nsIDocShell> parent(do_QueryInterface(parentAsItem));
8675 nsAutoCString forceCharset;
8676 nsAutoCString hintCharset;
8677 int32_t hintCharsetSource;
8678 int32_t minFontSize;
8679 float textZoom;
8680 float pageZoom;
8681 bool styleDisabled;
8682 // |newMUDV| also serves as a flag to set the data from the above vars
8683 nsCOMPtr<nsIContentViewer> newCv;
8685 if (mContentViewer || parent) {
8686 nsCOMPtr<nsIContentViewer> oldCv;
8687 if (mContentViewer) {
8688 // Get any interesting state from old content viewer
8689 // XXX: it would be far better to just reuse the document viewer ,
8690 // since we know we're just displaying the same document as before
8691 oldCv = mContentViewer;
8693 // Tell the old content viewer to hibernate in session history when
8694 // it is destroyed.
8696 if (mSavingOldViewer && NS_FAILED(CaptureState())) {
8697 if (mOSHE) {
8698 mOSHE->SyncPresentationState();
8700 mSavingOldViewer = false;
8703 else {
8704 // No old content viewer, so get state from parent's content viewer
8705 parent->GetContentViewer(getter_AddRefs(oldCv));
8708 if (oldCv) {
8709 newCv = aNewViewer;
8710 if (newCv) {
8711 NS_ENSURE_SUCCESS(oldCv->
8712 GetForceCharacterSet(forceCharset),
8713 NS_ERROR_FAILURE);
8714 NS_ENSURE_SUCCESS(oldCv->
8715 GetHintCharacterSet(hintCharset),
8716 NS_ERROR_FAILURE);
8717 NS_ENSURE_SUCCESS(oldCv->
8718 GetHintCharacterSetSource(&hintCharsetSource),
8719 NS_ERROR_FAILURE);
8720 NS_ENSURE_SUCCESS(oldCv->
8721 GetMinFontSize(&minFontSize),
8722 NS_ERROR_FAILURE);
8723 NS_ENSURE_SUCCESS(oldCv->
8724 GetTextZoom(&textZoom),
8725 NS_ERROR_FAILURE);
8726 NS_ENSURE_SUCCESS(oldCv->
8727 GetFullZoom(&pageZoom),
8728 NS_ERROR_FAILURE);
8729 NS_ENSURE_SUCCESS(oldCv->
8730 GetAuthorStyleDisabled(&styleDisabled),
8731 NS_ERROR_FAILURE);
8736 nscolor bgcolor = NS_RGBA(0, 0, 0, 0);
8737 // Ensure that the content viewer is destroyed *after* the GC - bug 71515
8738 nsCOMPtr<nsIContentViewer> kungfuDeathGrip = mContentViewer;
8739 if (mContentViewer) {
8740 // Stop any activity that may be happening in the old document before
8741 // releasing it...
8742 mContentViewer->Stop();
8744 // Try to extract the canvas background color from the old
8745 // presentation shell, so we can use it for the next document.
8746 nsCOMPtr<nsIPresShell> shell;
8747 mContentViewer->GetPresShell(getter_AddRefs(shell));
8749 if (shell) {
8750 bgcolor = shell->GetCanvasBackground();
8753 mContentViewer->Close(mSavingOldViewer ? mOSHE.get() : nullptr);
8754 aNewViewer->SetPreviousViewer(mContentViewer);
8756 if (mOSHE && (!mContentViewer || !mSavingOldViewer)) {
8757 // We don't plan to save a viewer in mOSHE; tell it to drop
8758 // any other state it's holding.
8759 mOSHE->SyncPresentationState();
8762 mContentViewer = nullptr;
8764 // Now that we're about to switch documents, forget all of our children.
8765 // Note that we cached them as needed up in CaptureState above.
8766 DestroyChildren();
8768 mContentViewer = aNewViewer;
8770 nsCOMPtr<nsIWidget> widget;
8771 NS_ENSURE_SUCCESS(GetMainWidget(getter_AddRefs(widget)), NS_ERROR_FAILURE);
8773 nsIntRect bounds(x, y, cx, cy);
8775 mContentViewer->SetNavigationTiming(mTiming);
8777 if (NS_FAILED(mContentViewer->Init(widget, bounds))) {
8778 mContentViewer = nullptr;
8779 NS_WARNING("ContentViewer Initialization failed");
8780 return NS_ERROR_FAILURE;
8783 // If we have old state to copy, set the old state onto the new content
8784 // viewer
8785 if (newCv) {
8786 NS_ENSURE_SUCCESS(newCv->SetForceCharacterSet(forceCharset),
8787 NS_ERROR_FAILURE);
8788 NS_ENSURE_SUCCESS(newCv->SetHintCharacterSet(hintCharset),
8789 NS_ERROR_FAILURE);
8790 NS_ENSURE_SUCCESS(newCv->
8791 SetHintCharacterSetSource(hintCharsetSource),
8792 NS_ERROR_FAILURE);
8793 NS_ENSURE_SUCCESS(newCv->SetMinFontSize(minFontSize),
8794 NS_ERROR_FAILURE);
8795 NS_ENSURE_SUCCESS(newCv->SetTextZoom(textZoom),
8796 NS_ERROR_FAILURE);
8797 NS_ENSURE_SUCCESS(newCv->SetFullZoom(pageZoom),
8798 NS_ERROR_FAILURE);
8799 NS_ENSURE_SUCCESS(newCv->SetAuthorStyleDisabled(styleDisabled),
8800 NS_ERROR_FAILURE);
8803 // Stuff the bgcolor from the old pres shell into the new
8804 // pres shell. This improves page load continuity.
8805 nsCOMPtr<nsIPresShell> shell;
8806 mContentViewer->GetPresShell(getter_AddRefs(shell));
8808 if (shell) {
8809 shell->SetCanvasBackground(bgcolor);
8812 // XXX: It looks like the LayoutState gets restored again in Embed()
8813 // right after the call to SetupNewViewer(...)
8815 // We don't show the mContentViewer yet, since we want to draw the old page
8816 // until we have enough of the new page to show. Just return with the new
8817 // viewer still set to hidden.
8819 return NS_OK;
8822 nsresult
8823 nsDocShell::SetDocCurrentStateObj(nsISHEntry *shEntry)
8825 NS_ENSURE_STATE(mContentViewer);
8826 nsCOMPtr<nsIDocument> document = GetDocument();
8827 NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
8829 nsCOMPtr<nsIStructuredCloneContainer> scContainer;
8830 if (shEntry) {
8831 nsresult rv = shEntry->GetStateData(getter_AddRefs(scContainer));
8832 NS_ENSURE_SUCCESS(rv, rv);
8834 // If shEntry is null, just set the document's state object to null.
8837 // It's OK for scContainer too be null here; that just means there's no
8838 // state data associated with this history entry.
8839 document->SetStateObject(scContainer);
8841 return NS_OK;
8844 nsresult
8845 nsDocShell::CheckLoadingPermissions()
8847 // This method checks whether the caller may load content into
8848 // this docshell. Even though we've done our best to hide windows
8849 // from code that doesn't have the right to access them, it's
8850 // still possible for an evil site to open a window and access
8851 // frames in the new window through window.frames[] (which is
8852 // allAccess for historic reasons), so we still need to do this
8853 // check on load.
8854 nsresult rv = NS_OK;
8856 if (!gValidateOrigin || !IsFrame()) {
8857 // Origin validation was turned off, or we're not a frame.
8858 // Permit all loads.
8860 return rv;
8863 // Note - The check for a current JSContext here isn't necessarily sensical.
8864 // It's just designed to preserve the old semantics during a mass-conversion
8865 // patch.
8866 NS_ENSURE_TRUE(nsContentUtils::GetCurrentJSContext(), NS_OK);
8868 // Check if the caller is from the same origin as this docshell,
8869 // or any of its ancestors.
8870 nsCOMPtr<nsIDocShellTreeItem> item(this);
8871 do {
8872 nsCOMPtr<nsIScriptGlobalObject> sgo = do_GetInterface(item);
8873 nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(sgo));
8875 nsIPrincipal *p;
8876 if (!sop || !(p = sop->GetPrincipal())) {
8877 return NS_ERROR_UNEXPECTED;
8880 if (nsContentUtils::SubjectPrincipal()->Subsumes(p)) {
8881 // Same origin, permit load
8882 return NS_OK;
8885 nsCOMPtr<nsIDocShellTreeItem> tmp;
8886 item->GetSameTypeParent(getter_AddRefs(tmp));
8887 item.swap(tmp);
8888 } while (item);
8890 return NS_ERROR_DOM_PROP_ACCESS_DENIED;
8893 //*****************************************************************************
8894 // nsDocShell: Site Loading
8895 //*****************************************************************************
8896 namespace
8899 #ifdef MOZ_PLACES
8900 // Callback used by CopyFavicon to inform the favicon service that one URI
8901 // (mNewURI) has the same favicon URI (OnComplete's aFaviconURI) as another.
8902 class nsCopyFaviconCallback MOZ_FINAL : public nsIFaviconDataCallback
8904 public:
8905 NS_DECL_ISUPPORTS
8907 nsCopyFaviconCallback(nsIURI *aNewURI, bool aInPrivateBrowsing)
8908 : mNewURI(aNewURI)
8909 , mInPrivateBrowsing(aInPrivateBrowsing)
8913 NS_IMETHODIMP
8914 OnComplete(nsIURI *aFaviconURI, uint32_t aDataLen,
8915 const uint8_t *aData, const nsACString &aMimeType)
8917 // Continue only if there is an associated favicon.
8918 if (!aFaviconURI) {
8919 return NS_OK;
8922 NS_ASSERTION(aDataLen == 0,
8923 "We weren't expecting the callback to deliver data.");
8924 nsCOMPtr<mozIAsyncFavicons> favSvc =
8925 do_GetService("@mozilla.org/browser/favicon-service;1");
8926 NS_ENSURE_STATE(favSvc);
8928 return favSvc->SetAndFetchFaviconForPage(mNewURI, aFaviconURI,
8929 false,
8930 mInPrivateBrowsing ?
8931 nsIFaviconService::FAVICON_LOAD_PRIVATE :
8932 nsIFaviconService::FAVICON_LOAD_NON_PRIVATE,
8933 nullptr);
8936 private:
8937 ~nsCopyFaviconCallback() {}
8939 nsCOMPtr<nsIURI> mNewURI;
8940 bool mInPrivateBrowsing;
8943 NS_IMPL_ISUPPORTS(nsCopyFaviconCallback, nsIFaviconDataCallback)
8944 #endif
8946 // Tell the favicon service that aNewURI has the same favicon as aOldURI.
8947 void CopyFavicon(nsIURI *aOldURI, nsIURI *aNewURI, bool inPrivateBrowsing)
8949 #ifdef MOZ_PLACES
8950 nsCOMPtr<mozIAsyncFavicons> favSvc =
8951 do_GetService("@mozilla.org/browser/favicon-service;1");
8952 if (favSvc) {
8953 nsCOMPtr<nsIFaviconDataCallback> callback =
8954 new nsCopyFaviconCallback(aNewURI, inPrivateBrowsing);
8955 favSvc->GetFaviconURLForPage(aOldURI, callback);
8957 #endif
8960 } // anonymous namespace
8962 class InternalLoadEvent : public nsRunnable
8964 public:
8965 InternalLoadEvent(nsDocShell* aDocShell, nsIURI * aURI, nsIURI * aReferrer,
8966 nsISupports * aOwner, uint32_t aFlags,
8967 const char* aTypeHint, nsIInputStream * aPostData,
8968 nsIInputStream * aHeadersData, uint32_t aLoadType,
8969 nsISHEntry * aSHEntry, bool aFirstParty,
8970 const nsAString &aSrcdoc, nsIDocShell* aSourceDocShell,
8971 nsIURI * aBaseURI) :
8972 mSrcdoc(aSrcdoc),
8973 mDocShell(aDocShell),
8974 mURI(aURI),
8975 mReferrer(aReferrer),
8976 mOwner(aOwner),
8977 mPostData(aPostData),
8978 mHeadersData(aHeadersData),
8979 mSHEntry(aSHEntry),
8980 mFlags(aFlags),
8981 mLoadType(aLoadType),
8982 mFirstParty(aFirstParty),
8983 mSourceDocShell(aSourceDocShell),
8984 mBaseURI(aBaseURI)
8986 // Make sure to keep null things null as needed
8987 if (aTypeHint) {
8988 mTypeHint = aTypeHint;
8992 NS_IMETHOD Run() {
8993 return mDocShell->InternalLoad(mURI, mReferrer, mOwner, mFlags,
8994 nullptr, mTypeHint.get(),
8995 NullString(), mPostData, mHeadersData,
8996 mLoadType, mSHEntry, mFirstParty,
8997 mSrcdoc, mSourceDocShell, mBaseURI,
8998 nullptr, nullptr);
9001 private:
9003 // Use IDL strings so .get() returns null by default
9004 nsXPIDLString mWindowTarget;
9005 nsXPIDLCString mTypeHint;
9006 nsString mSrcdoc;
9008 nsRefPtr<nsDocShell> mDocShell;
9009 nsCOMPtr<nsIURI> mURI;
9010 nsCOMPtr<nsIURI> mReferrer;
9011 nsCOMPtr<nsISupports> mOwner;
9012 nsCOMPtr<nsIInputStream> mPostData;
9013 nsCOMPtr<nsIInputStream> mHeadersData;
9014 nsCOMPtr<nsISHEntry> mSHEntry;
9015 uint32_t mFlags;
9016 uint32_t mLoadType;
9017 bool mFirstParty;
9018 nsCOMPtr<nsIDocShell> mSourceDocShell;
9019 nsCOMPtr<nsIURI> mBaseURI;
9023 * Returns true if we started an asynchronous load (i.e., from the network), but
9024 * the document we're loading there hasn't yet become this docshell's active
9025 * document.
9027 * When JustStartedNetworkLoad is true, you should be careful about modifying
9028 * mLoadType and mLSHE. These are both set when the asynchronous load first
9029 * starts, and the load expects that, when it eventually runs InternalLoad,
9030 * mLoadType and mLSHE will have their original values.
9032 bool
9033 nsDocShell::JustStartedNetworkLoad()
9035 return mDocumentRequest &&
9036 mDocumentRequest != GetCurrentDocChannel();
9039 NS_IMETHODIMP
9040 nsDocShell::InternalLoad(nsIURI * aURI,
9041 nsIURI * aReferrer,
9042 nsISupports * aOwner,
9043 uint32_t aFlags,
9044 const char16_t *aWindowTarget,
9045 const char* aTypeHint,
9046 const nsAString& aFileName,
9047 nsIInputStream * aPostData,
9048 nsIInputStream * aHeadersData,
9049 uint32_t aLoadType,
9050 nsISHEntry * aSHEntry,
9051 bool aFirstParty,
9052 const nsAString &aSrcdoc,
9053 nsIDocShell* aSourceDocShell,
9054 nsIURI* aBaseURI,
9055 nsIDocShell** aDocShell,
9056 nsIRequest** aRequest)
9058 nsresult rv = NS_OK;
9059 mOriginalUriString.Truncate();
9061 #ifdef PR_LOGGING
9062 if (gDocShellLeakLog && PR_LOG_TEST(gDocShellLeakLog, PR_LOG_DEBUG)) {
9063 nsAutoCString spec;
9064 if (aURI)
9065 aURI->GetSpec(spec);
9066 PR_LogPrint("DOCSHELL %p InternalLoad %s\n", this, spec.get());
9068 #endif
9069 // Initialize aDocShell/aRequest
9070 if (aDocShell) {
9071 *aDocShell = nullptr;
9073 if (aRequest) {
9074 *aRequest = nullptr;
9077 if (!aURI) {
9078 return NS_ERROR_NULL_POINTER;
9081 NS_ENSURE_TRUE(IsValidLoadType(aLoadType), NS_ERROR_INVALID_ARG);
9083 NS_ENSURE_TRUE(!mIsBeingDestroyed, NS_ERROR_NOT_AVAILABLE);
9085 // wyciwyg urls can only be loaded through history. Any normal load of
9086 // wyciwyg through docshell is illegal. Disallow such loads.
9087 if (aLoadType & LOAD_CMD_NORMAL) {
9088 bool isWyciwyg = false;
9089 rv = aURI->SchemeIs("wyciwyg", &isWyciwyg);
9090 if ((isWyciwyg && NS_SUCCEEDED(rv)) || NS_FAILED(rv))
9091 return NS_ERROR_FAILURE;
9094 bool bIsJavascript = false;
9095 if (NS_FAILED(aURI->SchemeIs("javascript", &bIsJavascript))) {
9096 bIsJavascript = false;
9100 // First, notify any nsIContentPolicy listeners about the document load.
9101 // Only abort the load if a content policy listener explicitly vetos it!
9103 nsCOMPtr<Element> requestingElement;
9104 // Use nsPIDOMWindow since we _want_ to cross the chrome boundary if needed
9105 if (mScriptGlobal)
9106 requestingElement = mScriptGlobal->GetFrameElementInternal();
9108 nsRefPtr<nsGlobalWindow> MMADeathGrip = mScriptGlobal;
9110 int16_t shouldLoad = nsIContentPolicy::ACCEPT;
9111 uint32_t contentType;
9112 bool isNewDocShell = false;
9113 bool isTargetTopLevelDocShell = false;
9114 nsCOMPtr<nsIDocShell> targetDocShell;
9115 if (aWindowTarget && *aWindowTarget) {
9116 // Locate the target DocShell.
9117 nsCOMPtr<nsIDocShellTreeItem> targetItem;
9118 rv = FindItemWithName(aWindowTarget, nullptr, this,
9119 getter_AddRefs(targetItem));
9120 NS_ENSURE_SUCCESS(rv, rv);
9122 targetDocShell = do_QueryInterface(targetItem);
9123 // If the targetDocShell doesn't exist, then this is a new docShell
9124 // and we should consider this a TYPE_DOCUMENT load
9125 isNewDocShell = !targetDocShell;
9127 // If the targetDocShell and the rootDocShell are the same, then the
9128 // targetDocShell is the top level document and hence we should
9129 // consider this TYPE_DOCUMENT
9130 if (targetDocShell) {
9131 nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
9132 targetDocShell->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
9133 NS_ASSERTION(sameTypeRoot, "No document shell root tree item from targetDocShell!");
9134 nsCOMPtr<nsIDocShell> rootShell = do_QueryInterface(sameTypeRoot);
9135 NS_ASSERTION(rootShell, "No root docshell from document shell root tree item.");
9137 if (targetDocShell == rootShell) {
9138 isTargetTopLevelDocShell = true;
9142 if (IsFrame() && !isNewDocShell && !isTargetTopLevelDocShell) {
9143 NS_ASSERTION(requestingElement, "A frame but no DOM element!?");
9144 contentType = nsIContentPolicy::TYPE_SUBDOCUMENT;
9145 } else {
9146 contentType = nsIContentPolicy::TYPE_DOCUMENT;
9149 nsISupports* context = requestingElement;
9150 if (!context) {
9151 context = ToSupports(mScriptGlobal);
9154 // XXXbz would be nice to know the loading principal here... but we don't
9155 nsCOMPtr<nsIPrincipal> loadingPrincipal = do_QueryInterface(aOwner);
9156 if (!loadingPrincipal && aReferrer) {
9157 nsCOMPtr<nsIScriptSecurityManager> secMan =
9158 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
9159 NS_ENSURE_SUCCESS(rv, rv);
9161 rv = secMan->GetSimpleCodebasePrincipal(aReferrer,
9162 getter_AddRefs(loadingPrincipal));
9165 rv = NS_CheckContentLoadPolicy(contentType,
9166 aURI,
9167 loadingPrincipal,
9168 context,
9169 EmptyCString(), //mime guess
9170 nullptr, //extra
9171 &shouldLoad);
9173 if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
9174 if (NS_SUCCEEDED(rv) && shouldLoad == nsIContentPolicy::REJECT_TYPE) {
9175 return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
9178 return NS_ERROR_CONTENT_BLOCKED;
9181 nsCOMPtr<nsISupports> owner(aOwner);
9183 // Get an owner from the current document if necessary. Note that we only
9184 // do this for URIs that inherit a security context and local file URIs;
9185 // in particular we do NOT do this for about:blank. This way, random
9186 // about:blank loads that have no owner (which basically means they were
9187 // done by someone from chrome manually messing with our nsIWebNavigation
9188 // or by C++ setting document.location) don't get a funky principal. If
9189 // callers want something interesting to happen with the about:blank
9190 // principal in this case, they should pass an owner in.
9193 bool inherits;
9194 // One more twist: Don't inherit the owner for external loads.
9195 if (aLoadType != LOAD_NORMAL_EXTERNAL && !owner &&
9196 (aFlags & INTERNAL_LOAD_FLAGS_INHERIT_OWNER) &&
9197 NS_SUCCEEDED(nsContentUtils::URIInheritsSecurityContext(aURI,
9198 &inherits)) &&
9199 inherits) {
9201 owner = GetInheritedPrincipal(true);
9205 // Don't allow loads that would inherit our security context
9206 // if this document came from an unsafe channel.
9208 bool willInherit;
9209 // This condition needs to match the one in
9210 // nsContentUtils::SetUpChannelOwner.
9211 // Except we reverse the rv check to be safe in case
9212 // nsContentUtils::URIInheritsSecurityContext fails here and
9213 // succeeds there.
9214 rv = nsContentUtils::URIInheritsSecurityContext(aURI, &willInherit);
9215 if (NS_FAILED(rv) || willInherit || NS_IsAboutBlank(aURI)) {
9216 nsCOMPtr<nsIDocShellTreeItem> treeItem = this;
9217 do {
9218 nsCOMPtr<nsIDocShell> itemDocShell =
9219 do_QueryInterface(treeItem);
9220 bool isUnsafe;
9221 if (itemDocShell &&
9222 NS_SUCCEEDED(itemDocShell->GetChannelIsUnsafe(&isUnsafe)) &&
9223 isUnsafe) {
9224 return NS_ERROR_DOM_SECURITY_ERR;
9227 nsCOMPtr<nsIDocShellTreeItem> parent;
9228 treeItem->GetSameTypeParent(getter_AddRefs(parent));
9229 parent.swap(treeItem);
9230 } while (treeItem);
9235 // Resolve the window target before going any further...
9236 // If the load has been targeted to another DocShell, then transfer the
9237 // load to it...
9239 if (aWindowTarget && *aWindowTarget) {
9240 // We've already done our owner-inheriting. Mask out that bit, so we
9241 // don't try inheriting an owner from the target window if we came up
9242 // with a null owner above.
9243 aFlags = aFlags & ~INTERNAL_LOAD_FLAGS_INHERIT_OWNER;
9245 bool isNewWindow = false;
9246 if (!targetDocShell) {
9247 // If the docshell's document is sandboxed, only open a new window
9248 // if the document's SANDBOXED_AUXILLARY_NAVIGATION flag is not set.
9249 // (i.e. if allow-popups is specified)
9250 NS_ENSURE_TRUE(mContentViewer, NS_ERROR_FAILURE);
9251 nsIDocument* doc = mContentViewer->GetDocument();
9252 uint32_t sandboxFlags = 0;
9254 if (doc) {
9255 sandboxFlags = doc->GetSandboxFlags();
9256 if (sandboxFlags & SANDBOXED_AUXILIARY_NAVIGATION) {
9257 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
9261 nsCOMPtr<nsPIDOMWindow> win = GetWindow();
9262 NS_ENSURE_TRUE(win, NS_ERROR_NOT_AVAILABLE);
9264 nsDependentString name(aWindowTarget);
9265 nsCOMPtr<nsIDOMWindow> newWin;
9266 nsAutoCString spec;
9267 if (aURI)
9268 aURI->GetSpec(spec);
9269 rv = win->OpenNoNavigate(NS_ConvertUTF8toUTF16(spec),
9270 name, // window name
9271 EmptyString(), // Features
9272 getter_AddRefs(newWin));
9274 // In some cases the Open call doesn't actually result in a new
9275 // window being opened. We can detect these cases by examining the
9276 // document in |newWin|, if any.
9277 nsCOMPtr<nsPIDOMWindow> piNewWin = do_QueryInterface(newWin);
9278 if (piNewWin) {
9279 nsCOMPtr<nsIDocument> newDoc = piNewWin->GetExtantDoc();
9280 if (!newDoc || newDoc->IsInitialDocument()) {
9281 isNewWindow = true;
9282 aFlags |= INTERNAL_LOAD_FLAGS_FIRST_LOAD;
9284 // set opener object to null for noreferrer
9285 if (aFlags & INTERNAL_LOAD_FLAGS_NO_OPENER) {
9286 piNewWin->SetOpenerWindow(nullptr, false);
9291 nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(newWin);
9292 targetDocShell = do_QueryInterface(webNav);
9296 // Transfer the load to the target DocShell... Pass nullptr as the
9297 // window target name from to prevent recursive retargeting!
9299 if (NS_SUCCEEDED(rv) && targetDocShell) {
9300 rv = targetDocShell->InternalLoad(aURI,
9301 aReferrer,
9302 owner,
9303 aFlags,
9304 nullptr, // No window target
9305 aTypeHint,
9306 NullString(), // No forced download
9307 aPostData,
9308 aHeadersData,
9309 aLoadType,
9310 aSHEntry,
9311 aFirstParty,
9312 aSrcdoc,
9313 aSourceDocShell,
9314 aBaseURI,
9315 aDocShell,
9316 aRequest);
9317 if (rv == NS_ERROR_NO_CONTENT) {
9318 // XXXbz except we never reach this code!
9319 if (isNewWindow) {
9321 // At this point, a new window has been created, but the
9322 // URI did not have any data associated with it...
9324 // So, the best we can do, is to tear down the new window
9325 // that was just created!
9327 nsCOMPtr<nsIDOMWindow> domWin = targetDocShell->GetWindow();
9328 if (domWin) {
9329 domWin->Close();
9333 // NS_ERROR_NO_CONTENT should not be returned to the
9334 // caller... This is an internal error code indicating that
9335 // the URI had no data associated with it - probably a
9336 // helper-app style protocol (ie. mailto://)
9338 rv = NS_OK;
9340 else if (isNewWindow) {
9341 // XXX: Once new windows are created hidden, the new
9342 // window will need to be made visible... For now,
9343 // do nothing.
9347 // Else we ran out of memory, or were a popup and got blocked,
9348 // or something.
9350 return rv;
9354 // Load is being targetted at this docshell so return an error if the
9355 // docshell is in the process of being destroyed.
9357 if (mIsBeingDestroyed) {
9358 return NS_ERROR_FAILURE;
9361 NS_ENSURE_STATE(!HasUnloadedParent());
9363 rv = CheckLoadingPermissions();
9364 if (NS_FAILED(rv)) {
9365 return rv;
9368 if (mFiredUnloadEvent) {
9369 if (IsOKToLoadURI(aURI)) {
9370 NS_PRECONDITION(!aWindowTarget || !*aWindowTarget,
9371 "Shouldn't have a window target here!");
9373 // If this is a replace load, make whatever load triggered
9374 // the unload event also a replace load, so we don't
9375 // create extra history entries.
9376 if (LOAD_TYPE_HAS_FLAGS(aLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
9377 mLoadType = LOAD_NORMAL_REPLACE;
9380 // Do this asynchronously
9381 nsCOMPtr<nsIRunnable> ev =
9382 new InternalLoadEvent(this, aURI, aReferrer, aOwner, aFlags,
9383 aTypeHint, aPostData, aHeadersData,
9384 aLoadType, aSHEntry, aFirstParty, aSrcdoc,
9385 aSourceDocShell, aBaseURI);
9386 return NS_DispatchToCurrentThread(ev);
9389 // Just ignore this load attempt
9390 return NS_OK;
9393 // If a source docshell has been passed, check to see if we are sandboxed
9394 // from it as the result of an iframe or CSP sandbox.
9395 if (aSourceDocShell && aSourceDocShell->IsSandboxedFrom(this)) {
9396 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
9399 // If this docshell is owned by a frameloader, make sure to cancel
9400 // possible frameloader initialization before loading a new page.
9401 nsCOMPtr<nsIDocShellTreeItem> parent = GetParentDocshell();
9402 if (parent) {
9403 nsCOMPtr<nsIDocument> doc = do_GetInterface(parent);
9404 if (doc) {
9405 doc->TryCancelFrameLoaderInitialization(this);
9409 // Before going any further vet loads initiated by external programs.
9410 if (aLoadType == LOAD_NORMAL_EXTERNAL) {
9411 // Disallow external chrome: loads targetted at content windows
9412 bool isChrome = false;
9413 if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome)) && isChrome) {
9414 NS_WARNING("blocked external chrome: url -- use '-chrome' option");
9415 return NS_ERROR_FAILURE;
9418 // clear the decks to prevent context bleed-through (bug 298255)
9419 rv = CreateAboutBlankContentViewer(nullptr, nullptr);
9420 if (NS_FAILED(rv))
9421 return NS_ERROR_FAILURE;
9423 // reset loadType so we don't have to add lots of tests for
9424 // LOAD_NORMAL_EXTERNAL after this point
9425 aLoadType = LOAD_NORMAL;
9428 mAllowKeywordFixup =
9429 (aFlags & INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) != 0;
9430 mURIResultedInDocument = false; // reset the clock...
9432 if (aLoadType == LOAD_NORMAL ||
9433 aLoadType == LOAD_STOP_CONTENT ||
9434 LOAD_TYPE_HAS_FLAGS(aLoadType, LOAD_FLAGS_REPLACE_HISTORY) ||
9435 aLoadType == LOAD_HISTORY ||
9436 aLoadType == LOAD_LINK) {
9438 nsCOMPtr<nsIURI> currentURI = mCurrentURI;
9439 // Split currentURI and aURI on the '#' character. Make sure we read
9440 // the return values of SplitURIAtHash; if it fails, we don't want to
9441 // allow a short-circuited navigation.
9442 nsAutoCString curBeforeHash, curHash, newBeforeHash, newHash;
9443 nsresult splitRv1, splitRv2;
9444 splitRv1 = currentURI ?
9445 nsContentUtils::SplitURIAtHash(currentURI,
9446 curBeforeHash, curHash) :
9447 NS_ERROR_FAILURE;
9448 splitRv2 = nsContentUtils::SplitURIAtHash(aURI, newBeforeHash, newHash);
9450 bool sameExceptHashes = NS_SUCCEEDED(splitRv1) &&
9451 NS_SUCCEEDED(splitRv2) &&
9452 curBeforeHash.Equals(newBeforeHash);
9454 if (!sameExceptHashes && sURIFixup && currentURI &&
9455 NS_SUCCEEDED(splitRv2)) {
9456 // Maybe aURI came from the exposable form of currentURI?
9457 nsCOMPtr<nsIURI> currentExposableURI;
9458 rv = sURIFixup->CreateExposableURI(currentURI,
9459 getter_AddRefs(currentExposableURI));
9460 NS_ENSURE_SUCCESS(rv, rv);
9461 splitRv1 = nsContentUtils::SplitURIAtHash(currentExposableURI,
9462 curBeforeHash, curHash);
9463 sameExceptHashes = NS_SUCCEEDED(splitRv1) &&
9464 curBeforeHash.Equals(newBeforeHash);
9467 bool historyNavBetweenSameDoc = false;
9468 if (mOSHE && aSHEntry) {
9469 // We're doing a history load.
9471 mOSHE->SharesDocumentWith(aSHEntry, &historyNavBetweenSameDoc);
9473 #ifdef DEBUG
9474 if (historyNavBetweenSameDoc) {
9475 nsCOMPtr<nsIInputStream> currentPostData;
9476 mOSHE->GetPostData(getter_AddRefs(currentPostData));
9477 NS_ASSERTION(currentPostData == aPostData,
9478 "Different POST data for entries for the same page?");
9480 #endif
9483 // A short-circuited load happens when we navigate between two SHEntries
9484 // for the same document. We do a short-circuited load under two
9485 // circumstances. Either
9487 // a) we're navigating between two different SHEntries which share a
9488 // document, or
9490 // b) we're navigating to a new shentry whose URI differs from the
9491 // current URI only in its hash, the new hash is non-empty, and
9492 // we're not doing a POST.
9494 // The restriction tha the SHEntries in (a) must be different ensures
9495 // that history.go(0) and the like trigger full refreshes, rather than
9496 // short-circuited loads.
9497 bool doShortCircuitedLoad =
9498 (historyNavBetweenSameDoc && mOSHE != aSHEntry) ||
9499 (!aSHEntry && aPostData == nullptr &&
9500 sameExceptHashes && !newHash.IsEmpty());
9502 if (doShortCircuitedLoad) {
9503 // Save the position of the scrollers.
9504 nscoord cx = 0, cy = 0;
9505 GetCurScrollPos(ScrollOrientation_X, &cx);
9506 GetCurScrollPos(ScrollOrientation_Y, &cy);
9508 // Reset mLoadType to its original value once we exit this block,
9509 // because this short-circuited load might have started after a
9510 // normal, network load, and we don't want to clobber its load type.
9511 // See bug 737307.
9512 AutoRestore<uint32_t> loadTypeResetter(mLoadType);
9514 // If a non-short-circuit load (i.e., a network load) is pending,
9515 // make this a replacement load, so that we don't add a SHEntry here
9516 // and the network load goes into the SHEntry it expects to.
9517 if (JustStartedNetworkLoad() && (aLoadType & LOAD_CMD_NORMAL)) {
9518 mLoadType = LOAD_NORMAL_REPLACE;
9520 else {
9521 mLoadType = aLoadType;
9524 mURIResultedInDocument = true;
9526 nsCOMPtr<nsISHEntry> oldLSHE = mLSHE;
9528 /* we need to assign mLSHE to aSHEntry right here, so that on History loads,
9529 * SetCurrentURI() called from OnNewURI() will send proper
9530 * onLocationChange() notifications to the browser to update
9531 * back/forward buttons.
9533 SetHistoryEntry(&mLSHE, aSHEntry);
9535 /* This is a anchor traversal with in the same page.
9536 * call OnNewURI() so that, this traversal will be
9537 * recorded in session and global history.
9539 nsCOMPtr<nsISupports> owner;
9540 if (mOSHE) {
9541 mOSHE->GetOwner(getter_AddRefs(owner));
9543 // Pass true for aCloneSHChildren, since we're not
9544 // changing documents here, so all of our subframes are
9545 // still relevant to the new session history entry.
9547 // It also makes OnNewURI(...) set LOCATION_CHANGE_SAME_DOCUMENT
9548 // flag on firing onLocationChange(...).
9549 // Anyway, aCloneSHChildren param is simply reflecting
9550 // doShortCircuitedLoad in this scope.
9551 OnNewURI(aURI, nullptr, owner, mLoadType, true, true, true);
9553 nsCOMPtr<nsIInputStream> postData;
9554 nsCOMPtr<nsISupports> cacheKey;
9556 if (mOSHE) {
9557 /* save current position of scroller(s) (bug 59774) */
9558 mOSHE->SetScrollPosition(cx, cy);
9559 // Get the postdata and page ident from the current page, if
9560 // the new load is being done via normal means. Note that
9561 // "normal means" can be checked for just by checking for
9562 // LOAD_CMD_NORMAL, given the loadType and allowScroll check
9563 // above -- it filters out some LOAD_CMD_NORMAL cases that we
9564 // wouldn't want here.
9565 if (aLoadType & LOAD_CMD_NORMAL) {
9566 mOSHE->GetPostData(getter_AddRefs(postData));
9567 mOSHE->GetCacheKey(getter_AddRefs(cacheKey));
9569 // Link our new SHEntry to the old SHEntry's back/forward
9570 // cache data, since the two SHEntries correspond to the
9571 // same document.
9572 if (mLSHE)
9573 mLSHE->AdoptBFCacheEntry(mOSHE);
9577 /* Assign mOSHE to mLSHE. This will either be a new entry created
9578 * by OnNewURI() for normal loads or aSHEntry for history loads.
9580 if (mLSHE) {
9581 SetHistoryEntry(&mOSHE, mLSHE);
9582 // Save the postData obtained from the previous page
9583 // in to the session history entry created for the
9584 // anchor page, so that any history load of the anchor
9585 // page will restore the appropriate postData.
9586 if (postData)
9587 mOSHE->SetPostData(postData);
9589 // Make sure we won't just repost without hitting the
9590 // cache first
9591 if (cacheKey)
9592 mOSHE->SetCacheKey(cacheKey);
9595 /* Restore the original LSHE if we were loading something
9596 * while short-circuited load was initiated.
9598 SetHistoryEntry(&mLSHE, oldLSHE);
9599 /* Set the title for the SH entry for this target url. so that
9600 * SH menus in go/back/forward buttons won't be empty for this.
9602 if (mSessionHistory) {
9603 int32_t index = -1;
9604 mSessionHistory->GetIndex(&index);
9605 nsCOMPtr<nsISHEntry> shEntry;
9606 mSessionHistory->GetEntryAtIndex(index, false,
9607 getter_AddRefs(shEntry));
9608 NS_ENSURE_TRUE(shEntry, NS_ERROR_FAILURE);
9609 shEntry->SetTitle(mTitle);
9612 /* Set the title for the Global History entry for this anchor url.
9614 if (mUseGlobalHistory && !mInPrivateBrowsing) {
9615 nsCOMPtr<IHistory> history = services::GetHistoryService();
9616 if (history) {
9617 history->SetURITitle(aURI, mTitle);
9619 else if (mGlobalHistory) {
9620 mGlobalHistory->SetPageTitle(aURI, mTitle);
9624 // Set the doc's URI according to the new history entry's URI.
9625 nsCOMPtr<nsIDocument> doc = GetDocument();
9626 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
9627 doc->SetDocumentURI(aURI);
9629 SetDocCurrentStateObj(mOSHE);
9631 // Inform the favicon service that the favicon for oldURI also
9632 // applies to aURI.
9633 CopyFavicon(currentURI, aURI, mInPrivateBrowsing);
9635 nsRefPtr<nsGlobalWindow> win = mScriptGlobal ?
9636 mScriptGlobal->GetCurrentInnerWindowInternal() : nullptr;
9638 // ScrollToAnchor doesn't necessarily cause us to scroll the window;
9639 // the function decides whether a scroll is appropriate based on the
9640 // arguments it receives. But even if we don't end up scrolling,
9641 // ScrollToAnchor performs other important tasks, such as informing
9642 // the presShell that we have a new hash. See bug 680257.
9643 rv = ScrollToAnchor(curHash, newHash, aLoadType);
9644 NS_ENSURE_SUCCESS(rv, rv);
9646 /* restore previous position of scroller(s), if we're moving
9647 * back in history (bug 59774)
9649 if (mOSHE && (aLoadType == LOAD_HISTORY ||
9650 aLoadType == LOAD_RELOAD_NORMAL)) {
9651 nscoord bx, by;
9652 mOSHE->GetScrollPosition(&bx, &by);
9653 SetCurScrollPosEx(bx, by);
9656 // Dispatch the popstate and hashchange events, as appropriate.
9658 // The event dispatch below can cause us to re-enter script and
9659 // destroy the docshell, nulling out mScriptGlobal. Hold a stack
9660 // reference to avoid null derefs. See bug 914521.
9661 if (win) {
9662 // Fire a hashchange event URIs differ, and only in their hashes.
9663 bool doHashchange = sameExceptHashes && !curHash.Equals(newHash);
9665 if (historyNavBetweenSameDoc || doHashchange) {
9666 win->DispatchSyncPopState();
9669 if (doHashchange) {
9670 // Note that currentURI hasn't changed because it's on the
9671 // stack, so we can just use it directly as the old URI.
9672 win->DispatchAsyncHashchange(currentURI, aURI);
9676 return NS_OK;
9680 // mContentViewer->PermitUnload can destroy |this| docShell, which
9681 // causes the next call of CanSavePresentation to crash.
9682 // Hold onto |this| until we return, to prevent a crash from happening.
9683 // (bug#331040)
9684 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
9686 // Don't init timing for javascript:, since it generally doesn't
9687 // actually start a load or anything. If it does, we'll init
9688 // timing then, from OnStateChange.
9690 // XXXbz mTiming should know what channel it's for, so we don't
9691 // need this hackery. Note that this is still broken in cases
9692 // when we're loading something that's not javascript: and the
9693 // beforeunload handler denies the load. That will screw up
9694 // timing for the next load!
9695 if (!bIsJavascript) {
9696 MaybeInitTiming();
9698 bool timeBeforeUnload = aFileName.IsVoid();
9699 if (mTiming && timeBeforeUnload) {
9700 mTiming->NotifyBeforeUnload();
9702 // Check if the page doesn't want to be unloaded. The javascript:
9703 // protocol handler deals with this for javascript: URLs.
9704 if (!bIsJavascript && aFileName.IsVoid() && mContentViewer) {
9705 bool okToUnload;
9706 rv = mContentViewer->PermitUnload(false, &okToUnload);
9708 if (NS_SUCCEEDED(rv) && !okToUnload) {
9709 // The user chose not to unload the page, interrupt the
9710 // load.
9711 return NS_OK;
9715 if (mTiming && timeBeforeUnload) {
9716 mTiming->NotifyUnloadAccepted(mCurrentURI);
9719 // Check for saving the presentation here, before calling Stop().
9720 // This is necessary so that we can catch any pending requests.
9721 // Since the new request has not been created yet, we pass null for the
9722 // new request parameter.
9723 // Also pass nullptr for the document, since it doesn't affect the return
9724 // value for our purposes here.
9725 bool savePresentation = CanSavePresentation(aLoadType, nullptr, nullptr);
9727 // Don't stop current network activity for javascript: URL's since
9728 // they might not result in any data, and thus nothing should be
9729 // stopped in those cases. In the case where they do result in
9730 // data, the javascript: URL channel takes care of stopping
9731 // current network activity.
9732 if (!bIsJavascript && aFileName.IsVoid()) {
9733 // Stop any current network activity.
9734 // Also stop content if this is a zombie doc. otherwise
9735 // the onload will be delayed by other loads initiated in the
9736 // background by the first document that
9737 // didn't fully load before the next load was initiated.
9738 // If not a zombie, don't stop content until data
9739 // starts arriving from the new URI...
9741 nsCOMPtr<nsIContentViewer> zombieViewer;
9742 if (mContentViewer) {
9743 mContentViewer->GetPreviousViewer(getter_AddRefs(zombieViewer));
9746 if (zombieViewer ||
9747 LOAD_TYPE_HAS_FLAGS(aLoadType, LOAD_FLAGS_STOP_CONTENT)) {
9748 rv = Stop(nsIWebNavigation::STOP_ALL);
9749 } else {
9750 rv = Stop(nsIWebNavigation::STOP_NETWORK);
9753 if (NS_FAILED(rv))
9754 return rv;
9757 mLoadType = aLoadType;
9759 // mLSHE should be assigned to aSHEntry, only after Stop() has
9760 // been called. But when loading an error page, do not clear the
9761 // mLSHE for the real page.
9762 if (mLoadType != LOAD_ERROR_PAGE)
9763 SetHistoryEntry(&mLSHE, aSHEntry);
9765 mSavingOldViewer = savePresentation;
9767 // If we have a saved content viewer in history, restore and show it now.
9768 if (aSHEntry && (mLoadType & LOAD_CMD_HISTORY)) {
9769 // Make sure our history ID points to the same ID as
9770 // SHEntry's docshell ID.
9771 aSHEntry->GetDocshellID(&mHistoryID);
9773 // It's possible that the previous viewer of mContentViewer is the
9774 // viewer that will end up in aSHEntry when it gets closed. If that's
9775 // the case, we need to go ahead and force it into its shentry so we
9776 // can restore it.
9777 if (mContentViewer) {
9778 nsCOMPtr<nsIContentViewer> prevViewer;
9779 mContentViewer->GetPreviousViewer(getter_AddRefs(prevViewer));
9780 if (prevViewer) {
9781 #ifdef DEBUG
9782 nsCOMPtr<nsIContentViewer> prevPrevViewer;
9783 prevViewer->GetPreviousViewer(getter_AddRefs(prevPrevViewer));
9784 NS_ASSERTION(!prevPrevViewer, "Should never have viewer chain here");
9785 #endif
9786 nsCOMPtr<nsISHEntry> viewerEntry;
9787 prevViewer->GetHistoryEntry(getter_AddRefs(viewerEntry));
9788 if (viewerEntry == aSHEntry) {
9789 // Make sure this viewer ends up in the right place
9790 mContentViewer->SetPreviousViewer(nullptr);
9791 prevViewer->Destroy();
9795 nsCOMPtr<nsISHEntry> oldEntry = mOSHE;
9796 bool restoring;
9797 rv = RestorePresentation(aSHEntry, &restoring);
9798 if (restoring)
9799 return rv;
9801 // We failed to restore the presentation, so clean up.
9802 // Both the old and new history entries could potentially be in
9803 // an inconsistent state.
9804 if (NS_FAILED(rv)) {
9805 if (oldEntry)
9806 oldEntry->SyncPresentationState();
9808 aSHEntry->SyncPresentationState();
9812 nsAutoString srcdoc;
9813 if (aFlags & INTERNAL_LOAD_FLAGS_IS_SRCDOC)
9814 srcdoc = aSrcdoc;
9815 else
9816 srcdoc = NullString();
9818 mozilla::net::PredictorPredict(aURI, nullptr,
9819 nsINetworkPredictor::PREDICT_LOAD,
9820 this, nullptr);
9822 nsCOMPtr<nsIRequest> req;
9823 rv = DoURILoad(aURI, aReferrer,
9824 !(aFlags & INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER),
9825 owner, aTypeHint, aFileName, aPostData, aHeadersData,
9826 aFirstParty, aDocShell, getter_AddRefs(req),
9827 (aFlags & INTERNAL_LOAD_FLAGS_FIRST_LOAD) != 0,
9828 (aFlags & INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER) != 0,
9829 (aFlags & INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES) != 0,
9830 srcdoc, aBaseURI);
9831 if (req && aRequest)
9832 NS_ADDREF(*aRequest = req);
9834 if (NS_FAILED(rv)) {
9835 nsCOMPtr<nsIChannel> chan(do_QueryInterface(req));
9836 DisplayLoadError(rv, aURI, nullptr, chan);
9839 return rv;
9842 nsIPrincipal*
9843 nsDocShell::GetInheritedPrincipal(bool aConsiderCurrentDocument)
9845 nsCOMPtr<nsIDocument> document;
9846 bool inheritedFromCurrent = false;
9848 if (aConsiderCurrentDocument && mContentViewer) {
9849 document = mContentViewer->GetDocument();
9850 inheritedFromCurrent = true;
9853 if (!document) {
9854 nsCOMPtr<nsIDocShellTreeItem> parentItem;
9855 GetSameTypeParent(getter_AddRefs(parentItem));
9856 if (parentItem) {
9857 document = parentItem->GetDocument();
9861 if (!document) {
9862 if (!aConsiderCurrentDocument) {
9863 return nullptr;
9866 // Make sure we end up with _something_ as the principal no matter
9867 // what.
9868 EnsureContentViewer(); // If this fails, we'll just get a null
9869 // docViewer and bail.
9871 if (!mContentViewer)
9872 return nullptr;
9873 document = mContentViewer->GetDocument();
9876 //-- Get the document's principal
9877 if (document) {
9878 nsIPrincipal *docPrincipal = document->NodePrincipal();
9880 // Don't allow loads in typeContent docShells to inherit the system
9881 // principal from existing documents.
9882 if (inheritedFromCurrent &&
9883 mItemType == typeContent &&
9884 nsContentUtils::IsSystemPrincipal(docPrincipal)) {
9885 return nullptr;
9888 return docPrincipal;
9891 return nullptr;
9894 nsresult
9895 nsDocShell::DoURILoad(nsIURI * aURI,
9896 nsIURI * aReferrerURI,
9897 bool aSendReferrer,
9898 nsISupports * aOwner,
9899 const char * aTypeHint,
9900 const nsAString & aFileName,
9901 nsIInputStream * aPostData,
9902 nsIInputStream * aHeadersData,
9903 bool aFirstParty,
9904 nsIDocShell ** aDocShell,
9905 nsIRequest ** aRequest,
9906 bool aIsNewWindowTarget,
9907 bool aBypassClassifier,
9908 bool aForceAllowCookies,
9909 const nsAString &aSrcdoc,
9910 nsIURI * aBaseURI)
9912 #ifdef MOZ_VISUAL_EVENT_TRACER
9913 nsAutoCString urlSpec;
9914 aURI->GetAsciiSpec(urlSpec);
9915 MOZ_EVENT_TRACER_NAME_OBJECT(this, urlSpec.BeginReading());
9916 MOZ_EVENT_TRACER_EXEC(this, "docshell::pageload");
9917 #endif
9919 nsresult rv;
9920 nsCOMPtr<nsIURILoader> uriLoader;
9922 uriLoader = do_GetService(NS_URI_LOADER_CONTRACTID, &rv);
9923 if (NS_FAILED(rv)) return rv;
9925 nsLoadFlags loadFlags = mDefaultLoadFlags;
9926 if (aFirstParty) {
9927 // tag first party URL loads
9928 loadFlags |= nsIChannel::LOAD_INITIAL_DOCUMENT_URI;
9931 if (mLoadType == LOAD_ERROR_PAGE) {
9932 // Error pages are LOAD_BACKGROUND
9933 loadFlags |= nsIChannel::LOAD_BACKGROUND;
9936 // check for Content Security Policy to pass along with the
9937 // new channel we are creating
9938 nsCOMPtr<nsIChannelPolicy> channelPolicy;
9939 if (IsFrame()) {
9940 // check the parent docshell for a CSP
9941 nsCOMPtr<nsIContentSecurityPolicy> csp;
9942 nsCOMPtr<nsIDocShellTreeItem> parentItem;
9943 GetSameTypeParent(getter_AddRefs(parentItem));
9944 if (parentItem) {
9945 nsCOMPtr<nsIDocument> doc = parentItem->GetDocument();
9946 if (doc) {
9947 rv = doc->NodePrincipal()->GetCsp(getter_AddRefs(csp));
9948 NS_ENSURE_SUCCESS(rv, rv);
9949 if (csp) {
9950 channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
9951 channelPolicy->SetContentSecurityPolicy(csp);
9952 channelPolicy->SetLoadType(nsIContentPolicy::TYPE_SUBDOCUMENT);
9957 // Only allow view-source scheme in top-level docshells. view-source is
9958 // the only scheme to which this applies at the moment due to potential
9959 // timing attacks to read data from cross-origin iframes. If this widens
9960 // we should add a protocol flag for whether the scheme is allowed in
9961 // frames and use something like nsNetUtil::NS_URIChainHasFlags.
9962 nsCOMPtr<nsIURI> tempURI = aURI;
9963 nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(tempURI);
9964 while (nestedURI) {
9965 // view-source should always be an nsINestedURI, loop and check the
9966 // scheme on this and all inner URIs that are also nested URIs.
9967 bool isViewSource = false;
9968 rv = tempURI->SchemeIs("view-source", &isViewSource);
9969 if (NS_FAILED(rv) || isViewSource) {
9970 return NS_ERROR_UNKNOWN_PROTOCOL;
9972 nestedURI->GetInnerURI(getter_AddRefs(tempURI));
9973 nestedURI = do_QueryInterface(tempURI);
9977 // open a channel for the url
9978 nsCOMPtr<nsIChannel> channel;
9980 bool isSrcdoc = !aSrcdoc.IsVoid();
9981 if (!isSrcdoc) {
9982 rv = NS_NewChannel(getter_AddRefs(channel),
9983 aURI,
9984 nullptr,
9985 nullptr,
9986 static_cast<nsIInterfaceRequestor *>(this),
9987 loadFlags,
9988 channelPolicy);
9989 if (NS_FAILED(rv)) {
9990 if (rv == NS_ERROR_UNKNOWN_PROTOCOL) {
9991 // This is a uri with a protocol scheme we don't know how
9992 // to handle. Embedders might still be interested in
9993 // handling the load, though, so we fire a notification
9994 // before throwing the load away.
9995 bool abort = false;
9996 nsresult rv2 = mContentListener->OnStartURIOpen(aURI, &abort);
9997 if (NS_SUCCEEDED(rv2) && abort) {
9998 // Hey, they're handling the load for us! How convenient!
9999 return NS_OK;
10002 return rv;
10004 if (aBaseURI) {
10005 nsCOMPtr<nsIViewSourceChannel> vsc = do_QueryInterface(channel);
10006 if (vsc) {
10007 vsc->SetBaseURI(aBaseURI);
10011 else {
10012 nsAutoCString scheme;
10013 rv = aURI->GetScheme(scheme);
10014 NS_ENSURE_SUCCESS(rv,rv);
10015 bool isViewSource;
10016 aURI->SchemeIs("view-source",&isViewSource);
10018 if (isViewSource) {
10019 nsViewSourceHandler *vsh = nsViewSourceHandler::GetInstance();
10020 NS_ENSURE_TRUE(vsh,NS_ERROR_FAILURE);
10022 rv = vsh->NewSrcdocChannel(aURI, aSrcdoc, aBaseURI,
10023 getter_AddRefs(channel));
10024 NS_ENSURE_SUCCESS(rv, rv);
10026 else {
10027 rv = NS_NewInputStreamChannel(getter_AddRefs(channel),aURI,
10028 aSrcdoc,
10029 NS_LITERAL_CSTRING("text/html"),
10030 true);
10031 NS_ENSURE_SUCCESS(rv, rv);
10032 nsCOMPtr<nsIInputStreamChannel> isc = do_QueryInterface(channel);
10033 MOZ_ASSERT(isc);
10034 isc->SetBaseURI(aBaseURI);
10038 nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
10039 do_QueryInterface(channel);
10040 if (appCacheChannel) {
10041 // Any document load should not inherit application cache.
10042 appCacheChannel->SetInheritApplicationCache(false);
10044 // Loads with the correct permissions should check for a matching
10045 // application cache.
10046 if (GeckoProcessType_Default != XRE_GetProcessType()) {
10047 // Permission will be checked in the parent process
10048 appCacheChannel->SetChooseApplicationCache(true);
10049 } else {
10050 nsCOMPtr<nsIScriptSecurityManager> secMan =
10051 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
10053 if (secMan) {
10054 nsCOMPtr<nsIPrincipal> principal;
10055 secMan->GetDocShellCodebasePrincipal(aURI, this, getter_AddRefs(principal));
10056 appCacheChannel->SetChooseApplicationCache(
10057 NS_ShouldCheckAppCache(principal, mInPrivateBrowsing));
10062 // Make sure to give the caller a channel if we managed to create one
10063 // This is important for correct error page/session history interaction
10064 if (aRequest)
10065 NS_ADDREF(*aRequest = channel);
10067 channel->SetOriginalURI(aURI);
10068 if (aTypeHint && *aTypeHint) {
10069 channel->SetContentType(nsDependentCString(aTypeHint));
10070 mContentTypeHint = aTypeHint;
10071 } else {
10072 mContentTypeHint.Truncate();
10075 if (!aFileName.IsVoid()) {
10076 rv = channel->SetContentDisposition(nsIChannel::DISPOSITION_ATTACHMENT);
10077 NS_ENSURE_SUCCESS(rv, rv);
10078 if (!aFileName.IsEmpty()) {
10079 rv = channel->SetContentDispositionFilename(aFileName);
10080 NS_ENSURE_SUCCESS(rv, rv);
10084 if (mLoadType == LOAD_NORMAL_ALLOW_MIXED_CONTENT ||
10085 mLoadType == LOAD_RELOAD_ALLOW_MIXED_CONTENT) {
10086 rv = SetMixedContentChannel(channel);
10087 NS_ENSURE_SUCCESS(rv, rv);
10088 } else if (mMixedContentChannel) {
10090 * If the user "Disables Protection on This Page", we call
10091 * SetMixedContentChannel for the first time, otherwise
10092 * mMixedContentChannel is still null.
10093 * Later, if the new channel passes a same orign check, we remember the
10094 * users decision by calling SetMixedContentChannel using the new channel.
10095 * This way, the user does not have to click the disable protection button
10096 * over and over for browsing the same site.
10098 rv = nsContentUtils::CheckSameOrigin(mMixedContentChannel, channel);
10099 if (NS_FAILED(rv) || NS_FAILED(SetMixedContentChannel(channel))) {
10100 SetMixedContentChannel(nullptr);
10104 //hack
10105 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
10106 nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal(do_QueryInterface(channel));
10107 if (httpChannelInternal) {
10108 if (aForceAllowCookies) {
10109 httpChannelInternal->SetForceAllowThirdPartyCookie(true);
10111 if (aFirstParty) {
10112 httpChannelInternal->SetDocumentURI(aURI);
10113 } else {
10114 httpChannelInternal->SetDocumentURI(aReferrerURI);
10118 nsCOMPtr<nsIWritablePropertyBag2> props(do_QueryInterface(channel));
10119 if (props)
10121 // save true referrer for those who need it (e.g. xpinstall whitelisting)
10122 // Currently only http and ftp channels support this.
10123 props->SetPropertyAsInterface(NS_LITERAL_STRING("docshell.internalReferrer"),
10124 aReferrerURI);
10128 // If this is a HTTP channel, then set up the HTTP specific information
10129 // (ie. POST data, referrer, ...)
10131 if (httpChannel) {
10132 nsCOMPtr<nsICachingChannel> cacheChannel(do_QueryInterface(httpChannel));
10133 /* Get the cache Key from SH */
10134 nsCOMPtr<nsISupports> cacheKey;
10135 if (mLSHE) {
10136 mLSHE->GetCacheKey(getter_AddRefs(cacheKey));
10138 else if (mOSHE) // for reload cases
10139 mOSHE->GetCacheKey(getter_AddRefs(cacheKey));
10141 // figure out if we need to set the post data stream on the channel...
10142 // right now, this is only done for http channels.....
10143 if (aPostData) {
10144 // XXX it's a bit of a hack to rewind the postdata stream here but
10145 // it has to be done in case the post data is being reused multiple
10146 // times.
10147 nsCOMPtr<nsISeekableStream>
10148 postDataSeekable(do_QueryInterface(aPostData));
10149 if (postDataSeekable) {
10150 rv = postDataSeekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
10151 NS_ENSURE_SUCCESS(rv, rv);
10154 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
10155 NS_ASSERTION(uploadChannel, "http must support nsIUploadChannel");
10157 // we really need to have a content type associated with this stream!!
10158 uploadChannel->SetUploadStream(aPostData, EmptyCString(), -1);
10159 /* If there is a valid postdata *and* it is a History Load,
10160 * set up the cache key on the channel, to retrieve the
10161 * data *only* from the cache. If it is a normal reload, the
10162 * cache is free to go to the server for updated postdata.
10164 if (cacheChannel && cacheKey) {
10165 if (mLoadType == LOAD_HISTORY || mLoadType == LOAD_RELOAD_CHARSET_CHANGE) {
10166 cacheChannel->SetCacheKey(cacheKey);
10167 uint32_t loadFlags;
10168 if (NS_SUCCEEDED(channel->GetLoadFlags(&loadFlags)))
10169 channel->SetLoadFlags(loadFlags | nsICachingChannel::LOAD_ONLY_FROM_CACHE);
10171 else if (mLoadType == LOAD_RELOAD_NORMAL)
10172 cacheChannel->SetCacheKey(cacheKey);
10175 else {
10176 /* If there is no postdata, set the cache key on the channel, and
10177 * do not set the LOAD_ONLY_FROM_CACHE flag, so that the channel
10178 * will be free to get it from net if it is not found in cache.
10179 * New cache may use it creatively on CGI pages with GET
10180 * method and even on those that say "no-cache"
10182 if (mLoadType == LOAD_HISTORY || mLoadType == LOAD_RELOAD_NORMAL
10183 || mLoadType == LOAD_RELOAD_CHARSET_CHANGE) {
10184 if (cacheChannel && cacheKey)
10185 cacheChannel->SetCacheKey(cacheKey);
10188 if (aHeadersData) {
10189 rv = AddHeadersToChannel(aHeadersData, httpChannel);
10191 // Set the referrer explicitly
10192 if (aReferrerURI && aSendReferrer) {
10193 // Referrer is currenly only set for link clicks here.
10194 httpChannel->SetReferrer(aReferrerURI);
10198 nsCOMPtr<nsIPrincipal> ownerPrincipal = do_QueryInterface(aOwner);
10199 nsContentUtils::SetUpChannelOwner(ownerPrincipal, channel, aURI, true,
10200 mSandboxFlags & SANDBOXED_ORIGIN,
10201 isSrcdoc);
10203 nsCOMPtr<nsIScriptChannel> scriptChannel = do_QueryInterface(channel);
10204 if (scriptChannel) {
10205 // Allow execution against our context if the principals match
10206 scriptChannel->
10207 SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
10210 if (aIsNewWindowTarget) {
10211 nsCOMPtr<nsIWritablePropertyBag2> props = do_QueryInterface(channel);
10212 if (props) {
10213 props->SetPropertyAsBool(
10214 NS_LITERAL_STRING("docshell.newWindowTarget"),
10215 true);
10219 nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(channel));
10220 if (timedChannel) {
10221 timedChannel->SetTimingEnabled(true);
10222 if (IsFrame()) {
10223 timedChannel->SetInitiatorType(NS_LITERAL_STRING("subdocument"));
10227 rv = DoChannelLoad(channel, uriLoader, aBypassClassifier);
10230 // If the channel load failed, we failed and nsIWebProgress just ain't
10231 // gonna happen.
10233 if (NS_SUCCEEDED(rv)) {
10234 if (aDocShell) {
10235 *aDocShell = this;
10236 NS_ADDREF(*aDocShell);
10240 return rv;
10243 static NS_METHOD
10244 AppendSegmentToString(nsIInputStream *in,
10245 void *closure,
10246 const char *fromRawSegment,
10247 uint32_t toOffset,
10248 uint32_t count,
10249 uint32_t *writeCount)
10251 // aFromSegment now contains aCount bytes of data.
10253 nsAutoCString *buf = static_cast<nsAutoCString *>(closure);
10254 buf->Append(fromRawSegment, count);
10256 // Indicate that we have consumed all of aFromSegment
10257 *writeCount = count;
10258 return NS_OK;
10261 NS_IMETHODIMP
10262 nsDocShell::AddHeadersToChannel(nsIInputStream *aHeadersData,
10263 nsIChannel *aGenericChannel)
10265 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aGenericChannel);
10266 NS_ENSURE_STATE(httpChannel);
10268 uint32_t numRead;
10269 nsAutoCString headersString;
10270 nsresult rv = aHeadersData->ReadSegments(AppendSegmentToString,
10271 &headersString,
10272 UINT32_MAX,
10273 &numRead);
10274 NS_ENSURE_SUCCESS(rv, rv);
10276 // used during the manipulation of the String from the InputStream
10277 nsAutoCString headerName;
10278 nsAutoCString headerValue;
10279 int32_t crlf;
10280 int32_t colon;
10283 // Iterate over the headersString: for each "\r\n" delimited chunk,
10284 // add the value as a header to the nsIHttpChannel
10287 static const char kWhitespace[] = "\b\t\r\n ";
10288 while (true) {
10289 crlf = headersString.Find("\r\n");
10290 if (crlf == kNotFound)
10291 return NS_OK;
10293 const nsCSubstring &oneHeader = StringHead(headersString, crlf);
10295 colon = oneHeader.FindChar(':');
10296 if (colon == kNotFound)
10297 return NS_ERROR_UNEXPECTED;
10299 headerName = StringHead(oneHeader, colon);
10300 headerValue = Substring(oneHeader, colon + 1);
10302 headerName.Trim(kWhitespace);
10303 headerValue.Trim(kWhitespace);
10305 headersString.Cut(0, crlf + 2);
10308 // FINALLY: we can set the header!
10311 rv = httpChannel->SetRequestHeader(headerName, headerValue, true);
10312 NS_ENSURE_SUCCESS(rv, rv);
10315 NS_NOTREACHED("oops");
10316 return NS_ERROR_UNEXPECTED;
10319 nsresult nsDocShell::DoChannelLoad(nsIChannel * aChannel,
10320 nsIURILoader * aURILoader,
10321 bool aBypassClassifier)
10323 nsresult rv;
10324 // Mark the channel as being a document URI and allow content sniffing...
10325 nsLoadFlags loadFlags = 0;
10326 (void) aChannel->GetLoadFlags(&loadFlags);
10327 loadFlags |= nsIChannel::LOAD_DOCUMENT_URI |
10328 nsIChannel::LOAD_CALL_CONTENT_SNIFFERS;
10330 // Load attributes depend on load type...
10331 switch (mLoadType) {
10332 case LOAD_HISTORY:
10334 // Only send VALIDATE_NEVER if mLSHE's URI was never changed via
10335 // push/replaceState (bug 669671).
10336 bool uriModified = false;
10337 if (mLSHE) {
10338 mLSHE->GetURIWasModified(&uriModified);
10341 if (!uriModified)
10342 loadFlags |= nsIRequest::VALIDATE_NEVER;
10344 break;
10346 case LOAD_RELOAD_CHARSET_CHANGE:
10347 loadFlags |= nsIRequest::LOAD_FROM_CACHE;
10348 break;
10350 case LOAD_RELOAD_NORMAL:
10351 case LOAD_REFRESH:
10352 loadFlags |= nsIRequest::VALIDATE_ALWAYS;
10353 break;
10355 case LOAD_NORMAL_BYPASS_CACHE:
10356 case LOAD_NORMAL_BYPASS_PROXY:
10357 case LOAD_NORMAL_BYPASS_PROXY_AND_CACHE:
10358 case LOAD_NORMAL_ALLOW_MIXED_CONTENT:
10359 case LOAD_RELOAD_BYPASS_CACHE:
10360 case LOAD_RELOAD_BYPASS_PROXY:
10361 case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
10362 case LOAD_RELOAD_ALLOW_MIXED_CONTENT:
10363 case LOAD_REPLACE_BYPASS_CACHE:
10364 loadFlags |= nsIRequest::LOAD_BYPASS_CACHE |
10365 nsIRequest::LOAD_FRESH_CONNECTION;
10366 break;
10368 case LOAD_NORMAL:
10369 case LOAD_LINK:
10370 // Set cache checking flags
10371 switch (Preferences::GetInt("browser.cache.check_doc_frequency", -1)) {
10372 case 0:
10373 loadFlags |= nsIRequest::VALIDATE_ONCE_PER_SESSION;
10374 break;
10375 case 1:
10376 loadFlags |= nsIRequest::VALIDATE_ALWAYS;
10377 break;
10378 case 2:
10379 loadFlags |= nsIRequest::VALIDATE_NEVER;
10380 break;
10382 break;
10385 if (!aBypassClassifier) {
10386 loadFlags |= nsIChannel::LOAD_CLASSIFY_URI;
10389 (void) aChannel->SetLoadFlags(loadFlags);
10391 uint32_t openFlags = 0;
10392 if (mLoadType == LOAD_LINK) {
10393 openFlags |= nsIURILoader::IS_CONTENT_PREFERRED;
10395 if (!mAllowContentRetargeting) {
10396 openFlags |= nsIURILoader::DONT_RETARGET;
10398 rv = aURILoader->OpenURI(aChannel, openFlags, this);
10399 NS_ENSURE_SUCCESS(rv, rv);
10401 return NS_OK;
10404 nsresult
10405 nsDocShell::ScrollToAnchor(nsACString & aCurHash, nsACString & aNewHash,
10406 uint32_t aLoadType)
10408 if (!mCurrentURI) {
10409 return NS_OK;
10412 nsCOMPtr<nsIPresShell> shell = GetPresShell();
10413 if (!shell) {
10414 // If we failed to get the shell, or if there is no shell,
10415 // nothing left to do here.
10416 return NS_OK;
10419 nsIScrollableFrame* rootScroll = shell->GetRootScrollFrameAsScrollable();
10420 if (rootScroll) {
10421 rootScroll->ClearDidHistoryRestore();
10424 // If we have no new anchor, we do not want to scroll, unless there is a
10425 // current anchor and we are doing a history load. So return if we have no
10426 // new anchor, and there is no current anchor or the load is not a history
10427 // load.
10428 if ((aCurHash.IsEmpty() || aLoadType != LOAD_HISTORY) &&
10429 aNewHash.IsEmpty()) {
10430 return NS_OK;
10433 // Take the '#' off aNewHash to get the ref name. (aNewHash might be empty,
10434 // but that's fine.)
10435 nsDependentCSubstring newHashName(aNewHash, 1);
10437 // Both the new and current URIs refer to the same page. We can now
10438 // browse to the hash stored in the new URI.
10440 if (!newHashName.IsEmpty()) {
10441 // anchor is there, but if it's a load from history,
10442 // we don't have any anchor jumping to do
10443 bool scroll = aLoadType != LOAD_HISTORY &&
10444 aLoadType != LOAD_RELOAD_NORMAL;
10446 char *str = ToNewCString(newHashName);
10447 if (!str) {
10448 return NS_ERROR_OUT_OF_MEMORY;
10451 // nsUnescape modifies the string that is passed into it.
10452 nsUnescape(str);
10454 // We assume that the bytes are in UTF-8, as it says in the
10455 // spec:
10456 // http://www.w3.org/TR/html4/appendix/notes.html#h-B.2.1
10458 // We try the UTF-8 string first, and then try the document's
10459 // charset (see below). If the string is not UTF-8,
10460 // conversion will fail and give us an empty Unicode string.
10461 // In that case, we should just fall through to using the
10462 // page's charset.
10463 nsresult rv = NS_ERROR_FAILURE;
10464 NS_ConvertUTF8toUTF16 uStr(str);
10465 if (!uStr.IsEmpty()) {
10466 rv = shell->GoToAnchor(NS_ConvertUTF8toUTF16(str), scroll);
10468 nsMemory::Free(str);
10470 // Above will fail if the anchor name is not UTF-8. Need to
10471 // convert from document charset to unicode.
10472 if (NS_FAILED(rv)) {
10474 // Get a document charset
10475 NS_ENSURE_TRUE(mContentViewer, NS_ERROR_FAILURE);
10476 nsIDocument* doc = mContentViewer->GetDocument();
10477 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
10478 const nsACString &aCharset = doc->GetDocumentCharacterSet();
10480 nsCOMPtr<nsITextToSubURI> textToSubURI =
10481 do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
10482 NS_ENSURE_SUCCESS(rv, rv);
10484 // Unescape and convert to unicode
10485 nsXPIDLString uStr;
10487 rv = textToSubURI->UnEscapeAndConvert(PromiseFlatCString(aCharset).get(),
10488 PromiseFlatCString(newHashName).get(),
10489 getter_Copies(uStr));
10490 NS_ENSURE_SUCCESS(rv, rv);
10492 // Ignore return value of GoToAnchor, since it will return an error
10493 // if there is no such anchor in the document, which is actually a
10494 // success condition for us (we want to update the session history
10495 // with the new URI no matter whether we actually scrolled
10496 // somewhere).
10498 // When newHashName contains "%00", unescaped string may be empty.
10499 // And GoToAnchor asserts if we ask it to scroll to an empty ref.
10500 shell->GoToAnchor(uStr, scroll && !uStr.IsEmpty());
10503 else {
10505 // Tell the shell it's at an anchor, without scrolling.
10506 shell->GoToAnchor(EmptyString(), false);
10508 // An empty anchor was found, but if it's a load from history,
10509 // we don't have to jump to the top of the page. Scrollbar
10510 // position will be restored by the caller, based on positions
10511 // stored in session history.
10512 if (aLoadType == LOAD_HISTORY || aLoadType == LOAD_RELOAD_NORMAL)
10513 return NS_OK;
10514 // An empty anchor. Scroll to the top of the page. Ignore the
10515 // return value; failure to scroll here (e.g. if there is no
10516 // root scrollframe) is not grounds for canceling the load!
10517 SetCurScrollPosEx(0, 0);
10520 return NS_OK;
10523 void
10524 nsDocShell::SetupReferrerFromChannel(nsIChannel * aChannel)
10526 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
10527 if (httpChannel) {
10528 nsCOMPtr<nsIURI> referrer;
10529 nsresult rv = httpChannel->GetReferrer(getter_AddRefs(referrer));
10530 if (NS_SUCCEEDED(rv)) {
10531 SetReferrerURI(referrer);
10536 bool
10537 nsDocShell::OnNewURI(nsIURI * aURI, nsIChannel * aChannel, nsISupports* aOwner,
10538 uint32_t aLoadType, bool aFireOnLocationChange,
10539 bool aAddToGlobalHistory, bool aCloneSHChildren)
10541 NS_PRECONDITION(aURI, "uri is null");
10542 NS_PRECONDITION(!aChannel || !aOwner, "Shouldn't have both set");
10544 #if defined(PR_LOGGING) && defined(DEBUG)
10545 if (PR_LOG_TEST(gDocShellLog, PR_LOG_DEBUG)) {
10546 nsAutoCString spec;
10547 aURI->GetSpec(spec);
10549 nsAutoCString chanName;
10550 if (aChannel)
10551 aChannel->GetName(chanName);
10552 else
10553 chanName.AssignLiteral("<no channel>");
10555 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
10556 ("nsDocShell[%p]::OnNewURI(\"%s\", [%s], 0x%x)\n", this, spec.get(),
10557 chanName.get(), aLoadType));
10559 #endif
10561 bool equalUri = false;
10563 // Get the post data and the HTTP response code from the channel.
10564 uint32_t responseStatus = 0;
10565 nsCOMPtr<nsIInputStream> inputStream;
10566 if (aChannel) {
10567 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
10569 // Check if the HTTPChannel is hiding under a multiPartChannel
10570 if (!httpChannel) {
10571 GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
10574 if (httpChannel) {
10575 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
10576 if (uploadChannel) {
10577 uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
10580 // If the response status indicates an error, unlink this session
10581 // history entry from any entries sharing its document.
10582 nsresult rv = httpChannel->GetResponseStatus(&responseStatus);
10583 if (mLSHE && NS_SUCCEEDED(rv) && responseStatus >= 400) {
10584 mLSHE->AbandonBFCacheEntry();
10589 // Determine if this type of load should update history.
10590 bool updateGHistory = !(aLoadType == LOAD_BYPASS_HISTORY ||
10591 aLoadType == LOAD_ERROR_PAGE ||
10592 aLoadType & LOAD_CMD_HISTORY);
10594 // We don't update session history on reload.
10595 bool updateSHistory = updateGHistory && (!(aLoadType & LOAD_CMD_RELOAD));
10597 /* Create SH Entry (mLSHE) only if there is a SessionHistory object (mSessionHistory) in
10598 * the current frame or in the root docshell
10600 nsCOMPtr<nsISHistory> rootSH = mSessionHistory;
10601 if (!rootSH) {
10602 // Get the handle to SH from the root docshell
10603 GetRootSessionHistory(getter_AddRefs(rootSH));
10604 if (!rootSH) {
10605 updateSHistory = false;
10606 updateGHistory = false; // XXX Why global history too?
10608 } // rootSH
10610 // Check if the url to be loaded is the same as the one already loaded.
10611 if (mCurrentURI)
10612 aURI->Equals(mCurrentURI, &equalUri);
10614 #ifdef DEBUG
10615 bool shAvailable = (rootSH != nullptr);
10617 // XXX This log message is almost useless because |updateSHistory|
10618 // and |updateGHistory| are not correct at this point.
10620 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
10621 (" shAvailable=%i updateSHistory=%i updateGHistory=%i"
10622 " equalURI=%i\n",
10623 shAvailable, updateSHistory, updateGHistory, equalUri));
10625 if (shAvailable && mCurrentURI && !mOSHE && aLoadType != LOAD_ERROR_PAGE) {
10626 NS_ASSERTION(NS_IsAboutBlank(mCurrentURI), "no SHEntry for a non-transient viewer?");
10628 #endif
10630 /* If the url to be loaded is the same as the one already there,
10631 * and the original loadType is LOAD_NORMAL, LOAD_LINK, or
10632 * LOAD_STOP_CONTENT, set loadType to LOAD_NORMAL_REPLACE so that
10633 * AddToSessionHistory() won't mess with the current SHEntry and
10634 * if this page has any frame children, it also will be handled
10635 * properly. see bug 83684
10637 * NB: If mOSHE is null but we have a current URI, then it means
10638 * that we must be at the transient about:blank content viewer
10639 * (asserted above) and we should let the normal load continue,
10640 * since there's nothing to replace.
10642 * XXX Hopefully changing the loadType at this time will not hurt
10643 * anywhere. The other way to take care of sequentially repeating
10644 * frameset pages is to add new methods to nsIDocShellTreeItem.
10645 * Hopefully I don't have to do that.
10647 if (equalUri &&
10648 mOSHE &&
10649 (mLoadType == LOAD_NORMAL ||
10650 mLoadType == LOAD_LINK ||
10651 mLoadType == LOAD_STOP_CONTENT) &&
10652 !inputStream)
10654 mLoadType = LOAD_NORMAL_REPLACE;
10657 // If this is a refresh to the currently loaded url, we don't
10658 // have to update session or global history.
10659 if (mLoadType == LOAD_REFRESH && !inputStream && equalUri) {
10660 SetHistoryEntry(&mLSHE, mOSHE);
10663 /* If the user pressed shift-reload, cache will create a new cache key
10664 * for the page. Save the new cacheKey in Session History.
10665 * see bug 90098
10667 if (aChannel &&
10668 (aLoadType == LOAD_RELOAD_BYPASS_CACHE ||
10669 aLoadType == LOAD_RELOAD_BYPASS_PROXY ||
10670 aLoadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE ||
10671 aLoadType == LOAD_RELOAD_ALLOW_MIXED_CONTENT)) {
10672 NS_ASSERTION(!updateSHistory,
10673 "We shouldn't be updating session history for forced"
10674 " reloads!");
10676 nsCOMPtr<nsICachingChannel> cacheChannel(do_QueryInterface(aChannel));
10677 nsCOMPtr<nsISupports> cacheKey;
10678 // Get the Cache Key and store it in SH.
10679 if (cacheChannel)
10680 cacheChannel->GetCacheKey(getter_AddRefs(cacheKey));
10681 // If we already have a loading history entry, store the new cache key
10682 // in it. Otherwise, since we're doing a reload and won't be updating
10683 // our history entry, store the cache key in our current history entry.
10684 if (mLSHE)
10685 mLSHE->SetCacheKey(cacheKey);
10686 else if (mOSHE)
10687 mOSHE->SetCacheKey(cacheKey);
10689 // Since we're force-reloading, clear all the sub frame history.
10690 ClearFrameHistory(mLSHE);
10691 ClearFrameHistory(mOSHE);
10694 if (aLoadType == LOAD_RELOAD_NORMAL) {
10695 nsCOMPtr<nsISHEntry> currentSH;
10696 bool oshe = false;
10697 GetCurrentSHEntry(getter_AddRefs(currentSH), &oshe);
10698 bool dynamicallyAddedChild = false;
10699 if (currentSH) {
10700 currentSH->HasDynamicallyAddedChild(&dynamicallyAddedChild);
10702 if (dynamicallyAddedChild) {
10703 ClearFrameHistory(currentSH);
10707 if (aLoadType == LOAD_REFRESH) {
10708 ClearFrameHistory(mLSHE);
10709 ClearFrameHistory(mOSHE);
10712 if (updateSHistory) {
10713 // Update session history if necessary...
10714 if (!mLSHE && (mItemType == typeContent) && mURIResultedInDocument) {
10715 /* This is a fresh page getting loaded for the first time
10716 *.Create a Entry for it and add it to SH, if this is the
10717 * rootDocShell
10719 (void) AddToSessionHistory(aURI, aChannel, aOwner, aCloneSHChildren,
10720 getter_AddRefs(mLSHE));
10724 // If this is a POST request, we do not want to include this in global
10725 // history.
10726 if (updateGHistory &&
10727 aAddToGlobalHistory &&
10728 !ChannelIsPost(aChannel)) {
10729 nsCOMPtr<nsIURI> previousURI;
10730 uint32_t previousFlags = 0;
10732 if (aLoadType & LOAD_CMD_RELOAD) {
10733 // On a reload request, we don't set redirecting flags.
10734 previousURI = aURI;
10735 } else {
10736 ExtractLastVisit(aChannel, getter_AddRefs(previousURI),
10737 &previousFlags);
10740 // Note: We don't use |referrer| when our global history is
10741 // based on IHistory.
10742 nsCOMPtr<nsIURI> referrer;
10743 // Treat referrer as null if there is an error getting it.
10744 (void)NS_GetReferrerFromChannel(aChannel, getter_AddRefs(referrer));
10746 AddURIVisit(aURI, referrer, previousURI, previousFlags, responseStatus);
10749 // If this was a history load or a refresh,
10750 // update the index in SH.
10751 if (rootSH && (mLoadType & (LOAD_CMD_HISTORY | LOAD_CMD_RELOAD))) {
10752 nsCOMPtr<nsISHistoryInternal> shInternal(do_QueryInterface(rootSH));
10753 if (shInternal) {
10754 rootSH->GetIndex(&mPreviousTransIndex);
10755 shInternal->UpdateIndex();
10756 rootSH->GetIndex(&mLoadedTransIndex);
10757 #ifdef DEBUG_PAGE_CACHE
10758 printf("Previous index: %d, Loaded index: %d\n\n",
10759 mPreviousTransIndex, mLoadedTransIndex);
10760 #endif
10764 // aCloneSHChildren exactly means "we are not loading a new document".
10765 uint32_t locationFlags = aCloneSHChildren?
10766 uint32_t(LOCATION_CHANGE_SAME_DOCUMENT) : 0;
10768 bool onLocationChangeNeeded = SetCurrentURI(aURI, aChannel,
10769 aFireOnLocationChange,
10770 locationFlags);
10771 // Make sure to store the referrer from the channel, if any
10772 SetupReferrerFromChannel(aChannel);
10773 return onLocationChangeNeeded;
10776 bool
10777 nsDocShell::OnLoadingSite(nsIChannel * aChannel, bool aFireOnLocationChange,
10778 bool aAddToGlobalHistory)
10780 nsCOMPtr<nsIURI> uri;
10781 // If this a redirect, use the final url (uri)
10782 // else use the original url
10784 // Note that this should match what documents do (see nsDocument::Reset).
10785 NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
10786 NS_ENSURE_TRUE(uri, false);
10788 // Pass false for aCloneSHChildren, since we're loading a new page here.
10789 return OnNewURI(uri, aChannel, nullptr, mLoadType, aFireOnLocationChange,
10790 aAddToGlobalHistory, false);
10794 void
10795 nsDocShell::SetReferrerURI(nsIURI * aURI)
10797 mReferrerURI = aURI; // This assigment addrefs
10800 //*****************************************************************************
10801 // nsDocShell: Session History
10802 //*****************************************************************************
10804 NS_IMETHODIMP
10805 nsDocShell::AddState(JS::Handle<JS::Value> aData, const nsAString& aTitle,
10806 const nsAString& aURL, bool aReplace, JSContext* aCx)
10808 // Implements History.pushState and History.replaceState
10810 // Here's what we do, roughly in the order specified by HTML5:
10811 // 1. Serialize aData using structured clone.
10812 // 2. If the third argument is present,
10813 // a. Resolve the url, relative to the first script's base URL
10814 // b. If (a) fails, raise a SECURITY_ERR
10815 // c. Compare the resulting absolute URL to the document's address. If
10816 // any part of the URLs difer other than the <path>, <query>, and
10817 // <fragment> components, raise a SECURITY_ERR and abort.
10818 // 3. If !aReplace:
10819 // Remove from the session history all entries after the current entry,
10820 // as we would after a regular navigation, and save the current
10821 // entry's scroll position (bug 590573).
10822 // 4. As apropriate, either add a state object entry to the session history
10823 // after the current entry with the following properties, or modify the
10824 // current session history entry to set
10825 // a. cloned data as the state object,
10826 // b. if the third argument was present, the absolute URL found in
10827 // step 2
10828 // Also clear the new history entry's POST data (see bug 580069).
10829 // 5. If aReplace is false (i.e. we're doing a pushState instead of a
10830 // replaceState), notify bfcache that we've navigated to a new page.
10831 // 6. If the third argument is present, set the document's current address
10832 // to the absolute URL found in step 2.
10834 // It's important that this function not run arbitrary scripts after step 1
10835 // and before completing step 5. For example, if a script called
10836 // history.back() before we completed step 5, bfcache might destroy an
10837 // active content viewer. Since EvictOutOfRangeContentViewers at the end of
10838 // step 5 might run script, we can't just put a script blocker around the
10839 // critical section.
10841 // Note that we completely ignore the aTitle parameter.
10843 nsresult rv;
10845 // Don't clobber the load type of an existing network load.
10846 AutoRestore<uint32_t> loadTypeResetter(mLoadType);
10848 // pushState effectively becomes replaceState when we've started a network
10849 // load but haven't adopted its document yet. This mirrors what we do with
10850 // changes to the hash at this stage of the game.
10851 if (JustStartedNetworkLoad()) {
10852 aReplace = true;
10855 nsCOMPtr<nsIDocument> document = GetDocument();
10856 NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
10858 // Step 1: Serialize aData using structured clone.
10859 nsCOMPtr<nsIStructuredCloneContainer> scContainer;
10861 // scContainer->Init might cause arbitrary JS to run, and this code might
10862 // navigate the page we're on, potentially to a different origin! (bug
10863 // 634834) To protect against this, we abort if our principal changes due
10864 // to the InitFromJSVal() call.
10866 nsCOMPtr<nsIDocument> origDocument = GetDocument();
10867 if (!origDocument)
10868 return NS_ERROR_DOM_SECURITY_ERR;
10869 nsCOMPtr<nsIPrincipal> origPrincipal = origDocument->NodePrincipal();
10871 scContainer = new nsStructuredCloneContainer();
10872 rv = scContainer->InitFromJSVal(aData);
10873 NS_ENSURE_SUCCESS(rv, rv);
10875 nsCOMPtr<nsIDocument> newDocument = GetDocument();
10876 if (!newDocument)
10877 return NS_ERROR_DOM_SECURITY_ERR;
10878 nsCOMPtr<nsIPrincipal> newPrincipal = newDocument->NodePrincipal();
10880 bool principalsEqual = false;
10881 origPrincipal->Equals(newPrincipal, &principalsEqual);
10882 NS_ENSURE_TRUE(principalsEqual, NS_ERROR_DOM_SECURITY_ERR);
10885 // Check that the state object isn't too long.
10886 // Default max length: 640k bytes.
10887 int32_t maxStateObjSize =
10888 Preferences::GetInt("browser.history.maxStateObjectSize", 0xA0000);
10889 if (maxStateObjSize < 0) {
10890 maxStateObjSize = 0;
10893 uint64_t scSize;
10894 rv = scContainer->GetSerializedNBytes(&scSize);
10895 NS_ENSURE_SUCCESS(rv, rv);
10897 NS_ENSURE_TRUE(scSize <= (uint32_t)maxStateObjSize,
10898 NS_ERROR_ILLEGAL_VALUE);
10900 // Step 2: Resolve aURL
10901 bool equalURIs = true;
10902 nsCOMPtr<nsIURI> currentURI;
10903 if (sURIFixup && mCurrentURI) {
10904 rv = sURIFixup->CreateExposableURI(mCurrentURI,
10905 getter_AddRefs(currentURI));
10906 NS_ENSURE_SUCCESS(rv, rv);
10907 } else {
10908 currentURI = mCurrentURI;
10910 nsCOMPtr<nsIURI> oldURI = currentURI;
10911 nsCOMPtr<nsIURI> newURI;
10912 if (aURL.Length() == 0) {
10913 newURI = currentURI;
10915 else {
10916 // 2a: Resolve aURL relative to mURI
10918 nsIURI* docBaseURI = document->GetDocBaseURI();
10919 if (!docBaseURI)
10920 return NS_ERROR_FAILURE;
10922 nsAutoCString spec;
10923 docBaseURI->GetSpec(spec);
10925 nsAutoCString charset;
10926 rv = docBaseURI->GetOriginCharset(charset);
10927 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
10929 rv = NS_NewURI(getter_AddRefs(newURI), aURL,
10930 charset.get(), docBaseURI);
10932 // 2b: If 2a fails, raise a SECURITY_ERR
10933 if (NS_FAILED(rv)) {
10934 return NS_ERROR_DOM_SECURITY_ERR;
10937 // 2c: Same-origin check.
10938 if (!nsContentUtils::URIIsLocalFile(newURI)) {
10939 // In addition to checking that the security manager says that
10940 // the new URI has the same origin as our current URI, we also
10941 // check that the two URIs have the same userpass. (The
10942 // security manager says that |http://foo.com| and
10943 // |http://me@foo.com| have the same origin.) currentURI
10944 // won't contain the password part of the userpass, so this
10945 // means that it's never valid to specify a password in a
10946 // pushState or replaceState URI.
10948 nsCOMPtr<nsIScriptSecurityManager> secMan =
10949 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
10950 NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE);
10952 // It's very important that we check that newURI is of the same
10953 // origin as currentURI, not docBaseURI, because a page can
10954 // set docBaseURI arbitrarily to any domain.
10955 nsAutoCString currentUserPass, newUserPass;
10956 NS_ENSURE_SUCCESS(currentURI->GetUserPass(currentUserPass),
10957 NS_ERROR_FAILURE);
10958 NS_ENSURE_SUCCESS(newURI->GetUserPass(newUserPass),
10959 NS_ERROR_FAILURE);
10960 if (NS_FAILED(secMan->CheckSameOriginURI(currentURI,
10961 newURI, true)) ||
10962 !currentUserPass.Equals(newUserPass)) {
10964 return NS_ERROR_DOM_SECURITY_ERR;
10967 else {
10968 // It's a file:// URI
10969 nsCOMPtr<nsIScriptObjectPrincipal> docScriptObj =
10970 do_QueryInterface(document);
10972 if (!docScriptObj) {
10973 return NS_ERROR_DOM_SECURITY_ERR;
10976 nsCOMPtr<nsIPrincipal> principal = docScriptObj->GetPrincipal();
10978 if (!principal ||
10979 NS_FAILED(principal->CheckMayLoad(newURI, true, false))) {
10981 return NS_ERROR_DOM_SECURITY_ERR;
10985 if (currentURI) {
10986 currentURI->Equals(newURI, &equalURIs);
10988 else {
10989 equalURIs = false;
10992 } // end of same-origin check
10994 // Step 3: Create a new entry in the session history. This will erase
10995 // all SHEntries after the new entry and make this entry the current
10996 // one. This operation may modify mOSHE, which we need later, so we
10997 // keep a reference here.
10998 NS_ENSURE_TRUE(mOSHE, NS_ERROR_FAILURE);
10999 nsCOMPtr<nsISHEntry> oldOSHE = mOSHE;
11001 mLoadType = LOAD_PUSHSTATE;
11003 nsCOMPtr<nsISHEntry> newSHEntry;
11004 if (!aReplace) {
11005 // Save the current scroll position (bug 590573).
11006 nscoord cx = 0, cy = 0;
11007 GetCurScrollPos(ScrollOrientation_X, &cx);
11008 GetCurScrollPos(ScrollOrientation_Y, &cy);
11009 mOSHE->SetScrollPosition(cx, cy);
11011 // Since we're not changing which page we have loaded, pass
11012 // true for aCloneChildren.
11013 rv = AddToSessionHistory(newURI, nullptr, nullptr, true,
11014 getter_AddRefs(newSHEntry));
11015 NS_ENSURE_SUCCESS(rv, rv);
11017 NS_ENSURE_TRUE(newSHEntry, NS_ERROR_FAILURE);
11019 // Link the new SHEntry to the old SHEntry's BFCache entry, since the
11020 // two entries correspond to the same document.
11021 NS_ENSURE_SUCCESS(newSHEntry->AdoptBFCacheEntry(oldOSHE),
11022 NS_ERROR_FAILURE);
11024 // Set the new SHEntry's title (bug 655273).
11025 nsString title;
11026 mOSHE->GetTitle(getter_Copies(title));
11027 newSHEntry->SetTitle(title);
11029 // AddToSessionHistory may not modify mOSHE. In case it doesn't,
11030 // we'll just set mOSHE here.
11031 mOSHE = newSHEntry;
11033 } else {
11034 newSHEntry = mOSHE;
11035 newSHEntry->SetURI(newURI);
11038 // Step 4: Modify new/original session history entry and clear its POST
11039 // data, if there is any.
11040 newSHEntry->SetStateData(scContainer);
11041 newSHEntry->SetPostData(nullptr);
11043 // If this push/replaceState changed the document's current URI and the new
11044 // URI differs from the old URI in more than the hash, or if the old
11045 // SHEntry's URI was modified in this way by a push/replaceState call
11046 // set URIWasModified to true for the current SHEntry (bug 669671).
11047 bool sameExceptHashes = true, oldURIWasModified = false;
11048 newURI->EqualsExceptRef(currentURI, &sameExceptHashes);
11049 oldOSHE->GetURIWasModified(&oldURIWasModified);
11050 newSHEntry->SetURIWasModified(!sameExceptHashes || oldURIWasModified);
11052 // Step 5: If aReplace is false, indicating that we're doing a pushState
11053 // rather than a replaceState, notify bfcache that we've added a page to
11054 // the history so it can evict content viewers if appropriate. Otherwise
11055 // call ReplaceEntry so that we notify nsIHistoryListeners that an entry
11056 // was replaced.
11057 nsCOMPtr<nsISHistory> rootSH;
11058 GetRootSessionHistory(getter_AddRefs(rootSH));
11059 NS_ENSURE_TRUE(rootSH, NS_ERROR_UNEXPECTED);
11061 nsCOMPtr<nsISHistoryInternal> internalSH =
11062 do_QueryInterface(rootSH);
11063 NS_ENSURE_TRUE(internalSH, NS_ERROR_UNEXPECTED);
11065 if (!aReplace) {
11066 int32_t curIndex = -1;
11067 rv = rootSH->GetIndex(&curIndex);
11068 if (NS_SUCCEEDED(rv) && curIndex > -1) {
11069 internalSH->EvictOutOfRangeContentViewers(curIndex);
11071 } else {
11072 nsCOMPtr<nsISHEntry> rootSHEntry = GetRootSHEntry(newSHEntry);
11074 int32_t index = -1;
11075 rv = rootSH->GetIndexOfEntry(rootSHEntry, &index);
11076 if (NS_SUCCEEDED(rv) && index > -1) {
11077 internalSH->ReplaceEntry(index, rootSHEntry);
11081 // Step 6: If the document's URI changed, update document's URI and update
11082 // global history.
11084 // We need to call FireOnLocationChange so that the browser's address bar
11085 // gets updated and the back button is enabled, but we only need to
11086 // explicitly call FireOnLocationChange if we're not calling SetCurrentURI,
11087 // since SetCurrentURI will call FireOnLocationChange for us.
11089 // Both SetCurrentURI(...) and FireDummyOnLocationChange() pass
11090 // nullptr for aRequest param to FireOnLocationChange(...). Such an update
11091 // notification is allowed only when we know docshell is not loading a new
11092 // document and it requires LOCATION_CHANGE_SAME_DOCUMENT flag. Otherwise,
11093 // FireOnLocationChange(...) breaks security UI.
11094 if (!equalURIs) {
11095 SetCurrentURI(newURI, nullptr, true, LOCATION_CHANGE_SAME_DOCUMENT);
11096 document->SetDocumentURI(newURI);
11098 AddURIVisit(newURI, oldURI, oldURI, 0);
11100 // AddURIVisit doesn't set the title for the new URI in global history,
11101 // so do that here.
11102 if (mUseGlobalHistory && !mInPrivateBrowsing) {
11103 nsCOMPtr<IHistory> history = services::GetHistoryService();
11104 if (history) {
11105 history->SetURITitle(newURI, mTitle);
11107 else if (mGlobalHistory) {
11108 mGlobalHistory->SetPageTitle(newURI, mTitle);
11112 // Inform the favicon service that our old favicon applies to this new
11113 // URI.
11114 CopyFavicon(oldURI, newURI, mInPrivateBrowsing);
11116 else {
11117 FireDummyOnLocationChange();
11119 document->SetStateObject(scContainer);
11121 return NS_OK;
11124 bool
11125 nsDocShell::ShouldAddToSessionHistory(nsIURI * aURI)
11127 // I believe none of the about: urls should go in the history. But then
11128 // that could just be me... If the intent is only deny about:blank then we
11129 // should just do a spec compare, rather than two gets of the scheme and
11130 // then the path. -Gagan
11131 nsresult rv;
11132 nsAutoCString buf, pref;
11134 rv = aURI->GetScheme(buf);
11135 if (NS_FAILED(rv))
11136 return false;
11138 if (buf.EqualsLiteral("about")) {
11139 rv = aURI->GetPath(buf);
11140 if (NS_FAILED(rv))
11141 return false;
11143 if (buf.EqualsLiteral("blank")) {
11144 return false;
11148 rv = Preferences::GetDefaultCString("browser.newtab.url", &pref);
11150 if (NS_FAILED(rv)) {
11151 return true;
11154 rv = aURI->GetSpec(buf);
11155 NS_ENSURE_SUCCESS(rv, true);
11157 return !buf.Equals(pref);
11160 nsresult
11161 nsDocShell::AddToSessionHistory(nsIURI * aURI, nsIChannel * aChannel,
11162 nsISupports* aOwner, bool aCloneChildren,
11163 nsISHEntry ** aNewEntry)
11165 NS_PRECONDITION(aURI, "uri is null");
11166 NS_PRECONDITION(!aChannel || !aOwner, "Shouldn't have both set");
11168 #if defined(PR_LOGGING) && defined(DEBUG)
11169 if (PR_LOG_TEST(gDocShellLog, PR_LOG_DEBUG)) {
11170 nsAutoCString spec;
11171 aURI->GetSpec(spec);
11173 nsAutoCString chanName;
11174 if (aChannel)
11175 aChannel->GetName(chanName);
11176 else
11177 chanName.AssignLiteral("<no channel>");
11179 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
11180 ("nsDocShell[%p]::AddToSessionHistory(\"%s\", [%s])\n", this, spec.get(),
11181 chanName.get()));
11183 #endif
11185 nsresult rv = NS_OK;
11186 nsCOMPtr<nsISHEntry> entry;
11187 bool shouldPersist;
11189 shouldPersist = ShouldAddToSessionHistory(aURI);
11191 // Get a handle to the root docshell
11192 nsCOMPtr<nsIDocShellTreeItem> root;
11193 GetSameTypeRootTreeItem(getter_AddRefs(root));
11195 * If this is a LOAD_FLAGS_REPLACE_HISTORY in a subframe, we use
11196 * the existing SH entry in the page and replace the url and
11197 * other vitalities.
11199 if (LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY) &&
11200 root != static_cast<nsIDocShellTreeItem *>(this)) {
11201 // This is a subframe
11202 entry = mOSHE;
11203 nsCOMPtr<nsISHContainer> shContainer(do_QueryInterface(entry));
11204 if (shContainer) {
11205 int32_t childCount = 0;
11206 shContainer->GetChildCount(&childCount);
11207 // Remove all children of this entry
11208 for (int32_t i = childCount - 1; i >= 0; i--) {
11209 nsCOMPtr<nsISHEntry> child;
11210 shContainer->GetChildAt(i, getter_AddRefs(child));
11211 shContainer->RemoveChild(child);
11212 } // for
11213 entry->AbandonBFCacheEntry();
11214 } // shContainer
11217 // Create a new entry if necessary.
11218 if (!entry) {
11219 entry = do_CreateInstance(NS_SHENTRY_CONTRACTID);
11221 if (!entry) {
11222 return NS_ERROR_OUT_OF_MEMORY;
11226 // Get the post data & referrer
11227 nsCOMPtr<nsIInputStream> inputStream;
11228 nsCOMPtr<nsIURI> referrerURI;
11229 nsCOMPtr<nsISupports> cacheKey;
11230 nsCOMPtr<nsISupports> owner = aOwner;
11231 bool expired = false;
11232 bool discardLayoutState = false;
11233 nsCOMPtr<nsICachingChannel> cacheChannel;
11234 if (aChannel) {
11235 cacheChannel = do_QueryInterface(aChannel);
11237 /* If there is a caching channel, get the Cache Key and store it
11238 * in SH.
11240 if (cacheChannel) {
11241 cacheChannel->GetCacheKey(getter_AddRefs(cacheKey));
11243 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
11245 // Check if the httpChannel is hiding under a multipartChannel
11246 if (!httpChannel) {
11247 GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
11249 if (httpChannel) {
11250 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
11251 if (uploadChannel) {
11252 uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
11254 httpChannel->GetReferrer(getter_AddRefs(referrerURI));
11256 discardLayoutState = ShouldDiscardLayoutState(httpChannel);
11258 aChannel->GetOwner(getter_AddRefs(owner));
11259 if (!owner) {
11260 nsCOMPtr<nsILoadInfo> loadInfo;
11261 aChannel->GetLoadInfo(getter_AddRefs(loadInfo));
11262 if (loadInfo) {
11263 // For now keep storing just the principal in the SHEntry.
11264 if (loadInfo->GetLoadingSandboxed()) {
11265 owner = nsNullPrincipal::CreateWithInheritedAttributes(loadInfo->LoadingPrincipal());
11266 NS_ENSURE_TRUE(owner, NS_ERROR_FAILURE);
11267 } else if (loadInfo->GetForceInheritPrincipal()) {
11268 owner = loadInfo->LoadingPrincipal();
11274 //Title is set in nsDocShell::SetTitle()
11275 entry->Create(aURI, // uri
11276 EmptyString(), // Title
11277 inputStream, // Post data stream
11278 nullptr, // LayoutHistory state
11279 cacheKey, // CacheKey
11280 mContentTypeHint, // Content-type
11281 owner, // Channel or provided owner
11282 mHistoryID,
11283 mDynamicallyCreated);
11284 entry->SetReferrerURI(referrerURI);
11285 nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(aChannel);
11286 if (inStrmChan) {
11287 bool isSrcdocChannel;
11288 inStrmChan->GetIsSrcdocChannel(&isSrcdocChannel);
11289 if (isSrcdocChannel) {
11290 nsAutoString srcdoc;
11291 inStrmChan->GetSrcdocData(srcdoc);
11292 entry->SetSrcdocData(srcdoc);
11293 nsCOMPtr<nsIURI> baseURI;
11294 inStrmChan->GetBaseURI(getter_AddRefs(baseURI));
11295 entry->SetBaseURI(baseURI);
11298 /* If cache got a 'no-store', ask SH not to store
11299 * HistoryLayoutState. By default, SH will set this
11300 * flag to true and save HistoryLayoutState.
11302 if (discardLayoutState) {
11303 entry->SetSaveLayoutStateFlag(false);
11305 if (cacheChannel) {
11306 // Check if the page has expired from cache
11307 uint32_t expTime = 0;
11308 cacheChannel->GetCacheTokenExpirationTime(&expTime);
11309 uint32_t now = PRTimeToSeconds(PR_Now());
11310 if (expTime <= now)
11311 expired = true;
11313 if (expired)
11314 entry->SetExpirationStatus(true);
11317 if (root == static_cast<nsIDocShellTreeItem *>(this) && mSessionHistory) {
11318 // If we need to clone our children onto the new session
11319 // history entry, do so now.
11320 if (aCloneChildren && mOSHE) {
11321 uint32_t cloneID;
11322 mOSHE->GetID(&cloneID);
11323 nsCOMPtr<nsISHEntry> newEntry;
11324 CloneAndReplace(mOSHE, this, cloneID, entry, true, getter_AddRefs(newEntry));
11325 NS_ASSERTION(entry == newEntry, "The new session history should be in the new entry");
11328 // This is the root docshell
11329 if (LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
11330 // Replace current entry in session history.
11331 int32_t index = 0;
11332 mSessionHistory->GetIndex(&index);
11333 nsCOMPtr<nsISHistoryInternal> shPrivate(do_QueryInterface(mSessionHistory));
11334 // Replace the current entry with the new entry
11335 if (shPrivate)
11336 rv = shPrivate->ReplaceEntry(index, entry);
11338 else {
11339 // Add to session history
11340 nsCOMPtr<nsISHistoryInternal>
11341 shPrivate(do_QueryInterface(mSessionHistory));
11342 NS_ENSURE_TRUE(shPrivate, NS_ERROR_FAILURE);
11343 mSessionHistory->GetIndex(&mPreviousTransIndex);
11344 rv = shPrivate->AddEntry(entry, shouldPersist);
11345 mSessionHistory->GetIndex(&mLoadedTransIndex);
11346 #ifdef DEBUG_PAGE_CACHE
11347 printf("Previous index: %d, Loaded index: %d\n\n",
11348 mPreviousTransIndex, mLoadedTransIndex);
11349 #endif
11352 else {
11353 // This is a subframe.
11354 if (!mOSHE || !LOAD_TYPE_HAS_FLAGS(mLoadType,
11355 LOAD_FLAGS_REPLACE_HISTORY))
11356 rv = DoAddChildSHEntry(entry, mChildOffset, aCloneChildren);
11359 // Return the new SH entry...
11360 if (aNewEntry) {
11361 *aNewEntry = nullptr;
11362 if (NS_SUCCEEDED(rv)) {
11363 *aNewEntry = entry;
11364 NS_ADDREF(*aNewEntry);
11368 return rv;
11372 NS_IMETHODIMP
11373 nsDocShell::LoadHistoryEntry(nsISHEntry * aEntry, uint32_t aLoadType)
11375 if (!IsNavigationAllowed()) {
11376 return NS_OK;
11379 nsCOMPtr<nsIURI> uri;
11380 nsCOMPtr<nsIInputStream> postData;
11381 nsCOMPtr<nsIURI> referrerURI;
11382 nsAutoCString contentType;
11383 nsCOMPtr<nsISupports> owner;
11385 NS_ENSURE_TRUE(aEntry, NS_ERROR_FAILURE);
11387 NS_ENSURE_SUCCESS(aEntry->GetURI(getter_AddRefs(uri)), NS_ERROR_FAILURE);
11388 NS_ENSURE_SUCCESS(aEntry->GetReferrerURI(getter_AddRefs(referrerURI)),
11389 NS_ERROR_FAILURE);
11390 NS_ENSURE_SUCCESS(aEntry->GetPostData(getter_AddRefs(postData)),
11391 NS_ERROR_FAILURE);
11392 NS_ENSURE_SUCCESS(aEntry->GetContentType(contentType), NS_ERROR_FAILURE);
11393 NS_ENSURE_SUCCESS(aEntry->GetOwner(getter_AddRefs(owner)),
11394 NS_ERROR_FAILURE);
11396 // Calling CreateAboutBlankContentViewer can set mOSHE to null, and if
11397 // that's the only thing holding a ref to aEntry that will cause aEntry to
11398 // die while we're loading it. So hold a strong ref to aEntry here, just
11399 // in case.
11400 nsCOMPtr<nsISHEntry> kungFuDeathGrip(aEntry);
11401 bool isJS;
11402 nsresult rv = uri->SchemeIs("javascript", &isJS);
11403 if (NS_FAILED(rv) || isJS) {
11404 // We're loading a URL that will execute script from inside asyncOpen.
11405 // Replace the current document with about:blank now to prevent
11406 // anything from the current document from leaking into any JavaScript
11407 // code in the URL.
11408 nsCOMPtr<nsIPrincipal> prin = do_QueryInterface(owner);
11409 // Don't cache the presentation if we're going to just reload the
11410 // current entry. Caching would lead to trying to save the different
11411 // content viewers in the same nsISHEntry object.
11412 rv = CreateAboutBlankContentViewer(prin, nullptr, aEntry != mOSHE);
11414 if (NS_FAILED(rv)) {
11415 // The creation of the intermittent about:blank content
11416 // viewer failed for some reason (potentially because the
11417 // user prevented it). Interrupt the history load.
11418 return NS_OK;
11421 if (!owner) {
11422 // Ensure that we have an owner. Otherwise javascript: URIs will
11423 // pick it up from the about:blank page we just loaded, and we
11424 // don't really want even that in this case.
11425 owner = do_CreateInstance("@mozilla.org/nullprincipal;1");
11426 NS_ENSURE_TRUE(owner, NS_ERROR_OUT_OF_MEMORY);
11430 /* If there is a valid postdata *and* the user pressed
11431 * reload or shift-reload, take user's permission before we
11432 * repost the data to the server.
11434 if ((aLoadType & LOAD_CMD_RELOAD) && postData) {
11435 bool repost;
11436 rv = ConfirmRepost(&repost);
11437 if (NS_FAILED(rv)) return rv;
11439 // If the user pressed cancel in the dialog, return. We're done here.
11440 if (!repost)
11441 return NS_BINDING_ABORTED;
11444 // Do not inherit owner from document (security-critical!);
11445 uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
11447 nsAutoString srcdoc;
11448 bool isSrcdoc;
11449 nsCOMPtr<nsIURI> baseURI;
11450 aEntry->GetIsSrcdocEntry(&isSrcdoc);
11451 if (isSrcdoc) {
11452 aEntry->GetSrcdocData(srcdoc);
11453 aEntry->GetBaseURI(getter_AddRefs(baseURI));
11454 flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC;
11456 else {
11457 srcdoc = NullString();
11460 // Passing nullptr as aSourceDocShell gives the same behaviour as before
11461 // aSourceDocShell was introduced. According to spec we should be passing
11462 // the source browsing context that was used when the history entry was
11463 // first created. bug 947716 has been created to address this issue.
11464 rv = InternalLoad(uri,
11465 referrerURI,
11466 owner,
11467 flags,
11468 nullptr, // No window target
11469 contentType.get(), // Type hint
11470 NullString(), // No forced file download
11471 postData, // Post data stream
11472 nullptr, // No headers stream
11473 aLoadType, // Load type
11474 aEntry, // SHEntry
11475 true,
11476 srcdoc,
11477 nullptr, // Source docshell, see comment above
11478 baseURI,
11479 nullptr, // No nsIDocShell
11480 nullptr); // No nsIRequest
11481 return rv;
11484 NS_IMETHODIMP nsDocShell::GetShouldSaveLayoutState(bool* aShould)
11486 *aShould = false;
11487 if (mOSHE) {
11488 // Don't capture historystate and save it in history
11489 // if the page asked not to do so.
11490 mOSHE->GetSaveLayoutStateFlag(aShould);
11493 return NS_OK;
11496 NS_IMETHODIMP nsDocShell::PersistLayoutHistoryState()
11498 nsresult rv = NS_OK;
11500 if (mOSHE) {
11501 nsCOMPtr<nsIPresShell> shell = GetPresShell();
11502 if (shell) {
11503 nsCOMPtr<nsILayoutHistoryState> layoutState;
11504 rv = shell->CaptureHistoryState(getter_AddRefs(layoutState));
11508 return rv;
11511 /* static */ nsresult
11512 nsDocShell::WalkHistoryEntries(nsISHEntry *aRootEntry,
11513 nsDocShell *aRootShell,
11514 WalkHistoryEntriesFunc aCallback,
11515 void *aData)
11517 NS_ENSURE_TRUE(aRootEntry, NS_ERROR_FAILURE);
11519 nsCOMPtr<nsISHContainer> container(do_QueryInterface(aRootEntry));
11520 if (!container)
11521 return NS_ERROR_FAILURE;
11523 int32_t childCount;
11524 container->GetChildCount(&childCount);
11525 for (int32_t i = 0; i < childCount; i++) {
11526 nsCOMPtr<nsISHEntry> childEntry;
11527 container->GetChildAt(i, getter_AddRefs(childEntry));
11528 if (!childEntry) {
11529 // childEntry can be null for valid reasons, for example if the
11530 // docshell at index i never loaded anything useful.
11531 // Remember to clone also nulls in the child array (bug 464064).
11532 aCallback(nullptr, nullptr, i, aData);
11533 continue;
11536 nsDocShell *childShell = nullptr;
11537 if (aRootShell) {
11538 // Walk the children of aRootShell and see if one of them
11539 // has srcChild as a SHEntry.
11541 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(aRootShell->mChildList);
11542 while (iter.HasMore()) {
11543 nsDocShell *child = static_cast<nsDocShell*>(iter.GetNext());
11545 if (child->HasHistoryEntry(childEntry)) {
11546 childShell = child;
11547 break;
11551 nsresult rv = aCallback(childEntry, childShell, i, aData);
11552 NS_ENSURE_SUCCESS(rv, rv);
11555 return NS_OK;
11558 // callback data for WalkHistoryEntries
11559 struct MOZ_STACK_CLASS CloneAndReplaceData
11561 CloneAndReplaceData(uint32_t aCloneID, nsISHEntry *aReplaceEntry,
11562 bool aCloneChildren, nsISHEntry *aDestTreeParent)
11563 : cloneID(aCloneID),
11564 cloneChildren(aCloneChildren),
11565 replaceEntry(aReplaceEntry),
11566 destTreeParent(aDestTreeParent) { }
11568 uint32_t cloneID;
11569 bool cloneChildren;
11570 nsISHEntry *replaceEntry;
11571 nsISHEntry *destTreeParent;
11572 nsCOMPtr<nsISHEntry> resultEntry;
11575 /* static */ nsresult
11576 nsDocShell::CloneAndReplaceChild(nsISHEntry *aEntry, nsDocShell *aShell,
11577 int32_t aEntryIndex, void *aData)
11579 nsCOMPtr<nsISHEntry> dest;
11581 CloneAndReplaceData *data = static_cast<CloneAndReplaceData*>(aData);
11582 uint32_t cloneID = data->cloneID;
11583 nsISHEntry *replaceEntry = data->replaceEntry;
11585 nsCOMPtr<nsISHContainer> container =
11586 do_QueryInterface(data->destTreeParent);
11587 if (!aEntry) {
11588 if (container) {
11589 container->AddChild(nullptr, aEntryIndex);
11591 return NS_OK;
11594 uint32_t srcID;
11595 aEntry->GetID(&srcID);
11597 nsresult rv = NS_OK;
11598 if (srcID == cloneID) {
11599 // Replace the entry
11600 dest = replaceEntry;
11601 } else {
11602 // Clone the SHEntry...
11603 rv = aEntry->Clone(getter_AddRefs(dest));
11604 NS_ENSURE_SUCCESS(rv, rv);
11606 dest->SetIsSubFrame(true);
11608 if (srcID != cloneID || data->cloneChildren) {
11609 // Walk the children
11610 CloneAndReplaceData childData(cloneID, replaceEntry,
11611 data->cloneChildren, dest);
11612 rv = WalkHistoryEntries(aEntry, aShell,
11613 CloneAndReplaceChild, &childData);
11614 NS_ENSURE_SUCCESS(rv, rv);
11617 if (srcID != cloneID && aShell) {
11618 aShell->SwapHistoryEntries(aEntry, dest);
11621 if (container)
11622 container->AddChild(dest, aEntryIndex);
11624 data->resultEntry = dest;
11625 return rv;
11628 /* static */ nsresult
11629 nsDocShell::CloneAndReplace(nsISHEntry *aSrcEntry,
11630 nsDocShell *aSrcShell,
11631 uint32_t aCloneID,
11632 nsISHEntry *aReplaceEntry,
11633 bool aCloneChildren,
11634 nsISHEntry **aResultEntry)
11636 NS_ENSURE_ARG_POINTER(aResultEntry);
11637 NS_ENSURE_TRUE(aReplaceEntry, NS_ERROR_FAILURE);
11639 CloneAndReplaceData data(aCloneID, aReplaceEntry, aCloneChildren, nullptr);
11640 nsresult rv = CloneAndReplaceChild(aSrcEntry, aSrcShell, 0, &data);
11642 data.resultEntry.swap(*aResultEntry);
11643 return rv;
11646 void
11647 nsDocShell::SwapHistoryEntries(nsISHEntry *aOldEntry, nsISHEntry *aNewEntry)
11649 if (aOldEntry == mOSHE)
11650 mOSHE = aNewEntry;
11652 if (aOldEntry == mLSHE)
11653 mLSHE = aNewEntry;
11657 struct SwapEntriesData
11659 nsDocShell *ignoreShell; // constant; the shell to ignore
11660 nsISHEntry *destTreeRoot; // constant; the root of the dest tree
11661 nsISHEntry *destTreeParent; // constant; the node under destTreeRoot
11662 // whose children will correspond to aEntry
11666 nsresult
11667 nsDocShell::SetChildHistoryEntry(nsISHEntry *aEntry, nsDocShell *aShell,
11668 int32_t aEntryIndex, void *aData)
11670 SwapEntriesData *data = static_cast<SwapEntriesData*>(aData);
11671 nsDocShell *ignoreShell = data->ignoreShell;
11673 if (!aShell || aShell == ignoreShell)
11674 return NS_OK;
11676 nsISHEntry *destTreeRoot = data->destTreeRoot;
11678 nsCOMPtr<nsISHEntry> destEntry;
11679 nsCOMPtr<nsISHContainer> container =
11680 do_QueryInterface(data->destTreeParent);
11682 if (container) {
11683 // aEntry is a clone of some child of destTreeParent, but since the
11684 // trees aren't necessarily in sync, we'll have to locate it.
11685 // Note that we could set aShell's entry to null if we don't find a
11686 // corresponding entry under destTreeParent.
11688 uint32_t targetID, id;
11689 aEntry->GetID(&targetID);
11691 // First look at the given index, since this is the common case.
11692 nsCOMPtr<nsISHEntry> entry;
11693 container->GetChildAt(aEntryIndex, getter_AddRefs(entry));
11694 if (entry && NS_SUCCEEDED(entry->GetID(&id)) && id == targetID) {
11695 destEntry.swap(entry);
11696 } else {
11697 int32_t childCount;
11698 container->GetChildCount(&childCount);
11699 for (int32_t i = 0; i < childCount; ++i) {
11700 container->GetChildAt(i, getter_AddRefs(entry));
11701 if (!entry)
11702 continue;
11704 entry->GetID(&id);
11705 if (id == targetID) {
11706 destEntry.swap(entry);
11707 break;
11711 } else {
11712 destEntry = destTreeRoot;
11715 aShell->SwapHistoryEntries(aEntry, destEntry);
11717 // Now handle the children of aEntry.
11718 SwapEntriesData childData = { ignoreShell, destTreeRoot, destEntry };
11719 return WalkHistoryEntries(aEntry, aShell,
11720 SetChildHistoryEntry, &childData);
11724 static nsISHEntry*
11725 GetRootSHEntry(nsISHEntry *aEntry)
11727 nsCOMPtr<nsISHEntry> rootEntry = aEntry;
11728 nsISHEntry *result = nullptr;
11729 while (rootEntry) {
11730 result = rootEntry;
11731 result->GetParent(getter_AddRefs(rootEntry));
11734 return result;
11738 void
11739 nsDocShell::SetHistoryEntry(nsCOMPtr<nsISHEntry> *aPtr, nsISHEntry *aEntry)
11741 // We need to sync up the docshell and session history trees for
11742 // subframe navigation. If the load was in a subframe, we forward up to
11743 // the root docshell, which will then recursively sync up all docshells
11744 // to their corresponding entries in the new session history tree.
11745 // If we don't do this, then we can cache a content viewer on the wrong
11746 // cloned entry, and subsequently restore it at the wrong time.
11748 nsISHEntry *newRootEntry = GetRootSHEntry(aEntry);
11749 if (newRootEntry) {
11750 // newRootEntry is now the new root entry.
11751 // Find the old root entry as well.
11753 // Need a strong ref. on |oldRootEntry| so it isn't destroyed when
11754 // SetChildHistoryEntry() does SwapHistoryEntries() (bug 304639).
11755 nsCOMPtr<nsISHEntry> oldRootEntry = GetRootSHEntry(*aPtr);
11756 if (oldRootEntry) {
11757 nsCOMPtr<nsIDocShellTreeItem> rootAsItem;
11758 GetSameTypeRootTreeItem(getter_AddRefs(rootAsItem));
11759 nsCOMPtr<nsIDocShell> rootShell = do_QueryInterface(rootAsItem);
11760 if (rootShell) { // if we're the root just set it, nothing to swap
11761 SwapEntriesData data = { this, newRootEntry };
11762 nsIDocShell *rootIDocShell =
11763 static_cast<nsIDocShell*>(rootShell);
11764 nsDocShell *rootDocShell = static_cast<nsDocShell*>
11765 (rootIDocShell);
11767 #ifdef DEBUG
11768 nsresult rv =
11769 #endif
11770 SetChildHistoryEntry(oldRootEntry, rootDocShell,
11771 0, &data);
11772 NS_ASSERTION(NS_SUCCEEDED(rv), "SetChildHistoryEntry failed");
11777 *aPtr = aEntry;
11781 nsresult
11782 nsDocShell::GetRootSessionHistory(nsISHistory ** aReturn)
11784 nsresult rv;
11786 nsCOMPtr<nsIDocShellTreeItem> root;
11787 //Get the root docshell
11788 rv = GetSameTypeRootTreeItem(getter_AddRefs(root));
11789 // QI to nsIWebNavigation
11790 nsCOMPtr<nsIWebNavigation> rootAsWebnav(do_QueryInterface(root));
11791 if (rootAsWebnav) {
11792 // Get the handle to SH from the root docshell
11793 rv = rootAsWebnav->GetSessionHistory(aReturn);
11795 return rv;
11798 nsresult
11799 nsDocShell::GetHttpChannel(nsIChannel * aChannel, nsIHttpChannel ** aReturn)
11801 NS_ENSURE_ARG_POINTER(aReturn);
11802 if (!aChannel)
11803 return NS_ERROR_FAILURE;
11805 nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(aChannel));
11806 if (multiPartChannel) {
11807 nsCOMPtr<nsIChannel> baseChannel;
11808 multiPartChannel->GetBaseChannel(getter_AddRefs(baseChannel));
11809 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(baseChannel));
11810 *aReturn = httpChannel;
11811 NS_IF_ADDREF(*aReturn);
11813 return NS_OK;
11816 bool
11817 nsDocShell::ShouldDiscardLayoutState(nsIHttpChannel * aChannel)
11819 // By default layout State will be saved.
11820 if (!aChannel)
11821 return false;
11823 // figure out if SH should be saving layout state
11824 nsCOMPtr<nsISupports> securityInfo;
11825 bool noStore = false, noCache = false;
11826 aChannel->GetSecurityInfo(getter_AddRefs(securityInfo));
11827 aChannel->IsNoStoreResponse(&noStore);
11828 aChannel->IsNoCacheResponse(&noCache);
11830 return (noStore || (noCache && securityInfo));
11833 NS_IMETHODIMP nsDocShell::GetEditor(nsIEditor * *aEditor)
11835 NS_ENSURE_ARG_POINTER(aEditor);
11837 if (!mEditorData) {
11838 *aEditor = nullptr;
11839 return NS_OK;
11842 return mEditorData->GetEditor(aEditor);
11845 NS_IMETHODIMP nsDocShell::SetEditor(nsIEditor * aEditor)
11847 nsresult rv = EnsureEditorData();
11848 if (NS_FAILED(rv)) return rv;
11850 return mEditorData->SetEditor(aEditor);
11854 NS_IMETHODIMP nsDocShell::GetEditable(bool *aEditable)
11856 NS_ENSURE_ARG_POINTER(aEditable);
11857 *aEditable = mEditorData && mEditorData->GetEditable();
11858 return NS_OK;
11862 NS_IMETHODIMP nsDocShell::GetHasEditingSession(bool *aHasEditingSession)
11864 NS_ENSURE_ARG_POINTER(aHasEditingSession);
11866 if (mEditorData)
11868 nsCOMPtr<nsIEditingSession> editingSession;
11869 mEditorData->GetEditingSession(getter_AddRefs(editingSession));
11870 *aHasEditingSession = (editingSession.get() != nullptr);
11872 else
11874 *aHasEditingSession = false;
11877 return NS_OK;
11880 NS_IMETHODIMP nsDocShell::MakeEditable(bool inWaitForUriLoad)
11882 nsresult rv = EnsureEditorData();
11883 if (NS_FAILED(rv)) return rv;
11885 return mEditorData->MakeEditable(inWaitForUriLoad);
11888 bool
11889 nsDocShell::ChannelIsPost(nsIChannel* aChannel)
11891 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
11892 if (!httpChannel) {
11893 return false;
11896 nsAutoCString method;
11897 httpChannel->GetRequestMethod(method);
11898 return method.EqualsLiteral("POST");
11901 void
11902 nsDocShell::ExtractLastVisit(nsIChannel* aChannel,
11903 nsIURI** aURI,
11904 uint32_t* aChannelRedirectFlags)
11906 nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(aChannel));
11907 if (!props) {
11908 return;
11911 nsresult rv = props->GetPropertyAsInterface(
11912 NS_LITERAL_STRING("docshell.previousURI"),
11913 NS_GET_IID(nsIURI),
11914 reinterpret_cast<void**>(aURI)
11917 if (NS_FAILED(rv)) {
11918 // There is no last visit for this channel, so this must be the first
11919 // link. Link the visit to the referrer of this request, if any.
11920 // Treat referrer as null if there is an error getting it.
11921 (void)NS_GetReferrerFromChannel(aChannel, aURI);
11923 else {
11924 rv = props->GetPropertyAsUint32(
11925 NS_LITERAL_STRING("docshell.previousFlags"),
11926 aChannelRedirectFlags
11929 NS_WARN_IF_FALSE(
11930 NS_SUCCEEDED(rv),
11931 "Could not fetch previous flags, URI will be treated like referrer"
11936 void
11937 nsDocShell::SaveLastVisit(nsIChannel* aChannel,
11938 nsIURI* aURI,
11939 uint32_t aChannelRedirectFlags)
11941 nsCOMPtr<nsIWritablePropertyBag2> props(do_QueryInterface(aChannel));
11942 if (!props || !aURI) {
11943 return;
11946 props->SetPropertyAsInterface(NS_LITERAL_STRING("docshell.previousURI"),
11947 aURI);
11948 props->SetPropertyAsUint32(NS_LITERAL_STRING("docshell.previousFlags"),
11949 aChannelRedirectFlags);
11952 void
11953 nsDocShell::AddURIVisit(nsIURI* aURI,
11954 nsIURI* aReferrerURI,
11955 nsIURI* aPreviousURI,
11956 uint32_t aChannelRedirectFlags,
11957 uint32_t aResponseStatus)
11959 MOZ_ASSERT(aURI, "Visited URI is null!");
11960 MOZ_ASSERT(mLoadType != LOAD_ERROR_PAGE &&
11961 mLoadType != LOAD_BYPASS_HISTORY,
11962 "Do not add error or bypass pages to global history");
11964 // Only content-type docshells save URI visits. Also don't do
11965 // anything here if we're not supposed to use global history.
11966 if (mItemType != typeContent || !mUseGlobalHistory || mInPrivateBrowsing) {
11967 return;
11970 nsCOMPtr<IHistory> history = services::GetHistoryService();
11972 if (history) {
11973 uint32_t visitURIFlags = 0;
11975 if (!IsFrame()) {
11976 visitURIFlags |= IHistory::TOP_LEVEL;
11979 if (aChannelRedirectFlags & nsIChannelEventSink::REDIRECT_TEMPORARY) {
11980 visitURIFlags |= IHistory::REDIRECT_TEMPORARY;
11982 else if (aChannelRedirectFlags &
11983 nsIChannelEventSink::REDIRECT_PERMANENT) {
11984 visitURIFlags |= IHistory::REDIRECT_PERMANENT;
11987 if (aResponseStatus >= 300 && aResponseStatus < 400) {
11988 visitURIFlags |= IHistory::REDIRECT_SOURCE;
11990 // Errors 400-501 and 505 are considered unrecoverable, in the sense a
11991 // simple retry attempt by the user is unlikely to solve them.
11992 // 408 is special cased, since may actually indicate a temporary
11993 // connection problem.
11994 else if (aResponseStatus != 408 &&
11995 ((aResponseStatus >= 400 && aResponseStatus <= 501) ||
11996 aResponseStatus == 505)) {
11997 visitURIFlags |= IHistory::UNRECOVERABLE_ERROR;
12000 (void)history->VisitURI(aURI, aPreviousURI, visitURIFlags);
12002 else if (mGlobalHistory) {
12003 // Falls back to sync global history interface.
12004 (void)mGlobalHistory->AddURI(aURI,
12005 !!aChannelRedirectFlags,
12006 !IsFrame(),
12007 aReferrerURI);
12011 //*****************************************************************************
12012 // nsDocShell: Helper Routines
12013 //*****************************************************************************
12015 NS_IMETHODIMP
12016 nsDocShell::SetLoadType(uint32_t aLoadType)
12018 mLoadType = aLoadType;
12019 return NS_OK;
12022 NS_IMETHODIMP
12023 nsDocShell::GetLoadType(uint32_t * aLoadType)
12025 *aLoadType = mLoadType;
12026 return NS_OK;
12029 nsresult
12030 nsDocShell::ConfirmRepost(bool * aRepost)
12032 nsCOMPtr<nsIPrompt> prompter;
12033 CallGetInterface(this, static_cast<nsIPrompt**>(getter_AddRefs(prompter)));
12034 if (!prompter) {
12035 return NS_ERROR_NOT_AVAILABLE;
12038 nsCOMPtr<nsIStringBundleService> stringBundleService =
12039 mozilla::services::GetStringBundleService();
12040 if (!stringBundleService)
12041 return NS_ERROR_FAILURE;
12043 nsCOMPtr<nsIStringBundle> appBundle;
12044 nsresult rv = stringBundleService->CreateBundle(kAppstringsBundleURL,
12045 getter_AddRefs(appBundle));
12046 NS_ENSURE_SUCCESS(rv, rv);
12048 nsCOMPtr<nsIStringBundle> brandBundle;
12049 rv = stringBundleService->CreateBundle(kBrandBundleURL, getter_AddRefs(brandBundle));
12050 NS_ENSURE_SUCCESS(rv, rv);
12052 NS_ASSERTION(prompter && brandBundle && appBundle,
12053 "Unable to set up repost prompter.");
12055 nsXPIDLString brandName;
12056 rv = brandBundle->GetStringFromName(MOZ_UTF16("brandShortName"),
12057 getter_Copies(brandName));
12059 nsXPIDLString msgString, button0Title;
12060 if (NS_FAILED(rv)) { // No brand, use the generic version.
12061 rv = appBundle->GetStringFromName(MOZ_UTF16("confirmRepostPrompt"),
12062 getter_Copies(msgString));
12064 else {
12065 // Brand available - if the app has an override file with formatting, the app name will
12066 // be included. Without an override, the prompt will look like the generic version.
12067 const char16_t *formatStrings[] = { brandName.get() };
12068 rv = appBundle->FormatStringFromName(MOZ_UTF16("confirmRepostPrompt"),
12069 formatStrings, ArrayLength(formatStrings),
12070 getter_Copies(msgString));
12072 if (NS_FAILED(rv)) return rv;
12074 rv = appBundle->GetStringFromName(MOZ_UTF16("resendButton.label"),
12075 getter_Copies(button0Title));
12076 if (NS_FAILED(rv)) return rv;
12078 int32_t buttonPressed;
12079 // The actual value here is irrelevant, but we can't pass an invalid
12080 // bool through XPConnect.
12081 bool checkState = false;
12082 rv = prompter->
12083 ConfirmEx(nullptr, msgString.get(),
12084 (nsIPrompt::BUTTON_POS_0 * nsIPrompt::BUTTON_TITLE_IS_STRING) +
12085 (nsIPrompt::BUTTON_POS_1 * nsIPrompt::BUTTON_TITLE_CANCEL),
12086 button0Title.get(), nullptr, nullptr, nullptr, &checkState, &buttonPressed);
12087 if (NS_FAILED(rv)) return rv;
12089 *aRepost = (buttonPressed == 0);
12090 return NS_OK;
12093 NS_IMETHODIMP
12094 nsDocShell::GetPromptAndStringBundle(nsIPrompt ** aPrompt,
12095 nsIStringBundle ** aStringBundle)
12097 NS_ENSURE_SUCCESS(GetInterface(NS_GET_IID(nsIPrompt), (void **) aPrompt),
12098 NS_ERROR_FAILURE);
12100 nsCOMPtr<nsIStringBundleService> stringBundleService =
12101 mozilla::services::GetStringBundleService();
12102 NS_ENSURE_TRUE(stringBundleService, NS_ERROR_FAILURE);
12104 NS_ENSURE_SUCCESS(stringBundleService->
12105 CreateBundle(kAppstringsBundleURL,
12106 aStringBundle),
12107 NS_ERROR_FAILURE);
12109 return NS_OK;
12112 NS_IMETHODIMP
12113 nsDocShell::GetChildOffset(nsIDOMNode * aChild, nsIDOMNode * aParent,
12114 int32_t * aOffset)
12116 NS_ENSURE_ARG_POINTER(aChild || aParent);
12118 nsCOMPtr<nsIDOMNodeList> childNodes;
12119 NS_ENSURE_SUCCESS(aParent->GetChildNodes(getter_AddRefs(childNodes)),
12120 NS_ERROR_FAILURE);
12121 NS_ENSURE_TRUE(childNodes, NS_ERROR_FAILURE);
12123 int32_t i = 0;
12125 for (; true; i++) {
12126 nsCOMPtr<nsIDOMNode> childNode;
12127 NS_ENSURE_SUCCESS(childNodes->Item(i, getter_AddRefs(childNode)),
12128 NS_ERROR_FAILURE);
12129 NS_ENSURE_TRUE(childNode, NS_ERROR_FAILURE);
12131 if (childNode.get() == aChild) {
12132 *aOffset = i;
12133 return NS_OK;
12137 return NS_ERROR_FAILURE;
12140 nsIScrollableFrame *
12141 nsDocShell::GetRootScrollFrame()
12143 nsCOMPtr<nsIPresShell> shell = GetPresShell();
12144 NS_ENSURE_TRUE(shell, nullptr);
12146 return shell->GetRootScrollFrameAsScrollableExternal();
12149 NS_IMETHODIMP
12150 nsDocShell::EnsureScriptEnvironment()
12152 if (mScriptGlobal)
12153 return NS_OK;
12155 if (mIsBeingDestroyed) {
12156 return NS_ERROR_NOT_AVAILABLE;
12159 #ifdef DEBUG
12160 NS_ASSERTION(!mInEnsureScriptEnv,
12161 "Infinite loop! Calling EnsureScriptEnvironment() from "
12162 "within EnsureScriptEnvironment()!");
12164 // Yeah, this isn't re-entrant safe, but that's ok since if we
12165 // re-enter this method, we'll infinitely loop...
12166 AutoRestore<bool> boolSetter(mInEnsureScriptEnv);
12167 mInEnsureScriptEnv = true;
12168 #endif
12170 nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
12171 NS_ENSURE_TRUE(browserChrome, NS_ERROR_NOT_AVAILABLE);
12173 uint32_t chromeFlags;
12174 browserChrome->GetChromeFlags(&chromeFlags);
12176 bool isModalContentWindow = (mItemType == typeContent) &&
12177 (chromeFlags & nsIWebBrowserChrome::CHROME_MODAL_CONTENT_WINDOW);
12178 // There can be various other content docshells associated with the
12179 // top-level window, like sidebars. Make sure that we only create an
12180 // nsGlobalModalWindow for the primary content shell.
12181 if (isModalContentWindow) {
12182 nsCOMPtr<nsIDocShellTreeItem> primaryItem;
12183 nsresult rv = mTreeOwner->GetPrimaryContentShell(getter_AddRefs(primaryItem));
12184 NS_ENSURE_SUCCESS(rv, rv);
12185 isModalContentWindow = (primaryItem == this);
12188 // If our window is modal and we're not opened as chrome, make
12189 // this window a modal content window.
12190 mScriptGlobal =
12191 NS_NewScriptGlobalObject(mItemType == typeChrome, isModalContentWindow);
12192 MOZ_ASSERT(mScriptGlobal);
12194 mScriptGlobal->SetDocShell(this);
12196 // Ensure the script object is set up to run script.
12197 return mScriptGlobal->EnsureScriptEnvironment();
12201 NS_IMETHODIMP
12202 nsDocShell::EnsureEditorData()
12204 bool openDocHasDetachedEditor = mOSHE && mOSHE->HasDetachedEditor();
12205 if (!mEditorData && !mIsBeingDestroyed && !openDocHasDetachedEditor) {
12206 // We shouldn't recreate the editor data if it already exists, or
12207 // we're shutting down, or we already have a detached editor data
12208 // stored in the session history. We should only have one editordata
12209 // per docshell.
12210 mEditorData = new nsDocShellEditorData(this);
12213 return mEditorData ? NS_OK : NS_ERROR_NOT_AVAILABLE;
12216 nsresult
12217 nsDocShell::EnsureTransferableHookData()
12219 if (!mTransferableHookData) {
12220 mTransferableHookData = new nsTransferableHookData();
12221 if (!mTransferableHookData) return NS_ERROR_OUT_OF_MEMORY;
12224 return NS_OK;
12228 NS_IMETHODIMP nsDocShell::EnsureFind()
12230 nsresult rv;
12231 if (!mFind)
12233 mFind = do_CreateInstance("@mozilla.org/embedcomp/find;1", &rv);
12234 if (NS_FAILED(rv)) return rv;
12237 // we promise that the nsIWebBrowserFind that we return has been set
12238 // up to point to the focused, or content window, so we have to
12239 // set that up each time.
12241 nsIScriptGlobalObject* scriptGO = GetScriptGlobalObject();
12242 NS_ENSURE_TRUE(scriptGO, NS_ERROR_UNEXPECTED);
12244 // default to our window
12245 nsCOMPtr<nsPIDOMWindow> ourWindow = do_QueryInterface(scriptGO);
12246 nsCOMPtr<nsPIDOMWindow> windowToSearch;
12247 nsFocusManager::GetFocusedDescendant(ourWindow, true, getter_AddRefs(windowToSearch));
12249 nsCOMPtr<nsIWebBrowserFindInFrames> findInFrames = do_QueryInterface(mFind);
12250 if (!findInFrames) return NS_ERROR_NO_INTERFACE;
12252 rv = findInFrames->SetRootSearchFrame(ourWindow);
12253 if (NS_FAILED(rv)) return rv;
12254 rv = findInFrames->SetCurrentSearchFrame(windowToSearch);
12255 if (NS_FAILED(rv)) return rv;
12257 return NS_OK;
12260 bool
12261 nsDocShell::IsFrame()
12263 nsCOMPtr<nsIDocShellTreeItem> parent;
12264 GetSameTypeParent(getter_AddRefs(parent));
12265 return !!parent;
12268 /* boolean IsBeingDestroyed (); */
12269 NS_IMETHODIMP
12270 nsDocShell::IsBeingDestroyed(bool *aDoomed)
12272 NS_ENSURE_ARG(aDoomed);
12273 *aDoomed = mIsBeingDestroyed;
12274 return NS_OK;
12278 NS_IMETHODIMP
12279 nsDocShell::GetIsExecutingOnLoadHandler(bool *aResult)
12281 NS_ENSURE_ARG(aResult);
12282 *aResult = mIsExecutingOnLoadHandler;
12283 return NS_OK;
12286 NS_IMETHODIMP
12287 nsDocShell::GetLayoutHistoryState(nsILayoutHistoryState **aLayoutHistoryState)
12289 if (mOSHE)
12290 mOSHE->GetLayoutHistoryState(aLayoutHistoryState);
12291 return NS_OK;
12294 NS_IMETHODIMP
12295 nsDocShell::SetLayoutHistoryState(nsILayoutHistoryState *aLayoutHistoryState)
12297 if (mOSHE)
12298 mOSHE->SetLayoutHistoryState(aLayoutHistoryState);
12299 return NS_OK;
12302 //*****************************************************************************
12303 //*** nsRefreshTimer: Object Management
12304 //*****************************************************************************
12306 nsRefreshTimer::nsRefreshTimer()
12307 : mDelay(0), mRepeat(false), mMetaRefresh(false)
12311 nsRefreshTimer::~nsRefreshTimer()
12315 //*****************************************************************************
12316 // nsRefreshTimer::nsISupports
12317 //*****************************************************************************
12319 NS_IMPL_ADDREF(nsRefreshTimer)
12320 NS_IMPL_RELEASE(nsRefreshTimer)
12322 NS_INTERFACE_MAP_BEGIN(nsRefreshTimer)
12323 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITimerCallback)
12324 NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
12325 NS_INTERFACE_MAP_END_THREADSAFE
12327 ///*****************************************************************************
12328 // nsRefreshTimer::nsITimerCallback
12329 //******************************************************************************
12330 NS_IMETHODIMP
12331 nsRefreshTimer::Notify(nsITimer * aTimer)
12333 NS_ASSERTION(mDocShell, "DocShell is somehow null");
12335 if (mDocShell && aTimer) {
12336 // Get the delay count to determine load type
12337 uint32_t delay = 0;
12338 aTimer->GetDelay(&delay);
12339 mDocShell->ForceRefreshURIFromTimer(mURI, delay, mMetaRefresh, aTimer);
12341 return NS_OK;
12344 //*****************************************************************************
12345 // nsDocShell::InterfaceRequestorProxy
12346 //*****************************************************************************
12347 nsDocShell::InterfaceRequestorProxy::InterfaceRequestorProxy(nsIInterfaceRequestor* p)
12349 if (p) {
12350 mWeakPtr = do_GetWeakReference(p);
12354 nsDocShell::InterfaceRequestorProxy::~InterfaceRequestorProxy()
12356 mWeakPtr = nullptr;
12359 NS_IMPL_ISUPPORTS(nsDocShell::InterfaceRequestorProxy, nsIInterfaceRequestor)
12361 NS_IMETHODIMP
12362 nsDocShell::InterfaceRequestorProxy::GetInterface(const nsIID & aIID, void **aSink)
12364 NS_ENSURE_ARG_POINTER(aSink);
12365 nsCOMPtr<nsIInterfaceRequestor> ifReq = do_QueryReferent(mWeakPtr);
12366 if (ifReq) {
12367 return ifReq->GetInterface(aIID, aSink);
12369 *aSink = nullptr;
12370 return NS_NOINTERFACE;
12373 nsresult
12374 nsDocShell::SetBaseUrlForWyciwyg(nsIContentViewer * aContentViewer)
12376 if (!aContentViewer)
12377 return NS_ERROR_FAILURE;
12379 nsCOMPtr<nsIURI> baseURI;
12380 nsresult rv = NS_ERROR_NOT_AVAILABLE;
12382 if (sURIFixup)
12383 rv = sURIFixup->CreateExposableURI(mCurrentURI,
12384 getter_AddRefs(baseURI));
12386 // Get the current document and set the base uri
12387 if (baseURI) {
12388 nsIDocument* document = aContentViewer->GetDocument();
12389 if (document) {
12390 rv = document->SetBaseURI(baseURI);
12393 return rv;
12396 //*****************************************************************************
12397 // nsDocShell::nsIAuthPromptProvider
12398 //*****************************************************************************
12400 NS_IMETHODIMP
12401 nsDocShell::GetAuthPrompt(uint32_t aPromptReason, const nsIID& iid,
12402 void** aResult)
12404 // a priority prompt request will override a false mAllowAuth setting
12405 bool priorityPrompt = (aPromptReason == PROMPT_PROXY);
12407 if (!mAllowAuth && !priorityPrompt)
12408 return NS_ERROR_NOT_AVAILABLE;
12410 // we're either allowing auth, or it's a proxy request
12411 nsresult rv;
12412 nsCOMPtr<nsIPromptFactory> wwatch =
12413 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
12414 NS_ENSURE_SUCCESS(rv, rv);
12416 rv = EnsureScriptEnvironment();
12417 NS_ENSURE_SUCCESS(rv, rv);
12419 // Get the an auth prompter for our window so that the parenting
12420 // of the dialogs works as it should when using tabs.
12422 return wwatch->GetPrompt(mScriptGlobal, iid,
12423 reinterpret_cast<void**>(aResult));
12426 //*****************************************************************************
12427 // nsDocShell::nsILoadContext
12428 //*****************************************************************************
12429 NS_IMETHODIMP
12430 nsDocShell::GetAssociatedWindow(nsIDOMWindow** aWindow)
12432 CallGetInterface(this, aWindow);
12433 return NS_OK;
12436 NS_IMETHODIMP
12437 nsDocShell::GetTopWindow(nsIDOMWindow** aWindow)
12439 nsCOMPtr<nsIDOMWindow> win = GetWindow();
12440 if (win) {
12441 win->GetTop(aWindow);
12443 return NS_OK;
12446 NS_IMETHODIMP
12447 nsDocShell::GetTopFrameElement(nsIDOMElement** aElement)
12449 *aElement = nullptr;
12450 nsCOMPtr<nsIDOMWindow> win = GetWindow();
12451 if (!win) {
12452 return NS_OK;
12455 nsCOMPtr<nsIDOMWindow> top;
12456 win->GetScriptableTop(getter_AddRefs(top));
12457 NS_ENSURE_TRUE(top, NS_ERROR_FAILURE);
12459 // GetFrameElement, /not/ GetScriptableFrameElement -- if |top| is inside
12460 // <iframe mozbrowser>, we want to return the iframe, not null.
12461 return top->GetFrameElement(aElement);
12464 NS_IMETHODIMP
12465 nsDocShell::GetNestedFrameId(uint64_t* aId)
12467 *aId = 0;
12468 return NS_OK;
12471 NS_IMETHODIMP
12472 nsDocShell::IsAppOfType(uint32_t aAppType, bool *aIsOfType)
12474 nsRefPtr<nsDocShell> shell = this;
12475 while (shell) {
12476 uint32_t type;
12477 shell->GetAppType(&type);
12478 if (type == aAppType) {
12479 *aIsOfType = true;
12480 return NS_OK;
12482 shell = shell->GetParentDocshell();
12485 *aIsOfType = false;
12486 return NS_OK;
12489 NS_IMETHODIMP
12490 nsDocShell::GetIsContent(bool *aIsContent)
12492 *aIsContent = (mItemType == typeContent);
12493 return NS_OK;
12496 bool
12497 nsDocShell::IsOKToLoadURI(nsIURI* aURI)
12499 NS_PRECONDITION(aURI, "Must have a URI!");
12501 if (!mFiredUnloadEvent) {
12502 return true;
12505 if (!mLoadingURI) {
12506 return false;
12509 nsCOMPtr<nsIScriptSecurityManager> secMan =
12510 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
12511 return
12512 secMan &&
12513 NS_SUCCEEDED(secMan->CheckSameOriginURI(aURI, mLoadingURI, false));
12517 // Routines for selection and clipboard
12519 nsresult
12520 nsDocShell::GetControllerForCommand(const char * inCommand,
12521 nsIController** outController)
12523 NS_ENSURE_ARG_POINTER(outController);
12524 *outController = nullptr;
12526 NS_ENSURE_TRUE(mScriptGlobal, NS_ERROR_FAILURE);
12528 nsCOMPtr<nsPIWindowRoot> root = mScriptGlobal->GetTopWindowRoot();
12529 NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
12531 return root->GetControllerForCommand(inCommand, outController);
12534 NS_IMETHODIMP
12535 nsDocShell::IsCommandEnabled(const char * inCommand, bool* outEnabled)
12537 NS_ENSURE_ARG_POINTER(outEnabled);
12538 *outEnabled = false;
12540 nsresult rv = NS_ERROR_FAILURE;
12542 nsCOMPtr<nsIController> controller;
12543 rv = GetControllerForCommand (inCommand, getter_AddRefs(controller));
12544 if (controller)
12545 rv = controller->IsCommandEnabled(inCommand, outEnabled);
12547 return rv;
12550 NS_IMETHODIMP
12551 nsDocShell::DoCommand(const char * inCommand)
12553 nsresult rv = NS_ERROR_FAILURE;
12555 nsCOMPtr<nsIController> controller;
12556 rv = GetControllerForCommand(inCommand, getter_AddRefs(controller));
12557 if (controller)
12558 rv = controller->DoCommand(inCommand);
12560 return rv;
12563 nsresult
12564 nsDocShell::EnsureCommandHandler()
12566 if (!mCommandManager)
12568 nsCOMPtr<nsPICommandUpdater> commandUpdater =
12569 do_CreateInstance("@mozilla.org/embedcomp/command-manager;1");
12570 if (!commandUpdater) return NS_ERROR_OUT_OF_MEMORY;
12572 nsCOMPtr<nsIDOMWindow> domWindow = GetWindow();
12573 nsresult rv = commandUpdater->Init(domWindow);
12574 if (NS_SUCCEEDED(rv))
12575 mCommandManager = do_QueryInterface(commandUpdater);
12578 return mCommandManager ? NS_OK : NS_ERROR_FAILURE;
12581 NS_IMETHODIMP
12582 nsDocShell::CanCutSelection(bool* aResult)
12584 return IsCommandEnabled("cmd_cut", aResult);
12587 NS_IMETHODIMP
12588 nsDocShell::CanCopySelection(bool* aResult)
12590 return IsCommandEnabled("cmd_copy", aResult);
12593 NS_IMETHODIMP
12594 nsDocShell::CanCopyLinkLocation(bool* aResult)
12596 return IsCommandEnabled("cmd_copyLink", aResult);
12599 NS_IMETHODIMP
12600 nsDocShell::CanCopyImageLocation(bool* aResult)
12602 return IsCommandEnabled("cmd_copyImageLocation",
12603 aResult);
12606 NS_IMETHODIMP
12607 nsDocShell::CanCopyImageContents(bool* aResult)
12609 return IsCommandEnabled("cmd_copyImageContents",
12610 aResult);
12613 NS_IMETHODIMP
12614 nsDocShell::CanPaste(bool* aResult)
12616 return IsCommandEnabled("cmd_paste", aResult);
12619 NS_IMETHODIMP
12620 nsDocShell::CutSelection(void)
12622 return DoCommand ( "cmd_cut" );
12625 NS_IMETHODIMP
12626 nsDocShell::CopySelection(void)
12628 return DoCommand ( "cmd_copy" );
12631 NS_IMETHODIMP
12632 nsDocShell::CopyLinkLocation(void)
12634 return DoCommand ( "cmd_copyLink" );
12637 NS_IMETHODIMP
12638 nsDocShell::CopyImageLocation(void)
12640 return DoCommand ( "cmd_copyImageLocation" );
12643 NS_IMETHODIMP
12644 nsDocShell::CopyImageContents(void)
12646 return DoCommand ( "cmd_copyImageContents" );
12649 NS_IMETHODIMP
12650 nsDocShell::Paste(void)
12652 return DoCommand ( "cmd_paste" );
12655 NS_IMETHODIMP
12656 nsDocShell::SelectAll(void)
12658 return DoCommand ( "cmd_selectAll" );
12662 // SelectNone
12664 // Collapses the current selection, insertion point ends up at beginning
12665 // of previous selection.
12667 NS_IMETHODIMP
12668 nsDocShell::SelectNone(void)
12670 return DoCommand ( "cmd_selectNone" );
12673 //----------------------------------------------------------------------
12675 // link handling
12677 class OnLinkClickEvent : public nsRunnable {
12678 public:
12679 OnLinkClickEvent(nsDocShell* aHandler, nsIContent* aContent,
12680 nsIURI* aURI,
12681 const char16_t* aTargetSpec,
12682 const nsAString& aFileName,
12683 nsIInputStream* aPostDataStream,
12684 nsIInputStream* aHeadersDataStream,
12685 bool aIsTrusted);
12687 NS_IMETHOD Run() {
12688 nsAutoPopupStatePusher popupStatePusher(mPopupState);
12690 AutoJSAPI jsapi;
12691 if (mIsTrusted || jsapi.Init(mContent->OwnerDoc()->GetScopeObject())) {
12692 mHandler->OnLinkClickSync(mContent, mURI,
12693 mTargetSpec.get(), mFileName,
12694 mPostDataStream, mHeadersDataStream,
12695 nullptr, nullptr);
12697 return NS_OK;
12700 private:
12701 nsRefPtr<nsDocShell> mHandler;
12702 nsCOMPtr<nsIURI> mURI;
12703 nsString mTargetSpec;
12704 nsString mFileName;
12705 nsCOMPtr<nsIInputStream> mPostDataStream;
12706 nsCOMPtr<nsIInputStream> mHeadersDataStream;
12707 nsCOMPtr<nsIContent> mContent;
12708 PopupControlState mPopupState;
12709 bool mIsTrusted;
12712 OnLinkClickEvent::OnLinkClickEvent(nsDocShell* aHandler,
12713 nsIContent *aContent,
12714 nsIURI* aURI,
12715 const char16_t* aTargetSpec,
12716 const nsAString& aFileName,
12717 nsIInputStream* aPostDataStream,
12718 nsIInputStream* aHeadersDataStream,
12719 bool aIsTrusted)
12720 : mHandler(aHandler)
12721 , mURI(aURI)
12722 , mTargetSpec(aTargetSpec)
12723 , mFileName(aFileName)
12724 , mPostDataStream(aPostDataStream)
12725 , mHeadersDataStream(aHeadersDataStream)
12726 , mContent(aContent)
12727 , mPopupState(mHandler->mScriptGlobal->GetPopupControlState())
12728 , mIsTrusted(aIsTrusted)
12732 //----------------------------------------
12734 NS_IMETHODIMP
12735 nsDocShell::OnLinkClick(nsIContent* aContent,
12736 nsIURI* aURI,
12737 const char16_t* aTargetSpec,
12738 const nsAString& aFileName,
12739 nsIInputStream* aPostDataStream,
12740 nsIInputStream* aHeadersDataStream,
12741 bool aIsTrusted)
12743 NS_ASSERTION(NS_IsMainThread(), "wrong thread");
12745 if (!IsOKToLoadURI(aURI)) {
12746 return NS_OK;
12749 // On history navigation through Back/Forward buttons, don't execute
12750 // automatic JavaScript redirection such as |anchorElement.click()| or
12751 // |formElement.submit()|.
12753 // XXX |formElement.submit()| bypasses this checkpoint because it calls
12754 // nsDocShell::OnLinkClickSync(...) instead.
12755 if (ShouldBlockLoadingForBackButton()) {
12756 return NS_OK;
12759 if (aContent->IsEditable()) {
12760 return NS_OK;
12763 nsresult rv = NS_ERROR_FAILURE;
12764 nsAutoString target;
12766 nsCOMPtr<nsIWebBrowserChrome3> browserChrome3 = do_GetInterface(mTreeOwner);
12767 if (browserChrome3) {
12768 nsCOMPtr<nsIDOMNode> linkNode = do_QueryInterface(aContent);
12769 nsAutoString oldTarget(aTargetSpec);
12770 rv = browserChrome3->OnBeforeLinkTraversal(oldTarget, aURI,
12771 linkNode, mIsAppTab, target);
12774 if (NS_FAILED(rv))
12775 target = aTargetSpec;
12777 nsCOMPtr<nsIRunnable> ev =
12778 new OnLinkClickEvent(this, aContent, aURI, target.get(), aFileName,
12779 aPostDataStream, aHeadersDataStream, aIsTrusted);
12780 return NS_DispatchToCurrentThread(ev);
12783 NS_IMETHODIMP
12784 nsDocShell::OnLinkClickSync(nsIContent *aContent,
12785 nsIURI* aURI,
12786 const char16_t* aTargetSpec,
12787 const nsAString& aFileName,
12788 nsIInputStream* aPostDataStream,
12789 nsIInputStream* aHeadersDataStream,
12790 nsIDocShell** aDocShell,
12791 nsIRequest** aRequest)
12793 // Initialize the DocShell / Request
12794 if (aDocShell) {
12795 *aDocShell = nullptr;
12797 if (aRequest) {
12798 *aRequest = nullptr;
12801 if (!IsOKToLoadURI(aURI)) {
12802 return NS_OK;
12805 // XXX When the linking node was HTMLFormElement, it is synchronous event.
12806 // That is, the caller of this method is not |OnLinkClickEvent::Run()|
12807 // but |HTMLFormElement::SubmitSubmission(...)|.
12808 if (nsGkAtoms::form == aContent->Tag() && ShouldBlockLoadingForBackButton()) {
12809 return NS_OK;
12812 if (aContent->IsEditable()) {
12813 return NS_OK;
12817 // defer to an external protocol handler if necessary...
12818 nsCOMPtr<nsIExternalProtocolService> extProtService =
12819 do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID);
12820 if (extProtService) {
12821 nsAutoCString scheme;
12822 aURI->GetScheme(scheme);
12823 if (!scheme.IsEmpty()) {
12824 // if the URL scheme does not correspond to an exposed protocol, then we
12825 // need to hand this link click over to the external protocol handler.
12826 bool isExposed;
12827 nsresult rv = extProtService->IsExposedProtocol(scheme.get(), &isExposed);
12828 if (NS_SUCCEEDED(rv) && !isExposed) {
12829 return extProtService->LoadURI(aURI, this);
12835 uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
12836 if (IsElementAnchor(aContent)) {
12837 MOZ_ASSERT(aContent->IsHTML());
12838 if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::rel,
12839 NS_LITERAL_STRING("noreferrer"),
12840 aContent->IsInHTMLDocument() ?
12841 eIgnoreCase : eCaseMatters)) {
12842 flags |= INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER |
12843 INTERNAL_LOAD_FLAGS_NO_OPENER;
12847 // Get the owner document of the link that was clicked, this will be
12848 // the document that the link is in, or the last document that the
12849 // link was in. From that document, we'll get the URI to use as the
12850 // referer, since the current URI in this docshell may be a
12851 // new document that we're in the process of loading.
12852 nsCOMPtr<nsIDocument> refererDoc = aContent->OwnerDoc();
12853 NS_ENSURE_TRUE(refererDoc, NS_ERROR_UNEXPECTED);
12855 // Now check that the refererDoc's inner window is the current inner
12856 // window for mScriptGlobal. If it's not, then we don't want to
12857 // follow this link.
12858 nsPIDOMWindow* refererInner = refererDoc->GetInnerWindow();
12859 NS_ENSURE_TRUE(refererInner, NS_ERROR_UNEXPECTED);
12860 if (!mScriptGlobal ||
12861 mScriptGlobal->GetCurrentInnerWindow() != refererInner) {
12862 // We're no longer the current inner window
12863 return NS_OK;
12866 nsCOMPtr<nsIURI> referer = refererDoc->GetDocumentURI();
12868 // referer could be null here in some odd cases, but that's ok,
12869 // we'll just load the link w/o sending a referer in those cases.
12871 nsAutoString target(aTargetSpec);
12873 // If this is an anchor element, grab its type property to use as a hint
12874 nsAutoString typeHint;
12875 nsCOMPtr<nsIDOMHTMLAnchorElement> anchor(do_QueryInterface(aContent));
12876 if (anchor) {
12877 anchor->GetType(typeHint);
12878 NS_ConvertUTF16toUTF8 utf8Hint(typeHint);
12879 nsAutoCString type, dummy;
12880 NS_ParseContentType(utf8Hint, type, dummy);
12881 CopyUTF8toUTF16(type, typeHint);
12884 // Clone the URI now, in case a content policy or something messes
12885 // with it under InternalLoad; we do _not_ want to change the URI
12886 // our caller passed in.
12887 nsCOMPtr<nsIURI> clonedURI;
12888 aURI->Clone(getter_AddRefs(clonedURI));
12889 if (!clonedURI) {
12890 return NS_ERROR_OUT_OF_MEMORY;
12893 nsresult rv = InternalLoad(clonedURI, // New URI
12894 referer, // Referer URI
12895 aContent->NodePrincipal(), // Owner is our node's
12896 // principal
12897 flags,
12898 target.get(), // Window target
12899 NS_LossyConvertUTF16toASCII(typeHint).get(),
12900 aFileName, // Download as file
12901 aPostDataStream, // Post data stream
12902 aHeadersDataStream, // Headers stream
12903 LOAD_LINK, // Load type
12904 nullptr, // No SHEntry
12905 true, // first party site
12906 NullString(), // No srcdoc
12907 this, // We are the source
12908 nullptr, // baseURI not needed
12909 aDocShell, // DocShell out-param
12910 aRequest); // Request out-param
12911 if (NS_SUCCEEDED(rv)) {
12912 DispatchPings(aContent, aURI, referer);
12914 return rv;
12917 NS_IMETHODIMP
12918 nsDocShell::OnOverLink(nsIContent* aContent,
12919 nsIURI* aURI,
12920 const char16_t* aTargetSpec)
12922 if (aContent->IsEditable()) {
12923 return NS_OK;
12926 nsCOMPtr<nsIWebBrowserChrome2> browserChrome2 = do_GetInterface(mTreeOwner);
12927 nsresult rv = NS_ERROR_FAILURE;
12929 nsCOMPtr<nsIWebBrowserChrome> browserChrome;
12930 if (!browserChrome2) {
12931 browserChrome = do_GetInterface(mTreeOwner);
12932 if (!browserChrome)
12933 return rv;
12936 nsCOMPtr<nsITextToSubURI> textToSubURI =
12937 do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
12938 if (NS_FAILED(rv))
12939 return rv;
12941 // use url origin charset to unescape the URL
12942 nsAutoCString charset;
12943 rv = aURI->GetOriginCharset(charset);
12944 NS_ENSURE_SUCCESS(rv, rv);
12946 nsAutoCString spec;
12947 rv = aURI->GetSpec(spec);
12948 NS_ENSURE_SUCCESS(rv, rv);
12950 nsAutoString uStr;
12951 rv = textToSubURI->UnEscapeURIForUI(charset, spec, uStr);
12952 NS_ENSURE_SUCCESS(rv, rv);
12954 mozilla::net::PredictorPredict(aURI, mCurrentURI,
12955 nsINetworkPredictor::PREDICT_LINK,
12956 this, nullptr);
12958 if (browserChrome2) {
12959 nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aContent);
12960 rv = browserChrome2->SetStatusWithContext(nsIWebBrowserChrome::STATUS_LINK,
12961 uStr, element);
12962 } else {
12963 rv = browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_LINK, uStr.get());
12965 return rv;
12968 NS_IMETHODIMP
12969 nsDocShell::OnLeaveLink()
12971 nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
12972 nsresult rv = NS_ERROR_FAILURE;
12974 if (browserChrome) {
12975 rv = browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_LINK,
12976 EmptyString().get());
12978 return rv;
12981 bool
12982 nsDocShell::ShouldBlockLoadingForBackButton()
12984 if (!(mLoadType & LOAD_CMD_HISTORY) ||
12985 EventStateManager::IsHandlingUserInput() ||
12986 !Preferences::GetBool("accessibility.blockjsredirection")) {
12987 return false;
12990 bool canGoForward = false;
12991 GetCanGoForward(&canGoForward);
12992 return canGoForward;
12995 bool
12996 nsDocShell::PluginsAllowedInCurrentDoc()
12998 bool pluginsAllowed = false;
13000 if (!mContentViewer) {
13001 return false;
13004 nsIDocument* doc = mContentViewer->GetDocument();
13005 if (!doc) {
13006 return false;
13009 doc->GetAllowPlugins(&pluginsAllowed);
13010 return pluginsAllowed;
13013 //----------------------------------------------------------------------
13014 // Web Shell Services API
13016 //This functions is only called when a new charset is detected in loading a document.
13017 //Its name should be changed to "CharsetReloadDocument"
13018 NS_IMETHODIMP
13019 nsDocShell::ReloadDocument(const char* aCharset,
13020 int32_t aSource)
13023 // XXX hack. keep the aCharset and aSource wait to pick it up
13024 nsCOMPtr<nsIContentViewer> cv;
13025 NS_ENSURE_SUCCESS(GetContentViewer(getter_AddRefs(cv)), NS_ERROR_FAILURE);
13026 if (cv) {
13027 int32_t hint;
13028 cv->GetHintCharacterSetSource(&hint);
13029 if (aSource > hint) {
13030 nsCString charset(aCharset);
13031 cv->SetHintCharacterSet(charset);
13032 cv->SetHintCharacterSetSource(aSource);
13033 if(eCharsetReloadRequested != mCharsetReloadState) {
13034 mCharsetReloadState = eCharsetReloadRequested;
13035 return Reload(LOAD_FLAGS_CHARSET_CHANGE);
13039 //return failure if this request is not accepted due to mCharsetReloadState
13040 return NS_ERROR_DOCSHELL_REQUEST_REJECTED;
13044 NS_IMETHODIMP
13045 nsDocShell::StopDocumentLoad(void)
13047 if(eCharsetReloadRequested != mCharsetReloadState)
13049 Stop(nsIWebNavigation::STOP_ALL);
13050 return NS_OK;
13052 //return failer if this request is not accepted due to mCharsetReloadState
13053 return NS_ERROR_DOCSHELL_REQUEST_REJECTED;
13056 NS_IMETHODIMP
13057 nsDocShell::GetPrintPreview(nsIWebBrowserPrint** aPrintPreview)
13059 *aPrintPreview = nullptr;
13060 #if NS_PRINT_PREVIEW
13061 nsCOMPtr<nsIDocumentViewerPrint> print = do_QueryInterface(mContentViewer);
13062 if (!print || !print->IsInitializedForPrintPreview()) {
13063 Stop(nsIWebNavigation::STOP_ALL);
13064 nsCOMPtr<nsIPrincipal> principal =
13065 do_CreateInstance("@mozilla.org/nullprincipal;1");
13066 NS_ENSURE_STATE(principal);
13067 nsresult rv = CreateAboutBlankContentViewer(principal, nullptr);
13068 NS_ENSURE_SUCCESS(rv, rv);
13069 print = do_QueryInterface(mContentViewer);
13070 NS_ENSURE_STATE(print);
13071 print->InitializeForPrintPreview();
13073 nsCOMPtr<nsIWebBrowserPrint> result = do_QueryInterface(print);
13074 result.forget(aPrintPreview);
13075 return NS_OK;
13076 #else
13077 return NS_ERROR_NOT_IMPLEMENTED;
13078 #endif
13082 #ifdef DEBUG
13083 unsigned long nsDocShell::gNumberOfDocShells = 0;
13084 #endif
13086 NS_IMETHODIMP
13087 nsDocShell::GetCanExecuteScripts(bool *aResult)
13089 *aResult = mCanExecuteScripts;
13090 return NS_OK;
13093 NS_IMETHODIMP
13094 nsDocShell::SetIsApp(uint32_t aOwnAppId)
13096 mOwnOrContainingAppId = aOwnAppId;
13097 if (aOwnAppId != nsIScriptSecurityManager::NO_APP_ID &&
13098 aOwnAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID) {
13099 mFrameType = eFrameTypeApp;
13100 } else {
13101 mFrameType = eFrameTypeRegular;
13104 return NS_OK;
13107 NS_IMETHODIMP
13108 nsDocShell::SetIsBrowserInsideApp(uint32_t aContainingAppId)
13110 mOwnOrContainingAppId = aContainingAppId;
13111 mFrameType = eFrameTypeBrowser;
13112 return NS_OK;
13115 /* [infallible] */ NS_IMETHODIMP
13116 nsDocShell::GetIsBrowserElement(bool* aIsBrowser)
13118 *aIsBrowser = (mFrameType == eFrameTypeBrowser);
13119 return NS_OK;
13122 /* [infallible] */ NS_IMETHODIMP
13123 nsDocShell::GetIsApp(bool* aIsApp)
13125 *aIsApp = (mFrameType == eFrameTypeApp);
13126 return NS_OK;
13129 /* [infallible] */ NS_IMETHODIMP
13130 nsDocShell::GetIsBrowserOrApp(bool* aIsBrowserOrApp)
13132 switch (mFrameType) {
13133 case eFrameTypeRegular:
13134 *aIsBrowserOrApp = false;
13135 break;
13136 case eFrameTypeBrowser:
13137 case eFrameTypeApp:
13138 *aIsBrowserOrApp = true;
13139 break;
13142 return NS_OK;
13145 nsDocShell::FrameType
13146 nsDocShell::GetInheritedFrameType()
13148 if (mFrameType != eFrameTypeRegular) {
13149 return mFrameType;
13152 nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
13153 GetSameTypeParent(getter_AddRefs(parentAsItem));
13155 nsCOMPtr<nsIDocShell> parent = do_QueryInterface(parentAsItem);
13156 if (!parent) {
13157 return eFrameTypeRegular;
13160 return static_cast<nsDocShell*>(parent.get())->GetInheritedFrameType();
13163 /* [infallible] */ NS_IMETHODIMP
13164 nsDocShell::GetIsInBrowserElement(bool* aIsInBrowserElement)
13166 *aIsInBrowserElement = (GetInheritedFrameType() == eFrameTypeBrowser);
13167 return NS_OK;
13170 /* [infallible] */ NS_IMETHODIMP
13171 nsDocShell::GetIsInBrowserOrApp(bool* aIsInBrowserOrApp)
13173 switch (GetInheritedFrameType()) {
13174 case eFrameTypeRegular:
13175 *aIsInBrowserOrApp = false;
13176 break;
13177 case eFrameTypeBrowser:
13178 case eFrameTypeApp:
13179 *aIsInBrowserOrApp = true;
13180 break;
13183 return NS_OK;
13186 /* [infallible] */ NS_IMETHODIMP
13187 nsDocShell::GetAppId(uint32_t* aAppId)
13189 if (mOwnOrContainingAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID) {
13190 *aAppId = mOwnOrContainingAppId;
13191 return NS_OK;
13194 nsCOMPtr<nsIDocShell> parent;
13195 GetSameTypeParentIgnoreBrowserAndAppBoundaries(getter_AddRefs(parent));
13197 if (!parent) {
13198 *aAppId = nsIScriptSecurityManager::NO_APP_ID;
13199 return NS_OK;
13202 return parent->GetAppId(aAppId);
13205 NS_IMETHODIMP
13206 nsDocShell::GetAppManifestURL(nsAString& aAppManifestURL)
13208 uint32_t appId;
13209 GetAppId(&appId);
13211 if (appId != nsIScriptSecurityManager::NO_APP_ID &&
13212 appId != nsIScriptSecurityManager::UNKNOWN_APP_ID) {
13213 nsCOMPtr<nsIAppsService> appsService =
13214 do_GetService(APPS_SERVICE_CONTRACTID);
13215 NS_ASSERTION(appsService, "No AppsService available");
13216 appsService->GetManifestURLByLocalId(appId, aAppManifestURL);
13217 } else {
13218 aAppManifestURL.SetLength(0);
13221 return NS_OK;
13224 NS_IMETHODIMP
13225 nsDocShell::GetAsyncPanZoomEnabled(bool* aOut)
13227 if (TabChild* tabChild = TabChild::GetFrom(this)) {
13228 *aOut = tabChild->IsAsyncPanZoomEnabled();
13229 return NS_OK;
13231 *aOut = false;
13232 return NS_OK;
13235 bool
13236 nsDocShell::HasUnloadedParent()
13238 nsRefPtr<nsDocShell> parent = GetParentDocshell();
13239 while (parent) {
13240 bool inUnload = false;
13241 parent->GetIsInUnload(&inUnload);
13242 if (inUnload) {
13243 return true;
13245 parent = parent->GetParentDocshell();
13247 return false;
13250 bool
13251 nsDocShell::IsInvisible()
13253 return mInvisible;
13256 void
13257 nsDocShell::SetInvisible(bool aInvisible)
13259 mInvisible = aInvisible;
13262 void
13263 nsDocShell::SetOpener(nsITabParent* aOpener)
13265 mOpener = do_GetWeakReference(aOpener);
13268 nsITabParent*
13269 nsDocShell::GetOpener()
13271 nsCOMPtr<nsITabParent> opener(do_QueryReferent(mOpener));
13272 return opener;
13275 void
13276 nsDocShell::SetOpenedRemote(nsITabParent* aOpenedRemote)
13278 mOpenedRemote = do_GetWeakReference(aOpenedRemote);
13281 nsITabParent*
13282 nsDocShell::GetOpenedRemote()
13284 nsCOMPtr<nsITabParent> openedRemote(do_QueryReferent(mOpenedRemote));
13285 return openedRemote;
13288 URLSearchParams*
13289 nsDocShell::GetURLSearchParams()
13291 return mURLSearchParams;