diff -r 000000000000 -r 6474c204b198 docshell/base/nsDocShell.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docshell/base/nsDocShell.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,13098 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=4 sw=4 tw=80 et: */ +/* 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 + +#include "mozilla/ArrayUtils.h" +#include "mozilla/Attributes.h" +#include "mozilla/AutoRestore.h" +#include "mozilla/Casting.h" +#include "mozilla/dom/ContentChild.h" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/TabChild.h" +#include "mozilla/EventStateManager.h" +#include "mozilla/Preferences.h" +#include "mozilla/Services.h" +#include "mozilla/StartupTimeline.h" +#include "mozilla/Telemetry.h" +#include "mozilla/unused.h" +#include "mozilla/VisualEventTracer.h" + +#ifdef MOZ_LOGGING +// so we can get logging even in release builds (but only for some things) +#define FORCE_PR_LOG 1 +#endif + +#include "nsIContent.h" +#include "nsIDocument.h" +#include "nsIDOMDocument.h" +#include "nsIDOMElement.h" +#include "nsIDOMStorage.h" +#include "nsPIDOMStorage.h" +#include "nsIContentViewer.h" +#include "nsIDocumentLoaderFactory.h" +#include "nsCURILoader.h" +#include "nsDocShellCID.h" +#include "nsDOMCID.h" +#include "nsNetUtil.h" +#include "nsRect.h" +#include "prenv.h" +#include "nsIMarkupDocumentViewer.h" +#include "nsIDOMWindow.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 "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" + +// 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" + +// Helper Classes +#include "nsError.h" +#include "nsEscape.h" + +// Interfaces Needed +#include "nsIUploadChannel.h" +#include "nsIUploadChannel2.h" +#include "nsIWebProgress.h" +#include "nsILayoutHistoryState.h" +#include "nsITimer.h" +#include "nsISHistoryInternal.h" +#include "nsIPrincipal.h" +#include "nsISHEntry.h" +#include "nsIWindowWatcher.h" +#include "nsIPromptFactory.h" +#include "nsITransportSecurityInfo.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 "mozIAsyncFavicons.h" +#endif +#include "nsINetworkSeer.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 "prlog.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 "nsCxPusher.h" +#include "nsIChannelPolicy.h" +#include "nsIContentSecurityPolicy.h" +#include "nsSandboxFlags.h" +#include "mozIThirdPartyUtil.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 "nsISHContainer.h" +#include "nsISHistory.h" +#include "nsISecureBrowserUI.h" +#include "nsIStringBundle.h" +#include "nsISupportsArray.h" +#include "nsIURIFixup.h" +#include "nsIURILoader.h" +#include "nsIWebBrowserFind.h" +#include "nsIWidget.h" +#include "mozilla/dom/EncodingUtils.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 +#define getpid _getpid +#else +#include // for getpid() +#endif + +using namespace mozilla; +using namespace mozilla::dom; + +// 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 PR_LOGGING +#ifdef DEBUG +static PRLogModuleInfo* gDocShellLog; +#endif +static PRLogModuleInfo* gDocShellLeakLog; +#endif + +const char kBrandBundleURL[] = "chrome://branding/locale/brand.properties"; +const char kAppstringsBundleURL[] = "chrome://global/locale/appstrings.properties"; + +static void +FavorPerformanceHint(bool perfOverStarvation) +{ + nsCOMPtr appShell = do_GetService(kAppShellCID); + if (appShell) { + appShell->FavorPerformanceHint(perfOverStarvation, + Preferences::GetUint("docshell.event_starvation_delay_hint", + NS_EVENT_STARVATION_DELAY_HINT)); + } +} + +//***************************************************************************** +// 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 *maxPerLink, bool *requireSameHost) +{ + bool allow = Preferences::GetBool(PREF_PINGS_ENABLED, false); + + *maxPerLink = 1; + *requireSameHost = true; + + if (allow) { + Preferences::GetInt(PREF_PINGS_MAX_PER_LINK, maxPerLink); + Preferences::GetBool(PREF_PINGS_REQUIRE_SAME_HOST, requireSameHost); + } + + return allow; +} + +static bool +CheckPingURI(nsIURI* uri, nsIContent* content) +{ + if (!uri) + return false; + + // Check with nsIScriptSecurityManager + nsCOMPtr ssmgr = + do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID); + NS_ENSURE_TRUE(ssmgr, false); + + nsresult rv = + ssmgr->CheckLoadURIWithPrincipal(content->NodePrincipal(), uri, + nsIScriptSecurityManager::STANDARD); + if (NS_FAILED(rv)) { + return false; + } + + // Ignore non-HTTP(S) + bool match; + if ((NS_FAILED(uri->SchemeIs("http", &match)) || !match) && + (NS_FAILED(uri->SchemeIs("https", &match)) || !match)) { + return false; + } + + // Check with contentpolicy + int16_t shouldLoad = nsIContentPolicy::ACCEPT; + rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_PING, + uri, + content->NodePrincipal(), + content, + EmptyCString(), // mime hint + nullptr, //extra + &shouldLoad); + return NS_SUCCEEDED(rv) && NS_CP_ACCEPTED(shouldLoad); +} + +typedef void (* ForEachPingCallback)(void *closure, nsIContent *content, + nsIURI *uri, nsIIOService *ios); + +static void +ForEachPing(nsIContent *content, ForEachPingCallback callback, void *closure) +{ + // 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 or element in the HTML + // or XHTML namespace. + if (!content->IsHTML()) + return; + nsIAtom *nameAtom = content->Tag(); + if (nameAtom != nsGkAtoms::a && nameAtom != nsGkAtoms::area) + return; + + nsCOMPtr pingAtom = do_GetAtom("ping"); + if (!pingAtom) + return; + + nsAutoString value; + content->GetAttr(kNameSpaceID_None, pingAtom, value); + if (value.IsEmpty()) + return; + + nsCOMPtr ios = do_GetIOService(); + if (!ios) + return; + + nsIDocument *doc = content->OwnerDoc(); + + // value contains relative URIs split on spaces (U+0020) + const char16_t *start = value.BeginReading(); + const char16_t *end = value.EndReading(); + const char16_t *iter = start; + for (;;) { + if (iter < end && *iter != ' ') { + ++iter; + } else { // iter is pointing at either end or a space + while (*start == ' ' && start < iter) + ++start; + if (iter != start) { + nsCOMPtr uri, baseURI = content->GetBaseURI(); + ios->NewURI(NS_ConvertUTF16toUTF8(Substring(start, iter)), + doc->GetDocumentCharacterSet().get(), + baseURI, getter_AddRefs(uri)); + if (CheckPingURI(uri, content)) { + callback(closure, content, uri, ios); + } + } + start = iter = iter + 1; + if (iter >= end) + break; + } + } +} + +//---------------------------------------------------------------------- + +// We wait this many milliseconds before killing the ping channel... +#define PING_TIMEOUT 10000 + +static void +OnPingTimeout(nsITimer *timer, void *closure) +{ + nsILoadGroup *loadGroup = static_cast(closure); + if (loadGroup) + loadGroup->Cancel(NS_ERROR_ABORT); +} + +// Check to see if two URIs have the same host or not +static bool +IsSameHost(nsIURI *uri1, nsIURI *uri2) +{ + nsAutoCString host1, host2; + uri1->GetAsciiHost(host1); + uri2->GetAsciiHost(host2); + return host1.Equals(host2); +} + +class nsPingListener MOZ_FINAL : public nsIStreamListener + , public nsIInterfaceRequestor + , public nsIChannelEventSink +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIREQUESTOBSERVER + NS_DECL_NSISTREAMLISTENER + NS_DECL_NSIINTERFACEREQUESTOR + NS_DECL_NSICHANNELEVENTSINK + + nsPingListener(bool requireSameHost, nsIContent* content, nsILoadGroup* loadGroup) + : mRequireSameHost(requireSameHost), + mContent(content), + mLoadGroup(loadGroup) + {} + + ~nsPingListener(); + + nsresult StartTimeout(); + +private: + bool mRequireSameHost; + nsCOMPtr mContent; + nsCOMPtr mLoadGroup; + nsCOMPtr mTimer; +}; + +NS_IMPL_ISUPPORTS(nsPingListener, nsIStreamListener, nsIRequestObserver, + nsIInterfaceRequestor, nsIChannelEventSink) + +nsPingListener::~nsPingListener() +{ + if (mTimer) { + mTimer->Cancel(); + mTimer = nullptr; + } +} + +nsresult +nsPingListener::StartTimeout() +{ + nsCOMPtr 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 *request, nsISupports *context) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsPingListener::OnDataAvailable(nsIRequest *request, nsISupports *context, + nsIInputStream *stream, uint64_t offset, + uint32_t count) +{ + uint32_t result; + return stream->ReadSegments(NS_DiscardSegment, nullptr, count, &result); +} + +NS_IMETHODIMP +nsPingListener::OnStopRequest(nsIRequest *request, nsISupports *context, + nsresult status) +{ + mLoadGroup = nullptr; + + if (mTimer) { + mTimer->Cancel(); + mTimer = nullptr; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsPingListener::GetInterface(const nsIID &iid, void **result) +{ + if (iid.Equals(NS_GET_IID(nsIChannelEventSink))) { + NS_ADDREF_THIS(); + *result = (nsIChannelEventSink *) this; + return NS_OK; + } + + return NS_ERROR_NO_INTERFACE; +} + +NS_IMETHODIMP +nsPingListener::AsyncOnChannelRedirect(nsIChannel *oldChan, nsIChannel *newChan, + uint32_t flags, + nsIAsyncVerifyRedirectCallback *callback) +{ + nsCOMPtr newURI; + newChan->GetURI(getter_AddRefs(newURI)); + + if (!CheckPingURI(newURI, mContent)) + return NS_ERROR_ABORT; + + if (!mRequireSameHost) { + callback->OnRedirectVerifyCallback(NS_OK); + return NS_OK; + } + + // XXXbz should this be using something more like the nsContentUtils + // same-origin checker? + nsCOMPtr oldURI; + oldChan->GetURI(getter_AddRefs(oldURI)); + NS_ENSURE_STATE(oldURI && newURI); + + if (!IsSameHost(oldURI, newURI)) + return NS_ERROR_ABORT; + + callback->OnRedirectVerifyCallback(NS_OK); + return NS_OK; +} + +struct SendPingInfo { + int32_t numPings; + int32_t maxPings; + bool requireSameHost; + nsIURI *target; + nsIURI *referrer; +}; + +static void +SendPing(void *closure, nsIContent *content, nsIURI *uri, nsIIOService *ios) +{ + SendPingInfo *info = static_cast(closure); + if (info->numPings >= info->maxPings) + return; + + if (info->requireSameHost) { + // Make sure the referrer and the given uri share the same origin. We + // only require the same hostname. The scheme and port may differ. + if (!IsSameHost(uri, info->referrer)) + return; + } + + nsIDocument *doc = content->OwnerDoc(); + + nsCOMPtr chan; + ios->NewChannelFromURI(uri, getter_AddRefs(chan)); + if (!chan) + return; + + // Don't bother caching the result of this URI load. + chan->SetLoadFlags(nsIRequest::INHIBIT_CACHING); + + nsCOMPtr httpChan = do_QueryInterface(chan); + if (!httpChan) + return; + + // This is needed in order for 3rd-party cookie blocking to work. + nsCOMPtr 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 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, uri, 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->SetReferrer(info->referrer); + } + + nsCOMPtr uploadChan = do_QueryInterface(httpChan); + if (!uploadChan) + return; + + NS_NAMED_LITERAL_CSTRING(uploadData, "PING"); + + nsCOMPtr 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 loadGroup = + do_CreateInstance(NS_LOADGROUP_CONTRACTID); + if (!loadGroup) + return; + chan->SetLoadGroup(loadGroup); + + // Construct a listener that merely discards any response. If successful at + // opening the channel, then it is not necessary to hold a reference to the + // channel. The networking subsystem will take care of that for us. + nsPingListener *pingListener = + new nsPingListener(info->requireSameHost, content, loadGroup); + if (!pingListener) + return; + + nsCOMPtr listener(pingListener); + + // Observe redirects as well: + nsCOMPtr callbacks = do_QueryInterface(listener); + NS_ASSERTION(callbacks, "oops"); + loadGroup->SetNotificationCallbacks(callbacks); + + chan->AsyncOpen(listener, nullptr); + + // 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); + } +} + +// Spec: http://whatwg.org/specs/web-apps/current-work/#ping +static void +DispatchPings(nsIContent *content, nsIURI *target, nsIURI *referrer) +{ + SendPingInfo info; + + if (!PingsEnabled(&info.maxPings, &info.requireSameHost)) + return; + if (info.maxPings == 0) + return; + + info.numPings = 0; + info.target = target; + info.referrer = referrer; + + ForEachPing(content, SendPing, &info); +} + +static nsDOMPerformanceNavigationType +ConvertLoadTypeToNavigationType(uint32_t aLoadType) +{ + // Not initialized, assume it's normal load. + if (aLoadType == 0) { + aLoadType = LOAD_NORMAL; + } + + nsDOMPerformanceNavigationType result = dom::PerformanceNavigation::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 = dom::PerformanceNavigation::TYPE_NAVIGATE; + break; + case LOAD_HISTORY: + result = dom::PerformanceNavigation::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 = dom::PerformanceNavigation::TYPE_RELOAD; + break; + case LOAD_STOP_CONTENT_AND_REPLACE: + case LOAD_REFRESH: + case LOAD_BYPASS_HISTORY: + case LOAD_ERROR_PAGE: + case LOAD_PUSHSTATE: + result = dom::PerformanceNavigation::TYPE_RESERVED; + break; + default: + // NS_NOTREACHED("Unexpected load type value"); + result = dom::PerformanceNavigation::TYPE_RESERVED; + break; + } + + return result; +} + +static nsISHEntry* GetRootSHEntry(nsISHEntry *entry); + +static void +IncreasePrivateDocShellCount() +{ + gNumberOfPrivateDocShells++; + if (gNumberOfPrivateDocShells > 1 || + XRE_GetProcessType() != GeckoProcessType_Content) { + return; + } + + mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton(); + cc->SendPrivateDocShellsExist(true); +} + +static void +DecreasePrivateDocShellCount() +{ + MOZ_ASSERT(gNumberOfPrivateDocShells > 0); + gNumberOfPrivateDocShells--; + if (!gNumberOfPrivateDocShells) + { + if (XRE_GetProcessType() == GeckoProcessType_Content) { + mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton(); + cc->SendPrivateDocShellsExist(false); + return; + } + + nsCOMPtr obsvc = mozilla::services::GetObserverService(); + if (obsvc) { + obsvc->NotifyObservers(nullptr, "last-pb-context-exited", nullptr); + } + } +} + +//***************************************************************************** +//*** nsDocShell: Object Management +//***************************************************************************** + +static uint64_t gDocshellIDCounter = 0; + +// Note: operator new zeros our memory +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), + mFullscreenAllowed(CHECK_ATTRIBUTES), + mCreated(false), + mAllowSubframes(true), + mAllowPlugins(true), + mAllowJavascript(true), + mAllowMetaRedirects(true), + mAllowImages(true), + mAllowMedia(true), + mAllowDNSPrefetch(true), + mAllowWindowControl(true), + mAllowContentRetargeting(true), + mCreatingDocument(false), + mUseErrorPages(false), + mObserveErrorPages(true), + mAllowAuth(true), + mAllowKeywordFixup(false), + mIsOffScreenBrowser(false), + mIsActive(true), + mIsAppTab(false), + mUseGlobalHistory(false), + mInPrivateBrowsing(false), + mUseRemoteTabs(false), + mDeviceSizeIsPageSize(false), + mCanExecuteScripts(false), + mFiredUnloadEvent(false), + mEODForCurrentDocument(false), + mURIResultedInDocument(false), + mIsBeingDestroyed(false), + mIsExecutingOnLoadHandler(false), + mIsPrintingOrPP(false), + mSavingOldViewer(false), +#ifdef DEBUG + mInEnsureScriptEnv(false), +#endif + mAffectPrivateSessionLifetime(true), + mInvisible(false), + mDefaultLoadFlags(nsIRequest::LOAD_NORMAL), + mFrameType(eFrameTypeRegular), + mOwnOrContainingAppId(nsIScriptSecurityManager::UNKNOWN_APP_ID), + mParentCharsetSource(0) +{ + mHistoryID = ++gDocshellIDCounter; + if (gDocShellCount++ == 0) { + NS_ASSERTION(sURIFixup == nullptr, + "Huh, sURIFixup not null in first nsDocShell ctor!"); + + CallGetService(NS_URIFIXUP_CONTRACTID, &sURIFixup); + } + +#ifdef PR_LOGGING +#ifdef DEBUG + if (! gDocShellLog) + gDocShellLog = PR_NewLogModule("nsDocShell"); +#endif + if (nullptr == gDocShellLeakLog) + gDocShellLeakLog = PR_NewLogModule("nsDocShellLeak"); + if (gDocShellLeakLog) + PR_LOG(gDocShellLeakLog, PR_LOG_DEBUG, ("DOCSHELL %p created\n", this)); +#endif + +#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(), + SafeCast(mHistoryID)); + } +#endif +} + +nsDocShell::~nsDocShell() +{ + Destroy(); + + nsCOMPtr + shPrivate(do_QueryInterface(mSessionHistory)); + if (shPrivate) { + shPrivate->SetRootDocShell(nullptr); + } + + if (--gDocShellCount == 0) { + NS_IF_RELEASE(sURIFixup); + } + +#ifdef PR_LOGGING + if (gDocShellLeakLog) + PR_LOG(gDocShellLeakLog, PR_LOG_DEBUG, ("DOCSHELL %p destroyed\n", this)); +#endif + +#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(), + SafeCast(mHistoryID)); + } +#endif +} + +nsresult +nsDocShell::Init() +{ + nsresult rv = nsDocLoader::Init(); + NS_ENSURE_SUCCESS(rv, rv); + + NS_ASSERTION(mLoadGroup, "Something went wrong!"); + + mContentListener = new nsDSURIContentListener(this); + NS_ENSURE_TRUE(mContentListener, NS_ERROR_OUT_OF_MEMORY); + + 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 proxy = + new InterfaceRequestorProxy(static_cast + (this)); + NS_ENSURE_TRUE(proxy, NS_ERROR_OUT_OF_MEMORY); + 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 shell; + nsTObserverArray::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(); +} + +//***************************************************************************** +// nsDocShell::nsISupports +//***************************************************************************** + +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_END_INHERITING(nsDocLoader) + +///***************************************************************************** +// nsDocShell::nsIInterfaceRequestor +//***************************************************************************** +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(nsPIDOMWindow)) || + 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 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 contentViewer; + GetContentViewer(getter_AddRefs(contentViewer)); + if (!contentViewer) + return NS_ERROR_NO_INTERFACE; + + nsCOMPtr domDoc; + contentViewer->GetDOMDocument(getter_AddRefs(domDoc)); + NS_ASSERTION(domDoc, "Should have a document."); + if (!domDoc) + return NS_ERROR_NO_INTERFACE; + +#if defined(PR_LOGGING) && defined(DEBUG) + PR_LOG(gDocShellLog, PR_LOG_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 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, &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 shistory; + nsresult + rv = + GetSessionHistory(getter_AddRefs(shistory)); + if (NS_SUCCEEDED(rv) && shistory) { + *aSink = shistory; + NS_ADDREF((nsISupports *) * 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)) && NS_SUCCEEDED(EnsureEditorData())) { + nsCOMPtr editingSession; + mEditorData->GetEditingSession(getter_AddRefs(editingSession)); + if (editingSession) + { + *aSink = editingSession; + NS_ADDREF((nsISupports *)*aSink); + return NS_OK; + } + + return 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 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))) { + nsCOMPtr treeOwner; + nsresult rv = GetTreeOwner(getter_AddRefs(treeOwner)); + if (NS_SUCCEEDED(rv) && treeOwner) { + nsCOMPtr ir = do_QueryInterface(treeOwner); + if (ir) + return ir->GetInterface(aIID, aSink); + } + } + else if (aIID.Equals(NS_GET_IID(nsIContentFrameMessageManager))) { + nsCOMPtr tabChild = + do_GetInterface(static_cast(this)); + nsCOMPtr mm; + if (tabChild) { + tabChild-> + GetMessageManager(getter_AddRefs(mm)); + } else { + nsCOMPtr win = + do_GetInterface(static_cast(this)); + if (win) { + 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; +} + +//***************************************************************************** +// nsDocShell::nsIDocShell +//***************************************************************************** +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. + if (IsPrintingOrPP()) { + return NS_OK; // JS may not handle returning of an error code + } + nsCOMPtr referrer; + nsCOMPtr postStream; + nsCOMPtr headersStream; + nsCOMPtr owner; + bool inheritOwner = false; + bool ownerIsExplicit = false; + bool sendReferrer = true; + bool isSrcdoc = false; + nsCOMPtr shEntry; + nsXPIDLString target; + nsAutoString srcdoc; + nsCOMPtr sourceDocShell; + nsCOMPtr 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)); + + nsDocShellInfoLoadType lt = nsIDocShellLoadInfo::loadNormal; + aLoadInfo->GetLoadType(<); + // Get the appropriate loadType from nsIDocShellLoadInfo type + loadType = ConvertDocShellLoadInfoToLoadType(lt); + + aLoadInfo->GetOwner(getter_AddRefs(owner)); + aLoadInfo->GetInheritOwner(&inheritOwner); + aLoadInfo->GetOwnerIsExplicit(&ownerIsExplicit); + aLoadInfo->GetSHEntry(getter_AddRefs(shEntry)); + aLoadInfo->GetTarget(getter_Copies(target)); + aLoadInfo->GetPostDataStream(getter_AddRefs(postStream)); + aLoadInfo->GetHeadersStream(getter_AddRefs(headersStream)); + aLoadInfo->GetSendReferrer(&sendReferrer); + aLoadInfo->GetIsSrcdocLoad(&isSrcdoc); + aLoadInfo->GetSrcdocData(srcdoc); + aLoadInfo->GetSourceDocShell(getter_AddRefs(sourceDocShell)); + aLoadInfo->GetBaseURI(getter_AddRefs(baseURI)); + } + +#if defined(PR_LOGGING) && defined(DEBUG) + if (PR_LOG_TEST(gDocShellLog, PR_LOG_DEBUG)) { + nsAutoCString uristr; + aURI->GetAsciiSpec(uristr); + PR_LOG(gDocShellLog, PR_LOG_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 parentAsItem; + GetSameTypeParent(getter_AddRefs(parentAsItem)); + nsCOMPtr parentDS(do_QueryInterface(parentAsItem)); + uint32_t parentLoadType; + + if (parentDS && parentDS != static_cast(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 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 == nullptr) { + // 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 + PR_LOG(gDocShellLog, PR_LOG_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 an owner (a referring principal). + // + // If ownerIsExplicit 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 inheritOwner should be true and InternalLoad will get + // an owner from the current document. If none of these things are + // true, then + // (4) we pass a null owner into the channel, and an owner will be + // created later from the channel's internal data. + // + // If ownerIsExplicit *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 inheritOwner should be true and InternalLoad will get an owner + // from the current document. If none of these things are true, then + // (4) we pass a null owner into the channel, and an owner will be + // created later from the channel's internal data. + // + // NOTE: This all only works because the only thing the owner is used + // for in InternalLoad is data:, javascript:, and about:blank + // URIs. For other URIs this would all be dead wrong! + + if (owner && mItemType != typeChrome) { + nsCOMPtr ownerPrincipal = do_QueryInterface(owner); + if (nsContentUtils::IsSystemOrExpandedPrincipal(ownerPrincipal)) { + if (ownerIsExplicit) { + return NS_ERROR_DOM_SECURITY_ERR; + } + owner = nullptr; + inheritOwner = true; + } + } + if (!owner && !inheritOwner && !ownerIsExplicit) { + // See if there's system or chrome JS code running + inheritOwner = nsContentUtils::IsSystemPrincipal( + nsContentUtils::GetSubjectPrincipal()); + } + + if (aLoadFlags & LOAD_FLAGS_DISALLOW_INHERIT_OWNER) { + inheritOwner = false; + owner = do_CreateInstance("@mozilla.org/nullprincipal;1"); + } + + uint32_t flags = 0; + + if (inheritOwner) + flags |= INTERNAL_LOAD_FLAGS_INHERIT_OWNER; + + 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_FIXUP_SCHEME_TYPOS) + flags |= INTERNAL_LOAD_FLAGS_FIXUP_SCHEME_TYPOS; + + 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, + referrer, + owner, + flags, + target.get(), + 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 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; + if (aLoadInfo) { + nsDocShellInfoLoadType lt = nsIDocShellLoadInfo::loadNormal; + (void) aLoadInfo->GetLoadType(<); + // Get the appropriate LoadType from nsIDocShellLoadInfo type + loadType = ConvertDocShellLoadInfoToLoadType(lt); + } + + NS_ENSURE_SUCCESS(Stop(nsIWebNavigation::STOP_NETWORK), NS_ERROR_FAILURE); + + mLoadType = loadType; + + // build up a channel for this stream. + nsCOMPtr channel; + NS_ENSURE_SUCCESS(NS_NewInputStreamChannel + (getter_AddRefs(channel), uri, aStream, + aContentType, aContentCharset), + NS_ERROR_FAILURE); + + nsCOMPtr + 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(); + NS_ENSURE_TRUE(loadInfo, NS_ERROR_OUT_OF_MEMORY); + nsCOMPtr localRef(loadInfo); + + *aLoadInfo = localRef; + NS_ADDREF(*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 kungFuDeathGrip(mContentViewer); + mFiredUnloadEvent = true; + + if (mTiming) { + mTiming->NotifyUnloadEventStart(); + } + + mContentViewer->PageHide(aIsUnload); + + if (mTiming) { + mTiming->NotifyUnloadEventEnd(); + } + + nsAutoTArray, 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) { + return; + } + + mTiming = new nsDOMNavigationTiming(); + mTiming->NotifyNavigationStart(); +} + + +// +// 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; + } + + // Get origin document principal + nsCOMPtr originDocument(do_GetInterface(aOriginTreeItem)); + NS_ENSURE_TRUE(originDocument, false); + + // Get target principal + nsCOMPtr targetDocument(do_GetInterface(aTargetTreeItem)); + 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 originURI; + nsCOMPtr targetURI; + nsCOMPtr innerOriginURI; + nsCOMPtr 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; +} + +NS_IMETHODIMP +nsDocShell::GetEldestPresContext(nsPresContext** aPresContext) +{ + NS_ENSURE_ARG_POINTER(aPresContext); + *aPresContext = nullptr; + + nsCOMPtr viewer = mContentViewer; + while (viewer) { + nsCOMPtr 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() +{ + nsRefPtr 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; + + nsRefPtr 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 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 handler = mChromeEventHandler; + handler.forget(aChromeEventHandler); + return NS_OK; +} + +/* void setCurrentURI (in nsIURI uri); */ +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) +{ +#ifdef PR_LOGGING + if (gDocShellLeakLog && PR_LOG_TEST(gDocShellLeakLog, PR_LOG_DEBUG)) { + nsAutoCString spec; + if (aURI) + aURI->GetSpec(spec); + PR_LogPrint("DOCSHELL %p SetCurrentURI %s\n", this, spec.get()); + } +#endif + + // 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); + + bool isRoot = false; // Is this the root docshell + bool isSubFrame = false; // Is this a subframe navigation? + + nsCOMPtr root; + + GetSameTypeRootTreeItem(getter_AddRefs(root)); + if (root.get() == static_cast(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 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 jarChannel = do_QueryInterface(channel); + if (!jarChannel) { + return NS_OK; + } + + return jarChannel->GetIsUnsafe(aUnsafe); +} + +NS_IMETHODIMP +nsDocShell::GetHasMixedActiveContentLoaded(bool* aHasMixedActiveContentLoaded) +{ + nsCOMPtr doc(do_GetInterface(GetAsSupports(this))); + *aHasMixedActiveContentLoaded = doc && doc->GetHasMixedActiveContentLoaded(); + return NS_OK; +} + +NS_IMETHODIMP +nsDocShell::GetHasMixedActiveContentBlocked(bool* aHasMixedActiveContentBlocked) +{ + nsCOMPtr doc(do_GetInterface(GetAsSupports(this))); + *aHasMixedActiveContentBlocked = doc && doc->GetHasMixedActiveContentBlocked(); + return NS_OK; +} + +NS_IMETHODIMP +nsDocShell::GetHasMixedDisplayContentLoaded(bool* aHasMixedDisplayContentLoaded) +{ + nsCOMPtr doc(do_GetInterface(GetAsSupports(this))); + *aHasMixedDisplayContentLoaded = doc && doc->GetHasMixedDisplayContentLoaded(); + return NS_OK; +} + +NS_IMETHODIMP +nsDocShell::GetHasMixedDisplayContentBlocked(bool* aHasMixedDisplayContentBlocked) +{ + nsCOMPtr doc(do_GetInterface(GetAsSupports(this))); + *aHasMixedDisplayContentBlocked = doc && doc->GetHasMixedDisplayContentBlocked(); + 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); + + *aUsePrivateBrowsing = mInPrivateBrowsing; + 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); + + return SetPrivateBrowsing(aUsePrivateBrowsing); +} + +NS_IMETHODIMP +nsDocShell::SetPrivateBrowsing(bool aUsePrivateBrowsing) +{ + bool changed = aUsePrivateBrowsing != mInPrivateBrowsing; + if (changed) { + mInPrivateBrowsing = aUsePrivateBrowsing; + if (mAffectPrivateSessionLifetime) { + if (aUsePrivateBrowsing) { + IncreasePrivateDocShellCount(); + } else { + DecreasePrivateDocShellCount(); + } + } + } + + nsTObserverArray::ForwardIterator iter(mChildList); + while (iter.HasMore()) { + nsCOMPtr shell = do_QueryObject(iter.GetNext()); + if (shell) { + shell->SetPrivateBrowsing(aUsePrivateBrowsing); + } + } + + if (changed) { + nsTObserverArray::ForwardIterator iter(mPrivacyObservers); + while (iter.HasMore()) { + nsWeakPtr ref = iter.GetNext(); + nsCOMPtr obs = do_QueryReferent(ref); + if (!obs) { + mPrivacyObservers.RemoveElement(ref); + } else { + obs->PrivateModeChanged(aUsePrivateBrowsing); + } + } + } + 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 && mInPrivateBrowsing) { + if (aAffectLifetime) { + IncreasePrivateDocShellCount(); + } else { + DecreasePrivateDocShellCount(); + } + } + mAffectPrivateSessionLifetime = aAffectLifetime; + + nsTObserverArray::ForwardIterator iter(mChildList); + while (iter.HasMore()) { + nsCOMPtr 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::ForwardIterator iter(mReflowObservers); + while (iter.HasMore()) { + nsWeakPtr ref = iter.GetNext(); + nsCOMPtr 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) { + nsPIDOMWindow* innerWin = mScriptGlobal->GetCurrentInnerWindow(); + if (innerWin) { + 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) +{ + mAllowContentRetargeting = aAllowContentRetargeting; + 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; + + // For non-browsers/apps, check that the enclosing iframe element + // has the allowfullscreen attribute set to true. If any ancestor + // iframe does not have mozallowfullscreen=true, then fullscreen is + // prohibited. + nsCOMPtr win = do_GetInterface(GetAsSupports(this)); + if (!win) { + return NS_OK; + } + nsCOMPtr frameElement = do_QueryInterface(win->GetFrameElementInternal()); + if (frameElement && + frameElement->IsHTML(nsGkAtoms::iframe) && + !frameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::allowfullscreen) && + !frameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::mozallowfullscreen)) { + 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. + nsCOMPtr dsti = do_GetInterface(GetAsSupports(this)); + NS_ENSURE_TRUE(dsti, NS_OK); + + nsCOMPtr parentTreeItem; + dsti->GetParent(getter_AddRefs(parentTreeItem)); + if (!parentTreeItem) { + *aFullscreenAllowed = true; + return NS_OK; + } + // Otherwise, we have a parent, continue the checking for + // mozFullscreenAllowed in the parent docshell's ancestors. + nsCOMPtr parent = do_QueryInterface(parentTreeItem); + NS_ENSURE_TRUE(parent, NS_OK); + + return parent->GetFullscreenAllowed(aFullscreenAllowed); +} + +NS_IMETHODIMP +nsDocShell::SetFullscreenAllowed(bool aFullscreenAllowed) +{ + if (!nsIDocShell::GetIsBrowserOrApp()) { + // 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; +} + +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 **outEnum) +{ + NS_ENSURE_ARG_POINTER(outEnum); + *outEnum = nullptr; + + nsRefPtr docShellEnum; + if (aDirection == ENUMERATE_FORWARDS) + docShellEnum = new nsDocShellForwardsEnumerator; + else + docShellEnum = new nsDocShellBackwardsEnumerator; + + if (!docShellEnum) return NS_ERROR_OUT_OF_MEMORY; + + 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 **)outEnum); + + 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 *zoom) +{ + NS_ENSURE_ARG_POINTER(zoom); + *zoom = 1.0f; + return NS_OK; +} + +NS_IMETHODIMP +nsDocShell::SetZoom(float zoom) +{ + 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* aTookFocus) +{ + NS_ENSURE_ARG_POINTER(aTookFocus); + + nsCOMPtr chromeFocus = do_GetInterface(mTreeOwner); + if (chromeFocus) { + if (aForward) + *aTookFocus = NS_SUCCEEDED(chromeFocus->FocusNextElement()); + else + *aTookFocus = NS_SUCCEEDED(chromeFocus->FocusPrevElement()); + } 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::ForwardIterator iter(mChildList); + while (iter.HasMore()) { + nsCOMPtr 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::ForwardIterator iter(mChildList); + while (iter.HasMore()) { + nsCOMPtr shell = do_QueryObject(iter.GetNext()); + if (shell) { + static_cast(shell.get())-> + HistoryTransactionRemoved(aIndex); + } + } + + return NS_OK; +} + +nsIDOMStorageManager* +nsDocShell::TopSessionStorageManager() +{ + nsresult rv; + + nsCOMPtr topItem; + rv = GetSameTypeRootTreeItem(getter_AddRefs(topItem)); + if (NS_FAILED(rv)) { + return nullptr; + } + + if (!topItem) { + return nullptr; + } + + nsDocShell* topDocShell = static_cast(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 manager = TopSessionStorageManager(); + if (!manager) { + return NS_ERROR_UNEXPECTED; + } + + nsCOMPtr thirdPartyUtil = + do_GetService(THIRDPARTYUTIL_CONTRACTID); + if (!thirdPartyUtil) + return NS_ERROR_FAILURE; + + nsCOMPtr doc(do_GetInterface(GetAsSupports(this))); + nsCOMPtr firstPartyIsolationURI; + nsresult rv = thirdPartyUtil->GetFirstPartyIsolationURI(nullptr, doc, + getter_AddRefs(firstPartyIsolationURI)); + NS_ENSURE_SUCCESS(rv, rv); + + if (aCreate) { + return manager->CreateStorageForFirstParty(firstPartyIsolationURI, + aPrincipal, aDocumentURI, + mInPrivateBrowsing, aStorage); + } + + return manager->GetStorageForFirstParty(firstPartyIsolationURI, aPrincipal, + mInPrivateBrowsing, aStorage); +} + +// Bacause it is not called from anywhere, nsDocShell::AddSessionStorage() +// does not need to be modified to isolate DOM Storage to the first party URI. +nsresult +nsDocShell::AddSessionStorage(nsIPrincipal* aPrincipal, + nsIDOMStorage* aStorage) +{ + nsCOMPtr pistorage = do_QueryInterface(aStorage); + nsIPrincipal* storagePrincipal = pistorage->GetPrincipal(); + if (storagePrincipal != aPrincipal) { + NS_ERROR("Wanting to add a sessionStorage for different principal"); + return NS_ERROR_DOM_SECURITY_ERR; + } + + nsCOMPtr 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; +} + +NS_IMETHODIMP +nsDocShell::NotifyScrollObservers() +{ + nsTObserverArray::ForwardIterator iter(mScrollObservers); + while (iter.HasMore()) { + nsWeakPtr ref = iter.GetNext(); + nsCOMPtr 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 char16_t *aName, bool *_retval) +{ + NS_ENSURE_ARG_POINTER(aName); + NS_ENSURE_ARG_POINTER(_retval); + *_retval = mName.Equals(aName); + 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 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; + + nsRefPtr 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::GetParentDocshell() +{ + nsCOMPtr docshell = do_QueryInterface(GetAsSupports(mParent)); + return docshell.forget().downcast(); +} + +void +nsDocShell::RecomputeCanExecuteScripts() +{ + bool old = mCanExecuteScripts; + nsRefPtr 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::ForwardIterator iter(mChildList); + while (iter.HasMore()) { + static_cast(iter.GetNext())->RecomputeCanExecuteScripts(); + } + } +} + +nsresult +nsDocShell::SetDocLoaderParent(nsDocLoader * aParent) +{ + bool wasFrame = IsFrame(); + + nsDocLoader::SetDocLoaderParent(aParent); + + nsCOMPtr 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; + nsCOMPtr parentAsDocShell(do_QueryInterface(parent)); + if (parentAsDocShell) + { + if (NS_SUCCEEDED(parentAsDocShell->GetAllowPlugins(&value))) + { + SetAllowPlugins(value); + } + if (NS_SUCCEEDED(parentAsDocShell->GetAllowJavascript(&value))) + { + SetAllowJavascript(value); + } + if (NS_SUCCEEDED(parentAsDocShell->GetAllowMetaRedirects(&value))) + { + SetAllowMetaRedirects(value); + } + if (NS_SUCCEEDED(parentAsDocShell->GetAllowSubframes(&value))) + { + SetAllowSubframes(value); + } + if (NS_SUCCEEDED(parentAsDocShell->GetAllowImages(&value))) + { + SetAllowImages(value); + } + SetAllowMedia(parentAsDocShell->GetAllowMedia()); + if (NS_SUCCEEDED(parentAsDocShell->GetAllowWindowControl(&value))) + { + SetAllowWindowControl(value); + } + SetAllowContentRetargeting( + parentAsDocShell->GetAllowContentRetargeting()); + if (NS_SUCCEEDED(parentAsDocShell->GetIsActive(&value))) + { + SetIsActive(value); + } + if (NS_FAILED(parentAsDocShell->GetAllowDNSPrefetch(&value))) { + value = false; + } + SetAllowDNSPrefetch(value); + value = parentAsDocShell->GetAffectPrivateSessionLifetime(); + SetAffectPrivateSessionLifetime(value); + uint32_t flags; + if (NS_SUCCEEDED(parentAsDocShell->GetDefaultLoadFlags(&flags))) + { + SetDefaultLoadFlags(flags); + } + + } + + nsCOMPtr parentAsLoadContext(do_QueryInterface(parent)); + if (parentAsLoadContext && + NS_SUCCEEDED(parentAsLoadContext->GetUsePrivateBrowsing(&value))) + { + SetPrivateBrowsing(value); + } + + nsCOMPtr parentURIListener(do_GetInterface(parent)); + if (parentURIListener) + mContentListener->SetParentContentListener(parentURIListener); + + // Our parent has changed. Recompute scriptability. + RecomputeCanExecuteScripts(); + + return NS_OK; +} + +NS_IMETHODIMP +nsDocShell::GetSameTypeParent(nsIDocShellTreeItem ** aParent) +{ + NS_ENSURE_ARG_POINTER(aParent); + *aParent = nullptr; + + if (nsIDocShell::GetIsBrowserOrApp()) { + return NS_OK; + } + + nsCOMPtr 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 parent = + do_QueryInterface(GetAsSupports(mParent)); + if (!parent) + return NS_OK; + + if (parent->ItemType() == mItemType) { + nsCOMPtr parentDS = do_QueryInterface(parent); + parentDS.forget(aParent); + } + return NS_OK; +} + +NS_IMETHODIMP +nsDocShell::GetRootTreeItem(nsIDocShellTreeItem ** aRootTreeItem) +{ + NS_ENSURE_ARG_POINTER(aRootTreeItem); + *aRootTreeItem = static_cast(this); + + nsCOMPtr parent; + NS_ENSURE_SUCCESS(GetParent(getter_AddRefs(parent)), NS_ERROR_FAILURE); + while (parent) { + *aRootTreeItem = parent; + NS_ENSURE_SUCCESS((*aRootTreeItem)->GetParent(getter_AddRefs(parent)), + NS_ERROR_FAILURE); + } + NS_ADDREF(*aRootTreeItem); + return NS_OK; +} + +NS_IMETHODIMP +nsDocShell::GetSameTypeRootTreeItem(nsIDocShellTreeItem ** aRootTreeItem) +{ + NS_ENSURE_ARG_POINTER(aRootTreeItem); + *aRootTreeItem = static_cast(this); + + nsCOMPtr 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; +} + +/* 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 targetDS = do_QueryInterface(aTargetItem); + nsCOMPtr accessingDS = do_QueryInterface(aAccessingItem); + if (!!targetDS != !!accessingDS) { + // We must be able to convert both or neither to nsIDocShell. + return false; + } + + if (targetDS && accessingDS && + (targetDS->GetIsInBrowserElement() != + accessingDS->GetIsInBrowserElement() || + targetDS->GetAppId() != accessingDS->GetAppId())) { + return false; + } + + nsCOMPtr accessingRoot; + aAccessingItem->GetSameTypeRootTreeItem(getter_AddRefs(accessingRoot)); + + if (aTargetItem == accessingRoot) { + // A frame can navigate its root. + return true; + } + + // Check if aAccessingItem can navigate one of aTargetItem's ancestors. + nsCOMPtr target = aTargetItem; + do { + if (ValidateOrigin(aAccessingItem, target)) { + return true; + } + + nsCOMPtr parent; + target->GetSameTypeParent(getter_AddRefs(parent)); + parent.swap(target); + } while (target); + + nsCOMPtr targetRoot; + aTargetItem->GetSameTypeRootTreeItem(getter_AddRefs(targetRoot)); + + 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 targetWindow = do_GetInterface(aTargetItem); + if (!targetWindow) { + NS_ERROR("This should not happen, really"); + return false; + } + + nsCOMPtr targetOpener; + targetWindow->GetOpener(getter_AddRefs(targetOpener)); + nsCOMPtr openerWebNav(do_GetInterface(targetOpener)); + nsCOMPtr openerItem(do_QueryInterface(openerWebNav)); + + if (!openerItem) { + return false; + } + + return CanAccessItem(openerItem, aAccessingItem, false); +} + +static bool +ItemIsActive(nsIDocShellTreeItem *aItem) +{ + nsCOMPtr window(do_GetInterface(aItem)); + + if (window) { + bool isClosed; + + if (NS_SUCCEEDED(window->GetClosed(&isClosed)) && !isClosed) { + return true; + } + } + + return false; +} + +NS_IMETHODIMP +nsDocShell::FindItemWithName(const char16_t * aName, + nsISupports * aRequestor, + nsIDocShellTreeItem * aOriginalRequestor, + nsIDocShellTreeItem ** _retval) +{ + NS_ENSURE_ARG(aName); + NS_ENSURE_ARG_POINTER(_retval); + + // If we don't find one, we return NS_OK and a null result + *_retval = nullptr; + + if (!*aName) + 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, + _retval); + } 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 foundItem; + nsDependentString name(aName); + if (name.LowerCaseEqualsLiteral("_self")) { + foundItem = this; + } + else if (name.LowerCaseEqualsLiteral("_blank")) + { + // Just return null. Caller must handle creating a new window with + // a blank name himself. + return NS_OK; + } + else if (name.LowerCaseEqualsLiteral("_parent")) + { + GetSameTypeParent(getter_AddRefs(foundItem)); + if(!foundItem) + foundItem = this; + } + else if (name.LowerCaseEqualsLiteral("_top")) + { + GetSameTypeRootTreeItem(getter_AddRefs(foundItem)); + NS_ASSERTION(foundItem, "Must have this; worst case it's us!"); + } + // _main is an IE target which should be case-insensitive but isn't + // see bug 217886 for details + else if (name.LowerCaseEqualsLiteral("_content") || + name.EqualsLiteral("_main")) + { + // Must pass our same type root as requestor to the + // treeowner to make sure things work right. + nsCOMPtr root; + GetSameTypeRootTreeItem(getter_AddRefs(root)); + if (mTreeOwner) { + NS_ASSERTION(root, "Must have this; worst case it's us!"); + mTreeOwner->FindItemWithName(aName, root, aOriginalRequestor, + getter_AddRefs(foundItem)); + } +#ifdef DEBUG + else { + NS_ERROR("Someone isn't setting up the tree owner. " + "You might like to try that. " + "Things will.....you know, work."); + // Note: _content should always exist. If we don't have one + // hanging off the treeowner, just create a named window.... + // so don't return here, in case we did that and can now find + // it. + // XXXbz should we be using |root| instead of creating + // a new window? + } +#endif + } 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(*_retval); + } + return NS_OK; + } +} + +nsresult +nsDocShell::DoFindItemWithName(const char16_t* aName, + nsISupports* aRequestor, + nsIDocShellTreeItem* aOriginalRequestor, + nsIDocShellTreeItem** _retval) +{ + // First we check our name. + if (mName.Equals(aName) && ItemIsActive(this) && + CanAccessItem(this, aOriginalRequestor)) { + NS_ADDREF(*_retval = this); + return NS_OK; + } + + // This QI may fail, but the places where we want to compare, comparing + // against nullptr serves the same purpose. + nsCOMPtr 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, _retval); + NS_ASSERTION(NS_SUCCEEDED(rv), + "FindChildWithName should not be failing here."); + if (*_retval) + 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 parentAsTreeItem = + do_QueryInterface(GetAsSupports(mParent)); + if (parentAsTreeItem) { + if (parentAsTreeItem == reqAsTreeItem) + return NS_OK; + + if (parentAsTreeItem->ItemType() == mItemType) { + return parentAsTreeItem-> + FindItemWithName(aName, + static_cast + (this), + aOriginalRequestor, + _retval); + } + } + + // If the parent is null or not of the same type fall through and ask tree + // owner. + + // This may fail, but comparing against null serves the same purpose + nsCOMPtr + reqAsTreeOwner(do_QueryInterface(aRequestor)); + + if (mTreeOwner && mTreeOwner != reqAsTreeOwner) { + return mTreeOwner-> + FindItemWithName(aName, this, aOriginalRequestor, _retval); + } + + 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 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 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 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 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 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;iGetChildCount(&childWebshellCount); + nsCOMPtr parentAsDocShell(do_QueryInterface(aParentNode)); + int32_t type = aParentNode->ItemType(); + nsCOMPtr presShell = parentAsDocShell->GetPresShell(); + nsRefPtr presContext; + parentAsDocShell->GetPresContext(getter_AddRefs(presContext)); + nsIDocument *doc = presShell->GetDocument(); + + nsCOMPtr domwin(doc->GetWindow()); + + nsCOMPtr 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 child; + aParentNode->GetChildAt(i, getter_AddRefs(child)); + PrintDocTree(child, aLevel+1); + } + } +} + +static void +PrintDocTree(nsIDocShellTreeItem * aParentNode) +{ + NS_ASSERTION(aParentNode, "Pointer is null!"); + + nsCOMPtr parentItem; + aParentNode->GetParent(getter_AddRefs(parentItem)); + while (parentItem) { + nsCOMPtrtmp; + 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 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 webProgress = + do_QueryInterface(GetAsSupports(this)); + + if (webProgress) { + nsCOMPtr + oldListener(do_QueryInterface(mTreeOwner)); + nsCOMPtr + newListener(do_QueryInterface(aTreeOwner)); + + if (oldListener) { + webProgress->RemoveProgressListener(oldListener); + } + + if (newListener) { + webProgress->AddProgressListener(newListener, + nsIWebProgress::NOTIFY_ALL); + } + } + } + + mTreeOwner = aTreeOwner; // Weak reference per API + + nsTObserverArray::ForwardIterator iter(mChildList); + while (iter.HasMore()) { + nsCOMPtr 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); + + nsRefPtr 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) { + childsParent->RemoveChildLoader(childAsDocLoader); + } + + // 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 childDocShell = do_QueryInterface(aChild); + bool dynamic = false; + childDocShell->GetCreatedDynamically(&dynamic); + if (!dynamic) { + nsCOMPtr 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 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); + + nsRefPtr 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(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 char16_t * aName, + bool aRecurse, bool aSameType, + nsIDocShellTreeItem * aRequestor, + nsIDocShellTreeItem * aOriginalRequestor, + nsIDocShellTreeItem ** _retval) +{ + NS_ENSURE_ARG(aName); + NS_ENSURE_ARG_POINTER(_retval); + + *_retval = nullptr; // if we don't find one, we return NS_OK and a null result + + if (!*aName) + return NS_OK; + + nsXPIDLString childName; + nsTObserverArray::ForwardIterator iter(mChildList); + while (iter.HasMore()) { + nsCOMPtr 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(*_retval); + break; + } + + if (childType != mItemType) //Only ask it to check children if it is same type + continue; + + if (aRecurse && (aRequestor != child)) // Only ask the child if it isn't the requestor + { + // See if child contains the shell with the given name +#ifdef DEBUG + nsresult rv = +#endif + child->FindChildWithName(aName, true, + aSameType, + static_cast + (this), + aOriginalRequestor, + _retval); + NS_ASSERTION(NS_SUCCEEDED(rv), + "FindChildWithName should not fail here"); + if (*_retval) // 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 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 loadType, + bool aCloneChildren) +{ + nsresult rv; + + if (mLSHE && loadType != LOAD_PUSHSTATE) { + /* You get here if you are currently building a + * hierarchy ie.,you just visited a frameset page + */ + nsCOMPtr container(do_QueryInterface(mLSHE, &rv)); + if (container) { + rv = container->AddChild(aNewEntry, aChildOffset); + } + } + else if (!aCloneRef) { + /* This is an initial load in some subframe. Just append it if we can */ + nsCOMPtr container(do_QueryInterface(mOSHE, &rv)); + if (container) { + rv = container->AddChild(aNewEntry, aChildOffset); + } + } + else 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 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 currentEntry(do_QueryInterface(currentHE)); + if (currentEntry) { + uint32_t cloneID = 0; + nsCOMPtr nextEntry; + aCloneRef->GetID(&cloneID); + rv = CloneAndReplace(currentEntry, this, cloneID, aNewEntry, + aCloneChildren, getter_AddRefs(nextEntry)); + + if (NS_SUCCEEDED(rv)) { + nsCOMPtr + shPrivate(do_QueryInterface(mSessionHistory)); + NS_ENSURE_TRUE(shPrivate, NS_ERROR_FAILURE); + rv = shPrivate->AddEntry(nextEntry, true); + } + } + } + else { + /* Just pass this along */ + nsCOMPtr parent = + do_QueryInterface(GetAsSupports(mParent), &rv); + if (parent) { + rv = parent->AddChildSHEntry(aCloneRef, aNewEntry, aChildOffset, + loadType, aCloneChildren); + } + } + return rv; +} + +nsresult +nsDocShell::DoAddChildSHEntry(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 rootSH; + GetRootSessionHistory(getter_AddRefs(rootSH)); + if (rootSH) { + rootSH->GetIndex(&mPreviousTransIndex); + } + + nsresult rv; + nsCOMPtr 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 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 internalHistory; + nsCOMPtr sessionHistory; + nsCOMPtr root; + GetSameTypeRootTreeItem(getter_AddRefs(root)); + if (root) { + nsCOMPtr 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); + nsAutoTArray ids; + ids.AppendElement(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; +} + +NS_IMETHODIMP +nsDocShell::SetDeviceSizeIsPageSize(bool aValue) +{ + if (mDeviceSizeIsPageSize != aValue) { + mDeviceSizeIsPageSize = aValue; + nsRefPtr presContext; + GetPresContext(getter_AddRefs(presContext)); + if (presContext) { + presContext->MediaFeatureValuesChanged(presContext->eAlwaysRebuildStyle); + } + } + return NS_OK; +} + +NS_IMETHODIMP +nsDocShell::GetDeviceSizeIsPageSize(bool* aValue) +{ + *aValue = mDeviceSizeIsPageSize; + return NS_OK; +} + +void +nsDocShell::ClearFrameHistory(nsISHEntry* aEntry) +{ + nsCOMPtr shcontainer = do_QueryInterface(aEntry); + nsCOMPtr rootSH; + GetRootSessionHistory(getter_AddRefs(rootSH)); + nsCOMPtr history = do_QueryInterface(rootSH); + if (!history || !shcontainer) { + return; + } + + int32_t count = 0; + shcontainer->GetChildCount(&count); + nsAutoTArray ids; + for (int32_t i = 0; i < count; ++i) { + nsCOMPtr 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 isAllowed = !IsPrintingOrPP(aDisplayPrintErrorDialog) && !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 rootSH; + rv = GetRootSessionHistory(getter_AddRefs(rootSH)); + nsCOMPtr 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 rootSH; + rv = GetRootSessionHistory(getter_AddRefs(rootSH)); + nsCOMPtr 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 rootSH; + rv = GetRootSessionHistory(getter_AddRefs(rootSH)); + nsCOMPtr 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 rootSH; + rv = GetRootSessionHistory(getter_AddRefs(rootSH)); + nsCOMPtr 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 rootSH; + rv = GetRootSessionHistory(getter_AddRefs(rootSH)); + nsCOMPtr 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 LoadURIWithBase(aURI, aLoadFlags, aReferringURI, aPostStream, + aHeaderStream, nullptr); +} + +NS_IMETHODIMP +nsDocShell::LoadURIWithBase(const char16_t * aURI, + uint32_t aLoadFlags, + nsIURI * aReferringURI, + 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 uri; + nsCOMPtr 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; + } + + 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 fixupStream; + rv = sURIFixup->CreateFixupURI(uriString, fixupFlags, + getter_AddRefs(fixupStream), + getter_AddRefs(uri)); + if (fixupStream) { + // CreateFixupURI 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; + } + } + // else no fixup service so just use the URI we created and see + // what happens + + if (NS_ERROR_MALFORMED_URI == rv) { + DisplayLoadError(rv, uri, aURI, nullptr); + } + + 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 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->SetHeadersStream(aHeaderStream); + loadInfo->SetBaseURI(aBaseURI); + + 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) +{ + // Get prompt and string bundle servcies + nsCOMPtr prompter; + nsCOMPtr 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 nestedURI = do_QueryInterface(aURI); + while (nestedURI) { + nsCOMPtr tempURI; + nsresult rv2; + rv2 = nestedURI->GetInnerURI(getter_AddRefs(tempURI)); + if (NS_SUCCEEDED(rv2) && tempURI) { + tempURI->GetScheme(scheme); + formatStrs[0].Append(NS_LITERAL_STRING(", ")); + 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_UNKNOWN_HOST == aError) { + NS_ENSURE_ARG_POINTER(aURI); + // Get the host + nsAutoCString host; + nsCOMPtr 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) { + // CSP error + cssClass.AssignLiteral("neterror"); + error.AssignLiteral("cspFrameAncestorBlocked"); + } + else if (NS_ERROR_GET_MODULE(aError) == NS_ERROR_MODULE_SECURITY) { + nsCOMPtr 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 securityInfo; + nsCOMPtr tsi; + if (aFailedChannel) + aFailedChannel->GetSecurityInfo(getter_AddRefs(securityInfo)); + tsi = do_QueryInterface(securityInfo); + if (tsi) { + // 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 a Strict-Transport-Security host and the cert + // is bad, don't allow overrides (STS Spec section 7.3). + nsCOMPtr sss = + do_GetService(NS_SSSERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + uint32_t flags = + mInPrivateBrowsing ? nsISocketProvider::NO_PERMANENT_STORAGE : 0; + + bool isStsHost = false; + rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, + aURI, flags, &isStsHost); + NS_ENSURE_SUCCESS(rv, rv); + + uint32_t bucketId; + if (isStsHost) { + cssClass.AssignLiteral("badStsCert"); + //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; + } + + + if (Preferences::GetBool( + "browser.xul.error_pages.expert_bad_cert", false)) { + cssClass.AssignLiteral("expertBadCert"); + } + + // 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")) + mozilla::Telemetry::Accumulate(mozilla::Telemetry::SECURITY_UI, bucketId); + + } else { + error.AssignLiteral("nssFailure2"); + } + } + } else if (NS_ERROR_PHISHING_URI == aError || NS_ERROR_MALWARE_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; + if (NS_ERROR_PHISHING_URI == aError) { + error.AssignLiteral("phishingBlocked"); + bucketId = IsFrame() ? nsISecurityUITelemetry::WARNING_PHISHING_PAGE_FRAME : + nsISecurityUITelemetry::WARNING_PHISHING_PAGE_TOP ; + } else { + error.AssignLiteral("malwareBlocked"); + bucketId = IsFrame() ? nsISecurityUITelemetry::WARNING_MALWARE_PAGE_FRAME : + nsISecurityUITelemetry::WARNING_MALWARE_PAGE_TOP ; + } + + if (errorPage.EqualsIgnoreCase("blocked")) + mozilla::Telemetry::Accumulate(mozilla::Telemetry::SECURITY_UI, + bucketId); + + cssClass.AssignLiteral("blacklist"); + } else if (NS_ERROR_CONTENT_CRASHED == aError) { + errorPage.AssignLiteral("tabcrashed"); + error.AssignLiteral("tabcrashed"); + + nsCOMPtr handler = mChromeEventHandler; + if (handler) { + nsCOMPtr 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.Assign(NS_LITERAL_STRING(" ")); + } + } + 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("corruptedContentError"); + 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.AssignLiteral("?"); + } + 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 textToSubURI( + do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv)); + if (NS_SUCCEEDED(rv)) { + rv = textToSubURI->UnEscapeURIForUI(charset, spec, formatStrs[formatStrCount]); + } + } else { + spec.AssignLiteral("?"); + } + 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 (UseErrorPages()) { + // Display an error page + LoadErrorPage(aURI, aURL, errorPage.get(), error.get(), + messageStr.get(), cssClass.get(), aFailedChannel); + } + 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; +} + + +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(PR_LOGGING) && defined(DEBUG) + if (PR_LOG_TEST(gDocShellLog, PR_LOG_DEBUG)) { + nsAutoCString spec; + aURI->GetSpec(spec); + + nsAutoCString chanName; + if (aFailedChannel) + aFailedChannel->GetName(chanName); + else + chanName.AssignLiteral(""); + + PR_LOG(gDocShellLog, PR_LOG_DEBUG, + ("nsDocShell[%p]::LoadErrorPage(\"%s\", \"%s\", {...}, [%s])\n", this, + spec.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(cstring, escArg1, escArg2) \ + { \ + char* s = nsEscape(escArg1, escArg2); \ + if (!s) \ + return NS_ERROR_OUT_OF_MEMORY; \ + cstring.Adopt(s); \ + } + nsCString escapedUrl, escapedCharset, escapedError, escapedDescription, + escapedCSSClass; + SAFE_ESCAPE(escapedUrl, url.get(), url_Path); + SAFE_ESCAPE(escapedCharset, charset.get(), url_Path); + SAFE_ESCAPE(escapedError, + NS_ConvertUTF16toUTF8(aErrorType).get(), url_Path); + SAFE_ESCAPE(escapedDescription, + NS_ConvertUTF16toUTF8(aDescription).get(), url_Path); + if (aCSSClass) { + SAFE_ESCAPE(escapedCSSClass, aCSSClass, url_Path); + } + nsCString errorPageUrl("about:"); + errorPageUrl.AppendASCII(aErrorPage); + errorPageUrl.AppendLiteral("?e="); + + errorPageUrl.AppendASCII(escapedError.get()); + errorPageUrl.AppendLiteral("&u="); + errorPageUrl.AppendASCII(escapedUrl.get()); + 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).get(), + url_Path); + errorPageUrl.AppendLiteral("&m="); + errorPageUrl.AppendASCII(manifestParam.get()); + } + + // 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 errorPageURI; + rv = NS_NewURI(getter_AddRefs(errorPageURI), errorPageUrl); + NS_ENSURE_SUCCESS(rv, rv); + + return InternalLoad(errorPageURI, nullptr, nullptr, + INTERNAL_LOAD_FLAGS_INHERIT_OWNER, nullptr, 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 rootSH; + rv = GetRootSessionHistory(getter_AddRefs(rootSH)); + nsCOMPtr 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 doc(do_GetInterface(GetAsSupports(this))); + + // Do not inherit owner from document + uint32_t flags = INTERNAL_LOAD_FLAGS_NONE; + nsAutoString srcdoc; + nsIPrincipal* principal = nullptr; + nsAutoString contentTypeHint; + nsCOMPtr baseURI; + if (doc) { + principal = doc->NodePrincipal(); + doc->GetContentType(contentTypeHint); + + if (doc->IsSrcdocDocument()) { + doc->GetSrcdocData(srcdoc); + flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC; + baseURI = doc->GetBaseURI(); + } + } + rv = InternalLoad(mCurrentURI, + mReferrerURI, + principal, + flags, + nullptr, // 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 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::ForwardIterator iter(mChildList); + while (iter.HasMore()) { + nsCOMPtr 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 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(this)) { + mSessionHistory = aSessionHistory; + nsCOMPtr + 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 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 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 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); + } + + 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 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 parentNativeWindow, + nsIWidget * parentWidget, int32_t x, int32_t y, + int32_t cx, int32_t cy) +{ + SetParentWidget(parentWidget); + SetPositionAndSize(x, y, cx, cy, false); + + 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; + + mAllowSubframes = + Preferences::GetBool("browser.frames.enabled", mAllowSubframes); + + 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; + } + + mDeviceSizeIsPageSize = + Preferences::GetBool("docshell.device_size_is_page_size", + mDeviceSizeIsPageSize); + + nsCOMPtr 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 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; + + // 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 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 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 (mInPrivateBrowsing) { + mInPrivateBrowsing = false; + if (mAffectPrivateSessionLifetime) { + DecreasePrivateDocShellCount(); + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsDocShell::GetUnscaledDevicePixelsPerCSSPixel(double *aScale) +{ + if (mParentWidget) { + *aScale = mParentWidget->GetDefaultScale().scale; + return NS_OK; + } + + nsCOMPtr ownerWindow(do_QueryInterface(mTreeOwner)); + if (ownerWindow) { + return ownerWindow->GetUnscaledDevicePixelsPerCSSPixel(aScale); + } + + *aScale = 1.0; + return NS_OK; +} + +NS_IMETHODIMP +nsDocShell::SetPosition(int32_t x, int32_t y) +{ + mBounds.x = x; + mBounds.y = y; + + if (mContentViewer) + NS_ENSURE_SUCCESS(mContentViewer->Move(x, y), NS_ERROR_FAILURE); + + return NS_OK; +} + +NS_IMETHODIMP +nsDocShell::GetPosition(int32_t * aX, int32_t * aY) +{ + int32_t dummyHolder; + return GetPositionAndSize(aX, aY, &dummyHolder, &dummyHolder); +} + +NS_IMETHODIMP +nsDocShell::SetSize(int32_t aCX, int32_t aCY, bool aRepaint) +{ + int32_t x = 0, y = 0; + GetPosition(&x, &y); + return SetPositionAndSize(x, y, aCX, aCY, aRepaint); +} + +NS_IMETHODIMP +nsDocShell::GetSize(int32_t * aCX, int32_t * aCY) +{ + int32_t dummyHolder; + return GetPositionAndSize(&dummyHolder, &dummyHolder, aCX, aCY); +} + +NS_IMETHODIMP +nsDocShell::SetPositionAndSize(int32_t x, int32_t y, int32_t cx, + int32_t cy, bool fRepaint) +{ + mBounds.x = x; + mBounds.y = y; + mBounds.width = cx; + mBounds.height = cy; + + // Hold strong ref, since SetBounds can make us null out mContentViewer + nsCOMPtr viewer = mContentViewer; + if (viewer) { + //XXX Border figured in here or is that handled elsewhere? + NS_ENSURE_SUCCESS(viewer->SetBounds(mBounds), NS_ERROR_FAILURE); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsDocShell::GetPositionAndSize(int32_t * x, int32_t * y, int32_t * cx, + int32_t * cy) +{ + if (mParentWidget) { + // ensure size is up-to-date if window has changed resolution + nsIntRect r; + mParentWidget->GetClientBounds(r); + SetPositionAndSize(mBounds.x, mBounds.y, r.width, r.height, false); + } + + // We should really consider just getting this information from + // our window instead of duplicating the storage and code... + if (cx || cy) { + // Caller wants to know our size; make sure to give them up to + // date information. + nsCOMPtr doc(do_GetInterface(GetAsSupports(mParent))); + if (doc) { + doc->FlushPendingNotifications(Flush_Layout); + } + } + + DoGetPositionAndSize(x, y, cx, cy); + return NS_OK; +} + +void +nsDocShell::DoGetPositionAndSize(int32_t * x, int32_t * y, int32_t * cx, + int32_t * cy) +{ + if (x) + *x = mBounds.x; + if (y) + *y = mBounds.y; + if (cx) + *cx = mBounds.width; + if (cy) + *cy = mBounds.height; +} + +NS_IMETHODIMP +nsDocShell::Repaint(bool aForce) +{ + nsCOMPtr 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 ** parentWidget) +{ + NS_ENSURE_ARG_POINTER(parentWidget); + + *parentWidget = mParentWidget; + NS_IF_ADDREF(*parentWidget); + + return NS_OK; +} + +NS_IMETHODIMP +nsDocShell::SetParentWidget(nsIWidget * aParentWidget) +{ + mParentWidget = aParentWidget; + + return NS_OK; +} + +NS_IMETHODIMP +nsDocShell::GetParentNativeWindow(nativeWindow * parentNativeWindow) +{ + NS_ENSURE_ARG_POINTER(parentNativeWindow); + + if (mParentWidget) + *parentNativeWindow = mParentWidget->GetNativeData(NS_NATIVE_WIDGET); + else + *parentNativeWindow = nullptr; + + return NS_OK; +} + +NS_IMETHODIMP +nsDocShell::SetParentNativeWindow(nativeWindow parentNativeWindow) +{ + 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 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. + + nsCOMPtr treeItem = this; + nsCOMPtr parentItem; + treeItem->GetParent(getter_AddRefs(parentItem)); + while (parentItem) { + nsCOMPtr docShell(do_QueryInterface(treeItem)); + presShell = docShell->GetPresShell(); + + nsCOMPtr parentDS = do_QueryInterface(parentItem); + nsCOMPtr pPresShell = parentDS->GetPresShell(); + + // Null-check for crash in bug 267804 + if (!pPresShell) { + NS_NOTREACHED("parent docshell has null pres shell"); + return NS_OK; + } + + nsIContent *shellContent = + pPresShell->GetDocument()->FindContentForSubDocument(presShell->GetDocument()); + NS_ASSERTION(shellContent, "subshell not in the map"); + + nsIFrame* frame = shellContent ? shellContent->GetPrimaryFrame() : nullptr; + bool isDocShellOffScreen = false; + docShell->GetIsOffScreenBrowser(&isDocShellOffScreen); + if (frame && + !frame->IsVisibleConsideringAncestors(nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY) && + !isDocShellOffScreen) { + return NS_OK; + } + + treeItem = parentItem; + treeItem->GetParent(getter_AddRefs(parentItem)); + } + + nsCOMPtr 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; + + // Tell the PresShell about it. + nsCOMPtr pshell = GetPresShell(); + if (pshell) + pshell->SetIsActive(aIsActive); + + // Tell the window about it + if (mScriptGlobal) { + mScriptGlobal->SetIsBackground(!aIsActive); + if (nsCOMPtr doc = mScriptGlobal->GetExtantDoc()) { + doc->PostVisibilityUpdateEvent(); + } + } + + // Recursively tell all of our children, but don't tell