summaryrefslogtreecommitdiffstats
path: root/docshell/base/nsDocShell.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'docshell/base/nsDocShell.cpp')
-rw-r--r--docshell/base/nsDocShell.cpp14853
1 files changed, 14853 insertions, 0 deletions
diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp
new file mode 100644
index 000000000..ab119a016
--- /dev/null
+++ b/docshell/base/nsDocShell.cpp
@@ -0,0 +1,14853 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsDocShell.h"
+
+#include <algorithm>
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/AutoRestore.h"
+#include "mozilla/BasePrincipal.h"
+#include "mozilla/Casting.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/ChromeUtils.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/TabChild.h"
+#include "mozilla/dom/ProfileTimelineMarkerBinding.h"
+#include "mozilla/dom/ScreenOrientation.h"
+#include "mozilla/dom/ToJSValue.h"
+#include "mozilla/dom/PermissionMessageUtils.h"
+#include "mozilla/dom/workers/ServiceWorkerManager.h"
+#include "mozilla/EventStateManager.h"
+#include "mozilla/LoadInfo.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Services.h"
+#include "mozilla/StartupTimeline.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/Unused.h"
+#include "Navigator.h"
+#include "URIUtils.h"
+#include "mozilla/dom/DocGroup.h"
+#include "mozilla/dom/TabGroup.h"
+
+#include "nsIContent.h"
+#include "nsIContentInlines.h"
+#include "nsIDocument.h"
+#include "nsIDOMDocument.h"
+#include "nsIDOMElement.h"
+
+#include "nsArray.h"
+#include "nsArrayUtils.h"
+#include "nsICaptivePortalService.h"
+#include "nsIDOMStorage.h"
+#include "nsIContentViewer.h"
+#include "nsIDocumentLoaderFactory.h"
+#include "nsCURILoader.h"
+#include "nsDocShellCID.h"
+#include "nsDOMCID.h"
+#include "nsNetCID.h"
+#include "nsNetUtil.h"
+#include "mozilla/net/ReferrerPolicy.h"
+#include "nsRect.h"
+#include "prenv.h"
+#include "nsIDOMWindow.h"
+#include "nsIGlobalObject.h"
+#include "nsIViewSourceChannel.h"
+#include "nsIWebBrowserChrome.h"
+#include "nsPoint.h"
+#include "nsIObserverService.h"
+#include "nsIPrompt.h"
+#include "nsIAuthPrompt.h"
+#include "nsIAuthPrompt2.h"
+#include "nsIChannelEventSink.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsIScriptObjectPrincipal.h"
+#include "nsIScrollableFrame.h"
+#include "nsContentPolicyUtils.h" // NS_CheckContentLoadPolicy(...)
+#include "nsISeekableStream.h"
+#include "nsAutoPtr.h"
+#include "nsQueryObject.h"
+#include "nsIWritablePropertyBag2.h"
+#include "nsIAppShell.h"
+#include "nsWidgetsCID.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsView.h"
+#include "nsViewManager.h"
+#include "nsIScriptChannel.h"
+#include "nsITimedChannel.h"
+#include "nsIPrivacyTransitionObserver.h"
+#include "nsIReflowObserver.h"
+#include "nsIScrollObserver.h"
+#include "nsIDocShellTreeItem.h"
+#include "nsIChannel.h"
+#include "IHistory.h"
+#include "nsViewSourceHandler.h"
+#include "nsWhitespaceTokenizer.h"
+#include "nsICookieService.h"
+#include "nsIConsoleReportCollector.h"
+#include "nsObjectLoadingContent.h"
+
+// we want to explore making the document own the load group
+// so we can associate the document URI with the load group.
+// until this point, we have an evil hack:
+#include "nsIHttpChannelInternal.h"
+#include "nsPILoadGroupInternal.h"
+
+// Local Includes
+#include "nsDocShellLoadInfo.h"
+#include "nsCDefaultURIFixup.h"
+#include "nsDocShellEnumerator.h"
+#include "nsSHistory.h"
+#include "nsDocShellEditorData.h"
+#include "GeckoProfiler.h"
+#include "timeline/JavascriptTimelineMarker.h"
+
+// Helper Classes
+#include "nsError.h"
+#include "nsEscape.h"
+
+// Interfaces Needed
+#include "nsIFormPOSTActionChannel.h"
+#include "nsIUploadChannel.h"
+#include "nsIUploadChannel2.h"
+#include "nsIWebProgress.h"
+#include "nsILayoutHistoryState.h"
+#include "nsITimer.h"
+#include "nsISHistoryInternal.h"
+#include "nsIPrincipal.h"
+#include "nsNullPrincipal.h"
+#include "nsISHEntry.h"
+#include "nsIWindowWatcher.h"
+#include "nsIPromptFactory.h"
+#include "nsITransportSecurityInfo.h"
+#include "nsINode.h"
+#include "nsINSSErrorsService.h"
+#include "nsIApplicationCacheChannel.h"
+#include "nsIApplicationCacheContainer.h"
+#include "nsStreamUtils.h"
+#include "nsIController.h"
+#include "nsPICommandUpdater.h"
+#include "nsIDOMHTMLAnchorElement.h"
+#include "nsIWebBrowserChrome3.h"
+#include "nsITabChild.h"
+#include "nsISiteSecurityService.h"
+#include "nsStructuredCloneContainer.h"
+#include "nsIStructuredCloneContainer.h"
+#ifdef MOZ_PLACES
+#include "nsIFaviconService.h"
+#include "mozIPlacesPendingOperation.h"
+#include "mozIAsyncFavicons.h"
+#endif
+#include "nsINetworkPredictor.h"
+
+// Editor-related
+#include "nsIEditingSession.h"
+
+#include "nsPIDOMWindow.h"
+#include "nsGlobalWindow.h"
+#include "nsPIWindowRoot.h"
+#include "nsICachingChannel.h"
+#include "nsIMultiPartChannel.h"
+#include "nsIWyciwygChannel.h"
+
+// For reporting errors with the console service.
+// These can go away if error reporting is propagated up past nsDocShell.
+#include "nsIScriptError.h"
+
+// used to dispatch urls to default protocol handlers
+#include "nsCExternalHandlerService.h"
+#include "nsIExternalProtocolService.h"
+
+#include "nsFocusManager.h"
+
+#include "nsITextToSubURI.h"
+
+#include "nsIJARChannel.h"
+
+#include "mozilla/Logging.h"
+
+#include "nsISelectionDisplay.h"
+
+#include "nsIGlobalHistory2.h"
+
+#include "nsIFrame.h"
+#include "nsSubDocumentFrame.h"
+
+// for embedding
+#include "nsIWebBrowserChromeFocus.h"
+
+#if NS_PRINT_PREVIEW
+#include "nsIDocumentViewerPrint.h"
+#include "nsIWebBrowserPrint.h"
+#endif
+
+#include "nsContentUtils.h"
+#include "nsIContentSecurityPolicy.h"
+#include "nsILoadInfo.h"
+#include "nsSandboxFlags.h"
+#include "nsXULAppAPI.h"
+#include "nsDOMNavigationTiming.h"
+#include "nsISecurityUITelemetry.h"
+#include "nsIAppsService.h"
+#include "nsDSURIContentListener.h"
+#include "nsDocShellLoadTypes.h"
+#include "nsDocShellTransferableHooks.h"
+#include "nsICommandManager.h"
+#include "nsIDOMNode.h"
+#include "nsIDocShellTreeOwner.h"
+#include "nsIHttpChannel.h"
+#include "nsIIDNService.h"
+#include "nsIInputStreamChannel.h"
+#include "nsINestedURI.h"
+#include "nsISHContainer.h"
+#include "nsISHistory.h"
+#include "nsISecureBrowserUI.h"
+#include "nsISocketProvider.h"
+#include "nsIStringBundle.h"
+#include "nsIURIFixup.h"
+#include "nsIURILoader.h"
+#include "nsIURL.h"
+#include "nsIWebBrowserFind.h"
+#include "nsIWidget.h"
+#include "mozilla/dom/EncodingUtils.h"
+#include "mozilla/dom/PerformanceNavigation.h"
+#include "mozilla/dom/ScriptSettings.h"
+
+#ifdef MOZ_TOOLKIT_SEARCH
+#include "nsIBrowserSearchService.h"
+#endif
+
+#include "mozIThirdPartyUtil.h"
+
+static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
+
+#if defined(DEBUG_bryner) || defined(DEBUG_chb)
+//#define DEBUG_DOCSHELL_FOCUS
+#define DEBUG_PAGE_CACHE
+#endif
+
+#ifdef XP_WIN
+#include <process.h>
+#define getpid _getpid
+#else
+#include <unistd.h> // for getpid()
+#endif
+
+using namespace mozilla;
+using namespace mozilla::dom;
+using mozilla::dom::workers::ServiceWorkerManager;
+
+// True means sUseErrorPages has been added to
+// preferences var cache.
+static bool gAddedPreferencesVarCache = false;
+
+bool nsDocShell::sUseErrorPages = false;
+
+// Number of documents currently loading
+static int32_t gNumberOfDocumentsLoading = 0;
+
+// Global count of existing docshells.
+static int32_t gDocShellCount = 0;
+
+// Global count of docshells with the private attribute set
+static uint32_t gNumberOfPrivateDocShells = 0;
+
+// Global reference to the URI fixup service.
+nsIURIFixup* nsDocShell::sURIFixup = 0;
+
+// True means we validate window targets to prevent frameset
+// spoofing. Initialize this to a non-bolean value so we know to check
+// the pref on the creation of the first docshell.
+static uint32_t gValidateOrigin = 0xffffffff;
+
+// Hint for native dispatch of events on how long to delay after
+// all documents have loaded in milliseconds before favoring normal
+// native event dispatch priorites over performance
+// Can be overridden with docshell.event_starvation_delay_hint pref.
+#define NS_EVENT_STARVATION_DELAY_HINT 2000
+
+#ifdef DEBUG
+static mozilla::LazyLogModule gDocShellLog("nsDocShell");
+#endif
+static mozilla::LazyLogModule gDocShellLeakLog("nsDocShellLeak");;
+
+const char kBrandBundleURL[] = "chrome://branding/locale/brand.properties";
+const char kAppstringsBundleURL[] = "chrome://global/locale/appstrings.properties";
+
+static void
+FavorPerformanceHint(bool aPerfOverStarvation)
+{
+ nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
+ if (appShell) {
+ appShell->FavorPerformanceHint(
+ aPerfOverStarvation,
+ Preferences::GetUint("docshell.event_starvation_delay_hint",
+ NS_EVENT_STARVATION_DELAY_HINT));
+ }
+}
+
+//*****************************************************************************
+// <a ping> support
+//*****************************************************************************
+
+#define PREF_PINGS_ENABLED "browser.send_pings"
+#define PREF_PINGS_MAX_PER_LINK "browser.send_pings.max_per_link"
+#define PREF_PINGS_REQUIRE_SAME_HOST "browser.send_pings.require_same_host"
+
+// Check prefs to see if pings are enabled and if so what restrictions might
+// be applied.
+//
+// @param maxPerLink
+// This parameter returns the number of pings that are allowed per link click
+//
+// @param requireSameHost
+// This parameter returns true if pings are restricted to the same host as
+// the document in which the click occurs. If the same host restriction is
+// imposed, then we still allow for pings to cross over to different
+// protocols and ports for flexibility and because it is not possible to send
+// a ping via FTP.
+//
+// @returns
+// true if pings are enabled and false otherwise.
+//
+static bool
+PingsEnabled(int32_t* aMaxPerLink, bool* aRequireSameHost)
+{
+ bool allow = Preferences::GetBool(PREF_PINGS_ENABLED, false);
+
+ *aMaxPerLink = 1;
+ *aRequireSameHost = true;
+
+ if (allow) {
+ Preferences::GetInt(PREF_PINGS_MAX_PER_LINK, aMaxPerLink);
+ Preferences::GetBool(PREF_PINGS_REQUIRE_SAME_HOST, aRequireSameHost);
+ }
+
+ return allow;
+}
+
+typedef void (*ForEachPingCallback)(void* closure, nsIContent* content,
+ nsIURI* uri, nsIIOService* ios);
+
+static bool
+IsElementAnchor(nsIContent* aContent)
+{
+ // Make sure we are dealing with either an <A> or <AREA> element in the HTML
+ // or XHTML namespace.
+ return aContent->IsAnyOfHTMLElements(nsGkAtoms::a, nsGkAtoms::area);
+}
+
+static void
+ForEachPing(nsIContent* aContent, ForEachPingCallback aCallback, void* aClosure)
+{
+ // NOTE: Using nsIDOMHTMLAnchorElement::GetPing isn't really worth it here
+ // since we'd still need to parse the resulting string. Instead, we
+ // just parse the raw attribute. It might be nice if the content node
+ // implemented an interface that exposed an enumeration of nsIURIs.
+
+ // Make sure we are dealing with either an <A> or <AREA> element in the HTML
+ // or XHTML namespace.
+ if (!IsElementAnchor(aContent)) {
+ return;
+ }
+
+ nsCOMPtr<nsIAtom> pingAtom = NS_Atomize("ping");
+ if (!pingAtom) {
+ return;
+ }
+
+ nsAutoString value;
+ aContent->GetAttr(kNameSpaceID_None, pingAtom, value);
+ if (value.IsEmpty()) {
+ return;
+ }
+
+ nsCOMPtr<nsIIOService> ios = do_GetIOService();
+ if (!ios) {
+ return;
+ }
+
+ nsIDocument* doc = aContent->OwnerDoc();
+
+ nsWhitespaceTokenizer tokenizer(value);
+
+ while (tokenizer.hasMoreTokens()) {
+ nsCOMPtr<nsIURI> uri, baseURI = aContent->GetBaseURI();
+ ios->NewURI(NS_ConvertUTF16toUTF8(tokenizer.nextToken()),
+ doc->GetDocumentCharacterSet().get(),
+ baseURI, getter_AddRefs(uri));
+ // if we can't generate a valid URI, then there is nothing to do
+ if (!uri) {
+ continue;
+ }
+ // Explicitly not allow loading data: URIs
+ bool isDataScheme =
+ (NS_SUCCEEDED(uri->SchemeIs("data", &isDataScheme)) && isDataScheme);
+
+ if (!isDataScheme) {
+ aCallback(aClosure, aContent, uri, ios);
+ }
+ }
+}
+
+//----------------------------------------------------------------------
+
+// We wait this many milliseconds before killing the ping channel...
+#define PING_TIMEOUT 10000
+
+static void
+OnPingTimeout(nsITimer* aTimer, void* aClosure)
+{
+ nsILoadGroup* loadGroup = static_cast<nsILoadGroup*>(aClosure);
+ if (loadGroup) {
+ loadGroup->Cancel(NS_ERROR_ABORT);
+ }
+}
+
+class nsPingListener final
+ : public nsIStreamListener
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIREQUESTOBSERVER
+ NS_DECL_NSISTREAMLISTENER
+
+ nsPingListener()
+ {
+ }
+
+ void SetLoadGroup(nsILoadGroup* aLoadGroup) {
+ mLoadGroup = aLoadGroup;
+ }
+
+ nsresult StartTimeout();
+
+private:
+ ~nsPingListener();
+
+ nsCOMPtr<nsILoadGroup> mLoadGroup;
+ nsCOMPtr<nsITimer> mTimer;
+};
+
+NS_IMPL_ISUPPORTS(nsPingListener, nsIStreamListener, nsIRequestObserver)
+
+nsPingListener::~nsPingListener()
+{
+ if (mTimer) {
+ mTimer->Cancel();
+ mTimer = nullptr;
+ }
+}
+
+nsresult
+nsPingListener::StartTimeout()
+{
+ nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
+
+ if (timer) {
+ nsresult rv = timer->InitWithFuncCallback(OnPingTimeout, mLoadGroup,
+ PING_TIMEOUT,
+ nsITimer::TYPE_ONE_SHOT);
+ if (NS_SUCCEEDED(rv)) {
+ mTimer = timer;
+ return NS_OK;
+ }
+ }
+
+ return NS_ERROR_OUT_OF_MEMORY;
+}
+
+NS_IMETHODIMP
+nsPingListener::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPingListener::OnDataAvailable(nsIRequest* aRequest, nsISupports* aContext,
+ nsIInputStream* aStream, uint64_t aOffset,
+ uint32_t aCount)
+{
+ uint32_t result;
+ return aStream->ReadSegments(NS_DiscardSegment, nullptr, aCount, &result);
+}
+
+NS_IMETHODIMP
+nsPingListener::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext,
+ nsresult aStatus)
+{
+ mLoadGroup = nullptr;
+
+ if (mTimer) {
+ mTimer->Cancel();
+ mTimer = nullptr;
+ }
+
+ return NS_OK;
+}
+
+struct MOZ_STACK_CLASS SendPingInfo
+{
+ int32_t numPings;
+ int32_t maxPings;
+ bool requireSameHost;
+ nsIURI* target;
+ nsIURI* referrer;
+ nsIDocShell* docShell;
+ uint32_t referrerPolicy;
+};
+
+static void
+SendPing(void* aClosure, nsIContent* aContent, nsIURI* aURI,
+ nsIIOService* aIOService)
+{
+ SendPingInfo* info = static_cast<SendPingInfo*>(aClosure);
+ if (info->maxPings > -1 && info->numPings >= info->maxPings) {
+ return;
+ }
+
+ nsIDocument* doc = aContent->OwnerDoc();
+
+ nsCOMPtr<nsIChannel> chan;
+ NS_NewChannel(getter_AddRefs(chan),
+ aURI,
+ doc,
+ info->requireSameHost
+ ? nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED
+ : nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
+ nsIContentPolicy::TYPE_PING,
+ nullptr, // aLoadGroup
+ nullptr, // aCallbacks
+ nsIRequest::LOAD_NORMAL, // aLoadFlags,
+ aIOService);
+
+ if (!chan) {
+ return;
+ }
+
+ // Don't bother caching the result of this URI load, but do not exempt
+ // it from Safe Browsing.
+ chan->SetLoadFlags(nsIRequest::INHIBIT_CACHING | nsIChannel::LOAD_CLASSIFY_URI);
+
+ nsCOMPtr<nsIHttpChannel> httpChan = do_QueryInterface(chan);
+ if (!httpChan) {
+ return;
+ }
+
+ // This is needed in order for 3rd-party cookie blocking to work.
+ nsCOMPtr<nsIHttpChannelInternal> httpInternal = do_QueryInterface(httpChan);
+ if (httpInternal) {
+ httpInternal->SetDocumentURI(doc->GetDocumentURI());
+ }
+
+ httpChan->SetRequestMethod(NS_LITERAL_CSTRING("POST"));
+
+ // Remove extraneous request headers (to reduce request size)
+ httpChan->SetRequestHeader(NS_LITERAL_CSTRING("accept"),
+ EmptyCString(), false);
+ httpChan->SetRequestHeader(NS_LITERAL_CSTRING("accept-language"),
+ EmptyCString(), false);
+ httpChan->SetRequestHeader(NS_LITERAL_CSTRING("accept-encoding"),
+ EmptyCString(), false);
+
+ // Always send a Ping-To header.
+ nsAutoCString pingTo;
+ if (NS_SUCCEEDED(info->target->GetSpec(pingTo))) {
+ httpChan->SetRequestHeader(NS_LITERAL_CSTRING("Ping-To"), pingTo, false);
+ }
+
+ nsCOMPtr<nsIScriptSecurityManager> sm =
+ do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
+
+ if (sm && info->referrer) {
+ bool referrerIsSecure;
+ uint32_t flags = nsIProtocolHandler::URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT;
+ nsresult rv = NS_URIChainHasFlags(info->referrer, flags, &referrerIsSecure);
+
+ // Default to sending less data if NS_URIChainHasFlags() fails.
+ referrerIsSecure = NS_FAILED(rv) || referrerIsSecure;
+
+ bool sameOrigin =
+ NS_SUCCEEDED(sm->CheckSameOriginURI(info->referrer, aURI, false));
+
+ // If both the address of the document containing the hyperlink being
+ // audited and "ping URL" have the same origin or the document containing
+ // the hyperlink being audited was not retrieved over an encrypted
+ // connection, send a Ping-From header.
+ if (sameOrigin || !referrerIsSecure) {
+ nsAutoCString pingFrom;
+ if (NS_SUCCEEDED(info->referrer->GetSpec(pingFrom))) {
+ httpChan->SetRequestHeader(NS_LITERAL_CSTRING("Ping-From"), pingFrom,
+ false);
+ }
+ }
+
+ // If the document containing the hyperlink being audited was not retrieved
+ // over an encrypted connection and its address does not have the same
+ // origin as "ping URL", send a referrer.
+ if (!sameOrigin && !referrerIsSecure) {
+ httpChan->SetReferrerWithPolicy(info->referrer, info->referrerPolicy);
+ }
+ }
+
+ nsCOMPtr<nsIUploadChannel2> uploadChan = do_QueryInterface(httpChan);
+ if (!uploadChan) {
+ return;
+ }
+
+ NS_NAMED_LITERAL_CSTRING(uploadData, "PING");
+
+ nsCOMPtr<nsIInputStream> uploadStream;
+ NS_NewPostDataStream(getter_AddRefs(uploadStream), false, uploadData);
+ if (!uploadStream) {
+ return;
+ }
+
+ uploadChan->ExplicitSetUploadStream(uploadStream,
+ NS_LITERAL_CSTRING("text/ping"),
+ uploadData.Length(),
+ NS_LITERAL_CSTRING("POST"), false);
+
+ // The channel needs to have a loadgroup associated with it, so that we can
+ // cancel the channel and any redirected channels it may create.
+ nsCOMPtr<nsILoadGroup> loadGroup = do_CreateInstance(NS_LOADGROUP_CONTRACTID);
+ if (!loadGroup) {
+ return;
+ }
+ nsCOMPtr<nsIInterfaceRequestor> callbacks = do_QueryInterface(info->docShell);
+ loadGroup->SetNotificationCallbacks(callbacks);
+ chan->SetLoadGroup(loadGroup);
+
+ RefPtr<nsPingListener> pingListener = new nsPingListener();
+ chan->AsyncOpen2(pingListener);
+
+ // Even if AsyncOpen failed, we still count this as a successful ping. It's
+ // possible that AsyncOpen may have failed after triggering some background
+ // process that may have written something to the network.
+ info->numPings++;
+
+ // Prevent ping requests from stalling and never being garbage collected...
+ if (NS_FAILED(pingListener->StartTimeout())) {
+ // If we failed to setup the timer, then we should just cancel the channel
+ // because we won't be able to ensure that it goes away in a timely manner.
+ chan->Cancel(NS_ERROR_ABORT);
+ return;
+ }
+ // if the channel openend successfully, then make the pingListener hold
+ // a strong reference to the loadgroup which is released in ::OnStopRequest
+ pingListener->SetLoadGroup(loadGroup);
+}
+
+// Spec: http://whatwg.org/specs/web-apps/current-work/#ping
+static void
+DispatchPings(nsIDocShell* aDocShell,
+ nsIContent* aContent,
+ nsIURI* aTarget,
+ nsIURI* aReferrer,
+ uint32_t aReferrerPolicy)
+{
+ SendPingInfo info;
+
+ if (!PingsEnabled(&info.maxPings, &info.requireSameHost)) {
+ return;
+ }
+ if (info.maxPings == 0) {
+ return;
+ }
+
+ info.numPings = 0;
+ info.target = aTarget;
+ info.referrer = aReferrer;
+ info.referrerPolicy = aReferrerPolicy;
+ info.docShell = aDocShell;
+
+ ForEachPing(aContent, SendPing, &info);
+}
+
+static nsDOMNavigationTiming::Type
+ConvertLoadTypeToNavigationType(uint32_t aLoadType)
+{
+ // Not initialized, assume it's normal load.
+ if (aLoadType == 0) {
+ aLoadType = LOAD_NORMAL;
+ }
+
+ auto result = nsDOMNavigationTiming::TYPE_RESERVED;
+ switch (aLoadType) {
+ case LOAD_NORMAL:
+ case LOAD_NORMAL_EXTERNAL:
+ case LOAD_NORMAL_BYPASS_CACHE:
+ case LOAD_NORMAL_BYPASS_PROXY:
+ case LOAD_NORMAL_BYPASS_PROXY_AND_CACHE:
+ case LOAD_NORMAL_REPLACE:
+ case LOAD_NORMAL_ALLOW_MIXED_CONTENT:
+ case LOAD_LINK:
+ case LOAD_STOP_CONTENT:
+ case LOAD_REPLACE_BYPASS_CACHE:
+ result = nsDOMNavigationTiming::TYPE_NAVIGATE;
+ break;
+ case LOAD_HISTORY:
+ result = nsDOMNavigationTiming::TYPE_BACK_FORWARD;
+ break;
+ case LOAD_RELOAD_NORMAL:
+ case LOAD_RELOAD_CHARSET_CHANGE:
+ case LOAD_RELOAD_BYPASS_CACHE:
+ case LOAD_RELOAD_BYPASS_PROXY:
+ case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
+ case LOAD_RELOAD_ALLOW_MIXED_CONTENT:
+ result = nsDOMNavigationTiming::TYPE_RELOAD;
+ break;
+ case LOAD_STOP_CONTENT_AND_REPLACE:
+ case LOAD_REFRESH:
+ case LOAD_BYPASS_HISTORY:
+ case LOAD_ERROR_PAGE:
+ case LOAD_PUSHSTATE:
+ result = nsDOMNavigationTiming::TYPE_RESERVED;
+ break;
+ default:
+ // NS_NOTREACHED("Unexpected load type value");
+ result = nsDOMNavigationTiming::TYPE_RESERVED;
+ break;
+ }
+
+ return result;
+}
+
+static nsISHEntry* GetRootSHEntry(nsISHEntry* aEntry);
+
+static void
+IncreasePrivateDocShellCount()
+{
+ gNumberOfPrivateDocShells++;
+ if (gNumberOfPrivateDocShells > 1 ||
+ !XRE_IsContentProcess()) {
+ return;
+ }
+
+ mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton();
+ cc->SendPrivateDocShellsExist(true);
+}
+
+static void
+DecreasePrivateDocShellCount()
+{
+ MOZ_ASSERT(gNumberOfPrivateDocShells > 0);
+ gNumberOfPrivateDocShells--;
+ if (!gNumberOfPrivateDocShells) {
+ if (XRE_IsContentProcess()) {
+ dom::ContentChild* cc = dom::ContentChild::GetSingleton();
+ cc->SendPrivateDocShellsExist(false);
+ return;
+ }
+
+ nsCOMPtr<nsIObserverService> obsvc = services::GetObserverService();
+ if (obsvc) {
+ obsvc->NotifyObservers(nullptr, "last-pb-context-exited", nullptr);
+ }
+ }
+}
+
+static uint64_t gDocshellIDCounter = 0;
+
+nsDocShell::nsDocShell()
+ : nsDocLoader()
+ , mDefaultScrollbarPref(Scrollbar_Auto, Scrollbar_Auto)
+ , mTreeOwner(nullptr)
+ , mChromeEventHandler(nullptr)
+ , mCharsetReloadState(eCharsetReloadInit)
+ , mChildOffset(0)
+ , mBusyFlags(BUSY_FLAGS_NONE)
+ , mAppType(nsIDocShell::APP_TYPE_UNKNOWN)
+ , mLoadType(0)
+ , mMarginWidth(-1)
+ , mMarginHeight(-1)
+ , mItemType(typeContent)
+ , mPreviousTransIndex(-1)
+ , mLoadedTransIndex(-1)
+ , mSandboxFlags(0)
+ , mOrientationLock(eScreenOrientation_None)
+ , mFullscreenAllowed(CHECK_ATTRIBUTES)
+ , mCreated(false)
+ , mAllowSubframes(true)
+ , mAllowPlugins(true)
+ , mAllowJavascript(true)
+ , mAllowMetaRedirects(true)
+ , mAllowImages(true)
+ , mAllowMedia(true)
+ , mAllowDNSPrefetch(true)
+ , mAllowWindowControl(true)
+ , mAllowContentRetargeting(true)
+ , mAllowContentRetargetingOnChildren(true)
+ , mUseErrorPages(false)
+ , mObserveErrorPages(true)
+ , mAllowAuth(true)
+ , mAllowKeywordFixup(false)
+ , mIsOffScreenBrowser(false)
+ , mIsActive(true)
+ , mDisableMetaRefreshWhenInactive(false)
+ , mIsPrerendered(false)
+ , mIsAppTab(false)
+ , mUseGlobalHistory(false)
+ , mUseRemoteTabs(false)
+ , mDeviceSizeIsPageSize(false)
+ , mWindowDraggingAllowed(false)
+ , mInFrameSwap(false)
+ , mInheritPrivateBrowsingId(true)
+ , mCanExecuteScripts(false)
+ , mFiredUnloadEvent(false)
+ , mEODForCurrentDocument(false)
+ , mURIResultedInDocument(false)
+ , mIsBeingDestroyed(false)
+ , mIsExecutingOnLoadHandler(false)
+ , mIsPrintingOrPP(false)
+ , mSavingOldViewer(false)
+ , mAffectPrivateSessionLifetime(true)
+ , mInvisible(false)
+ , mHasLoadedNonBlankURI(false)
+ , mBlankTiming(false)
+ , mCreatingDocument(false)
+#ifdef DEBUG
+ , mInEnsureScriptEnv(false)
+#endif
+ , mDefaultLoadFlags(nsIRequest::LOAD_NORMAL)
+ , mFrameType(FRAME_TYPE_REGULAR)
+ , mPrivateBrowsingId(0)
+ , mParentCharsetSource(0)
+ , mJSRunToCompletionDepth(0)
+ , mTouchEventsOverride(nsIDocShell::TOUCHEVENTS_OVERRIDE_NONE)
+{
+ AssertOriginAttributesMatchPrivateBrowsing();
+ mHistoryID = ++gDocshellIDCounter;
+ if (gDocShellCount++ == 0) {
+ NS_ASSERTION(sURIFixup == nullptr,
+ "Huh, sURIFixup not null in first nsDocShell ctor!");
+
+ CallGetService(NS_URIFIXUP_CONTRACTID, &sURIFixup);
+ }
+
+ MOZ_LOG(gDocShellLeakLog, LogLevel::Debug, ("DOCSHELL %p created\n", this));
+
+#ifdef DEBUG
+ // We're counting the number of |nsDocShells| to help find leaks
+ ++gNumberOfDocShells;
+ if (!PR_GetEnv("MOZ_QUIET")) {
+ printf_stderr("++DOCSHELL %p == %ld [pid = %d] [id = %llu]\n",
+ (void*)this,
+ gNumberOfDocShells,
+ getpid(),
+ AssertedCast<unsigned long long>(mHistoryID));
+ }
+#endif
+}
+
+nsDocShell::~nsDocShell()
+{
+ MOZ_ASSERT(!mObserved);
+
+ // Avoid notifying observers while we're in the dtor.
+ mIsBeingDestroyed = true;
+
+ Destroy();
+
+ nsCOMPtr<nsISHistoryInternal> shPrivate(do_QueryInterface(mSessionHistory));
+ if (shPrivate) {
+ shPrivate->SetRootDocShell(nullptr);
+ }
+
+ if (--gDocShellCount == 0) {
+ NS_IF_RELEASE(sURIFixup);
+ }
+
+ if (gDocShellLeakLog) {
+ MOZ_LOG(gDocShellLeakLog, LogLevel::Debug, ("DOCSHELL %p destroyed\n", this));
+ }
+
+#ifdef DEBUG
+ // We're counting the number of |nsDocShells| to help find leaks
+ --gNumberOfDocShells;
+ if (!PR_GetEnv("MOZ_QUIET")) {
+ printf_stderr("--DOCSHELL %p == %ld [pid = %d] [id = %llu]\n",
+ (void*)this,
+ gNumberOfDocShells,
+ getpid(),
+ AssertedCast<unsigned long long>(mHistoryID));
+ }
+#endif
+}
+
+nsresult
+nsDocShell::Init()
+{
+ nsresult rv = nsDocLoader::Init();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_ASSERTION(mLoadGroup, "Something went wrong!");
+
+ mContentListener = new nsDSURIContentListener(this);
+ rv = mContentListener->Init();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // We want to hold a strong ref to the loadgroup, so it better hold a weak
+ // ref to us... use an InterfaceRequestorProxy to do this.
+ nsCOMPtr<nsIInterfaceRequestor> proxy =
+ new InterfaceRequestorProxy(static_cast<nsIInterfaceRequestor*>(this));
+ mLoadGroup->SetNotificationCallbacks(proxy);
+
+ rv = nsDocLoader::AddDocLoaderAsChildOfRoot(this);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Add as |this| a progress listener to itself. A little weird, but
+ // simpler than reproducing all the listener-notification logic in
+ // overrides of the various methods via which nsDocLoader can be
+ // notified. Note that this holds an nsWeakPtr to ourselves, so it's ok.
+ return AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_DOCUMENT |
+ nsIWebProgress::NOTIFY_STATE_NETWORK);
+}
+
+void
+nsDocShell::DestroyChildren()
+{
+ nsCOMPtr<nsIDocShellTreeItem> shell;
+ nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+ while (iter.HasMore()) {
+ shell = do_QueryObject(iter.GetNext());
+ NS_ASSERTION(shell, "docshell has null child");
+
+ if (shell) {
+ shell->SetTreeOwner(nullptr);
+ }
+ }
+
+ nsDocLoader::DestroyChildren();
+}
+
+NS_IMPL_ADDREF_INHERITED(nsDocShell, nsDocLoader)
+NS_IMPL_RELEASE_INHERITED(nsDocShell, nsDocLoader)
+
+NS_INTERFACE_MAP_BEGIN(nsDocShell)
+ NS_INTERFACE_MAP_ENTRY(nsIDocShell)
+ NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeItem)
+ NS_INTERFACE_MAP_ENTRY(nsIWebNavigation)
+ NS_INTERFACE_MAP_ENTRY(nsIBaseWindow)
+ NS_INTERFACE_MAP_ENTRY(nsIScrollable)
+ NS_INTERFACE_MAP_ENTRY(nsITextScroll)
+ NS_INTERFACE_MAP_ENTRY(nsIDocCharset)
+ NS_INTERFACE_MAP_ENTRY(nsIRefreshURI)
+ NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
+ NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+ NS_INTERFACE_MAP_ENTRY(nsIContentViewerContainer)
+ NS_INTERFACE_MAP_ENTRY(nsIWebPageDescriptor)
+ NS_INTERFACE_MAP_ENTRY(nsIAuthPromptProvider)
+ NS_INTERFACE_MAP_ENTRY(nsILoadContext)
+ NS_INTERFACE_MAP_ENTRY(nsIWebShellServices)
+ NS_INTERFACE_MAP_ENTRY(nsILinkHandler)
+ NS_INTERFACE_MAP_ENTRY(nsIClipboardCommands)
+ NS_INTERFACE_MAP_ENTRY(nsIDOMStorageManager)
+ NS_INTERFACE_MAP_ENTRY(nsINetworkInterceptController)
+ NS_INTERFACE_MAP_ENTRY(nsIDeprecationWarner)
+NS_INTERFACE_MAP_END_INHERITING(nsDocLoader)
+
+NS_IMETHODIMP
+nsDocShell::GetInterface(const nsIID& aIID, void** aSink)
+{
+ NS_PRECONDITION(aSink, "null out param");
+
+ *aSink = nullptr;
+
+ if (aIID.Equals(NS_GET_IID(nsICommandManager))) {
+ NS_ENSURE_SUCCESS(EnsureCommandHandler(), NS_ERROR_FAILURE);
+ *aSink = mCommandManager;
+ } else if (aIID.Equals(NS_GET_IID(nsIURIContentListener))) {
+ *aSink = mContentListener;
+ } else if ((aIID.Equals(NS_GET_IID(nsIScriptGlobalObject)) ||
+ aIID.Equals(NS_GET_IID(nsIGlobalObject)) ||
+ aIID.Equals(NS_GET_IID(nsPIDOMWindowOuter)) ||
+ aIID.Equals(NS_GET_IID(mozIDOMWindowProxy)) ||
+ aIID.Equals(NS_GET_IID(nsIDOMWindow)) ||
+ aIID.Equals(NS_GET_IID(nsIDOMWindowInternal))) &&
+ NS_SUCCEEDED(EnsureScriptEnvironment())) {
+ return mScriptGlobal->QueryInterface(aIID, aSink);
+ } else if (aIID.Equals(NS_GET_IID(nsIDOMDocument)) &&
+ NS_SUCCEEDED(EnsureContentViewer())) {
+ mContentViewer->GetDOMDocument((nsIDOMDocument**)aSink);
+ return *aSink ? NS_OK : NS_NOINTERFACE;
+ } else if (aIID.Equals(NS_GET_IID(nsIDocument)) &&
+ NS_SUCCEEDED(EnsureContentViewer())) {
+ nsCOMPtr<nsIDocument> doc = mContentViewer->GetDocument();
+ doc.forget(aSink);
+ return *aSink ? NS_OK : NS_NOINTERFACE;
+ } else if (aIID.Equals(NS_GET_IID(nsIApplicationCacheContainer))) {
+ *aSink = nullptr;
+
+ // Return application cache associated with this docshell, if any
+
+ nsCOMPtr<nsIContentViewer> contentViewer;
+ GetContentViewer(getter_AddRefs(contentViewer));
+ if (!contentViewer) {
+ return NS_ERROR_NO_INTERFACE;
+ }
+
+ nsCOMPtr<nsIDOMDocument> domDoc;
+ contentViewer->GetDOMDocument(getter_AddRefs(domDoc));
+ NS_ASSERTION(domDoc, "Should have a document.");
+ if (!domDoc) {
+ return NS_ERROR_NO_INTERFACE;
+ }
+
+#if defined(DEBUG)
+ MOZ_LOG(gDocShellLog, LogLevel::Debug,
+ ("nsDocShell[%p]: returning app cache container %p",
+ this, domDoc.get()));
+#endif
+ return domDoc->QueryInterface(aIID, aSink);
+ } else if (aIID.Equals(NS_GET_IID(nsIPrompt)) &&
+ NS_SUCCEEDED(EnsureScriptEnvironment())) {
+ nsresult rv;
+ nsCOMPtr<nsIWindowWatcher> wwatch =
+ do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Get the an auth prompter for our window so that the parenting
+ // of the dialogs works as it should when using tabs.
+ nsIPrompt* prompt;
+ rv = wwatch->GetNewPrompter(mScriptGlobal->AsOuter(), &prompt);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ *aSink = prompt;
+ return NS_OK;
+ } else if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
+ aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
+ return NS_SUCCEEDED(GetAuthPrompt(PROMPT_NORMAL, aIID, aSink)) ?
+ NS_OK : NS_NOINTERFACE;
+ } else if (aIID.Equals(NS_GET_IID(nsISHistory))) {
+ nsCOMPtr<nsISHistory> shistory;
+ nsresult rv = GetSessionHistory(getter_AddRefs(shistory));
+ if (NS_SUCCEEDED(rv) && shistory) {
+ shistory.forget(aSink);
+ return NS_OK;
+ }
+ return NS_NOINTERFACE;
+ } else if (aIID.Equals(NS_GET_IID(nsIWebBrowserFind))) {
+ nsresult rv = EnsureFind();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ *aSink = mFind;
+ NS_ADDREF((nsISupports*)*aSink);
+ return NS_OK;
+ } else if (aIID.Equals(NS_GET_IID(nsIEditingSession))) {
+ nsCOMPtr<nsIEditingSession> es;
+ GetEditingSession(getter_AddRefs(es));
+ es.forget(aSink);
+ return *aSink ? NS_OK : NS_NOINTERFACE;
+ } else if (aIID.Equals(NS_GET_IID(nsIClipboardDragDropHookList)) &&
+ NS_SUCCEEDED(EnsureTransferableHookData())) {
+ *aSink = mTransferableHookData;
+ NS_ADDREF((nsISupports*)*aSink);
+ return NS_OK;
+ } else if (aIID.Equals(NS_GET_IID(nsISelectionDisplay))) {
+ nsIPresShell* shell = GetPresShell();
+ if (shell) {
+ return shell->QueryInterface(aIID, aSink);
+ }
+ } else if (aIID.Equals(NS_GET_IID(nsIDocShellTreeOwner))) {
+ nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
+ nsresult rv = GetTreeOwner(getter_AddRefs(treeOwner));
+ if (NS_SUCCEEDED(rv) && treeOwner) {
+ return treeOwner->QueryInterface(aIID, aSink);
+ }
+ } else if (aIID.Equals(NS_GET_IID(nsITabChild))) {
+ *aSink = GetTabChild().take();
+ return *aSink ? NS_OK : NS_ERROR_FAILURE;
+ } else if (aIID.Equals(NS_GET_IID(nsIContentFrameMessageManager))) {
+ nsCOMPtr<nsITabChild> tabChild =
+ do_GetInterface(static_cast<nsIDocShell*>(this));
+ nsCOMPtr<nsIContentFrameMessageManager> mm;
+ if (tabChild) {
+ tabChild->GetMessageManager(getter_AddRefs(mm));
+ } else {
+ if (nsPIDOMWindowOuter* win = GetWindow()) {
+ mm = do_QueryInterface(win->GetParentTarget());
+ }
+ }
+ *aSink = mm.get();
+ } else {
+ return nsDocLoader::GetInterface(aIID, aSink);
+ }
+
+ NS_IF_ADDREF(((nsISupports*)*aSink));
+ return *aSink ? NS_OK : NS_NOINTERFACE;
+}
+
+uint32_t
+nsDocShell::ConvertDocShellLoadInfoToLoadType(
+ nsDocShellInfoLoadType aDocShellLoadType)
+{
+ uint32_t loadType = LOAD_NORMAL;
+
+ switch (aDocShellLoadType) {
+ case nsIDocShellLoadInfo::loadNormal:
+ loadType = LOAD_NORMAL;
+ break;
+ case nsIDocShellLoadInfo::loadNormalReplace:
+ loadType = LOAD_NORMAL_REPLACE;
+ break;
+ case nsIDocShellLoadInfo::loadNormalExternal:
+ loadType = LOAD_NORMAL_EXTERNAL;
+ break;
+ case nsIDocShellLoadInfo::loadHistory:
+ loadType = LOAD_HISTORY;
+ break;
+ case nsIDocShellLoadInfo::loadNormalBypassCache:
+ loadType = LOAD_NORMAL_BYPASS_CACHE;
+ break;
+ case nsIDocShellLoadInfo::loadNormalBypassProxy:
+ loadType = LOAD_NORMAL_BYPASS_PROXY;
+ break;
+ case nsIDocShellLoadInfo::loadNormalBypassProxyAndCache:
+ loadType = LOAD_NORMAL_BYPASS_PROXY_AND_CACHE;
+ break;
+ case nsIDocShellLoadInfo::loadNormalAllowMixedContent:
+ loadType = LOAD_NORMAL_ALLOW_MIXED_CONTENT;
+ break;
+ case nsIDocShellLoadInfo::loadReloadNormal:
+ loadType = LOAD_RELOAD_NORMAL;
+ break;
+ case nsIDocShellLoadInfo::loadReloadCharsetChange:
+ loadType = LOAD_RELOAD_CHARSET_CHANGE;
+ break;
+ case nsIDocShellLoadInfo::loadReloadBypassCache:
+ loadType = LOAD_RELOAD_BYPASS_CACHE;
+ break;
+ case nsIDocShellLoadInfo::loadReloadBypassProxy:
+ loadType = LOAD_RELOAD_BYPASS_PROXY;
+ break;
+ case nsIDocShellLoadInfo::loadReloadBypassProxyAndCache:
+ loadType = LOAD_RELOAD_BYPASS_PROXY_AND_CACHE;
+ break;
+ case nsIDocShellLoadInfo::loadLink:
+ loadType = LOAD_LINK;
+ break;
+ case nsIDocShellLoadInfo::loadRefresh:
+ loadType = LOAD_REFRESH;
+ break;
+ case nsIDocShellLoadInfo::loadBypassHistory:
+ loadType = LOAD_BYPASS_HISTORY;
+ break;
+ case nsIDocShellLoadInfo::loadStopContent:
+ loadType = LOAD_STOP_CONTENT;
+ break;
+ case nsIDocShellLoadInfo::loadStopContentAndReplace:
+ loadType = LOAD_STOP_CONTENT_AND_REPLACE;
+ break;
+ case nsIDocShellLoadInfo::loadPushState:
+ loadType = LOAD_PUSHSTATE;
+ break;
+ case nsIDocShellLoadInfo::loadReplaceBypassCache:
+ loadType = LOAD_REPLACE_BYPASS_CACHE;
+ break;
+ case nsIDocShellLoadInfo::loadReloadMixedContent:
+ loadType = LOAD_RELOAD_ALLOW_MIXED_CONTENT;
+ break;
+ default:
+ NS_NOTREACHED("Unexpected nsDocShellInfoLoadType value");
+ }
+
+ return loadType;
+}
+
+nsDocShellInfoLoadType
+nsDocShell::ConvertLoadTypeToDocShellLoadInfo(uint32_t aLoadType)
+{
+ nsDocShellInfoLoadType docShellLoadType = nsIDocShellLoadInfo::loadNormal;
+ switch (aLoadType) {
+ case LOAD_NORMAL:
+ docShellLoadType = nsIDocShellLoadInfo::loadNormal;
+ break;
+ case LOAD_NORMAL_REPLACE:
+ docShellLoadType = nsIDocShellLoadInfo::loadNormalReplace;
+ break;
+ case LOAD_NORMAL_EXTERNAL:
+ docShellLoadType = nsIDocShellLoadInfo::loadNormalExternal;
+ break;
+ case LOAD_NORMAL_BYPASS_CACHE:
+ docShellLoadType = nsIDocShellLoadInfo::loadNormalBypassCache;
+ break;
+ case LOAD_NORMAL_BYPASS_PROXY:
+ docShellLoadType = nsIDocShellLoadInfo::loadNormalBypassProxy;
+ break;
+ case LOAD_NORMAL_BYPASS_PROXY_AND_CACHE:
+ docShellLoadType = nsIDocShellLoadInfo::loadNormalBypassProxyAndCache;
+ break;
+ case LOAD_NORMAL_ALLOW_MIXED_CONTENT:
+ docShellLoadType = nsIDocShellLoadInfo::loadNormalAllowMixedContent;
+ break;
+ case LOAD_HISTORY:
+ docShellLoadType = nsIDocShellLoadInfo::loadHistory;
+ break;
+ case LOAD_RELOAD_NORMAL:
+ docShellLoadType = nsIDocShellLoadInfo::loadReloadNormal;
+ break;
+ case LOAD_RELOAD_CHARSET_CHANGE:
+ docShellLoadType = nsIDocShellLoadInfo::loadReloadCharsetChange;
+ break;
+ case LOAD_RELOAD_BYPASS_CACHE:
+ docShellLoadType = nsIDocShellLoadInfo::loadReloadBypassCache;
+ break;
+ case LOAD_RELOAD_BYPASS_PROXY:
+ docShellLoadType = nsIDocShellLoadInfo::loadReloadBypassProxy;
+ break;
+ case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
+ docShellLoadType = nsIDocShellLoadInfo::loadReloadBypassProxyAndCache;
+ break;
+ case LOAD_LINK:
+ docShellLoadType = nsIDocShellLoadInfo::loadLink;
+ break;
+ case LOAD_REFRESH:
+ docShellLoadType = nsIDocShellLoadInfo::loadRefresh;
+ break;
+ case LOAD_BYPASS_HISTORY:
+ case LOAD_ERROR_PAGE:
+ docShellLoadType = nsIDocShellLoadInfo::loadBypassHistory;
+ break;
+ case LOAD_STOP_CONTENT:
+ docShellLoadType = nsIDocShellLoadInfo::loadStopContent;
+ break;
+ case LOAD_STOP_CONTENT_AND_REPLACE:
+ docShellLoadType = nsIDocShellLoadInfo::loadStopContentAndReplace;
+ break;
+ case LOAD_PUSHSTATE:
+ docShellLoadType = nsIDocShellLoadInfo::loadPushState;
+ break;
+ case LOAD_REPLACE_BYPASS_CACHE:
+ docShellLoadType = nsIDocShellLoadInfo::loadReplaceBypassCache;
+ break;
+ case LOAD_RELOAD_ALLOW_MIXED_CONTENT:
+ docShellLoadType = nsIDocShellLoadInfo::loadReloadMixedContent;
+ break;
+ default:
+ NS_NOTREACHED("Unexpected load type value");
+ }
+
+ return docShellLoadType;
+}
+
+NS_IMETHODIMP
+nsDocShell::LoadURI(nsIURI* aURI,
+ nsIDocShellLoadInfo* aLoadInfo,
+ uint32_t aLoadFlags,
+ bool aFirstParty)
+{
+ NS_PRECONDITION(aLoadInfo || (aLoadFlags & EXTRA_LOAD_FLAGS) == 0,
+ "Unexpected flags");
+ NS_PRECONDITION((aLoadFlags & 0xf) == 0, "Should not have these flags set");
+
+ // Note: we allow loads to get through here even if mFiredUnloadEvent is
+ // true; that case will get handled in LoadInternal or LoadHistoryEntry,
+ // so we pass false as the second parameter to IsNavigationAllowed.
+ // However, we don't allow the page to change location *in the middle of*
+ // firing beforeunload, so we do need to check if *beforeunload* is currently
+ // firing, so we call IsNavigationAllowed rather than just IsPrintingOrPP.
+ if (!IsNavigationAllowed(true, false)) {
+ return NS_OK; // JS may not handle returning of an error code
+ }
+
+ nsCOMPtr<nsIURI> referrer;
+ nsCOMPtr<nsIURI> originalURI;
+ bool loadReplace = false;
+ nsCOMPtr<nsIInputStream> postStream;
+ nsCOMPtr<nsIInputStream> headersStream;
+ nsCOMPtr<nsIPrincipal> triggeringPrincipal;
+ bool inheritPrincipal = false;
+ bool principalIsExplicit = false;
+ bool sendReferrer = true;
+ uint32_t referrerPolicy = mozilla::net::RP_Default;
+ bool isSrcdoc = false;
+ nsCOMPtr<nsISHEntry> shEntry;
+ nsXPIDLString target;
+ nsAutoString srcdoc;
+ nsCOMPtr<nsIDocShell> sourceDocShell;
+ nsCOMPtr<nsIURI> baseURI;
+
+ uint32_t loadType = MAKE_LOAD_TYPE(LOAD_NORMAL, aLoadFlags);
+
+ NS_ENSURE_ARG(aURI);
+
+ if (!StartupTimeline::HasRecord(StartupTimeline::FIRST_LOAD_URI) &&
+ mItemType == typeContent && !NS_IsAboutBlank(aURI)) {
+ StartupTimeline::RecordOnce(StartupTimeline::FIRST_LOAD_URI);
+ }
+
+ // Extract the info from the DocShellLoadInfo struct...
+ if (aLoadInfo) {
+ aLoadInfo->GetReferrer(getter_AddRefs(referrer));
+ aLoadInfo->GetOriginalURI(getter_AddRefs(originalURI));
+ aLoadInfo->GetLoadReplace(&loadReplace);
+ nsDocShellInfoLoadType lt = nsIDocShellLoadInfo::loadNormal;
+ aLoadInfo->GetLoadType(&lt);
+ // Get the appropriate loadType from nsIDocShellLoadInfo type
+ loadType = ConvertDocShellLoadInfoToLoadType(lt);
+
+ aLoadInfo->GetTriggeringPrincipal(getter_AddRefs(triggeringPrincipal));
+ aLoadInfo->GetInheritPrincipal(&inheritPrincipal);
+ aLoadInfo->GetPrincipalIsExplicit(&principalIsExplicit);
+ aLoadInfo->GetSHEntry(getter_AddRefs(shEntry));
+ aLoadInfo->GetTarget(getter_Copies(target));
+ aLoadInfo->GetPostDataStream(getter_AddRefs(postStream));
+ aLoadInfo->GetHeadersStream(getter_AddRefs(headersStream));
+ aLoadInfo->GetSendReferrer(&sendReferrer);
+ aLoadInfo->GetReferrerPolicy(&referrerPolicy);
+ aLoadInfo->GetIsSrcdocLoad(&isSrcdoc);
+ aLoadInfo->GetSrcdocData(srcdoc);
+ aLoadInfo->GetSourceDocShell(getter_AddRefs(sourceDocShell));
+ aLoadInfo->GetBaseURI(getter_AddRefs(baseURI));
+ }
+
+#if defined(DEBUG)
+ if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) {
+ nsAutoCString uristr;
+ aURI->GetAsciiSpec(uristr);
+ MOZ_LOG(gDocShellLog, LogLevel::Debug,
+ ("nsDocShell[%p]: loading %s with flags 0x%08x",
+ this, uristr.get(), aLoadFlags));
+ }
+#endif
+
+ if (!shEntry &&
+ !LOAD_TYPE_HAS_FLAGS(loadType, LOAD_FLAGS_REPLACE_HISTORY)) {
+ // First verify if this is a subframe.
+ nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
+ GetSameTypeParent(getter_AddRefs(parentAsItem));
+ nsCOMPtr<nsIDocShell> parentDS(do_QueryInterface(parentAsItem));
+ uint32_t parentLoadType;
+
+ if (parentDS && parentDS != static_cast<nsIDocShell*>(this)) {
+ /* OK. It is a subframe. Checkout the
+ * parent's loadtype. If the parent was loaded thro' a history
+ * mechanism, then get the SH entry for the child from the parent.
+ * This is done to restore frameset navigation while going back/forward.
+ * If the parent was loaded through any other loadType, set the
+ * child's loadType too accordingly, so that session history does not
+ * get confused.
+ */
+
+ // Get the parent's load type
+ parentDS->GetLoadType(&parentLoadType);
+
+ // Get the ShEntry for the child from the parent
+ nsCOMPtr<nsISHEntry> currentSH;
+ bool oshe = false;
+ parentDS->GetCurrentSHEntry(getter_AddRefs(currentSH), &oshe);
+ bool dynamicallyAddedChild = mDynamicallyCreated;
+ if (!dynamicallyAddedChild && !oshe && currentSH) {
+ currentSH->HasDynamicallyAddedChild(&dynamicallyAddedChild);
+ }
+ if (!dynamicallyAddedChild) {
+ // Only use the old SHEntry, if we're sure enough that
+ // it wasn't originally for some other frame.
+ parentDS->GetChildSHEntry(mChildOffset, getter_AddRefs(shEntry));
+ }
+
+ // Make some decisions on the child frame's loadType based on the
+ // parent's loadType.
+ if (!mCurrentURI) {
+ // This is a newly created frame. Check for exception cases first.
+ // By default the subframe will inherit the parent's loadType.
+ if (shEntry && (parentLoadType == LOAD_NORMAL ||
+ parentLoadType == LOAD_LINK ||
+ parentLoadType == LOAD_NORMAL_EXTERNAL)) {
+ // The parent was loaded normally. In this case, this *brand new*
+ // child really shouldn't have a SHEntry. If it does, it could be
+ // because the parent is replacing an existing frame with a new frame,
+ // in the onLoadHandler. We don't want this url to get into session
+ // history. Clear off shEntry, and set load type to
+ // LOAD_BYPASS_HISTORY.
+ bool inOnLoadHandler = false;
+ parentDS->GetIsExecutingOnLoadHandler(&inOnLoadHandler);
+ if (inOnLoadHandler) {
+ loadType = LOAD_NORMAL_REPLACE;
+ shEntry = nullptr;
+ }
+ } else if (parentLoadType == LOAD_REFRESH) {
+ // Clear shEntry. For refresh loads, we have to load
+ // what comes thro' the pipe, not what's in history.
+ shEntry = nullptr;
+ } else if ((parentLoadType == LOAD_BYPASS_HISTORY) ||
+ (shEntry &&
+ ((parentLoadType & LOAD_CMD_HISTORY) ||
+ (parentLoadType == LOAD_RELOAD_NORMAL) ||
+ (parentLoadType == LOAD_RELOAD_CHARSET_CHANGE)))) {
+ // If the parent url, bypassed history or was loaded from
+ // history, pass on the parent's loadType to the new child
+ // frame too, so that the child frame will also
+ // avoid getting into history.
+ loadType = parentLoadType;
+ } else if (parentLoadType == LOAD_ERROR_PAGE) {
+ // If the parent document is an error page, we don't
+ // want to update global/session history. However,
+ // this child frame is not an error page.
+ loadType = LOAD_BYPASS_HISTORY;
+ } else if ((parentLoadType == LOAD_RELOAD_BYPASS_CACHE) ||
+ (parentLoadType == LOAD_RELOAD_BYPASS_PROXY) ||
+ (parentLoadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE)) {
+ // the new frame should inherit the parent's load type so that it also
+ // bypasses the cache and/or proxy
+ loadType = parentLoadType;
+ }
+ } else {
+ // This is a pre-existing subframe. If the load was not originally
+ // initiated by session history, (if (!shEntry) condition succeeded) and
+ // mCurrentURI is not null, it is possible that a parent's onLoadHandler
+ // or even self's onLoadHandler is loading a new page in this child.
+ // Check parent's and self's busy flag and if it is set, we don't want
+ // this onLoadHandler load to get in to session history.
+ uint32_t parentBusy = BUSY_FLAGS_NONE;
+ uint32_t selfBusy = BUSY_FLAGS_NONE;
+ parentDS->GetBusyFlags(&parentBusy);
+ GetBusyFlags(&selfBusy);
+ if (parentBusy & BUSY_FLAGS_BUSY ||
+ selfBusy & BUSY_FLAGS_BUSY) {
+ loadType = LOAD_NORMAL_REPLACE;
+ shEntry = nullptr;
+ }
+ }
+ } // parentDS
+ else {
+ // This is the root docshell. If we got here while
+ // executing an onLoad Handler,this load will not go
+ // into session history.
+ bool inOnLoadHandler = false;
+ GetIsExecutingOnLoadHandler(&inOnLoadHandler);
+ if (inOnLoadHandler) {
+ loadType = LOAD_NORMAL_REPLACE;
+ }
+ }
+ } // !shEntry
+
+ if (shEntry) {
+#ifdef DEBUG
+ MOZ_LOG(gDocShellLog, LogLevel::Debug,
+ ("nsDocShell[%p]: loading from session history", this));
+#endif
+
+ return LoadHistoryEntry(shEntry, loadType);
+ }
+
+ // On history navigation via Back/Forward buttons, don't execute
+ // automatic JavaScript redirection such as |location.href = ...| or
+ // |window.open()|
+ //
+ // LOAD_NORMAL: window.open(...) etc.
+ // LOAD_STOP_CONTENT: location.href = ..., location.assign(...)
+ if ((loadType == LOAD_NORMAL || loadType == LOAD_STOP_CONTENT) &&
+ ShouldBlockLoadingForBackButton()) {
+ return NS_OK;
+ }
+
+ // Perform the load...
+
+ // We need a principalToInherit.
+ //
+ // If principalIsExplicit is not set there are 4 possibilities:
+ // (1) If the system principal or an expanded principal was passed
+ // in and we're a typeContent docshell, inherit the principal
+ // from the current document instead.
+ // (2) In all other cases when the principal passed in is not null,
+ // use that principal.
+ // (3) If the caller has allowed inheriting from the current document,
+ // or if we're being called from system code (eg chrome JS or pure
+ // C++) then inheritPrincipal should be true and InternalLoad will get
+ // a principal from the current document. If none of these things are
+ // true, then
+ // (4) we don't pass a principal into the channel, and a principal will be
+ // created later from the channel's internal data.
+ //
+ // If principalIsExplicit *is* set, there are 4 possibilities
+ // (1) If the system principal or an expanded principal was passed in
+ // and we're a typeContent docshell, return an error.
+ // (2) In all other cases when the principal passed in is not null,
+ // use that principal.
+ // (3) If the caller has allowed inheriting from the current document,
+ // then inheritPrincipal should be true and InternalLoad will get
+ // a principal from the current document. If none of these things are
+ // true, then
+ // (4) we dont' pass a principal into the channel, and a principal will be
+ // created later from the channel's internal data.
+ nsCOMPtr<nsIPrincipal> principalToInherit = triggeringPrincipal;
+ if (principalToInherit && mItemType != typeChrome) {
+ if (nsContentUtils::IsSystemPrincipal(principalToInherit)) {
+ if (principalIsExplicit) {
+ return NS_ERROR_DOM_SECURITY_ERR;
+ }
+ principalToInherit = nullptr;
+ inheritPrincipal = true;
+ } else if (nsContentUtils::IsExpandedPrincipal(principalToInherit)) {
+ if (principalIsExplicit) {
+ return NS_ERROR_DOM_SECURITY_ERR;
+ }
+ // Don't inherit from the current page. Just do the safe thing
+ // and pretend that we were loaded by a nullprincipal.
+ //
+ // We didn't inherit OriginAttributes here as ExpandedPrincipal doesn't
+ // have origin attributes.
+ principalToInherit = nsNullPrincipal::CreateWithInheritedAttributes(this);
+ inheritPrincipal = false;
+ }
+ }
+ if (!principalToInherit && !inheritPrincipal && !principalIsExplicit) {
+ // See if there's system or chrome JS code running
+ inheritPrincipal = nsContentUtils::LegacyIsCallerChromeOrNativeCode();
+ }
+
+ if (aLoadFlags & LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL) {
+ inheritPrincipal = false;
+ principalToInherit = nsNullPrincipal::CreateWithInheritedAttributes(this);
+ }
+
+ // If the triggeringPrincipal is not passed explicitly, we first try to create
+ // a principal from the referrer, since the referrer URI reflects the web origin
+ // that triggered the load. If there is no referrer URI, we fall back to using
+ // the SystemPrincipal. It's safe to assume that no provided triggeringPrincipal
+ // and no referrer simulate a load that was triggered by the system.
+ // It's important to note that this block of code needs to appear *after* the block
+ // where we munge the principalToInherit, because otherwise we would never enter
+ // code blocks checking if the principalToInherit is null and we will end up with
+ // a wrong inheritPrincipal flag.
+ if (!triggeringPrincipal) {
+ if (referrer) {
+ nsresult rv = CreatePrincipalFromReferrer(referrer,
+ getter_AddRefs(triggeringPrincipal));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ else {
+ triggeringPrincipal = nsContentUtils::GetSystemPrincipal();
+ }
+ }
+
+ uint32_t flags = 0;
+
+ if (inheritPrincipal) {
+ flags |= INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL;
+ }
+
+ if (!sendReferrer) {
+ flags |= INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER;
+ }
+
+ if (aLoadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) {
+ flags |= INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
+ }
+
+ if (aLoadFlags & LOAD_FLAGS_FIRST_LOAD) {
+ flags |= INTERNAL_LOAD_FLAGS_FIRST_LOAD;
+ }
+
+ if (aLoadFlags & LOAD_FLAGS_BYPASS_CLASSIFIER) {
+ flags |= INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER;
+ }
+
+ if (aLoadFlags & LOAD_FLAGS_FORCE_ALLOW_COOKIES) {
+ flags |= INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES;
+ }
+
+ if (isSrcdoc) {
+ flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC;
+ }
+
+ return InternalLoad(aURI,
+ originalURI,
+ loadReplace,
+ referrer,
+ referrerPolicy,
+ triggeringPrincipal,
+ principalToInherit,
+ flags,
+ target,
+ nullptr, // No type hint
+ NullString(), // No forced download
+ postStream,
+ headersStream,
+ loadType,
+ nullptr, // No SHEntry
+ aFirstParty,
+ srcdoc,
+ sourceDocShell,
+ baseURI,
+ nullptr, // No nsIDocShell
+ nullptr); // No nsIRequest
+}
+
+NS_IMETHODIMP
+nsDocShell::LoadStream(nsIInputStream* aStream, nsIURI* aURI,
+ const nsACString& aContentType,
+ const nsACString& aContentCharset,
+ nsIDocShellLoadInfo* aLoadInfo)
+{
+ NS_ENSURE_ARG(aStream);
+
+ mAllowKeywordFixup = false;
+
+ // if the caller doesn't pass in a URI we need to create a dummy URI. necko
+ // currently requires a URI in various places during the load. Some consumers
+ // do as well.
+ nsCOMPtr<nsIURI> uri = aURI;
+ if (!uri) {
+ // HACK ALERT
+ nsresult rv = NS_OK;
+ uri = do_CreateInstance(NS_SIMPLEURI_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ // Make sure that the URI spec "looks" like a protocol and path...
+ // For now, just use a bogus protocol called "internal"
+ rv = uri->SetSpec(NS_LITERAL_CSTRING("internal:load-stream"));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+
+ uint32_t loadType = LOAD_NORMAL;
+ nsCOMPtr<nsIPrincipal> triggeringPrincipal;
+ if (aLoadInfo) {
+ nsDocShellInfoLoadType lt = nsIDocShellLoadInfo::loadNormal;
+ (void)aLoadInfo->GetLoadType(&lt);
+ // Get the appropriate LoadType from nsIDocShellLoadInfo type
+ loadType = ConvertDocShellLoadInfoToLoadType(lt);
+ aLoadInfo->GetTriggeringPrincipal(getter_AddRefs(triggeringPrincipal));
+ }
+
+ NS_ENSURE_SUCCESS(Stop(nsIWebNavigation::STOP_NETWORK), NS_ERROR_FAILURE);
+
+ mLoadType = loadType;
+
+ if (!triggeringPrincipal) {
+ triggeringPrincipal = nsContentUtils::GetSystemPrincipal();
+ }
+
+ // build up a channel for this stream.
+ nsCOMPtr<nsIChannel> channel;
+ nsresult rv = NS_NewInputStreamChannel(getter_AddRefs(channel),
+ uri,
+ aStream,
+ triggeringPrincipal,
+ nsILoadInfo::SEC_NORMAL,
+ nsIContentPolicy::TYPE_OTHER,
+ aContentType,
+ aContentCharset);
+ NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+
+ nsCOMPtr<nsIURILoader> uriLoader(do_GetService(NS_URI_LOADER_CONTRACTID));
+ NS_ENSURE_TRUE(uriLoader, NS_ERROR_FAILURE);
+
+ NS_ENSURE_SUCCESS(DoChannelLoad(channel, uriLoader, false),
+ NS_ERROR_FAILURE);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::CreateLoadInfo(nsIDocShellLoadInfo** aLoadInfo)
+{
+ nsDocShellLoadInfo* loadInfo = new nsDocShellLoadInfo();
+ nsCOMPtr<nsIDocShellLoadInfo> localRef(loadInfo);
+
+ localRef.forget(aLoadInfo);
+ return NS_OK;
+}
+
+/*
+ * Reset state to a new content model within the current document and the
+ * document viewer. Called by the document before initiating an out of band
+ * document.write().
+ */
+NS_IMETHODIMP
+nsDocShell::PrepareForNewContentModel()
+{
+ mEODForCurrentDocument = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::FirePageHideNotification(bool aIsUnload)
+{
+ if (mContentViewer && !mFiredUnloadEvent) {
+ // Keep an explicit reference since calling PageHide could release
+ // mContentViewer
+ nsCOMPtr<nsIContentViewer> contentViewer(mContentViewer);
+ mFiredUnloadEvent = true;
+
+ if (mTiming) {
+ mTiming->NotifyUnloadEventStart();
+ }
+
+ contentViewer->PageHide(aIsUnload);
+
+ if (mTiming) {
+ mTiming->NotifyUnloadEventEnd();
+ }
+
+ AutoTArray<nsCOMPtr<nsIDocShell>, 8> kids;
+ uint32_t n = mChildList.Length();
+ kids.SetCapacity(n);
+ for (uint32_t i = 0; i < n; i++) {
+ kids.AppendElement(do_QueryInterface(ChildAt(i)));
+ }
+
+ n = kids.Length();
+ for (uint32_t i = 0; i < n; ++i) {
+ if (kids[i]) {
+ kids[i]->FirePageHideNotification(aIsUnload);
+ }
+ }
+ // Now make sure our editor, if any, is detached before we go
+ // any farther.
+ DetachEditorFromWindow();
+ }
+
+ return NS_OK;
+}
+
+void
+nsDocShell::MaybeInitTiming()
+{
+ if (mTiming && !mBlankTiming) {
+ return;
+ }
+
+ if (mScriptGlobal && mBlankTiming) {
+ nsPIDOMWindowInner* innerWin =
+ mScriptGlobal->AsOuter()->GetCurrentInnerWindow();
+ if (innerWin && innerWin->GetPerformance()) {
+ mTiming = innerWin->GetPerformance()->GetDOMTiming();
+ mBlankTiming = false;
+ }
+ }
+
+ if (!mTiming) {
+ mTiming = new nsDOMNavigationTiming();
+ }
+
+ mTiming->NotifyNavigationStart(
+ mIsActive ? nsDOMNavigationTiming::DocShellState::eActive
+ : nsDOMNavigationTiming::DocShellState::eInactive);
+}
+
+//
+// Bug 13871: Prevent frameset spoofing
+//
+// This routine answers: 'Is origin's document from same domain as
+// target's document?'
+//
+// file: uris are considered the same domain for the purpose of
+// frame navigation regardless of script accessibility (bug 420425)
+//
+/* static */ bool
+nsDocShell::ValidateOrigin(nsIDocShellTreeItem* aOriginTreeItem,
+ nsIDocShellTreeItem* aTargetTreeItem)
+{
+ // We want to bypass this check for chrome callers, but only if there's
+ // JS on the stack. System callers still need to do it.
+ if (nsContentUtils::GetCurrentJSContext() &&
+ nsContentUtils::IsCallerChrome()) {
+ return true;
+ }
+
+ MOZ_ASSERT(aOriginTreeItem && aTargetTreeItem, "need two docshells");
+
+ // Get origin document principal
+ nsCOMPtr<nsIDocument> originDocument = aOriginTreeItem->GetDocument();
+ NS_ENSURE_TRUE(originDocument, false);
+
+ // Get target principal
+ nsCOMPtr<nsIDocument> targetDocument = aTargetTreeItem->GetDocument();
+ NS_ENSURE_TRUE(targetDocument, false);
+
+ bool equal;
+ nsresult rv = originDocument->NodePrincipal()->Equals(
+ targetDocument->NodePrincipal(), &equal);
+ if (NS_SUCCEEDED(rv) && equal) {
+ return true;
+ }
+
+ // Not strictly equal, special case if both are file: uris
+ bool originIsFile = false;
+ bool targetIsFile = false;
+ nsCOMPtr<nsIURI> originURI;
+ nsCOMPtr<nsIURI> targetURI;
+ nsCOMPtr<nsIURI> innerOriginURI;
+ nsCOMPtr<nsIURI> innerTargetURI;
+
+ rv = originDocument->NodePrincipal()->GetURI(getter_AddRefs(originURI));
+ if (NS_SUCCEEDED(rv) && originURI) {
+ innerOriginURI = NS_GetInnermostURI(originURI);
+ }
+
+ rv = targetDocument->NodePrincipal()->GetURI(getter_AddRefs(targetURI));
+ if (NS_SUCCEEDED(rv) && targetURI) {
+ innerTargetURI = NS_GetInnermostURI(targetURI);
+ }
+
+ return innerOriginURI && innerTargetURI &&
+ NS_SUCCEEDED(innerOriginURI->SchemeIs("file", &originIsFile)) &&
+ NS_SUCCEEDED(innerTargetURI->SchemeIs("file", &targetIsFile)) &&
+ originIsFile && targetIsFile;
+}
+
+nsresult
+nsDocShell::GetEldestPresContext(nsPresContext** aPresContext)
+{
+ NS_ENSURE_ARG_POINTER(aPresContext);
+ *aPresContext = nullptr;
+
+ nsCOMPtr<nsIContentViewer> viewer = mContentViewer;
+ while (viewer) {
+ nsCOMPtr<nsIContentViewer> prevViewer;
+ viewer->GetPreviousViewer(getter_AddRefs(prevViewer));
+ if (!prevViewer) {
+ return viewer->GetPresContext(aPresContext);
+ }
+ viewer = prevViewer;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetPresContext(nsPresContext** aPresContext)
+{
+ NS_ENSURE_ARG_POINTER(aPresContext);
+ *aPresContext = nullptr;
+
+ if (!mContentViewer) {
+ return NS_OK;
+ }
+
+ return mContentViewer->GetPresContext(aPresContext);
+}
+
+NS_IMETHODIMP_(nsIPresShell*)
+nsDocShell::GetPresShell()
+{
+ RefPtr<nsPresContext> presContext;
+ (void)GetPresContext(getter_AddRefs(presContext));
+ return presContext ? presContext->GetPresShell() : nullptr;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetEldestPresShell(nsIPresShell** aPresShell)
+{
+ nsresult rv = NS_OK;
+
+ NS_ENSURE_ARG_POINTER(aPresShell);
+ *aPresShell = nullptr;
+
+ RefPtr<nsPresContext> presContext;
+ (void)GetEldestPresContext(getter_AddRefs(presContext));
+
+ if (presContext) {
+ NS_IF_ADDREF(*aPresShell = presContext->GetPresShell());
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetContentViewer(nsIContentViewer** aContentViewer)
+{
+ NS_ENSURE_ARG_POINTER(aContentViewer);
+
+ *aContentViewer = mContentViewer;
+ NS_IF_ADDREF(*aContentViewer);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetChromeEventHandler(nsIDOMEventTarget* aChromeEventHandler)
+{
+ // Weak reference. Don't addref.
+ nsCOMPtr<EventTarget> handler = do_QueryInterface(aChromeEventHandler);
+ mChromeEventHandler = handler.get();
+
+ if (mScriptGlobal) {
+ mScriptGlobal->SetChromeEventHandler(mChromeEventHandler);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetChromeEventHandler(nsIDOMEventTarget** aChromeEventHandler)
+{
+ NS_ENSURE_ARG_POINTER(aChromeEventHandler);
+ nsCOMPtr<EventTarget> handler = mChromeEventHandler;
+ handler.forget(aChromeEventHandler);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetCurrentURI(nsIURI* aURI)
+{
+ // Note that securityUI will set STATE_IS_INSECURE, even if
+ // the scheme of |aURI| is "https".
+ SetCurrentURI(aURI, nullptr, true, 0);
+ return NS_OK;
+}
+
+bool
+nsDocShell::SetCurrentURI(nsIURI* aURI, nsIRequest* aRequest,
+ bool aFireOnLocationChange, uint32_t aLocationFlags)
+{
+ if (gDocShellLeakLog && MOZ_LOG_TEST(gDocShellLeakLog, LogLevel::Debug)) {
+ PR_LogPrint("DOCSHELL %p SetCurrentURI %s\n",
+ this, aURI ? aURI->GetSpecOrDefault().get() : "");
+ }
+
+ // We don't want to send a location change when we're displaying an error
+ // page, and we don't want to change our idea of "current URI" either
+ if (mLoadType == LOAD_ERROR_PAGE) {
+ return false;
+ }
+
+ mCurrentURI = NS_TryToMakeImmutable(aURI);
+
+ if (!NS_IsAboutBlank(mCurrentURI)) {
+ mHasLoadedNonBlankURI = true;
+ }
+
+ bool isRoot = false; // Is this the root docshell
+ bool isSubFrame = false; // Is this a subframe navigation?
+
+ nsCOMPtr<nsIDocShellTreeItem> root;
+
+ GetSameTypeRootTreeItem(getter_AddRefs(root));
+ if (root.get() == static_cast<nsIDocShellTreeItem*>(this)) {
+ // This is the root docshell
+ isRoot = true;
+ }
+ if (mLSHE) {
+ mLSHE->GetIsSubFrame(&isSubFrame);
+ }
+
+ if (!isSubFrame && !isRoot) {
+ /*
+ * We don't want to send OnLocationChange notifications when
+ * a subframe is being loaded for the first time, while
+ * visiting a frameset page
+ */
+ return false;
+ }
+
+ if (aFireOnLocationChange) {
+ FireOnLocationChange(this, aRequest, aURI, aLocationFlags);
+ }
+ return !aFireOnLocationChange;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetCharset(nsACString& aCharset)
+{
+ aCharset.Truncate();
+
+ nsIPresShell* presShell = GetPresShell();
+ NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
+ nsIDocument* doc = presShell->GetDocument();
+ NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
+ aCharset = doc->GetDocumentCharacterSet();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GatherCharsetMenuTelemetry()
+{
+ nsCOMPtr<nsIContentViewer> viewer;
+ GetContentViewer(getter_AddRefs(viewer));
+ if (!viewer) {
+ return NS_OK;
+ }
+
+ nsIDocument* doc = viewer->GetDocument();
+ if (!doc || doc->WillIgnoreCharsetOverride()) {
+ return NS_OK;
+ }
+
+ Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_USED, true);
+
+ bool isFileURL = false;
+ nsIURI* url = doc->GetOriginalURI();
+ if (url) {
+ url->SchemeIs("file", &isFileURL);
+ }
+
+ int32_t charsetSource = doc->GetDocumentCharacterSetSource();
+ switch (charsetSource) {
+ case kCharsetFromTopLevelDomain:
+ // Unlabeled doc on a domain that we map to a fallback encoding
+ Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 7);
+ break;
+ case kCharsetFromFallback:
+ case kCharsetFromDocTypeDefault:
+ case kCharsetFromCache:
+ case kCharsetFromParentFrame:
+ case kCharsetFromHintPrevDoc:
+ // Changing charset on an unlabeled doc.
+ if (isFileURL) {
+ Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 0);
+ } else {
+ Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 1);
+ }
+ break;
+ case kCharsetFromAutoDetection:
+ // Changing charset on unlabeled doc where chardet fired
+ if (isFileURL) {
+ Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 2);
+ } else {
+ Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 3);
+ }
+ break;
+ case kCharsetFromMetaPrescan:
+ case kCharsetFromMetaTag:
+ case kCharsetFromChannel:
+ // Changing charset on a doc that had a charset label.
+ Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 4);
+ break;
+ case kCharsetFromParentForced:
+ case kCharsetFromUserForced:
+ // Changing charset on a document that already had an override.
+ Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 5);
+ break;
+ case kCharsetFromIrreversibleAutoDetection:
+ case kCharsetFromOtherComponent:
+ case kCharsetFromByteOrderMark:
+ case kCharsetUninitialized:
+ default:
+ // Bug. This isn't supposed to happen.
+ Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 6);
+ break;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetCharset(const nsACString& aCharset)
+{
+ // set the charset override
+ return SetForcedCharset(aCharset);
+}
+
+NS_IMETHODIMP
+nsDocShell::SetForcedCharset(const nsACString& aCharset)
+{
+ if (aCharset.IsEmpty()) {
+ mForcedCharset.Truncate();
+ return NS_OK;
+ }
+ nsAutoCString encoding;
+ if (!EncodingUtils::FindEncodingForLabel(aCharset, encoding)) {
+ // Reject unknown labels
+ return NS_ERROR_INVALID_ARG;
+ }
+ if (!EncodingUtils::IsAsciiCompatible(encoding)) {
+ // Reject XSS hazards
+ return NS_ERROR_INVALID_ARG;
+ }
+ mForcedCharset = encoding;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetForcedCharset(nsACString& aResult)
+{
+ aResult = mForcedCharset;
+ return NS_OK;
+}
+
+void
+nsDocShell::SetParentCharset(const nsACString& aCharset,
+ int32_t aCharsetSource,
+ nsIPrincipal* aPrincipal)
+{
+ mParentCharset = aCharset;
+ mParentCharsetSource = aCharsetSource;
+ mParentCharsetPrincipal = aPrincipal;
+}
+
+void
+nsDocShell::GetParentCharset(nsACString& aCharset,
+ int32_t* aCharsetSource,
+ nsIPrincipal** aPrincipal)
+{
+ aCharset = mParentCharset;
+ *aCharsetSource = mParentCharsetSource;
+ NS_IF_ADDREF(*aPrincipal = mParentCharsetPrincipal);
+}
+
+NS_IMETHODIMP
+nsDocShell::GetChannelIsUnsafe(bool* aUnsafe)
+{
+ *aUnsafe = false;
+
+ nsIChannel* channel = GetCurrentDocChannel();
+ if (!channel) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(channel);
+ if (!jarChannel) {
+ return NS_OK;
+ }
+
+ return jarChannel->GetIsUnsafe(aUnsafe);
+}
+
+NS_IMETHODIMP
+nsDocShell::GetHasMixedActiveContentLoaded(bool* aHasMixedActiveContentLoaded)
+{
+ nsCOMPtr<nsIDocument> doc(GetDocument());
+ *aHasMixedActiveContentLoaded = doc && doc->GetHasMixedActiveContentLoaded();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetHasMixedActiveContentBlocked(bool* aHasMixedActiveContentBlocked)
+{
+ nsCOMPtr<nsIDocument> doc(GetDocument());
+ *aHasMixedActiveContentBlocked =
+ doc && doc->GetHasMixedActiveContentBlocked();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetHasMixedDisplayContentLoaded(bool* aHasMixedDisplayContentLoaded)
+{
+ nsCOMPtr<nsIDocument> doc(GetDocument());
+ *aHasMixedDisplayContentLoaded =
+ doc && doc->GetHasMixedDisplayContentLoaded();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetHasMixedDisplayContentBlocked(
+ bool* aHasMixedDisplayContentBlocked)
+{
+ nsCOMPtr<nsIDocument> doc(GetDocument());
+ *aHasMixedDisplayContentBlocked =
+ doc && doc->GetHasMixedDisplayContentBlocked();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetHasTrackingContentBlocked(bool* aHasTrackingContentBlocked)
+{
+ nsCOMPtr<nsIDocument> doc(GetDocument());
+ *aHasTrackingContentBlocked = doc && doc->GetHasTrackingContentBlocked();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetHasTrackingContentLoaded(bool* aHasTrackingContentLoaded)
+{
+ nsCOMPtr<nsIDocument> doc(GetDocument());
+ *aHasTrackingContentLoaded = doc && doc->GetHasTrackingContentLoaded();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetAllowPlugins(bool* aAllowPlugins)
+{
+ NS_ENSURE_ARG_POINTER(aAllowPlugins);
+
+ *aAllowPlugins = mAllowPlugins;
+ if (!mAllowPlugins) {
+ return NS_OK;
+ }
+
+ bool unsafe;
+ *aAllowPlugins = NS_SUCCEEDED(GetChannelIsUnsafe(&unsafe)) && !unsafe;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetAllowPlugins(bool aAllowPlugins)
+{
+ mAllowPlugins = aAllowPlugins;
+ // XXX should enable or disable a plugin host
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetAllowJavascript(bool* aAllowJavascript)
+{
+ NS_ENSURE_ARG_POINTER(aAllowJavascript);
+
+ *aAllowJavascript = mAllowJavascript;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetAllowJavascript(bool aAllowJavascript)
+{
+ mAllowJavascript = aAllowJavascript;
+ RecomputeCanExecuteScripts();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetUsePrivateBrowsing(bool* aUsePrivateBrowsing)
+{
+ NS_ENSURE_ARG_POINTER(aUsePrivateBrowsing);
+ AssertOriginAttributesMatchPrivateBrowsing();
+ *aUsePrivateBrowsing = mPrivateBrowsingId > 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetUsePrivateBrowsing(bool aUsePrivateBrowsing)
+{
+ nsContentUtils::ReportToConsoleNonLocalized(
+ NS_LITERAL_STRING("Only internal code is allowed to set the usePrivateBrowsing attribute"),
+ nsIScriptError::warningFlag,
+ NS_LITERAL_CSTRING("Internal API Used"),
+ mContentViewer ? mContentViewer->GetDocument() : nullptr);
+
+ if (!CanSetOriginAttributes()) {
+ bool changed = aUsePrivateBrowsing != (mPrivateBrowsingId > 0);
+
+ return changed ? NS_ERROR_FAILURE : NS_OK;
+ }
+
+ return SetPrivateBrowsing(aUsePrivateBrowsing);
+}
+
+NS_IMETHODIMP
+nsDocShell::SetPrivateBrowsing(bool aUsePrivateBrowsing)
+{
+ bool changed = aUsePrivateBrowsing != (mPrivateBrowsingId > 0);
+ if (changed) {
+ mPrivateBrowsingId = aUsePrivateBrowsing ? 1 : 0;
+
+ if (mItemType != typeChrome) {
+ mOriginAttributes.SyncAttributesWithPrivateBrowsing(aUsePrivateBrowsing);
+ }
+
+ if (mAffectPrivateSessionLifetime) {
+ if (aUsePrivateBrowsing) {
+ IncreasePrivateDocShellCount();
+ } else {
+ DecreasePrivateDocShellCount();
+ }
+ }
+ }
+
+ nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+ while (iter.HasMore()) {
+ nsCOMPtr<nsILoadContext> shell = do_QueryObject(iter.GetNext());
+ if (shell) {
+ shell->SetPrivateBrowsing(aUsePrivateBrowsing);
+ }
+ }
+
+ if (changed) {
+ nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mPrivacyObservers);
+ while (iter.HasMore()) {
+ nsWeakPtr ref = iter.GetNext();
+ nsCOMPtr<nsIPrivacyTransitionObserver> obs = do_QueryReferent(ref);
+ if (!obs) {
+ mPrivacyObservers.RemoveElement(ref);
+ } else {
+ obs->PrivateModeChanged(aUsePrivateBrowsing);
+ }
+ }
+ }
+
+ AssertOriginAttributesMatchPrivateBrowsing();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetHasLoadedNonBlankURI(bool* aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ *aResult = mHasLoadedNonBlankURI;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetUseRemoteTabs(bool* aUseRemoteTabs)
+{
+ NS_ENSURE_ARG_POINTER(aUseRemoteTabs);
+
+ *aUseRemoteTabs = mUseRemoteTabs;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetRemoteTabs(bool aUseRemoteTabs)
+{
+#ifdef MOZ_CRASHREPORTER
+ if (aUseRemoteTabs) {
+ CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("DOMIPCEnabled"),
+ NS_LITERAL_CSTRING("1"));
+ }
+#endif
+
+ mUseRemoteTabs = aUseRemoteTabs;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetAffectPrivateSessionLifetime(bool aAffectLifetime)
+{
+ bool change = aAffectLifetime != mAffectPrivateSessionLifetime;
+ if (change && UsePrivateBrowsing()) {
+ AssertOriginAttributesMatchPrivateBrowsing();
+ if (aAffectLifetime) {
+ IncreasePrivateDocShellCount();
+ } else {
+ DecreasePrivateDocShellCount();
+ }
+ }
+ mAffectPrivateSessionLifetime = aAffectLifetime;
+
+ nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+ while (iter.HasMore()) {
+ nsCOMPtr<nsIDocShell> shell = do_QueryObject(iter.GetNext());
+ if (shell) {
+ shell->SetAffectPrivateSessionLifetime(aAffectLifetime);
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetAffectPrivateSessionLifetime(bool* aAffectLifetime)
+{
+ *aAffectLifetime = mAffectPrivateSessionLifetime;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::AddWeakPrivacyTransitionObserver(
+ nsIPrivacyTransitionObserver* aObserver)
+{
+ nsWeakPtr weakObs = do_GetWeakReference(aObserver);
+ if (!weakObs) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ return mPrivacyObservers.AppendElement(weakObs) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsDocShell::AddWeakReflowObserver(nsIReflowObserver* aObserver)
+{
+ nsWeakPtr weakObs = do_GetWeakReference(aObserver);
+ if (!weakObs) {
+ return NS_ERROR_FAILURE;
+ }
+ return mReflowObservers.AppendElement(weakObs) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsDocShell::RemoveWeakReflowObserver(nsIReflowObserver* aObserver)
+{
+ nsWeakPtr obs = do_GetWeakReference(aObserver);
+ return mReflowObservers.RemoveElement(obs) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsDocShell::NotifyReflowObservers(bool aInterruptible,
+ DOMHighResTimeStamp aStart,
+ DOMHighResTimeStamp aEnd)
+{
+ nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mReflowObservers);
+ while (iter.HasMore()) {
+ nsWeakPtr ref = iter.GetNext();
+ nsCOMPtr<nsIReflowObserver> obs = do_QueryReferent(ref);
+ if (!obs) {
+ mReflowObservers.RemoveElement(ref);
+ } else if (aInterruptible) {
+ obs->ReflowInterruptible(aStart, aEnd);
+ } else {
+ obs->Reflow(aStart, aEnd);
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetAllowMetaRedirects(bool* aReturn)
+{
+ NS_ENSURE_ARG_POINTER(aReturn);
+
+ *aReturn = mAllowMetaRedirects;
+ if (!mAllowMetaRedirects) {
+ return NS_OK;
+ }
+
+ bool unsafe;
+ *aReturn = NS_SUCCEEDED(GetChannelIsUnsafe(&unsafe)) && !unsafe;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetAllowMetaRedirects(bool aValue)
+{
+ mAllowMetaRedirects = aValue;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetAllowSubframes(bool* aAllowSubframes)
+{
+ NS_ENSURE_ARG_POINTER(aAllowSubframes);
+
+ *aAllowSubframes = mAllowSubframes;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetAllowSubframes(bool aAllowSubframes)
+{
+ mAllowSubframes = aAllowSubframes;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetAllowImages(bool* aAllowImages)
+{
+ NS_ENSURE_ARG_POINTER(aAllowImages);
+
+ *aAllowImages = mAllowImages;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetAllowImages(bool aAllowImages)
+{
+ mAllowImages = aAllowImages;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetAllowMedia(bool* aAllowMedia)
+{
+ *aAllowMedia = mAllowMedia;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetAllowMedia(bool aAllowMedia)
+{
+ mAllowMedia = aAllowMedia;
+
+ // Mute or unmute audio contexts attached to the inner window.
+ if (mScriptGlobal) {
+ if (nsPIDOMWindowInner* innerWin =
+ mScriptGlobal->AsOuter()->GetCurrentInnerWindow()) {
+ if (aAllowMedia) {
+ innerWin->UnmuteAudioContexts();
+ } else {
+ innerWin->MuteAudioContexts();
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetAllowDNSPrefetch(bool* aAllowDNSPrefetch)
+{
+ *aAllowDNSPrefetch = mAllowDNSPrefetch;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetAllowDNSPrefetch(bool aAllowDNSPrefetch)
+{
+ mAllowDNSPrefetch = aAllowDNSPrefetch;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetAllowWindowControl(bool* aAllowWindowControl)
+{
+ *aAllowWindowControl = mAllowWindowControl;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetAllowWindowControl(bool aAllowWindowControl)
+{
+ mAllowWindowControl = aAllowWindowControl;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetAllowContentRetargeting(bool* aAllowContentRetargeting)
+{
+ *aAllowContentRetargeting = mAllowContentRetargeting;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetAllowContentRetargeting(bool aAllowContentRetargeting)
+{
+ mAllowContentRetargetingOnChildren = aAllowContentRetargeting;
+ mAllowContentRetargeting = aAllowContentRetargeting;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetAllowContentRetargetingOnChildren(
+ bool* aAllowContentRetargetingOnChildren)
+{
+ *aAllowContentRetargetingOnChildren = mAllowContentRetargetingOnChildren;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetAllowContentRetargetingOnChildren(
+ bool aAllowContentRetargetingOnChildren)
+{
+ mAllowContentRetargetingOnChildren = aAllowContentRetargetingOnChildren;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetInheritPrivateBrowsingId(bool* aInheritPrivateBrowsingId)
+{
+ *aInheritPrivateBrowsingId = mInheritPrivateBrowsingId;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetInheritPrivateBrowsingId(bool aInheritPrivateBrowsingId)
+{
+ mInheritPrivateBrowsingId = aInheritPrivateBrowsingId;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetFullscreenAllowed(bool* aFullscreenAllowed)
+{
+ NS_ENSURE_ARG_POINTER(aFullscreenAllowed);
+
+ // Browsers and apps have their mFullscreenAllowed retrieved from their
+ // corresponding iframe in their parent upon creation.
+ if (mFullscreenAllowed != CHECK_ATTRIBUTES) {
+ *aFullscreenAllowed = (mFullscreenAllowed == PARENT_ALLOWS);
+ return NS_OK;
+ }
+
+ // Assume false until we determine otherwise...
+ *aFullscreenAllowed = false;
+
+ nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
+ if (!win) {
+ return NS_OK;
+ }
+ if (nsCOMPtr<Element> frameElement = win->GetFrameElementInternal()) {
+ if (frameElement->IsXULElement()) {
+ if (frameElement->HasAttr(kNameSpaceID_None,
+ nsGkAtoms::disablefullscreen)) {
+ // Document inside this frame is explicitly disabled.
+ return NS_OK;
+ }
+ } else {
+ // We do not allow document inside any containing element other
+ // than iframe to enter fullscreen.
+ if (frameElement->IsHTMLElement(nsGkAtoms::iframe)) {
+ // If any ancestor iframe does not have allowfullscreen attribute
+ // set, then fullscreen is not allowed.
+ if (!frameElement->HasAttr(kNameSpaceID_None,
+ nsGkAtoms::allowfullscreen) &&
+ !frameElement->HasAttr(kNameSpaceID_None,
+ nsGkAtoms::mozallowfullscreen)) {
+ return NS_OK;
+ }
+ } else if (frameElement->IsHTMLElement(nsGkAtoms::embed)) {
+ // Respect allowfullscreen only if this is a rewritten YouTube embed.
+ nsCOMPtr<nsIObjectLoadingContent> objectLoadingContent =
+ do_QueryInterface(frameElement);
+ if (!objectLoadingContent) {
+ return NS_OK;
+ }
+ nsObjectLoadingContent* olc =
+ static_cast<nsObjectLoadingContent*>(objectLoadingContent.get());
+ if (!olc->IsRewrittenYoutubeEmbed()) {
+ return NS_OK;
+ }
+ // We don't have to check prefixed attributes because Flash does not
+ // support them.
+ if (!frameElement->HasAttr(kNameSpaceID_None,
+ nsGkAtoms::allowfullscreen)) {
+ return NS_OK;
+ }
+ } else {
+ // neither iframe nor embed
+ return NS_OK;
+ }
+ }
+ }
+
+ // If we have no parent then we're the root docshell; no ancestor of the
+ // original docshell doesn't have a allowfullscreen attribute, so
+ // report fullscreen as allowed.
+ RefPtr<nsDocShell> parent = GetParentDocshell();
+ if (!parent) {
+ *aFullscreenAllowed = true;
+ return NS_OK;
+ }
+
+ // Otherwise, we have a parent, continue the checking for
+ // mozFullscreenAllowed in the parent docshell's ancestors.
+ return parent->GetFullscreenAllowed(aFullscreenAllowed);
+}
+
+NS_IMETHODIMP
+nsDocShell::SetFullscreenAllowed(bool aFullscreenAllowed)
+{
+ if (!nsIDocShell::GetIsMozBrowserOrApp()) {
+ // Only allow setting of fullscreenAllowed on content/process boundaries.
+ // At non-boundaries the fullscreenAllowed attribute is calculated based on
+ // whether all enclosing frames have the "mozFullscreenAllowed" attribute
+ // set to "true". fullscreenAllowed is set at the process boundaries to
+ // propagate the value of the parent's "mozFullscreenAllowed" attribute
+ // across process boundaries.
+ return NS_ERROR_UNEXPECTED;
+ }
+ mFullscreenAllowed = (aFullscreenAllowed ? PARENT_ALLOWS : PARENT_PROHIBITS);
+ return NS_OK;
+}
+
+ScreenOrientationInternal
+nsDocShell::OrientationLock()
+{
+ return mOrientationLock;
+}
+
+void
+nsDocShell::SetOrientationLock(ScreenOrientationInternal aOrientationLock)
+{
+ mOrientationLock = aOrientationLock;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetMayEnableCharacterEncodingMenu(
+ bool* aMayEnableCharacterEncodingMenu)
+{
+ *aMayEnableCharacterEncodingMenu = false;
+ if (!mContentViewer) {
+ return NS_OK;
+ }
+ nsIDocument* doc = mContentViewer->GetDocument();
+ if (!doc) {
+ return NS_OK;
+ }
+ if (doc->WillIgnoreCharsetOverride()) {
+ return NS_OK;
+ }
+
+ *aMayEnableCharacterEncodingMenu = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetDocShellEnumerator(int32_t aItemType, int32_t aDirection,
+ nsISimpleEnumerator** aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+ *aResult = nullptr;
+
+ RefPtr<nsDocShellEnumerator> docShellEnum;
+ if (aDirection == ENUMERATE_FORWARDS) {
+ docShellEnum = new nsDocShellForwardsEnumerator;
+ } else {
+ docShellEnum = new nsDocShellBackwardsEnumerator;
+ }
+
+ nsresult rv = docShellEnum->SetEnumDocShellType(aItemType);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ rv = docShellEnum->SetEnumerationRootItem((nsIDocShellTreeItem*)this);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ rv = docShellEnum->First();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ rv = docShellEnum->QueryInterface(NS_GET_IID(nsISimpleEnumerator),
+ (void**)aResult);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetAppType(uint32_t* aAppType)
+{
+ *aAppType = mAppType;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetAppType(uint32_t aAppType)
+{
+ mAppType = aAppType;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetAllowAuth(bool* aAllowAuth)
+{
+ *aAllowAuth = mAllowAuth;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetAllowAuth(bool aAllowAuth)
+{
+ mAllowAuth = aAllowAuth;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetZoom(float* aZoom)
+{
+ NS_ENSURE_ARG_POINTER(aZoom);
+ *aZoom = 1.0f;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetZoom(float aZoom)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetMarginWidth(int32_t* aWidth)
+{
+ NS_ENSURE_ARG_POINTER(aWidth);
+
+ *aWidth = mMarginWidth;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetMarginWidth(int32_t aWidth)
+{
+ mMarginWidth = aWidth;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetMarginHeight(int32_t* aHeight)
+{
+ NS_ENSURE_ARG_POINTER(aHeight);
+
+ *aHeight = mMarginHeight;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetMarginHeight(int32_t aHeight)
+{
+ mMarginHeight = aHeight;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetBusyFlags(uint32_t* aBusyFlags)
+{
+ NS_ENSURE_ARG_POINTER(aBusyFlags);
+
+ *aBusyFlags = mBusyFlags;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::TabToTreeOwner(bool aForward, bool aForDocumentNavigation, bool* aTookFocus)
+{
+ NS_ENSURE_ARG_POINTER(aTookFocus);
+
+ nsCOMPtr<nsIWebBrowserChromeFocus> chromeFocus = do_GetInterface(mTreeOwner);
+ if (chromeFocus) {
+ if (aForward) {
+ *aTookFocus = NS_SUCCEEDED(chromeFocus->FocusNextElement(aForDocumentNavigation));
+ } else {
+ *aTookFocus = NS_SUCCEEDED(chromeFocus->FocusPrevElement(aForDocumentNavigation));
+ }
+ } else {
+ *aTookFocus = false;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetSecurityUI(nsISecureBrowserUI** aSecurityUI)
+{
+ NS_IF_ADDREF(*aSecurityUI = mSecurityUI);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetSecurityUI(nsISecureBrowserUI* aSecurityUI)
+{
+ mSecurityUI = aSecurityUI;
+ mSecurityUI->SetDocShell(this);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetUseErrorPages(bool* aUseErrorPages)
+{
+ *aUseErrorPages = UseErrorPages();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetUseErrorPages(bool aUseErrorPages)
+{
+ // If mUseErrorPages is set explicitly, stop using sUseErrorPages.
+ if (mObserveErrorPages) {
+ mObserveErrorPages = false;
+ }
+ mUseErrorPages = aUseErrorPages;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetPreviousTransIndex(int32_t* aPreviousTransIndex)
+{
+ *aPreviousTransIndex = mPreviousTransIndex;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetLoadedTransIndex(int32_t* aLoadedTransIndex)
+{
+ *aLoadedTransIndex = mLoadedTransIndex;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::HistoryPurged(int32_t aNumEntries)
+{
+ // These indices are used for fastback cache eviction, to determine
+ // which session history entries are candidates for content viewer
+ // eviction. We need to adjust by the number of entries that we
+ // just purged from history, so that we look at the right session history
+ // entries during eviction.
+ mPreviousTransIndex = std::max(-1, mPreviousTransIndex - aNumEntries);
+ mLoadedTransIndex = std::max(0, mLoadedTransIndex - aNumEntries);
+
+ nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+ while (iter.HasMore()) {
+ nsCOMPtr<nsIDocShell> shell = do_QueryObject(iter.GetNext());
+ if (shell) {
+ shell->HistoryPurged(aNumEntries);
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsDocShell::HistoryTransactionRemoved(int32_t aIndex)
+{
+ // These indices are used for fastback cache eviction, to determine
+ // which session history entries are candidates for content viewer
+ // eviction. We need to adjust by the number of entries that we
+ // just purged from history, so that we look at the right session history
+ // entries during eviction.
+ if (aIndex == mPreviousTransIndex) {
+ mPreviousTransIndex = -1;
+ } else if (aIndex < mPreviousTransIndex) {
+ --mPreviousTransIndex;
+ }
+ if (mLoadedTransIndex == aIndex) {
+ mLoadedTransIndex = 0;
+ } else if (aIndex < mLoadedTransIndex) {
+ --mLoadedTransIndex;
+ }
+
+ nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+ while (iter.HasMore()) {
+ nsCOMPtr<nsIDocShell> shell = do_QueryObject(iter.GetNext());
+ if (shell) {
+ static_cast<nsDocShell*>(shell.get())->HistoryTransactionRemoved(aIndex);
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetRecordProfileTimelineMarkers(bool aValue)
+{
+ bool currentValue = nsIDocShell::GetRecordProfileTimelineMarkers();
+ if (currentValue == aValue) {
+ return NS_OK;
+ }
+
+ RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
+ if (!timelines) {
+ return NS_OK;
+ }
+
+ if (aValue) {
+ MOZ_ASSERT(!timelines->HasConsumer(this));
+ timelines->AddConsumer(this);
+ MOZ_ASSERT(timelines->HasConsumer(this));
+ UseEntryScriptProfiling();
+ } else {
+ MOZ_ASSERT(timelines->HasConsumer(this));
+ timelines->RemoveConsumer(this);
+ MOZ_ASSERT(!timelines->HasConsumer(this));
+ UnuseEntryScriptProfiling();
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetRecordProfileTimelineMarkers(bool* aValue)
+{
+ *aValue = !!mObserved;
+ return NS_OK;
+}
+
+nsresult
+nsDocShell::PopProfileTimelineMarkers(
+ JSContext* aCx,
+ JS::MutableHandle<JS::Value> aOut)
+{
+ RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
+ if (!timelines) {
+ return NS_OK;
+ }
+
+ nsTArray<dom::ProfileTimelineMarker> store;
+ SequenceRooter<dom::ProfileTimelineMarker> rooter(aCx, &store);
+
+ timelines->PopMarkers(this, aCx, store);
+
+ if (!ToJSValue(aCx, store, aOut)) {
+ JS_ClearPendingException(aCx);
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsDocShell::Now(DOMHighResTimeStamp* aWhen)
+{
+ bool ignore;
+ *aWhen =
+ (TimeStamp::Now() - TimeStamp::ProcessCreation(ignore)).ToMilliseconds();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetWindowDraggingAllowed(bool aValue)
+{
+ RefPtr<nsDocShell> parent = GetParentDocshell();
+ if (!aValue && mItemType == typeChrome && !parent) {
+ // Window dragging is always allowed for top level
+ // chrome docshells.
+ return NS_ERROR_FAILURE;
+ }
+ mWindowDraggingAllowed = aValue;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetWindowDraggingAllowed(bool* aValue)
+{
+ // window dragging regions in CSS (-moz-window-drag:drag)
+ // can be slow. Default behavior is to only allow it for
+ // chrome top level windows.
+ RefPtr<nsDocShell> parent = GetParentDocshell();
+ if (mItemType == typeChrome && !parent) {
+ // Top level chrome window
+ *aValue = true;
+ } else {
+ *aValue = mWindowDraggingAllowed;
+ }
+ return NS_OK;
+}
+
+nsIDOMStorageManager*
+nsDocShell::TopSessionStorageManager()
+{
+ nsresult rv;
+
+ nsCOMPtr<nsIDocShellTreeItem> topItem;
+ rv = GetSameTypeRootTreeItem(getter_AddRefs(topItem));
+ if (NS_FAILED(rv)) {
+ return nullptr;
+ }
+
+ if (!topItem) {
+ return nullptr;
+ }
+
+ nsDocShell* topDocShell = static_cast<nsDocShell*>(topItem.get());
+ if (topDocShell != this) {
+ return topDocShell->TopSessionStorageManager();
+ }
+
+ if (!mSessionStorageManager) {
+ mSessionStorageManager =
+ do_CreateInstance("@mozilla.org/dom/sessionStorage-manager;1");
+ }
+
+ return mSessionStorageManager;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetSessionStorageForPrincipal(nsIPrincipal* aPrincipal,
+ const nsAString& aDocumentURI,
+ bool aCreate,
+ nsIDOMStorage** aStorage)
+{
+ nsCOMPtr<nsIDOMStorageManager> manager = TopSessionStorageManager();
+ if (!manager) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ nsCOMPtr<nsPIDOMWindowOuter> domWin = GetWindow();
+
+ AssertOriginAttributesMatchPrivateBrowsing();
+ if (aCreate) {
+ return manager->CreateStorage(domWin->GetCurrentInnerWindow(), aPrincipal,
+ aDocumentURI, UsePrivateBrowsing(), aStorage);
+ }
+
+ return manager->GetStorage(domWin->GetCurrentInnerWindow(), aPrincipal,
+ UsePrivateBrowsing(), aStorage);
+}
+
+nsresult
+nsDocShell::AddSessionStorage(nsIPrincipal* aPrincipal, nsIDOMStorage* aStorage)
+{
+ RefPtr<DOMStorage> storage = static_cast<DOMStorage*>(aStorage);
+ if (!storage) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ nsIPrincipal* storagePrincipal = storage->GetPrincipal();
+ if (storagePrincipal != aPrincipal) {
+ NS_ERROR("Wanting to add a sessionStorage for different principal");
+ return NS_ERROR_DOM_SECURITY_ERR;
+ }
+
+ nsCOMPtr<nsIDOMStorageManager> manager = TopSessionStorageManager();
+ if (!manager) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ return manager->CloneStorage(aStorage);
+}
+
+NS_IMETHODIMP
+nsDocShell::GetCurrentDocumentChannel(nsIChannel** aResult)
+{
+ NS_IF_ADDREF(*aResult = GetCurrentDocChannel());
+ return NS_OK;
+}
+
+nsIChannel*
+nsDocShell::GetCurrentDocChannel()
+{
+ if (mContentViewer) {
+ nsIDocument* doc = mContentViewer->GetDocument();
+ if (doc) {
+ return doc->GetChannel();
+ }
+ }
+ return nullptr;
+}
+
+NS_IMETHODIMP
+nsDocShell::AddWeakScrollObserver(nsIScrollObserver* aObserver)
+{
+ nsWeakPtr weakObs = do_GetWeakReference(aObserver);
+ if (!weakObs) {
+ return NS_ERROR_FAILURE;
+ }
+ return mScrollObservers.AppendElement(weakObs) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsDocShell::RemoveWeakScrollObserver(nsIScrollObserver* aObserver)
+{
+ nsWeakPtr obs = do_GetWeakReference(aObserver);
+ return mScrollObservers.RemoveElement(obs) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+void
+nsDocShell::NotifyAsyncPanZoomStarted()
+{
+ nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
+ while (iter.HasMore()) {
+ nsWeakPtr ref = iter.GetNext();
+ nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
+ if (obs) {
+ obs->AsyncPanZoomStarted();
+ } else {
+ mScrollObservers.RemoveElement(ref);
+ }
+ }
+}
+
+void
+nsDocShell::NotifyAsyncPanZoomStopped()
+{
+ nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
+ while (iter.HasMore()) {
+ nsWeakPtr ref = iter.GetNext();
+ nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
+ if (obs) {
+ obs->AsyncPanZoomStopped();
+ } else {
+ mScrollObservers.RemoveElement(ref);
+ }
+ }
+}
+
+NS_IMETHODIMP
+nsDocShell::NotifyScrollObservers()
+{
+ nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
+ while (iter.HasMore()) {
+ nsWeakPtr ref = iter.GetNext();
+ nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
+ if (obs) {
+ obs->ScrollPositionChanged();
+ } else {
+ mScrollObservers.RemoveElement(ref);
+ }
+ }
+ return NS_OK;
+}
+
+//*****************************************************************************
+// nsDocShell::nsIDocShellTreeItem
+//*****************************************************************************
+
+NS_IMETHODIMP
+nsDocShell::GetName(nsAString& aName)
+{
+ aName = mName;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetName(const nsAString& aName)
+{
+ mName = aName;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::NameEquals(const nsAString& aName, bool* aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+ *aResult = mName.Equals(aName);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetCustomUserAgent(nsAString& aCustomUserAgent)
+{
+ aCustomUserAgent = mCustomUserAgent;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetCustomUserAgent(const nsAString& aCustomUserAgent)
+{
+ mCustomUserAgent = aCustomUserAgent;
+ RefPtr<nsGlobalWindow> win = mScriptGlobal ?
+ mScriptGlobal->GetCurrentInnerWindowInternal() : nullptr;
+ if (win) {
+ ErrorResult ignored;
+ Navigator* navigator = win->GetNavigator(ignored);
+ ignored.SuppressException();
+ if (navigator) {
+ navigator->ClearUserAgentCache();
+ }
+ }
+
+ uint32_t childCount = mChildList.Length();
+ for (uint32_t i = 0; i < childCount; ++i) {
+ nsCOMPtr<nsIDocShell> childShell = do_QueryInterface(ChildAt(i));
+ if (childShell) {
+ childShell->SetCustomUserAgent(aCustomUserAgent);
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetTouchEventsOverride(uint32_t* aTouchEventsOverride)
+{
+ NS_ENSURE_ARG_POINTER(aTouchEventsOverride);
+
+ *aTouchEventsOverride = mTouchEventsOverride;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetTouchEventsOverride(uint32_t aTouchEventsOverride)
+{
+ if (!(aTouchEventsOverride == nsIDocShell::TOUCHEVENTS_OVERRIDE_NONE ||
+ aTouchEventsOverride == nsIDocShell::TOUCHEVENTS_OVERRIDE_ENABLED ||
+ aTouchEventsOverride == nsIDocShell::TOUCHEVENTS_OVERRIDE_DISABLED)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ mTouchEventsOverride = aTouchEventsOverride;
+
+ uint32_t childCount = mChildList.Length();
+ for (uint32_t i = 0; i < childCount; ++i) {
+ nsCOMPtr<nsIDocShell> childShell = do_QueryInterface(ChildAt(i));
+ if (childShell) {
+ childShell->SetTouchEventsOverride(aTouchEventsOverride);
+ }
+ }
+ return NS_OK;
+}
+
+/* virtual */ int32_t
+nsDocShell::ItemType()
+{
+ return mItemType;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetItemType(int32_t* aItemType)
+{
+ NS_ENSURE_ARG_POINTER(aItemType);
+
+ *aItemType = ItemType();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetItemType(int32_t aItemType)
+{
+ NS_ENSURE_ARG((aItemType == typeChrome) || (typeContent == aItemType));
+
+ // Only allow setting the type on root docshells. Those would be the ones
+ // that have the docloader service as mParent or have no mParent at all.
+ nsCOMPtr<nsIDocumentLoader> docLoaderService =
+ do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID);
+ NS_ENSURE_TRUE(docLoaderService, NS_ERROR_UNEXPECTED);
+
+ NS_ENSURE_STATE(!mParent || mParent == docLoaderService);
+
+ mItemType = aItemType;
+
+ // disable auth prompting for anything but content
+ mAllowAuth = mItemType == typeContent;
+
+ RefPtr<nsPresContext> presContext = nullptr;
+ GetPresContext(getter_AddRefs(presContext));
+ if (presContext) {
+ presContext->UpdateIsChrome();
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetParent(nsIDocShellTreeItem** aParent)
+{
+ if (!mParent) {
+ *aParent = nullptr;
+ } else {
+ CallQueryInterface(mParent, aParent);
+ }
+ // Note that in the case when the parent is not an nsIDocShellTreeItem we
+ // don't want to throw; we just want to return null.
+ return NS_OK;
+}
+
+already_AddRefed<nsDocShell>
+nsDocShell::GetParentDocshell()
+{
+ nsCOMPtr<nsIDocShell> docshell = do_QueryInterface(GetAsSupports(mParent));
+ return docshell.forget().downcast<nsDocShell>();
+}
+
+void
+nsDocShell::RecomputeCanExecuteScripts()
+{
+ bool old = mCanExecuteScripts;
+ RefPtr<nsDocShell> parent = GetParentDocshell();
+
+ // If we have no tree owner, that means that we've been detached from the
+ // docshell tree (this is distinct from having no parent dochshell, which
+ // is the case for root docshells). It would be nice to simply disallow
+ // script in detached docshells, but bug 986542 demonstrates that this
+ // behavior breaks at least one website.
+ //
+ // So instead, we use our previous value, unless mAllowJavascript has been
+ // explicitly set to false.
+ if (!mTreeOwner) {
+ mCanExecuteScripts = mCanExecuteScripts && mAllowJavascript;
+ // If scripting has been explicitly disabled on our docshell, we're done.
+ } else if (!mAllowJavascript) {
+ mCanExecuteScripts = false;
+ // If we have a parent, inherit.
+ } else if (parent) {
+ mCanExecuteScripts = parent->mCanExecuteScripts;
+ // Otherwise, we're the root of the tree, and we haven't explicitly disabled
+ // script. Allow.
+ } else {
+ mCanExecuteScripts = true;
+ }
+
+ // Inform our active DOM window.
+ //
+ // This will pass the outer, which will be in the scope of the active inner.
+ if (mScriptGlobal && mScriptGlobal->GetGlobalJSObject()) {
+ xpc::Scriptability& scriptability =
+ xpc::Scriptability::Get(mScriptGlobal->GetGlobalJSObject());
+ scriptability.SetDocShellAllowsScript(mCanExecuteScripts);
+ }
+
+ // If our value has changed, our children might be affected. Recompute their
+ // value as well.
+ if (old != mCanExecuteScripts) {
+ nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+ while (iter.HasMore()) {
+ static_cast<nsDocShell*>(iter.GetNext())->RecomputeCanExecuteScripts();
+ }
+ }
+}
+
+nsresult
+nsDocShell::SetDocLoaderParent(nsDocLoader* aParent)
+{
+ bool wasFrame = IsFrame();
+#ifdef DEBUG
+ bool wasPrivate = UsePrivateBrowsing();
+#endif
+
+ nsresult rv = nsDocLoader::SetDocLoaderParent(aParent);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsISupportsPriority> priorityGroup = do_QueryInterface(mLoadGroup);
+ if (wasFrame != IsFrame() && priorityGroup) {
+ priorityGroup->AdjustPriority(wasFrame ? -1 : 1);
+ }
+
+ // Curse ambiguous nsISupports inheritance!
+ nsISupports* parent = GetAsSupports(aParent);
+
+ // If parent is another docshell, we inherit all their flags for
+ // allowing plugins, scripting etc.
+ bool value;
+ nsString customUserAgent;
+ nsCOMPtr<nsIDocShell> parentAsDocShell(do_QueryInterface(parent));
+ if (parentAsDocShell) {
+ if (mAllowPlugins && NS_SUCCEEDED(parentAsDocShell->GetAllowPlugins(&value))) {
+ SetAllowPlugins(value);
+ }
+ if (mAllowJavascript && NS_SUCCEEDED(parentAsDocShell->GetAllowJavascript(&value))) {
+ SetAllowJavascript(value);
+ }
+ if (mAllowMetaRedirects && NS_SUCCEEDED(parentAsDocShell->GetAllowMetaRedirects(&value))) {
+ SetAllowMetaRedirects(value);
+ }
+ if (mAllowSubframes && NS_SUCCEEDED(parentAsDocShell->GetAllowSubframes(&value))) {
+ SetAllowSubframes(value);
+ }
+ if (mAllowImages && NS_SUCCEEDED(parentAsDocShell->GetAllowImages(&value))) {
+ SetAllowImages(value);
+ }
+ SetAllowMedia(parentAsDocShell->GetAllowMedia() && mAllowMedia);
+ if (mAllowWindowControl && NS_SUCCEEDED(parentAsDocShell->GetAllowWindowControl(&value))) {
+ SetAllowWindowControl(value);
+ }
+ SetAllowContentRetargeting(mAllowContentRetargeting &&
+ parentAsDocShell->GetAllowContentRetargetingOnChildren());
+ if (parentAsDocShell->GetIsPrerendered()) {
+ SetIsPrerendered();
+ }
+ if (NS_SUCCEEDED(parentAsDocShell->GetIsActive(&value))) {
+ // a prerendered docshell is not active yet
+ SetIsActive(value && !mIsPrerendered);
+ }
+ if (NS_SUCCEEDED(parentAsDocShell->GetCustomUserAgent(customUserAgent)) &&
+ !customUserAgent.IsEmpty()) {
+ SetCustomUserAgent(customUserAgent);
+ }
+ if (NS_FAILED(parentAsDocShell->GetAllowDNSPrefetch(&value))) {
+ value = false;
+ }
+ SetAllowDNSPrefetch(mAllowDNSPrefetch && value);
+ if (mInheritPrivateBrowsingId) {
+ value = parentAsDocShell->GetAffectPrivateSessionLifetime();
+ SetAffectPrivateSessionLifetime(value);
+ }
+ uint32_t flags;
+ if (NS_SUCCEEDED(parentAsDocShell->GetDefaultLoadFlags(&flags))) {
+ SetDefaultLoadFlags(flags);
+ }
+ uint32_t touchEventsOverride;
+ if (NS_SUCCEEDED(parentAsDocShell->GetTouchEventsOverride(&touchEventsOverride))) {
+ SetTouchEventsOverride(touchEventsOverride);
+ }
+ }
+
+ nsCOMPtr<nsILoadContext> parentAsLoadContext(do_QueryInterface(parent));
+ if (parentAsLoadContext && mInheritPrivateBrowsingId &&
+ NS_SUCCEEDED(parentAsLoadContext->GetUsePrivateBrowsing(&value))) {
+ SetPrivateBrowsing(value);
+ }
+
+ nsCOMPtr<nsIURIContentListener> parentURIListener(do_GetInterface(parent));
+ if (parentURIListener) {
+ mContentListener->SetParentContentListener(parentURIListener);
+ }
+
+ // Our parent has changed. Recompute scriptability.
+ RecomputeCanExecuteScripts();
+
+ NS_ASSERTION(mInheritPrivateBrowsingId || wasPrivate == UsePrivateBrowsing(),
+ "Private browsing state changed while inheritance was disabled");
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetSameTypeParent(nsIDocShellTreeItem** aParent)
+{
+ NS_ENSURE_ARG_POINTER(aParent);
+ *aParent = nullptr;
+
+ if (nsIDocShell::GetIsMozBrowserOrApp()) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIDocShellTreeItem> parent =
+ do_QueryInterface(GetAsSupports(mParent));
+ if (!parent) {
+ return NS_OK;
+ }
+
+ if (parent->ItemType() == mItemType) {
+ parent.swap(*aParent);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetSameTypeParentIgnoreBrowserAndAppBoundaries(nsIDocShell** aParent)
+{
+ NS_ENSURE_ARG_POINTER(aParent);
+ *aParent = nullptr;
+
+ nsCOMPtr<nsIDocShellTreeItem> parent =
+ do_QueryInterface(GetAsSupports(mParent));
+ if (!parent) {
+ return NS_OK;
+ }
+
+ if (parent->ItemType() == mItemType) {
+ nsCOMPtr<nsIDocShell> parentDS = do_QueryInterface(parent);
+ parentDS.forget(aParent);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetRootTreeItem(nsIDocShellTreeItem** aRootTreeItem)
+{
+ NS_ENSURE_ARG_POINTER(aRootTreeItem);
+
+ RefPtr<nsDocShell> root = this;
+ RefPtr<nsDocShell> parent = root->GetParentDocshell();
+ while (parent) {
+ root = parent;
+ parent = root->GetParentDocshell();
+ }
+
+ root.forget(aRootTreeItem);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetSameTypeRootTreeItem(nsIDocShellTreeItem** aRootTreeItem)
+{
+ NS_ENSURE_ARG_POINTER(aRootTreeItem);
+ *aRootTreeItem = static_cast<nsIDocShellTreeItem*>(this);
+
+ nsCOMPtr<nsIDocShellTreeItem> parent;
+ NS_ENSURE_SUCCESS(GetSameTypeParent(getter_AddRefs(parent)),
+ NS_ERROR_FAILURE);
+ while (parent) {
+ *aRootTreeItem = parent;
+ NS_ENSURE_SUCCESS(
+ (*aRootTreeItem)->GetSameTypeParent(getter_AddRefs(parent)),
+ NS_ERROR_FAILURE);
+ }
+ NS_ADDREF(*aRootTreeItem);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetSameTypeRootTreeItemIgnoreBrowserAndAppBoundaries(nsIDocShell ** aRootTreeItem)
+{
+ NS_ENSURE_ARG_POINTER(aRootTreeItem);
+ *aRootTreeItem = static_cast<nsIDocShell *>(this);
+
+ nsCOMPtr<nsIDocShell> parent;
+ NS_ENSURE_SUCCESS(GetSameTypeParentIgnoreBrowserAndAppBoundaries(getter_AddRefs(parent)),
+ NS_ERROR_FAILURE);
+ while (parent) {
+ *aRootTreeItem = parent;
+ NS_ENSURE_SUCCESS((*aRootTreeItem)->
+ GetSameTypeParentIgnoreBrowserAndAppBoundaries(getter_AddRefs(parent)),
+ NS_ERROR_FAILURE);
+ }
+ NS_ADDREF(*aRootTreeItem);
+ return NS_OK;
+}
+
+/* static */
+bool
+nsDocShell::CanAccessItem(nsIDocShellTreeItem* aTargetItem,
+ nsIDocShellTreeItem* aAccessingItem,
+ bool aConsiderOpener)
+{
+ NS_PRECONDITION(aTargetItem, "Must have target item!");
+
+ if (!gValidateOrigin || !aAccessingItem) {
+ // Good to go
+ return true;
+ }
+
+ // XXXbz should we care if aAccessingItem or the document therein is
+ // chrome? Should those get extra privileges?
+
+ // For historical context, see:
+ //
+ // Bug 13871: Prevent frameset spoofing
+ // Bug 103638: Targets with same name in different windows open in wrong
+ // window with javascript
+ // Bug 408052: Adopt "ancestor" frame navigation policy
+
+ // Now do a security check.
+ //
+ // Disallow navigation if the two frames are not part of the same app, or if
+ // they have different is-in-browser-element states.
+ //
+ // Allow navigation if
+ // 1) aAccessingItem can script aTargetItem or one of its ancestors in
+ // the frame hierarchy or
+ // 2) aTargetItem is a top-level frame and aAccessingItem is its descendant
+ // 3) aTargetItem is a top-level frame and aAccessingItem can target
+ // its opener per rule (1) or (2).
+
+ if (aTargetItem == aAccessingItem) {
+ // A frame is allowed to navigate itself.
+ return true;
+ }
+
+ nsCOMPtr<nsIDocShell> targetDS = do_QueryInterface(aTargetItem);
+ nsCOMPtr<nsIDocShell> accessingDS = do_QueryInterface(aAccessingItem);
+ if (!targetDS || !accessingDS) {
+ // We must be able to convert both to nsIDocShell.
+ return false;
+ }
+
+ if (targetDS->GetIsInIsolatedMozBrowserElement() !=
+ accessingDS->GetIsInIsolatedMozBrowserElement() ||
+ targetDS->GetAppId() != accessingDS->GetAppId()) {
+ return false;
+ }
+
+ nsCOMPtr<nsIDocShellTreeItem> accessingRoot;
+ aAccessingItem->GetSameTypeRootTreeItem(getter_AddRefs(accessingRoot));
+ nsCOMPtr<nsIDocShell> accessingRootDS = do_QueryInterface(accessingRoot);
+
+ nsCOMPtr<nsIDocShellTreeItem> targetRoot;
+ aTargetItem->GetSameTypeRootTreeItem(getter_AddRefs(targetRoot));
+ nsCOMPtr<nsIDocShell> targetRootDS = do_QueryInterface(targetRoot);
+
+ DocShellOriginAttributes targetOA =
+ static_cast<nsDocShell*>(targetDS.get())->GetOriginAttributes();
+ DocShellOriginAttributes accessingOA =
+ static_cast<nsDocShell*>(accessingDS.get())->GetOriginAttributes();
+
+ // When the first party isolation is on, the top-level docShell may not have
+ // the firstPartyDomain in its originAttributes, but its document will have
+ // it. So we get the firstPartyDomain from the nodePrincipal of the document
+ // before we compare the originAttributes.
+ if (OriginAttributes::IsFirstPartyEnabled()) {
+ if (accessingDS == accessingRootDS &&
+ aAccessingItem->ItemType() == nsIDocShellTreeItem::typeContent &&
+ !accessingDS->GetIsMozBrowserOrApp()) {
+
+ nsCOMPtr<nsIDocument> accessingDoc = aAccessingItem->GetDocument();
+
+ if (accessingDoc) {
+ nsCOMPtr<nsIPrincipal> accessingPrincipal = accessingDoc->NodePrincipal();
+
+ accessingOA.mFirstPartyDomain =
+ BasePrincipal::Cast(accessingPrincipal)->OriginAttributesRef().mFirstPartyDomain;
+ }
+ }
+
+ if (targetDS == targetRootDS &&
+ aTargetItem->ItemType() == nsIDocShellTreeItem::typeContent &&
+ !targetDS->GetIsMozBrowserOrApp()) {
+
+ nsCOMPtr<nsIDocument> targetDoc = aAccessingItem->GetDocument();
+
+ if (targetDoc) {
+ nsCOMPtr<nsIPrincipal> targetPrincipal = targetDoc->NodePrincipal();
+
+ targetOA.mFirstPartyDomain =
+ BasePrincipal::Cast(targetPrincipal)->OriginAttributesRef().mFirstPartyDomain;
+ }
+ }
+ }
+
+ if (targetOA != accessingOA) {
+ return false;
+ }
+
+ // A private document can't access a non-private one, and vice versa.
+ if (static_cast<nsDocShell*>(targetDS.get())->UsePrivateBrowsing() !=
+ static_cast<nsDocShell*>(accessingDS.get())->UsePrivateBrowsing()) {
+ return false;
+ }
+
+ if (aTargetItem == accessingRoot) {
+ // A frame can navigate its root.
+ return true;
+ }
+
+ // Check if aAccessingItem can navigate one of aTargetItem's ancestors.
+ nsCOMPtr<nsIDocShellTreeItem> target = aTargetItem;
+ do {
+ if (ValidateOrigin(aAccessingItem, target)) {
+ return true;
+ }
+
+ nsCOMPtr<nsIDocShellTreeItem> parent;
+ target->GetSameTypeParent(getter_AddRefs(parent));
+ parent.swap(target);
+ } while (target);
+
+ if (aTargetItem != targetRoot) {
+ // target is a subframe, not in accessor's frame hierarchy, and all its
+ // ancestors have origins different from that of the accessor. Don't
+ // allow access.
+ return false;
+ }
+
+ if (!aConsiderOpener) {
+ // All done here
+ return false;
+ }
+
+ nsCOMPtr<nsPIDOMWindowOuter> targetWindow = aTargetItem->GetWindow();
+ if (!targetWindow) {
+ NS_ERROR("This should not happen, really");
+ return false;
+ }
+
+ nsCOMPtr<mozIDOMWindowProxy> targetOpener = targetWindow->GetOpener();
+ nsCOMPtr<nsIWebNavigation> openerWebNav(do_GetInterface(targetOpener));
+ nsCOMPtr<nsIDocShellTreeItem> openerItem(do_QueryInterface(openerWebNav));
+
+ if (!openerItem) {
+ return false;
+ }
+
+ return CanAccessItem(openerItem, aAccessingItem, false);
+}
+
+static bool
+ItemIsActive(nsIDocShellTreeItem* aItem)
+{
+ if (nsCOMPtr<nsPIDOMWindowOuter> window = aItem->GetWindow()) {
+ auto* win = nsGlobalWindow::Cast(window);
+ MOZ_ASSERT(win->IsOuterWindow());
+ if (!win->GetClosedOuter()) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+NS_IMETHODIMP
+nsDocShell::FindItemWithName(const nsAString& aName,
+ nsISupports* aRequestor,
+ nsIDocShellTreeItem* aOriginalRequestor,
+ nsIDocShellTreeItem** aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ // If we don't find one, we return NS_OK and a null result
+ *aResult = nullptr;
+
+ if (aName.IsEmpty()) {
+ return NS_OK;
+ }
+
+ if (aRequestor) {
+ // If aRequestor is not null we don't need to check special names, so
+ // just hand straight off to the search by actual name function.
+ return DoFindItemWithName(aName, aRequestor, aOriginalRequestor, aResult);
+ } else {
+ // This is the entry point into the target-finding algorithm. Check
+ // for special names. This should only be done once, hence the check
+ // for a null aRequestor.
+
+ nsCOMPtr<nsIDocShellTreeItem> foundItem;
+ if (aName.LowerCaseEqualsLiteral("_self")) {
+ foundItem = this;
+ } else if (aName.LowerCaseEqualsLiteral("_blank")) {
+ // Just return null. Caller must handle creating a new window with
+ // a blank name himself.
+ return NS_OK;
+ } else if (aName.LowerCaseEqualsLiteral("_parent")) {
+ GetSameTypeParent(getter_AddRefs(foundItem));
+ if (!foundItem) {
+ foundItem = this;
+ }
+ } else if (aName.LowerCaseEqualsLiteral("_top")) {
+ GetSameTypeRootTreeItem(getter_AddRefs(foundItem));
+ NS_ASSERTION(foundItem, "Must have this; worst case it's us!");
+ } else {
+ // Do the search for item by an actual name.
+ DoFindItemWithName(aName, aRequestor, aOriginalRequestor,
+ getter_AddRefs(foundItem));
+ }
+
+ if (foundItem && !CanAccessItem(foundItem, aOriginalRequestor)) {
+ foundItem = nullptr;
+ }
+
+ // DoFindItemWithName only returns active items and we don't check if
+ // the item is active for the special cases.
+ if (foundItem) {
+ foundItem.swap(*aResult);
+ }
+ return NS_OK;
+ }
+}
+
+void
+nsDocShell::AssertOriginAttributesMatchPrivateBrowsing() {
+ // Chrome docshells must not have a private browsing OriginAttribute
+ // Content docshells must maintain the equality:
+ // mOriginAttributes.mPrivateBrowsingId == mPrivateBrowsingId
+ if (mItemType == typeChrome) {
+ MOZ_DIAGNOSTIC_ASSERT(mOriginAttributes.mPrivateBrowsingId == 0);
+ } else {
+ MOZ_DIAGNOSTIC_ASSERT(mOriginAttributes.mPrivateBrowsingId == mPrivateBrowsingId);
+ }
+}
+
+nsresult
+nsDocShell::DoFindItemWithName(const nsAString& aName,
+ nsISupports* aRequestor,
+ nsIDocShellTreeItem* aOriginalRequestor,
+ nsIDocShellTreeItem** aResult)
+{
+ // First we check our name.
+ if (mName.Equals(aName) && ItemIsActive(this) &&
+ CanAccessItem(this, aOriginalRequestor)) {
+ NS_ADDREF(*aResult = this);
+ return NS_OK;
+ }
+
+ // This QI may fail, but the places where we want to compare, comparing
+ // against nullptr serves the same purpose.
+ nsCOMPtr<nsIDocShellTreeItem> reqAsTreeItem(do_QueryInterface(aRequestor));
+
+ // Second we check our children making sure not to ask a child if
+ // it is the aRequestor.
+#ifdef DEBUG
+ nsresult rv =
+#endif
+ FindChildWithName(aName, true, true, reqAsTreeItem, aOriginalRequestor,
+ aResult);
+ NS_ASSERTION(NS_SUCCEEDED(rv),
+ "FindChildWithName should not be failing here.");
+ if (*aResult) {
+ return NS_OK;
+ }
+
+ // Third if we have a parent and it isn't the requestor then we
+ // should ask it to do the search. If it is the requestor we
+ // should just stop here and let the parent do the rest. If we
+ // don't have a parent, then we should ask the
+ // docShellTreeOwner to do the search.
+ nsCOMPtr<nsIDocShellTreeItem> parentAsTreeItem =
+ do_QueryInterface(GetAsSupports(mParent));
+ if (parentAsTreeItem) {
+ if (parentAsTreeItem == reqAsTreeItem) {
+ return NS_OK;
+ }
+
+ // If we have a same-type parent, respecting browser and app boundaries.
+ // NOTE: Could use GetSameTypeParent if the issues described in bug 1310344 are fixed.
+ if (!GetIsMozBrowserOrApp() && parentAsTreeItem->ItemType() == mItemType) {
+ return parentAsTreeItem->FindItemWithName(
+ aName,
+ static_cast<nsIDocShellTreeItem*>(this),
+ aOriginalRequestor,
+ aResult);
+ }
+ }
+
+ // If we have a null parent or the parent is not of the same type, we need to
+ // give up on finding it in our tree, and start looking in our TabGroup.
+ nsCOMPtr<nsPIDOMWindowOuter> window = GetWindow();
+ if (window) {
+ RefPtr<mozilla::dom::TabGroup> tabGroup = window->TabGroup();
+ // We don't want to make the request to our TabGroup if they are the ones
+ // which made a request to us.
+ if (tabGroup != aRequestor) {
+ tabGroup->FindItemWithName(aName, this, aOriginalRequestor, aResult);
+ }
+ }
+
+ return NS_OK;
+}
+
+bool
+nsDocShell::IsSandboxedFrom(nsIDocShell* aTargetDocShell)
+{
+ // If no target then not sandboxed.
+ if (!aTargetDocShell) {
+ return false;
+ }
+
+ // We cannot be sandboxed from ourselves.
+ if (aTargetDocShell == this) {
+ return false;
+ }
+
+ // Default the sandbox flags to our flags, so that if we can't retrieve the
+ // active document, we will still enforce our own.
+ uint32_t sandboxFlags = mSandboxFlags;
+ if (mContentViewer) {
+ nsCOMPtr<nsIDocument> doc = mContentViewer->GetDocument();
+ if (doc) {
+ sandboxFlags = doc->GetSandboxFlags();
+ }
+ }
+
+ // If no flags, we are not sandboxed at all.
+ if (!sandboxFlags) {
+ return false;
+ }
+
+ // If aTargetDocShell has an ancestor, it is not top level.
+ nsCOMPtr<nsIDocShellTreeItem> ancestorOfTarget;
+ aTargetDocShell->GetSameTypeParent(getter_AddRefs(ancestorOfTarget));
+ if (ancestorOfTarget) {
+ do {
+ // We are not sandboxed if we are an ancestor of target.
+ if (ancestorOfTarget == this) {
+ return false;
+ }
+ nsCOMPtr<nsIDocShellTreeItem> tempTreeItem;
+ ancestorOfTarget->GetSameTypeParent(getter_AddRefs(tempTreeItem));
+ tempTreeItem.swap(ancestorOfTarget);
+ } while (ancestorOfTarget);
+
+ // Otherwise, we are sandboxed from aTargetDocShell.
+ return true;
+ }
+
+ // aTargetDocShell is top level, are we the "one permitted sandboxed
+ // navigator", i.e. did we open aTargetDocShell?
+ nsCOMPtr<nsIDocShell> permittedNavigator;
+ aTargetDocShell->GetOnePermittedSandboxedNavigator(
+ getter_AddRefs(permittedNavigator));
+ if (permittedNavigator == this) {
+ return false;
+ }
+
+ // If SANDBOXED_TOPLEVEL_NAVIGATION flag is not on, we are not sandboxed
+ // from our top.
+ if (!(sandboxFlags & SANDBOXED_TOPLEVEL_NAVIGATION)) {
+ nsCOMPtr<nsIDocShellTreeItem> rootTreeItem;
+ GetSameTypeRootTreeItem(getter_AddRefs(rootTreeItem));
+ if (SameCOMIdentity(aTargetDocShell, rootTreeItem)) {
+ return false;
+ }
+ }
+
+ // Otherwise, we are sandboxed from aTargetDocShell.
+ return true;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetTreeOwner(nsIDocShellTreeOwner** aTreeOwner)
+{
+ NS_ENSURE_ARG_POINTER(aTreeOwner);
+
+ *aTreeOwner = mTreeOwner;
+ NS_IF_ADDREF(*aTreeOwner);
+ return NS_OK;
+}
+
+#ifdef DEBUG_DOCSHELL_FOCUS
+static void
+PrintDocTree(nsIDocShellTreeItem* aParentNode, int aLevel)
+{
+ for (int32_t i = 0; i < aLevel; i++) {
+ printf(" ");
+ }
+
+ int32_t childWebshellCount;
+ aParentNode->GetChildCount(&childWebshellCount);
+ nsCOMPtr<nsIDocShell> parentAsDocShell(do_QueryInterface(aParentNode));
+ int32_t type = aParentNode->ItemType();
+ nsCOMPtr<nsIPresShell> presShell = parentAsDocShell->GetPresShell();
+ RefPtr<nsPresContext> presContext;
+ parentAsDocShell->GetPresContext(getter_AddRefs(presContext));
+ nsIDocument* doc = presShell->GetDocument();
+
+ nsCOMPtr<nsPIDOMWindowOuter> domwin(doc->GetWindow());
+
+ nsCOMPtr<nsIWidget> widget;
+ nsViewManager* vm = presShell->GetViewManager();
+ if (vm) {
+ vm->GetWidget(getter_AddRefs(widget));
+ }
+ dom::Element* rootElement = doc->GetRootElement();
+
+ printf("DS %p Ty %s Doc %p DW %p EM %p CN %p\n",
+ (void*)parentAsDocShell.get(),
+ type == nsIDocShellTreeItem::typeChrome ? "Chr" : "Con",
+ (void*)doc, (void*)domwin.get(),
+ (void*)presContext->EventStateManager(), (void*)rootElement);
+
+ if (childWebshellCount > 0) {
+ for (int32_t i = 0; i < childWebshellCount; i++) {
+ nsCOMPtr<nsIDocShellTreeItem> child;
+ aParentNode->GetChildAt(i, getter_AddRefs(child));
+ PrintDocTree(child, aLevel + 1);
+ }
+ }
+}
+
+static void
+PrintDocTree(nsIDocShellTreeItem* aParentNode)
+{
+ NS_ASSERTION(aParentNode, "Pointer is null!");
+
+ nsCOMPtr<nsIDocShellTreeItem> parentItem;
+ aParentNode->GetParent(getter_AddRefs(parentItem));
+ while (parentItem) {
+ nsCOMPtr<nsIDocShellTreeItem> tmp;
+ parentItem->GetParent(getter_AddRefs(tmp));
+ if (!tmp) {
+ break;
+ }
+ parentItem = tmp;
+ }
+
+ if (!parentItem) {
+ parentItem = aParentNode;
+ }
+
+ PrintDocTree(parentItem, 0);
+}
+#endif
+
+NS_IMETHODIMP
+nsDocShell::SetTreeOwner(nsIDocShellTreeOwner* aTreeOwner)
+{
+#ifdef DEBUG_DOCSHELL_FOCUS
+ nsCOMPtr<nsIDocShellTreeItem> item(do_QueryInterface(aTreeOwner));
+ if (item) {
+ PrintDocTree(item);
+ }
+#endif
+
+ // Don't automatically set the progress based on the tree owner for frames
+ if (!IsFrame()) {
+ nsCOMPtr<nsIWebProgress> webProgress =
+ do_QueryInterface(GetAsSupports(this));
+
+ if (webProgress) {
+ nsCOMPtr<nsIWebProgressListener> oldListener =
+ do_QueryInterface(mTreeOwner);
+ nsCOMPtr<nsIWebProgressListener> newListener =
+ do_QueryInterface(aTreeOwner);
+
+ if (oldListener) {
+ webProgress->RemoveProgressListener(oldListener);
+ }
+
+ if (newListener) {
+ webProgress->AddProgressListener(newListener,
+ nsIWebProgress::NOTIFY_ALL);
+ }
+ }
+ }
+
+ mTreeOwner = aTreeOwner; // Weak reference per API
+
+ nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+ while (iter.HasMore()) {
+ nsCOMPtr<nsIDocShellTreeItem> child = do_QueryObject(iter.GetNext());
+ NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
+
+ if (child->ItemType() == mItemType) {
+ child->SetTreeOwner(aTreeOwner);
+ }
+ }
+
+ // Our tree owner has changed. Recompute scriptability.
+ //
+ // Note that this is near-redundant with the recomputation in
+ // SetDocLoaderParent(), but not so for the root DocShell, where the call to
+ // SetTreeOwner() happens after the initial AddDocLoaderAsChildOfRoot(),
+ // and we never set another parent. Given that this is neither expensive nor
+ // performance-critical, let's be safe and unconditionally recompute this
+ // state whenever dependent state changes.
+ RecomputeCanExecuteScripts();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetChildOffset(uint32_t aChildOffset)
+{
+ mChildOffset = aChildOffset;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetHistoryID(uint64_t* aID)
+{
+ *aID = mHistoryID;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetIsInUnload(bool* aIsInUnload)
+{
+ *aIsInUnload = mFiredUnloadEvent;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetChildCount(int32_t* aChildCount)
+{
+ NS_ENSURE_ARG_POINTER(aChildCount);
+ *aChildCount = mChildList.Length();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::AddChild(nsIDocShellTreeItem* aChild)
+{
+ NS_ENSURE_ARG_POINTER(aChild);
+
+ RefPtr<nsDocLoader> childAsDocLoader = GetAsDocLoader(aChild);
+ NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED);
+
+ // Make sure we're not creating a loop in the docshell tree
+ nsDocLoader* ancestor = this;
+ do {
+ if (childAsDocLoader == ancestor) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ ancestor = ancestor->GetParent();
+ } while (ancestor);
+
+ // Make sure to remove the child from its current parent.
+ nsDocLoader* childsParent = childAsDocLoader->GetParent();
+ if (childsParent) {
+ nsresult rv = childsParent->RemoveChildLoader(childAsDocLoader);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // Make sure to clear the treeowner in case this child is a different type
+ // from us.
+ aChild->SetTreeOwner(nullptr);
+
+ nsresult res = AddChildLoader(childAsDocLoader);
+ NS_ENSURE_SUCCESS(res, res);
+ NS_ASSERTION(!mChildList.IsEmpty(),
+ "child list must not be empty after a successful add");
+
+ nsCOMPtr<nsIDocShell> childDocShell = do_QueryInterface(aChild);
+ bool dynamic = false;
+ childDocShell->GetCreatedDynamically(&dynamic);
+ if (!dynamic) {
+ nsCOMPtr<nsISHEntry> currentSH;
+ bool oshe = false;
+ GetCurrentSHEntry(getter_AddRefs(currentSH), &oshe);
+ if (currentSH) {
+ currentSH->HasDynamicallyAddedChild(&dynamic);
+ }
+ }
+ childDocShell->SetChildOffset(dynamic ? -1 : mChildList.Length() - 1);
+
+ /* Set the child's global history if the parent has one */
+ if (mUseGlobalHistory) {
+ childDocShell->SetUseGlobalHistory(true);
+ }
+
+ if (aChild->ItemType() != mItemType) {
+ return NS_OK;
+ }
+
+ aChild->SetTreeOwner(mTreeOwner);
+
+ nsCOMPtr<nsIDocShell> childAsDocShell(do_QueryInterface(aChild));
+ if (!childAsDocShell) {
+ return NS_OK;
+ }
+
+ // charset, style-disabling, and zoom will be inherited in SetupNewViewer()
+
+ // Now take this document's charset and set the child's parentCharset field
+ // to it. We'll later use that field, in the loading process, for the
+ // charset choosing algorithm.
+ // If we fail, at any point, we just return NS_OK.
+ // This code has some performance impact. But this will be reduced when
+ // the current charset will finally be stored as an Atom, avoiding the
+ // alias resolution extra look-up.
+
+ // we are NOT going to propagate the charset is this Chrome's docshell
+ if (mItemType == nsIDocShellTreeItem::typeChrome) {
+ return NS_OK;
+ }
+
+ // get the parent's current charset
+ if (!mContentViewer) {
+ return NS_OK;
+ }
+ nsIDocument* doc = mContentViewer->GetDocument();
+ if (!doc) {
+ return NS_OK;
+ }
+
+ bool isWyciwyg = false;
+
+ if (mCurrentURI) {
+ // Check if the url is wyciwyg
+ mCurrentURI->SchemeIs("wyciwyg", &isWyciwyg);
+ }
+
+ if (!isWyciwyg) {
+ // If this docshell is loaded from a wyciwyg: URI, don't
+ // advertise our charset since it does not in any way reflect
+ // the actual source charset, which is what we're trying to
+ // expose here.
+
+ const nsACString& parentCS = doc->GetDocumentCharacterSet();
+ int32_t charsetSource = doc->GetDocumentCharacterSetSource();
+ // set the child's parentCharset
+ childAsDocShell->SetParentCharset(parentCS,
+ charsetSource,
+ doc->NodePrincipal());
+ }
+
+ // printf("### 1 >>> Adding child. Parent CS = %s. ItemType = %d.\n",
+ // NS_LossyConvertUTF16toASCII(parentCS).get(), mItemType);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::RemoveChild(nsIDocShellTreeItem* aChild)
+{
+ NS_ENSURE_ARG_POINTER(aChild);
+
+ RefPtr<nsDocLoader> childAsDocLoader = GetAsDocLoader(aChild);
+ NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED);
+
+ nsresult rv = RemoveChildLoader(childAsDocLoader);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aChild->SetTreeOwner(nullptr);
+
+ return nsDocLoader::AddDocLoaderAsChildOfRoot(childAsDocLoader);
+}
+
+NS_IMETHODIMP
+nsDocShell::GetChildAt(int32_t aIndex, nsIDocShellTreeItem** aChild)
+{
+ NS_ENSURE_ARG_POINTER(aChild);
+
+#ifdef DEBUG
+ if (aIndex < 0) {
+ NS_WARNING("Negative index passed to GetChildAt");
+ } else if (static_cast<uint32_t>(aIndex) >= mChildList.Length()) {
+ NS_WARNING("Too large an index passed to GetChildAt");
+ }
+#endif
+
+ nsIDocumentLoader* child = ChildAt(aIndex);
+ NS_ENSURE_TRUE(child, NS_ERROR_UNEXPECTED);
+
+ return CallQueryInterface(child, aChild);
+}
+
+NS_IMETHODIMP
+nsDocShell::FindChildWithName(const nsAString& aName,
+ bool aRecurse, bool aSameType,
+ nsIDocShellTreeItem* aRequestor,
+ nsIDocShellTreeItem* aOriginalRequestor,
+ nsIDocShellTreeItem** aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ // if we don't find one, we return NS_OK and a null result
+ *aResult = nullptr;
+
+ if (aName.IsEmpty()) {
+ return NS_OK;
+ }
+
+ nsXPIDLString childName;
+ nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+ while (iter.HasMore()) {
+ nsCOMPtr<nsIDocShellTreeItem> child = do_QueryObject(iter.GetNext());
+ NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
+ int32_t childType = child->ItemType();
+
+ if (aSameType && (childType != mItemType)) {
+ continue;
+ }
+
+ bool childNameEquals = false;
+ child->NameEquals(aName, &childNameEquals);
+ if (childNameEquals && ItemIsActive(child) &&
+ CanAccessItem(child, aOriginalRequestor)) {
+ child.swap(*aResult);
+ break;
+ }
+
+ // Only ask it to check children if it is same type
+ if (childType != mItemType) {
+ continue;
+ }
+
+ // Only ask the child if it isn't the requestor
+ if (aRecurse && (aRequestor != child)) {
+ // See if child contains the shell with the given name
+#ifdef DEBUG
+ nsresult rv =
+#endif
+ child->FindChildWithName(aName, true, aSameType,
+ static_cast<nsIDocShellTreeItem*>(this),
+ aOriginalRequestor, aResult);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "FindChildWithName should not fail here");
+ if (*aResult) {
+ // found it
+ return NS_OK;
+ }
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetChildSHEntry(int32_t aChildOffset, nsISHEntry** aResult)
+{
+ nsresult rv = NS_OK;
+
+ NS_ENSURE_ARG_POINTER(aResult);
+ *aResult = nullptr;
+
+ // A nsISHEntry for a child is *only* available when the parent is in
+ // the progress of loading a document too...
+
+ if (mLSHE) {
+ /* Before looking for the subframe's url, check
+ * the expiration status of the parent. If the parent
+ * has expired from cache, then subframes will not be
+ * loaded from history in certain situations.
+ */
+ bool parentExpired = false;
+ mLSHE->GetExpirationStatus(&parentExpired);
+
+ /* Get the parent's Load Type so that it can be set on the child too.
+ * By default give a loadHistory value
+ */
+ uint32_t loadType = nsIDocShellLoadInfo::loadHistory;
+ mLSHE->GetLoadType(&loadType);
+ // If the user did a shift-reload on this frameset page,
+ // we don't want to load the subframes from history.
+ if (loadType == nsIDocShellLoadInfo::loadReloadBypassCache ||
+ loadType == nsIDocShellLoadInfo::loadReloadBypassProxy ||
+ loadType == nsIDocShellLoadInfo::loadReloadBypassProxyAndCache ||
+ loadType == nsIDocShellLoadInfo::loadRefresh) {
+ return rv;
+ }
+
+ /* If the user pressed reload and the parent frame has expired
+ * from cache, we do not want to load the child frame from history.
+ */
+ if (parentExpired && (loadType == nsIDocShellLoadInfo::loadReloadNormal)) {
+ // The parent has expired. Return null.
+ *aResult = nullptr;
+ return rv;
+ }
+
+ nsCOMPtr<nsISHContainer> container(do_QueryInterface(mLSHE));
+ if (container) {
+ // Get the child subframe from session history.
+ rv = container->GetChildAt(aChildOffset, aResult);
+ if (*aResult) {
+ (*aResult)->SetLoadType(loadType);
+ }
+ }
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocShell::AddChildSHEntry(nsISHEntry* aCloneRef, nsISHEntry* aNewEntry,
+ int32_t aChildOffset, uint32_t aLoadType,
+ bool aCloneChildren)
+{
+ nsresult rv = NS_OK;
+
+ if (mLSHE && aLoadType != LOAD_PUSHSTATE) {
+ /* You get here if you are currently building a
+ * hierarchy ie.,you just visited a frameset page
+ */
+ nsCOMPtr<nsISHContainer> container(do_QueryInterface(mLSHE, &rv));
+ if (container) {
+ if (NS_FAILED(container->ReplaceChild(aNewEntry))) {
+ rv = container->AddChild(aNewEntry, aChildOffset);
+ }
+ }
+ } else if (!aCloneRef) {
+ /* This is an initial load in some subframe. Just append it if we can */
+ nsCOMPtr<nsISHContainer> container(do_QueryInterface(mOSHE, &rv));
+ if (container) {
+ rv = container->AddChild(aNewEntry, aChildOffset);
+ }
+ } else {
+ rv = AddChildSHEntryInternal(aCloneRef, aNewEntry, aChildOffset,
+ aLoadType, aCloneChildren);
+ }
+ return rv;
+}
+
+nsresult
+nsDocShell::AddChildSHEntryInternal(nsISHEntry* aCloneRef,
+ nsISHEntry* aNewEntry,
+ int32_t aChildOffset,
+ uint32_t aLoadType,
+ bool aCloneChildren)
+{
+ nsresult rv = NS_OK;
+ if (mSessionHistory) {
+ /* You are currently in the rootDocShell.
+ * You will get here when a subframe has a new url
+ * to load and you have walked up the tree all the
+ * way to the top to clone the current SHEntry hierarchy
+ * and replace the subframe where a new url was loaded with
+ * a new entry.
+ */
+ int32_t index = -1;
+ nsCOMPtr<nsISHEntry> currentHE;
+ mSessionHistory->GetIndex(&index);
+ if (index < 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ rv = mSessionHistory->GetEntryAtIndex(index, false,
+ getter_AddRefs(currentHE));
+ NS_ENSURE_TRUE(currentHE, NS_ERROR_FAILURE);
+
+ nsCOMPtr<nsISHEntry> currentEntry(do_QueryInterface(currentHE));
+ if (currentEntry) {
+ uint32_t cloneID = 0;
+ nsCOMPtr<nsISHEntry> nextEntry;
+ aCloneRef->GetID(&cloneID);
+ rv = CloneAndReplace(currentEntry, this, cloneID, aNewEntry,
+ aCloneChildren, getter_AddRefs(nextEntry));
+
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsISHistoryInternal> shPrivate =
+ do_QueryInterface(mSessionHistory);
+ NS_ENSURE_TRUE(shPrivate, NS_ERROR_FAILURE);
+ rv = shPrivate->AddEntry(nextEntry, true);
+ }
+ }
+ } else {
+ /* Just pass this along */
+ nsCOMPtr<nsIDocShell> parent =
+ do_QueryInterface(GetAsSupports(mParent), &rv);
+ if (parent) {
+ rv = static_cast<nsDocShell*>(parent.get())->AddChildSHEntryInternal(
+ aCloneRef, aNewEntry, aChildOffset, aLoadType, aCloneChildren);
+ }
+ }
+ return rv;
+}
+
+nsresult
+nsDocShell::AddChildSHEntryToParent(nsISHEntry* aNewEntry, int32_t aChildOffset,
+ bool aCloneChildren)
+{
+ /* You will get here when you are in a subframe and
+ * a new url has been loaded on you.
+ * The mOSHE in this subframe will be the previous url's
+ * mOSHE. This mOSHE will be used as the identification
+ * for this subframe in the CloneAndReplace function.
+ */
+
+ // In this case, we will end up calling AddEntry, which increases the
+ // current index by 1
+ nsCOMPtr<nsISHistory> rootSH;
+ GetRootSessionHistory(getter_AddRefs(rootSH));
+ if (rootSH) {
+ rootSH->GetIndex(&mPreviousTransIndex);
+ }
+
+ nsresult rv;
+ nsCOMPtr<nsIDocShell> parent = do_QueryInterface(GetAsSupports(mParent), &rv);
+ if (parent) {
+ rv = parent->AddChildSHEntry(mOSHE, aNewEntry, aChildOffset, mLoadType,
+ aCloneChildren);
+ }
+
+ if (rootSH) {
+ rootSH->GetIndex(&mLoadedTransIndex);
+#ifdef DEBUG_PAGE_CACHE
+ printf("Previous index: %d, Loaded index: %d\n\n", mPreviousTransIndex,
+ mLoadedTransIndex);
+#endif
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetUseGlobalHistory(bool aUseGlobalHistory)
+{
+ nsresult rv;
+
+ mUseGlobalHistory = aUseGlobalHistory;
+
+ if (!aUseGlobalHistory) {
+ mGlobalHistory = nullptr;
+ return NS_OK;
+ }
+
+ // No need to initialize mGlobalHistory if IHistory is available.
+ nsCOMPtr<IHistory> history = services::GetHistoryService();
+ if (history) {
+ return NS_OK;
+ }
+
+ if (mGlobalHistory) {
+ return NS_OK;
+ }
+
+ mGlobalHistory = do_GetService(NS_GLOBALHISTORY2_CONTRACTID, &rv);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetUseGlobalHistory(bool* aUseGlobalHistory)
+{
+ *aUseGlobalHistory = mUseGlobalHistory;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::RemoveFromSessionHistory()
+{
+ nsCOMPtr<nsISHistoryInternal> internalHistory;
+ nsCOMPtr<nsISHistory> sessionHistory;
+ nsCOMPtr<nsIDocShellTreeItem> root;
+ GetSameTypeRootTreeItem(getter_AddRefs(root));
+ if (root) {
+ nsCOMPtr<nsIWebNavigation> rootAsWebnav = do_QueryInterface(root);
+ if (rootAsWebnav) {
+ rootAsWebnav->GetSessionHistory(getter_AddRefs(sessionHistory));
+ internalHistory = do_QueryInterface(sessionHistory);
+ }
+ }
+ if (!internalHistory) {
+ return NS_OK;
+ }
+
+ int32_t index = 0;
+ sessionHistory->GetIndex(&index);
+ AutoTArray<uint64_t, 16> ids({mHistoryID});
+ internalHistory->RemoveEntries(ids, index);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetCreatedDynamically(bool aDynamic)
+{
+ mDynamicallyCreated = aDynamic;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetCreatedDynamically(bool* aDynamic)
+{
+ *aDynamic = mDynamicallyCreated;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetCurrentSHEntry(nsISHEntry** aEntry, bool* aOSHE)
+{
+ *aOSHE = false;
+ *aEntry = nullptr;
+ if (mLSHE) {
+ NS_ADDREF(*aEntry = mLSHE);
+ } else if (mOSHE) {
+ NS_ADDREF(*aEntry = mOSHE);
+ *aOSHE = true;
+ }
+ return NS_OK;
+}
+
+nsIScriptGlobalObject*
+nsDocShell::GetScriptGlobalObject()
+{
+ NS_ENSURE_SUCCESS(EnsureScriptEnvironment(), nullptr);
+ return mScriptGlobal;
+}
+
+nsIDocument*
+nsDocShell::GetDocument()
+{
+ NS_ENSURE_SUCCESS(EnsureContentViewer(), nullptr);
+ return mContentViewer->GetDocument();
+}
+
+nsPIDOMWindowOuter*
+nsDocShell::GetWindow()
+{
+ if (NS_FAILED(EnsureScriptEnvironment())) {
+ return nullptr;
+ }
+ return mScriptGlobal->AsOuter();
+}
+
+NS_IMETHODIMP
+nsDocShell::SetDeviceSizeIsPageSize(bool aValue)
+{
+ if (mDeviceSizeIsPageSize != aValue) {
+ mDeviceSizeIsPageSize = aValue;
+ RefPtr<nsPresContext> presContext;
+ GetPresContext(getter_AddRefs(presContext));
+ if (presContext) {
+ presContext->MediaFeatureValuesChanged(nsRestyleHint(0));
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetDeviceSizeIsPageSize(bool* aValue)
+{
+ *aValue = mDeviceSizeIsPageSize;
+ return NS_OK;
+}
+
+void
+nsDocShell::ClearFrameHistory(nsISHEntry* aEntry)
+{
+ nsCOMPtr<nsISHContainer> shcontainer = do_QueryInterface(aEntry);
+ nsCOMPtr<nsISHistory> rootSH;
+ GetRootSessionHistory(getter_AddRefs(rootSH));
+ nsCOMPtr<nsISHistoryInternal> history = do_QueryInterface(rootSH);
+ if (!history || !shcontainer) {
+ return;
+ }
+
+ int32_t count = 0;
+ shcontainer->GetChildCount(&count);
+ AutoTArray<uint64_t, 16> ids;
+ for (int32_t i = 0; i < count; ++i) {
+ nsCOMPtr<nsISHEntry> child;
+ shcontainer->GetChildAt(i, getter_AddRefs(child));
+ if (child) {
+ uint64_t id = 0;
+ child->GetDocshellID(&id);
+ ids.AppendElement(id);
+ }
+ }
+ int32_t index = 0;
+ rootSH->GetIndex(&index);
+ history->RemoveEntries(ids, index);
+}
+
+//-------------------------------------
+//-- Helper Method for Print discovery
+//-------------------------------------
+bool
+nsDocShell::IsPrintingOrPP(bool aDisplayErrorDialog)
+{
+ if (mIsPrintingOrPP && aDisplayErrorDialog) {
+ DisplayLoadError(NS_ERROR_DOCUMENT_IS_PRINTMODE, nullptr, nullptr, nullptr);
+ }
+
+ return mIsPrintingOrPP;
+}
+
+bool
+nsDocShell::IsNavigationAllowed(bool aDisplayPrintErrorDialog,
+ bool aCheckIfUnloadFired)
+{
+ bool isAllowed = !IsPrintingOrPP(aDisplayPrintErrorDialog) &&
+ (!aCheckIfUnloadFired || !mFiredUnloadEvent);
+ if (!isAllowed) {
+ return false;
+ }
+ if (!mContentViewer) {
+ return true;
+ }
+ bool firingBeforeUnload;
+ mContentViewer->GetBeforeUnloadFiring(&firingBeforeUnload);
+ return !firingBeforeUnload;
+}
+
+//*****************************************************************************
+// nsDocShell::nsIWebNavigation
+//*****************************************************************************
+
+NS_IMETHODIMP
+nsDocShell::GetCanGoBack(bool* aCanGoBack)
+{
+ if (!IsNavigationAllowed(false)) {
+ *aCanGoBack = false;
+ return NS_OK; // JS may not handle returning of an error code
+ }
+ nsresult rv;
+ nsCOMPtr<nsISHistory> rootSH;
+ rv = GetRootSessionHistory(getter_AddRefs(rootSH));
+ nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
+ NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
+ rv = webnav->GetCanGoBack(aCanGoBack);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetCanGoForward(bool* aCanGoForward)
+{
+ if (!IsNavigationAllowed(false)) {
+ *aCanGoForward = false;
+ return NS_OK; // JS may not handle returning of an error code
+ }
+ nsresult rv;
+ nsCOMPtr<nsISHistory> rootSH;
+ rv = GetRootSessionHistory(getter_AddRefs(rootSH));
+ nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
+ NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
+ rv = webnav->GetCanGoForward(aCanGoForward);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocShell::GoBack()
+{
+ if (!IsNavigationAllowed()) {
+ return NS_OK; // JS may not handle returning of an error code
+ }
+ nsresult rv;
+ nsCOMPtr<nsISHistory> rootSH;
+ rv = GetRootSessionHistory(getter_AddRefs(rootSH));
+ nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
+ NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
+ rv = webnav->GoBack();
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocShell::GoForward()
+{
+ if (!IsNavigationAllowed()) {
+ return NS_OK; // JS may not handle returning of an error code
+ }
+ nsresult rv;
+ nsCOMPtr<nsISHistory> rootSH;
+ rv = GetRootSessionHistory(getter_AddRefs(rootSH));
+ nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
+ NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
+ rv = webnav->GoForward();
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocShell::GotoIndex(int32_t aIndex)
+{
+ if (!IsNavigationAllowed()) {
+ return NS_OK; // JS may not handle returning of an error code
+ }
+ nsresult rv;
+ nsCOMPtr<nsISHistory> rootSH;
+ rv = GetRootSessionHistory(getter_AddRefs(rootSH));
+ nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
+ NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
+ rv = webnav->GotoIndex(aIndex);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocShell::LoadURI(const char16_t* aURI,
+ uint32_t aLoadFlags,
+ nsIURI* aReferringURI,
+ nsIInputStream* aPostStream,
+ nsIInputStream* aHeaderStream)
+{
+ return LoadURIWithOptions(aURI, aLoadFlags, aReferringURI,
+ mozilla::net::RP_Default, aPostStream,
+ aHeaderStream, nullptr);
+}
+
+NS_IMETHODIMP
+nsDocShell::LoadURIWithOptions(const char16_t* aURI,
+ uint32_t aLoadFlags,
+ nsIURI* aReferringURI,
+ uint32_t aReferrerPolicy,
+ nsIInputStream* aPostStream,
+ nsIInputStream* aHeaderStream,
+ nsIURI* aBaseURI)
+{
+ NS_ASSERTION((aLoadFlags & 0xf) == 0, "Unexpected flags");
+
+ if (!IsNavigationAllowed()) {
+ return NS_OK; // JS may not handle returning of an error code
+ }
+ nsCOMPtr<nsIURI> uri;
+ nsCOMPtr<nsIInputStream> postStream(aPostStream);
+ nsresult rv = NS_OK;
+
+ // Create a URI from our string; if that succeeds, we want to
+ // change aLoadFlags to not include the ALLOW_THIRD_PARTY_FIXUP
+ // flag.
+
+ NS_ConvertUTF16toUTF8 uriString(aURI);
+ // Cleanup the empty spaces that might be on each end.
+ uriString.Trim(" ");
+ // Eliminate embedded newlines, which single-line text fields now allow:
+ uriString.StripChars("\r\n");
+ NS_ENSURE_TRUE(!uriString.IsEmpty(), NS_ERROR_FAILURE);
+
+ rv = NS_NewURI(getter_AddRefs(uri), uriString);
+ if (uri) {
+ aLoadFlags &= ~LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
+ }
+
+ nsCOMPtr<nsIURIFixupInfo> fixupInfo;
+ if (sURIFixup) {
+ // Call the fixup object. This will clobber the rv from NS_NewURI
+ // above, but that's fine with us. Note that we need to do this even
+ // if NS_NewURI returned a URI, because fixup handles nested URIs, etc
+ // (things like view-source:mozilla.org for example).
+ uint32_t fixupFlags = 0;
+ if (aLoadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) {
+ fixupFlags |= nsIURIFixup::FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP;
+ }
+ if (aLoadFlags & LOAD_FLAGS_FIXUP_SCHEME_TYPOS) {
+ fixupFlags |= nsIURIFixup::FIXUP_FLAG_FIX_SCHEME_TYPOS;
+ }
+ nsCOMPtr<nsIInputStream> fixupStream;
+ rv = sURIFixup->GetFixupURIInfo(uriString, fixupFlags,
+ getter_AddRefs(fixupStream),
+ getter_AddRefs(fixupInfo));
+
+ if (NS_SUCCEEDED(rv)) {
+ fixupInfo->GetPreferredURI(getter_AddRefs(uri));
+ fixupInfo->SetConsumer(GetAsSupports(this));
+ }
+
+ if (fixupStream) {
+ // GetFixupURIInfo only returns a post data stream if it succeeded
+ // and changed the URI, in which case we should override the
+ // passed-in post data.
+ postStream = fixupStream;
+ }
+
+ if (aLoadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) {
+ nsCOMPtr<nsIObserverService> serv = services::GetObserverService();
+ if (serv) {
+ serv->NotifyObservers(fixupInfo, "keyword-uri-fixup", aURI);
+ }
+ }
+ }
+ // else no fixup service so just use the URI we created and see
+ // what happens
+
+ if (NS_ERROR_MALFORMED_URI == rv) {
+ if (DisplayLoadError(rv, uri, aURI, nullptr) &&
+ (aLoadFlags & LOAD_FLAGS_ERROR_LOAD_CHANGES_RV) != 0) {
+ return NS_ERROR_LOAD_SHOWED_ERRORPAGE;
+ }
+ }
+
+ if (NS_FAILED(rv) || !uri) {
+ return NS_ERROR_FAILURE;
+ }
+
+ PopupControlState popupState;
+ if (aLoadFlags & LOAD_FLAGS_ALLOW_POPUPS) {
+ popupState = openAllowed;
+ aLoadFlags &= ~LOAD_FLAGS_ALLOW_POPUPS;
+ } else {
+ popupState = openOverridden;
+ }
+ nsAutoPopupStatePusher statePusher(popupState);
+
+ // Don't pass certain flags that aren't needed and end up confusing
+ // ConvertLoadTypeToDocShellLoadInfo. We do need to ensure that they are
+ // passed to LoadURI though, since it uses them.
+ uint32_t extraFlags = (aLoadFlags & EXTRA_LOAD_FLAGS);
+ aLoadFlags &= ~EXTRA_LOAD_FLAGS;
+
+ nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
+ rv = CreateLoadInfo(getter_AddRefs(loadInfo));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ /*
+ * If the user "Disables Protection on This Page", we have to make sure to
+ * remember the users decision when opening links in child tabs [Bug 906190]
+ */
+ uint32_t loadType;
+ if (aLoadFlags & LOAD_FLAGS_ALLOW_MIXED_CONTENT) {
+ loadType = MAKE_LOAD_TYPE(LOAD_NORMAL_ALLOW_MIXED_CONTENT, aLoadFlags);
+ } else {
+ loadType = MAKE_LOAD_TYPE(LOAD_NORMAL, aLoadFlags);
+ }
+
+ loadInfo->SetLoadType(ConvertLoadTypeToDocShellLoadInfo(loadType));
+ loadInfo->SetPostDataStream(postStream);
+ loadInfo->SetReferrer(aReferringURI);
+ loadInfo->SetReferrerPolicy(aReferrerPolicy);
+ loadInfo->SetHeadersStream(aHeaderStream);
+ loadInfo->SetBaseURI(aBaseURI);
+
+ if (fixupInfo) {
+ nsAutoString searchProvider, keyword;
+ fixupInfo->GetKeywordProviderName(searchProvider);
+ fixupInfo->GetKeywordAsSent(keyword);
+ MaybeNotifyKeywordSearchLoading(searchProvider, keyword);
+ }
+
+ rv = LoadURI(uri, loadInfo, extraFlags, true);
+
+ // Save URI string in case it's needed later when
+ // sending to search engine service in EndPageLoad()
+ mOriginalUriString = uriString;
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocShell::DisplayLoadError(nsresult aError, nsIURI* aURI,
+ const char16_t* aURL,
+ nsIChannel* aFailedChannel,
+ bool* aDisplayedErrorPage)
+{
+ *aDisplayedErrorPage = false;
+ // Get prompt and string bundle servcies
+ nsCOMPtr<nsIPrompt> prompter;
+ nsCOMPtr<nsIStringBundle> stringBundle;
+ GetPromptAndStringBundle(getter_AddRefs(prompter),
+ getter_AddRefs(stringBundle));
+
+ NS_ENSURE_TRUE(stringBundle, NS_ERROR_FAILURE);
+ NS_ENSURE_TRUE(prompter, NS_ERROR_FAILURE);
+
+ nsAutoString error;
+ const uint32_t kMaxFormatStrArgs = 3;
+ nsAutoString formatStrs[kMaxFormatStrArgs];
+ uint32_t formatStrCount = 0;
+ bool addHostPort = false;
+ nsresult rv = NS_OK;
+ nsAutoString messageStr;
+ nsAutoCString cssClass;
+ nsAutoCString errorPage;
+
+ errorPage.AssignLiteral("neterror");
+
+ // Turn the error code into a human readable error message.
+ if (NS_ERROR_UNKNOWN_PROTOCOL == aError) {
+ NS_ENSURE_ARG_POINTER(aURI);
+
+ // Extract the schemes into a comma delimited list.
+ nsAutoCString scheme;
+ aURI->GetScheme(scheme);
+ CopyASCIItoUTF16(scheme, formatStrs[0]);
+ nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(aURI);
+ while (nestedURI) {
+ nsCOMPtr<nsIURI> tempURI;
+ nsresult rv2;
+ rv2 = nestedURI->GetInnerURI(getter_AddRefs(tempURI));
+ if (NS_SUCCEEDED(rv2) && tempURI) {
+ tempURI->GetScheme(scheme);
+ formatStrs[0].AppendLiteral(", ");
+ AppendASCIItoUTF16(scheme, formatStrs[0]);
+ }
+ nestedURI = do_QueryInterface(tempURI);
+ }
+ formatStrCount = 1;
+ error.AssignLiteral("unknownProtocolFound");
+ } else if (NS_ERROR_FILE_NOT_FOUND == aError) {
+ NS_ENSURE_ARG_POINTER(aURI);
+ error.AssignLiteral("fileNotFound");
+ } else if (NS_ERROR_FILE_ACCESS_DENIED == aError) {
+ NS_ENSURE_ARG_POINTER(aURI);
+ error.AssignLiteral("fileAccessDenied");
+ } else if (NS_ERROR_UNKNOWN_HOST == aError) {
+ NS_ENSURE_ARG_POINTER(aURI);
+ // Get the host
+ nsAutoCString host;
+ nsCOMPtr<nsIURI> innermostURI = NS_GetInnermostURI(aURI);
+ innermostURI->GetHost(host);
+ CopyUTF8toUTF16(host, formatStrs[0]);
+ formatStrCount = 1;
+ error.AssignLiteral("dnsNotFound");
+ } else if (NS_ERROR_CONNECTION_REFUSED == aError) {
+ NS_ENSURE_ARG_POINTER(aURI);
+ addHostPort = true;
+ error.AssignLiteral("connectionFailure");
+ } else if (NS_ERROR_NET_INTERRUPT == aError) {
+ NS_ENSURE_ARG_POINTER(aURI);
+ addHostPort = true;
+ error.AssignLiteral("netInterrupt");
+ } else if (NS_ERROR_NET_TIMEOUT == aError) {
+ NS_ENSURE_ARG_POINTER(aURI);
+ // Get the host
+ nsAutoCString host;
+ aURI->GetHost(host);
+ CopyUTF8toUTF16(host, formatStrs[0]);
+ formatStrCount = 1;
+ error.AssignLiteral("netTimeout");
+ } else if (NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION == aError ||
+ NS_ERROR_CSP_FORM_ACTION_VIOLATION == aError) {
+ // CSP error
+ cssClass.AssignLiteral("neterror");
+ error.AssignLiteral("cspBlocked");
+ } else if (NS_ERROR_GET_MODULE(aError) == NS_ERROR_MODULE_SECURITY) {
+ nsCOMPtr<nsINSSErrorsService> nsserr =
+ do_GetService(NS_NSS_ERRORS_SERVICE_CONTRACTID);
+
+ uint32_t errorClass;
+ if (!nsserr || NS_FAILED(nsserr->GetErrorClass(aError, &errorClass))) {
+ errorClass = nsINSSErrorsService::ERROR_CLASS_SSL_PROTOCOL;
+ }
+
+ nsCOMPtr<nsISupports> securityInfo;
+ nsCOMPtr<nsITransportSecurityInfo> tsi;
+ if (aFailedChannel) {
+ aFailedChannel->GetSecurityInfo(getter_AddRefs(securityInfo));
+ }
+ tsi = do_QueryInterface(securityInfo);
+ if (tsi) {
+ uint32_t securityState;
+ tsi->GetSecurityState(&securityState);
+ if (securityState & nsIWebProgressListener::STATE_USES_SSL_3) {
+ error.AssignLiteral("sslv3Used");
+ addHostPort = true;
+ } else if (securityState & nsIWebProgressListener::STATE_USES_WEAK_CRYPTO) {
+ error.AssignLiteral("weakCryptoUsed");
+ addHostPort = true;
+ } else {
+ // Usually we should have aFailedChannel and get a detailed message
+ tsi->GetErrorMessage(getter_Copies(messageStr));
+ }
+ } else {
+ // No channel, let's obtain the generic error message
+ if (nsserr) {
+ nsserr->GetErrorMessage(aError, messageStr);
+ }
+ }
+ if (!messageStr.IsEmpty()) {
+ if (errorClass == nsINSSErrorsService::ERROR_CLASS_BAD_CERT) {
+ error.AssignLiteral("nssBadCert");
+
+ // If this is an HTTP Strict Transport Security host or a pinned host
+ // and the certificate is bad, don't allow overrides (RFC 6797 section
+ // 12.1, HPKP draft spec section 2.6).
+ uint32_t flags =
+ UsePrivateBrowsing() ? nsISocketProvider::NO_PERMANENT_STORAGE : 0;
+ bool isStsHost = false;
+ bool isPinnedHost = false;
+ if (XRE_IsParentProcess()) {
+ nsCOMPtr<nsISiteSecurityService> sss =
+ do_GetService(NS_SSSERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, aURI,
+ flags, nullptr, &isStsHost);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HPKP, aURI,
+ flags, nullptr, &isPinnedHost);
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else {
+ mozilla::dom::ContentChild* cc =
+ mozilla::dom::ContentChild::GetSingleton();
+ mozilla::ipc::URIParams uri;
+ SerializeURI(aURI, uri);
+ cc->SendIsSecureURI(nsISiteSecurityService::HEADER_HSTS, uri, flags,
+ &isStsHost);
+ cc->SendIsSecureURI(nsISiteSecurityService::HEADER_HPKP, uri, flags,
+ &isPinnedHost);
+ }
+
+ if (Preferences::GetBool(
+ "browser.xul.error_pages.expert_bad_cert", false)) {
+ cssClass.AssignLiteral("expertBadCert");
+ }
+
+ // HSTS/pinning takes precedence over the expert bad cert pref. We
+ // never want to show the "Add Exception" button for these sites.
+ // In the future we should differentiate between an HSTS host and a
+ // pinned host and display a more informative message to the user.
+ if (isStsHost || isPinnedHost) {
+ cssClass.AssignLiteral("badStsCert");
+ }
+
+ uint32_t bucketId;
+ if (isStsHost) {
+ // measuring STS separately allows us to measure click through
+ // rates easily
+ bucketId = nsISecurityUITelemetry::WARNING_BAD_CERT_TOP_STS;
+ } else {
+ bucketId = nsISecurityUITelemetry::WARNING_BAD_CERT_TOP;
+ }
+
+ // See if an alternate cert error page is registered
+ nsAdoptingCString alternateErrorPage =
+ Preferences::GetCString("security.alternate_certificate_error_page");
+ if (alternateErrorPage) {
+ errorPage.Assign(alternateErrorPage);
+ }
+
+ if (!IsFrame() && errorPage.EqualsIgnoreCase("certerror")) {
+ Telemetry::Accumulate(mozilla::Telemetry::SECURITY_UI, bucketId);
+ }
+
+ } else {
+ error.AssignLiteral("nssFailure2");
+ }
+ }
+ } else if (NS_ERROR_PHISHING_URI == aError ||
+ NS_ERROR_MALWARE_URI == aError ||
+ NS_ERROR_UNWANTED_URI == aError) {
+ nsAutoCString host;
+ aURI->GetHost(host);
+ CopyUTF8toUTF16(host, formatStrs[0]);
+ formatStrCount = 1;
+
+ // Malware and phishing detectors may want to use an alternate error
+ // page, but if the pref's not set, we'll fall back on the standard page
+ nsAdoptingCString alternateErrorPage =
+ Preferences::GetCString("urlclassifier.alternate_error_page");
+ if (alternateErrorPage) {
+ errorPage.Assign(alternateErrorPage);
+ }
+
+ uint32_t bucketId;
+ bool sendTelemetry = false;
+ if (NS_ERROR_PHISHING_URI == aError) {
+ sendTelemetry = true;
+ error.AssignLiteral("deceptiveBlocked");
+ bucketId = IsFrame() ? nsISecurityUITelemetry::WARNING_PHISHING_PAGE_FRAME
+ : nsISecurityUITelemetry::WARNING_PHISHING_PAGE_TOP;
+ } else if (NS_ERROR_MALWARE_URI == aError) {
+ sendTelemetry = true;
+ error.AssignLiteral("malwareBlocked");
+ bucketId = IsFrame() ? nsISecurityUITelemetry::WARNING_MALWARE_PAGE_FRAME
+ : nsISecurityUITelemetry::WARNING_MALWARE_PAGE_TOP;
+ } else if (NS_ERROR_UNWANTED_URI == aError) {
+ sendTelemetry = true;
+ error.AssignLiteral("unwantedBlocked");
+ bucketId = IsFrame() ? nsISecurityUITelemetry::WARNING_UNWANTED_PAGE_FRAME
+ : nsISecurityUITelemetry::WARNING_UNWANTED_PAGE_TOP;
+ }
+
+ if (sendTelemetry && errorPage.EqualsIgnoreCase("blocked")) {
+ Telemetry::Accumulate(Telemetry::SECURITY_UI, bucketId);
+ }
+
+ cssClass.AssignLiteral("blacklist");
+ } else if (NS_ERROR_CONTENT_CRASHED == aError) {
+ errorPage.AssignLiteral("tabcrashed");
+ error.AssignLiteral("tabcrashed");
+
+ nsCOMPtr<EventTarget> handler = mChromeEventHandler;
+ if (handler) {
+ nsCOMPtr<Element> element = do_QueryInterface(handler);
+ element->GetAttribute(NS_LITERAL_STRING("crashedPageTitle"), messageStr);
+ }
+
+ // DisplayLoadError requires a non-empty messageStr to proceed and call
+ // LoadErrorPage. If the page doesn't have a title, we will use a blank
+ // space which will be trimmed and thus treated as empty by the front-end.
+ if (messageStr.IsEmpty()) {
+ messageStr.AssignLiteral(u" ");
+ }
+ } else {
+ // Errors requiring simple formatting
+ switch (aError) {
+ case NS_ERROR_MALFORMED_URI:
+ // URI is malformed
+ error.AssignLiteral("malformedURI");
+ break;
+ case NS_ERROR_REDIRECT_LOOP:
+ // Doc failed to load because the server generated too many redirects
+ error.AssignLiteral("redirectLoop");
+ break;
+ case NS_ERROR_UNKNOWN_SOCKET_TYPE:
+ // Doc failed to load because PSM is not installed
+ error.AssignLiteral("unknownSocketType");
+ break;
+ case NS_ERROR_NET_RESET:
+ // Doc failed to load because the server kept reseting the connection
+ // before we could read any data from it
+ error.AssignLiteral("netReset");
+ break;
+ case NS_ERROR_DOCUMENT_NOT_CACHED:
+ // Doc failed to load because the cache does not contain a copy of
+ // the document.
+ error.AssignLiteral("notCached");
+ break;
+ case NS_ERROR_OFFLINE:
+ // Doc failed to load because we are offline.
+ error.AssignLiteral("netOffline");
+ break;
+ case NS_ERROR_DOCUMENT_IS_PRINTMODE:
+ // Doc navigation attempted while Printing or Print Preview
+ error.AssignLiteral("isprinting");
+ break;
+ case NS_ERROR_PORT_ACCESS_NOT_ALLOWED:
+ // Port blocked for security reasons
+ addHostPort = true;
+ error.AssignLiteral("deniedPortAccess");
+ break;
+ case NS_ERROR_UNKNOWN_PROXY_HOST:
+ // Proxy hostname could not be resolved.
+ error.AssignLiteral("proxyResolveFailure");
+ break;
+ case NS_ERROR_PROXY_CONNECTION_REFUSED:
+ // Proxy connection was refused.
+ error.AssignLiteral("proxyConnectFailure");
+ break;
+ case NS_ERROR_INVALID_CONTENT_ENCODING:
+ // Bad Content Encoding.
+ error.AssignLiteral("contentEncodingError");
+ break;
+ case NS_ERROR_REMOTE_XUL:
+ error.AssignLiteral("remoteXUL");
+ break;
+ case NS_ERROR_UNSAFE_CONTENT_TYPE:
+ // Channel refused to load from an unrecognized content type.
+ error.AssignLiteral("unsafeContentType");
+ break;
+ case NS_ERROR_CORRUPTED_CONTENT:
+ // Broken Content Detected. e.g. Content-MD5 check failure.
+ error.AssignLiteral("corruptedContentErrorv2");
+ break;
+ case NS_ERROR_INTERCEPTION_FAILED:
+ // ServiceWorker intercepted request, but something went wrong.
+ error.AssignLiteral("corruptedContentErrorv2");
+ break;
+ case NS_ERROR_NET_INADEQUATE_SECURITY:
+ // Server negotiated bad TLS for HTTP/2.
+ error.AssignLiteral("inadequateSecurityError");
+ addHostPort = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ // Test if the error should be displayed
+ if (error.IsEmpty()) {
+ return NS_OK;
+ }
+
+ // Test if the error needs to be formatted
+ if (!messageStr.IsEmpty()) {
+ // already obtained message
+ } else {
+ if (addHostPort) {
+ // Build up the host:port string.
+ nsAutoCString hostport;
+ if (aURI) {
+ aURI->GetHostPort(hostport);
+ } else {
+ hostport.Assign('?');
+ }
+ CopyUTF8toUTF16(hostport, formatStrs[formatStrCount++]);
+ }
+
+ nsAutoCString spec;
+ rv = NS_ERROR_NOT_AVAILABLE;
+ if (aURI) {
+ // displaying "file://" is aesthetically unpleasing and could even be
+ // confusing to the user
+ bool isFileURI = false;
+ rv = aURI->SchemeIs("file", &isFileURI);
+ if (NS_SUCCEEDED(rv) && isFileURI) {
+ aURI->GetPath(spec);
+ } else {
+ aURI->GetSpec(spec);
+ }
+
+ nsAutoCString charset;
+ // unescape and convert from origin charset
+ aURI->GetOriginCharset(charset);
+ nsCOMPtr<nsITextToSubURI> textToSubURI(
+ do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv));
+ if (NS_SUCCEEDED(rv)) {
+ rv = textToSubURI->UnEscapeURIForUI(charset, spec,
+ formatStrs[formatStrCount]);
+ }
+ } else {
+ spec.Assign('?');
+ }
+ if (NS_FAILED(rv)) {
+ CopyUTF8toUTF16(spec, formatStrs[formatStrCount]);
+ }
+ rv = NS_OK;
+ ++formatStrCount;
+
+ const char16_t* strs[kMaxFormatStrArgs];
+ for (uint32_t i = 0; i < formatStrCount; i++) {
+ strs[i] = formatStrs[i].get();
+ }
+ nsXPIDLString str;
+ rv = stringBundle->FormatStringFromName(error.get(), strs, formatStrCount,
+ getter_Copies(str));
+ NS_ENSURE_SUCCESS(rv, rv);
+ messageStr.Assign(str.get());
+ }
+
+ // Display the error as a page or an alert prompt
+ NS_ENSURE_FALSE(messageStr.IsEmpty(), NS_ERROR_FAILURE);
+
+ if (NS_ERROR_NET_INTERRUPT == aError || NS_ERROR_NET_RESET == aError) {
+ bool isSecureURI = false;
+ rv = aURI->SchemeIs("https", &isSecureURI);
+ if (NS_SUCCEEDED(rv) && isSecureURI) {
+ // Maybe TLS intolerant. Treat this as an SSL error.
+ error.AssignLiteral("nssFailure2");
+ }
+ }
+
+ if (UseErrorPages()) {
+ // Display an error page
+ nsresult loadedPage = LoadErrorPage(aURI, aURL, errorPage.get(),
+ error.get(), messageStr.get(),
+ cssClass.get(), aFailedChannel);
+ *aDisplayedErrorPage = NS_SUCCEEDED(loadedPage);
+ } else {
+ // The prompter reqires that our private window has a document (or it
+ // asserts). Satisfy that assertion now since GetDoc will force
+ // creation of one if it hasn't already been created.
+ if (mScriptGlobal) {
+ Unused << mScriptGlobal->GetDoc();
+ }
+
+ // Display a message box
+ prompter->Alert(nullptr, messageStr.get());
+ }
+
+ return NS_OK;
+}
+
+#define PREF_SAFEBROWSING_ALLOWOVERRIDE "browser.safebrowsing.allowOverride"
+
+NS_IMETHODIMP
+nsDocShell::LoadErrorPage(nsIURI* aURI, const char16_t* aURL,
+ const char* aErrorPage,
+ const char16_t* aErrorType,
+ const char16_t* aDescription,
+ const char* aCSSClass,
+ nsIChannel* aFailedChannel)
+{
+#if defined(DEBUG)
+ if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) {
+ nsAutoCString chanName;
+ if (aFailedChannel) {
+ aFailedChannel->GetName(chanName);
+ } else {
+ chanName.AssignLiteral("<no channel>");
+ }
+
+ MOZ_LOG(gDocShellLog, LogLevel::Debug,
+ ("nsDocShell[%p]::LoadErrorPage(\"%s\", \"%s\", {...}, [%s])\n", this,
+ aURI->GetSpecOrDefault().get(), NS_ConvertUTF16toUTF8(aURL).get(),
+ chanName.get()));
+ }
+#endif
+ mFailedChannel = aFailedChannel;
+ mFailedURI = aURI;
+ mFailedLoadType = mLoadType;
+
+ if (mLSHE) {
+ // Abandon mLSHE's BFCache entry and create a new one. This way, if
+ // we go back or forward to another SHEntry with the same doc
+ // identifier, the error page won't persist.
+ mLSHE->AbandonBFCacheEntry();
+ }
+
+ nsAutoCString url;
+ nsAutoCString charset;
+ if (aURI) {
+ nsresult rv = aURI->GetSpec(url);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = aURI->GetOriginCharset(charset);
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else if (aURL) {
+ CopyUTF16toUTF8(aURL, url);
+ } else {
+ return NS_ERROR_INVALID_POINTER;
+ }
+
+ // Create a URL to pass all the error information through to the page.
+
+#undef SAFE_ESCAPE
+#define SAFE_ESCAPE(output, input, params) \
+ if (NS_WARN_IF(!NS_Escape(input, output, params))) { \
+ return NS_ERROR_OUT_OF_MEMORY; \
+ }
+
+ nsCString escapedUrl, escapedCharset, escapedError, escapedDescription,
+ escapedCSSClass;
+ SAFE_ESCAPE(escapedUrl, url, url_Path);
+ SAFE_ESCAPE(escapedCharset, charset, url_Path);
+ SAFE_ESCAPE(escapedError, NS_ConvertUTF16toUTF8(aErrorType), url_Path);
+ SAFE_ESCAPE(escapedDescription,
+ NS_ConvertUTF16toUTF8(aDescription), url_Path);
+ if (aCSSClass) {
+ nsCString cssClass(aCSSClass);
+ SAFE_ESCAPE(escapedCSSClass, cssClass, url_Path);
+ }
+ nsCString errorPageUrl("about:");
+ errorPageUrl.AppendASCII(aErrorPage);
+ errorPageUrl.AppendLiteral("?e=");
+
+ errorPageUrl.AppendASCII(escapedError.get());
+ errorPageUrl.AppendLiteral("&u=");
+ errorPageUrl.AppendASCII(escapedUrl.get());
+ if ((strcmp(aErrorPage, "blocked") == 0) &&
+ Preferences::GetBool(PREF_SAFEBROWSING_ALLOWOVERRIDE, true)) {
+ errorPageUrl.AppendLiteral("&o=1");
+ }
+ if (!escapedCSSClass.IsEmpty()) {
+ errorPageUrl.AppendLiteral("&s=");
+ errorPageUrl.AppendASCII(escapedCSSClass.get());
+ }
+ errorPageUrl.AppendLiteral("&c=");
+ errorPageUrl.AppendASCII(escapedCharset.get());
+
+ nsAutoCString frameType(FrameTypeToString(mFrameType));
+ errorPageUrl.AppendLiteral("&f=");
+ errorPageUrl.AppendASCII(frameType.get());
+
+ // Append the manifest URL if the error comes from an app.
+ nsString manifestURL;
+ nsresult rv = GetAppManifestURL(manifestURL);
+ if (manifestURL.Length() > 0) {
+ nsCString manifestParam;
+ SAFE_ESCAPE(manifestParam, NS_ConvertUTF16toUTF8(manifestURL), url_Path);
+ errorPageUrl.AppendLiteral("&m=");
+ errorPageUrl.AppendASCII(manifestParam.get());
+ }
+
+ nsCOMPtr<nsICaptivePortalService> cps = do_GetService(NS_CAPTIVEPORTAL_CID);
+ int32_t cpsState;
+ if (cps && NS_SUCCEEDED(cps->GetState(&cpsState)) &&
+ cpsState == nsICaptivePortalService::LOCKED_PORTAL) {
+ errorPageUrl.AppendLiteral("&captive=true");
+ }
+
+ // netError.xhtml's getDescription only handles the "d" parameter at the
+ // end of the URL, so append it last.
+ errorPageUrl.AppendLiteral("&d=");
+ errorPageUrl.AppendASCII(escapedDescription.get());
+
+ nsCOMPtr<nsIURI> errorPageURI;
+ rv = NS_NewURI(getter_AddRefs(errorPageURI), errorPageUrl);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return InternalLoad(errorPageURI, nullptr, false, nullptr,
+ mozilla::net::RP_Default,
+ nsContentUtils::GetSystemPrincipal(), nullptr,
+ INTERNAL_LOAD_FLAGS_NONE, EmptyString(),
+ nullptr, NullString(), nullptr, nullptr, LOAD_ERROR_PAGE,
+ nullptr, true, NullString(), this, nullptr, nullptr,
+ nullptr);
+}
+
+NS_IMETHODIMP
+nsDocShell::Reload(uint32_t aReloadFlags)
+{
+ if (!IsNavigationAllowed()) {
+ return NS_OK; // JS may not handle returning of an error code
+ }
+ nsresult rv;
+ NS_ASSERTION(((aReloadFlags & 0xf) == 0),
+ "Reload command not updated to use load flags!");
+ NS_ASSERTION((aReloadFlags & EXTRA_LOAD_FLAGS) == 0,
+ "Don't pass these flags to Reload");
+
+ uint32_t loadType = MAKE_LOAD_TYPE(LOAD_RELOAD_NORMAL, aReloadFlags);
+ NS_ENSURE_TRUE(IsValidLoadType(loadType), NS_ERROR_INVALID_ARG);
+
+ // Send notifications to the HistoryListener if any, about the impending
+ // reload
+ nsCOMPtr<nsISHistory> rootSH;
+ rv = GetRootSessionHistory(getter_AddRefs(rootSH));
+ nsCOMPtr<nsISHistoryInternal> shistInt(do_QueryInterface(rootSH));
+ bool canReload = true;
+ if (rootSH) {
+ shistInt->NotifyOnHistoryReload(mCurrentURI, aReloadFlags, &canReload);
+ }
+
+ if (!canReload) {
+ return NS_OK;
+ }
+
+ /* If you change this part of code, make sure bug 45297 does not re-occur */
+ if (mOSHE) {
+ rv = LoadHistoryEntry(mOSHE, loadType);
+ } else if (mLSHE) { // In case a reload happened before the current load is done
+ rv = LoadHistoryEntry(mLSHE, loadType);
+ } else {
+ nsCOMPtr<nsIDocument> doc(GetDocument());
+
+ if (!doc) {
+ return NS_OK;
+ }
+
+ // Do not inherit owner from document
+ uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
+ nsAutoString srcdoc;
+ nsCOMPtr<nsIURI> baseURI;
+ nsCOMPtr<nsIURI> originalURI;
+ bool loadReplace = false;
+
+ nsIPrincipal* triggeringPrincipal = doc->NodePrincipal();
+ nsAutoString contentTypeHint;
+ doc->GetContentType(contentTypeHint);
+
+ if (doc->IsSrcdocDocument()) {
+ doc->GetSrcdocData(srcdoc);
+ flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC;
+ baseURI = doc->GetBaseURI();
+ }
+ nsCOMPtr<nsIChannel> chan = doc->GetChannel();
+ if (chan) {
+ uint32_t loadFlags;
+ chan->GetLoadFlags(&loadFlags);
+ loadReplace = loadFlags & nsIChannel::LOAD_REPLACE;
+ nsCOMPtr<nsIHttpChannel> httpChan(do_QueryInterface(chan));
+ if (httpChan) {
+ httpChan->GetOriginalURI(getter_AddRefs(originalURI));
+ }
+ }
+
+ MOZ_ASSERT(triggeringPrincipal, "Need a valid triggeringPrincipal");
+
+ // Stack variables to ensure changes to the member variables don't affect to
+ // the call.
+ nsCOMPtr<nsIURI> currentURI = mCurrentURI;
+ nsCOMPtr<nsIURI> referrerURI = mReferrerURI;
+ uint32_t referrerPolicy = mReferrerPolicy;
+ rv = InternalLoad(currentURI,
+ originalURI,
+ loadReplace,
+ referrerURI,
+ referrerPolicy,
+ triggeringPrincipal,
+ triggeringPrincipal,
+ flags,
+ EmptyString(), // No window target
+ NS_LossyConvertUTF16toASCII(contentTypeHint).get(),
+ NullString(), // No forced download
+ nullptr, // No post data
+ nullptr, // No headers data
+ loadType, // Load type
+ nullptr, // No SHEntry
+ true,
+ srcdoc, // srcdoc argument for iframe
+ this, // For reloads we are the source
+ baseURI,
+ nullptr, // No nsIDocShell
+ nullptr); // No nsIRequest
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocShell::Stop(uint32_t aStopFlags)
+{
+ // Revoke any pending event related to content viewer restoration
+ mRestorePresentationEvent.Revoke();
+
+ if (mLoadType == LOAD_ERROR_PAGE) {
+ if (mLSHE) {
+ // Since error page loads never unset mLSHE, do so now
+ SetHistoryEntry(&mOSHE, mLSHE);
+ SetHistoryEntry(&mLSHE, nullptr);
+ }
+
+ mFailedChannel = nullptr;
+ mFailedURI = nullptr;
+ }
+
+ if (nsIWebNavigation::STOP_CONTENT & aStopFlags) {
+ // Stop the document loading
+ if (mContentViewer) {
+ nsCOMPtr<nsIContentViewer> cv = mContentViewer;
+ cv->Stop();
+ }
+ }
+
+ if (nsIWebNavigation::STOP_NETWORK & aStopFlags) {
+ // Suspend any timers that were set for this loader. We'll clear
+ // them out for good in CreateContentViewer.
+ if (mRefreshURIList) {
+ SuspendRefreshURIs();
+ mSavedRefreshURIList.swap(mRefreshURIList);
+ mRefreshURIList = nullptr;
+ }
+
+ // XXXbz We could also pass |this| to nsIURILoader::Stop. That will
+ // just call Stop() on us as an nsIDocumentLoader... We need fewer
+ // redundant apis!
+ Stop();
+ }
+
+ nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+ while (iter.HasMore()) {
+ nsCOMPtr<nsIWebNavigation> shellAsNav(do_QueryObject(iter.GetNext()));
+ if (shellAsNav) {
+ shellAsNav->Stop(aStopFlags);
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetDocument(nsIDOMDocument** aDocument)
+{
+ NS_ENSURE_ARG_POINTER(aDocument);
+ NS_ENSURE_SUCCESS(EnsureContentViewer(), NS_ERROR_FAILURE);
+
+ return mContentViewer->GetDOMDocument(aDocument);
+}
+
+NS_IMETHODIMP
+nsDocShell::GetCurrentURI(nsIURI** aURI)
+{
+ NS_ENSURE_ARG_POINTER(aURI);
+
+ if (mCurrentURI) {
+ return NS_EnsureSafeToReturn(mCurrentURI, aURI);
+ }
+
+ *aURI = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetReferringURI(nsIURI** aURI)
+{
+ NS_ENSURE_ARG_POINTER(aURI);
+
+ *aURI = mReferrerURI;
+ NS_IF_ADDREF(*aURI);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetSessionHistory(nsISHistory* aSessionHistory)
+{
+ NS_ENSURE_TRUE(aSessionHistory, NS_ERROR_FAILURE);
+ // make sure that we are the root docshell and
+ // set a handle to root docshell in SH.
+
+ nsCOMPtr<nsIDocShellTreeItem> root;
+ /* Get the root docshell. If *this* is the root docshell
+ * then save a handle to *this* in SH. SH needs it to do
+ * traversions thro' its entries
+ */
+ GetSameTypeRootTreeItem(getter_AddRefs(root));
+ NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
+ if (root.get() == static_cast<nsIDocShellTreeItem*>(this)) {
+ mSessionHistory = aSessionHistory;
+ nsCOMPtr<nsISHistoryInternal> shPrivate =
+ do_QueryInterface(mSessionHistory);
+ NS_ENSURE_TRUE(shPrivate, NS_ERROR_FAILURE);
+ shPrivate->SetRootDocShell(this);
+ return NS_OK;
+ }
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetSessionHistory(nsISHistory** aSessionHistory)
+{
+ NS_ENSURE_ARG_POINTER(aSessionHistory);
+ *aSessionHistory = mSessionHistory;
+ NS_IF_ADDREF(*aSessionHistory);
+ return NS_OK;
+}
+
+//*****************************************************************************
+// nsDocShell::nsIWebPageDescriptor
+//*****************************************************************************
+
+NS_IMETHODIMP
+nsDocShell::LoadPage(nsISupports* aPageDescriptor, uint32_t aDisplayType)
+{
+ nsCOMPtr<nsISHEntry> shEntryIn(do_QueryInterface(aPageDescriptor));
+
+ // Currently, the opaque 'page descriptor' is an nsISHEntry...
+ if (!shEntryIn) {
+ return NS_ERROR_INVALID_POINTER;
+ }
+
+ // Now clone shEntryIn, since we might end up modifying it later on, and we
+ // want a page descriptor to be reusable.
+ nsCOMPtr<nsISHEntry> shEntry;
+ nsresult rv = shEntryIn->Clone(getter_AddRefs(shEntry));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Give our cloned shEntry a new bfcache entry so this load is independent
+ // of all other loads. (This is important, in particular, for bugs 582795
+ // and 585298.)
+ rv = shEntry->AbandonBFCacheEntry();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ //
+ // load the page as view-source
+ //
+ if (nsIWebPageDescriptor::DISPLAY_AS_SOURCE == aDisplayType) {
+ nsCOMPtr<nsIURI> oldUri, newUri;
+ nsCString spec, newSpec;
+
+ // Create a new view-source URI and replace the original.
+ rv = shEntry->GetURI(getter_AddRefs(oldUri));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ oldUri->GetSpec(spec);
+ newSpec.AppendLiteral("view-source:");
+ newSpec.Append(spec);
+
+ rv = NS_NewURI(getter_AddRefs(newUri), newSpec);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ shEntry->SetURI(newUri);
+ shEntry->SetOriginalURI(nullptr);
+ }
+
+ rv = LoadHistoryEntry(shEntry, LOAD_HISTORY);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetCurrentDescriptor(nsISupports** aPageDescriptor)
+{
+ NS_PRECONDITION(aPageDescriptor, "Null out param?");
+
+ *aPageDescriptor = nullptr;
+
+ nsISHEntry* src = mOSHE ? mOSHE : mLSHE;
+ if (src) {
+ nsCOMPtr<nsISHEntry> dest;
+
+ nsresult rv = src->Clone(getter_AddRefs(dest));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // null out inappropriate cloned attributes...
+ dest->SetParent(nullptr);
+ dest->SetIsSubFrame(false);
+
+ return CallQueryInterface(dest, aPageDescriptor);
+ }
+
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+//*****************************************************************************
+// nsDocShell::nsIBaseWindow
+//*****************************************************************************
+
+NS_IMETHODIMP
+nsDocShell::InitWindow(nativeWindow aParentNativeWindow,
+ nsIWidget* aParentWidget, int32_t aX, int32_t aY,
+ int32_t aWidth, int32_t aHeight)
+{
+ SetParentWidget(aParentWidget);
+ SetPositionAndSize(aX, aY, aWidth, aHeight, 0);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::Create()
+{
+ if (mCreated) {
+ // We've already been created
+ return NS_OK;
+ }
+
+ NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome,
+ "Unexpected item type in docshell");
+
+ NS_ENSURE_TRUE(Preferences::GetRootBranch(), NS_ERROR_FAILURE);
+ mCreated = true;
+
+ if (gValidateOrigin == 0xffffffff) {
+ // Check pref to see if we should prevent frameset spoofing
+ gValidateOrigin =
+ Preferences::GetBool("browser.frame.validate_origin", true);
+ }
+
+ // Should we use XUL error pages instead of alerts if possible?
+ mUseErrorPages =
+ Preferences::GetBool("browser.xul.error_pages.enabled", mUseErrorPages);
+
+ if (!gAddedPreferencesVarCache) {
+ Preferences::AddBoolVarCache(&sUseErrorPages,
+ "browser.xul.error_pages.enabled",
+ mUseErrorPages);
+ gAddedPreferencesVarCache = true;
+ }
+
+ mDisableMetaRefreshWhenInactive =
+ Preferences::GetBool("browser.meta_refresh_when_inactive.disabled",
+ mDisableMetaRefreshWhenInactive);
+
+ mDeviceSizeIsPageSize =
+ Preferences::GetBool("docshell.device_size_is_page_size",
+ mDeviceSizeIsPageSize);
+
+ nsCOMPtr<nsIObserverService> serv = services::GetObserverService();
+ if (serv) {
+ const char* msg = mItemType == typeContent ?
+ NS_WEBNAVIGATION_CREATE : NS_CHROME_WEBNAVIGATION_CREATE;
+ serv->NotifyObservers(GetAsSupports(this), msg, nullptr);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::Destroy()
+{
+ NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome,
+ "Unexpected item type in docshell");
+
+ if (!mIsBeingDestroyed) {
+ nsCOMPtr<nsIObserverService> serv = services::GetObserverService();
+ if (serv) {
+ const char* msg = mItemType == typeContent ?
+ NS_WEBNAVIGATION_DESTROY : NS_CHROME_WEBNAVIGATION_DESTROY;
+ serv->NotifyObservers(GetAsSupports(this), msg, nullptr);
+ }
+ }
+
+ mIsBeingDestroyed = true;
+
+ // Make sure we don't record profile timeline markers anymore
+ SetRecordProfileTimelineMarkers(false);
+
+ // Remove our pref observers
+ if (mObserveErrorPages) {
+ mObserveErrorPages = false;
+ }
+
+ // Make sure to blow away our mLoadingURI just in case. No loads
+ // from inside this pagehide.
+ mLoadingURI = nullptr;
+
+ // Fire unload event before we blow anything away.
+ (void)FirePageHideNotification(true);
+
+ // Clear pointers to any detached nsEditorData that's lying
+ // around in shistory entries. Breaks cycle. See bug 430921.
+ if (mOSHE) {
+ mOSHE->SetEditorData(nullptr);
+ }
+ if (mLSHE) {
+ mLSHE->SetEditorData(nullptr);
+ }
+
+ // Note: mContentListener can be null if Init() failed and we're being
+ // called from the destructor.
+ if (mContentListener) {
+ mContentListener->DropDocShellReference();
+ mContentListener->SetParentContentListener(nullptr);
+ // Note that we do NOT set mContentListener to null here; that
+ // way if someone tries to do a load in us after this point
+ // the nsDSURIContentListener will block it. All of which
+ // means that we should do this before calling Stop(), of
+ // course.
+ }
+
+ // Stop any URLs that are currently being loaded...
+ Stop(nsIWebNavigation::STOP_ALL);
+
+ mEditorData = nullptr;
+
+ mTransferableHookData = nullptr;
+
+ // Save the state of the current document, before destroying the window.
+ // This is needed to capture the state of a frameset when the new document
+ // causes the frameset to be destroyed...
+ PersistLayoutHistoryState();
+
+ // Remove this docshell from its parent's child list
+ nsCOMPtr<nsIDocShellTreeItem> docShellParentAsItem =
+ do_QueryInterface(GetAsSupports(mParent));
+ if (docShellParentAsItem) {
+ docShellParentAsItem->RemoveChild(this);
+ }
+
+ if (mContentViewer) {
+ mContentViewer->Close(nullptr);
+ mContentViewer->Destroy();
+ mContentViewer = nullptr;
+ }
+
+ nsDocLoader::Destroy();
+
+ mParentWidget = nullptr;
+ mCurrentURI = nullptr;
+
+ if (mScriptGlobal) {
+ mScriptGlobal->DetachFromDocShell();
+ mScriptGlobal = nullptr;
+ }
+
+ if (mSessionHistory) {
+ // We want to destroy these content viewers now rather than
+ // letting their destruction wait for the session history
+ // entries to get garbage collected. (Bug 488394)
+ nsCOMPtr<nsISHistoryInternal> shPrivate =
+ do_QueryInterface(mSessionHistory);
+ if (shPrivate) {
+ shPrivate->EvictAllContentViewers();
+ }
+ mSessionHistory = nullptr;
+ }
+
+ SetTreeOwner(nullptr);
+
+ mOnePermittedSandboxedNavigator = nullptr;
+
+ // required to break ref cycle
+ mSecurityUI = nullptr;
+
+ // Cancel any timers that were set for this docshell; this is needed
+ // to break the cycle between us and the timers.
+ CancelRefreshURITimers();
+
+ if (UsePrivateBrowsing()) {
+ mPrivateBrowsingId = 0;
+ mOriginAttributes.SyncAttributesWithPrivateBrowsing(false);
+ if (mAffectPrivateSessionLifetime) {
+ DecreasePrivateDocShellCount();
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetUnscaledDevicePixelsPerCSSPixel(double* aScale)
+{
+ if (mParentWidget) {
+ *aScale = mParentWidget->GetDefaultScale().scale;
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIBaseWindow> ownerWindow(do_QueryInterface(mTreeOwner));
+ if (ownerWindow) {
+ return ownerWindow->GetUnscaledDevicePixelsPerCSSPixel(aScale);
+ }
+
+ *aScale = 1.0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetDevicePixelsPerDesktopPixel(double* aScale)
+{
+ if (mParentWidget) {
+ *aScale = mParentWidget->GetDesktopToDeviceScale().scale;
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIBaseWindow> ownerWindow(do_QueryInterface(mTreeOwner));
+ if (ownerWindow) {
+ return ownerWindow->GetDevicePixelsPerDesktopPixel(aScale);
+ }
+
+ *aScale = 1.0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetPosition(int32_t aX, int32_t aY)
+{
+ mBounds.x = aX;
+ mBounds.y = aY;
+
+ if (mContentViewer) {
+ NS_ENSURE_SUCCESS(mContentViewer->Move(aX, aY), NS_ERROR_FAILURE);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetPositionDesktopPix(int32_t aX, int32_t aY)
+{
+ nsCOMPtr<nsIBaseWindow> ownerWindow(do_QueryInterface(mTreeOwner));
+ if (ownerWindow) {
+ return ownerWindow->SetPositionDesktopPix(aX, aY);
+ }
+
+ double scale = 1.0;
+ GetDevicePixelsPerDesktopPixel(&scale);
+ return SetPosition(NSToIntRound(aX * scale), NSToIntRound(aY * scale));
+}
+
+NS_IMETHODIMP
+nsDocShell::GetPosition(int32_t* aX, int32_t* aY)
+{
+ return GetPositionAndSize(aX, aY, nullptr, nullptr);
+}
+
+NS_IMETHODIMP
+nsDocShell::SetSize(int32_t aWidth, int32_t aHeight, bool aRepaint)
+{
+ int32_t x = 0, y = 0;
+ GetPosition(&x, &y);
+ return SetPositionAndSize(x, y, aWidth, aHeight,
+ aRepaint ? nsIBaseWindow::eRepaint : 0);
+}
+
+NS_IMETHODIMP
+nsDocShell::GetSize(int32_t* aWidth, int32_t* aHeight)
+{
+ return GetPositionAndSize(nullptr, nullptr, aWidth, aHeight);
+}
+
+NS_IMETHODIMP
+nsDocShell::SetPositionAndSize(int32_t aX, int32_t aY, int32_t aWidth,
+ int32_t aHeight, uint32_t aFlags)
+{
+ mBounds.x = aX;
+ mBounds.y = aY;
+ mBounds.width = aWidth;
+ mBounds.height = aHeight;
+
+ // Hold strong ref, since SetBounds can make us null out mContentViewer
+ nsCOMPtr<nsIContentViewer> viewer = mContentViewer;
+ if (viewer) {
+ uint32_t cvflags = (aFlags & nsIBaseWindow::eDelayResize) ?
+ nsIContentViewer::eDelayResize : 0;
+ // XXX Border figured in here or is that handled elsewhere?
+ nsresult rv = viewer->SetBoundsWithFlags(mBounds, cvflags);
+ NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetPositionAndSize(int32_t* aX, int32_t* aY, int32_t* aWidth,
+ int32_t* aHeight)
+{
+ if (mParentWidget) {
+ // ensure size is up-to-date if window has changed resolution
+ LayoutDeviceIntRect r = mParentWidget->GetClientBounds();
+ SetPositionAndSize(mBounds.x, mBounds.y, r.width, r.height, 0);
+ }
+
+ // We should really consider just getting this information from
+ // our window instead of duplicating the storage and code...
+ if (aWidth || aHeight) {
+ // Caller wants to know our size; make sure to give them up to
+ // date information.
+ nsCOMPtr<nsIDocument> doc(do_GetInterface(GetAsSupports(mParent)));
+ if (doc) {
+ doc->FlushPendingNotifications(Flush_Layout);
+ }
+ }
+
+ DoGetPositionAndSize(aX, aY, aWidth, aHeight);
+ return NS_OK;
+}
+
+void
+nsDocShell::DoGetPositionAndSize(int32_t* aX, int32_t* aY, int32_t* aWidth,
+ int32_t* aHeight)
+{
+ if (aX) {
+ *aX = mBounds.x;
+ }
+ if (aY) {
+ *aY = mBounds.y;
+ }
+ if (aWidth) {
+ *aWidth = mBounds.width;
+ }
+ if (aHeight) {
+ *aHeight = mBounds.height;
+ }
+}
+
+NS_IMETHODIMP
+nsDocShell::Repaint(bool aForce)
+{
+ nsCOMPtr<nsIPresShell> presShell = GetPresShell();
+ NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
+
+ nsViewManager* viewManager = presShell->GetViewManager();
+ NS_ENSURE_TRUE(viewManager, NS_ERROR_FAILURE);
+
+ viewManager->InvalidateAllViews();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetParentWidget(nsIWidget** aParentWidget)
+{
+ NS_ENSURE_ARG_POINTER(aParentWidget);
+
+ *aParentWidget = mParentWidget;
+ NS_IF_ADDREF(*aParentWidget);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetParentWidget(nsIWidget* aParentWidget)
+{
+ mParentWidget = aParentWidget;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetParentNativeWindow(nativeWindow* aParentNativeWindow)
+{
+ NS_ENSURE_ARG_POINTER(aParentNativeWindow);
+
+ if (mParentWidget) {
+ *aParentNativeWindow = mParentWidget->GetNativeData(NS_NATIVE_WIDGET);
+ } else {
+ *aParentNativeWindow = nullptr;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetParentNativeWindow(nativeWindow aParentNativeWindow)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetNativeHandle(nsAString& aNativeHandle)
+{
+ // the nativeHandle should be accessed from nsIXULWindow
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetVisibility(bool* aVisibility)
+{
+ NS_ENSURE_ARG_POINTER(aVisibility);
+
+ *aVisibility = false;
+
+ if (!mContentViewer) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIPresShell> presShell = GetPresShell();
+ if (!presShell) {
+ return NS_OK;
+ }
+
+ // get the view manager
+ nsViewManager* vm = presShell->GetViewManager();
+ NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE);
+
+ // get the root view
+ nsView* view = vm->GetRootView(); // views are not ref counted
+ NS_ENSURE_TRUE(view, NS_ERROR_FAILURE);
+
+ // if our root view is hidden, we are not visible
+ if (view->GetVisibility() == nsViewVisibility_kHide) {
+ return NS_OK;
+ }
+
+ // otherwise, we must walk up the document and view trees checking
+ // for a hidden view, unless we're an off screen browser, which
+ // would make this test meaningless.
+
+ RefPtr<nsDocShell> docShell = this;
+ RefPtr<nsDocShell> parentItem = docShell->GetParentDocshell();
+ while (parentItem) {
+ presShell = docShell->GetPresShell();
+
+ nsCOMPtr<nsIPresShell> pPresShell = parentItem->GetPresShell();
+
+ // Null-check for crash in bug 267804
+ if (!pPresShell) {
+ NS_NOTREACHED("parent docshell has null pres shell");
+ return NS_OK;
+ }
+
+ vm = presShell->GetViewManager();
+ if (vm) {
+ view = vm->GetRootView();
+ }
+
+ if (view) {
+ view = view->GetParent(); // anonymous inner view
+ if (view) {
+ view = view->GetParent(); // subdocumentframe's view
+ }
+ }
+
+ nsIFrame* frame = view ? view->GetFrame() : nullptr;
+ bool isDocShellOffScreen = false;
+ docShell->GetIsOffScreenBrowser(&isDocShellOffScreen);
+ if (frame &&
+ !frame->IsVisibleConsideringAncestors(
+ nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY) &&
+ !isDocShellOffScreen) {
+ return NS_OK;
+ }
+
+ docShell = parentItem;
+ parentItem = docShell->GetParentDocshell();
+ }
+
+ nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(mTreeOwner));
+ if (!treeOwnerAsWin) {
+ *aVisibility = true;
+ return NS_OK;
+ }
+
+ // Check with the tree owner as well to give embedders a chance to
+ // expose visibility as well.
+ return treeOwnerAsWin->GetVisibility(aVisibility);
+}
+
+NS_IMETHODIMP
+nsDocShell::SetIsOffScreenBrowser(bool aIsOffScreen)
+{
+ mIsOffScreenBrowser = aIsOffScreen;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetIsOffScreenBrowser(bool* aIsOffScreen)
+{
+ *aIsOffScreen = mIsOffScreenBrowser;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetIsActive(bool aIsActive)
+{
+ // We disallow setting active on chrome docshells.
+ if (mItemType == nsIDocShellTreeItem::typeChrome) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ // Keep track ourselves.
+ mIsActive = aIsActive;
+
+ // Clear prerender flag if necessary.
+ mIsPrerendered &= !aIsActive;
+
+ // Tell the PresShell about it.
+ nsCOMPtr<nsIPresShell> pshell = GetPresShell();
+ if (pshell) {
+ pshell->SetIsActive(aIsActive);
+ }
+
+ // Tell the window about it
+ if (mScriptGlobal) {
+ mScriptGlobal->SetIsBackground(!aIsActive);
+ if (nsCOMPtr<nsIDocument> doc = mScriptGlobal->GetExtantDoc()) {
+ // Update orientation when the top-level browsing context becomes active.
+ // We make an exception for apps because they currently rely on
+ // orientation locks persisting across browsing contexts.
+ if (aIsActive && !GetIsApp()) {
+ nsCOMPtr<nsIDocShellTreeItem> parent;
+ GetSameTypeParent(getter_AddRefs(parent));
+ if (!parent) {
+ // We only care about the top-level browsing context.
+ uint16_t orientation = OrientationLock();
+ ScreenOrientation::UpdateActiveOrientationLock(orientation);
+ }
+ }
+
+ doc->PostVisibilityUpdateEvent();
+ }
+ }
+
+ // Tell the nsDOMNavigationTiming about it
+ RefPtr<nsDOMNavigationTiming> timing = mTiming;
+ if (!timing && mContentViewer) {
+ nsIDocument* doc = mContentViewer->GetDocument();
+ if (doc) {
+ timing = doc->GetNavigationTiming();
+ }
+ }
+ if (timing) {
+ timing->NotifyDocShellStateChanged(
+ aIsActive ? nsDOMNavigationTiming::DocShellState::eActive
+ : nsDOMNavigationTiming::DocShellState::eInactive);
+ }
+
+ // Recursively tell all of our children, but don't tell <iframe mozbrowser>
+ // children; they handle their state separately.
+ nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+ while (iter.HasMore()) {
+ nsCOMPtr<nsIDocShell> docshell = do_QueryObject(iter.GetNext());
+ if (!docshell) {
+ continue;
+ }
+
+ if (!docshell->GetIsMozBrowserOrApp()) {
+ docshell->SetIsActive(aIsActive);
+ }
+ }
+
+ // Restart or stop meta refresh timers if necessary
+ if (mDisableMetaRefreshWhenInactive) {
+ if (mIsActive) {
+ ResumeRefreshURIs();
+ } else {
+ SuspendRefreshURIs();
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetIsActive(bool* aIsActive)
+{
+ *aIsActive = mIsActive;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetIsPrerendered()
+{
+ MOZ_ASSERT(!mIsPrerendered,
+ "SetIsPrerendered() called on already prerendered docshell");
+ SetIsActive(false);
+ mIsPrerendered = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetIsPrerendered(bool* aIsPrerendered)
+{
+ *aIsPrerendered = mIsPrerendered;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetIsAppTab(bool aIsAppTab)
+{
+ mIsAppTab = aIsAppTab;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetIsAppTab(bool* aIsAppTab)
+{
+ *aIsAppTab = mIsAppTab;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetSandboxFlags(uint32_t aSandboxFlags)
+{
+ mSandboxFlags = aSandboxFlags;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetSandboxFlags(uint32_t* aSandboxFlags)
+{
+ *aSandboxFlags = mSandboxFlags;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetOnePermittedSandboxedNavigator(nsIDocShell* aSandboxedNavigator)
+{
+ if (mOnePermittedSandboxedNavigator) {
+ NS_ERROR("One Permitted Sandboxed Navigator should only be set once.");
+ return NS_OK;
+ }
+
+ mOnePermittedSandboxedNavigator = do_GetWeakReference(aSandboxedNavigator);
+ NS_ASSERTION(mOnePermittedSandboxedNavigator,
+ "One Permitted Sandboxed Navigator must support weak references.");
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetOnePermittedSandboxedNavigator(nsIDocShell** aSandboxedNavigator)
+{
+ NS_ENSURE_ARG_POINTER(aSandboxedNavigator);
+ nsCOMPtr<nsIDocShell> permittedNavigator =
+ do_QueryReferent(mOnePermittedSandboxedNavigator);
+ permittedNavigator.forget(aSandboxedNavigator);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetDefaultLoadFlags(uint32_t aDefaultLoadFlags)
+{
+ mDefaultLoadFlags = aDefaultLoadFlags;
+
+ // Tell the load group to set these flags all requests in the group
+ if (mLoadGroup) {
+ mLoadGroup->SetDefaultLoadFlags(aDefaultLoadFlags);
+ } else {
+ NS_WARNING("nsDocShell::SetDefaultLoadFlags has no loadGroup to propagate the flags to");
+ }
+
+ // Recursively tell all of our children. We *do not* skip
+ // <iframe mozbrowser> children - if someone sticks custom flags in this
+ // docShell then they too get the same flags.
+ nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+ while (iter.HasMore()) {
+ nsCOMPtr<nsIDocShell> docshell = do_QueryObject(iter.GetNext());
+ if (!docshell) {
+ continue;
+ }
+ docshell->SetDefaultLoadFlags(aDefaultLoadFlags);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetDefaultLoadFlags(uint32_t* aDefaultLoadFlags)
+{
+ *aDefaultLoadFlags = mDefaultLoadFlags;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetMixedContentChannel(nsIChannel* aMixedContentChannel)
+{
+#ifdef DEBUG
+ // if the channel is non-null
+ if (aMixedContentChannel) {
+ // Get the root docshell.
+ nsCOMPtr<nsIDocShellTreeItem> root;
+ GetSameTypeRootTreeItem(getter_AddRefs(root));
+ NS_WARNING_ASSERTION(root.get() == static_cast<nsIDocShellTreeItem*>(this),
+ "Setting mMixedContentChannel on a docshell that is "
+ "not the root docshell");
+ }
+#endif
+ mMixedContentChannel = aMixedContentChannel;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetFailedChannel(nsIChannel** aFailedChannel)
+{
+ NS_ENSURE_ARG_POINTER(aFailedChannel);
+ nsIDocument* doc = GetDocument();
+ if (!doc) {
+ *aFailedChannel = nullptr;
+ return NS_OK;
+ }
+ NS_IF_ADDREF(*aFailedChannel = doc->GetFailedChannel());
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetMixedContentChannel(nsIChannel** aMixedContentChannel)
+{
+ NS_ENSURE_ARG_POINTER(aMixedContentChannel);
+ NS_IF_ADDREF(*aMixedContentChannel = mMixedContentChannel);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetAllowMixedContentAndConnectionData(bool* aRootHasSecureConnection,
+ bool* aAllowMixedContent,
+ bool* aIsRootDocShell)
+{
+ *aRootHasSecureConnection = true;
+ *aAllowMixedContent = false;
+ *aIsRootDocShell = false;
+
+ nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
+ GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
+ NS_ASSERTION(sameTypeRoot,
+ "No document shell root tree item from document shell tree item!");
+ *aIsRootDocShell =
+ sameTypeRoot.get() == static_cast<nsIDocShellTreeItem*>(this);
+
+ // now get the document from sameTypeRoot
+ nsCOMPtr<nsIDocument> rootDoc = sameTypeRoot->GetDocument();
+ if (rootDoc) {
+ nsCOMPtr<nsIPrincipal> rootPrincipal = rootDoc->NodePrincipal();
+
+ // For things with system principal (e.g. scratchpad) there is no uri
+ // aRootHasSecureConnection should be false.
+ nsCOMPtr<nsIURI> rootUri;
+ if (nsContentUtils::IsSystemPrincipal(rootPrincipal) ||
+ NS_FAILED(rootPrincipal->GetURI(getter_AddRefs(rootUri))) || !rootUri ||
+ NS_FAILED(rootUri->SchemeIs("https", aRootHasSecureConnection))) {
+ *aRootHasSecureConnection = false;
+ }
+
+ // Check the root doc's channel against the root docShell's
+ // mMixedContentChannel to see if they are the same. If they are the same,
+ // the user has overriden the block.
+ nsCOMPtr<nsIDocShell> rootDocShell = do_QueryInterface(sameTypeRoot);
+ nsCOMPtr<nsIChannel> mixedChannel;
+ rootDocShell->GetMixedContentChannel(getter_AddRefs(mixedChannel));
+ *aAllowMixedContent =
+ mixedChannel && (mixedChannel == rootDoc->GetChannel());
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetVisibility(bool aVisibility)
+{
+ // Show()/Hide() may change mContentViewer.
+ nsCOMPtr<nsIContentViewer> cv = mContentViewer;
+ if (!cv) {
+ return NS_OK;
+ }
+ if (aVisibility) {
+ cv->Show();
+ } else {
+ cv->Hide();
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetEnabled(bool* aEnabled)
+{
+ NS_ENSURE_ARG_POINTER(aEnabled);
+ *aEnabled = true;
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetEnabled(bool aEnabled)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetFocus()
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetMainWidget(nsIWidget** aMainWidget)
+{
+ // We don't create our own widget, so simply return the parent one.
+ return GetParentWidget(aMainWidget);
+}
+
+NS_IMETHODIMP
+nsDocShell::GetTitle(char16_t** aTitle)
+{
+ NS_ENSURE_ARG_POINTER(aTitle);
+
+ *aTitle = ToNewUnicode(mTitle);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetTitle(const char16_t* aTitle)
+{
+ // Store local title
+ mTitle = aTitle;
+
+ nsCOMPtr<nsIDocShellTreeItem> parent;
+ GetSameTypeParent(getter_AddRefs(parent));
+
+ // When title is set on the top object it should then be passed to the
+ // tree owner.
+ if (!parent) {
+ nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(mTreeOwner));
+ if (treeOwnerAsWin) {
+ treeOwnerAsWin->SetTitle(aTitle);
+ }
+ }
+
+ AssertOriginAttributesMatchPrivateBrowsing();
+ if (mCurrentURI && mLoadType != LOAD_ERROR_PAGE && mUseGlobalHistory &&
+ !UsePrivateBrowsing()) {
+ nsCOMPtr<IHistory> history = services::GetHistoryService();
+ if (history) {
+ history->SetURITitle(mCurrentURI, mTitle);
+ } else if (mGlobalHistory) {
+ mGlobalHistory->SetPageTitle(mCurrentURI, nsString(mTitle));
+ }
+ }
+
+ // Update SessionHistory with the document's title.
+ if (mOSHE && mLoadType != LOAD_BYPASS_HISTORY &&
+ mLoadType != LOAD_ERROR_PAGE) {
+ mOSHE->SetTitle(mTitle);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsDocShell::GetCurScrollPos(int32_t aScrollOrientation, int32_t* aCurPos)
+{
+ NS_ENSURE_ARG_POINTER(aCurPos);
+
+ nsIScrollableFrame* sf = GetRootScrollFrame();
+ if (!sf) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsPoint pt = sf->GetScrollPosition();
+
+ switch (aScrollOrientation) {
+ case ScrollOrientation_X:
+ *aCurPos = pt.x;
+ return NS_OK;
+
+ case ScrollOrientation_Y:
+ *aCurPos = pt.y;
+ return NS_OK;
+
+ default:
+ NS_ENSURE_TRUE(false, NS_ERROR_INVALID_ARG);
+ }
+}
+
+nsresult
+nsDocShell::SetCurScrollPosEx(int32_t aCurHorizontalPos,
+ int32_t aCurVerticalPos)
+{
+ nsIScrollableFrame* sf = GetRootScrollFrame();
+ NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
+
+ sf->ScrollTo(nsPoint(aCurHorizontalPos, aCurVerticalPos),
+ nsIScrollableFrame::INSTANT);
+ return NS_OK;
+}
+
+//*****************************************************************************
+// nsDocShell::nsIScrollable
+//*****************************************************************************
+
+NS_IMETHODIMP
+nsDocShell::GetDefaultScrollbarPreferences(int32_t aScrollOrientation,
+ int32_t* aScrollbarPref)
+{
+ NS_ENSURE_ARG_POINTER(aScrollbarPref);
+ switch (aScrollOrientation) {
+ case ScrollOrientation_X:
+ *aScrollbarPref = mDefaultScrollbarPref.x;
+ return NS_OK;
+
+ case ScrollOrientation_Y:
+ *aScrollbarPref = mDefaultScrollbarPref.y;
+ return NS_OK;
+
+ default:
+ NS_ENSURE_TRUE(false, NS_ERROR_INVALID_ARG);
+ }
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetDefaultScrollbarPreferences(int32_t aScrollOrientation,
+ int32_t aScrollbarPref)
+{
+ switch (aScrollOrientation) {
+ case ScrollOrientation_X:
+ mDefaultScrollbarPref.x = aScrollbarPref;
+ return NS_OK;
+
+ case ScrollOrientation_Y:
+ mDefaultScrollbarPref.y = aScrollbarPref;
+ return NS_OK;
+
+ default:
+ NS_ENSURE_TRUE(false, NS_ERROR_INVALID_ARG);
+ }
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetScrollbarVisibility(bool* aVerticalVisible,
+ bool* aHorizontalVisible)
+{
+ nsIScrollableFrame* sf = GetRootScrollFrame();
+ NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
+
+ uint32_t scrollbarVisibility = sf->GetScrollbarVisibility();
+ if (aVerticalVisible) {
+ *aVerticalVisible =
+ (scrollbarVisibility & nsIScrollableFrame::VERTICAL) != 0;
+ }
+ if (aHorizontalVisible) {
+ *aHorizontalVisible =
+ (scrollbarVisibility & nsIScrollableFrame::HORIZONTAL) != 0;
+ }
+
+ return NS_OK;
+}
+
+//*****************************************************************************
+// nsDocShell::nsITextScroll
+//*****************************************************************************
+
+NS_IMETHODIMP
+nsDocShell::ScrollByLines(int32_t aNumLines)
+{
+ nsIScrollableFrame* sf = GetRootScrollFrame();
+ NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
+
+ sf->ScrollBy(nsIntPoint(0, aNumLines), nsIScrollableFrame::LINES,
+ nsIScrollableFrame::SMOOTH);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::ScrollByPages(int32_t aNumPages)
+{
+ nsIScrollableFrame* sf = GetRootScrollFrame();
+ NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
+
+ sf->ScrollBy(nsIntPoint(0, aNumPages), nsIScrollableFrame::PAGES,
+ nsIScrollableFrame::SMOOTH);
+ return NS_OK;
+}
+
+//*****************************************************************************
+// nsDocShell::nsIRefreshURI
+//*****************************************************************************
+
+NS_IMETHODIMP
+nsDocShell::RefreshURI(nsIURI* aURI,
+ int32_t aDelay, bool aRepeat,
+ bool aMetaRefresh,
+ nsIPrincipal* aPrincipal)
+{
+ NS_ENSURE_ARG(aURI);
+
+ /* Check if Meta refresh/redirects are permitted. Some
+ * embedded applications may not want to do this.
+ * Must do this before sending out NOTIFY_REFRESH events
+ * because listeners may have side effects (e.g. displaying a
+ * button to manually trigger the refresh later).
+ */
+ bool allowRedirects = true;
+ GetAllowMetaRedirects(&allowRedirects);
+ if (!allowRedirects) {
+ return NS_OK;
+ }
+
+ // If any web progress listeners are listening for NOTIFY_REFRESH events,
+ // give them a chance to block this refresh.
+ bool sameURI;
+ nsresult rv = aURI->Equals(mCurrentURI, &sameURI);
+ if (NS_FAILED(rv)) {
+ sameURI = false;
+ }
+ if (!RefreshAttempted(this, aURI, aDelay, sameURI)) {
+ return NS_OK;
+ }
+
+ nsRefreshTimer* refreshTimer = new nsRefreshTimer();
+ uint32_t busyFlags = 0;
+ GetBusyFlags(&busyFlags);
+
+ nsCOMPtr<nsISupports> dataRef = refreshTimer; // Get the ref count to 1
+
+ refreshTimer->mDocShell = this;
+ refreshTimer->mPrincipal = aPrincipal;
+ refreshTimer->mURI = aURI;
+ refreshTimer->mDelay = aDelay;
+ refreshTimer->mRepeat = aRepeat;
+ refreshTimer->mMetaRefresh = aMetaRefresh;
+
+ if (!mRefreshURIList) {
+ mRefreshURIList = nsArray::Create();
+ }
+
+ if (busyFlags & BUSY_FLAGS_BUSY || (!mIsActive && mDisableMetaRefreshWhenInactive)) {
+ // We don't want to create the timer right now. Instead queue up the request
+ // and trigger the timer in EndPageLoad() or whenever we become active.
+ mRefreshURIList->AppendElement(refreshTimer, /*weak =*/ false);
+ } else {
+ // There is no page loading going on right now. Create the
+ // timer and fire it right away.
+ nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1");
+ NS_ENSURE_TRUE(timer, NS_ERROR_FAILURE);
+
+ mRefreshURIList->AppendElement(timer, /*weak =*/ false); // owning timer ref
+ timer->InitWithCallback(refreshTimer, aDelay, nsITimer::TYPE_ONE_SHOT);
+ }
+ return NS_OK;
+}
+
+nsresult
+nsDocShell::ForceRefreshURIFromTimer(nsIURI* aURI,
+ int32_t aDelay,
+ bool aMetaRefresh,
+ nsITimer* aTimer,
+ nsIPrincipal* aPrincipal)
+{
+ NS_PRECONDITION(aTimer, "Must have a timer here");
+
+ // Remove aTimer from mRefreshURIList if needed
+ if (mRefreshURIList) {
+ uint32_t n = 0;
+ mRefreshURIList->GetLength(&n);
+
+ for (uint32_t i = 0; i < n; ++i) {
+ nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i);
+ if (timer == aTimer) {
+ mRefreshURIList->RemoveElementAt(i);
+ break;
+ }
+ }
+ }
+
+ return ForceRefreshURI(aURI, aDelay, aMetaRefresh, aPrincipal);
+}
+
+NS_IMETHODIMP
+nsDocShell::ForceRefreshURI(nsIURI* aURI, int32_t aDelay, bool aMetaRefresh, nsIPrincipal* aPrincipal)
+{
+ NS_ENSURE_ARG(aURI);
+
+ nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
+ CreateLoadInfo(getter_AddRefs(loadInfo));
+ NS_ENSURE_TRUE(loadInfo, NS_ERROR_OUT_OF_MEMORY);
+
+ /* We do need to pass in a referrer, but we don't want it to
+ * be sent to the server.
+ */
+ loadInfo->SetSendReferrer(false);
+
+ /* for most refreshes the current URI is an appropriate
+ * internal referrer
+ */
+ loadInfo->SetReferrer(mCurrentURI);
+
+ /* Don't ever "guess" on which principal to use to avoid picking
+ * the current principal.
+ */
+ loadInfo->SetPrincipalIsExplicit(true);
+
+ /* Check if this META refresh causes a redirection
+ * to another site.
+ */
+ bool equalUri = false;
+ nsresult rv = aURI->Equals(mCurrentURI, &equalUri);
+ if (NS_SUCCEEDED(rv) && (!equalUri) && aMetaRefresh &&
+ aDelay <= REFRESH_REDIRECT_TIMER) {
+ /* It is a META refresh based redirection within the threshold time
+ * we have in mind (15000 ms as defined by REFRESH_REDIRECT_TIMER).
+ * Pass a REPLACE flag to LoadURI().
+ */
+ loadInfo->SetLoadType(nsIDocShellLoadInfo::loadNormalReplace);
+
+ /* for redirects we mimic HTTP, which passes the
+ * original referrer
+ */
+ nsCOMPtr<nsIURI> internalReferrer;
+ GetReferringURI(getter_AddRefs(internalReferrer));
+ if (internalReferrer) {
+ loadInfo->SetReferrer(internalReferrer);
+ }
+ } else {
+ loadInfo->SetLoadType(nsIDocShellLoadInfo::loadRefresh);
+ }
+
+ // If the principal is null, the refresh will have a triggeringPrincipal
+ // derived from the referrer URI, or will be set to the system principal
+ // if there is no refererrer. See LoadURI()
+ if (aPrincipal) {
+ loadInfo->SetTriggeringPrincipal(aPrincipal);
+ }
+
+ /*
+ * LoadURI(...) will cancel all refresh timers... This causes the
+ * Timer and its refreshData instance to be released...
+ */
+ LoadURI(aURI, loadInfo, nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL, true);
+
+ return NS_OK;
+}
+
+nsresult
+nsDocShell::SetupRefreshURIFromHeader(nsIURI* aBaseURI,
+ nsIPrincipal* aPrincipal,
+ const nsACString& aHeader)
+{
+ // Refresh headers are parsed with the following format in mind
+ // <META HTTP-EQUIV=REFRESH CONTENT="5; URL=http://uri">
+ // By the time we are here, the following is true:
+ // header = "REFRESH"
+ // content = "5; URL=http://uri" // note the URL attribute is
+ // optional, if it is absent, the currently loaded url is used.
+ // Also note that the seconds and URL separator can be either
+ // a ';' or a ','. The ',' separator should be illegal but CNN
+ // is using it.
+ //
+ // We need to handle the following strings, where
+ // - X is a set of digits
+ // - URI is either a relative or absolute URI
+ //
+ // Note that URI should start with "url=" but we allow omission
+ //
+ // "" || ";" || ","
+ // empty string. use the currently loaded URI
+ // and refresh immediately.
+ // "X" || "X;" || "X,"
+ // Refresh the currently loaded URI in X seconds.
+ // "X; URI" || "X, URI"
+ // Refresh using URI as the destination in X seconds.
+ // "URI" || "; URI" || ", URI"
+ // Refresh immediately using URI as the destination.
+ //
+ // Currently, anything immediately following the URI, if
+ // separated by any char in the set "'\"\t\r\n " will be
+ // ignored. So "10; url=go.html ; foo=bar" will work,
+ // and so will "10; url='go.html'; foo=bar". However,
+ // "10; url=go.html; foo=bar" will result in the uri
+ // "go.html;" since ';' and ',' are valid uri characters.
+ //
+ // Note that we need to remove any tokens wrapping the URI.
+ // These tokens currently include spaces, double and single
+ // quotes.
+
+ // when done, seconds is 0 or the given number of seconds
+ // uriAttrib is empty or the URI specified
+ MOZ_ASSERT(aPrincipal);
+
+ nsAutoCString uriAttrib;
+ int32_t seconds = 0;
+ bool specifiesSeconds = false;
+
+ nsACString::const_iterator iter, tokenStart, doneIterating;
+
+ aHeader.BeginReading(iter);
+ aHeader.EndReading(doneIterating);
+
+ // skip leading whitespace
+ while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter)) {
+ ++iter;
+ }
+
+ tokenStart = iter;
+
+ // skip leading + and -
+ if (iter != doneIterating && (*iter == '-' || *iter == '+')) {
+ ++iter;
+ }
+
+ // parse number
+ while (iter != doneIterating && (*iter >= '0' && *iter <= '9')) {
+ seconds = seconds * 10 + (*iter - '0');
+ specifiesSeconds = true;
+ ++iter;
+ }
+
+ if (iter != doneIterating) {
+ // if we started with a '-', number is negative
+ if (*tokenStart == '-') {
+ seconds = -seconds;
+ }
+
+ // skip to next ';' or ','
+ nsACString::const_iterator iterAfterDigit = iter;
+ while (iter != doneIterating && !(*iter == ';' || *iter == ',')) {
+ if (specifiesSeconds) {
+ // Non-whitespace characters here mean that the string is
+ // malformed but tolerate sites that specify a decimal point,
+ // even though meta refresh only works on whole seconds.
+ if (iter == iterAfterDigit &&
+ !nsCRT::IsAsciiSpace(*iter) && *iter != '.') {
+ // The characters between the seconds and the next
+ // section are just garbage!
+ // e.g. content="2a0z+,URL=http://www.mozilla.org/"
+ // Just ignore this redirect.
+ return NS_ERROR_FAILURE;
+ } else if (nsCRT::IsAsciiSpace(*iter)) {
+ // We've had at least one whitespace so tolerate the mistake
+ // and drop through.
+ // e.g. content="10 foo"
+ ++iter;
+ break;
+ }
+ }
+ ++iter;
+ }
+
+ // skip any remaining whitespace
+ while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter)) {
+ ++iter;
+ }
+
+ // skip ';' or ','
+ if (iter != doneIterating && (*iter == ';' || *iter == ',')) {
+ ++iter;
+ }
+
+ // skip whitespace
+ while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter)) {
+ ++iter;
+ }
+ }
+
+ // possible start of URI
+ tokenStart = iter;
+
+ // skip "url = " to real start of URI
+ if (iter != doneIterating && (*iter == 'u' || *iter == 'U')) {
+ ++iter;
+ if (iter != doneIterating && (*iter == 'r' || *iter == 'R')) {
+ ++iter;
+ if (iter != doneIterating && (*iter == 'l' || *iter == 'L')) {
+ ++iter;
+
+ // skip whitespace
+ while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter)) {
+ ++iter;
+ }
+
+ if (iter != doneIterating && *iter == '=') {
+ ++iter;
+
+ // skip whitespace
+ while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter)) {
+ ++iter;
+ }
+
+ // found real start of URI
+ tokenStart = iter;
+ }
+ }
+ }
+ }
+
+ // skip a leading '"' or '\''.
+
+ bool isQuotedURI = false;
+ if (tokenStart != doneIterating &&
+ (*tokenStart == '"' || *tokenStart == '\'')) {
+ isQuotedURI = true;
+ ++tokenStart;
+ }
+
+ // set iter to start of URI
+ iter = tokenStart;
+
+ // tokenStart here points to the beginning of URI
+
+ // grab the rest of the URI
+ while (iter != doneIterating) {
+ if (isQuotedURI && (*iter == '"' || *iter == '\'')) {
+ break;
+ }
+ ++iter;
+ }
+
+ // move iter one back if the last character is a '"' or '\''
+ if (iter != tokenStart && isQuotedURI) {
+ --iter;
+ if (!(*iter == '"' || *iter == '\'')) {
+ ++iter;
+ }
+ }
+
+ // URI is whatever's contained from tokenStart to iter.
+ // note: if tokenStart == doneIterating, so is iter.
+
+ nsresult rv = NS_OK;
+
+ nsCOMPtr<nsIURI> uri;
+ bool specifiesURI = false;
+ if (tokenStart == iter) {
+ uri = aBaseURI;
+ } else {
+ uriAttrib = Substring(tokenStart, iter);
+ // NS_NewURI takes care of any whitespace surrounding the URL
+ rv = NS_NewURI(getter_AddRefs(uri), uriAttrib, nullptr, aBaseURI);
+ specifiesURI = true;
+ }
+
+ // No URI or seconds were specified
+ if (!specifiesSeconds && !specifiesURI) {
+ // Do nothing because the alternative is to spin around in a refresh
+ // loop forever!
+ return NS_ERROR_FAILURE;
+ }
+
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIScriptSecurityManager> securityManager(
+ do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv));
+ if (NS_SUCCEEDED(rv)) {
+ rv = securityManager->CheckLoadURIWithPrincipal(
+ aPrincipal, uri,
+ nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT);
+
+ if (NS_SUCCEEDED(rv)) {
+ bool isjs = true;
+ rv = NS_URIChainHasFlags(
+ uri, nsIProtocolHandler::URI_OPENING_EXECUTES_SCRIPT, &isjs);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (isjs) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ if (NS_SUCCEEDED(rv)) {
+ // Since we can't travel back in time yet, just pretend
+ // negative numbers do nothing at all.
+ if (seconds < 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ rv = RefreshURI(uri, seconds * 1000, false, true, aPrincipal);
+ }
+ }
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetupRefreshURI(nsIChannel* aChannel)
+{
+ nsresult rv;
+ nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel, &rv));
+ if (NS_SUCCEEDED(rv)) {
+ nsAutoCString refreshHeader;
+ rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("refresh"),
+ refreshHeader);
+
+ if (!refreshHeader.IsEmpty()) {
+ nsCOMPtr<nsIScriptSecurityManager> secMan =
+ do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIPrincipal> principal;
+ rv = secMan->GetChannelResultPrincipal(aChannel,
+ getter_AddRefs(principal));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ SetupReferrerFromChannel(aChannel);
+ rv = SetupRefreshURIFromHeader(mCurrentURI, principal, refreshHeader);
+ if (NS_SUCCEEDED(rv)) {
+ return NS_REFRESHURI_HEADER_FOUND;
+ }
+ }
+ }
+ return rv;
+}
+
+static void
+DoCancelRefreshURITimers(nsIMutableArray* aTimerList)
+{
+ if (!aTimerList) {
+ return;
+ }
+
+ uint32_t n = 0;
+ aTimerList->GetLength(&n);
+
+ while (n) {
+ nsCOMPtr<nsITimer> timer(do_QueryElementAt(aTimerList, --n));
+
+ aTimerList->RemoveElementAt(n); // bye bye owning timer ref
+
+ if (timer) {
+ timer->Cancel();
+ }
+ }
+}
+
+NS_IMETHODIMP
+nsDocShell::CancelRefreshURITimers()
+{
+ DoCancelRefreshURITimers(mRefreshURIList);
+ DoCancelRefreshURITimers(mSavedRefreshURIList);
+ mRefreshURIList = nullptr;
+ mSavedRefreshURIList = nullptr;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetRefreshPending(bool* aResult)
+{
+ if (!mRefreshURIList) {
+ *aResult = false;
+ return NS_OK;
+ }
+
+ uint32_t count;
+ nsresult rv = mRefreshURIList->GetLength(&count);
+ if (NS_SUCCEEDED(rv)) {
+ *aResult = (count != 0);
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocShell::SuspendRefreshURIs()
+{
+ if (mRefreshURIList) {
+ uint32_t n = 0;
+ mRefreshURIList->GetLength(&n);
+
+ for (uint32_t i = 0; i < n; ++i) {
+ nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i);
+ if (!timer) {
+ continue; // this must be a nsRefreshURI already
+ }
+
+ // Replace this timer object with a nsRefreshTimer object.
+ nsCOMPtr<nsITimerCallback> callback;
+ timer->GetCallback(getter_AddRefs(callback));
+
+ timer->Cancel();
+
+ nsCOMPtr<nsITimerCallback> rt = do_QueryInterface(callback);
+ NS_ASSERTION(rt,
+ "RefreshURIList timer callbacks should only be RefreshTimer objects");
+
+ mRefreshURIList->ReplaceElementAt(rt, i, /*weak =*/ false);
+ }
+ }
+
+ // Suspend refresh URIs for our child shells as well.
+ nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+ while (iter.HasMore()) {
+ nsCOMPtr<nsIDocShell> shell = do_QueryObject(iter.GetNext());
+ if (shell) {
+ shell->SuspendRefreshURIs();
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::ResumeRefreshURIs()
+{
+ RefreshURIFromQueue();
+
+ // Resume refresh URIs for our child shells as well.
+ nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+ while (iter.HasMore()) {
+ nsCOMPtr<nsIDocShell> shell = do_QueryObject(iter.GetNext());
+ if (shell) {
+ shell->ResumeRefreshURIs();
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsDocShell::RefreshURIFromQueue()
+{
+ if (!mRefreshURIList) {
+ return NS_OK;
+ }
+ uint32_t n = 0;
+ mRefreshURIList->GetLength(&n);
+
+ while (n) {
+ nsCOMPtr<nsITimerCallback> refreshInfo =
+ do_QueryElementAt(mRefreshURIList, --n);
+
+ if (refreshInfo) {
+ // This is the nsRefreshTimer object, waiting to be
+ // setup in a timer object and fired.
+ // Create the timer and trigger it.
+ uint32_t delay =
+ static_cast<nsRefreshTimer*>(
+ static_cast<nsITimerCallback*>(refreshInfo))->GetDelay();
+ nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1");
+ if (timer) {
+ // Replace the nsRefreshTimer element in the queue with
+ // its corresponding timer object, so that in case another
+ // load comes through before the timer can go off, the timer will
+ // get cancelled in CancelRefreshURITimer()
+ mRefreshURIList->ReplaceElementAt(timer, n, /*weak =*/ false);
+ timer->InitWithCallback(refreshInfo, delay, nsITimer::TYPE_ONE_SHOT);
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+//*****************************************************************************
+// nsDocShell::nsIContentViewerContainer
+//*****************************************************************************
+
+NS_IMETHODIMP
+nsDocShell::Embed(nsIContentViewer* aContentViewer,
+ const char* aCommand, nsISupports* aExtraInfo)
+{
+ // Save the LayoutHistoryState of the previous document, before
+ // setting up new document
+ PersistLayoutHistoryState();
+
+ nsresult rv = SetupNewViewer(aContentViewer);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // If we are loading a wyciwyg url from history, change the base URI for
+ // the document to the original http url that created the document.write().
+ // This makes sure that all relative urls in a document.written page loaded
+ // via history work properly.
+ if (mCurrentURI &&
+ (mLoadType & LOAD_CMD_HISTORY ||
+ mLoadType == LOAD_RELOAD_NORMAL ||
+ mLoadType == LOAD_RELOAD_CHARSET_CHANGE)) {
+ bool isWyciwyg = false;
+ // Check if the url is wyciwyg
+ rv = mCurrentURI->SchemeIs("wyciwyg", &isWyciwyg);
+ if (isWyciwyg && NS_SUCCEEDED(rv)) {
+ SetBaseUrlForWyciwyg(aContentViewer);
+ }
+ }
+ // XXX What if SetupNewViewer fails?
+ if (mLSHE) {
+ // Restore the editing state, if it's stored in session history.
+ if (mLSHE->HasDetachedEditor()) {
+ ReattachEditorToWindow(mLSHE);
+ }
+ // Set history.state
+ SetDocCurrentStateObj(mLSHE);
+
+ SetHistoryEntry(&mOSHE, mLSHE);
+ }
+
+ bool updateHistory = true;
+
+ // Determine if this type of load should update history
+ switch (mLoadType) {
+ case LOAD_NORMAL_REPLACE:
+ case LOAD_STOP_CONTENT_AND_REPLACE:
+ case LOAD_RELOAD_BYPASS_CACHE:
+ case LOAD_RELOAD_BYPASS_PROXY:
+ case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
+ case LOAD_REPLACE_BYPASS_CACHE:
+ updateHistory = false;
+ break;
+ default:
+ break;
+ }
+
+ if (!updateHistory) {
+ SetLayoutHistoryState(nullptr);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetIsPrinting(bool aIsPrinting)
+{
+ mIsPrintingOrPP = aIsPrinting;
+ return NS_OK;
+}
+
+//*****************************************************************************
+// nsDocShell::nsIWebProgressListener
+//*****************************************************************************
+
+NS_IMETHODIMP
+nsDocShell::OnProgressChange(nsIWebProgress* aProgress,
+ nsIRequest* aRequest,
+ int32_t aCurSelfProgress,
+ int32_t aMaxSelfProgress,
+ int32_t aCurTotalProgress,
+ int32_t aMaxTotalProgress)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::OnStateChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
+ uint32_t aStateFlags, nsresult aStatus)
+{
+ if ((~aStateFlags & (STATE_START | STATE_IS_NETWORK)) == 0) {
+ // Save timing statistics.
+ nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
+ nsCOMPtr<nsIURI> uri;
+ channel->GetURI(getter_AddRefs(uri));
+ nsAutoCString aURI;
+ uri->GetAsciiSpec(aURI);
+
+ nsCOMPtr<nsIWyciwygChannel> wcwgChannel(do_QueryInterface(aRequest));
+ nsCOMPtr<nsIWebProgress> webProgress =
+ do_QueryInterface(GetAsSupports(this));
+
+ // We don't update navigation timing for wyciwyg channels
+ if (this == aProgress && !wcwgChannel) {
+ MaybeInitTiming();
+ mTiming->NotifyFetchStart(uri,
+ ConvertLoadTypeToNavigationType(mLoadType));
+ }
+
+ // Was the wyciwyg document loaded on this docshell?
+ if (wcwgChannel && !mLSHE && (mItemType == typeContent) &&
+ aProgress == webProgress.get()) {
+ bool equalUri = true;
+ // Store the wyciwyg url in session history, only if it is
+ // being loaded fresh for the first time. We don't want
+ // multiple entries for successive loads
+ if (mCurrentURI &&
+ NS_SUCCEEDED(uri->Equals(mCurrentURI, &equalUri)) &&
+ !equalUri) {
+ nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
+ GetSameTypeParent(getter_AddRefs(parentAsItem));
+ nsCOMPtr<nsIDocShell> parentDS(do_QueryInterface(parentAsItem));
+ bool inOnLoadHandler = false;
+ if (parentDS) {
+ parentDS->GetIsExecutingOnLoadHandler(&inOnLoadHandler);
+ }
+ if (inOnLoadHandler) {
+ // We're handling parent's load event listener, which causes
+ // document.write in a subdocument.
+ // Need to clear the session history for all child
+ // docshells so that we can handle them like they would
+ // all be added dynamically.
+ nsCOMPtr<nsIDocShell> parent = do_QueryInterface(parentAsItem);
+ if (parent) {
+ bool oshe = false;
+ nsCOMPtr<nsISHEntry> entry;
+ parent->GetCurrentSHEntry(getter_AddRefs(entry), &oshe);
+ static_cast<nsDocShell*>(parent.get())->ClearFrameHistory(entry);
+ }
+ }
+
+ // This is a document.write(). Get the made-up url
+ // from the channel and store it in session history.
+ // Pass false for aCloneChildren, since we're creating
+ // a new DOM here.
+ AddToSessionHistory(uri, wcwgChannel, nullptr, nullptr, false,
+ getter_AddRefs(mLSHE));
+ SetCurrentURI(uri, aRequest, true, 0);
+ // Save history state of the previous page
+ PersistLayoutHistoryState();
+ // We'll never get an Embed() for this load, so just go ahead
+ // and SetHistoryEntry now.
+ SetHistoryEntry(&mOSHE, mLSHE);
+ }
+ }
+ // Page has begun to load
+ mBusyFlags = BUSY_FLAGS_BUSY | BUSY_FLAGS_BEFORE_PAGE_LOAD;
+
+ if ((aStateFlags & STATE_RESTORING) == 0) {
+ // Show the progress cursor if the pref is set
+ if (nsContentUtils::UseActivityCursor()) {
+ nsCOMPtr<nsIWidget> mainWidget;
+ GetMainWidget(getter_AddRefs(mainWidget));
+ if (mainWidget) {
+ mainWidget->SetCursor(eCursor_spinning);
+ }
+ }
+ }
+ } else if ((~aStateFlags & (STATE_TRANSFERRING | STATE_IS_DOCUMENT)) == 0) {
+ // Page is loading
+ mBusyFlags = BUSY_FLAGS_BUSY | BUSY_FLAGS_PAGE_LOADING;
+ } else if ((aStateFlags & STATE_STOP) && (aStateFlags & STATE_IS_NETWORK)) {
+ // Page has finished loading
+ mBusyFlags = BUSY_FLAGS_NONE;
+
+ // Hide the progress cursor if the pref is set
+ if (nsContentUtils::UseActivityCursor()) {
+ nsCOMPtr<nsIWidget> mainWidget;
+ GetMainWidget(getter_AddRefs(mainWidget));
+ if (mainWidget) {
+ mainWidget->SetCursor(eCursor_standard);
+ }
+ }
+ }
+ if ((~aStateFlags & (STATE_IS_DOCUMENT | STATE_STOP)) == 0) {
+ nsCOMPtr<nsIWebProgress> webProgress =
+ do_QueryInterface(GetAsSupports(this));
+ // Is the document stop notification for this document?
+ if (aProgress == webProgress.get()) {
+ nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
+ EndPageLoad(aProgress, channel, aStatus);
+ }
+ }
+ // note that redirect state changes will go through here as well, but it
+ // is better to handle those in OnRedirectStateChange where more
+ // information is available.
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::OnLocationChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
+ nsIURI* aURI, uint32_t aFlags)
+{
+ NS_NOTREACHED("notification excluded in AddProgressListener(...)");
+ return NS_OK;
+}
+
+void
+nsDocShell::OnRedirectStateChange(nsIChannel* aOldChannel,
+ nsIChannel* aNewChannel,
+ uint32_t aRedirectFlags,
+ uint32_t aStateFlags)
+{
+ NS_ASSERTION(aStateFlags & STATE_REDIRECTING,
+ "Calling OnRedirectStateChange when there is no redirect");
+
+ // If mixed content is allowed for the old channel, we forward
+ // the permission to the new channel if it has the same origin
+ // as the old one.
+ if (mMixedContentChannel && mMixedContentChannel == aOldChannel) {
+ nsresult rv = nsContentUtils::CheckSameOrigin(mMixedContentChannel, aNewChannel);
+ if (NS_SUCCEEDED(rv)) {
+ SetMixedContentChannel(aNewChannel); // Same origin: forward permission.
+ } else {
+ SetMixedContentChannel(nullptr); // Different origin: clear mMixedContentChannel.
+ }
+ }
+
+ if (!(aStateFlags & STATE_IS_DOCUMENT)) {
+ return; // not a toplevel document
+ }
+
+ nsCOMPtr<nsIURI> oldURI, newURI;
+ aOldChannel->GetURI(getter_AddRefs(oldURI));
+ aNewChannel->GetURI(getter_AddRefs(newURI));
+ if (!oldURI || !newURI) {
+ return;
+ }
+
+ // Below a URI visit is saved (see AddURIVisit method doc).
+ // The visit chain looks something like:
+ // ...
+ // Site N - 1
+ // => Site N
+ // (redirect to =>) Site N + 1 (we are here!)
+
+ // Get N - 1 and transition type
+ nsCOMPtr<nsIURI> previousURI;
+ uint32_t previousFlags = 0;
+ ExtractLastVisit(aOldChannel, getter_AddRefs(previousURI), &previousFlags);
+
+ if (aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL ||
+ ChannelIsPost(aOldChannel)) {
+ // 1. Internal redirects are ignored because they are specific to the
+ // channel implementation.
+ // 2. POSTs are not saved by global history.
+ //
+ // Regardless, we need to propagate the previous visit to the new
+ // channel.
+ SaveLastVisit(aNewChannel, previousURI, previousFlags);
+ } else {
+ nsCOMPtr<nsIURI> referrer;
+ // Treat referrer as null if there is an error getting it.
+ (void)NS_GetReferrerFromChannel(aOldChannel, getter_AddRefs(referrer));
+
+ // Get the HTTP response code, if available.
+ uint32_t responseStatus = 0;
+ nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aOldChannel);
+ if (httpChannel) {
+ (void)httpChannel->GetResponseStatus(&responseStatus);
+ }
+
+ // Add visit N -1 => N
+ AddURIVisit(oldURI, referrer, previousURI, previousFlags, responseStatus);
+
+ // Since N + 1 could be the final destination, we will not save N => N + 1
+ // here. OnNewURI will do that, so we will cache it.
+ SaveLastVisit(aNewChannel, oldURI, aRedirectFlags);
+ }
+
+ // check if the new load should go through the application cache.
+ nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
+ do_QueryInterface(aNewChannel);
+ if (appCacheChannel) {
+ if (GeckoProcessType_Default != XRE_GetProcessType()) {
+ // Permission will be checked in the parent process.
+ appCacheChannel->SetChooseApplicationCache(true);
+ } else {
+ nsCOMPtr<nsIScriptSecurityManager> secMan =
+ do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
+
+ if (secMan) {
+ nsCOMPtr<nsIPrincipal> principal;
+ secMan->GetDocShellCodebasePrincipal(newURI, this,
+ getter_AddRefs(principal));
+ appCacheChannel->SetChooseApplicationCache(
+ NS_ShouldCheckAppCache(principal, UsePrivateBrowsing()));
+ }
+ }
+ }
+
+ if (!(aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL) &&
+ mLoadType & (LOAD_CMD_RELOAD | LOAD_CMD_HISTORY)) {
+ mLoadType = LOAD_NORMAL_REPLACE;
+ SetHistoryEntry(&mLSHE, nullptr);
+ }
+}
+
+NS_IMETHODIMP
+nsDocShell::OnStatusChange(nsIWebProgress* aWebProgress,
+ nsIRequest* aRequest,
+ nsresult aStatus, const char16_t* aMessage)
+{
+ NS_NOTREACHED("notification excluded in AddProgressListener(...)");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::OnSecurityChange(nsIWebProgress* aWebProgress,
+ nsIRequest* aRequest, uint32_t aState)
+{
+ NS_NOTREACHED("notification excluded in AddProgressListener(...)");
+ return NS_OK;
+}
+
+nsresult
+nsDocShell::EndPageLoad(nsIWebProgress* aProgress,
+ nsIChannel* aChannel, nsresult aStatus)
+{
+ if (!aChannel) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ nsCOMPtr<nsIConsoleReportCollector> reporter = do_QueryInterface(aChannel);
+ if (reporter) {
+ reporter->FlushConsoleReports(GetDocument());
+ }
+
+ nsCOMPtr<nsIURI> url;
+ nsresult rv = aChannel->GetURI(getter_AddRefs(url));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ nsCOMPtr<nsITimedChannel> timingChannel = do_QueryInterface(aChannel);
+ if (timingChannel) {
+ TimeStamp channelCreationTime;
+ rv = timingChannel->GetChannelCreation(&channelCreationTime);
+ if (NS_SUCCEEDED(rv) && !channelCreationTime.IsNull()) {
+ Telemetry::AccumulateTimeDelta(Telemetry::TOTAL_CONTENT_PAGE_LOAD_TIME,
+ channelCreationTime);
+ nsCOMPtr<nsPILoadGroupInternal> internalLoadGroup =
+ do_QueryInterface(mLoadGroup);
+ if (internalLoadGroup) {
+ internalLoadGroup->OnEndPageLoad(aChannel);
+ }
+ }
+ }
+
+ // Timing is picked up by the window, we don't need it anymore
+ mTiming = nullptr;
+
+ // clean up reload state for meta charset
+ if (eCharsetReloadRequested == mCharsetReloadState) {
+ mCharsetReloadState = eCharsetReloadStopOrigional;
+ } else {
+ mCharsetReloadState = eCharsetReloadInit;
+ }
+
+ // Save a pointer to the currently-loading history entry.
+ // nsDocShell::EndPageLoad will clear mLSHE, but we may need this history
+ // entry further down in this method.
+ nsCOMPtr<nsISHEntry> loadingSHE = mLSHE;
+ mozilla::Unused << loadingSHE; // XXX: Not sure if we need this anymore
+
+ //
+ // one of many safeguards that prevent death and destruction if
+ // someone is so very very rude as to bring this window down
+ // during this load handler.
+ //
+ nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
+
+ // Notify the ContentViewer that the Document has finished loading. This
+ // will cause any OnLoad(...) and PopState(...) handlers to fire.
+ if (!mEODForCurrentDocument && mContentViewer) {
+ mIsExecutingOnLoadHandler = true;
+ mContentViewer->LoadComplete(aStatus);
+ mIsExecutingOnLoadHandler = false;
+
+ mEODForCurrentDocument = true;
+
+ // If all documents have completed their loading
+ // favor native event dispatch priorities
+ // over performance
+ if (--gNumberOfDocumentsLoading == 0) {
+ // Hint to use normal native event dispatch priorities
+ FavorPerformanceHint(false);
+ }
+ }
+ /* Check if the httpChannel has any cache-control related response headers,
+ * like no-store, no-cache. If so, update SHEntry so that
+ * when a user goes back/forward to this page, we appropriately do
+ * form value restoration or load from server.
+ */
+ nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
+ if (!httpChannel) {
+ // HttpChannel could be hiding underneath a Multipart channel.
+ GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
+ }
+
+ if (httpChannel) {
+ // figure out if SH should be saving layout state.
+ bool discardLayoutState = ShouldDiscardLayoutState(httpChannel);
+ if (mLSHE && discardLayoutState && (mLoadType & LOAD_CMD_NORMAL) &&
+ (mLoadType != LOAD_BYPASS_HISTORY) && (mLoadType != LOAD_ERROR_PAGE)) {
+ mLSHE->SetSaveLayoutStateFlag(false);
+ }
+ }
+
+ // Clear mLSHE after calling the onLoadHandlers. This way, if the
+ // onLoadHandler tries to load something different in
+ // itself or one of its children, we can deal with it appropriately.
+ if (mLSHE) {
+ mLSHE->SetLoadType(nsIDocShellLoadInfo::loadHistory);
+
+ // Clear the mLSHE reference to indicate document loading is done one
+ // way or another.
+ SetHistoryEntry(&mLSHE, nullptr);
+ }
+ // if there's a refresh header in the channel, this method
+ // will set it up for us.
+ if (mIsActive || !mDisableMetaRefreshWhenInactive)
+ RefreshURIFromQueue();
+
+ // Test whether this is the top frame or a subframe
+ bool isTopFrame = true;
+ nsCOMPtr<nsIDocShellTreeItem> targetParentTreeItem;
+ rv = GetSameTypeParent(getter_AddRefs(targetParentTreeItem));
+ if (NS_SUCCEEDED(rv) && targetParentTreeItem) {
+ isTopFrame = false;
+ }
+
+ //
+ // If the page load failed, then deal with the error condition...
+ // Errors are handled as follows:
+ // 1. Check to see if it's a file not found error or bad content
+ // encoding error.
+ // 2. Send the URI to a keyword server (if enabled)
+ // 3. If the error was DNS failure, then add www and .com to the URI
+ // (if appropriate).
+ // 4. Throw an error dialog box...
+ //
+ if (url && NS_FAILED(aStatus)) {
+ if (aStatus == NS_ERROR_FILE_NOT_FOUND ||
+ aStatus == NS_ERROR_FILE_ACCESS_DENIED ||
+ aStatus == NS_ERROR_CORRUPTED_CONTENT ||
+ aStatus == NS_ERROR_INVALID_CONTENT_ENCODING) {
+ DisplayLoadError(aStatus, url, nullptr, aChannel);
+ return NS_OK;
+ } else if (aStatus == NS_ERROR_INVALID_SIGNATURE) {
+ // NS_ERROR_INVALID_SIGNATURE indicates a content-signature error.
+ // This currently only happens in case a remote about page fails.
+ // We have to load a fallback in this case.
+ // XXX: We always load about blank here, firefox has to overwrite this if
+ // it wants to display something else.
+ return LoadURI(u"about:blank", // URI string
+ nsIChannel::LOAD_NORMAL, // Load flags
+ nullptr, // Referring URI
+ nullptr, // Post data stream
+ nullptr); // Headers stream
+ }
+
+ // Handle iframe document not loading error because source was
+ // a tracking URL. We make a note of this iframe node by including
+ // it in a dedicated array of blocked tracking nodes under its parent
+ // document. (document of parent window of blocked document)
+ if (isTopFrame == false && aStatus == NS_ERROR_TRACKING_URI) {
+ // frameElement is our nsIContent to be annotated
+ nsCOMPtr<nsIDOMElement> frameElement;
+ nsPIDOMWindowOuter* thisWindow = GetWindow();
+ if (!thisWindow) {
+ return NS_OK;
+ }
+
+ frameElement = thisWindow->GetFrameElement();
+ if (!frameElement) {
+ return NS_OK;
+ }
+
+ // Parent window
+ nsCOMPtr<nsIDocShellTreeItem> parentItem;
+ GetSameTypeParent(getter_AddRefs(parentItem));
+ if (!parentItem) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIDocument> parentDoc;
+ parentDoc = parentItem->GetDocument();
+ if (!parentDoc) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIContent> cont = do_QueryInterface(frameElement);
+ parentDoc->AddBlockedTrackingNode(cont);
+
+ return NS_OK;
+ }
+
+ if (sURIFixup) {
+ //
+ // Try and make an alternative URI from the old one
+ //
+ nsCOMPtr<nsIURI> newURI;
+ nsCOMPtr<nsIInputStream> newPostData;
+
+ nsAutoCString oldSpec;
+ url->GetSpec(oldSpec);
+
+ //
+ // First try keyword fixup
+ //
+ nsAutoString keywordProviderName, keywordAsSent;
+ if (aStatus == NS_ERROR_UNKNOWN_HOST && mAllowKeywordFixup) {
+ bool keywordsEnabled = Preferences::GetBool("keyword.enabled", false);
+
+ nsAutoCString host;
+ url->GetHost(host);
+
+ nsAutoCString scheme;
+ url->GetScheme(scheme);
+
+ int32_t dotLoc = host.FindChar('.');
+
+ // we should only perform a keyword search under the following
+ // conditions:
+ // (0) Pref keyword.enabled is true
+ // (1) the url scheme is http (or https)
+ // (2) the url does not have a protocol scheme
+ // If we don't enforce such a policy, then we end up doing
+ // keyword searchs on urls we don't intend like imap, file,
+ // mailbox, etc. This could lead to a security problem where we
+ // send data to the keyword server that we shouldn't be.
+ // Someone needs to clean up keywords in general so we can
+ // determine on a per url basis if we want keywords
+ // enabled...this is just a bandaid...
+ if (keywordsEnabled && !scheme.IsEmpty() &&
+ (scheme.Find("http") != 0)) {
+ keywordsEnabled = false;
+ }
+
+ if (keywordsEnabled && (kNotFound == dotLoc)) {
+ nsCOMPtr<nsIURIFixupInfo> info;
+ // only send non-qualified hosts to the keyword server
+ if (!mOriginalUriString.IsEmpty()) {
+ sURIFixup->KeywordToURI(mOriginalUriString,
+ getter_AddRefs(newPostData),
+ getter_AddRefs(info));
+ } else {
+ //
+ // If this string was passed through nsStandardURL by
+ // chance, then it may have been converted from UTF-8 to
+ // ACE, which would result in a completely bogus keyword
+ // query. Here we try to recover the original Unicode
+ // value, but this is not 100% correct since the value may
+ // have been normalized per the IDN normalization rules.
+ //
+ // Since we don't have access to the exact original string
+ // that was entered by the user, this will just have to do.
+ bool isACE;
+ nsAutoCString utf8Host;
+ nsCOMPtr<nsIIDNService> idnSrv =
+ do_GetService(NS_IDNSERVICE_CONTRACTID);
+ if (idnSrv &&
+ NS_SUCCEEDED(idnSrv->IsACE(host, &isACE)) && isACE &&
+ NS_SUCCEEDED(idnSrv->ConvertACEtoUTF8(host, utf8Host))) {
+ sURIFixup->KeywordToURI(utf8Host,
+ getter_AddRefs(newPostData),
+ getter_AddRefs(info));
+ } else {
+ sURIFixup->KeywordToURI(host,
+ getter_AddRefs(newPostData),
+ getter_AddRefs(info));
+ }
+ }
+
+ info->GetPreferredURI(getter_AddRefs(newURI));
+ if (newURI) {
+ info->GetKeywordAsSent(keywordAsSent);
+ info->GetKeywordProviderName(keywordProviderName);
+ }
+ } // end keywordsEnabled
+ }
+
+ //
+ // Now try change the address, e.g. turn http://foo into
+ // http://www.foo.com
+ //
+ if (aStatus == NS_ERROR_UNKNOWN_HOST ||
+ aStatus == NS_ERROR_NET_RESET) {
+ bool doCreateAlternate = true;
+
+ // Skip fixup for anything except a normal document load
+ // operation on the topframe.
+
+ if (mLoadType != LOAD_NORMAL || !isTopFrame) {
+ doCreateAlternate = false;
+ } else {
+ // Test if keyword lookup produced a new URI or not
+ if (newURI) {
+ bool sameURI = false;
+ url->Equals(newURI, &sameURI);
+ if (!sameURI) {
+ // Keyword lookup made a new URI so no need to try
+ // an alternate one.
+ doCreateAlternate = false;
+ }
+ }
+
+ if (doCreateAlternate) {
+ // Skip doing this if our channel was redirected, because we
+ // shouldn't be guessing things about the post-redirect URI.
+ nsLoadFlags loadFlags = 0;
+ if (NS_FAILED(aChannel->GetLoadFlags(&loadFlags)) ||
+ (loadFlags & nsIChannel::LOAD_REPLACE)) {
+ doCreateAlternate = false;
+ }
+ }
+ }
+ if (doCreateAlternate) {
+ newURI = nullptr;
+ newPostData = nullptr;
+ keywordProviderName.Truncate();
+ keywordAsSent.Truncate();
+ sURIFixup->CreateFixupURI(oldSpec,
+ nsIURIFixup::FIXUP_FLAGS_MAKE_ALTERNATE_URI,
+ getter_AddRefs(newPostData),
+ getter_AddRefs(newURI));
+ }
+ }
+
+ // Did we make a new URI that is different to the old one? If so
+ // load it.
+ //
+ if (newURI) {
+ // Make sure the new URI is different from the old one,
+ // otherwise there's little point trying to load it again.
+ bool sameURI = false;
+ url->Equals(newURI, &sameURI);
+ if (!sameURI) {
+ nsAutoCString newSpec;
+ newURI->GetSpec(newSpec);
+ NS_ConvertUTF8toUTF16 newSpecW(newSpec);
+
+ // This notification is meant for Firefox Health Report so it
+ // can increment counts from the search engine
+ MaybeNotifyKeywordSearchLoading(keywordProviderName, keywordAsSent);
+
+ return LoadURI(newSpecW.get(), // URI string
+ LOAD_FLAGS_NONE, // Load flags
+ nullptr, // Referring URI
+ newPostData, // Post data stream
+ nullptr); // Headers stream
+ }
+ }
+ }
+
+ // Well, fixup didn't work :-(
+ // It is time to throw an error dialog box, and be done with it...
+
+ // Errors to be shown only on top-level frames
+ if ((aStatus == NS_ERROR_UNKNOWN_HOST ||
+ aStatus == NS_ERROR_CONNECTION_REFUSED ||
+ aStatus == NS_ERROR_UNKNOWN_PROXY_HOST ||
+ aStatus == NS_ERROR_PROXY_CONNECTION_REFUSED) &&
+ (isTopFrame || UseErrorPages())) {
+ DisplayLoadError(aStatus, url, nullptr, aChannel);
+ } else if (aStatus == NS_ERROR_NET_TIMEOUT ||
+ aStatus == NS_ERROR_REDIRECT_LOOP ||
+ aStatus == NS_ERROR_UNKNOWN_SOCKET_TYPE ||
+ aStatus == NS_ERROR_NET_INTERRUPT ||
+ aStatus == NS_ERROR_NET_RESET ||
+ aStatus == NS_ERROR_OFFLINE ||
+ aStatus == NS_ERROR_MALWARE_URI ||
+ aStatus == NS_ERROR_PHISHING_URI ||
+ aStatus == NS_ERROR_UNWANTED_URI ||
+ aStatus == NS_ERROR_UNSAFE_CONTENT_TYPE ||
+ aStatus == NS_ERROR_REMOTE_XUL ||
+ aStatus == NS_ERROR_INTERCEPTION_FAILED ||
+ aStatus == NS_ERROR_NET_INADEQUATE_SECURITY ||
+ NS_ERROR_GET_MODULE(aStatus) == NS_ERROR_MODULE_SECURITY) {
+ // Errors to be shown for any frame
+ DisplayLoadError(aStatus, url, nullptr, aChannel);
+ } else if (aStatus == NS_ERROR_DOCUMENT_NOT_CACHED) {
+ // Non-caching channels will simply return NS_ERROR_OFFLINE.
+ // Caching channels would have to look at their flags to work
+ // out which error to return. Or we can fix up the error here.
+ if (!(mLoadType & LOAD_CMD_HISTORY)) {
+ aStatus = NS_ERROR_OFFLINE;
+ }
+ DisplayLoadError(aStatus, url, nullptr, aChannel);
+ }
+ } else if (url && NS_SUCCEEDED(aStatus)) {
+ // If we have a host
+ mozilla::net::PredictorLearnRedirect(url, aChannel, this);
+ }
+
+ return NS_OK;
+}
+
+//*****************************************************************************
+// nsDocShell: Content Viewer Management
+//*****************************************************************************
+
+nsresult
+nsDocShell::EnsureContentViewer()
+{
+ if (mContentViewer) {
+ return NS_OK;
+ }
+ if (mIsBeingDestroyed) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIURI> baseURI;
+ nsIPrincipal* principal = GetInheritedPrincipal(false);
+ nsCOMPtr<nsIDocShellTreeItem> parentItem;
+ GetSameTypeParent(getter_AddRefs(parentItem));
+ if (parentItem) {
+ if (nsCOMPtr<nsPIDOMWindowOuter> domWin = GetWindow()) {
+ nsCOMPtr<Element> parentElement = domWin->GetFrameElementInternal();
+ if (parentElement) {
+ baseURI = parentElement->GetBaseURI();
+ }
+ }
+ }
+
+ nsresult rv = CreateAboutBlankContentViewer(principal, baseURI);
+
+ NS_ENSURE_STATE(mContentViewer);
+
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIDocument> doc(GetDocument());
+ NS_ASSERTION(doc,
+ "Should have doc if CreateAboutBlankContentViewer "
+ "succeeded!");
+
+ doc->SetIsInitialDocument(true);
+ }
+
+ return rv;
+}
+
+nsresult
+nsDocShell::CreateAboutBlankContentViewer(nsIPrincipal* aPrincipal,
+ nsIURI* aBaseURI,
+ bool aTryToSaveOldPresentation)
+{
+ nsCOMPtr<nsIDocument> blankDoc;
+ nsCOMPtr<nsIContentViewer> viewer;
+ nsresult rv = NS_ERROR_FAILURE;
+
+ /* mCreatingDocument should never be true at this point. However, it's
+ a theoretical possibility. We want to know about it and make it stop,
+ and this sounds like a job for an assertion. */
+ NS_ASSERTION(!mCreatingDocument,
+ "infinite(?) loop creating document averted");
+ if (mCreatingDocument) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // mContentViewer->PermitUnload may release |this| docshell.
+ nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
+
+ AutoRestore<bool> creatingDocument(mCreatingDocument);
+ mCreatingDocument = true;
+
+ if (aPrincipal && !nsContentUtils::IsSystemPrincipal(aPrincipal) &&
+ mItemType != typeChrome) {
+ MOZ_ASSERT(ChromeUtils::IsOriginAttributesEqualIgnoringAddonId(
+ BasePrincipal::Cast(aPrincipal)->OriginAttributesRef(),
+ mOriginAttributes));
+ }
+
+ // Make sure timing is created. But first record whether we had it
+ // already, so we don't clobber the timing for an in-progress load.
+ bool hadTiming = mTiming;
+ MaybeInitTiming();
+ if (mContentViewer) {
+ // We've got a content viewer already. Make sure the user
+ // permits us to discard the current document and replace it
+ // with about:blank. And also ensure we fire the unload events
+ // in the current document.
+
+ // Unload gets fired first for
+ // document loaded from the session history.
+ mTiming->NotifyBeforeUnload();
+
+ bool okToUnload;
+ rv = mContentViewer->PermitUnload(&okToUnload);
+
+ if (NS_SUCCEEDED(rv) && !okToUnload) {
+ // The user chose not to unload the page, interrupt the load.
+ return NS_ERROR_FAILURE;
+ }
+
+ mSavingOldViewer = aTryToSaveOldPresentation &&
+ CanSavePresentation(LOAD_NORMAL, nullptr, nullptr);
+
+ if (mTiming) {
+ mTiming->NotifyUnloadAccepted(mCurrentURI);
+ }
+
+ // Make sure to blow away our mLoadingURI just in case. No loads
+ // from inside this pagehide.
+ mLoadingURI = nullptr;
+
+ // Stop any in-progress loading, so that we don't accidentally trigger any
+ // PageShow notifications from Embed() interrupting our loading below.
+ Stop();
+
+ // Notify the current document that it is about to be unloaded!!
+ //
+ // It is important to fire the unload() notification *before* any state
+ // is changed within the DocShell - otherwise, javascript will get the
+ // wrong information :-(
+ //
+ (void)FirePageHideNotification(!mSavingOldViewer);
+ }
+
+ // Now make sure we don't think we're in the middle of firing unload after
+ // this point. This will make us fire unload when the about:blank document
+ // unloads... but that's ok, more or less. Would be nice if it fired load
+ // too, of course.
+ mFiredUnloadEvent = false;
+
+ nsCOMPtr<nsIDocumentLoaderFactory> docFactory =
+ nsContentUtils::FindInternalContentViewer(NS_LITERAL_CSTRING("text/html"));
+
+ if (docFactory) {
+ nsCOMPtr<nsIPrincipal> principal;
+ if (mSandboxFlags & SANDBOXED_ORIGIN) {
+ if (aPrincipal) {
+ principal = nsNullPrincipal::CreateWithInheritedAttributes(aPrincipal);
+ } else {
+ principal = nsNullPrincipal::CreateWithInheritedAttributes(this);
+ }
+ } else {
+ principal = aPrincipal;
+ }
+ // generate (about:blank) document to load
+ docFactory->CreateBlankDocument(mLoadGroup, principal,
+ getter_AddRefs(blankDoc));
+ if (blankDoc) {
+ // Hack: set the base URI manually, since this document never
+ // got Reset() with a channel.
+ blankDoc->SetBaseURI(aBaseURI);
+
+ blankDoc->SetContainer(this);
+
+ // Copy our sandbox flags to the document. These are immutable
+ // after being set here.
+ blankDoc->SetSandboxFlags(mSandboxFlags);
+
+ // create a content viewer for us and the new document
+ docFactory->CreateInstanceForDocument(
+ NS_ISUPPORTS_CAST(nsIDocShell*, this), blankDoc, "view",
+ getter_AddRefs(viewer));
+
+ // hook 'em up
+ if (viewer) {
+ viewer->SetContainer(this);
+ rv = Embed(viewer, "", 0);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ SetCurrentURI(blankDoc->GetDocumentURI(), nullptr, true, 0);
+ rv = mIsBeingDestroyed ? NS_ERROR_NOT_AVAILABLE : NS_OK;
+ }
+ }
+ }
+
+ // The transient about:blank viewer doesn't have a session history entry.
+ SetHistoryEntry(&mOSHE, nullptr);
+
+ // Clear out our mTiming like we would in EndPageLoad, if we didn't
+ // have one before entering this function.
+ if (!hadTiming) {
+ mTiming = nullptr;
+ mBlankTiming = true;
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocShell::CreateAboutBlankContentViewer(nsIPrincipal* aPrincipal)
+{
+ return CreateAboutBlankContentViewer(aPrincipal, nullptr);
+}
+
+bool
+nsDocShell::CanSavePresentation(uint32_t aLoadType,
+ nsIRequest* aNewRequest,
+ nsIDocument* aNewDocument)
+{
+ if (!mOSHE) {
+ return false; // no entry to save into
+ }
+
+ nsCOMPtr<nsIContentViewer> viewer;
+ mOSHE->GetContentViewer(getter_AddRefs(viewer));
+ if (viewer) {
+ NS_WARNING("mOSHE already has a content viewer!");
+ return false;
+ }
+
+ // Only save presentation for "normal" loads and link loads. Anything else
+ // probably wants to refetch the page, so caching the old presentation
+ // would be incorrect.
+ if (aLoadType != LOAD_NORMAL &&
+ aLoadType != LOAD_HISTORY &&
+ aLoadType != LOAD_LINK &&
+ aLoadType != LOAD_STOP_CONTENT &&
+ aLoadType != LOAD_STOP_CONTENT_AND_REPLACE &&
+ aLoadType != LOAD_ERROR_PAGE) {
+ return false;
+ }
+
+ // If the session history entry has the saveLayoutState flag set to false,
+ // then we should not cache the presentation.
+ bool canSaveState;
+ mOSHE->GetSaveLayoutStateFlag(&canSaveState);
+ if (!canSaveState) {
+ return false;
+ }
+
+ // If the document is not done loading, don't cache it.
+ if (!mScriptGlobal || mScriptGlobal->IsLoading()) {
+ return false;
+ }
+
+ if (mScriptGlobal->WouldReuseInnerWindow(aNewDocument)) {
+ return false;
+ }
+
+ // Avoid doing the work of saving the presentation state in the case where
+ // the content viewer cache is disabled.
+ if (nsSHistory::GetMaxTotalViewers() == 0) {
+ return false;
+ }
+
+ // Don't cache the content viewer if we're in a subframe and the subframe
+ // pref is disabled.
+ bool cacheFrames =
+ Preferences::GetBool("browser.sessionhistory.cache_subframes", false);
+ if (!cacheFrames) {
+ nsCOMPtr<nsIDocShellTreeItem> root;
+ GetSameTypeParent(getter_AddRefs(root));
+ if (root && root != this) {
+ return false; // this is a subframe load
+ }
+ }
+
+ // If the document does not want its presentation cached, then don't.
+ nsCOMPtr<nsIDocument> doc = mScriptGlobal->GetExtantDoc();
+ return doc && doc->CanSavePresentation(aNewRequest);
+}
+
+void
+nsDocShell::ReattachEditorToWindow(nsISHEntry* aSHEntry)
+{
+ NS_ASSERTION(!mEditorData,
+ "Why reattach an editor when we already have one?");
+ NS_ASSERTION(aSHEntry && aSHEntry->HasDetachedEditor(),
+ "Reattaching when there's not a detached editor.");
+
+ if (mEditorData || !aSHEntry) {
+ return;
+ }
+
+ mEditorData = aSHEntry->ForgetEditorData();
+ if (mEditorData) {
+#ifdef DEBUG
+ nsresult rv =
+#endif
+ mEditorData->ReattachToWindow(this);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to reattach editing session");
+ }
+}
+
+void
+nsDocShell::DetachEditorFromWindow()
+{
+ if (!mEditorData || mEditorData->WaitingForLoad()) {
+ // If there's nothing to detach, or if the editor data is actually set
+ // up for the _new_ page that's coming in, don't detach.
+ return;
+ }
+
+ NS_ASSERTION(!mOSHE || !mOSHE->HasDetachedEditor(),
+ "Detaching editor when it's already detached.");
+
+ nsresult res = mEditorData->DetachFromWindow();
+ NS_ASSERTION(NS_SUCCEEDED(res), "Failed to detach editor");
+
+ if (NS_SUCCEEDED(res)) {
+ // Make mOSHE hold the owning ref to the editor data.
+ if (mOSHE) {
+ mOSHE->SetEditorData(mEditorData.forget());
+ } else {
+ mEditorData = nullptr;
+ }
+ }
+
+#ifdef DEBUG
+ {
+ bool isEditable;
+ GetEditable(&isEditable);
+ NS_ASSERTION(!isEditable,
+ "Window is still editable after detaching editor.");
+ }
+#endif // DEBUG
+}
+
+nsresult
+nsDocShell::CaptureState()
+{
+ if (!mOSHE || mOSHE == mLSHE) {
+ // No entry to save into, or we're replacing the existing entry.
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!mScriptGlobal) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsISupports> windowState = mScriptGlobal->SaveWindowState();
+ NS_ENSURE_TRUE(windowState, NS_ERROR_FAILURE);
+
+#ifdef DEBUG_PAGE_CACHE
+ nsCOMPtr<nsIURI> uri;
+ mOSHE->GetURI(getter_AddRefs(uri));
+ nsAutoCString spec;
+ if (uri) {
+ uri->GetSpec(spec);
+ }
+ printf("Saving presentation into session history\n");
+ printf(" SH URI: %s\n", spec.get());
+#endif
+
+ nsresult rv = mOSHE->SetWindowState(windowState);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Suspend refresh URIs and save off the timer queue
+ rv = mOSHE->SetRefreshURIList(mSavedRefreshURIList);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Capture the current content viewer bounds.
+ if (mContentViewer) {
+ nsIntRect bounds;
+ mContentViewer->GetBounds(bounds);
+ rv = mOSHE->SetViewerBounds(bounds);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // Capture the docshell hierarchy.
+ mOSHE->ClearChildShells();
+
+ uint32_t childCount = mChildList.Length();
+ for (uint32_t i = 0; i < childCount; ++i) {
+ nsCOMPtr<nsIDocShellTreeItem> childShell = do_QueryInterface(ChildAt(i));
+ NS_ASSERTION(childShell, "null child shell");
+
+ mOSHE->AddChildShell(childShell);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::RestorePresentationEvent::Run()
+{
+ if (mDocShell && NS_FAILED(mDocShell->RestoreFromHistory())) {
+ NS_WARNING("RestoreFromHistory failed");
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::BeginRestore(nsIContentViewer* aContentViewer, bool aTop)
+{
+ nsresult rv;
+ if (!aContentViewer) {
+ rv = EnsureContentViewer();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aContentViewer = mContentViewer;
+ }
+
+ // Dispatch events for restoring the presentation. We try to simulate
+ // the progress notifications loading the document would cause, so we add
+ // the document's channel to the loadgroup to initiate stateChange
+ // notifications.
+
+ nsCOMPtr<nsIDOMDocument> domDoc;
+ aContentViewer->GetDOMDocument(getter_AddRefs(domDoc));
+ nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
+ if (doc) {
+ nsIChannel* channel = doc->GetChannel();
+ if (channel) {
+ mEODForCurrentDocument = false;
+ mIsRestoringDocument = true;
+ mLoadGroup->AddRequest(channel, nullptr);
+ mIsRestoringDocument = false;
+ }
+ }
+
+ if (!aTop) {
+ // This point corresponds to us having gotten OnStartRequest or
+ // STATE_START, so do the same thing that CreateContentViewer does at
+ // this point to ensure that unload/pagehide events for this document
+ // will fire when it's unloaded again.
+ mFiredUnloadEvent = false;
+
+ // For non-top frames, there is no notion of making sure that the
+ // previous document is in the domwindow when STATE_START notifications
+ // happen. We can just call BeginRestore for all of the child shells
+ // now.
+ rv = BeginRestoreChildren();
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsDocShell::BeginRestoreChildren()
+{
+ nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+ while (iter.HasMore()) {
+ nsCOMPtr<nsIDocShell> child = do_QueryObject(iter.GetNext());
+ if (child) {
+ nsresult rv = child->BeginRestore(nullptr, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::FinishRestore()
+{
+ // First we call finishRestore() on our children. In the simulated load,
+ // all of the child frames finish loading before the main document.
+
+ nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+ while (iter.HasMore()) {
+ nsCOMPtr<nsIDocShell> child = do_QueryObject(iter.GetNext());
+ if (child) {
+ child->FinishRestore();
+ }
+ }
+
+ if (mOSHE && mOSHE->HasDetachedEditor()) {
+ ReattachEditorToWindow(mOSHE);
+ }
+
+ nsCOMPtr<nsIDocument> doc = GetDocument();
+ if (doc) {
+ // Finally, we remove the request from the loadgroup. This will
+ // cause onStateChange(STATE_STOP) to fire, which will fire the
+ // pageshow event to the chrome.
+
+ nsIChannel* channel = doc->GetChannel();
+ if (channel) {
+ mIsRestoringDocument = true;
+ mLoadGroup->RemoveRequest(channel, nullptr, NS_OK);
+ mIsRestoringDocument = false;
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetRestoringDocument(bool* aRestoring)
+{
+ *aRestoring = mIsRestoringDocument;
+ return NS_OK;
+}
+
+nsresult
+nsDocShell::RestorePresentation(nsISHEntry* aSHEntry, bool* aRestoring)
+{
+ NS_ASSERTION(mLoadType & LOAD_CMD_HISTORY,
+ "RestorePresentation should only be called for history loads");
+
+ nsCOMPtr<nsIContentViewer> viewer;
+ aSHEntry->GetContentViewer(getter_AddRefs(viewer));
+
+#ifdef DEBUG_PAGE_CACHE
+ nsCOMPtr<nsIURI> uri;
+ aSHEntry->GetURI(getter_AddRefs(uri));
+
+ nsAutoCString spec;
+ if (uri) {
+ uri->GetSpec(spec);
+ }
+#endif
+
+ *aRestoring = false;
+
+ if (!viewer) {
+#ifdef DEBUG_PAGE_CACHE
+ printf("no saved presentation for uri: %s\n", spec.get());
+#endif
+ return NS_OK;
+ }
+
+ // We need to make sure the content viewer's container is this docshell.
+ // In subframe navigation, it's possible for the docshell that the
+ // content viewer was originally loaded into to be replaced with a
+ // different one. We don't currently support restoring the presentation
+ // in that case.
+
+ nsCOMPtr<nsIDocShell> container;
+ viewer->GetContainer(getter_AddRefs(container));
+ if (!::SameCOMIdentity(container, GetAsSupports(this))) {
+#ifdef DEBUG_PAGE_CACHE
+ printf("No valid container, clearing presentation\n");
+#endif
+ aSHEntry->SetContentViewer(nullptr);
+ return NS_ERROR_FAILURE;
+ }
+
+ NS_ASSERTION(mContentViewer != viewer, "Restoring existing presentation");
+
+#ifdef DEBUG_PAGE_CACHE
+ printf("restoring presentation from session history: %s\n", spec.get());
+#endif
+
+ SetHistoryEntry(&mLSHE, aSHEntry);
+
+ // Post an event that will remove the request after we've returned
+ // to the event loop. This mimics the way it is called by nsIChannel
+ // implementations.
+
+ // Revoke any pending restore (just in case)
+ NS_ASSERTION(!mRestorePresentationEvent.IsPending(),
+ "should only have one RestorePresentationEvent");
+ mRestorePresentationEvent.Revoke();
+
+ RefPtr<RestorePresentationEvent> evt = new RestorePresentationEvent(this);
+ nsresult rv = NS_DispatchToCurrentThread(evt);
+ if (NS_SUCCEEDED(rv)) {
+ mRestorePresentationEvent = evt.get();
+ // The rest of the restore processing will happen on our event
+ // callback.
+ *aRestoring = true;
+ }
+
+ return rv;
+}
+
+namespace {
+class MOZ_STACK_CLASS PresentationEventForgetter
+{
+public:
+ explicit PresentationEventForgetter(
+ nsRevocableEventPtr<nsDocShell::RestorePresentationEvent>&
+ aRestorePresentationEvent)
+ : mRestorePresentationEvent(aRestorePresentationEvent)
+ , mEvent(aRestorePresentationEvent.get())
+ {
+ }
+
+ ~PresentationEventForgetter()
+ {
+ Forget();
+ }
+
+ void Forget()
+ {
+ if (mRestorePresentationEvent.get() == mEvent) {
+ mRestorePresentationEvent.Forget();
+ mEvent = nullptr;
+ }
+ }
+
+private:
+ nsRevocableEventPtr<nsDocShell::RestorePresentationEvent>&
+ mRestorePresentationEvent;
+ RefPtr<nsDocShell::RestorePresentationEvent> mEvent;
+};
+
+} // namespace
+
+nsresult
+nsDocShell::RestoreFromHistory()
+{
+ MOZ_ASSERT(mRestorePresentationEvent.IsPending());
+ PresentationEventForgetter forgetter(mRestorePresentationEvent);
+
+ // This section of code follows the same ordering as CreateContentViewer.
+ if (!mLSHE) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIContentViewer> viewer;
+ mLSHE->GetContentViewer(getter_AddRefs(viewer));
+ if (!viewer) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (mSavingOldViewer) {
+ // We determined that it was safe to cache the document presentation
+ // at the time we initiated the new load. We need to check whether
+ // it's still safe to do so, since there may have been DOM mutations
+ // or new requests initiated.
+ nsCOMPtr<nsIDOMDocument> domDoc;
+ viewer->GetDOMDocument(getter_AddRefs(domDoc));
+ nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
+ nsIRequest* request = nullptr;
+ if (doc) {
+ request = doc->GetChannel();
+ }
+ mSavingOldViewer = CanSavePresentation(mLoadType, request, doc);
+ }
+
+ nsCOMPtr<nsIContentViewer> oldCv(mContentViewer);
+ nsCOMPtr<nsIContentViewer> newCv(viewer);
+ int32_t minFontSize = 0;
+ float textZoom = 1.0f;
+ float pageZoom = 1.0f;
+ float overrideDPPX = 0.0f;
+
+ bool styleDisabled = false;
+ if (oldCv && newCv) {
+ oldCv->GetMinFontSize(&minFontSize);
+ oldCv->GetTextZoom(&textZoom);
+ oldCv->GetFullZoom(&pageZoom);
+ oldCv->GetOverrideDPPX(&overrideDPPX);
+ oldCv->GetAuthorStyleDisabled(&styleDisabled);
+ }
+
+ // Protect against mLSHE going away via a load triggered from
+ // pagehide or unload.
+ nsCOMPtr<nsISHEntry> origLSHE = mLSHE;
+
+ // Make sure to blow away our mLoadingURI just in case. No loads
+ // from inside this pagehide.
+ mLoadingURI = nullptr;
+
+ // Notify the old content viewer that it's being hidden.
+ FirePageHideNotification(!mSavingOldViewer);
+
+ // If mLSHE was changed as a result of the pagehide event, then
+ // something else was loaded. Don't finish restoring.
+ if (mLSHE != origLSHE) {
+ return NS_OK;
+ }
+
+ // Add the request to our load group. We do this before swapping out
+ // the content viewers so that consumers of STATE_START can access
+ // the old document. We only deal with the toplevel load at this time --
+ // to be consistent with normal document loading, subframes cannot start
+ // loading until after data arrives, which is after STATE_START completes.
+
+ RefPtr<RestorePresentationEvent> currentPresentationRestoration =
+ mRestorePresentationEvent.get();
+ Stop();
+ // Make sure we're still restoring the same presentation.
+ // If we aren't, docshell is in process doing another load already.
+ NS_ENSURE_STATE(currentPresentationRestoration ==
+ mRestorePresentationEvent.get());
+ BeginRestore(viewer, true);
+ NS_ENSURE_STATE(currentPresentationRestoration ==
+ mRestorePresentationEvent.get());
+ forgetter.Forget();
+
+ // Set mFiredUnloadEvent = false so that the unload handler for the
+ // *new* document will fire.
+ mFiredUnloadEvent = false;
+
+ mURIResultedInDocument = true;
+ nsCOMPtr<nsISHistory> rootSH;
+ GetRootSessionHistory(getter_AddRefs(rootSH));
+ if (rootSH) {
+ nsCOMPtr<nsISHistoryInternal> hist = do_QueryInterface(rootSH);
+ rootSH->GetIndex(&mPreviousTransIndex);
+ hist->UpdateIndex();
+ rootSH->GetIndex(&mLoadedTransIndex);
+#ifdef DEBUG_PAGE_CACHE
+ printf("Previous index: %d, Loaded index: %d\n\n", mPreviousTransIndex,
+ mLoadedTransIndex);
+#endif
+ }
+
+ // Rather than call Embed(), we will retrieve the viewer from the session
+ // history entry and swap it in.
+ // XXX can we refactor this so that we can just call Embed()?
+ PersistLayoutHistoryState();
+ nsresult rv;
+ if (mContentViewer) {
+ if (mSavingOldViewer && NS_FAILED(CaptureState())) {
+ if (mOSHE) {
+ mOSHE->SyncPresentationState();
+ }
+ mSavingOldViewer = false;
+ }
+ }
+
+ mSavedRefreshURIList = nullptr;
+
+ // In cases where we use a transient about:blank viewer between loads,
+ // we never show the transient viewer, so _its_ previous viewer is never
+ // unhooked from the view hierarchy. Destroy any such previous viewer now,
+ // before we grab the root view sibling, so that we don't grab a view
+ // that's about to go away.
+
+ if (mContentViewer) {
+ nsCOMPtr<nsIContentViewer> previousViewer;
+ mContentViewer->GetPreviousViewer(getter_AddRefs(previousViewer));
+ if (previousViewer) {
+ mContentViewer->SetPreviousViewer(nullptr);
+ previousViewer->Destroy();
+ }
+ }
+
+ // Save off the root view's parent and sibling so that we can insert the
+ // new content viewer's root view at the same position. Also save the
+ // bounds of the root view's widget.
+
+ nsView* rootViewSibling = nullptr;
+ nsView* rootViewParent = nullptr;
+ nsIntRect newBounds(0, 0, 0, 0);
+
+ nsCOMPtr<nsIPresShell> oldPresShell = GetPresShell();
+ if (oldPresShell) {
+ nsViewManager* vm = oldPresShell->GetViewManager();
+ if (vm) {
+ nsView* oldRootView = vm->GetRootView();
+
+ if (oldRootView) {
+ rootViewSibling = oldRootView->GetNextSibling();
+ rootViewParent = oldRootView->GetParent();
+
+ mContentViewer->GetBounds(newBounds);
+ }
+ }
+ }
+
+ nsCOMPtr<nsIContent> container;
+ nsCOMPtr<nsIDocument> sibling;
+ if (rootViewParent && rootViewParent->GetParent()) {
+ nsIFrame* frame = rootViewParent->GetParent()->GetFrame();
+ container = frame ? frame->GetContent() : nullptr;
+ }
+ if (rootViewSibling) {
+ nsIFrame* frame = rootViewSibling->GetFrame();
+ sibling =
+ frame ? frame->PresContext()->PresShell()->GetDocument() : nullptr;
+ }
+
+ // Transfer ownership to mContentViewer. By ensuring that either the
+ // docshell or the session history, but not both, have references to the
+ // content viewer, we prevent the viewer from being torn down after
+ // Destroy() is called.
+
+ if (mContentViewer) {
+ mContentViewer->Close(mSavingOldViewer ? mOSHE.get() : nullptr);
+ viewer->SetPreviousViewer(mContentViewer);
+ }
+ if (mOSHE && (!mContentViewer || !mSavingOldViewer)) {
+ // We don't plan to save a viewer in mOSHE; tell it to drop
+ // any other state it's holding.
+ mOSHE->SyncPresentationState();
+ }
+
+ // Order the mContentViewer setup just like Embed does.
+ mContentViewer = nullptr;
+
+ // Now that we're about to switch documents, forget all of our children.
+ // Note that we cached them as needed up in CaptureState above.
+ DestroyChildren();
+
+ mContentViewer.swap(viewer);
+
+ // Grab all of the related presentation from the SHEntry now.
+ // Clearing the viewer from the SHEntry will clear all of this state.
+ nsCOMPtr<nsISupports> windowState;
+ mLSHE->GetWindowState(getter_AddRefs(windowState));
+ mLSHE->SetWindowState(nullptr);
+
+ bool sticky;
+ mLSHE->GetSticky(&sticky);
+
+ nsCOMPtr<nsIDOMDocument> domDoc;
+ mContentViewer->GetDOMDocument(getter_AddRefs(domDoc));
+
+ nsCOMArray<nsIDocShellTreeItem> childShells;
+ int32_t i = 0;
+ nsCOMPtr<nsIDocShellTreeItem> child;
+ while (NS_SUCCEEDED(mLSHE->ChildShellAt(i++, getter_AddRefs(child))) &&
+ child) {
+ childShells.AppendObject(child);
+ }
+
+ // get the previous content viewer size
+ nsIntRect oldBounds(0, 0, 0, 0);
+ mLSHE->GetViewerBounds(oldBounds);
+
+ // Restore the refresh URI list. The refresh timers will be restarted
+ // when EndPageLoad() is called.
+ nsCOMPtr<nsIMutableArray> refreshURIList;
+ mLSHE->GetRefreshURIList(getter_AddRefs(refreshURIList));
+
+ // Reattach to the window object.
+ mIsRestoringDocument = true; // for MediaDocument::BecomeInteractive
+ rv = mContentViewer->Open(windowState, mLSHE);
+ mIsRestoringDocument = false;
+
+ // Hack to keep nsDocShellEditorData alive across the
+ // SetContentViewer(nullptr) call below.
+ nsAutoPtr<nsDocShellEditorData> data(mLSHE->ForgetEditorData());
+
+ // Now remove it from the cached presentation.
+ mLSHE->SetContentViewer(nullptr);
+ mEODForCurrentDocument = false;
+
+ mLSHE->SetEditorData(data.forget());
+
+#ifdef DEBUG
+ {
+ nsCOMPtr<nsIMutableArray> refreshURIs;
+ mLSHE->GetRefreshURIList(getter_AddRefs(refreshURIs));
+ nsCOMPtr<nsIDocShellTreeItem> childShell;
+ mLSHE->ChildShellAt(0, getter_AddRefs(childShell));
+ NS_ASSERTION(!refreshURIs && !childShell,
+ "SHEntry should have cleared presentation state");
+ }
+#endif
+
+ // Restore the sticky state of the viewer. The viewer has set this state
+ // on the history entry in Destroy() just before marking itself non-sticky,
+ // to avoid teardown of the presentation.
+ mContentViewer->SetSticky(sticky);
+
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // mLSHE is now our currently-loaded document.
+ SetHistoryEntry(&mOSHE, mLSHE);
+
+ // XXX special wyciwyg handling in Embed()?
+
+ // We aren't going to restore any items from the LayoutHistoryState,
+ // but we don't want them to stay around in case the page is reloaded.
+ SetLayoutHistoryState(nullptr);
+
+ // This is the end of our Embed() replacement
+
+ mSavingOldViewer = false;
+ mEODForCurrentDocument = false;
+
+ // Tell the event loop to favor plevents over user events, see comments
+ // in CreateContentViewer.
+ if (++gNumberOfDocumentsLoading == 1) {
+ FavorPerformanceHint(true);
+ }
+
+ if (oldCv && newCv) {
+ newCv->SetMinFontSize(minFontSize);
+ newCv->SetTextZoom(textZoom);
+ newCv->SetFullZoom(pageZoom);
+ newCv->SetOverrideDPPX(overrideDPPX);
+ newCv->SetAuthorStyleDisabled(styleDisabled);
+ }
+
+ nsCOMPtr<nsIDocument> document = do_QueryInterface(domDoc);
+ if (document) {
+ RefPtr<nsDocShell> parent = GetParentDocshell();
+ if (parent) {
+ nsCOMPtr<nsIDocument> d = parent->GetDocument();
+ if (d) {
+ if (d->EventHandlingSuppressed()) {
+ document->SuppressEventHandling(nsIDocument::eEvents,
+ d->EventHandlingSuppressed());
+ }
+
+ // Ick, it'd be nicer to not rewalk all of the subdocs here.
+ if (d->AnimationsPaused()) {
+ document->SuppressEventHandling(nsIDocument::eAnimationsOnly,
+ d->AnimationsPaused());
+ }
+ }
+ }
+
+ // Use the uri from the mLSHE we had when we entered this function
+ // (which need not match the document's URI if anchors are involved),
+ // since that's the history entry we're loading. Note that if we use
+ // origLSHE we don't have to worry about whether the entry in question
+ // is still mLSHE or whether it's now mOSHE.
+ nsCOMPtr<nsIURI> uri;
+ origLSHE->GetURI(getter_AddRefs(uri));
+ SetCurrentURI(uri, document->GetChannel(), true, 0);
+ }
+
+ // This is the end of our CreateContentViewer() replacement.
+ // Now we simulate a load. First, we restore the state of the javascript
+ // window object.
+ nsCOMPtr<nsPIDOMWindowOuter> privWin = GetWindow();
+ NS_ASSERTION(privWin, "could not get nsPIDOMWindow interface");
+
+ // Now, dispatch a title change event which would happen as the
+ // <head> is parsed.
+ document->NotifyPossibleTitleChange(false);
+
+ // Now we simulate appending child docshells for subframes.
+ for (i = 0; i < childShells.Count(); ++i) {
+ nsIDocShellTreeItem* childItem = childShells.ObjectAt(i);
+ nsCOMPtr<nsIDocShell> childShell = do_QueryInterface(childItem);
+
+ // Make sure to not clobber the state of the child. Since AddChild
+ // always clobbers it, save it off first.
+ bool allowPlugins;
+ childShell->GetAllowPlugins(&allowPlugins);
+
+ bool allowJavascript;
+ childShell->GetAllowJavascript(&allowJavascript);
+
+ bool allowRedirects;
+ childShell->GetAllowMetaRedirects(&allowRedirects);
+
+ bool allowSubframes;
+ childShell->GetAllowSubframes(&allowSubframes);
+
+ bool allowImages;
+ childShell->GetAllowImages(&allowImages);
+
+ bool allowMedia = childShell->GetAllowMedia();
+
+ bool allowDNSPrefetch;
+ childShell->GetAllowDNSPrefetch(&allowDNSPrefetch);
+
+ bool allowContentRetargeting = childShell->GetAllowContentRetargeting();
+ bool allowContentRetargetingOnChildren =
+ childShell->GetAllowContentRetargetingOnChildren();
+
+ uint32_t defaultLoadFlags;
+ childShell->GetDefaultLoadFlags(&defaultLoadFlags);
+
+ // this.AddChild(child) calls child.SetDocLoaderParent(this), meaning that
+ // the child inherits our state. Among other things, this means that the
+ // child inherits our mIsActive, mIsPrerendered and mPrivateBrowsingId,
+ // which is what we want.
+ AddChild(childItem);
+
+ childShell->SetAllowPlugins(allowPlugins);
+ childShell->SetAllowJavascript(allowJavascript);
+ childShell->SetAllowMetaRedirects(allowRedirects);
+ childShell->SetAllowSubframes(allowSubframes);
+ childShell->SetAllowImages(allowImages);
+ childShell->SetAllowMedia(allowMedia);
+ childShell->SetAllowDNSPrefetch(allowDNSPrefetch);
+ childShell->SetAllowContentRetargeting(allowContentRetargeting);
+ childShell->SetAllowContentRetargetingOnChildren(
+ allowContentRetargetingOnChildren);
+ childShell->SetDefaultLoadFlags(defaultLoadFlags);
+
+ rv = childShell->BeginRestore(nullptr, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // Make sure to restore the window state after adding the child shells back
+ // to the tree. This is necessary for Thaw() and Resume() to propagate
+ // properly.
+ rv = privWin->RestoreWindowState(windowState);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIPresShell> shell = GetPresShell();
+
+ // We may be displayed on a different monitor (or in a different
+ // HiDPI mode) than when we got into the history list. So we need
+ // to check if this has happened. See bug 838239.
+
+ // Because the prescontext normally handles resolution changes via
+ // a runnable (see nsPresContext::UIResolutionChanged), its device
+ // context won't be -immediately- updated as a result of calling
+ // shell->BackingScaleFactorChanged().
+
+ // But we depend on that device context when adjusting the view size
+ // via mContentViewer->SetBounds(newBounds) below. So we need to
+ // explicitly tell it to check for changed resolution here.
+ if (shell && shell->GetPresContext()->DeviceContext()->CheckDPIChange()) {
+ shell->BackingScaleFactorChanged();
+ }
+
+ nsViewManager* newVM = shell ? shell->GetViewManager() : nullptr;
+ nsView* newRootView = newVM ? newVM->GetRootView() : nullptr;
+
+ // Insert the new root view at the correct location in the view tree.
+ if (container) {
+ nsSubDocumentFrame* subDocFrame =
+ do_QueryFrame(container->GetPrimaryFrame());
+ rootViewParent = subDocFrame ? subDocFrame->EnsureInnerView() : nullptr;
+ } else {
+ rootViewParent = nullptr;
+ }
+ if (sibling &&
+ sibling->GetShell() &&
+ sibling->GetShell()->GetViewManager()) {
+ rootViewSibling = sibling->GetShell()->GetViewManager()->GetRootView();
+ } else {
+ rootViewSibling = nullptr;
+ }
+ if (rootViewParent && newRootView &&
+ newRootView->GetParent() != rootViewParent) {
+ nsViewManager* parentVM = rootViewParent->GetViewManager();
+ if (parentVM) {
+ // InsertChild(parent, child, sib, true) inserts the child after
+ // sib in content order, which is before sib in view order. BUT
+ // when sib is null it inserts at the end of the the document
+ // order, i.e., first in view order. But when oldRootSibling is
+ // null, the old root as at the end of the view list --- last in
+ // content order --- and we want to call InsertChild(parent, child,
+ // nullptr, false) in that case.
+ parentVM->InsertChild(rootViewParent, newRootView,
+ rootViewSibling,
+ rootViewSibling ? true : false);
+
+ NS_ASSERTION(newRootView->GetNextSibling() == rootViewSibling,
+ "error in InsertChild");
+ }
+ }
+
+ nsCOMPtr<nsPIDOMWindowInner> privWinInner = privWin->GetCurrentInnerWindow();
+
+ // If parent is suspended, increase suspension count.
+ // This can't be done as early as event suppression since this
+ // depends on docshell tree.
+ privWinInner->SyncStateFromParentWindow();
+
+ // Now that all of the child docshells have been put into place, we can
+ // restart the timers for the window and all of the child frames.
+ privWinInner->Resume();
+
+ // Restore the refresh URI list. The refresh timers will be restarted
+ // when EndPageLoad() is called.
+ mRefreshURIList = refreshURIList;
+
+ // Meta-refresh timers have been restarted for this shell, but not
+ // for our children. Walk the child shells and restart their timers.
+ nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+ while (iter.HasMore()) {
+ nsCOMPtr<nsIDocShell> child = do_QueryObject(iter.GetNext());
+ if (child) {
+ child->ResumeRefreshURIs();
+ }
+ }
+
+ // Make sure this presentation is the same size as the previous
+ // presentation. If this is not the same size we showed it at last time,
+ // then we need to resize the widget.
+
+ // XXXbryner This interacts poorly with Firefox's infobar. If the old
+ // presentation had the infobar visible, then we will resize the new
+ // presentation to that smaller size. However, firing the locationchanged
+ // event will hide the infobar, which will immediately resize the window
+ // back to the larger size. A future optimization might be to restore
+ // the presentation at the "wrong" size, then fire the locationchanged
+ // event and check whether the docshell's new size is the same as the
+ // cached viewer size (skipping the resize if they are equal).
+
+ if (newRootView) {
+ if (!newBounds.IsEmpty() && !newBounds.IsEqualEdges(oldBounds)) {
+#ifdef DEBUG_PAGE_CACHE
+ printf("resize widget(%d, %d, %d, %d)\n", newBounds.x,
+ newBounds.y, newBounds.width, newBounds.height);
+#endif
+ mContentViewer->SetBounds(newBounds);
+ } else {
+ nsIScrollableFrame* rootScrollFrame =
+ shell->GetRootScrollFrameAsScrollableExternal();
+ if (rootScrollFrame) {
+ rootScrollFrame->PostScrolledAreaEventForCurrentArea();
+ }
+ }
+ }
+
+ // The FinishRestore call below can kill these, null them out so we don't
+ // have invalid pointer lying around.
+ newRootView = rootViewSibling = rootViewParent = nullptr;
+ newVM = nullptr;
+
+ // Simulate the completion of the load.
+ nsDocShell::FinishRestore();
+
+ // Restart plugins, and paint the content.
+ if (shell) {
+ shell->Thaw();
+ }
+
+ return privWin->FireDelayedDOMEvents();
+}
+
+nsresult
+nsDocShell::CreateContentViewer(const nsACString& aContentType,
+ nsIRequest* aRequest,
+ nsIStreamListener** aContentHandler)
+{
+ *aContentHandler = nullptr;
+
+ // Can we check the content type of the current content viewer
+ // and reuse it without destroying it and re-creating it?
+
+ NS_ASSERTION(mLoadGroup, "Someone ignored return from Init()?");
+
+ // Instantiate the content viewer object
+ nsCOMPtr<nsIContentViewer> viewer;
+ nsresult rv = NewContentViewerObj(aContentType, aRequest, mLoadGroup,
+ aContentHandler, getter_AddRefs(viewer));
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // Notify the current document that it is about to be unloaded!!
+ //
+ // It is important to fire the unload() notification *before* any state
+ // is changed within the DocShell - otherwise, javascript will get the
+ // wrong information :-(
+ //
+
+ if (mSavingOldViewer) {
+ // We determined that it was safe to cache the document presentation
+ // at the time we initiated the new load. We need to check whether
+ // it's still safe to do so, since there may have been DOM mutations
+ // or new requests initiated.
+ nsCOMPtr<nsIDOMDocument> domDoc;
+ viewer->GetDOMDocument(getter_AddRefs(domDoc));
+ nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
+ mSavingOldViewer = CanSavePresentation(mLoadType, aRequest, doc);
+ }
+
+ NS_ASSERTION(!mLoadingURI, "Re-entering unload?");
+
+ nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(aRequest);
+ if (aOpenedChannel) {
+ aOpenedChannel->GetURI(getter_AddRefs(mLoadingURI));
+ }
+ FirePageHideNotification(!mSavingOldViewer);
+ mLoadingURI = nullptr;
+
+ // Set mFiredUnloadEvent = false so that the unload handler for the
+ // *new* document will fire.
+ mFiredUnloadEvent = false;
+
+ // we've created a new document so go ahead and call
+ // OnLoadingSite(), but don't fire OnLocationChange()
+ // notifications before we've called Embed(). See bug 284993.
+ mURIResultedInDocument = true;
+
+ if (mLoadType == LOAD_ERROR_PAGE) {
+ // We need to set the SH entry and our current URI here and not
+ // at the moment we load the page. We want the same behavior
+ // of Stop() as for a normal page load. See bug 514232 for details.
+
+ // Revert mLoadType to load type to state the page load failed,
+ // following function calls need it.
+ mLoadType = mFailedLoadType;
+
+ nsCOMPtr<nsIChannel> failedChannel = mFailedChannel;
+
+ nsIDocument* doc = viewer->GetDocument();
+ if (doc) {
+ doc->SetFailedChannel(failedChannel);
+ }
+
+ // Make sure we have a URI to set currentURI.
+ nsCOMPtr<nsIURI> failedURI;
+ if (failedChannel) {
+ NS_GetFinalChannelURI(failedChannel, getter_AddRefs(failedURI));
+ }
+
+ if (!failedURI) {
+ failedURI = mFailedURI;
+ }
+ if (!failedURI) {
+ // We need a URI object to store a session history entry, so make up a URI
+ NS_NewURI(getter_AddRefs(failedURI), "about:blank");
+ }
+
+ // When we don't have failedURI, something wrong will happen. See
+ // bug 291876.
+ MOZ_ASSERT(failedURI, "We don't have a URI for history APIs.");
+
+ mFailedChannel = nullptr;
+ mFailedURI = nullptr;
+
+ // Create an shistory entry for the old load.
+ if (failedURI) {
+ bool errorOnLocationChangeNeeded = OnNewURI(
+ failedURI, failedChannel, nullptr, nullptr, mLoadType, false, false, false);
+
+ if (errorOnLocationChangeNeeded) {
+ FireOnLocationChange(this, failedChannel, failedURI,
+ LOCATION_CHANGE_ERROR_PAGE);
+ }
+ }
+
+ // Be sure to have a correct mLSHE, it may have been cleared by
+ // EndPageLoad. See bug 302115.
+ if (mSessionHistory && !mLSHE) {
+ int32_t idx;
+ mSessionHistory->GetRequestedIndex(&idx);
+ if (idx == -1) {
+ mSessionHistory->GetIndex(&idx);
+ }
+ mSessionHistory->GetEntryAtIndex(idx, false, getter_AddRefs(mLSHE));
+ }
+
+ mLoadType = LOAD_ERROR_PAGE;
+ }
+
+ bool onLocationChangeNeeded = OnLoadingSite(aOpenedChannel, false);
+
+ // let's try resetting the load group if we need to...
+ nsCOMPtr<nsILoadGroup> currentLoadGroup;
+ NS_ENSURE_SUCCESS(
+ aOpenedChannel->GetLoadGroup(getter_AddRefs(currentLoadGroup)),
+ NS_ERROR_FAILURE);
+
+ if (currentLoadGroup != mLoadGroup) {
+ nsLoadFlags loadFlags = 0;
+
+ // Cancel any URIs that are currently loading...
+ // XXX: Need to do this eventually Stop();
+ //
+ // Retarget the document to this loadgroup...
+ //
+ /* First attach the channel to the right loadgroup
+ * and then remove from the old loadgroup. This
+ * puts the notifications in the right order and
+ * we don't null-out mLSHE in OnStateChange() for
+ * all redirected urls
+ */
+ aOpenedChannel->SetLoadGroup(mLoadGroup);
+
+ // Mark the channel as being a document URI...
+ aOpenedChannel->GetLoadFlags(&loadFlags);
+ loadFlags |= nsIChannel::LOAD_DOCUMENT_URI;
+
+ aOpenedChannel->SetLoadFlags(loadFlags);
+
+ mLoadGroup->AddRequest(aRequest, nullptr);
+ if (currentLoadGroup) {
+ currentLoadGroup->RemoveRequest(aRequest, nullptr, NS_BINDING_RETARGETED);
+ }
+
+ // Update the notification callbacks, so that progress and
+ // status information are sent to the right docshell...
+ aOpenedChannel->SetNotificationCallbacks(this);
+ }
+
+ NS_ENSURE_SUCCESS(Embed(viewer, "", nullptr), NS_ERROR_FAILURE);
+
+ mSavedRefreshURIList = nullptr;
+ mSavingOldViewer = false;
+ mEODForCurrentDocument = false;
+
+ // if this document is part of a multipart document,
+ // the ID can be used to distinguish it from the other parts.
+ nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(aRequest));
+ if (multiPartChannel) {
+ nsCOMPtr<nsIPresShell> shell = GetPresShell();
+ if (NS_SUCCEEDED(rv) && shell) {
+ nsIDocument* doc = shell->GetDocument();
+ if (doc) {
+ uint32_t partID;
+ multiPartChannel->GetPartID(&partID);
+ doc->SetPartID(partID);
+ }
+ }
+ }
+
+ // Give hint to native plevent dispatch mechanism. If a document
+ // is loading the native plevent dispatch mechanism should favor
+ // performance over normal native event dispatch priorities.
+ if (++gNumberOfDocumentsLoading == 1) {
+ // Hint to favor performance for the plevent notification mechanism.
+ // We want the pages to load as fast as possible even if its means
+ // native messages might be starved.
+ FavorPerformanceHint(true);
+ }
+
+ if (onLocationChangeNeeded) {
+ FireOnLocationChange(this, aRequest, mCurrentURI, 0);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsDocShell::NewContentViewerObj(const nsACString& aContentType,
+ nsIRequest* aRequest, nsILoadGroup* aLoadGroup,
+ nsIStreamListener** aContentHandler,
+ nsIContentViewer** aViewer)
+{
+ nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(aRequest);
+
+ nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
+ nsContentUtils::FindInternalContentViewer(aContentType);
+ if (!docLoaderFactory) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Now create an instance of the content viewer nsLayoutDLF makes the
+ // determination if it should be a "view-source" instead of "view"
+ nsresult rv = docLoaderFactory->CreateInstance("view",
+ aOpenedChannel,
+ aLoadGroup, aContentType,
+ this,
+ nullptr,
+ aContentHandler,
+ aViewer);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ (*aViewer)->SetContainer(this);
+ return NS_OK;
+}
+
+nsresult
+nsDocShell::SetupNewViewer(nsIContentViewer* aNewViewer)
+{
+ //
+ // Copy content viewer state from previous or parent content viewer.
+ //
+ // The following logic is mirrored in nsHTMLDocument::StartDocumentLoad!
+ //
+ // Do NOT to maintain a reference to the old content viewer outside
+ // of this "copying" block, or it will not be destroyed until the end of
+ // this routine and all <SCRIPT>s and event handlers fail! (bug 20315)
+ //
+ // In this block of code, if we get an error result, we return it
+ // but if we get a null pointer, that's perfectly legal for parent
+ // and parentContentViewer.
+ //
+
+ int32_t x = 0;
+ int32_t y = 0;
+ int32_t cx = 0;
+ int32_t cy = 0;
+
+ // This will get the size from the current content viewer or from the
+ // Init settings
+ DoGetPositionAndSize(&x, &y, &cx, &cy);
+
+ nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
+ NS_ENSURE_SUCCESS(GetSameTypeParent(getter_AddRefs(parentAsItem)),
+ NS_ERROR_FAILURE);
+ nsCOMPtr<nsIDocShell> parent(do_QueryInterface(parentAsItem));
+
+ nsAutoCString forceCharset;
+ nsAutoCString hintCharset;
+ int32_t hintCharsetSource;
+ int32_t minFontSize;
+ float textZoom;
+ float pageZoom;
+ float overrideDPPX;
+ bool styleDisabled;
+ // |newMUDV| also serves as a flag to set the data from the above vars
+ nsCOMPtr<nsIContentViewer> newCv;
+
+ if (mContentViewer || parent) {
+ nsCOMPtr<nsIContentViewer> oldCv;
+ if (mContentViewer) {
+ // Get any interesting state from old content viewer
+ // XXX: it would be far better to just reuse the document viewer ,
+ // since we know we're just displaying the same document as before
+ oldCv = mContentViewer;
+
+ // Tell the old content viewer to hibernate in session history when
+ // it is destroyed.
+
+ if (mSavingOldViewer && NS_FAILED(CaptureState())) {
+ if (mOSHE) {
+ mOSHE->SyncPresentationState();
+ }
+ mSavingOldViewer = false;
+ }
+ } else {
+ // No old content viewer, so get state from parent's content viewer
+ parent->GetContentViewer(getter_AddRefs(oldCv));
+ }
+
+ if (oldCv) {
+ newCv = aNewViewer;
+ if (newCv) {
+ NS_ENSURE_SUCCESS(oldCv->GetForceCharacterSet(forceCharset),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(oldCv->GetHintCharacterSet(hintCharset),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(oldCv->GetHintCharacterSetSource(&hintCharsetSource),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(oldCv->GetMinFontSize(&minFontSize),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(oldCv->GetTextZoom(&textZoom),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(oldCv->GetFullZoom(&pageZoom),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(oldCv->GetOverrideDPPX(&overrideDPPX),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(oldCv->GetAuthorStyleDisabled(&styleDisabled),
+ NS_ERROR_FAILURE);
+ }
+ }
+ }
+
+ nscolor bgcolor = NS_RGBA(0, 0, 0, 0);
+ // Ensure that the content viewer is destroyed *after* the GC - bug 71515
+ nsCOMPtr<nsIContentViewer> contentViewer = mContentViewer;
+ if (contentViewer) {
+ // Stop any activity that may be happening in the old document before
+ // releasing it...
+ contentViewer->Stop();
+
+ // Try to extract the canvas background color from the old
+ // presentation shell, so we can use it for the next document.
+ nsCOMPtr<nsIPresShell> shell;
+ contentViewer->GetPresShell(getter_AddRefs(shell));
+
+ if (shell) {
+ bgcolor = shell->GetCanvasBackground();
+ }
+
+ contentViewer->Close(mSavingOldViewer ? mOSHE.get() : nullptr);
+ aNewViewer->SetPreviousViewer(contentViewer);
+ }
+ if (mOSHE && (!mContentViewer || !mSavingOldViewer)) {
+ // We don't plan to save a viewer in mOSHE; tell it to drop
+ // any other state it's holding.
+ mOSHE->SyncPresentationState();
+ }
+
+ mContentViewer = nullptr;
+
+ // Now that we're about to switch documents, forget all of our children.
+ // Note that we cached them as needed up in CaptureState above.
+ DestroyChildren();
+
+ mContentViewer = aNewViewer;
+
+ nsCOMPtr<nsIWidget> widget;
+ NS_ENSURE_SUCCESS(GetMainWidget(getter_AddRefs(widget)), NS_ERROR_FAILURE);
+
+ nsIntRect bounds(x, y, cx, cy);
+
+ mContentViewer->SetNavigationTiming(mTiming);
+
+ if (NS_FAILED(mContentViewer->Init(widget, bounds))) {
+ mContentViewer = nullptr;
+ NS_WARNING("ContentViewer Initialization failed");
+ return NS_ERROR_FAILURE;
+ }
+
+ // If we have old state to copy, set the old state onto the new content
+ // viewer
+ if (newCv) {
+ NS_ENSURE_SUCCESS(newCv->SetForceCharacterSet(forceCharset),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(newCv->SetHintCharacterSet(hintCharset),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(newCv->SetHintCharacterSetSource(hintCharsetSource),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(newCv->SetMinFontSize(minFontSize),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(newCv->SetTextZoom(textZoom),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(newCv->SetFullZoom(pageZoom),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(newCv->SetOverrideDPPX(overrideDPPX),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(newCv->SetAuthorStyleDisabled(styleDisabled),
+ NS_ERROR_FAILURE);
+ }
+
+ // Stuff the bgcolor from the old pres shell into the new
+ // pres shell. This improves page load continuity.
+ nsCOMPtr<nsIPresShell> shell;
+ mContentViewer->GetPresShell(getter_AddRefs(shell));
+
+ if (shell) {
+ shell->SetCanvasBackground(bgcolor);
+ }
+
+ // XXX: It looks like the LayoutState gets restored again in Embed()
+ // right after the call to SetupNewViewer(...)
+
+ // We don't show the mContentViewer yet, since we want to draw the old page
+ // until we have enough of the new page to show. Just return with the new
+ // viewer still set to hidden.
+
+ return NS_OK;
+}
+
+nsresult
+nsDocShell::SetDocCurrentStateObj(nsISHEntry* aShEntry)
+{
+ NS_ENSURE_STATE(mContentViewer);
+ nsCOMPtr<nsIDocument> document = GetDocument();
+ NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
+
+ nsCOMPtr<nsIStructuredCloneContainer> scContainer;
+ if (aShEntry) {
+ nsresult rv = aShEntry->GetStateData(getter_AddRefs(scContainer));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // If aShEntry is null, just set the document's state object to null.
+ }
+
+ // It's OK for scContainer too be null here; that just means there's no
+ // state data associated with this history entry.
+ document->SetStateObject(scContainer);
+
+ return NS_OK;
+}
+
+nsresult
+nsDocShell::CheckLoadingPermissions()
+{
+ // This method checks whether the caller may load content into
+ // this docshell. Even though we've done our best to hide windows
+ // from code that doesn't have the right to access them, it's
+ // still possible for an evil site to open a window and access
+ // frames in the new window through window.frames[] (which is
+ // allAccess for historic reasons), so we still need to do this
+ // check on load.
+ nsresult rv = NS_OK;
+
+ if (!gValidateOrigin || !IsFrame()) {
+ // Origin validation was turned off, or we're not a frame.
+ // Permit all loads.
+
+ return rv;
+ }
+
+ // Note - The check for a current JSContext here isn't necessarily sensical.
+ // It's just designed to preserve the old semantics during a mass-conversion
+ // patch.
+ if (!nsContentUtils::GetCurrentJSContext()) {
+ return NS_OK;
+ }
+
+ // Check if the caller is from the same origin as this docshell,
+ // or any of its ancestors.
+ nsCOMPtr<nsIDocShellTreeItem> item(this);
+ do {
+ nsCOMPtr<nsIScriptGlobalObject> sgo = do_GetInterface(item);
+ nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(sgo));
+
+ nsIPrincipal* p;
+ if (!sop || !(p = sop->GetPrincipal())) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ if (nsContentUtils::SubjectPrincipal()->Subsumes(p)) {
+ // Same origin, permit load
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIDocShellTreeItem> tmp;
+ item->GetSameTypeParent(getter_AddRefs(tmp));
+ item.swap(tmp);
+ } while (item);
+
+ return NS_ERROR_DOM_PROP_ACCESS_DENIED;
+}
+
+//*****************************************************************************
+// nsDocShell: Site Loading
+//*****************************************************************************
+
+namespace {
+
+#ifdef MOZ_PLACES
+// Callback used by CopyFavicon to inform the favicon service that one URI
+// (mNewURI) has the same favicon URI (OnComplete's aFaviconURI) as another.
+class nsCopyFaviconCallback final : public nsIFaviconDataCallback
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ nsCopyFaviconCallback(mozIAsyncFavicons* aSvc,
+ nsIURI* aNewURI,
+ nsIPrincipal* aLoadingPrincipal,
+ bool aInPrivateBrowsing)
+ : mSvc(aSvc)
+ , mNewURI(aNewURI)
+ , mLoadingPrincipal(aLoadingPrincipal)
+ , mInPrivateBrowsing(aInPrivateBrowsing)
+ {
+ }
+
+ NS_IMETHOD
+ OnComplete(nsIURI* aFaviconURI, uint32_t aDataLen,
+ const uint8_t* aData, const nsACString& aMimeType) override
+ {
+ // Continue only if there is an associated favicon.
+ if (!aFaviconURI) {
+ return NS_OK;
+ }
+
+ MOZ_ASSERT(aDataLen == 0,
+ "We weren't expecting the callback to deliver data.");
+
+ nsCOMPtr<mozIPlacesPendingOperation> po;
+ return mSvc->SetAndFetchFaviconForPage(
+ mNewURI, aFaviconURI, false,
+ mInPrivateBrowsing ? nsIFaviconService::FAVICON_LOAD_PRIVATE :
+ nsIFaviconService::FAVICON_LOAD_NON_PRIVATE,
+ nullptr, mLoadingPrincipal, getter_AddRefs(po));
+ }
+
+private:
+ ~nsCopyFaviconCallback() {}
+
+ nsCOMPtr<mozIAsyncFavicons> mSvc;
+ nsCOMPtr<nsIURI> mNewURI;
+ nsCOMPtr<nsIPrincipal> mLoadingPrincipal;
+ bool mInPrivateBrowsing;
+};
+
+NS_IMPL_ISUPPORTS(nsCopyFaviconCallback, nsIFaviconDataCallback)
+#endif
+
+} // namespace
+
+void
+nsDocShell::CopyFavicon(nsIURI* aOldURI,
+ nsIURI* aNewURI,
+ nsIPrincipal* aLoadingPrincipal,
+ bool aInPrivateBrowsing)
+{
+ if (XRE_IsContentProcess()) {
+ dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
+ if (contentChild) {
+ mozilla::ipc::URIParams oldURI, newURI;
+ SerializeURI(aOldURI, oldURI);
+ SerializeURI(aNewURI, newURI);
+ contentChild->SendCopyFavicon(oldURI, newURI,
+ IPC::Principal(aLoadingPrincipal),
+ aInPrivateBrowsing);
+ }
+ return;
+ }
+
+#ifdef MOZ_PLACES
+ nsCOMPtr<mozIAsyncFavicons> favSvc =
+ do_GetService("@mozilla.org/browser/favicon-service;1");
+ if (favSvc) {
+ nsCOMPtr<nsIFaviconDataCallback> callback =
+ new nsCopyFaviconCallback(favSvc, aNewURI,
+ aLoadingPrincipal,
+ aInPrivateBrowsing);
+ favSvc->GetFaviconURLForPage(aOldURI, callback);
+ }
+#endif
+}
+
+class InternalLoadEvent : public Runnable
+{
+public:
+ InternalLoadEvent(nsDocShell* aDocShell, nsIURI* aURI,
+ nsIURI* aOriginalURI, bool aLoadReplace,
+ nsIURI* aReferrer, uint32_t aReferrerPolicy,
+ nsIPrincipal* aTriggeringPrincipal,
+ nsIPrincipal* aPrincipalToInherit, uint32_t aFlags,
+ const char* aTypeHint, nsIInputStream* aPostData,
+ nsIInputStream* aHeadersData, uint32_t aLoadType,
+ nsISHEntry* aSHEntry, bool aFirstParty,
+ const nsAString& aSrcdoc, nsIDocShell* aSourceDocShell,
+ nsIURI* aBaseURI)
+ : mSrcdoc(aSrcdoc)
+ , mDocShell(aDocShell)
+ , mURI(aURI)
+ , mOriginalURI(aOriginalURI)
+ , mLoadReplace(aLoadReplace)
+ , mReferrer(aReferrer)
+ , mReferrerPolicy(aReferrerPolicy)
+ , mTriggeringPrincipal(aTriggeringPrincipal)
+ , mPrincipalToInherit(aPrincipalToInherit)
+ , mPostData(aPostData)
+ , mHeadersData(aHeadersData)
+ , mSHEntry(aSHEntry)
+ , mFlags(aFlags)
+ , mLoadType(aLoadType)
+ , mFirstParty(aFirstParty)
+ , mSourceDocShell(aSourceDocShell)
+ , mBaseURI(aBaseURI)
+ {
+ // Make sure to keep null things null as needed
+ if (aTypeHint) {
+ mTypeHint = aTypeHint;
+ }
+ }
+
+ NS_IMETHOD
+ Run() override
+ {
+ return mDocShell->InternalLoad(mURI, mOriginalURI,
+ mLoadReplace,
+ mReferrer,
+ mReferrerPolicy,
+ mTriggeringPrincipal, mPrincipalToInherit,
+ mFlags, EmptyString(), mTypeHint.get(),
+ NullString(), mPostData, mHeadersData,
+ mLoadType, mSHEntry, mFirstParty,
+ mSrcdoc, mSourceDocShell, mBaseURI,
+ nullptr, nullptr);
+ }
+
+private:
+ // Use IDL strings so .get() returns null by default
+ nsXPIDLString mWindowTarget;
+ nsXPIDLCString mTypeHint;
+ nsString mSrcdoc;
+
+ RefPtr<nsDocShell> mDocShell;
+ nsCOMPtr<nsIURI> mURI;
+ nsCOMPtr<nsIURI> mOriginalURI;
+ bool mLoadReplace;
+ nsCOMPtr<nsIURI> mReferrer;
+ uint32_t mReferrerPolicy;
+ nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
+ nsCOMPtr<nsIPrincipal> mPrincipalToInherit;
+ nsCOMPtr<nsIInputStream> mPostData;
+ nsCOMPtr<nsIInputStream> mHeadersData;
+ nsCOMPtr<nsISHEntry> mSHEntry;
+ uint32_t mFlags;
+ uint32_t mLoadType;
+ bool mFirstParty;
+ nsCOMPtr<nsIDocShell> mSourceDocShell;
+ nsCOMPtr<nsIURI> mBaseURI;
+};
+
+/**
+ * Returns true if we started an asynchronous load (i.e., from the network), but
+ * the document we're loading there hasn't yet become this docshell's active
+ * document.
+ *
+ * When JustStartedNetworkLoad is true, you should be careful about modifying
+ * mLoadType and mLSHE. These are both set when the asynchronous load first
+ * starts, and the load expects that, when it eventually runs InternalLoad,
+ * mLoadType and mLSHE will have their original values.
+ */
+bool
+nsDocShell::JustStartedNetworkLoad()
+{
+ return mDocumentRequest && mDocumentRequest != GetCurrentDocChannel();
+}
+
+nsresult
+nsDocShell::CreatePrincipalFromReferrer(nsIURI* aReferrer,
+ nsIPrincipal** aResult)
+{
+ PrincipalOriginAttributes attrs;
+ attrs.InheritFromDocShellToDoc(mOriginAttributes, aReferrer);
+ nsCOMPtr<nsIPrincipal> prin =
+ BasePrincipal::CreateCodebasePrincipal(aReferrer, attrs);
+ prin.forget(aResult);
+
+ return *aResult ? NS_OK : NS_ERROR_FAILURE;
+}
+
+bool
+nsDocShell::IsAboutNewtab(nsIURI* aURI)
+{
+ if (!aURI) {
+ return false;
+ }
+ bool isAbout;
+ if (NS_WARN_IF(NS_FAILED(aURI->SchemeIs("about", &isAbout)))) {
+ return false;
+ }
+ if (!isAbout) {
+ return false;
+ }
+
+ nsAutoCString module;
+ if (NS_WARN_IF(NS_FAILED(NS_GetAboutModuleName(aURI, module)))) {
+ return false;
+ }
+ return module.Equals("newtab");
+}
+
+NS_IMETHODIMP
+nsDocShell::InternalLoad(nsIURI* aURI,
+ nsIURI* aOriginalURI,
+ bool aLoadReplace,
+ nsIURI* aReferrer,
+ uint32_t aReferrerPolicy,
+ nsIPrincipal* aTriggeringPrincipal,
+ nsIPrincipal* aPrincipalToInherit,
+ uint32_t aFlags,
+ const nsAString& aWindowTarget,
+ const char* aTypeHint,
+ const nsAString& aFileName,
+ nsIInputStream* aPostData,
+ nsIInputStream* aHeadersData,
+ uint32_t aLoadType,
+ nsISHEntry* aSHEntry,
+ bool aFirstParty,
+ const nsAString& aSrcdoc,
+ nsIDocShell* aSourceDocShell,
+ nsIURI* aBaseURI,
+ nsIDocShell** aDocShell,
+ nsIRequest** aRequest)
+{
+ MOZ_ASSERT(aTriggeringPrincipal, "need a valid TriggeringPrincipal");
+
+ nsresult rv = NS_OK;
+ mOriginalUriString.Truncate();
+
+ if (gDocShellLeakLog && MOZ_LOG_TEST(gDocShellLeakLog, LogLevel::Debug)) {
+ PR_LogPrint("DOCSHELL %p InternalLoad %s\n",
+ this, aURI ? aURI->GetSpecOrDefault().get() : "");
+ }
+ // Initialize aDocShell/aRequest
+ if (aDocShell) {
+ *aDocShell = nullptr;
+ }
+ if (aRequest) {
+ *aRequest = nullptr;
+ }
+
+ if (!aURI) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ NS_ENSURE_TRUE(IsValidLoadType(aLoadType), NS_ERROR_INVALID_ARG);
+
+ NS_ENSURE_TRUE(!mIsBeingDestroyed, NS_ERROR_NOT_AVAILABLE);
+
+ rv = EnsureScriptEnvironment();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // wyciwyg urls can only be loaded through history. Any normal load of
+ // wyciwyg through docshell is illegal. Disallow such loads.
+ if (aLoadType & LOAD_CMD_NORMAL) {
+ bool isWyciwyg = false;
+ rv = aURI->SchemeIs("wyciwyg", &isWyciwyg);
+ if ((isWyciwyg && NS_SUCCEEDED(rv)) || NS_FAILED(rv)) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ bool isJavaScript = false;
+ if (NS_FAILED(aURI->SchemeIs("javascript", &isJavaScript))) {
+ isJavaScript = false;
+ }
+
+ bool isTargetTopLevelDocShell = false;
+ nsCOMPtr<nsIDocShell> targetDocShell;
+ if (!aWindowTarget.IsEmpty()) {
+ // Locate the target DocShell.
+ nsCOMPtr<nsIDocShellTreeItem> targetItem;
+ // Only _self, _parent, and _top are supported in noopener case. But we
+ // have to be careful to not apply that to the noreferrer case. See bug
+ // 1358469.
+ bool allowNamedTarget = !(aFlags & INTERNAL_LOAD_FLAGS_NO_OPENER) ||
+ (aFlags & INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER);
+ if (allowNamedTarget ||
+ aWindowTarget.LowerCaseEqualsLiteral("_self") ||
+ aWindowTarget.LowerCaseEqualsLiteral("_parent") ||
+ aWindowTarget.LowerCaseEqualsLiteral("_top")) {
+ rv = FindItemWithName(aWindowTarget, nullptr, this,
+ getter_AddRefs(targetItem));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ targetDocShell = do_QueryInterface(targetItem);
+ if (targetDocShell) {
+ // If the targetDocShell and the rootDocShell are the same, then the
+ // targetDocShell is the top level document and hence we should
+ // consider this TYPE_DOCUMENT
+ //
+ // For example:
+ // 1. target="_top"
+ // 2. target="_parent", where this docshell is in the 2nd level of
+ // docshell tree.
+ nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
+ targetDocShell->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
+ NS_ASSERTION(sameTypeRoot,
+ "No document shell root tree item from targetDocShell!");
+ nsCOMPtr<nsIDocShell> rootShell = do_QueryInterface(sameTypeRoot);
+ NS_ASSERTION(rootShell,
+ "No root docshell from document shell root tree item.");
+ isTargetTopLevelDocShell = targetDocShell == rootShell;
+ } else {
+ // If the targetDocShell doesn't exist, then this is a new docShell
+ // and we should consider this a TYPE_DOCUMENT load
+ //
+ // For example, when target="_blank"
+ isTargetTopLevelDocShell = true;
+ }
+ }
+
+ // The contentType will be INTERNAL_(I)FRAME if:
+ // 1. This docshell is for iframe.
+ // 2. AND aWindowTarget is not a new window, nor a top-level window.
+ //
+ // This variable will be used when we call NS_CheckContentLoadPolicy, and
+ // later when we call DoURILoad.
+ uint32_t contentType;
+ if (IsFrame() && !isTargetTopLevelDocShell) {
+ nsCOMPtr<Element> requestingElement =
+ mScriptGlobal->AsOuter()->GetFrameElementInternal();
+ if (requestingElement) {
+ contentType = requestingElement->IsHTMLElement(nsGkAtoms::iframe) ?
+ nsIContentPolicy::TYPE_INTERNAL_IFRAME : nsIContentPolicy::TYPE_INTERNAL_FRAME;
+ } else {
+ // If we have lost our frame element by now, just assume we're
+ // an iframe since that's more common.
+ contentType = nsIContentPolicy::TYPE_INTERNAL_IFRAME;
+ }
+ } else {
+ contentType = nsIContentPolicy::TYPE_DOCUMENT;
+ }
+
+ // If there's no targetDocShell, that means we are about to create a new window,
+ // perform a content policy check before creating the window.
+ if (!targetDocShell) {
+ nsCOMPtr<Element> requestingElement;
+ nsISupports* requestingContext = nullptr;
+
+ if (contentType == nsIContentPolicy::TYPE_DOCUMENT) {
+ if (XRE_IsContentProcess()) {
+ // In e10s the child process doesn't have access to the element that
+ // contains the browsing context (because that element is in the chrome
+ // process). So we just pass mScriptGlobal.
+ requestingContext = ToSupports(mScriptGlobal);
+ } else {
+ // This is for loading non-e10s tabs and toplevel windows of various
+ // sorts.
+ // For the toplevel window cases, requestingElement will be null.
+ requestingElement = mScriptGlobal->AsOuter()->GetFrameElementInternal();
+ requestingContext = requestingElement;
+ }
+ } else {
+ requestingElement = mScriptGlobal->AsOuter()->GetFrameElementInternal();
+ requestingContext = requestingElement;
+
+#ifdef DEBUG
+ if (requestingElement) {
+ // Get the docshell type for requestingElement.
+ nsCOMPtr<nsIDocument> requestingDoc = requestingElement->OwnerDoc();
+ nsCOMPtr<nsIDocShell> elementDocShell = requestingDoc->GetDocShell();
+
+ // requestingElement docshell type = current docshell type.
+ MOZ_ASSERT(mItemType == elementDocShell->ItemType(),
+ "subframes should have the same docshell type as their parent");
+ }
+#endif
+ }
+
+ int16_t shouldLoad = nsIContentPolicy::ACCEPT;
+ rv = NS_CheckContentLoadPolicy(contentType,
+ aURI,
+ aTriggeringPrincipal,
+ requestingContext,
+ EmptyCString(), // mime guess
+ nullptr, // extra
+ &shouldLoad);
+
+ if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
+ if (NS_SUCCEEDED(rv) && shouldLoad == nsIContentPolicy::REJECT_TYPE) {
+ return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
+ }
+
+ return NS_ERROR_CONTENT_BLOCKED;
+ }
+
+ // If HSTS priming was set by nsMixedContentBlocker::ShouldLoad, and we
+ // would block due to mixed content, go ahead and block here. If we try to
+ // proceed with priming, we will error out later on.
+ nsCOMPtr<nsIDocShell> docShell = NS_CP_GetDocShellFromContext(requestingContext);
+ // When loading toplevel windows, requestingContext can be null. We don't
+ // really care about HSTS in that situation, though; loads in toplevel
+ // windows should all be browser UI.
+ if (docShell) {
+ nsIDocument* document = docShell->GetDocument();
+ NS_ENSURE_TRUE(document, NS_OK);
+
+ HSTSPrimingState state = document->GetHSTSPrimingStateForLocation(aURI);
+ if (state == HSTSPrimingState::eHSTS_PRIMING_BLOCK) {
+ // HSTS Priming currently disabled for InternalLoad, so we need to clear
+ // the location that was added by nsMixedContentBlocker::ShouldLoad
+ // Bug 1269815 will address images loaded via InternalLoad
+ document->ClearHSTSPrimingLocation(aURI);
+ return NS_ERROR_CONTENT_BLOCKED;
+ }
+ }
+ }
+
+ nsCOMPtr<nsIPrincipal> principalToInherit = aPrincipalToInherit;
+ //
+ // Get a principal from the current document if necessary. Note that we only
+ // do this for URIs that inherit a security context and local file URIs;
+ // in particular we do NOT do this for about:blank. This way, random
+ // about:blank loads that have no principal (which basically means they were
+ // done by someone from chrome manually messing with our nsIWebNavigation
+ // or by C++ setting document.location) don't get a funky principal. If
+ // callers want something interesting to happen with the about:blank
+ // principal in this case, they should pass aPrincipalToInherit in.
+ //
+ {
+ bool inherits;
+ // One more twist: Don't inherit the principal for external loads.
+ if (aLoadType != LOAD_NORMAL_EXTERNAL && !principalToInherit &&
+ (aFlags & INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL) &&
+ NS_SUCCEEDED(nsContentUtils::URIInheritsSecurityContext(aURI,
+ &inherits)) &&
+ inherits) {
+ principalToInherit = GetInheritedPrincipal(true);
+ }
+ }
+
+ // Don't allow loads that would inherit our security context
+ // if this document came from an unsafe channel.
+ {
+ bool willInherit;
+ // This condition needs to match the one in
+ // nsContentUtils::ChannelShouldInheritPrincipal.
+ // Except we reverse the rv check to be safe in case
+ // nsContentUtils::URIInheritsSecurityContext fails here and
+ // succeeds there.
+ rv = nsContentUtils::URIInheritsSecurityContext(aURI, &willInherit);
+ if (NS_FAILED(rv) || willInherit || NS_IsAboutBlank(aURI)) {
+ nsCOMPtr<nsIDocShellTreeItem> treeItem = this;
+ do {
+ nsCOMPtr<nsIDocShell> itemDocShell = do_QueryInterface(treeItem);
+ bool isUnsafe;
+ if (itemDocShell &&
+ NS_SUCCEEDED(itemDocShell->GetChannelIsUnsafe(&isUnsafe)) &&
+ isUnsafe) {
+ return NS_ERROR_DOM_SECURITY_ERR;
+ }
+
+ nsCOMPtr<nsIDocShellTreeItem> parent;
+ treeItem->GetSameTypeParent(getter_AddRefs(parent));
+ parent.swap(treeItem);
+ } while (treeItem);
+ }
+ }
+
+ //
+ // Resolve the window target before going any further...
+ // If the load has been targeted to another DocShell, then transfer the
+ // load to it...
+ //
+ if (!aWindowTarget.IsEmpty()) {
+ // We've already done our owner-inheriting. Mask out that bit, so we
+ // don't try inheriting an owner from the target window if we came up
+ // with a null owner above.
+ aFlags = aFlags & ~INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL;
+
+ bool isNewWindow = false;
+ if (!targetDocShell) {
+ // If the docshell's document is sandboxed, only open a new window
+ // if the document's SANDBOXED_AUXILLARY_NAVIGATION flag is not set.
+ // (i.e. if allow-popups is specified)
+ NS_ENSURE_TRUE(mContentViewer, NS_ERROR_FAILURE);
+ nsIDocument* doc = mContentViewer->GetDocument();
+ uint32_t sandboxFlags = 0;
+
+ if (doc) {
+ sandboxFlags = doc->GetSandboxFlags();
+ if (sandboxFlags & SANDBOXED_AUXILIARY_NAVIGATION) {
+ return NS_ERROR_DOM_INVALID_ACCESS_ERR;
+ }
+ }
+
+ nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
+ NS_ENSURE_TRUE(win, NS_ERROR_NOT_AVAILABLE);
+
+ nsCOMPtr<nsPIDOMWindowOuter> newWin;
+ nsAutoCString spec;
+ if (aURI) {
+ aURI->GetSpec(spec);
+ }
+ // If we are a noopener load, we just hand the whole thing over to our
+ // window.
+ if (aFlags & INTERNAL_LOAD_FLAGS_NO_OPENER) {
+ // Various asserts that we know to hold because NO_OPENER loads can only
+ // happen for links.
+ MOZ_ASSERT(!aLoadReplace);
+ MOZ_ASSERT(aPrincipalToInherit == aTriggeringPrincipal);
+ MOZ_ASSERT(aFlags == INTERNAL_LOAD_FLAGS_NO_OPENER ||
+ aFlags == (INTERNAL_LOAD_FLAGS_NO_OPENER |
+ INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER));
+ MOZ_ASSERT(!aPostData);
+ MOZ_ASSERT(!aHeadersData);
+ MOZ_ASSERT(aLoadType == LOAD_LINK);
+ MOZ_ASSERT(!aSHEntry);
+ MOZ_ASSERT(aFirstParty); // Windowwatcher will assume this.
+
+ nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
+ rv = CreateLoadInfo(getter_AddRefs(loadInfo));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // Set up our loadinfo so it will do the load as much like we would have
+ // as possible.
+ loadInfo->SetReferrer(aReferrer);
+ loadInfo->SetReferrerPolicy(aReferrerPolicy);
+ loadInfo->SetSendReferrer(!(aFlags &
+ INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER));
+ loadInfo->SetOriginalURI(aOriginalURI);
+ loadInfo->SetLoadReplace(aLoadReplace);
+ loadInfo->SetTriggeringPrincipal(aTriggeringPrincipal);
+ loadInfo->SetInheritPrincipal(
+ aFlags & INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL);
+ // Explicit principal because we do not want any guesses as to what the
+ // principal to inherit is: it should be aTriggeringPrincipal.
+ loadInfo->SetPrincipalIsExplicit(true);
+ loadInfo->SetLoadType(ConvertLoadTypeToDocShellLoadInfo(LOAD_LINK));
+
+ rv = win->Open(NS_ConvertUTF8toUTF16(spec),
+ aWindowTarget, // window name
+ EmptyString(), // Features
+ loadInfo,
+ true, // aForceNoOpener
+ getter_AddRefs(newWin));
+ MOZ_ASSERT(!newWin);
+ return rv;
+ }
+
+ rv = win->OpenNoNavigate(NS_ConvertUTF8toUTF16(spec),
+ aWindowTarget, // window name
+ EmptyString(), // Features
+ getter_AddRefs(newWin));
+
+ // In some cases the Open call doesn't actually result in a new
+ // window being opened. We can detect these cases by examining the
+ // document in |newWin|, if any.
+ nsCOMPtr<nsPIDOMWindowOuter> piNewWin = do_QueryInterface(newWin);
+ if (piNewWin) {
+ nsCOMPtr<nsIDocument> newDoc = piNewWin->GetExtantDoc();
+ if (!newDoc || newDoc->IsInitialDocument()) {
+ isNewWindow = true;
+ aFlags |= INTERNAL_LOAD_FLAGS_FIRST_LOAD;
+ }
+ }
+
+ nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(newWin);
+ targetDocShell = do_QueryInterface(webNav);
+ }
+
+ //
+ // Transfer the load to the target DocShell... Pass nullptr as the
+ // window target name from to prevent recursive retargeting!
+ //
+ if (NS_SUCCEEDED(rv) && targetDocShell) {
+ rv = targetDocShell->InternalLoad(aURI,
+ aOriginalURI,
+ aLoadReplace,
+ aReferrer,
+ aReferrerPolicy,
+ aTriggeringPrincipal,
+ principalToInherit,
+ aFlags,
+ EmptyString(), // No window target
+ aTypeHint,
+ NullString(), // No forced download
+ aPostData,
+ aHeadersData,
+ aLoadType,
+ aSHEntry,
+ aFirstParty,
+ aSrcdoc,
+ aSourceDocShell,
+ aBaseURI,
+ aDocShell,
+ aRequest);
+ if (rv == NS_ERROR_NO_CONTENT) {
+ // XXXbz except we never reach this code!
+ if (isNewWindow) {
+ //
+ // At this point, a new window has been created, but the
+ // URI did not have any data associated with it...
+ //
+ // So, the best we can do, is to tear down the new window
+ // that was just created!
+ //
+ if (nsCOMPtr<nsPIDOMWindowOuter> domWin = targetDocShell->GetWindow()) {
+ domWin->Close();
+ }
+ }
+ //
+ // NS_ERROR_NO_CONTENT should not be returned to the
+ // caller... This is an internal error code indicating that
+ // the URI had no data associated with it - probably a
+ // helper-app style protocol (ie. mailto://)
+ //
+ rv = NS_OK;
+ } else if (isNewWindow) {
+ // XXX: Once new windows are created hidden, the new
+ // window will need to be made visible... For now,
+ // do nothing.
+ }
+ }
+
+ // Else we ran out of memory, or were a popup and got blocked,
+ // or something.
+
+ return rv;
+ }
+
+ //
+ // Load is being targetted at this docshell so return an error if the
+ // docshell is in the process of being destroyed.
+ //
+ if (mIsBeingDestroyed) {
+ return NS_ERROR_FAILURE;
+ }
+
+ NS_ENSURE_STATE(!HasUnloadedParent());
+
+ rv = CheckLoadingPermissions();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (mFiredUnloadEvent) {
+ if (IsOKToLoadURI(aURI)) {
+ NS_PRECONDITION(aWindowTarget.IsEmpty(),
+ "Shouldn't have a window target here!");
+
+ // If this is a replace load, make whatever load triggered
+ // the unload event also a replace load, so we don't
+ // create extra history entries.
+ if (LOAD_TYPE_HAS_FLAGS(aLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
+ mLoadType = LOAD_NORMAL_REPLACE;
+ }
+
+ // Do this asynchronously
+ nsCOMPtr<nsIRunnable> ev =
+ new InternalLoadEvent(this, aURI, aOriginalURI, aLoadReplace,
+ aReferrer, aReferrerPolicy,
+ aTriggeringPrincipal, principalToInherit,
+ aFlags, aTypeHint, aPostData, aHeadersData,
+ aLoadType, aSHEntry, aFirstParty, aSrcdoc,
+ aSourceDocShell, aBaseURI);
+ return NS_DispatchToCurrentThread(ev);
+ }
+
+ // Just ignore this load attempt
+ return NS_OK;
+ }
+
+ // If a source docshell has been passed, check to see if we are sandboxed
+ // from it as the result of an iframe or CSP sandbox.
+ if (aSourceDocShell && aSourceDocShell->IsSandboxedFrom(this)) {
+ return NS_ERROR_DOM_INVALID_ACCESS_ERR;
+ }
+
+ // If this docshell is owned by a frameloader, make sure to cancel
+ // possible frameloader initialization before loading a new page.
+ nsCOMPtr<nsIDocShellTreeItem> parent = GetParentDocshell();
+ if (parent) {
+ nsCOMPtr<nsIDocument> doc = parent->GetDocument();
+ if (doc) {
+ doc->TryCancelFrameLoaderInitialization(this);
+ }
+ }
+
+ // Before going any further vet loads initiated by external programs.
+ if (aLoadType == LOAD_NORMAL_EXTERNAL) {
+ // Disallow external chrome: loads targetted at content windows
+ bool isChrome = false;
+ if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome)) && isChrome) {
+ NS_WARNING("blocked external chrome: url -- use '--chrome' option");
+ return NS_ERROR_FAILURE;
+ }
+
+ // clear the decks to prevent context bleed-through (bug 298255)
+ rv = CreateAboutBlankContentViewer(nullptr, nullptr);
+ if (NS_FAILED(rv)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // reset loadType so we don't have to add lots of tests for
+ // LOAD_NORMAL_EXTERNAL after this point
+ aLoadType = LOAD_NORMAL;
+ }
+
+ mAllowKeywordFixup =
+ (aFlags & INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) != 0;
+ mURIResultedInDocument = false; // reset the clock...
+
+ if (aLoadType == LOAD_NORMAL ||
+ aLoadType == LOAD_STOP_CONTENT ||
+ LOAD_TYPE_HAS_FLAGS(aLoadType, LOAD_FLAGS_REPLACE_HISTORY) ||
+ aLoadType == LOAD_HISTORY ||
+ aLoadType == LOAD_LINK) {
+ nsCOMPtr<nsIURI> currentURI = mCurrentURI;
+
+ nsAutoCString curHash, newHash;
+ bool curURIHasRef = false, newURIHasRef = false;
+
+ nsresult rvURINew = aURI->GetRef(newHash);
+ if (NS_SUCCEEDED(rvURINew)) {
+ rvURINew = aURI->GetHasRef(&newURIHasRef);
+ }
+
+ bool sameExceptHashes = false;
+ if (currentURI && NS_SUCCEEDED(rvURINew)) {
+ nsresult rvURIOld = currentURI->GetRef(curHash);
+ if (NS_SUCCEEDED(rvURIOld)) {
+ rvURIOld = currentURI->GetHasRef(&curURIHasRef);
+ }
+ if (NS_SUCCEEDED(rvURIOld)) {
+ if (NS_FAILED(currentURI->EqualsExceptRef(aURI, &sameExceptHashes))) {
+ sameExceptHashes = false;
+ }
+ }
+ }
+
+ if (!sameExceptHashes && sURIFixup && currentURI &&
+ NS_SUCCEEDED(rvURINew)) {
+ // Maybe aURI came from the exposable form of currentURI?
+ nsCOMPtr<nsIURI> currentExposableURI;
+ rv = sURIFixup->CreateExposableURI(currentURI,
+ getter_AddRefs(currentExposableURI));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsresult rvURIOld = currentExposableURI->GetRef(curHash);
+ if (NS_SUCCEEDED(rvURIOld)) {
+ rvURIOld = currentExposableURI->GetHasRef(&curURIHasRef);
+ }
+ if (NS_SUCCEEDED(rvURIOld)) {
+ if (NS_FAILED(currentExposableURI->EqualsExceptRef(aURI, &sameExceptHashes))) {
+ sameExceptHashes = false;
+ }
+ }
+ }
+
+ bool historyNavBetweenSameDoc = false;
+ if (mOSHE && aSHEntry) {
+ // We're doing a history load.
+
+ mOSHE->SharesDocumentWith(aSHEntry, &historyNavBetweenSameDoc);
+
+#ifdef DEBUG
+ if (historyNavBetweenSameDoc) {
+ nsCOMPtr<nsIInputStream> currentPostData;
+ mOSHE->GetPostData(getter_AddRefs(currentPostData));
+ NS_ASSERTION(currentPostData == aPostData,
+ "Different POST data for entries for the same page?");
+ }
+#endif
+ }
+
+ // A short-circuited load happens when we navigate between two SHEntries
+ // for the same document. We do a short-circuited load under two
+ // circumstances. Either
+ //
+ // a) we're navigating between two different SHEntries which share a
+ // document, or
+ //
+ // b) we're navigating to a new shentry whose URI differs from the
+ // current URI only in its hash, the new hash is non-empty, and
+ // we're not doing a POST.
+ //
+ // The restriction tha the SHEntries in (a) must be different ensures
+ // that history.go(0) and the like trigger full refreshes, rather than
+ // short-circuited loads.
+ bool doShortCircuitedLoad =
+ (historyNavBetweenSameDoc && mOSHE != aSHEntry) ||
+ (!aSHEntry && !aPostData &&
+ sameExceptHashes && newURIHasRef);
+
+ if (doShortCircuitedLoad) {
+ // Save the position of the scrollers.
+ nscoord cx = 0, cy = 0;
+ GetCurScrollPos(ScrollOrientation_X, &cx);
+ GetCurScrollPos(ScrollOrientation_Y, &cy);
+
+ // Reset mLoadType to its original value once we exit this block,
+ // because this short-circuited load might have started after a
+ // normal, network load, and we don't want to clobber its load type.
+ // See bug 737307.
+ AutoRestore<uint32_t> loadTypeResetter(mLoadType);
+
+ // If a non-short-circuit load (i.e., a network load) is pending,
+ // make this a replacement load, so that we don't add a SHEntry here
+ // and the network load goes into the SHEntry it expects to.
+ if (JustStartedNetworkLoad() && (aLoadType & LOAD_CMD_NORMAL)) {
+ mLoadType = LOAD_NORMAL_REPLACE;
+ } else {
+ mLoadType = aLoadType;
+ }
+
+ mURIResultedInDocument = true;
+
+ nsCOMPtr<nsISHEntry> oldLSHE = mLSHE;
+
+ /* we need to assign mLSHE to aSHEntry right here, so that on History
+ * loads, SetCurrentURI() called from OnNewURI() will send proper
+ * onLocationChange() notifications to the browser to update
+ * back/forward buttons.
+ */
+ SetHistoryEntry(&mLSHE, aSHEntry);
+
+ // Set the doc's URI according to the new history entry's URI.
+ nsCOMPtr<nsIDocument> doc = GetDocument();
+ NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
+ doc->SetDocumentURI(aURI);
+
+ /* This is a anchor traversal with in the same page.
+ * call OnNewURI() so that, this traversal will be
+ * recorded in session and global history.
+ */
+ nsCOMPtr<nsIPrincipal> triggeringPrincipal, principalToInherit;
+ if (mOSHE) {
+ mOSHE->GetTriggeringPrincipal(getter_AddRefs(triggeringPrincipal));
+ mOSHE->GetPrincipalToInherit(getter_AddRefs(principalToInherit));
+ }
+ // Pass true for aCloneSHChildren, since we're not
+ // changing documents here, so all of our subframes are
+ // still relevant to the new session history entry.
+ //
+ // It also makes OnNewURI(...) set LOCATION_CHANGE_SAME_DOCUMENT
+ // flag on firing onLocationChange(...).
+ // Anyway, aCloneSHChildren param is simply reflecting
+ // doShortCircuitedLoad in this scope.
+ OnNewURI(aURI, nullptr, triggeringPrincipal, principalToInherit,
+ mLoadType, true, true, true);
+
+ nsCOMPtr<nsIInputStream> postData;
+ nsCOMPtr<nsISupports> cacheKey;
+
+ bool scrollRestorationIsManual = false;
+ if (mOSHE) {
+ /* save current position of scroller(s) (bug 59774) */
+ mOSHE->SetScrollPosition(cx, cy);
+ mOSHE->GetScrollRestorationIsManual(&scrollRestorationIsManual);
+ // Get the postdata and page ident from the current page, if
+ // the new load is being done via normal means. Note that
+ // "normal means" can be checked for just by checking for
+ // LOAD_CMD_NORMAL, given the loadType and allowScroll check
+ // above -- it filters out some LOAD_CMD_NORMAL cases that we
+ // wouldn't want here.
+ if (aLoadType & LOAD_CMD_NORMAL) {
+ mOSHE->GetPostData(getter_AddRefs(postData));
+ mOSHE->GetCacheKey(getter_AddRefs(cacheKey));
+
+ // Link our new SHEntry to the old SHEntry's back/forward
+ // cache data, since the two SHEntries correspond to the
+ // same document.
+ if (mLSHE) {
+ if (!aSHEntry) {
+ // If we're not doing a history load, scroll restoration
+ // should be inherited from the previous session history entry.
+ mLSHE->SetScrollRestorationIsManual(scrollRestorationIsManual);
+ }
+ mLSHE->AdoptBFCacheEntry(mOSHE);
+ }
+ }
+ }
+
+ // If we're doing a history load, use its scroll restoration state.
+ if (aSHEntry) {
+ aSHEntry->GetScrollRestorationIsManual(&scrollRestorationIsManual);
+ }
+
+ /* Assign mOSHE to mLSHE. This will either be a new entry created
+ * by OnNewURI() for normal loads or aSHEntry for history loads.
+ */
+ if (mLSHE) {
+ SetHistoryEntry(&mOSHE, mLSHE);
+ // Save the postData obtained from the previous page
+ // in to the session history entry created for the
+ // anchor page, so that any history load of the anchor
+ // page will restore the appropriate postData.
+ if (postData) {
+ mOSHE->SetPostData(postData);
+ }
+
+ // Make sure we won't just repost without hitting the
+ // cache first
+ if (cacheKey) {
+ mOSHE->SetCacheKey(cacheKey);
+ }
+ }
+
+ /* Restore the original LSHE if we were loading something
+ * while short-circuited load was initiated.
+ */
+ SetHistoryEntry(&mLSHE, oldLSHE);
+ /* Set the title for the SH entry for this target url. so that
+ * SH menus in go/back/forward buttons won't be empty for this.
+ */
+ if (mSessionHistory) {
+ int32_t index = -1;
+ mSessionHistory->GetIndex(&index);
+ nsCOMPtr<nsISHEntry> shEntry;
+ mSessionHistory->GetEntryAtIndex(index, false, getter_AddRefs(shEntry));
+ NS_ENSURE_TRUE(shEntry, NS_ERROR_FAILURE);
+ shEntry->SetTitle(mTitle);
+ }
+
+ /* Set the title for the Global History entry for this anchor url.
+ */
+ if (mUseGlobalHistory && !UsePrivateBrowsing()) {
+ nsCOMPtr<IHistory> history = services::GetHistoryService();
+ if (history) {
+ history->SetURITitle(aURI, mTitle);
+ } else if (mGlobalHistory) {
+ mGlobalHistory->SetPageTitle(aURI, mTitle);
+ }
+ }
+
+ SetDocCurrentStateObj(mOSHE);
+
+ // Inform the favicon service that the favicon for oldURI also
+ // applies to aURI.
+ CopyFavicon(currentURI, aURI, doc->NodePrincipal(), UsePrivateBrowsing());
+
+ RefPtr<nsGlobalWindow> scriptGlobal = mScriptGlobal;
+ RefPtr<nsGlobalWindow> win = scriptGlobal ?
+ scriptGlobal->GetCurrentInnerWindowInternal() : nullptr;
+
+ // ScrollToAnchor doesn't necessarily cause us to scroll the window;
+ // the function decides whether a scroll is appropriate based on the
+ // arguments it receives. But even if we don't end up scrolling,
+ // ScrollToAnchor performs other important tasks, such as informing
+ // the presShell that we have a new hash. See bug 680257.
+ rv = ScrollToAnchor(curURIHasRef, newURIHasRef, newHash, aLoadType);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ /* restore previous position of scroller(s), if we're moving
+ * back in history (bug 59774)
+ */
+ nscoord bx = 0;
+ nscoord by = 0;
+ bool needsScrollPosUpdate = false;
+ if (mOSHE && (aLoadType == LOAD_HISTORY ||
+ aLoadType == LOAD_RELOAD_NORMAL) &&
+ !scrollRestorationIsManual) {
+ needsScrollPosUpdate = true;
+ mOSHE->GetScrollPosition(&bx, &by);
+ }
+
+ // Dispatch the popstate and hashchange events, as appropriate.
+ //
+ // The event dispatch below can cause us to re-enter script and
+ // destroy the docshell, nulling out mScriptGlobal. Hold a stack
+ // reference to avoid null derefs. See bug 914521.
+ if (win) {
+ // Fire a hashchange event URIs differ, and only in their hashes.
+ bool doHashchange = sameExceptHashes &&
+ (curURIHasRef != newURIHasRef || !curHash.Equals(newHash));
+
+ if (historyNavBetweenSameDoc || doHashchange) {
+ win->DispatchSyncPopState();
+ }
+
+ if (needsScrollPosUpdate && win->AsInner()->HasActiveDocument()) {
+ SetCurScrollPosEx(bx, by);
+ }
+
+ if (doHashchange) {
+ // Note that currentURI hasn't changed because it's on the
+ // stack, so we can just use it directly as the old URI.
+ win->DispatchAsyncHashchange(currentURI, aURI);
+ }
+ }
+
+ return NS_OK;
+ }
+ }
+
+ // mContentViewer->PermitUnload can destroy |this| docShell, which
+ // causes the next call of CanSavePresentation to crash.
+ // Hold onto |this| until we return, to prevent a crash from happening.
+ // (bug#331040)
+ nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
+
+ // Don't init timing for javascript:, since it generally doesn't
+ // actually start a load or anything. If it does, we'll init
+ // timing then, from OnStateChange.
+
+ // XXXbz mTiming should know what channel it's for, so we don't
+ // need this hackery. Note that this is still broken in cases
+ // when we're loading something that's not javascript: and the
+ // beforeunload handler denies the load. That will screw up
+ // timing for the next load!
+ if (!isJavaScript) {
+ MaybeInitTiming();
+ }
+ bool timeBeforeUnload = aFileName.IsVoid();
+ if (mTiming && timeBeforeUnload) {
+ mTiming->NotifyBeforeUnload();
+ }
+ // Check if the page doesn't want to be unloaded. The javascript:
+ // protocol handler deals with this for javascript: URLs.
+ if (!isJavaScript && aFileName.IsVoid() && mContentViewer) {
+ bool okToUnload;
+ rv = mContentViewer->PermitUnload(&okToUnload);
+
+ if (NS_SUCCEEDED(rv) && !okToUnload) {
+ // The user chose not to unload the page, interrupt the
+ // load.
+ return NS_OK;
+ }
+ }
+
+ if (mTiming && timeBeforeUnload) {
+ mTiming->NotifyUnloadAccepted(mCurrentURI);
+ }
+
+ // Check if the webbrowser chrome wants the load to proceed; this can be
+ // used to cancel attempts to load URIs in the wrong process.
+ nsCOMPtr<nsIWebBrowserChrome3> browserChrome3 = do_GetInterface(mTreeOwner);
+ if (browserChrome3) {
+ // In case this is a remote newtab load, set aURI to aOriginalURI (newtab).
+ // This ensures that the verifySignedContent flag is set on loadInfo in
+ // DoURILoad.
+ nsIURI* uriForShouldLoadCheck = aURI;
+ if (IsAboutNewtab(aOriginalURI)) {
+ uriForShouldLoadCheck = aOriginalURI;
+ }
+ bool shouldLoad;
+ rv = browserChrome3->ShouldLoadURI(this, uriForShouldLoadCheck, aReferrer,
+ &shouldLoad);
+ if (NS_SUCCEEDED(rv) && !shouldLoad) {
+ return NS_OK;
+ }
+ }
+
+ // Whenever a top-level browsing context is navigated, the user agent MUST
+ // lock the orientation of the document to the document's default
+ // orientation. We don't explicitly check for a top-level browsing context
+ // here because orientation is only set on top-level browsing contexts.
+ // We make an exception for apps because they currently rely on
+ // orientation locks persisting across browsing contexts.
+ if (OrientationLock() != eScreenOrientation_None && !GetIsApp()) {
+#ifdef DEBUG
+ nsCOMPtr<nsIDocShellTreeItem> parent;
+ GetSameTypeParent(getter_AddRefs(parent));
+ MOZ_ASSERT(!parent);
+#endif
+ SetOrientationLock(eScreenOrientation_None);
+ if (mIsActive) {
+ ScreenOrientation::UpdateActiveOrientationLock(eScreenOrientation_None);
+ }
+ }
+
+ // Check for saving the presentation here, before calling Stop().
+ // This is necessary so that we can catch any pending requests.
+ // Since the new request has not been created yet, we pass null for the
+ // new request parameter.
+ // Also pass nullptr for the document, since it doesn't affect the return
+ // value for our purposes here.
+ bool savePresentation = CanSavePresentation(aLoadType, nullptr, nullptr);
+
+ // Don't stop current network activity for javascript: URL's since
+ // they might not result in any data, and thus nothing should be
+ // stopped in those cases. In the case where they do result in
+ // data, the javascript: URL channel takes care of stopping
+ // current network activity.
+ if (!isJavaScript && aFileName.IsVoid()) {
+ // Stop any current network activity.
+ // Also stop content if this is a zombie doc. otherwise
+ // the onload will be delayed by other loads initiated in the
+ // background by the first document that
+ // didn't fully load before the next load was initiated.
+ // If not a zombie, don't stop content until data
+ // starts arriving from the new URI...
+
+ nsCOMPtr<nsIContentViewer> zombieViewer;
+ if (mContentViewer) {
+ mContentViewer->GetPreviousViewer(getter_AddRefs(zombieViewer));
+ }
+
+ if (zombieViewer ||
+ LOAD_TYPE_HAS_FLAGS(aLoadType, LOAD_FLAGS_STOP_CONTENT)) {
+ rv = Stop(nsIWebNavigation::STOP_ALL);
+ } else {
+ rv = Stop(nsIWebNavigation::STOP_NETWORK);
+ }
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+
+ mLoadType = aLoadType;
+
+ // mLSHE should be assigned to aSHEntry, only after Stop() has
+ // been called. But when loading an error page, do not clear the
+ // mLSHE for the real page.
+ if (mLoadType != LOAD_ERROR_PAGE) {
+ SetHistoryEntry(&mLSHE, aSHEntry);
+ }
+
+ mSavingOldViewer = savePresentation;
+
+ // If we have a saved content viewer in history, restore and show it now.
+ if (aSHEntry && (mLoadType & LOAD_CMD_HISTORY)) {
+ // Make sure our history ID points to the same ID as
+ // SHEntry's docshell ID.
+ aSHEntry->GetDocshellID(&mHistoryID);
+
+ // It's possible that the previous viewer of mContentViewer is the
+ // viewer that will end up in aSHEntry when it gets closed. If that's
+ // the case, we need to go ahead and force it into its shentry so we
+ // can restore it.
+ if (mContentViewer) {
+ nsCOMPtr<nsIContentViewer> prevViewer;
+ mContentViewer->GetPreviousViewer(getter_AddRefs(prevViewer));
+ if (prevViewer) {
+#ifdef DEBUG
+ nsCOMPtr<nsIContentViewer> prevPrevViewer;
+ prevViewer->GetPreviousViewer(getter_AddRefs(prevPrevViewer));
+ NS_ASSERTION(!prevPrevViewer, "Should never have viewer chain here");
+#endif
+ nsCOMPtr<nsISHEntry> viewerEntry;
+ prevViewer->GetHistoryEntry(getter_AddRefs(viewerEntry));
+ if (viewerEntry == aSHEntry) {
+ // Make sure this viewer ends up in the right place
+ mContentViewer->SetPreviousViewer(nullptr);
+ prevViewer->Destroy();
+ }
+ }
+ }
+ nsCOMPtr<nsISHEntry> oldEntry = mOSHE;
+ bool restoring;
+ rv = RestorePresentation(aSHEntry, &restoring);
+ if (restoring) {
+ return rv;
+ }
+
+ // We failed to restore the presentation, so clean up.
+ // Both the old and new history entries could potentially be in
+ // an inconsistent state.
+ if (NS_FAILED(rv)) {
+ if (oldEntry) {
+ oldEntry->SyncPresentationState();
+ }
+
+ aSHEntry->SyncPresentationState();
+ }
+ }
+
+ nsAutoString srcdoc;
+ if (aFlags & INTERNAL_LOAD_FLAGS_IS_SRCDOC) {
+ srcdoc = aSrcdoc;
+ } else {
+ srcdoc = NullString();
+ }
+
+ net::PredictorLearn(aURI, nullptr,
+ nsINetworkPredictor::LEARN_LOAD_TOPLEVEL, this);
+ net::PredictorPredict(aURI, nullptr,
+ nsINetworkPredictor::PREDICT_LOAD, this, nullptr);
+
+ nsCOMPtr<nsIRequest> req;
+ rv = DoURILoad(aURI, aOriginalURI, aLoadReplace, aReferrer,
+ !(aFlags & INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER),
+ aReferrerPolicy,
+ aTriggeringPrincipal, principalToInherit, aTypeHint,
+ aFileName, aPostData, aHeadersData,
+ aFirstParty, aDocShell, getter_AddRefs(req),
+ (aFlags & INTERNAL_LOAD_FLAGS_FIRST_LOAD) != 0,
+ (aFlags & INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER) != 0,
+ (aFlags & INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES) != 0,
+ srcdoc, aBaseURI, contentType);
+ if (req && aRequest) {
+ NS_ADDREF(*aRequest = req);
+ }
+
+ if (NS_FAILED(rv)) {
+ nsCOMPtr<nsIChannel> chan(do_QueryInterface(req));
+ if (DisplayLoadError(rv, aURI, nullptr, chan) &&
+ (aFlags & LOAD_FLAGS_ERROR_LOAD_CHANGES_RV) != 0) {
+ return NS_ERROR_LOAD_SHOWED_ERRORPAGE;
+ }
+ }
+
+ return rv;
+}
+
+nsIPrincipal*
+nsDocShell::GetInheritedPrincipal(bool aConsiderCurrentDocument)
+{
+ nsCOMPtr<nsIDocument> document;
+ bool inheritedFromCurrent = false;
+
+ if (aConsiderCurrentDocument && mContentViewer) {
+ document = mContentViewer->GetDocument();
+ inheritedFromCurrent = true;
+ }
+
+ if (!document) {
+ nsCOMPtr<nsIDocShellTreeItem> parentItem;
+ GetSameTypeParent(getter_AddRefs(parentItem));
+ if (parentItem) {
+ document = parentItem->GetDocument();
+ }
+ }
+
+ if (!document) {
+ if (!aConsiderCurrentDocument) {
+ return nullptr;
+ }
+
+ // Make sure we end up with _something_ as the principal no matter
+ // what.If this fails, we'll just get a null docViewer and bail.
+ EnsureContentViewer();
+ if (!mContentViewer) {
+ return nullptr;
+ }
+ document = mContentViewer->GetDocument();
+ }
+
+ //-- Get the document's principal
+ if (document) {
+ nsIPrincipal* docPrincipal = document->NodePrincipal();
+
+ // Don't allow loads in typeContent docShells to inherit the system
+ // principal from existing documents.
+ if (inheritedFromCurrent &&
+ mItemType == typeContent &&
+ nsContentUtils::IsSystemPrincipal(docPrincipal)) {
+ return nullptr;
+ }
+
+ return docPrincipal;
+ }
+
+ return nullptr;
+}
+
+nsresult
+nsDocShell::DoURILoad(nsIURI* aURI,
+ nsIURI* aOriginalURI,
+ bool aLoadReplace,
+ nsIURI* aReferrerURI,
+ bool aSendReferrer,
+ uint32_t aReferrerPolicy,
+ nsIPrincipal* aTriggeringPrincipal,
+ nsIPrincipal* aPrincipalToInherit,
+ const char* aTypeHint,
+ const nsAString& aFileName,
+ nsIInputStream* aPostData,
+ nsIInputStream* aHeadersData,
+ bool aFirstParty,
+ nsIDocShell** aDocShell,
+ nsIRequest** aRequest,
+ bool aIsNewWindowTarget,
+ bool aBypassClassifier,
+ bool aForceAllowCookies,
+ const nsAString& aSrcdoc,
+ nsIURI* aBaseURI,
+ nsContentPolicyType aContentPolicyType)
+{
+ // Double-check that we're still around to load this URI.
+ if (mIsBeingDestroyed) {
+ // Return NS_OK despite not doing anything to avoid throwing exceptions from
+ // nsLocation::SetHref if the unload handler of the existing page tears us
+ // down.
+ return NS_OK;
+ }
+
+ nsresult rv;
+ nsCOMPtr<nsIURILoader> uriLoader = do_GetService(NS_URI_LOADER_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (IsFrame()) {
+
+ MOZ_ASSERT(aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_IFRAME ||
+ aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_FRAME,
+ "DoURILoad thinks this is a frame and InternalLoad does not");
+
+ // Only allow view-source scheme in top-level docshells. view-source is
+ // the only scheme to which this applies at the moment due to potential
+ // timing attacks to read data from cross-origin iframes. If this widens
+ // we should add a protocol flag for whether the scheme is allowed in
+ // frames and use something like nsNetUtil::NS_URIChainHasFlags.
+ nsCOMPtr<nsIURI> tempURI = aURI;
+ nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(tempURI);
+ while (nestedURI) {
+ // view-source should always be an nsINestedURI, loop and check the
+ // scheme on this and all inner URIs that are also nested URIs.
+ bool isViewSource = false;
+ rv = tempURI->SchemeIs("view-source", &isViewSource);
+ if (NS_FAILED(rv) || isViewSource) {
+ return NS_ERROR_UNKNOWN_PROTOCOL;
+ }
+ nestedURI->GetInnerURI(getter_AddRefs(tempURI));
+ nestedURI = do_QueryInterface(tempURI);
+ }
+ } else {
+ MOZ_ASSERT(aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT,
+ "DoURILoad thinks this is a document and InternalLoad does not");
+ }
+
+ // open a channel for the url
+ nsCOMPtr<nsIChannel> channel;
+
+ bool isSrcdoc = !aSrcdoc.IsVoid();
+
+ // There are two cases we care about:
+ // * Top-level load: In this case, loadingNode is null, but loadingWindow
+ // is our mScriptGlobal. We pass null for loadingPrincipal in this case.
+ // * Subframe load: loadingWindow is null, but loadingNode is the frame
+ // element for the load. loadingPrincipal is the NodePrincipal of the frame
+ // element.
+ nsCOMPtr<nsINode> loadingNode;
+ nsCOMPtr<nsPIDOMWindowOuter> loadingWindow;
+ nsCOMPtr<nsIPrincipal> loadingPrincipal;
+
+ if (aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT) {
+ loadingNode = nullptr;
+ loadingPrincipal = nullptr;
+ loadingWindow = mScriptGlobal->AsOuter();
+ } else {
+ loadingWindow = nullptr;
+ loadingNode = mScriptGlobal->AsOuter()->GetFrameElementInternal();
+ if (loadingNode) {
+ // If we have a loading node, then use that as our loadingPrincipal.
+ loadingPrincipal = loadingNode->NodePrincipal();
+ } else {
+ // If this isn't a top-level load and mScriptGlobal's frame element is
+ // null, then the element got removed from the DOM while we were trying
+ // to load this resource. This docshell is scheduled for destruction
+ // already, so bail out here.
+ return NS_OK;
+ }
+ }
+
+ // Getting the right triggeringPrincipal needs to be updated and is only
+ // ready for use once bug 1182569 landed. Until then, we cannot rely on
+ // the triggeringPrincipal for TYPE_DOCUMENT loads.
+ MOZ_ASSERT(aTriggeringPrincipal, "Need a valid triggeringPrincipal");
+
+ bool isSandBoxed = mSandboxFlags & SANDBOXED_ORIGIN;
+ // only inherit if we have a aPrincipalToInherit
+ bool inherit = false;
+
+ if (aPrincipalToInherit) {
+ inherit = nsContentUtils::ChannelShouldInheritPrincipal(
+ aPrincipalToInherit,
+ aURI,
+ true, // aInheritForAboutBlank
+ isSrcdoc);
+ }
+
+ nsLoadFlags loadFlags = mDefaultLoadFlags;
+ nsSecurityFlags securityFlags = nsILoadInfo::SEC_NORMAL;
+
+ if (aFirstParty) {
+ // tag first party URL loads
+ loadFlags |= nsIChannel::LOAD_INITIAL_DOCUMENT_URI;
+ }
+
+ if (mLoadType == LOAD_ERROR_PAGE) {
+ // Error pages are LOAD_BACKGROUND
+ loadFlags |= nsIChannel::LOAD_BACKGROUND;
+ securityFlags |= nsILoadInfo::SEC_LOAD_ERROR_PAGE;
+ }
+
+ if (inherit) {
+ securityFlags |= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
+ }
+ if (isSandBoxed) {
+ securityFlags |= nsILoadInfo::SEC_SANDBOXED;
+ }
+
+ nsCOMPtr<nsILoadInfo> loadInfo =
+ (aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT) ?
+ new LoadInfo(loadingWindow, aTriggeringPrincipal,
+ securityFlags) :
+ new LoadInfo(loadingPrincipal, aTriggeringPrincipal, loadingNode,
+ securityFlags, aContentPolicyType);
+
+ if (aPrincipalToInherit) {
+ loadInfo->SetPrincipalToInherit(aPrincipalToInherit);
+ }
+
+ // We have to do this in case our OriginAttributes are different from the
+ // OriginAttributes of the parent document. Or in case there isn't a
+ // parent document.
+ NeckoOriginAttributes neckoAttrs;
+ bool isTopLevelDoc = aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT &&
+ mItemType == typeContent &&
+ !GetIsMozBrowserOrApp();
+ neckoAttrs.InheritFromDocShellToNecko(GetOriginAttributes(), isTopLevelDoc, aURI);
+ rv = loadInfo->SetOriginAttributes(neckoAttrs);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (!isSrcdoc) {
+ rv = NS_NewChannelInternal(getter_AddRefs(channel),
+ aURI,
+ loadInfo,
+ nullptr, // loadGroup
+ static_cast<nsIInterfaceRequestor*>(this),
+ loadFlags);
+
+ if (NS_FAILED(rv)) {
+ if (rv == NS_ERROR_UNKNOWN_PROTOCOL) {
+ // This is a uri with a protocol scheme we don't know how
+ // to handle. Embedders might still be interested in
+ // handling the load, though, so we fire a notification
+ // before throwing the load away.
+ bool abort = false;
+ nsresult rv2 = mContentListener->OnStartURIOpen(aURI, &abort);
+ if (NS_SUCCEEDED(rv2) && abort) {
+ // Hey, they're handling the load for us! How convenient!
+ return NS_OK;
+ }
+ }
+ return rv;
+ }
+
+ if (aBaseURI) {
+ nsCOMPtr<nsIViewSourceChannel> vsc = do_QueryInterface(channel);
+ if (vsc) {
+ vsc->SetBaseURI(aBaseURI);
+ }
+ }
+ } else {
+ nsAutoCString scheme;
+ rv = aURI->GetScheme(scheme);
+ NS_ENSURE_SUCCESS(rv, rv);
+ bool isViewSource;
+ aURI->SchemeIs("view-source", &isViewSource);
+
+ if (isViewSource) {
+ nsViewSourceHandler* vsh = nsViewSourceHandler::GetInstance();
+ NS_ENSURE_TRUE(vsh, NS_ERROR_FAILURE);
+
+ rv = vsh->NewSrcdocChannel(aURI, aBaseURI, aSrcdoc,
+ loadInfo, getter_AddRefs(channel));
+ } else {
+ rv = NS_NewInputStreamChannelInternal(getter_AddRefs(channel),
+ aURI,
+ aSrcdoc,
+ NS_LITERAL_CSTRING("text/html"),
+ loadInfo,
+ true);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIInputStreamChannel> isc = do_QueryInterface(channel);
+ MOZ_ASSERT(isc);
+ isc->SetBaseURI(aBaseURI);
+ }
+ }
+
+ nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
+ do_QueryInterface(channel);
+ if (appCacheChannel) {
+ // Any document load should not inherit application cache.
+ appCacheChannel->SetInheritApplicationCache(false);
+
+ // Loads with the correct permissions should check for a matching
+ // application cache.
+ if (GeckoProcessType_Default != XRE_GetProcessType()) {
+ // Permission will be checked in the parent process
+ appCacheChannel->SetChooseApplicationCache(true);
+ } else {
+ nsCOMPtr<nsIScriptSecurityManager> secMan =
+ do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
+
+ if (secMan) {
+ nsCOMPtr<nsIPrincipal> principal;
+ secMan->GetDocShellCodebasePrincipal(aURI, this,
+ getter_AddRefs(principal));
+ appCacheChannel->SetChooseApplicationCache(
+ NS_ShouldCheckAppCache(principal, UsePrivateBrowsing()));
+ }
+ }
+ }
+
+ // Make sure to give the caller a channel if we managed to create one
+ // This is important for correct error page/session history interaction
+ if (aRequest) {
+ NS_ADDREF(*aRequest = channel);
+ }
+
+ if (aOriginalURI) {
+ channel->SetOriginalURI(aOriginalURI);
+ if (aLoadReplace) {
+ uint32_t loadFlags;
+ channel->GetLoadFlags(&loadFlags);
+ NS_ENSURE_SUCCESS(rv, rv);
+ channel->SetLoadFlags(loadFlags | nsIChannel::LOAD_REPLACE);
+ }
+ } else {
+ channel->SetOriginalURI(aURI);
+ }
+
+ if (aTypeHint && *aTypeHint) {
+ channel->SetContentType(nsDependentCString(aTypeHint));
+ mContentTypeHint = aTypeHint;
+ } else {
+ mContentTypeHint.Truncate();
+ }
+
+ if (!aFileName.IsVoid()) {
+ rv = channel->SetContentDisposition(nsIChannel::DISPOSITION_ATTACHMENT);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!aFileName.IsEmpty()) {
+ rv = channel->SetContentDispositionFilename(aFileName);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+
+ if (mLoadType == LOAD_NORMAL_ALLOW_MIXED_CONTENT ||
+ mLoadType == LOAD_RELOAD_ALLOW_MIXED_CONTENT) {
+ rv = SetMixedContentChannel(channel);
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else if (mMixedContentChannel) {
+ /*
+ * If the user "Disables Protection on This Page", we call
+ * SetMixedContentChannel for the first time, otherwise
+ * mMixedContentChannel is still null.
+ * Later, if the new channel passes a same orign check, we remember the
+ * users decision by calling SetMixedContentChannel using the new channel.
+ * This way, the user does not have to click the disable protection button
+ * over and over for browsing the same site.
+ */
+ rv = nsContentUtils::CheckSameOrigin(mMixedContentChannel, channel);
+ if (NS_FAILED(rv) || NS_FAILED(SetMixedContentChannel(channel))) {
+ SetMixedContentChannel(nullptr);
+ }
+ }
+
+ // hack
+ nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
+ nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal(
+ do_QueryInterface(channel));
+ if (httpChannelInternal) {
+ if (aForceAllowCookies) {
+ httpChannelInternal->SetThirdPartyFlags(
+ nsIHttpChannelInternal::THIRD_PARTY_FORCE_ALLOW);
+ }
+ if (aFirstParty) {
+ httpChannelInternal->SetDocumentURI(aURI);
+ } else {
+ httpChannelInternal->SetDocumentURI(aReferrerURI);
+ }
+ httpChannelInternal->SetRedirectMode(
+ nsIHttpChannelInternal::REDIRECT_MODE_MANUAL);
+ }
+
+ nsCOMPtr<nsIWritablePropertyBag2> props(do_QueryInterface(channel));
+ if (props) {
+ // save true referrer for those who need it (e.g. xpinstall whitelisting)
+ // Currently only http and ftp channels support this.
+ props->SetPropertyAsInterface(NS_LITERAL_STRING("docshell.internalReferrer"),
+ aReferrerURI);
+ }
+
+ nsCOMPtr<nsICacheInfoChannel> cacheChannel(do_QueryInterface(channel));
+ /* Get the cache Key from SH */
+ nsCOMPtr<nsISupports> cacheKey;
+ if (cacheChannel) {
+ if (mLSHE) {
+ mLSHE->GetCacheKey(getter_AddRefs(cacheKey));
+ } else if (mOSHE) { // for reload cases
+ mOSHE->GetCacheKey(getter_AddRefs(cacheKey));
+ }
+ }
+
+ // figure out if we need to set the post data stream on the channel...
+ if (aPostData) {
+ nsCOMPtr<nsIFormPOSTActionChannel> postChannel(do_QueryInterface(channel));
+ if (postChannel) {
+ // XXX it's a bit of a hack to rewind the postdata stream here but
+ // it has to be done in case the post data is being reused multiple
+ // times.
+ nsCOMPtr<nsISeekableStream> postDataSeekable =
+ do_QueryInterface(aPostData);
+ if (postDataSeekable) {
+ rv = postDataSeekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // we really need to have a content type associated with this stream!!
+ postChannel->SetUploadStream(aPostData, EmptyCString(), -1);
+ }
+
+ /* If there is a valid postdata *and* it is a History Load,
+ * set up the cache key on the channel, to retrieve the
+ * data *only* from the cache. If it is a normal reload, the
+ * cache is free to go to the server for updated postdata.
+ */
+ if (cacheChannel && cacheKey) {
+ if (mLoadType == LOAD_HISTORY ||
+ mLoadType == LOAD_RELOAD_CHARSET_CHANGE) {
+ cacheChannel->SetCacheKey(cacheKey);
+ uint32_t loadFlags;
+ if (NS_SUCCEEDED(channel->GetLoadFlags(&loadFlags))) {
+ channel->SetLoadFlags(
+ loadFlags | nsICachingChannel::LOAD_ONLY_FROM_CACHE);
+ }
+ } else if (mLoadType == LOAD_RELOAD_NORMAL) {
+ cacheChannel->SetCacheKey(cacheKey);
+ }
+ }
+ } else {
+ /* If there is no postdata, set the cache key on the channel, and
+ * do not set the LOAD_ONLY_FROM_CACHE flag, so that the channel
+ * will be free to get it from net if it is not found in cache.
+ * New cache may use it creatively on CGI pages with GET
+ * method and even on those that say "no-cache"
+ */
+ if (mLoadType == LOAD_HISTORY ||
+ mLoadType == LOAD_RELOAD_NORMAL ||
+ mLoadType == LOAD_RELOAD_CHARSET_CHANGE) {
+ if (cacheChannel && cacheKey) {
+ cacheChannel->SetCacheKey(cacheKey);
+ }
+ }
+ }
+
+ if (httpChannel) {
+ if (aHeadersData) {
+ rv = AddHeadersToChannel(aHeadersData, httpChannel);
+ }
+ // Set the referrer explicitly
+ if (aReferrerURI && aSendReferrer) {
+ // Referrer is currenly only set for link clicks here.
+ httpChannel->SetReferrerWithPolicy(aReferrerURI, aReferrerPolicy);
+ }
+ // set Content-Signature enforcing bit if aOriginalURI == about:newtab
+ if (aOriginalURI && httpChannel) {
+ if (IsAboutNewtab(aOriginalURI)) {
+ nsCOMPtr<nsILoadInfo> loadInfo = httpChannel->GetLoadInfo();
+ if (loadInfo) {
+ loadInfo->SetVerifySignedContent(true);
+ }
+ }
+ }
+ }
+
+ nsCOMPtr<nsIScriptChannel> scriptChannel = do_QueryInterface(channel);
+ if (scriptChannel) {
+ // Allow execution against our context if the principals match
+ scriptChannel->SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
+ }
+
+ if (aIsNewWindowTarget) {
+ nsCOMPtr<nsIWritablePropertyBag2> props = do_QueryInterface(channel);
+ if (props) {
+ props->SetPropertyAsBool(NS_LITERAL_STRING("docshell.newWindowTarget"),
+ true);
+ }
+ }
+
+ nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(channel));
+ if (timedChannel) {
+ timedChannel->SetTimingEnabled(true);
+
+ nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
+ if (IsFrame() && win) {
+ nsCOMPtr<Element> frameElement = win->GetFrameElementInternal();
+ if (frameElement) {
+ timedChannel->SetInitiatorType(frameElement->LocalName());
+ }
+ }
+ }
+
+ rv = DoChannelLoad(channel, uriLoader, aBypassClassifier);
+
+ //
+ // If the channel load failed, we failed and nsIWebProgress just ain't
+ // gonna happen.
+ //
+ if (NS_SUCCEEDED(rv)) {
+ if (aDocShell) {
+ *aDocShell = this;
+ NS_ADDREF(*aDocShell);
+ }
+ }
+
+ return rv;
+}
+
+static nsresult
+AppendSegmentToString(nsIInputStream* aIn,
+ void* aClosure,
+ const char* aFromRawSegment,
+ uint32_t aToOffset,
+ uint32_t aCount,
+ uint32_t* aWriteCount)
+{
+ // aFromSegment now contains aCount bytes of data.
+
+ nsAutoCString* buf = static_cast<nsAutoCString*>(aClosure);
+ buf->Append(aFromRawSegment, aCount);
+
+ // Indicate that we have consumed all of aFromSegment
+ *aWriteCount = aCount;
+ return NS_OK;
+}
+
+nsresult
+nsDocShell::AddHeadersToChannel(nsIInputStream* aHeadersData,
+ nsIChannel* aGenericChannel)
+{
+ nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aGenericChannel);
+ NS_ENSURE_STATE(httpChannel);
+
+ uint32_t numRead;
+ nsAutoCString headersString;
+ nsresult rv = aHeadersData->ReadSegments(AppendSegmentToString,
+ &headersString,
+ UINT32_MAX,
+ &numRead);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // used during the manipulation of the String from the InputStream
+ nsAutoCString headerName;
+ nsAutoCString headerValue;
+ int32_t crlf;
+ int32_t colon;
+
+ //
+ // Iterate over the headersString: for each "\r\n" delimited chunk,
+ // add the value as a header to the nsIHttpChannel
+ //
+
+ static const char kWhitespace[] = "\b\t\r\n ";
+ while (true) {
+ crlf = headersString.Find("\r\n");
+ if (crlf == kNotFound) {
+ return NS_OK;
+ }
+
+ const nsCSubstring& oneHeader = StringHead(headersString, crlf);
+
+ colon = oneHeader.FindChar(':');
+ if (colon == kNotFound) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ headerName = StringHead(oneHeader, colon);
+ headerValue = Substring(oneHeader, colon + 1);
+
+ headerName.Trim(kWhitespace);
+ headerValue.Trim(kWhitespace);
+
+ headersString.Cut(0, crlf + 2);
+
+ //
+ // FINALLY: we can set the header!
+ //
+
+ rv = httpChannel->SetRequestHeader(headerName, headerValue, true);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ NS_NOTREACHED("oops");
+ return NS_ERROR_UNEXPECTED;
+}
+
+nsresult
+nsDocShell::DoChannelLoad(nsIChannel* aChannel,
+ nsIURILoader* aURILoader,
+ bool aBypassClassifier)
+{
+ nsresult rv;
+ // Mark the channel as being a document URI and allow content sniffing...
+ nsLoadFlags loadFlags = 0;
+ (void)aChannel->GetLoadFlags(&loadFlags);
+ loadFlags |= nsIChannel::LOAD_DOCUMENT_URI |
+ nsIChannel::LOAD_CALL_CONTENT_SNIFFERS;
+
+ // Load attributes depend on load type...
+ switch (mLoadType) {
+ case LOAD_HISTORY: {
+ // Only send VALIDATE_NEVER if mLSHE's URI was never changed via
+ // push/replaceState (bug 669671).
+ bool uriModified = false;
+ if (mLSHE) {
+ mLSHE->GetURIWasModified(&uriModified);
+ }
+
+ if (!uriModified) {
+ loadFlags |= nsIRequest::VALIDATE_NEVER;
+ }
+ break;
+ }
+
+ case LOAD_RELOAD_CHARSET_CHANGE: {
+ // Use SetAllowStaleCacheContent (not LOAD_FROM_CACHE flag) since we only want
+ // to force cache load for this channel, not the whole loadGroup.
+ nsCOMPtr<nsICacheInfoChannel> cachingChannel = do_QueryInterface(aChannel);
+ if (cachingChannel) {
+ cachingChannel->SetAllowStaleCacheContent(true);
+ }
+ break;
+ }
+
+ case LOAD_RELOAD_NORMAL:
+ case LOAD_REFRESH:
+ loadFlags |= nsIRequest::VALIDATE_ALWAYS;
+ break;
+
+ case LOAD_NORMAL_BYPASS_CACHE:
+ case LOAD_NORMAL_BYPASS_PROXY:
+ case LOAD_NORMAL_BYPASS_PROXY_AND_CACHE:
+ case LOAD_NORMAL_ALLOW_MIXED_CONTENT:
+ case LOAD_RELOAD_BYPASS_CACHE:
+ case LOAD_RELOAD_BYPASS_PROXY:
+ case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
+ case LOAD_RELOAD_ALLOW_MIXED_CONTENT:
+ case LOAD_REPLACE_BYPASS_CACHE:
+ loadFlags |= nsIRequest::LOAD_BYPASS_CACHE |
+ nsIRequest::LOAD_FRESH_CONNECTION;
+ break;
+
+ case LOAD_NORMAL:
+ case LOAD_LINK:
+ // Set cache checking flags
+ switch (Preferences::GetInt("browser.cache.check_doc_frequency", -1)) {
+ case 0:
+ loadFlags |= nsIRequest::VALIDATE_ONCE_PER_SESSION;
+ break;
+ case 1:
+ loadFlags |= nsIRequest::VALIDATE_ALWAYS;
+ break;
+ case 2:
+ loadFlags |= nsIRequest::VALIDATE_NEVER;
+ break;
+ }
+ break;
+ }
+
+ if (!aBypassClassifier) {
+ loadFlags |= nsIChannel::LOAD_CLASSIFY_URI;
+ }
+
+ // If the user pressed shift-reload, then do not allow ServiceWorker
+ // interception to occur. See step 12.1 of the SW HandleFetch algorithm.
+ if (IsForceReloadType(mLoadType)) {
+ loadFlags |= nsIChannel::LOAD_BYPASS_SERVICE_WORKER;
+ }
+
+ (void)aChannel->SetLoadFlags(loadFlags);
+
+ uint32_t openFlags = 0;
+ if (mLoadType == LOAD_LINK) {
+ openFlags |= nsIURILoader::IS_CONTENT_PREFERRED;
+ }
+ if (!mAllowContentRetargeting) {
+ openFlags |= nsIURILoader::DONT_RETARGET;
+ }
+ rv = aURILoader->OpenURI(aChannel, openFlags, this);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+nsresult
+nsDocShell::ScrollToAnchor(bool aCurHasRef, bool aNewHasRef,
+ nsACString& aNewHash, uint32_t aLoadType)
+{
+ if (!mCurrentURI) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIPresShell> shell = GetPresShell();
+ if (!shell) {
+ // If we failed to get the shell, or if there is no shell,
+ // nothing left to do here.
+ return NS_OK;
+ }
+
+ nsIScrollableFrame* rootScroll = shell->GetRootScrollFrameAsScrollable();
+ if (rootScroll) {
+ rootScroll->ClearDidHistoryRestore();
+ }
+
+ // If we have no new anchor, we do not want to scroll, unless there is a
+ // current anchor and we are doing a history load. So return if we have no
+ // new anchor, and there is no current anchor or the load is not a history
+ // load.
+ if ((!aCurHasRef || aLoadType != LOAD_HISTORY) && !aNewHasRef) {
+ return NS_OK;
+ }
+
+ // Both the new and current URIs refer to the same page. We can now
+ // browse to the hash stored in the new URI.
+
+ if (!aNewHash.IsEmpty()) {
+ // anchor is there, but if it's a load from history,
+ // we don't have any anchor jumping to do
+ bool scroll = aLoadType != LOAD_HISTORY &&
+ aLoadType != LOAD_RELOAD_NORMAL;
+
+ char* str = ToNewCString(aNewHash);
+ if (!str) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // nsUnescape modifies the string that is passed into it.
+ nsUnescape(str);
+
+ // We assume that the bytes are in UTF-8, as it says in the
+ // spec:
+ // http://www.w3.org/TR/html4/appendix/notes.html#h-B.2.1
+
+ // We try the UTF-8 string first, and then try the document's
+ // charset (see below). If the string is not UTF-8,
+ // conversion will fail and give us an empty Unicode string.
+ // In that case, we should just fall through to using the
+ // page's charset.
+ nsresult rv = NS_ERROR_FAILURE;
+ NS_ConvertUTF8toUTF16 uStr(str);
+ if (!uStr.IsEmpty()) {
+ rv = shell->GoToAnchor(NS_ConvertUTF8toUTF16(str), scroll,
+ nsIPresShell::SCROLL_SMOOTH_AUTO);
+ }
+ free(str);
+
+ // Above will fail if the anchor name is not UTF-8. Need to
+ // convert from document charset to unicode.
+ if (NS_FAILED(rv)) {
+ // Get a document charset
+ NS_ENSURE_TRUE(mContentViewer, NS_ERROR_FAILURE);
+ nsIDocument* doc = mContentViewer->GetDocument();
+ NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
+ const nsACString& aCharset = doc->GetDocumentCharacterSet();
+
+ nsCOMPtr<nsITextToSubURI> textToSubURI =
+ do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Unescape and convert to unicode
+ nsXPIDLString uStr;
+
+ rv = textToSubURI->UnEscapeAndConvert(PromiseFlatCString(aCharset).get(),
+ PromiseFlatCString(aNewHash).get(),
+ getter_Copies(uStr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Ignore return value of GoToAnchor, since it will return an error
+ // if there is no such anchor in the document, which is actually a
+ // success condition for us (we want to update the session history
+ // with the new URI no matter whether we actually scrolled
+ // somewhere).
+ //
+ // When aNewHash contains "%00", unescaped string may be empty.
+ // And GoToAnchor asserts if we ask it to scroll to an empty ref.
+ shell->GoToAnchor(uStr, scroll && !uStr.IsEmpty(),
+ nsIPresShell::SCROLL_SMOOTH_AUTO);
+ }
+ } else {
+ // Tell the shell it's at an anchor, without scrolling.
+ shell->GoToAnchor(EmptyString(), false);
+
+ // An empty anchor was found, but if it's a load from history,
+ // we don't have to jump to the top of the page. Scrollbar
+ // position will be restored by the caller, based on positions
+ // stored in session history.
+ if (aLoadType == LOAD_HISTORY || aLoadType == LOAD_RELOAD_NORMAL) {
+ return NS_OK;
+ }
+ // An empty anchor. Scroll to the top of the page. Ignore the
+ // return value; failure to scroll here (e.g. if there is no
+ // root scrollframe) is not grounds for canceling the load!
+ SetCurScrollPosEx(0, 0);
+ }
+
+ return NS_OK;
+}
+
+void
+nsDocShell::SetupReferrerFromChannel(nsIChannel* aChannel)
+{
+ nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
+ if (httpChannel) {
+ nsCOMPtr<nsIURI> referrer;
+ nsresult rv = httpChannel->GetReferrer(getter_AddRefs(referrer));
+ if (NS_SUCCEEDED(rv)) {
+ SetReferrerURI(referrer);
+ }
+ uint32_t referrerPolicy;
+ rv = httpChannel->GetReferrerPolicy(&referrerPolicy);
+ if (NS_SUCCEEDED(rv)) {
+ SetReferrerPolicy(referrerPolicy);
+ }
+ }
+}
+
+bool
+nsDocShell::OnNewURI(nsIURI* aURI, nsIChannel* aChannel,
+ nsIPrincipal* aTriggeringPrincipal,
+ nsIPrincipal* aPrincipalToInherit,
+ uint32_t aLoadType, bool aFireOnLocationChange,
+ bool aAddToGlobalHistory, bool aCloneSHChildren)
+{
+ NS_PRECONDITION(aURI, "uri is null");
+ NS_PRECONDITION(!aChannel || !aTriggeringPrincipal, "Shouldn't have both set");
+
+ MOZ_ASSERT(!aPrincipalToInherit || (aPrincipalToInherit && aTriggeringPrincipal));
+
+#if defined(DEBUG)
+ if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) {
+ nsAutoCString chanName;
+ if (aChannel) {
+ aChannel->GetName(chanName);
+ } else {
+ chanName.AssignLiteral("<no channel>");
+ }
+
+ MOZ_LOG(gDocShellLog, LogLevel::Debug,
+ ("nsDocShell[%p]::OnNewURI(\"%s\", [%s], 0x%x)\n",
+ this, aURI->GetSpecOrDefault().get(), chanName.get(), aLoadType));
+ }
+#endif
+
+ bool equalUri = false;
+
+ // Get the post data and the HTTP response code from the channel.
+ uint32_t responseStatus = 0;
+ nsCOMPtr<nsIInputStream> inputStream;
+ if (aChannel) {
+ nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
+
+ // Check if the HTTPChannel is hiding under a multiPartChannel
+ if (!httpChannel) {
+ GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
+ }
+
+ if (httpChannel) {
+ nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
+ if (uploadChannel) {
+ uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
+ }
+
+ // If the response status indicates an error, unlink this session
+ // history entry from any entries sharing its document.
+ nsresult rv = httpChannel->GetResponseStatus(&responseStatus);
+ if (mLSHE && NS_SUCCEEDED(rv) && responseStatus >= 400) {
+ mLSHE->AbandonBFCacheEntry();
+ }
+ }
+ }
+
+ // Determine if this type of load should update history.
+ bool updateGHistory = !(aLoadType == LOAD_BYPASS_HISTORY ||
+ aLoadType == LOAD_ERROR_PAGE ||
+ aLoadType & LOAD_CMD_HISTORY);
+
+ // We don't update session history on reload unless we're loading
+ // an iframe in shift-reload case.
+ bool updateSHistory = updateGHistory &&
+ (!(aLoadType & LOAD_CMD_RELOAD) ||
+ (IsForceReloadType(aLoadType) && IsFrame()));
+
+ // Create SH Entry (mLSHE) only if there is a SessionHistory object in the
+ // current frame or in the root docshell.
+ nsCOMPtr<nsISHistory> rootSH = mSessionHistory;
+ if (!rootSH) {
+ // Get the handle to SH from the root docshell
+ GetRootSessionHistory(getter_AddRefs(rootSH));
+ if (!rootSH) {
+ updateSHistory = false;
+ updateGHistory = false; // XXX Why global history too?
+ }
+ }
+
+ // Check if the url to be loaded is the same as the one already loaded.
+ if (mCurrentURI) {
+ aURI->Equals(mCurrentURI, &equalUri);
+ }
+
+#ifdef DEBUG
+ bool shAvailable = (rootSH != nullptr);
+
+ // XXX This log message is almost useless because |updateSHistory|
+ // and |updateGHistory| are not correct at this point.
+
+ MOZ_LOG(gDocShellLog, LogLevel::Debug,
+ (" shAvailable=%i updateSHistory=%i updateGHistory=%i"
+ " equalURI=%i\n",
+ shAvailable, updateSHistory, updateGHistory, equalUri));
+
+ if (shAvailable && mCurrentURI && !mOSHE && aLoadType != LOAD_ERROR_PAGE) {
+ NS_ASSERTION(NS_IsAboutBlank(mCurrentURI),
+ "no SHEntry for a non-transient viewer?");
+ }
+#endif
+
+ /* If the url to be loaded is the same as the one already there,
+ * and the original loadType is LOAD_NORMAL, LOAD_LINK, or
+ * LOAD_STOP_CONTENT, set loadType to LOAD_NORMAL_REPLACE so that
+ * AddToSessionHistory() won't mess with the current SHEntry and
+ * if this page has any frame children, it also will be handled
+ * properly. see bug 83684
+ *
+ * NB: If mOSHE is null but we have a current URI, then it means
+ * that we must be at the transient about:blank content viewer
+ * (asserted above) and we should let the normal load continue,
+ * since there's nothing to replace.
+ *
+ * XXX Hopefully changing the loadType at this time will not hurt
+ * anywhere. The other way to take care of sequentially repeating
+ * frameset pages is to add new methods to nsIDocShellTreeItem.
+ * Hopefully I don't have to do that.
+ */
+ if (equalUri &&
+ mOSHE &&
+ (mLoadType == LOAD_NORMAL ||
+ mLoadType == LOAD_LINK ||
+ mLoadType == LOAD_STOP_CONTENT) &&
+ !inputStream) {
+ mLoadType = LOAD_NORMAL_REPLACE;
+ }
+
+ // If this is a refresh to the currently loaded url, we don't
+ // have to update session or global history.
+ if (mLoadType == LOAD_REFRESH && !inputStream && equalUri) {
+ SetHistoryEntry(&mLSHE, mOSHE);
+ }
+
+ /* If the user pressed shift-reload, cache will create a new cache key
+ * for the page. Save the new cacheKey in Session History.
+ * see bug 90098
+ */
+ if (aChannel && IsForceReloadType(aLoadType)) {
+ MOZ_ASSERT(!updateSHistory || IsFrame(),
+ "We shouldn't be updating session history for forced"
+ " reloads unless we're in a newly created iframe!");
+
+ nsCOMPtr<nsICacheInfoChannel> cacheChannel(do_QueryInterface(aChannel));
+ nsCOMPtr<nsISupports> cacheKey;
+ // Get the Cache Key and store it in SH.
+ if (cacheChannel) {
+ cacheChannel->GetCacheKey(getter_AddRefs(cacheKey));
+ }
+ // If we already have a loading history entry, store the new cache key
+ // in it. Otherwise, since we're doing a reload and won't be updating
+ // our history entry, store the cache key in our current history entry.
+ if (mLSHE) {
+ mLSHE->SetCacheKey(cacheKey);
+ } else if (mOSHE) {
+ mOSHE->SetCacheKey(cacheKey);
+ }
+
+ // Since we're force-reloading, clear all the sub frame history.
+ ClearFrameHistory(mLSHE);
+ ClearFrameHistory(mOSHE);
+ }
+
+ if (aLoadType == LOAD_RELOAD_NORMAL) {
+ nsCOMPtr<nsISHEntry> currentSH;
+ bool oshe = false;
+ GetCurrentSHEntry(getter_AddRefs(currentSH), &oshe);
+ bool dynamicallyAddedChild = false;
+ if (currentSH) {
+ currentSH->HasDynamicallyAddedChild(&dynamicallyAddedChild);
+ }
+ if (dynamicallyAddedChild) {
+ ClearFrameHistory(currentSH);
+ }
+ }
+
+ if (aLoadType == LOAD_REFRESH) {
+ ClearFrameHistory(mLSHE);
+ ClearFrameHistory(mOSHE);
+ }
+
+ if (updateSHistory) {
+ // Update session history if necessary...
+ if (!mLSHE && (mItemType == typeContent) && mURIResultedInDocument) {
+ /* This is a fresh page getting loaded for the first time
+ *.Create a Entry for it and add it to SH, if this is the
+ * rootDocShell
+ */
+ (void)AddToSessionHistory(aURI, aChannel, aTriggeringPrincipal,
+ aPrincipalToInherit, aCloneSHChildren,
+ getter_AddRefs(mLSHE));
+ }
+ } else if (mSessionHistory && mLSHE && mURIResultedInDocument) {
+ // Even if we don't add anything to SHistory, ensure the current index
+ // points to the same SHEntry as our mLSHE.
+ int32_t index = 0;
+ mSessionHistory->GetRequestedIndex(&index);
+ if (index == -1) {
+ mSessionHistory->GetIndex(&index);
+ }
+ nsCOMPtr<nsISHEntry> currentSH;
+ mSessionHistory->GetEntryAtIndex(index, false, getter_AddRefs(currentSH));
+ if (currentSH != mLSHE) {
+ nsCOMPtr<nsISHistoryInternal> shPrivate =
+ do_QueryInterface(mSessionHistory);
+ shPrivate->ReplaceEntry(index, mLSHE);
+ }
+ }
+
+ // If this is a POST request, we do not want to include this in global
+ // history.
+ if (updateGHistory && aAddToGlobalHistory && !ChannelIsPost(aChannel)) {
+ nsCOMPtr<nsIURI> previousURI;
+ uint32_t previousFlags = 0;
+
+ if (aLoadType & LOAD_CMD_RELOAD) {
+ // On a reload request, we don't set redirecting flags.
+ previousURI = aURI;
+ } else {
+ ExtractLastVisit(aChannel, getter_AddRefs(previousURI), &previousFlags);
+ }
+
+ // Note: We don't use |referrer| when our global history is
+ // based on IHistory.
+ nsCOMPtr<nsIURI> referrer;
+ // Treat referrer as null if there is an error getting it.
+ (void)NS_GetReferrerFromChannel(aChannel, getter_AddRefs(referrer));
+
+ AddURIVisit(aURI, referrer, previousURI, previousFlags, responseStatus);
+ }
+
+ // If this was a history load or a refresh, or it was a history load but
+ // later changed to LOAD_NORMAL_REPLACE due to redirection, update the index
+ // in session history.
+ if (rootSH &&
+ ((mLoadType & (LOAD_CMD_HISTORY | LOAD_CMD_RELOAD)) ||
+ mLoadType == LOAD_NORMAL_REPLACE)) {
+ nsCOMPtr<nsISHistoryInternal> shInternal(do_QueryInterface(rootSH));
+ if (shInternal) {
+ rootSH->GetIndex(&mPreviousTransIndex);
+ shInternal->UpdateIndex();
+ rootSH->GetIndex(&mLoadedTransIndex);
+#ifdef DEBUG_PAGE_CACHE
+ printf("Previous index: %d, Loaded index: %d\n\n",
+ mPreviousTransIndex, mLoadedTransIndex);
+#endif
+ }
+ }
+
+ // aCloneSHChildren exactly means "we are not loading a new document".
+ uint32_t locationFlags =
+ aCloneSHChildren ? uint32_t(LOCATION_CHANGE_SAME_DOCUMENT) : 0;
+
+ bool onLocationChangeNeeded = SetCurrentURI(aURI, aChannel,
+ aFireOnLocationChange,
+ locationFlags);
+ // Make sure to store the referrer from the channel, if any
+ SetupReferrerFromChannel(aChannel);
+ return onLocationChangeNeeded;
+}
+
+bool
+nsDocShell::OnLoadingSite(nsIChannel* aChannel, bool aFireOnLocationChange,
+ bool aAddToGlobalHistory)
+{
+ nsCOMPtr<nsIURI> uri;
+ // If this a redirect, use the final url (uri)
+ // else use the original url
+ //
+ // Note that this should match what documents do (see nsDocument::Reset).
+ NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
+ NS_ENSURE_TRUE(uri, false);
+
+ // Pass false for aCloneSHChildren, since we're loading a new page here.
+ return OnNewURI(uri, aChannel, nullptr, nullptr, mLoadType, aFireOnLocationChange,
+ aAddToGlobalHistory, false);
+}
+
+void
+nsDocShell::SetReferrerURI(nsIURI* aURI)
+{
+ mReferrerURI = aURI; // This assigment addrefs
+}
+
+void
+nsDocShell::SetReferrerPolicy(uint32_t aReferrerPolicy)
+{
+ mReferrerPolicy = aReferrerPolicy;
+}
+
+//*****************************************************************************
+// nsDocShell: Session History
+//*****************************************************************************
+
+NS_IMETHODIMP
+nsDocShell::AddState(JS::Handle<JS::Value> aData, const nsAString& aTitle,
+ const nsAString& aURL, bool aReplace, JSContext* aCx)
+{
+ // Implements History.pushState and History.replaceState
+
+ // Here's what we do, roughly in the order specified by HTML5:
+ // 1. Serialize aData using structured clone.
+ // 2. If the third argument is present,
+ // a. Resolve the url, relative to the first script's base URL
+ // b. If (a) fails, raise a SECURITY_ERR
+ // c. Compare the resulting absolute URL to the document's address. If
+ // any part of the URLs difer other than the <path>, <query>, and
+ // <fragment> components, raise a SECURITY_ERR and abort.
+ // 3. If !aReplace:
+ // Remove from the session history all entries after the current entry,
+ // as we would after a regular navigation, and save the current
+ // entry's scroll position (bug 590573).
+ // 4. As apropriate, either add a state object entry to the session history
+ // after the current entry with the following properties, or modify the
+ // current session history entry to set
+ // a. cloned data as the state object,
+ // b. if the third argument was present, the absolute URL found in
+ // step 2
+ // Also clear the new history entry's POST data (see bug 580069).
+ // 5. If aReplace is false (i.e. we're doing a pushState instead of a
+ // replaceState), notify bfcache that we've navigated to a new page.
+ // 6. If the third argument is present, set the document's current address
+ // to the absolute URL found in step 2.
+ //
+ // It's important that this function not run arbitrary scripts after step 1
+ // and before completing step 5. For example, if a script called
+ // history.back() before we completed step 5, bfcache might destroy an
+ // active content viewer. Since EvictOutOfRangeContentViewers at the end of
+ // step 5 might run script, we can't just put a script blocker around the
+ // critical section.
+ //
+ // Note that we completely ignore the aTitle parameter.
+
+ nsresult rv;
+
+ // Don't clobber the load type of an existing network load.
+ AutoRestore<uint32_t> loadTypeResetter(mLoadType);
+
+ // pushState effectively becomes replaceState when we've started a network
+ // load but haven't adopted its document yet. This mirrors what we do with
+ // changes to the hash at this stage of the game.
+ if (JustStartedNetworkLoad()) {
+ aReplace = true;
+ }
+
+ nsCOMPtr<nsIDocument> document = GetDocument();
+ NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
+
+ // Step 1: Serialize aData using structured clone.
+ nsCOMPtr<nsIStructuredCloneContainer> scContainer;
+
+ // scContainer->Init might cause arbitrary JS to run, and this code might
+ // navigate the page we're on, potentially to a different origin! (bug
+ // 634834) To protect against this, we abort if our principal changes due
+ // to the InitFromJSVal() call.
+ {
+ nsCOMPtr<nsIDocument> origDocument = GetDocument();
+ if (!origDocument) {
+ return NS_ERROR_DOM_SECURITY_ERR;
+ }
+ nsCOMPtr<nsIPrincipal> origPrincipal = origDocument->NodePrincipal();
+
+ scContainer = new nsStructuredCloneContainer();
+ rv = scContainer->InitFromJSVal(aData, aCx);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIDocument> newDocument = GetDocument();
+ if (!newDocument) {
+ return NS_ERROR_DOM_SECURITY_ERR;
+ }
+ nsCOMPtr<nsIPrincipal> newPrincipal = newDocument->NodePrincipal();
+
+ bool principalsEqual = false;
+ origPrincipal->Equals(newPrincipal, &principalsEqual);
+ NS_ENSURE_TRUE(principalsEqual, NS_ERROR_DOM_SECURITY_ERR);
+ }
+
+ // Check that the state object isn't too long.
+ // Default max length: 640k bytes.
+ int32_t maxStateObjSize =
+ Preferences::GetInt("browser.history.maxStateObjectSize", 0xA0000);
+ if (maxStateObjSize < 0) {
+ maxStateObjSize = 0;
+ }
+
+ uint64_t scSize;
+ rv = scContainer->GetSerializedNBytes(&scSize);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_ENSURE_TRUE(scSize <= (uint32_t)maxStateObjSize, NS_ERROR_ILLEGAL_VALUE);
+
+ // Step 2: Resolve aURL
+ bool equalURIs = true;
+ nsCOMPtr<nsIURI> currentURI;
+ if (sURIFixup && mCurrentURI) {
+ rv = sURIFixup->CreateExposableURI(mCurrentURI, getter_AddRefs(currentURI));
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else {
+ currentURI = mCurrentURI;
+ }
+ nsCOMPtr<nsIURI> oldURI = currentURI;
+ nsCOMPtr<nsIURI> newURI;
+ if (aURL.Length() == 0) {
+ newURI = currentURI;
+ } else {
+ // 2a: Resolve aURL relative to mURI
+
+ nsIURI* docBaseURI = document->GetDocBaseURI();
+ if (!docBaseURI) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsAutoCString spec;
+ docBaseURI->GetSpec(spec);
+
+ nsAutoCString charset;
+ rv = docBaseURI->GetOriginCharset(charset);
+ NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+
+ rv = NS_NewURI(getter_AddRefs(newURI), aURL, charset.get(), docBaseURI);
+
+ // 2b: If 2a fails, raise a SECURITY_ERR
+ if (NS_FAILED(rv)) {
+ return NS_ERROR_DOM_SECURITY_ERR;
+ }
+
+ // 2c: Same-origin check.
+ if (!nsContentUtils::URIIsLocalFile(newURI)) {
+ // In addition to checking that the security manager says that
+ // the new URI has the same origin as our current URI, we also
+ // check that the two URIs have the same userpass. (The
+ // security manager says that |http://foo.com| and
+ // |http://me@foo.com| have the same origin.) currentURI
+ // won't contain the password part of the userpass, so this
+ // means that it's never valid to specify a password in a
+ // pushState or replaceState URI.
+
+ nsCOMPtr<nsIScriptSecurityManager> secMan =
+ do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
+ NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE);
+
+ // It's very important that we check that newURI is of the same
+ // origin as currentURI, not docBaseURI, because a page can
+ // set docBaseURI arbitrarily to any domain.
+ nsAutoCString currentUserPass, newUserPass;
+ NS_ENSURE_SUCCESS(currentURI->GetUserPass(currentUserPass),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(newURI->GetUserPass(newUserPass), NS_ERROR_FAILURE);
+ if (NS_FAILED(secMan->CheckSameOriginURI(currentURI, newURI, true)) ||
+ !currentUserPass.Equals(newUserPass)) {
+ return NS_ERROR_DOM_SECURITY_ERR;
+ }
+ } else {
+ // It's a file:// URI
+ nsCOMPtr<nsIScriptObjectPrincipal> docScriptObj =
+ do_QueryInterface(document);
+
+ if (!docScriptObj) {
+ return NS_ERROR_DOM_SECURITY_ERR;
+ }
+
+ nsCOMPtr<nsIPrincipal> principal = docScriptObj->GetPrincipal();
+
+ if (!principal ||
+ NS_FAILED(principal->CheckMayLoad(newURI, true, false))) {
+ return NS_ERROR_DOM_SECURITY_ERR;
+ }
+ }
+
+ if (currentURI) {
+ currentURI->Equals(newURI, &equalURIs);
+ } else {
+ equalURIs = false;
+ }
+
+ } // end of same-origin check
+
+ // Step 3: Create a new entry in the session history. This will erase
+ // all SHEntries after the new entry and make this entry the current
+ // one. This operation may modify mOSHE, which we need later, so we
+ // keep a reference here.
+ NS_ENSURE_TRUE(mOSHE, NS_ERROR_FAILURE);
+ nsCOMPtr<nsISHEntry> oldOSHE = mOSHE;
+
+ mLoadType = LOAD_PUSHSTATE;
+
+ nsCOMPtr<nsISHEntry> newSHEntry;
+ if (!aReplace) {
+ // Save the current scroll position (bug 590573).
+ nscoord cx = 0, cy = 0;
+ GetCurScrollPos(ScrollOrientation_X, &cx);
+ GetCurScrollPos(ScrollOrientation_Y, &cy);
+ mOSHE->SetScrollPosition(cx, cy);
+
+ bool scrollRestorationIsManual = false;
+ mOSHE->GetScrollRestorationIsManual(&scrollRestorationIsManual);
+
+ // Since we're not changing which page we have loaded, pass
+ // true for aCloneChildren.
+ rv = AddToSessionHistory(newURI, nullptr, nullptr, nullptr, true,
+ getter_AddRefs(newSHEntry));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_ENSURE_TRUE(newSHEntry, NS_ERROR_FAILURE);
+
+ // Session history entries created by pushState inherit scroll restoration
+ // mode from the current entry.
+ newSHEntry->SetScrollRestorationIsManual(scrollRestorationIsManual);
+
+ // Link the new SHEntry to the old SHEntry's BFCache entry, since the
+ // two entries correspond to the same document.
+ NS_ENSURE_SUCCESS(newSHEntry->AdoptBFCacheEntry(oldOSHE), NS_ERROR_FAILURE);
+
+ // Set the new SHEntry's title (bug 655273).
+ nsString title;
+ mOSHE->GetTitle(getter_Copies(title));
+ newSHEntry->SetTitle(title);
+
+ // AddToSessionHistory may not modify mOSHE. In case it doesn't,
+ // we'll just set mOSHE here.
+ mOSHE = newSHEntry;
+
+ } else {
+ newSHEntry = mOSHE;
+ newSHEntry->SetURI(newURI);
+ newSHEntry->SetOriginalURI(newURI);
+ newSHEntry->SetLoadReplace(false);
+ }
+
+ // Step 4: Modify new/original session history entry and clear its POST
+ // data, if there is any.
+ newSHEntry->SetStateData(scContainer);
+ newSHEntry->SetPostData(nullptr);
+
+ // If this push/replaceState changed the document's current URI and the new
+ // URI differs from the old URI in more than the hash, or if the old
+ // SHEntry's URI was modified in this way by a push/replaceState call
+ // set URIWasModified to true for the current SHEntry (bug 669671).
+ bool sameExceptHashes = true, oldURIWasModified = false;
+ newURI->EqualsExceptRef(currentURI, &sameExceptHashes);
+ oldOSHE->GetURIWasModified(&oldURIWasModified);
+ newSHEntry->SetURIWasModified(!sameExceptHashes || oldURIWasModified);
+
+ // Step 5: If aReplace is false, indicating that we're doing a pushState
+ // rather than a replaceState, notify bfcache that we've added a page to
+ // the history so it can evict content viewers if appropriate. Otherwise
+ // call ReplaceEntry so that we notify nsIHistoryListeners that an entry
+ // was replaced.
+ nsCOMPtr<nsISHistory> rootSH;
+ GetRootSessionHistory(getter_AddRefs(rootSH));
+ NS_ENSURE_TRUE(rootSH, NS_ERROR_UNEXPECTED);
+
+ nsCOMPtr<nsISHistoryInternal> internalSH = do_QueryInterface(rootSH);
+ NS_ENSURE_TRUE(internalSH, NS_ERROR_UNEXPECTED);
+
+ if (!aReplace) {
+ int32_t curIndex = -1;
+ rv = rootSH->GetIndex(&curIndex);
+ if (NS_SUCCEEDED(rv) && curIndex > -1) {
+ internalSH->EvictOutOfRangeContentViewers(curIndex);
+ }
+ } else {
+ nsCOMPtr<nsISHEntry> rootSHEntry = GetRootSHEntry(newSHEntry);
+
+ int32_t index = -1;
+ rv = rootSH->GetIndexOfEntry(rootSHEntry, &index);
+ if (NS_SUCCEEDED(rv) && index > -1) {
+ internalSH->ReplaceEntry(index, rootSHEntry);
+ }
+ }
+
+ // Step 6: If the document's URI changed, update document's URI and update
+ // global history.
+ //
+ // We need to call FireOnLocationChange so that the browser's address bar
+ // gets updated and the back button is enabled, but we only need to
+ // explicitly call FireOnLocationChange if we're not calling SetCurrentURI,
+ // since SetCurrentURI will call FireOnLocationChange for us.
+ //
+ // Both SetCurrentURI(...) and FireDummyOnLocationChange() pass
+ // nullptr for aRequest param to FireOnLocationChange(...). Such an update
+ // notification is allowed only when we know docshell is not loading a new
+ // document and it requires LOCATION_CHANGE_SAME_DOCUMENT flag. Otherwise,
+ // FireOnLocationChange(...) breaks security UI.
+ if (!equalURIs) {
+ document->SetDocumentURI(newURI);
+ // We can't trust SetCurrentURI to do always fire locationchange events
+ // when we expect it to, so we hack around that by doing it ourselves...
+ SetCurrentURI(newURI, nullptr, false, LOCATION_CHANGE_SAME_DOCUMENT);
+ if (mLoadType != LOAD_ERROR_PAGE) {
+ FireDummyOnLocationChange();
+ }
+
+ AddURIVisit(newURI, oldURI, oldURI, 0);
+
+ // AddURIVisit doesn't set the title for the new URI in global history,
+ // so do that here.
+ if (mUseGlobalHistory && !UsePrivateBrowsing()) {
+ nsCOMPtr<IHistory> history = services::GetHistoryService();
+ if (history) {
+ history->SetURITitle(newURI, mTitle);
+ } else if (mGlobalHistory) {
+ mGlobalHistory->SetPageTitle(newURI, mTitle);
+ }
+ }
+
+ // Inform the favicon service that our old favicon applies to this new
+ // URI.
+ CopyFavicon(oldURI, newURI, document->NodePrincipal(), UsePrivateBrowsing());
+ } else {
+ FireDummyOnLocationChange();
+ }
+ document->SetStateObject(scContainer);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetCurrentScrollRestorationIsManual(bool* aIsManual)
+{
+ *aIsManual = false;
+ if (mOSHE) {
+ mOSHE->GetScrollRestorationIsManual(aIsManual);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetCurrentScrollRestorationIsManual(bool aIsManual)
+{
+ if (mOSHE) {
+ mOSHE->SetScrollRestorationIsManual(aIsManual);
+ }
+
+ return NS_OK;
+}
+
+bool
+nsDocShell::ShouldAddToSessionHistory(nsIURI* aURI)
+{
+ // I believe none of the about: urls should go in the history. But then
+ // that could just be me... If the intent is only deny about:blank then we
+ // should just do a spec compare, rather than two gets of the scheme and
+ // then the path. -Gagan
+ nsresult rv;
+ nsAutoCString buf;
+
+ rv = aURI->GetScheme(buf);
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+
+ if (buf.EqualsLiteral("about")) {
+ rv = aURI->GetPath(buf);
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+
+ if (buf.EqualsLiteral("blank") || buf.EqualsLiteral("newtab")) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+nsresult
+nsDocShell::AddToSessionHistory(nsIURI* aURI, nsIChannel* aChannel,
+ nsIPrincipal* aTriggeringPrincipal,
+ nsIPrincipal* aPrincipalToInherit,
+ bool aCloneChildren,
+ nsISHEntry** aNewEntry)
+{
+ NS_PRECONDITION(aURI, "uri is null");
+ NS_PRECONDITION(!aChannel || !aTriggeringPrincipal, "Shouldn't have both set");
+
+#if defined(DEBUG)
+ if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) {
+ nsAutoCString chanName;
+ if (aChannel) {
+ aChannel->GetName(chanName);
+ } else {
+ chanName.AssignLiteral("<no channel>");
+ }
+
+ MOZ_LOG(gDocShellLog, LogLevel::Debug,
+ ("nsDocShell[%p]::AddToSessionHistory(\"%s\", [%s])\n",
+ this, aURI->GetSpecOrDefault().get(), chanName.get()));
+ }
+#endif
+
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsISHEntry> entry;
+ bool shouldPersist;
+
+ shouldPersist = ShouldAddToSessionHistory(aURI);
+
+ // Get a handle to the root docshell
+ nsCOMPtr<nsIDocShellTreeItem> root;
+ GetSameTypeRootTreeItem(getter_AddRefs(root));
+ /*
+ * If this is a LOAD_FLAGS_REPLACE_HISTORY in a subframe, we use
+ * the existing SH entry in the page and replace the url and
+ * other vitalities.
+ */
+ if (LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY) &&
+ root != static_cast<nsIDocShellTreeItem*>(this)) {
+ // This is a subframe
+ entry = mOSHE;
+ nsCOMPtr<nsISHContainer> shContainer(do_QueryInterface(entry));
+ if (shContainer) {
+ int32_t childCount = 0;
+ shContainer->GetChildCount(&childCount);
+ // Remove all children of this entry
+ for (int32_t i = childCount - 1; i >= 0; i--) {
+ nsCOMPtr<nsISHEntry> child;
+ shContainer->GetChildAt(i, getter_AddRefs(child));
+ shContainer->RemoveChild(child);
+ }
+ entry->AbandonBFCacheEntry();
+ }
+ }
+
+ // Create a new entry if necessary.
+ if (!entry) {
+ entry = do_CreateInstance(NS_SHENTRY_CONTRACTID);
+
+ if (!entry) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+
+ // Get the post data & referrer
+ nsCOMPtr<nsIInputStream> inputStream;
+ nsCOMPtr<nsIURI> originalURI;
+ bool loadReplace = false;
+ nsCOMPtr<nsIURI> referrerURI;
+ uint32_t referrerPolicy = mozilla::net::RP_Default;
+ nsCOMPtr<nsISupports> cacheKey;
+ nsCOMPtr<nsIPrincipal> triggeringPrincipal = aTriggeringPrincipal;
+ nsCOMPtr<nsIPrincipal> principalToInherit = aPrincipalToInherit;
+ bool expired = false;
+ bool discardLayoutState = false;
+ nsCOMPtr<nsICacheInfoChannel> cacheChannel;
+ if (aChannel) {
+ cacheChannel = do_QueryInterface(aChannel);
+
+ /* If there is a caching channel, get the Cache Key and store it
+ * in SH.
+ */
+ if (cacheChannel) {
+ cacheChannel->GetCacheKey(getter_AddRefs(cacheKey));
+ }
+ nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
+
+ // Check if the httpChannel is hiding under a multipartChannel
+ if (!httpChannel) {
+ GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
+ }
+ if (httpChannel) {
+ nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
+ if (uploadChannel) {
+ uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
+ }
+ httpChannel->GetOriginalURI(getter_AddRefs(originalURI));
+ uint32_t loadFlags;
+ aChannel->GetLoadFlags(&loadFlags);
+ loadReplace = loadFlags & nsIChannel::LOAD_REPLACE;
+ httpChannel->GetReferrer(getter_AddRefs(referrerURI));
+ httpChannel->GetReferrerPolicy(&referrerPolicy);
+
+ discardLayoutState = ShouldDiscardLayoutState(httpChannel);
+ }
+
+ // XXX Bug 1286838: Replace channel owner with loadInfo triggeringPrincipal
+ nsCOMPtr<nsISupports> owner;
+ aChannel->GetOwner(getter_AddRefs(owner));
+ triggeringPrincipal = do_QueryInterface(owner);
+
+ nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
+ if (loadInfo) {
+ if (!triggeringPrincipal) {
+ triggeringPrincipal = loadInfo->TriggeringPrincipal();
+ }
+
+ // For now keep storing just the principal in the SHEntry.
+ if (!principalToInherit) {
+ if (loadInfo->GetLoadingSandboxed()) {
+ if (loadInfo->LoadingPrincipal()) {
+ principalToInherit = nsNullPrincipal::CreateWithInheritedAttributes(
+ loadInfo->LoadingPrincipal());
+ } else {
+ // get the OriginAttributes
+ NeckoOriginAttributes nAttrs;
+ loadInfo->GetOriginAttributes(&nAttrs);
+ PrincipalOriginAttributes pAttrs;
+ pAttrs.InheritFromNecko(nAttrs);
+ principalToInherit = nsNullPrincipal::Create(pAttrs);
+ }
+ } else {
+ principalToInherit = loadInfo->PrincipalToInherit();
+ }
+ }
+ }
+ }
+
+ // Title is set in nsDocShell::SetTitle()
+ entry->Create(aURI, // uri
+ EmptyString(), // Title
+ inputStream, // Post data stream
+ nullptr, // LayoutHistory state
+ cacheKey, // CacheKey
+ mContentTypeHint, // Content-type
+ triggeringPrincipal, // Channel or provided principal
+ principalToInherit,
+ mHistoryID,
+ mDynamicallyCreated);
+
+ entry->SetOriginalURI(originalURI);
+ entry->SetLoadReplace(loadReplace);
+ entry->SetReferrerURI(referrerURI);
+ entry->SetReferrerPolicy(referrerPolicy);
+ nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(aChannel);
+ if (inStrmChan) {
+ bool isSrcdocChannel;
+ inStrmChan->GetIsSrcdocChannel(&isSrcdocChannel);
+ if (isSrcdocChannel) {
+ nsAutoString srcdoc;
+ inStrmChan->GetSrcdocData(srcdoc);
+ entry->SetSrcdocData(srcdoc);
+ nsCOMPtr<nsIURI> baseURI;
+ inStrmChan->GetBaseURI(getter_AddRefs(baseURI));
+ entry->SetBaseURI(baseURI);
+ }
+ }
+ /* If cache got a 'no-store', ask SH not to store
+ * HistoryLayoutState. By default, SH will set this
+ * flag to true and save HistoryLayoutState.
+ */
+ if (discardLayoutState) {
+ entry->SetSaveLayoutStateFlag(false);
+ }
+ if (cacheChannel) {
+ // Check if the page has expired from cache
+ uint32_t expTime = 0;
+ cacheChannel->GetCacheTokenExpirationTime(&expTime);
+ uint32_t now = PRTimeToSeconds(PR_Now());
+ if (expTime <= now) {
+ expired = true;
+ }
+ }
+ if (expired) {
+ entry->SetExpirationStatus(true);
+ }
+
+ if (root == static_cast<nsIDocShellTreeItem*>(this) && mSessionHistory) {
+ // If we need to clone our children onto the new session
+ // history entry, do so now.
+ if (aCloneChildren && mOSHE) {
+ uint32_t cloneID;
+ mOSHE->GetID(&cloneID);
+ nsCOMPtr<nsISHEntry> newEntry;
+ CloneAndReplace(mOSHE, this, cloneID, entry, true,
+ getter_AddRefs(newEntry));
+ NS_ASSERTION(entry == newEntry,
+ "The new session history should be in the new entry");
+ }
+
+ // This is the root docshell
+ bool addToSHistory = !LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY);
+ if (!addToSHistory) {
+ // Replace current entry in session history; If the requested index is
+ // valid, it indicates the loading was triggered by a history load, and
+ // we should replace the entry at requested index instead.
+ int32_t index = 0;
+ mSessionHistory->GetRequestedIndex(&index);
+ if (index == -1) {
+ mSessionHistory->GetIndex(&index);
+ }
+ nsCOMPtr<nsISHistoryInternal> shPrivate =
+ do_QueryInterface(mSessionHistory);
+ // Replace the current entry with the new entry
+ if (index >= 0) {
+ if (shPrivate) {
+ rv = shPrivate->ReplaceEntry(index, entry);
+ }
+ } else {
+ // If we're trying to replace an inexistant shistory entry, append.
+ addToSHistory = true;
+ }
+ }
+
+ if (addToSHistory) {
+ // Add to session history
+ nsCOMPtr<nsISHistoryInternal> shPrivate =
+ do_QueryInterface(mSessionHistory);
+ NS_ENSURE_TRUE(shPrivate, NS_ERROR_FAILURE);
+ mSessionHistory->GetIndex(&mPreviousTransIndex);
+ rv = shPrivate->AddEntry(entry, shouldPersist);
+ mSessionHistory->GetIndex(&mLoadedTransIndex);
+#ifdef DEBUG_PAGE_CACHE
+ printf("Previous index: %d, Loaded index: %d\n\n",
+ mPreviousTransIndex, mLoadedTransIndex);
+#endif
+ }
+ } else {
+ // This is a subframe.
+ if (!mOSHE || !LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
+ rv = AddChildSHEntryToParent(entry, mChildOffset, aCloneChildren);
+ }
+ }
+
+ // Return the new SH entry...
+ if (aNewEntry) {
+ *aNewEntry = nullptr;
+ if (NS_SUCCEEDED(rv)) {
+ entry.forget(aNewEntry);
+ }
+ }
+
+ return rv;
+}
+
+nsresult
+nsDocShell::LoadHistoryEntry(nsISHEntry* aEntry, uint32_t aLoadType)
+{
+ if (!IsNavigationAllowed()) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIURI> uri;
+ nsCOMPtr<nsIURI> originalURI;
+ bool loadReplace = false;
+ nsCOMPtr<nsIInputStream> postData;
+ nsCOMPtr<nsIURI> referrerURI;
+ uint32_t referrerPolicy;
+ nsAutoCString contentType;
+ nsCOMPtr<nsIPrincipal> triggeringPrincipal;
+ nsCOMPtr<nsIPrincipal> principalToInherit;
+
+ NS_ENSURE_TRUE(aEntry, NS_ERROR_FAILURE);
+
+ NS_ENSURE_SUCCESS(aEntry->GetURI(getter_AddRefs(uri)), NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(aEntry->GetOriginalURI(getter_AddRefs(originalURI)),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(aEntry->GetLoadReplace(&loadReplace),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(aEntry->GetReferrerURI(getter_AddRefs(referrerURI)),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(aEntry->GetReferrerPolicy(&referrerPolicy),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(aEntry->GetPostData(getter_AddRefs(postData)),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(aEntry->GetContentType(contentType), NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(aEntry->GetTriggeringPrincipal(getter_AddRefs(triggeringPrincipal)),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(aEntry->GetPrincipalToInherit(getter_AddRefs(principalToInherit)),
+ NS_ERROR_FAILURE);
+
+ // Calling CreateAboutBlankContentViewer can set mOSHE to null, and if
+ // that's the only thing holding a ref to aEntry that will cause aEntry to
+ // die while we're loading it. So hold a strong ref to aEntry here, just
+ // in case.
+ nsCOMPtr<nsISHEntry> kungFuDeathGrip(aEntry);
+ bool isJS;
+ nsresult rv = uri->SchemeIs("javascript", &isJS);
+ if (NS_FAILED(rv) || isJS) {
+ // We're loading a URL that will execute script from inside asyncOpen.
+ // Replace the current document with about:blank now to prevent
+ // anything from the current document from leaking into any JavaScript
+ // code in the URL.
+ // Don't cache the presentation if we're going to just reload the
+ // current entry. Caching would lead to trying to save the different
+ // content viewers in the same nsISHEntry object.
+ rv = CreateAboutBlankContentViewer(principalToInherit, nullptr,
+ aEntry != mOSHE);
+
+ if (NS_FAILED(rv)) {
+ // The creation of the intermittent about:blank content
+ // viewer failed for some reason (potentially because the
+ // user prevented it). Interrupt the history load.
+ return NS_OK;
+ }
+
+ if (!triggeringPrincipal) {
+ // Ensure that we have a triggeringPrincipal. Otherwise javascript:
+ // URIs will pick it up from the about:blank page we just loaded,
+ // and we don't really want even that in this case.
+ triggeringPrincipal = nsNullPrincipal::CreateWithInheritedAttributes(this);
+ }
+ }
+
+ /* If there is a valid postdata *and* the user pressed
+ * reload or shift-reload, take user's permission before we
+ * repost the data to the server.
+ */
+ if ((aLoadType & LOAD_CMD_RELOAD) && postData) {
+ bool repost;
+ rv = ConfirmRepost(&repost);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // If the user pressed cancel in the dialog, return. We're done here.
+ if (!repost) {
+ return NS_BINDING_ABORTED;
+ }
+ }
+
+ // Do not inherit principal from document (security-critical!);
+ uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
+
+ nsAutoString srcdoc;
+ bool isSrcdoc;
+ nsCOMPtr<nsIURI> baseURI;
+ aEntry->GetIsSrcdocEntry(&isSrcdoc);
+ if (isSrcdoc) {
+ aEntry->GetSrcdocData(srcdoc);
+ aEntry->GetBaseURI(getter_AddRefs(baseURI));
+ flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC;
+ } else {
+ srcdoc = NullString();
+ }
+
+ // If there is no triggeringPrincipal we can fall back to using the
+ // SystemPrincipal as the triggeringPrincipal for loading the history
+ // entry, since the history entry can only end up in history if security
+ // checks passed in the initial loading phase.
+ if (!triggeringPrincipal) {
+ triggeringPrincipal = nsContentUtils::GetSystemPrincipal();
+ }
+
+ // Passing nullptr as aSourceDocShell gives the same behaviour as before
+ // aSourceDocShell was introduced. According to spec we should be passing
+ // the source browsing context that was used when the history entry was
+ // first created. bug 947716 has been created to address this issue.
+ rv = InternalLoad(uri,
+ originalURI,
+ loadReplace,
+ referrerURI,
+ referrerPolicy,
+ triggeringPrincipal,
+ principalToInherit,
+ flags,
+ EmptyString(), // No window target
+ contentType.get(), // Type hint
+ NullString(), // No forced file download
+ postData, // Post data stream
+ nullptr, // No headers stream
+ aLoadType, // Load type
+ aEntry, // SHEntry
+ true,
+ srcdoc,
+ nullptr, // Source docshell, see comment above
+ baseURI,
+ nullptr, // No nsIDocShell
+ nullptr); // No nsIRequest
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetShouldSaveLayoutState(bool* aShould)
+{
+ *aShould = false;
+ if (mOSHE) {
+ // Don't capture historystate and save it in history
+ // if the page asked not to do so.
+ mOSHE->GetSaveLayoutStateFlag(aShould);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsDocShell::PersistLayoutHistoryState()
+{
+ nsresult rv = NS_OK;
+
+ if (mOSHE) {
+ bool scrollRestorationIsManual = false;
+ mOSHE->GetScrollRestorationIsManual(&scrollRestorationIsManual);
+
+ nsCOMPtr<nsIPresShell> shell = GetPresShell();
+ nsCOMPtr<nsILayoutHistoryState> layoutState;
+ if (shell) {
+ rv = shell->CaptureHistoryState(getter_AddRefs(layoutState));
+ } else if (scrollRestorationIsManual) {
+ // Even if we don't have layout anymore, we may want to reset the current
+ // scroll state in layout history.
+ GetLayoutHistoryState(getter_AddRefs(layoutState));
+ }
+
+ if (scrollRestorationIsManual && layoutState) {
+ layoutState->ResetScrollState();
+ }
+ }
+
+ return rv;
+}
+
+/* static */ nsresult
+nsDocShell::WalkHistoryEntries(nsISHEntry* aRootEntry,
+ nsDocShell* aRootShell,
+ WalkHistoryEntriesFunc aCallback,
+ void* aData)
+{
+ NS_ENSURE_TRUE(aRootEntry, NS_ERROR_FAILURE);
+
+ nsCOMPtr<nsISHContainer> container(do_QueryInterface(aRootEntry));
+ if (!container) {
+ return NS_ERROR_FAILURE;
+ }
+
+ int32_t childCount;
+ container->GetChildCount(&childCount);
+ for (int32_t i = 0; i < childCount; i++) {
+ nsCOMPtr<nsISHEntry> childEntry;
+ container->GetChildAt(i, getter_AddRefs(childEntry));
+ if (!childEntry) {
+ // childEntry can be null for valid reasons, for example if the
+ // docshell at index i never loaded anything useful.
+ // Remember to clone also nulls in the child array (bug 464064).
+ aCallback(nullptr, nullptr, i, aData);
+ continue;
+ }
+
+ nsDocShell* childShell = nullptr;
+ if (aRootShell) {
+ // Walk the children of aRootShell and see if one of them
+ // has srcChild as a SHEntry.
+ nsTObserverArray<nsDocLoader*>::ForwardIterator iter(
+ aRootShell->mChildList);
+ while (iter.HasMore()) {
+ nsDocShell* child = static_cast<nsDocShell*>(iter.GetNext());
+
+ if (child->HasHistoryEntry(childEntry)) {
+ childShell = child;
+ break;
+ }
+ }
+ }
+ nsresult rv = aCallback(childEntry, childShell, i, aData);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+}
+
+// callback data for WalkHistoryEntries
+struct MOZ_STACK_CLASS CloneAndReplaceData
+{
+ CloneAndReplaceData(uint32_t aCloneID, nsISHEntry* aReplaceEntry,
+ bool aCloneChildren, nsISHEntry* aDestTreeParent)
+ : cloneID(aCloneID)
+ , cloneChildren(aCloneChildren)
+ , replaceEntry(aReplaceEntry)
+ , destTreeParent(aDestTreeParent)
+ {
+ }
+
+ uint32_t cloneID;
+ bool cloneChildren;
+ nsISHEntry* replaceEntry;
+ nsISHEntry* destTreeParent;
+ nsCOMPtr<nsISHEntry> resultEntry;
+};
+
+/* static */ nsresult
+nsDocShell::CloneAndReplaceChild(nsISHEntry* aEntry, nsDocShell* aShell,
+ int32_t aEntryIndex, void* aData)
+{
+ nsCOMPtr<nsISHEntry> dest;
+
+ CloneAndReplaceData* data = static_cast<CloneAndReplaceData*>(aData);
+ uint32_t cloneID = data->cloneID;
+ nsISHEntry* replaceEntry = data->replaceEntry;
+
+ nsCOMPtr<nsISHContainer> container = do_QueryInterface(data->destTreeParent);
+ if (!aEntry) {
+ if (container) {
+ container->AddChild(nullptr, aEntryIndex);
+ }
+ return NS_OK;
+ }
+
+ uint32_t srcID;
+ aEntry->GetID(&srcID);
+
+ nsresult rv = NS_OK;
+ if (srcID == cloneID) {
+ // Replace the entry
+ dest = replaceEntry;
+ } else {
+ // Clone the SHEntry...
+ rv = aEntry->Clone(getter_AddRefs(dest));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ dest->SetIsSubFrame(true);
+
+ if (srcID != cloneID || data->cloneChildren) {
+ // Walk the children
+ CloneAndReplaceData childData(cloneID, replaceEntry,
+ data->cloneChildren, dest);
+ rv = WalkHistoryEntries(aEntry, aShell,
+ CloneAndReplaceChild, &childData);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ if (srcID != cloneID && aShell) {
+ aShell->SwapHistoryEntries(aEntry, dest);
+ }
+
+ if (container) {
+ container->AddChild(dest, aEntryIndex);
+ }
+
+ data->resultEntry = dest;
+ return rv;
+}
+
+/* static */ nsresult
+nsDocShell::CloneAndReplace(nsISHEntry* aSrcEntry,
+ nsDocShell* aSrcShell,
+ uint32_t aCloneID,
+ nsISHEntry* aReplaceEntry,
+ bool aCloneChildren,
+ nsISHEntry** aResultEntry)
+{
+ NS_ENSURE_ARG_POINTER(aResultEntry);
+ NS_ENSURE_TRUE(aReplaceEntry, NS_ERROR_FAILURE);
+
+ CloneAndReplaceData data(aCloneID, aReplaceEntry, aCloneChildren, nullptr);
+ nsresult rv = CloneAndReplaceChild(aSrcEntry, aSrcShell, 0, &data);
+
+ data.resultEntry.swap(*aResultEntry);
+ return rv;
+}
+
+void
+nsDocShell::SwapHistoryEntries(nsISHEntry* aOldEntry, nsISHEntry* aNewEntry)
+{
+ if (aOldEntry == mOSHE) {
+ mOSHE = aNewEntry;
+ }
+
+ if (aOldEntry == mLSHE) {
+ mLSHE = aNewEntry;
+ }
+}
+
+struct SwapEntriesData
+{
+ nsDocShell* ignoreShell; // constant; the shell to ignore
+ nsISHEntry* destTreeRoot; // constant; the root of the dest tree
+ nsISHEntry* destTreeParent; // constant; the node under destTreeRoot
+ // whose children will correspond to aEntry
+};
+
+nsresult
+nsDocShell::SetChildHistoryEntry(nsISHEntry* aEntry, nsDocShell* aShell,
+ int32_t aEntryIndex, void* aData)
+{
+ SwapEntriesData* data = static_cast<SwapEntriesData*>(aData);
+ nsDocShell* ignoreShell = data->ignoreShell;
+
+ if (!aShell || aShell == ignoreShell) {
+ return NS_OK;
+ }
+
+ nsISHEntry* destTreeRoot = data->destTreeRoot;
+
+ nsCOMPtr<nsISHEntry> destEntry;
+ nsCOMPtr<nsISHContainer> container = do_QueryInterface(data->destTreeParent);
+
+ if (container) {
+ // aEntry is a clone of some child of destTreeParent, but since the
+ // trees aren't necessarily in sync, we'll have to locate it.
+ // Note that we could set aShell's entry to null if we don't find a
+ // corresponding entry under destTreeParent.
+
+ uint32_t targetID, id;
+ aEntry->GetID(&targetID);
+
+ // First look at the given index, since this is the common case.
+ nsCOMPtr<nsISHEntry> entry;
+ container->GetChildAt(aEntryIndex, getter_AddRefs(entry));
+ if (entry && NS_SUCCEEDED(entry->GetID(&id)) && id == targetID) {
+ destEntry.swap(entry);
+ } else {
+ int32_t childCount;
+ container->GetChildCount(&childCount);
+ for (int32_t i = 0; i < childCount; ++i) {
+ container->GetChildAt(i, getter_AddRefs(entry));
+ if (!entry) {
+ continue;
+ }
+
+ entry->GetID(&id);
+ if (id == targetID) {
+ destEntry.swap(entry);
+ break;
+ }
+ }
+ }
+ } else {
+ destEntry = destTreeRoot;
+ }
+
+ aShell->SwapHistoryEntries(aEntry, destEntry);
+
+ // Now handle the children of aEntry.
+ SwapEntriesData childData = { ignoreShell, destTreeRoot, destEntry };
+ return WalkHistoryEntries(aEntry, aShell, SetChildHistoryEntry, &childData);
+}
+
+static nsISHEntry*
+GetRootSHEntry(nsISHEntry* aEntry)
+{
+ nsCOMPtr<nsISHEntry> rootEntry = aEntry;
+ nsISHEntry* result = nullptr;
+ while (rootEntry) {
+ result = rootEntry;
+ result->GetParent(getter_AddRefs(rootEntry));
+ }
+
+ return result;
+}
+
+void
+nsDocShell::SetHistoryEntry(nsCOMPtr<nsISHEntry>* aPtr, nsISHEntry* aEntry)
+{
+ // We need to sync up the docshell and session history trees for
+ // subframe navigation. If the load was in a subframe, we forward up to
+ // the root docshell, which will then recursively sync up all docshells
+ // to their corresponding entries in the new session history tree.
+ // If we don't do this, then we can cache a content viewer on the wrong
+ // cloned entry, and subsequently restore it at the wrong time.
+
+ nsISHEntry* newRootEntry = GetRootSHEntry(aEntry);
+ if (newRootEntry) {
+ // newRootEntry is now the new root entry.
+ // Find the old root entry as well.
+
+ // Need a strong ref. on |oldRootEntry| so it isn't destroyed when
+ // SetChildHistoryEntry() does SwapHistoryEntries() (bug 304639).
+ nsCOMPtr<nsISHEntry> oldRootEntry = GetRootSHEntry(*aPtr);
+ if (oldRootEntry) {
+ nsCOMPtr<nsIDocShellTreeItem> rootAsItem;
+ GetSameTypeRootTreeItem(getter_AddRefs(rootAsItem));
+ nsCOMPtr<nsIDocShell> rootShell = do_QueryInterface(rootAsItem);
+ if (rootShell) { // if we're the root just set it, nothing to swap
+ SwapEntriesData data = { this, newRootEntry };
+ nsIDocShell* rootIDocShell = static_cast<nsIDocShell*>(rootShell);
+ nsDocShell* rootDocShell = static_cast<nsDocShell*>(rootIDocShell);
+
+#ifdef DEBUG
+ nsresult rv =
+#endif
+ SetChildHistoryEntry(oldRootEntry, rootDocShell, 0, &data);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "SetChildHistoryEntry failed");
+ }
+ }
+ }
+
+ *aPtr = aEntry;
+}
+
+nsresult
+nsDocShell::GetRootSessionHistory(nsISHistory** aReturn)
+{
+ nsresult rv;
+
+ nsCOMPtr<nsIDocShellTreeItem> root;
+ // Get the root docshell
+ rv = GetSameTypeRootTreeItem(getter_AddRefs(root));
+ // QI to nsIWebNavigation
+ nsCOMPtr<nsIWebNavigation> rootAsWebnav(do_QueryInterface(root));
+ if (rootAsWebnav) {
+ // Get the handle to SH from the root docshell
+ rv = rootAsWebnav->GetSessionHistory(aReturn);
+ }
+ return rv;
+}
+
+nsresult
+nsDocShell::GetHttpChannel(nsIChannel* aChannel, nsIHttpChannel** aReturn)
+{
+ NS_ENSURE_ARG_POINTER(aReturn);
+ if (!aChannel) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(aChannel));
+ if (multiPartChannel) {
+ nsCOMPtr<nsIChannel> baseChannel;
+ multiPartChannel->GetBaseChannel(getter_AddRefs(baseChannel));
+ nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(baseChannel));
+ *aReturn = httpChannel;
+ NS_IF_ADDREF(*aReturn);
+ }
+ return NS_OK;
+}
+
+bool
+nsDocShell::ShouldDiscardLayoutState(nsIHttpChannel* aChannel)
+{
+ // By default layout State will be saved.
+ if (!aChannel) {
+ return false;
+ }
+
+ // figure out if SH should be saving layout state
+ bool noStore = false;
+ aChannel->IsNoStoreResponse(&noStore);
+ return noStore;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetEditor(nsIEditor** aEditor)
+{
+ NS_ENSURE_ARG_POINTER(aEditor);
+
+ if (!mEditorData) {
+ *aEditor = nullptr;
+ return NS_OK;
+ }
+
+ return mEditorData->GetEditor(aEditor);
+}
+
+NS_IMETHODIMP
+nsDocShell::SetEditor(nsIEditor* aEditor)
+{
+ nsresult rv = EnsureEditorData();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ return mEditorData->SetEditor(aEditor);
+}
+
+NS_IMETHODIMP
+nsDocShell::GetEditable(bool* aEditable)
+{
+ NS_ENSURE_ARG_POINTER(aEditable);
+ *aEditable = mEditorData && mEditorData->GetEditable();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetHasEditingSession(bool* aHasEditingSession)
+{
+ NS_ENSURE_ARG_POINTER(aHasEditingSession);
+
+ if (mEditorData) {
+ nsCOMPtr<nsIEditingSession> editingSession;
+ mEditorData->GetEditingSession(getter_AddRefs(editingSession));
+ *aHasEditingSession = (editingSession.get() != nullptr);
+ } else {
+ *aHasEditingSession = false;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::MakeEditable(bool aInWaitForUriLoad)
+{
+ nsresult rv = EnsureEditorData();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ return mEditorData->MakeEditable(aInWaitForUriLoad);
+}
+
+bool
+nsDocShell::ChannelIsPost(nsIChannel* aChannel)
+{
+ nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
+ if (!httpChannel) {
+ return false;
+ }
+
+ nsAutoCString method;
+ httpChannel->GetRequestMethod(method);
+ return method.EqualsLiteral("POST");
+}
+
+void
+nsDocShell::ExtractLastVisit(nsIChannel* aChannel,
+ nsIURI** aURI,
+ uint32_t* aChannelRedirectFlags)
+{
+ nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(aChannel));
+ if (!props) {
+ return;
+ }
+
+ nsresult rv = props->GetPropertyAsInterface(
+ NS_LITERAL_STRING("docshell.previousURI"),
+ NS_GET_IID(nsIURI),
+ reinterpret_cast<void**>(aURI));
+
+ if (NS_FAILED(rv)) {
+ // There is no last visit for this channel, so this must be the first
+ // link. Link the visit to the referrer of this request, if any.
+ // Treat referrer as null if there is an error getting it.
+ (void)NS_GetReferrerFromChannel(aChannel, aURI);
+ } else {
+ rv = props->GetPropertyAsUint32(NS_LITERAL_STRING("docshell.previousFlags"),
+ aChannelRedirectFlags);
+
+ NS_WARNING_ASSERTION(
+ NS_SUCCEEDED(rv),
+ "Could not fetch previous flags, URI will be treated like referrer");
+ }
+}
+
+void
+nsDocShell::SaveLastVisit(nsIChannel* aChannel,
+ nsIURI* aURI,
+ uint32_t aChannelRedirectFlags)
+{
+ nsCOMPtr<nsIWritablePropertyBag2> props(do_QueryInterface(aChannel));
+ if (!props || !aURI) {
+ return;
+ }
+
+ props->SetPropertyAsInterface(NS_LITERAL_STRING("docshell.previousURI"),
+ aURI);
+ props->SetPropertyAsUint32(NS_LITERAL_STRING("docshell.previousFlags"),
+ aChannelRedirectFlags);
+}
+
+void
+nsDocShell::AddURIVisit(nsIURI* aURI,
+ nsIURI* aReferrerURI,
+ nsIURI* aPreviousURI,
+ uint32_t aChannelRedirectFlags,
+ uint32_t aResponseStatus)
+{
+ MOZ_ASSERT(aURI, "Visited URI is null!");
+ MOZ_ASSERT(mLoadType != LOAD_ERROR_PAGE &&
+ mLoadType != LOAD_BYPASS_HISTORY,
+ "Do not add error or bypass pages to global history");
+
+ // Only content-type docshells save URI visits. Also don't do
+ // anything here if we're not supposed to use global history.
+ if (mItemType != typeContent || !mUseGlobalHistory || UsePrivateBrowsing()) {
+ return;
+ }
+
+ nsCOMPtr<IHistory> history = services::GetHistoryService();
+
+ if (history) {
+ uint32_t visitURIFlags = 0;
+
+ if (!IsFrame()) {
+ visitURIFlags |= IHistory::TOP_LEVEL;
+ }
+
+ if (aChannelRedirectFlags & nsIChannelEventSink::REDIRECT_TEMPORARY) {
+ visitURIFlags |= IHistory::REDIRECT_TEMPORARY;
+ } else if (aChannelRedirectFlags & nsIChannelEventSink::REDIRECT_PERMANENT) {
+ visitURIFlags |= IHistory::REDIRECT_PERMANENT;
+ }
+
+ if (aResponseStatus >= 300 && aResponseStatus < 400) {
+ visitURIFlags |= IHistory::REDIRECT_SOURCE;
+ }
+ // Errors 400-501 and 505 are considered unrecoverable, in the sense a
+ // simple retry attempt by the user is unlikely to solve them.
+ // 408 is special cased, since may actually indicate a temporary
+ // connection problem.
+ else if (aResponseStatus != 408 &&
+ ((aResponseStatus >= 400 && aResponseStatus <= 501) ||
+ aResponseStatus == 505)) {
+ visitURIFlags |= IHistory::UNRECOVERABLE_ERROR;
+ }
+
+ (void)history->VisitURI(aURI, aPreviousURI, visitURIFlags);
+ } else if (mGlobalHistory) {
+ // Falls back to sync global history interface.
+ (void)mGlobalHistory->AddURI(aURI,
+ !!aChannelRedirectFlags,
+ !IsFrame(),
+ aReferrerURI);
+ }
+}
+
+//*****************************************************************************
+// nsDocShell: Helper Routines
+//*****************************************************************************
+
+NS_IMETHODIMP
+nsDocShell::SetLoadType(uint32_t aLoadType)
+{
+ mLoadType = aLoadType;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetLoadType(uint32_t* aLoadType)
+{
+ *aLoadType = mLoadType;
+ return NS_OK;
+}
+
+nsresult
+nsDocShell::ConfirmRepost(bool* aRepost)
+{
+ nsCOMPtr<nsIPrompt> prompter;
+ CallGetInterface(this, static_cast<nsIPrompt**>(getter_AddRefs(prompter)));
+ if (!prompter) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ nsCOMPtr<nsIStringBundleService> stringBundleService =
+ mozilla::services::GetStringBundleService();
+ if (!stringBundleService) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIStringBundle> appBundle;
+ nsresult rv = stringBundleService->CreateBundle(kAppstringsBundleURL,
+ getter_AddRefs(appBundle));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIStringBundle> brandBundle;
+ rv = stringBundleService->CreateBundle(kBrandBundleURL,
+ getter_AddRefs(brandBundle));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_ASSERTION(prompter && brandBundle && appBundle,
+ "Unable to set up repost prompter.");
+
+ nsXPIDLString brandName;
+ rv = brandBundle->GetStringFromName(u"brandShortName",
+ getter_Copies(brandName));
+
+ nsXPIDLString msgString, button0Title;
+ if (NS_FAILED(rv)) { // No brand, use the generic version.
+ rv = appBundle->GetStringFromName(u"confirmRepostPrompt",
+ getter_Copies(msgString));
+ } else {
+ // Brand available - if the app has an override file with formatting, the
+ // app name will be included. Without an override, the prompt will look
+ // like the generic version.
+ const char16_t* formatStrings[] = { brandName.get() };
+ rv = appBundle->FormatStringFromName(u"confirmRepostPrompt",
+ formatStrings,
+ ArrayLength(formatStrings),
+ getter_Copies(msgString));
+ }
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ rv = appBundle->GetStringFromName(u"resendButton.label",
+ getter_Copies(button0Title));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ int32_t buttonPressed;
+ // The actual value here is irrelevant, but we can't pass an invalid
+ // bool through XPConnect.
+ bool checkState = false;
+ rv = prompter->ConfirmEx(
+ nullptr, msgString.get(),
+ (nsIPrompt::BUTTON_POS_0 * nsIPrompt::BUTTON_TITLE_IS_STRING) +
+ (nsIPrompt::BUTTON_POS_1 * nsIPrompt::BUTTON_TITLE_CANCEL),
+ button0Title.get(), nullptr, nullptr, nullptr, &checkState, &buttonPressed);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ *aRepost = (buttonPressed == 0);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetPromptAndStringBundle(nsIPrompt** aPrompt,
+ nsIStringBundle** aStringBundle)
+{
+ NS_ENSURE_SUCCESS(GetInterface(NS_GET_IID(nsIPrompt), (void**)aPrompt),
+ NS_ERROR_FAILURE);
+
+ nsCOMPtr<nsIStringBundleService> stringBundleService =
+ mozilla::services::GetStringBundleService();
+ NS_ENSURE_TRUE(stringBundleService, NS_ERROR_FAILURE);
+
+ NS_ENSURE_SUCCESS(
+ stringBundleService->CreateBundle(kAppstringsBundleURL, aStringBundle),
+ NS_ERROR_FAILURE);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetChildOffset(nsIDOMNode* aChild, nsIDOMNode* aParent,
+ int32_t* aOffset)
+{
+ NS_ENSURE_ARG_POINTER(aChild || aParent);
+
+ nsCOMPtr<nsIDOMNodeList> childNodes;
+ NS_ENSURE_SUCCESS(aParent->GetChildNodes(getter_AddRefs(childNodes)),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_TRUE(childNodes, NS_ERROR_FAILURE);
+
+ int32_t i = 0;
+
+ for (; true; i++) {
+ nsCOMPtr<nsIDOMNode> childNode;
+ NS_ENSURE_SUCCESS(childNodes->Item(i, getter_AddRefs(childNode)),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_TRUE(childNode, NS_ERROR_FAILURE);
+
+ if (childNode.get() == aChild) {
+ *aOffset = i;
+ return NS_OK;
+ }
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+nsIScrollableFrame*
+nsDocShell::GetRootScrollFrame()
+{
+ nsCOMPtr<nsIPresShell> shell = GetPresShell();
+ NS_ENSURE_TRUE(shell, nullptr);
+
+ return shell->GetRootScrollFrameAsScrollableExternal();
+}
+
+NS_IMETHODIMP
+nsDocShell::EnsureScriptEnvironment()
+{
+ if (mScriptGlobal) {
+ return NS_OK;
+ }
+
+ if (mIsBeingDestroyed) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+#ifdef DEBUG
+ NS_ASSERTION(!mInEnsureScriptEnv,
+ "Infinite loop! Calling EnsureScriptEnvironment() from "
+ "within EnsureScriptEnvironment()!");
+
+ // Yeah, this isn't re-entrant safe, but that's ok since if we
+ // re-enter this method, we'll infinitely loop...
+ AutoRestore<bool> boolSetter(mInEnsureScriptEnv);
+ mInEnsureScriptEnv = true;
+#endif
+
+ nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
+ NS_ENSURE_TRUE(browserChrome, NS_ERROR_NOT_AVAILABLE);
+
+ uint32_t chromeFlags;
+ browserChrome->GetChromeFlags(&chromeFlags);
+
+ bool isModalContentWindow =
+ (mItemType == typeContent) &&
+ (chromeFlags & nsIWebBrowserChrome::CHROME_MODAL_CONTENT_WINDOW);
+ // There can be various other content docshells associated with the
+ // top-level window, like sidebars. Make sure that we only create an
+ // nsGlobalModalWindow for the primary content shell.
+ if (isModalContentWindow) {
+ nsCOMPtr<nsIDocShellTreeItem> primaryItem;
+ nsresult rv =
+ mTreeOwner->GetPrimaryContentShell(getter_AddRefs(primaryItem));
+ NS_ENSURE_SUCCESS(rv, rv);
+ isModalContentWindow = (primaryItem == this);
+ }
+
+ // If our window is modal and we're not opened as chrome, make
+ // this window a modal content window.
+ mScriptGlobal =
+ NS_NewScriptGlobalObject(mItemType == typeChrome, isModalContentWindow);
+ MOZ_ASSERT(mScriptGlobal);
+
+ mScriptGlobal->SetDocShell(this);
+
+ // Ensure the script object is set up to run script.
+ return mScriptGlobal->EnsureScriptEnvironment();
+}
+
+NS_IMETHODIMP
+nsDocShell::EnsureEditorData()
+{
+ bool openDocHasDetachedEditor = mOSHE && mOSHE->HasDetachedEditor();
+ if (!mEditorData && !mIsBeingDestroyed && !openDocHasDetachedEditor) {
+ // We shouldn't recreate the editor data if it already exists, or
+ // we're shutting down, or we already have a detached editor data
+ // stored in the session history. We should only have one editordata
+ // per docshell.
+ mEditorData = new nsDocShellEditorData(this);
+ }
+
+ return mEditorData ? NS_OK : NS_ERROR_NOT_AVAILABLE;
+}
+
+nsresult
+nsDocShell::EnsureTransferableHookData()
+{
+ if (!mTransferableHookData) {
+ mTransferableHookData = new nsTransferableHookData();
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::EnsureFind()
+{
+ nsresult rv;
+ if (!mFind) {
+ mFind = do_CreateInstance("@mozilla.org/embedcomp/find;1", &rv);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+
+ // we promise that the nsIWebBrowserFind that we return has been set
+ // up to point to the focused, or content window, so we have to
+ // set that up each time.
+
+ nsIScriptGlobalObject* scriptGO = GetScriptGlobalObject();
+ NS_ENSURE_TRUE(scriptGO, NS_ERROR_UNEXPECTED);
+
+ // default to our window
+ nsCOMPtr<nsPIDOMWindowOuter> ourWindow = do_QueryInterface(scriptGO);
+ nsCOMPtr<nsPIDOMWindowOuter> windowToSearch;
+ nsFocusManager::GetFocusedDescendant(ourWindow, true,
+ getter_AddRefs(windowToSearch));
+
+ nsCOMPtr<nsIWebBrowserFindInFrames> findInFrames = do_QueryInterface(mFind);
+ if (!findInFrames) {
+ return NS_ERROR_NO_INTERFACE;
+ }
+
+ rv = findInFrames->SetRootSearchFrame(ourWindow);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ rv = findInFrames->SetCurrentSearchFrame(windowToSearch);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+bool
+nsDocShell::IsFrame()
+{
+ nsCOMPtr<nsIDocShellTreeItem> parent;
+ GetSameTypeParent(getter_AddRefs(parent));
+ return !!parent;
+}
+
+NS_IMETHODIMP
+nsDocShell::IsBeingDestroyed(bool* aDoomed)
+{
+ NS_ENSURE_ARG(aDoomed);
+ *aDoomed = mIsBeingDestroyed;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetIsExecutingOnLoadHandler(bool* aResult)
+{
+ NS_ENSURE_ARG(aResult);
+ *aResult = mIsExecutingOnLoadHandler;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetLayoutHistoryState(nsILayoutHistoryState** aLayoutHistoryState)
+{
+ if (mOSHE) {
+ mOSHE->GetLayoutHistoryState(aLayoutHistoryState);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetLayoutHistoryState(nsILayoutHistoryState* aLayoutHistoryState)
+{
+ if (mOSHE) {
+ mOSHE->SetLayoutHistoryState(aLayoutHistoryState);
+ }
+ return NS_OK;
+}
+
+nsRefreshTimer::nsRefreshTimer()
+ : mDelay(0), mRepeat(false), mMetaRefresh(false)
+{
+}
+
+nsRefreshTimer::~nsRefreshTimer()
+{
+}
+
+NS_IMPL_ADDREF(nsRefreshTimer)
+NS_IMPL_RELEASE(nsRefreshTimer)
+
+NS_INTERFACE_MAP_BEGIN(nsRefreshTimer)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITimerCallback)
+ NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
+NS_INTERFACE_MAP_END_THREADSAFE
+
+NS_IMETHODIMP
+nsRefreshTimer::Notify(nsITimer* aTimer)
+{
+ NS_ASSERTION(mDocShell, "DocShell is somehow null");
+
+ if (mDocShell && aTimer) {
+ // Get the delay count to determine load type
+ uint32_t delay = 0;
+ aTimer->GetDelay(&delay);
+ mDocShell->ForceRefreshURIFromTimer(mURI, delay, mMetaRefresh, aTimer, mPrincipal);
+ }
+ return NS_OK;
+}
+
+nsDocShell::InterfaceRequestorProxy::InterfaceRequestorProxy(
+ nsIInterfaceRequestor* aRequestor)
+{
+ if (aRequestor) {
+ mWeakPtr = do_GetWeakReference(aRequestor);
+ }
+}
+
+nsDocShell::InterfaceRequestorProxy::~InterfaceRequestorProxy()
+{
+ mWeakPtr = nullptr;
+}
+
+NS_IMPL_ISUPPORTS(nsDocShell::InterfaceRequestorProxy, nsIInterfaceRequestor)
+
+NS_IMETHODIMP
+nsDocShell::InterfaceRequestorProxy::GetInterface(const nsIID& aIID,
+ void** aSink)
+{
+ NS_ENSURE_ARG_POINTER(aSink);
+ nsCOMPtr<nsIInterfaceRequestor> ifReq = do_QueryReferent(mWeakPtr);
+ if (ifReq) {
+ return ifReq->GetInterface(aIID, aSink);
+ }
+ *aSink = nullptr;
+ return NS_NOINTERFACE;
+}
+
+nsresult
+nsDocShell::SetBaseUrlForWyciwyg(nsIContentViewer* aContentViewer)
+{
+ if (!aContentViewer) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIURI> baseURI;
+ nsresult rv = NS_ERROR_NOT_AVAILABLE;
+
+ if (sURIFixup) {
+ rv = sURIFixup->CreateExposableURI(mCurrentURI, getter_AddRefs(baseURI));
+ }
+
+ // Get the current document and set the base uri
+ if (baseURI) {
+ nsIDocument* document = aContentViewer->GetDocument();
+ if (document) {
+ document->SetBaseURI(baseURI);
+ }
+ }
+ return rv;
+}
+
+//*****************************************************************************
+// nsDocShell::nsIAuthPromptProvider
+//*****************************************************************************
+
+NS_IMETHODIMP
+nsDocShell::GetAuthPrompt(uint32_t aPromptReason, const nsIID& aIID,
+ void** aResult)
+{
+ // a priority prompt request will override a false mAllowAuth setting
+ bool priorityPrompt = (aPromptReason == PROMPT_PROXY);
+
+ if (!mAllowAuth && !priorityPrompt) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ // we're either allowing auth, or it's a proxy request
+ nsresult rv;
+ nsCOMPtr<nsIPromptFactory> wwatch =
+ do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = EnsureScriptEnvironment();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Get the an auth prompter for our window so that the parenting
+ // of the dialogs works as it should when using tabs.
+
+ return wwatch->GetPrompt(mScriptGlobal->AsOuter(), aIID,
+ reinterpret_cast<void**>(aResult));
+}
+
+//*****************************************************************************
+// nsDocShell::nsILoadContext
+//*****************************************************************************
+
+NS_IMETHODIMP
+nsDocShell::GetAssociatedWindow(mozIDOMWindowProxy** aWindow)
+{
+ CallGetInterface(this, aWindow);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetTopWindow(mozIDOMWindowProxy** aWindow)
+{
+ nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
+ if (win) {
+ win = win->GetTop();
+ }
+ win.forget(aWindow);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetTopFrameElement(nsIDOMElement** aElement)
+{
+ *aElement = nullptr;
+ nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
+ if (!win) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsPIDOMWindowOuter> top = win->GetScriptableTop();
+ NS_ENSURE_TRUE(top, NS_ERROR_FAILURE);
+
+ // GetFrameElementInternal, /not/ GetScriptableFrameElement -- if |top| is
+ // inside <iframe mozbrowser>, we want to return the iframe, not null.
+ // And we want to cross the content/chrome boundary.
+ nsCOMPtr<nsIDOMElement> elt =
+ do_QueryInterface(top->GetFrameElementInternal());
+ elt.forget(aElement);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetNestedFrameId(uint64_t* aId)
+{
+ *aId = 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::IsTrackingProtectionOn(bool* aIsTrackingProtectionOn)
+{
+ if (Preferences::GetBool("privacy.trackingprotection.enabled", false)) {
+ *aIsTrackingProtectionOn = true;
+ } else if (UsePrivateBrowsing() &&
+ Preferences::GetBool("privacy.trackingprotection.pbmode.enabled", false)) {
+ *aIsTrackingProtectionOn = true;
+ } else {
+ *aIsTrackingProtectionOn = false;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetIsContent(bool* aIsContent)
+{
+ *aIsContent = (mItemType == typeContent);
+ return NS_OK;
+}
+
+bool
+nsDocShell::IsOKToLoadURI(nsIURI* aURI)
+{
+ NS_PRECONDITION(aURI, "Must have a URI!");
+
+ if (!mFiredUnloadEvent) {
+ return true;
+ }
+
+ if (!mLoadingURI) {
+ return false;
+ }
+
+ nsCOMPtr<nsIScriptSecurityManager> secMan =
+ do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
+ return secMan &&
+ NS_SUCCEEDED(secMan->CheckSameOriginURI(aURI, mLoadingURI, false));
+}
+
+//
+// Routines for selection and clipboard
+//
+nsresult
+nsDocShell::GetControllerForCommand(const char* aCommand,
+ nsIController** aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+ *aResult = nullptr;
+
+ NS_ENSURE_TRUE(mScriptGlobal, NS_ERROR_FAILURE);
+
+ nsCOMPtr<nsPIWindowRoot> root = mScriptGlobal->GetTopWindowRoot();
+ NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
+
+ return root->GetControllerForCommand(aCommand, aResult);
+}
+
+NS_IMETHODIMP
+nsDocShell::IsCommandEnabled(const char* aCommand, bool* aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+ *aResult = false;
+
+ nsresult rv = NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIController> controller;
+ rv = GetControllerForCommand(aCommand, getter_AddRefs(controller));
+ if (controller) {
+ rv = controller->IsCommandEnabled(aCommand, aResult);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocShell::DoCommand(const char* aCommand)
+{
+ nsresult rv = NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIController> controller;
+ rv = GetControllerForCommand(aCommand, getter_AddRefs(controller));
+ if (controller) {
+ rv = controller->DoCommand(aCommand);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocShell::DoCommandWithParams(const char* aCommand, nsICommandParams* aParams)
+{
+ nsCOMPtr<nsIController> controller;
+ nsresult rv = GetControllerForCommand(aCommand, getter_AddRefs(controller));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsCOMPtr<nsICommandController> commandController =
+ do_QueryInterface(controller, &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return commandController->DoCommandWithParams(aCommand, aParams);
+}
+
+nsresult
+nsDocShell::EnsureCommandHandler()
+{
+ if (!mCommandManager) {
+ nsCOMPtr<nsPICommandUpdater> commandUpdater =
+ do_CreateInstance("@mozilla.org/embedcomp/command-manager;1");
+ if (!commandUpdater) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ nsCOMPtr<nsPIDOMWindowOuter> domWindow = GetWindow();
+ nsresult rv = commandUpdater->Init(domWindow);
+ if (NS_SUCCEEDED(rv)) {
+ mCommandManager = do_QueryInterface(commandUpdater);
+ }
+ }
+
+ return mCommandManager ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsDocShell::CanCutSelection(bool* aResult)
+{
+ return IsCommandEnabled("cmd_cut", aResult);
+}
+
+NS_IMETHODIMP
+nsDocShell::CanCopySelection(bool* aResult)
+{
+ return IsCommandEnabled("cmd_copy", aResult);
+}
+
+NS_IMETHODIMP
+nsDocShell::CanCopyLinkLocation(bool* aResult)
+{
+ return IsCommandEnabled("cmd_copyLink", aResult);
+}
+
+NS_IMETHODIMP
+nsDocShell::CanCopyImageLocation(bool* aResult)
+{
+ return IsCommandEnabled("cmd_copyImageLocation", aResult);
+}
+
+NS_IMETHODIMP
+nsDocShell::CanCopyImageContents(bool* aResult)
+{
+ return IsCommandEnabled("cmd_copyImageContents", aResult);
+}
+
+NS_IMETHODIMP
+nsDocShell::CanPaste(bool* aResult)
+{
+ return IsCommandEnabled("cmd_paste", aResult);
+}
+
+NS_IMETHODIMP
+nsDocShell::CutSelection(void)
+{
+ return DoCommand("cmd_cut");
+}
+
+NS_IMETHODIMP
+nsDocShell::CopySelection(void)
+{
+ return DoCommand("cmd_copy");
+}
+
+NS_IMETHODIMP
+nsDocShell::CopyLinkLocation(void)
+{
+ return DoCommand("cmd_copyLink");
+}
+
+NS_IMETHODIMP
+nsDocShell::CopyImageLocation(void)
+{
+ return DoCommand("cmd_copyImageLocation");
+}
+
+NS_IMETHODIMP
+nsDocShell::CopyImageContents(void)
+{
+ return DoCommand("cmd_copyImageContents");
+}
+
+NS_IMETHODIMP
+nsDocShell::Paste(void)
+{
+ return DoCommand("cmd_paste");
+}
+
+NS_IMETHODIMP
+nsDocShell::SelectAll(void)
+{
+ return DoCommand("cmd_selectAll");
+}
+
+//
+// SelectNone
+//
+// Collapses the current selection, insertion point ends up at beginning
+// of previous selection.
+//
+NS_IMETHODIMP
+nsDocShell::SelectNone(void)
+{
+ return DoCommand("cmd_selectNone");
+}
+
+// link handling
+
+class OnLinkClickEvent : public Runnable
+{
+public:
+ OnLinkClickEvent(nsDocShell* aHandler, nsIContent* aContent,
+ nsIURI* aURI,
+ const char16_t* aTargetSpec,
+ const nsAString& aFileName,
+ nsIInputStream* aPostDataStream,
+ nsIInputStream* aHeadersDataStream,
+ bool aIsTrusted);
+
+ NS_IMETHOD Run() override
+ {
+ nsAutoPopupStatePusher popupStatePusher(mPopupState);
+
+ // We need to set up an AutoJSAPI here for the following reason: When we do
+ // OnLinkClickSync we'll eventually end up in nsGlobalWindow::OpenInternal
+ // which only does popup blocking if !LegacyIsCallerChromeOrNativeCode().
+ // So we need to fake things so that we don't look like native code as far
+ // as LegacyIsCallerNativeCode() is concerned.
+ AutoJSAPI jsapi;
+ if (mIsTrusted || jsapi.Init(mContent->OwnerDoc()->GetScopeObject())) {
+ mHandler->OnLinkClickSync(mContent, mURI,
+ mTargetSpec.get(), mFileName,
+ mPostDataStream, mHeadersDataStream,
+ nullptr, nullptr);
+ }
+ return NS_OK;
+ }
+
+private:
+ RefPtr<nsDocShell> mHandler;
+ nsCOMPtr<nsIURI> mURI;
+ nsString mTargetSpec;
+ nsString mFileName;
+ nsCOMPtr<nsIInputStream> mPostDataStream;
+ nsCOMPtr<nsIInputStream> mHeadersDataStream;
+ nsCOMPtr<nsIContent> mContent;
+ PopupControlState mPopupState;
+ bool mIsTrusted;
+};
+
+OnLinkClickEvent::OnLinkClickEvent(nsDocShell* aHandler,
+ nsIContent* aContent,
+ nsIURI* aURI,
+ const char16_t* aTargetSpec,
+ const nsAString& aFileName,
+ nsIInputStream* aPostDataStream,
+ nsIInputStream* aHeadersDataStream,
+ bool aIsTrusted)
+ : mHandler(aHandler)
+ , mURI(aURI)
+ , mTargetSpec(aTargetSpec)
+ , mFileName(aFileName)
+ , mPostDataStream(aPostDataStream)
+ , mHeadersDataStream(aHeadersDataStream)
+ , mContent(aContent)
+ , mPopupState(mHandler->mScriptGlobal->GetPopupControlState())
+ , mIsTrusted(aIsTrusted)
+{
+}
+
+NS_IMETHODIMP
+nsDocShell::OnLinkClick(nsIContent* aContent,
+ nsIURI* aURI,
+ const char16_t* aTargetSpec,
+ const nsAString& aFileName,
+ nsIInputStream* aPostDataStream,
+ nsIInputStream* aHeadersDataStream,
+ bool aIsTrusted)
+{
+ NS_ASSERTION(NS_IsMainThread(), "wrong thread");
+
+ if (!IsNavigationAllowed() || !IsOKToLoadURI(aURI)) {
+ return NS_OK;
+ }
+
+ // On history navigation through Back/Forward buttons, don't execute
+ // automatic JavaScript redirection such as |anchorElement.click()| or
+ // |formElement.submit()|.
+ //
+ // XXX |formElement.submit()| bypasses this checkpoint because it calls
+ // nsDocShell::OnLinkClickSync(...) instead.
+ if (ShouldBlockLoadingForBackButton()) {
+ return NS_OK;
+ }
+
+ if (aContent->IsEditable()) {
+ return NS_OK;
+ }
+
+ nsresult rv = NS_ERROR_FAILURE;
+ nsAutoString target;
+
+ nsCOMPtr<nsIWebBrowserChrome3> browserChrome3 = do_GetInterface(mTreeOwner);
+ if (browserChrome3) {
+ nsCOMPtr<nsIDOMNode> linkNode = do_QueryInterface(aContent);
+ nsAutoString oldTarget(aTargetSpec);
+ rv = browserChrome3->OnBeforeLinkTraversal(oldTarget, aURI,
+ linkNode, mIsAppTab, target);
+ }
+
+ if (NS_FAILED(rv)) {
+ target = aTargetSpec;
+ }
+
+ nsCOMPtr<nsIRunnable> ev =
+ new OnLinkClickEvent(this, aContent, aURI, target.get(), aFileName,
+ aPostDataStream, aHeadersDataStream, aIsTrusted);
+ return NS_DispatchToCurrentThread(ev);
+}
+
+NS_IMETHODIMP
+nsDocShell::OnLinkClickSync(nsIContent* aContent,
+ nsIURI* aURI,
+ const char16_t* aTargetSpec,
+ const nsAString& aFileName,
+ nsIInputStream* aPostDataStream,
+ nsIInputStream* aHeadersDataStream,
+ nsIDocShell** aDocShell,
+ nsIRequest** aRequest)
+{
+ // Initialize the DocShell / Request
+ if (aDocShell) {
+ *aDocShell = nullptr;
+ }
+ if (aRequest) {
+ *aRequest = nullptr;
+ }
+
+ if (!IsNavigationAllowed() || !IsOKToLoadURI(aURI)) {
+ return NS_OK;
+ }
+
+ // XXX When the linking node was HTMLFormElement, it is synchronous event.
+ // That is, the caller of this method is not |OnLinkClickEvent::Run()|
+ // but |HTMLFormElement::SubmitSubmission(...)|.
+ if (aContent->IsHTMLElement(nsGkAtoms::form) &&
+ ShouldBlockLoadingForBackButton()) {
+ return NS_OK;
+ }
+
+ if (aContent->IsEditable()) {
+ return NS_OK;
+ }
+
+ {
+ // defer to an external protocol handler if necessary...
+ nsCOMPtr<nsIExternalProtocolService> extProtService =
+ do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID);
+ if (extProtService) {
+ nsAutoCString scheme;
+ aURI->GetScheme(scheme);
+ if (!scheme.IsEmpty()) {
+ // if the URL scheme does not correspond to an exposed protocol, then we
+ // need to hand this link click over to the external protocol handler.
+ bool isExposed;
+ nsresult rv =
+ extProtService->IsExposedProtocol(scheme.get(), &isExposed);
+ if (NS_SUCCEEDED(rv) && !isExposed) {
+ return extProtService->LoadURI(aURI, this);
+ }
+ }
+ }
+ }
+
+ uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
+ if (IsElementAnchor(aContent)) {
+ MOZ_ASSERT(aContent->IsHTMLElement());
+ nsAutoString referrer;
+ aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::rel, referrer);
+ nsWhitespaceTokenizerTemplate<nsContentUtils::IsHTMLWhitespace> tok(referrer);
+ while (tok.hasMoreTokens()) {
+ const nsAString& token = tok.nextToken();
+ if (token.LowerCaseEqualsLiteral("noreferrer")) {
+ flags |= INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER |
+ INTERNAL_LOAD_FLAGS_NO_OPENER;
+ // We now have all the flags we could possibly have, so just stop.
+ break;
+ }
+ if (token.LowerCaseEqualsLiteral("noopener")) {
+ flags |= INTERNAL_LOAD_FLAGS_NO_OPENER;
+ }
+ }
+ }
+
+ // Get the owner document of the link that was clicked, this will be
+ // the document that the link is in, or the last document that the
+ // link was in. From that document, we'll get the URI to use as the
+ // referer, since the current URI in this docshell may be a
+ // new document that we're in the process of loading.
+ nsCOMPtr<nsIDocument> refererDoc = aContent->OwnerDoc();
+ NS_ENSURE_TRUE(refererDoc, NS_ERROR_UNEXPECTED);
+
+ // Now check that the refererDoc's inner window is the current inner
+ // window for mScriptGlobal. If it's not, then we don't want to
+ // follow this link.
+ nsPIDOMWindowInner* refererInner = refererDoc->GetInnerWindow();
+ NS_ENSURE_TRUE(refererInner, NS_ERROR_UNEXPECTED);
+ if (!mScriptGlobal ||
+ mScriptGlobal->AsOuter()->GetCurrentInnerWindow() != refererInner) {
+ // We're no longer the current inner window
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIURI> referer = refererDoc->GetDocumentURI();
+ uint32_t refererPolicy = refererDoc->GetReferrerPolicy();
+
+ // get referrer attribute from clicked link and parse it
+ // if per element referrer is enabled, the element referrer overrules
+ // the document wide referrer
+ if (IsElementAnchor(aContent)) {
+ net::ReferrerPolicy refPolEnum = aContent->AsElement()->GetReferrerPolicyAsEnum();
+ if (refPolEnum != net::RP_Unset) {
+ refererPolicy = refPolEnum;
+ }
+ }
+
+ // referer could be null here in some odd cases, but that's ok,
+ // we'll just load the link w/o sending a referer in those cases.
+
+ nsAutoString target(aTargetSpec);
+
+ // If this is an anchor element, grab its type property to use as a hint
+ nsAutoString typeHint;
+ nsCOMPtr<nsIDOMHTMLAnchorElement> anchor(do_QueryInterface(aContent));
+ if (anchor) {
+ anchor->GetType(typeHint);
+ NS_ConvertUTF16toUTF8 utf8Hint(typeHint);
+ nsAutoCString type, dummy;
+ NS_ParseRequestContentType(utf8Hint, type, dummy);
+ CopyUTF8toUTF16(type, typeHint);
+ }
+
+ // Clone the URI now, in case a content policy or something messes
+ // with it under InternalLoad; we do _not_ want to change the URI
+ // our caller passed in.
+ nsCOMPtr<nsIURI> clonedURI;
+ aURI->Clone(getter_AddRefs(clonedURI));
+ if (!clonedURI) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ nsresult rv = InternalLoad(clonedURI, // New URI
+ nullptr, // Original URI
+ false, // LoadReplace
+ referer, // Referer URI
+ refererPolicy, // Referer policy
+ aContent->NodePrincipal(), // Triggering is our node's
+ // principal
+ aContent->NodePrincipal(),
+ flags,
+ target, // Window target
+ NS_LossyConvertUTF16toASCII(typeHint).get(),
+ aFileName, // Download as file
+ aPostDataStream, // Post data stream
+ aHeadersDataStream, // Headers stream
+ LOAD_LINK, // Load type
+ nullptr, // No SHEntry
+ true, // first party site
+ NullString(), // No srcdoc
+ this, // We are the source
+ nullptr, // baseURI not needed
+ aDocShell, // DocShell out-param
+ aRequest); // Request out-param
+ if (NS_SUCCEEDED(rv)) {
+ DispatchPings(this, aContent, aURI, referer, refererPolicy);
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocShell::OnOverLink(nsIContent* aContent,
+ nsIURI* aURI,
+ const char16_t* aTargetSpec)
+{
+ if (aContent->IsEditable()) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIWebBrowserChrome2> browserChrome2 = do_GetInterface(mTreeOwner);
+ nsresult rv = NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIWebBrowserChrome> browserChrome;
+ if (!browserChrome2) {
+ browserChrome = do_GetInterface(mTreeOwner);
+ if (!browserChrome) {
+ return rv;
+ }
+ }
+
+ nsCOMPtr<nsITextToSubURI> textToSubURI =
+ do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // use url origin charset to unescape the URL
+ nsAutoCString charset;
+ rv = aURI->GetOriginCharset(charset);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString spec;
+ rv = aURI->GetSpec(spec);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString uStr;
+ rv = textToSubURI->UnEscapeURIForUI(charset, spec, uStr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mozilla::net::PredictorPredict(aURI, mCurrentURI,
+ nsINetworkPredictor::PREDICT_LINK,
+ this, nullptr);
+
+ if (browserChrome2) {
+ nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aContent);
+ rv = browserChrome2->SetStatusWithContext(nsIWebBrowserChrome::STATUS_LINK,
+ uStr, element);
+ } else {
+ rv = browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_LINK, uStr.get());
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocShell::OnLeaveLink()
+{
+ nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
+ nsresult rv = NS_ERROR_FAILURE;
+
+ if (browserChrome) {
+ rv = browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_LINK,
+ EmptyString().get());
+ }
+ return rv;
+}
+
+bool
+nsDocShell::ShouldBlockLoadingForBackButton()
+{
+ if (!(mLoadType & LOAD_CMD_HISTORY) ||
+ EventStateManager::IsHandlingUserInput() ||
+ !Preferences::GetBool("accessibility.blockjsredirection")) {
+ return false;
+ }
+
+ bool canGoForward = false;
+ GetCanGoForward(&canGoForward);
+ return canGoForward;
+}
+
+bool
+nsDocShell::PluginsAllowedInCurrentDoc()
+{
+ bool pluginsAllowed = false;
+
+ if (!mContentViewer) {
+ return false;
+ }
+
+ nsIDocument* doc = mContentViewer->GetDocument();
+ if (!doc) {
+ return false;
+ }
+
+ doc->GetAllowPlugins(&pluginsAllowed);
+ return pluginsAllowed;
+}
+
+//----------------------------------------------------------------------
+// Web Shell Services API
+
+// This functions is only called when a new charset is detected in loading a
+// document. Its name should be changed to "CharsetReloadDocument"
+NS_IMETHODIMP
+nsDocShell::ReloadDocument(const char* aCharset, int32_t aSource)
+{
+ // XXX hack. keep the aCharset and aSource wait to pick it up
+ nsCOMPtr<nsIContentViewer> cv;
+ NS_ENSURE_SUCCESS(GetContentViewer(getter_AddRefs(cv)), NS_ERROR_FAILURE);
+ if (cv) {
+ int32_t hint;
+ cv->GetHintCharacterSetSource(&hint);
+ if (aSource > hint) {
+ nsCString charset(aCharset);
+ cv->SetHintCharacterSet(charset);
+ cv->SetHintCharacterSetSource(aSource);
+ if (eCharsetReloadRequested != mCharsetReloadState) {
+ mCharsetReloadState = eCharsetReloadRequested;
+ return Reload(LOAD_FLAGS_CHARSET_CHANGE);
+ }
+ }
+ }
+ // return failure if this request is not accepted due to mCharsetReloadState
+ return NS_ERROR_DOCSHELL_REQUEST_REJECTED;
+}
+
+NS_IMETHODIMP
+nsDocShell::StopDocumentLoad(void)
+{
+ if (eCharsetReloadRequested != mCharsetReloadState) {
+ Stop(nsIWebNavigation::STOP_ALL);
+ return NS_OK;
+ }
+ // return failer if this request is not accepted due to mCharsetReloadState
+ return NS_ERROR_DOCSHELL_REQUEST_REJECTED;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetPrintPreview(nsIWebBrowserPrint** aPrintPreview)
+{
+ *aPrintPreview = nullptr;
+#if NS_PRINT_PREVIEW
+ nsCOMPtr<nsIDocumentViewerPrint> print = do_QueryInterface(mContentViewer);
+ if (!print || !print->IsInitializedForPrintPreview()) {
+ Stop(nsIWebNavigation::STOP_ALL);
+ nsCOMPtr<nsIPrincipal> principal = nsNullPrincipal::CreateWithInheritedAttributes(this);
+ nsresult rv = CreateAboutBlankContentViewer(principal, nullptr);
+ NS_ENSURE_SUCCESS(rv, rv);
+ print = do_QueryInterface(mContentViewer);
+ NS_ENSURE_STATE(print);
+ print->InitializeForPrintPreview();
+ }
+ nsCOMPtr<nsIWebBrowserPrint> result = do_QueryInterface(print);
+ result.forget(aPrintPreview);
+ return NS_OK;
+#else
+ return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+#ifdef DEBUG
+unsigned long nsDocShell::gNumberOfDocShells = 0;
+#endif
+
+NS_IMETHODIMP
+nsDocShell::GetCanExecuteScripts(bool* aResult)
+{
+ *aResult = mCanExecuteScripts;
+ return NS_OK;
+}
+
+/* [infallible] */ NS_IMETHODIMP
+nsDocShell::SetFrameType(uint32_t aFrameType)
+{
+ mFrameType = aFrameType;
+ return NS_OK;
+}
+
+/* [infallible] */ NS_IMETHODIMP
+nsDocShell::GetFrameType(uint32_t* aFrameType)
+{
+ *aFrameType = mFrameType;
+ return NS_OK;
+}
+
+/* [infallible] */ NS_IMETHODIMP
+nsDocShell::GetIsApp(bool* aIsApp)
+{
+ *aIsApp = (mFrameType == FRAME_TYPE_APP);
+ return NS_OK;
+}
+
+/* [infallible] */ NS_IMETHODIMP
+nsDocShell::GetIsMozBrowserOrApp(bool* aIsMozBrowserOrApp)
+{
+ *aIsMozBrowserOrApp = (mFrameType != FRAME_TYPE_REGULAR);
+ return NS_OK;
+}
+
+uint32_t
+nsDocShell::GetInheritedFrameType()
+{
+ if (mFrameType != FRAME_TYPE_REGULAR) {
+ return mFrameType;
+ }
+
+ nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
+ GetSameTypeParent(getter_AddRefs(parentAsItem));
+
+ nsCOMPtr<nsIDocShell> parent = do_QueryInterface(parentAsItem);
+ if (!parent) {
+ return FRAME_TYPE_REGULAR;
+ }
+
+ return static_cast<nsDocShell*>(parent.get())->GetInheritedFrameType();
+}
+
+/* [infallible] */ NS_IMETHODIMP
+nsDocShell::GetIsIsolatedMozBrowserElement(bool* aIsIsolatedMozBrowserElement)
+{
+ bool result = mFrameType == FRAME_TYPE_BROWSER &&
+ mOriginAttributes.mInIsolatedMozBrowser;
+ *aIsIsolatedMozBrowserElement = result;
+ return NS_OK;
+}
+
+/* [infallible] */ NS_IMETHODIMP
+nsDocShell::GetIsInIsolatedMozBrowserElement(bool* aIsInIsolatedMozBrowserElement)
+{
+ MOZ_ASSERT(!mOriginAttributes.mInIsolatedMozBrowser ||
+ (GetInheritedFrameType() == FRAME_TYPE_BROWSER),
+ "Isolated mozbrowser should only be true inside browser frames");
+ bool result = (GetInheritedFrameType() == FRAME_TYPE_BROWSER) &&
+ mOriginAttributes.mInIsolatedMozBrowser;
+ *aIsInIsolatedMozBrowserElement = result;
+ return NS_OK;
+}
+
+/* [infallible] */ NS_IMETHODIMP
+nsDocShell::GetIsInMozBrowserOrApp(bool* aIsInMozBrowserOrApp)
+{
+ *aIsInMozBrowserOrApp = (GetInheritedFrameType() != FRAME_TYPE_REGULAR);
+ return NS_OK;
+}
+
+/* [infallible] */ NS_IMETHODIMP
+nsDocShell::GetIsTopLevelContentDocShell(bool* aIsTopLevelContentDocShell)
+{
+ *aIsTopLevelContentDocShell = false;
+
+ if (mItemType == typeContent) {
+ nsCOMPtr<nsIDocShellTreeItem> root;
+ GetSameTypeRootTreeItem(getter_AddRefs(root));
+ *aIsTopLevelContentDocShell = root.get() == static_cast<nsIDocShellTreeItem*>(this);
+ }
+
+ return NS_OK;
+}
+
+/* [infallible] */ NS_IMETHODIMP
+nsDocShell::GetAppId(uint32_t* aAppId)
+{
+ if (mOriginAttributes.mAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID) {
+ *aAppId = mOriginAttributes.mAppId;
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIDocShell> parent;
+ GetSameTypeParentIgnoreBrowserAndAppBoundaries(getter_AddRefs(parent));
+
+ if (!parent) {
+ *aAppId = nsIScriptSecurityManager::NO_APP_ID;
+ return NS_OK;
+ }
+
+ return parent->GetAppId(aAppId);
+}
+
+// Implements nsILoadContext.originAttributes
+NS_IMETHODIMP
+nsDocShell::GetOriginAttributes(JS::MutableHandle<JS::Value> aVal)
+{
+ JSContext* cx = nsContentUtils::GetCurrentJSContext();
+ MOZ_ASSERT(cx);
+
+ return GetOriginAttributes(cx, aVal);
+}
+
+// Implements nsIDocShell.GetOriginAttributes()
+NS_IMETHODIMP
+nsDocShell::GetOriginAttributes(JSContext* aCx,
+ JS::MutableHandle<JS::Value> aVal)
+{
+ bool ok = ToJSValue(aCx, mOriginAttributes, aVal);
+ NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
+ return NS_OK;
+}
+
+bool
+nsDocShell::CanSetOriginAttributes()
+{
+ MOZ_ASSERT(mChildList.IsEmpty());
+ if (!mChildList.IsEmpty()) {
+ return false;
+ }
+
+ // TODO: Bug 1273058 - mContentViewer should be null when setting origin
+ // attributes.
+ if (mContentViewer) {
+ nsIDocument* doc = mContentViewer->GetDocument();
+ if (doc) {
+ nsIURI* uri = doc->GetDocumentURI();
+ if (!uri) {
+ return false;
+ }
+ nsCString uriSpec = uri->GetSpecOrDefault();
+ MOZ_ASSERT(uriSpec.EqualsLiteral("about:blank"));
+ if (!uriSpec.EqualsLiteral("about:blank")) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+nsresult
+nsDocShell::SetOriginAttributes(const DocShellOriginAttributes& aAttrs)
+{
+ if (!CanSetOriginAttributes()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ AssertOriginAttributesMatchPrivateBrowsing();
+ mOriginAttributes = aAttrs;
+
+ bool isPrivate = mOriginAttributes.mPrivateBrowsingId > 0;
+ // Chrome docshell can not contain OriginAttributes.mPrivateBrowsingId
+ if (mItemType == typeChrome && isPrivate) {
+ mOriginAttributes.mPrivateBrowsingId = 0;
+ }
+
+ SetPrivateBrowsing(isPrivate);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetOriginAttributesBeforeLoading(JS::Handle<JS::Value> aOriginAttributes)
+{
+ if (!aOriginAttributes.isObject()) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ AutoJSAPI jsapi;
+ if (!jsapi.Init(&aOriginAttributes.toObject())) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ JSContext* cx = jsapi.cx();
+ if (NS_WARN_IF(!cx)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ DocShellOriginAttributes attrs;
+ if (!aOriginAttributes.isObject() || !attrs.Init(cx, aOriginAttributes)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ return SetOriginAttributes(attrs);
+}
+
+NS_IMETHODIMP
+nsDocShell::SetOriginAttributes(JS::Handle<JS::Value> aOriginAttributes,
+ JSContext* aCx)
+{
+ DocShellOriginAttributes attrs;
+ if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ return SetOriginAttributes(attrs);
+}
+
+NS_IMETHODIMP
+nsDocShell::GetAppManifestURL(nsAString& aAppManifestURL)
+{
+ uint32_t appId = nsIDocShell::GetAppId();
+ if (appId != nsIScriptSecurityManager::NO_APP_ID &&
+ appId != nsIScriptSecurityManager::UNKNOWN_APP_ID) {
+ nsCOMPtr<nsIAppsService> appsService =
+ do_GetService(APPS_SERVICE_CONTRACTID);
+ NS_ASSERTION(appsService, "No AppsService available");
+ appsService->GetManifestURLByLocalId(appId, aAppManifestURL);
+ } else {
+ aAppManifestURL.SetLength(0);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetAsyncPanZoomEnabled(bool* aOut)
+{
+ if (nsIPresShell* presShell = GetPresShell()) {
+ *aOut = presShell->AsyncPanZoomEnabled();
+ return NS_OK;
+ }
+
+ // If we don't have a presShell, fall back to the default platform value of
+ // whether or not APZ is enabled.
+ *aOut = gfxPlatform::AsyncPanZoomEnabled();
+ return NS_OK;
+}
+
+bool
+nsDocShell::HasUnloadedParent()
+{
+ RefPtr<nsDocShell> parent = GetParentDocshell();
+ while (parent) {
+ bool inUnload = false;
+ parent->GetIsInUnload(&inUnload);
+ if (inUnload) {
+ return true;
+ }
+ parent = parent->GetParentDocshell();
+ }
+ return false;
+}
+
+bool
+nsDocShell::IsInvisible()
+{
+ return mInvisible;
+}
+
+void
+nsDocShell::SetInvisible(bool aInvisible)
+{
+ mInvisible = aInvisible;
+}
+
+void
+nsDocShell::SetOpener(nsITabParent* aOpener)
+{
+ mOpener = do_GetWeakReference(aOpener);
+}
+
+nsITabParent*
+nsDocShell::GetOpener()
+{
+ nsCOMPtr<nsITabParent> opener(do_QueryReferent(mOpener));
+ return opener;
+}
+
+// The caller owns |aAsyncCause| here.
+void
+nsDocShell::NotifyJSRunToCompletionStart(const char* aReason,
+ const char16_t* aFunctionName,
+ const char16_t* aFilename,
+ const uint32_t aLineNumber,
+ JS::Handle<JS::Value> aAsyncStack,
+ const char* aAsyncCause)
+{
+ // If first start, mark interval start.
+ if (mJSRunToCompletionDepth == 0) {
+ RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
+ if (timelines && timelines->HasConsumer(this)) {
+ timelines->AddMarkerForDocShell(this, Move(
+ mozilla::MakeUnique<JavascriptTimelineMarker>(
+ aReason, aFunctionName, aFilename, aLineNumber, MarkerTracingType::START,
+ aAsyncStack, aAsyncCause)));
+ }
+ }
+
+ mJSRunToCompletionDepth++;
+}
+
+void
+nsDocShell::NotifyJSRunToCompletionStop()
+{
+ mJSRunToCompletionDepth--;
+
+ // If last stop, mark interval end.
+ if (mJSRunToCompletionDepth == 0) {
+ RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
+ if (timelines && timelines->HasConsumer(this)) {
+ timelines->AddMarkerForDocShell(this, "Javascript", MarkerTracingType::END);
+ }
+ }
+}
+
+void
+nsDocShell::MaybeNotifyKeywordSearchLoading(const nsString& aProvider,
+ const nsString& aKeyword)
+{
+ if (aProvider.IsEmpty()) {
+ return;
+ }
+
+ if (XRE_IsContentProcess()) {
+ dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
+ if (contentChild) {
+ contentChild->SendNotifyKeywordSearchLoading(aProvider, aKeyword);
+ }
+ return;
+ }
+
+#ifdef MOZ_TOOLKIT_SEARCH
+ nsCOMPtr<nsIBrowserSearchService> searchSvc =
+ do_GetService("@mozilla.org/browser/search-service;1");
+ if (searchSvc) {
+ nsCOMPtr<nsISearchEngine> searchEngine;
+ searchSvc->GetEngineByName(aProvider, getter_AddRefs(searchEngine));
+ if (searchEngine) {
+ nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
+ if (obsSvc) {
+ // Note that "keyword-search" refers to a search via the url
+ // bar, not a bookmarks keyword search.
+ obsSvc->NotifyObservers(searchEngine, "keyword-search", aKeyword.get());
+ }
+ }
+ }
+#endif
+}
+
+NS_IMETHODIMP
+nsDocShell::ShouldPrepareForIntercept(nsIURI* aURI, bool aIsNonSubresourceRequest,
+ bool* aShouldIntercept)
+{
+ *aShouldIntercept = false;
+ // No in private browsing
+ if (UsePrivateBrowsing()) {
+ return NS_OK;
+ }
+
+ if (mSandboxFlags) {
+ // If we're sandboxed, don't intercept.
+ return NS_OK;
+ }
+
+ RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+ if (!swm) {
+ return NS_OK;
+ }
+
+ nsresult result;
+ nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
+ do_GetService(THIRDPARTYUTIL_CONTRACTID, &result);
+ NS_ENSURE_SUCCESS(result, result);
+
+ if (mCurrentURI &&
+ nsContentUtils::CookiesBehavior() == nsICookieService::BEHAVIOR_REJECT_FOREIGN) {
+ nsAutoCString uriSpec;
+ if (!(mCurrentURI->GetSpecOrDefault().EqualsLiteral("about:blank"))) {
+ // Reject the interception of third-party iframes if the cookie behaviour
+ // is set to reject all third-party cookies (1). In case that this pref
+ // is not set or can't be read, we default to allow all cookies (0) as
+ // this is the default value in all.js.
+ bool isThirdPartyURI = true;
+ result = thirdPartyUtil->IsThirdPartyURI(mCurrentURI, aURI,
+ &isThirdPartyURI);
+ if (NS_FAILED(result)) {
+ return result;
+ }
+
+ if (isThirdPartyURI) {
+ return NS_OK;
+ }
+ }
+ }
+
+ if (aIsNonSubresourceRequest) {
+ PrincipalOriginAttributes attrs;
+ attrs.InheritFromDocShellToDoc(mOriginAttributes, aURI);
+ nsCOMPtr<nsIPrincipal> principal =
+ BasePrincipal::CreateCodebasePrincipal(aURI, attrs);
+ *aShouldIntercept = swm->IsAvailable(principal, aURI);
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIDocument> doc = GetDocument();
+ if (!doc) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ ErrorResult rv;
+ *aShouldIntercept = swm->IsControlled(doc, rv);
+ if (NS_WARN_IF(rv.Failed())) {
+ return rv.StealNSResult();
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::ChannelIntercepted(nsIInterceptedChannel* aChannel)
+{
+ RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+ if (!swm) {
+ aChannel->Cancel(NS_ERROR_INTERCEPTION_FAILED);
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIChannel> channel;
+ nsresult rv = aChannel->GetChannel(getter_AddRefs(channel));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIDocument> doc;
+
+ bool isSubresourceLoad = !nsContentUtils::IsNonSubresourceRequest(channel);
+ if (isSubresourceLoad) {
+ doc = GetDocument();
+ if (!doc) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ } else {
+ // For top-level navigations, save a document ID which will be passed to
+ // the FetchEvent as the clientId later on.
+ rv = nsIDocument::GenerateDocumentId(mInterceptedDocumentId);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ bool isReload = mLoadType & LOAD_CMD_RELOAD;
+
+ nsCOMPtr<nsIURI> uri;
+ rv = channel->GetURI(getter_AddRefs(uri));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ PrincipalOriginAttributes attrs;
+ attrs.InheritFromDocShellToDoc(mOriginAttributes, uri);
+
+ ErrorResult error;
+ swm->DispatchFetchEvent(attrs, doc, mInterceptedDocumentId, aChannel,
+ isReload, isSubresourceLoad, error);
+ if (NS_WARN_IF(error.Failed())) {
+ return error.StealNSResult();
+ }
+
+ return NS_OK;
+}
+
+bool
+nsDocShell::InFrameSwap()
+{
+ RefPtr<nsDocShell> shell = this;
+ do {
+ if (shell->mInFrameSwap) {
+ return true;
+ }
+ shell = shell->GetParentDocshell();
+ } while (shell);
+ return false;
+}
+
+NS_IMETHODIMP
+nsDocShell::IssueWarning(uint32_t aWarning, bool aAsError)
+{
+ if (mContentViewer) {
+ nsCOMPtr<nsIDocument> doc = mContentViewer->GetDocument();
+ if (doc) {
+ doc->WarnOnceAbout(nsIDocument::DeprecatedOperations(aWarning), aAsError);
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetEditingSession(nsIEditingSession** aEditSession)
+{
+ if (!NS_SUCCEEDED(EnsureEditorData())) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mEditorData->GetEditingSession(aEditSession);
+ return *aEditSession ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetScriptableTabChild(nsITabChild** aTabChild)
+{
+ *aTabChild = GetTabChild().take();
+ return *aTabChild ? NS_OK : NS_ERROR_FAILURE;
+}
+
+already_AddRefed<nsITabChild>
+nsDocShell::GetTabChild()
+{
+ nsCOMPtr<nsIDocShellTreeOwner> owner(mTreeOwner);
+ nsCOMPtr<nsITabChild> tc = do_GetInterface(owner);
+ return tc.forget();
+}
+
+nsICommandManager*
+nsDocShell::GetCommandManager()
+{
+ NS_ENSURE_SUCCESS(EnsureCommandHandler(), nullptr);
+ return mCommandManager;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetProcessLockReason(uint32_t* aReason)
+{
+ MOZ_ASSERT(aReason);
+
+ nsPIDOMWindowOuter* outer = GetWindow();
+ MOZ_ASSERT(outer);
+
+ // Check if we are a toplevel window
+ if (outer->GetScriptableParentOrNull()) {
+ *aReason = PROCESS_LOCK_IFRAME;
+ return NS_OK;
+ }
+
+ // If we have any other toplevel windows in our tab group, then we cannot
+ // perform the navigation.
+ nsTArray<nsPIDOMWindowOuter*> toplevelWindows =
+ outer->TabGroup()->GetTopLevelWindows();
+ if (toplevelWindows.Length() > 1) {
+ *aReason = PROCESS_LOCK_RELATED_CONTEXTS;
+ return NS_OK;
+ }
+ MOZ_ASSERT(toplevelWindows.Length() == 1);
+ MOZ_ASSERT(toplevelWindows[0] == outer);
+
+ // If we aren't in a content process, we cannot perform a cross-process load.
+ if (!XRE_IsContentProcess()) {
+ *aReason = PROCESS_LOCK_NON_CONTENT;
+ return NS_OK;
+ }
+
+ *aReason = PROCESS_LOCK_NONE;
+ return NS_OK;
+}