michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* vim: set ts=4 sw=4 tw=80 et: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsDocShell.h" michael@0: michael@0: #include michael@0: michael@0: #include "mozilla/ArrayUtils.h" michael@0: #include "mozilla/Attributes.h" michael@0: #include "mozilla/AutoRestore.h" michael@0: #include "mozilla/Casting.h" michael@0: #include "mozilla/dom/ContentChild.h" michael@0: #include "mozilla/dom/Element.h" michael@0: #include "mozilla/dom/TabChild.h" michael@0: #include "mozilla/EventStateManager.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/Services.h" michael@0: #include "mozilla/StartupTimeline.h" michael@0: #include "mozilla/Telemetry.h" michael@0: #include "mozilla/unused.h" michael@0: #include "mozilla/VisualEventTracer.h" michael@0: michael@0: #ifdef MOZ_LOGGING michael@0: // so we can get logging even in release builds (but only for some things) michael@0: #define FORCE_PR_LOG 1 michael@0: #endif michael@0: michael@0: #include "nsIContent.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsIDOMDocument.h" michael@0: #include "nsIDOMElement.h" michael@0: #include "nsIDOMStorage.h" michael@0: #include "nsPIDOMStorage.h" michael@0: #include "nsIContentViewer.h" michael@0: #include "nsIDocumentLoaderFactory.h" michael@0: #include "nsCURILoader.h" michael@0: #include "nsDocShellCID.h" michael@0: #include "nsDOMCID.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsRect.h" michael@0: #include "prenv.h" michael@0: #include "nsIMarkupDocumentViewer.h" michael@0: #include "nsIDOMWindow.h" michael@0: #include "nsIWebBrowserChrome.h" michael@0: #include "nsPoint.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsIPrompt.h" michael@0: #include "nsIAuthPrompt.h" michael@0: #include "nsIAuthPrompt2.h" michael@0: #include "nsIChannelEventSink.h" michael@0: #include "nsIAsyncVerifyRedirectCallback.h" michael@0: #include "nsIScriptSecurityManager.h" michael@0: #include "nsIScriptObjectPrincipal.h" michael@0: #include "nsIScrollableFrame.h" michael@0: #include "nsContentPolicyUtils.h" // NS_CheckContentLoadPolicy(...) michael@0: #include "nsISeekableStream.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "nsIWritablePropertyBag2.h" michael@0: #include "nsIAppShell.h" michael@0: #include "nsWidgetsCID.h" michael@0: #include "nsIInterfaceRequestorUtils.h" michael@0: #include "nsView.h" michael@0: #include "nsViewManager.h" michael@0: #include "nsIScriptChannel.h" michael@0: #include "nsITimedChannel.h" michael@0: #include "nsIPrivacyTransitionObserver.h" michael@0: #include "nsIReflowObserver.h" michael@0: #include "nsIScrollObserver.h" michael@0: #include "nsIDocShellTreeItem.h" michael@0: #include "nsIChannel.h" michael@0: #include "IHistory.h" michael@0: #include "nsViewSourceHandler.h" michael@0: michael@0: // we want to explore making the document own the load group michael@0: // so we can associate the document URI with the load group. michael@0: // until this point, we have an evil hack: michael@0: #include "nsIHttpChannelInternal.h" michael@0: #include "nsPILoadGroupInternal.h" michael@0: michael@0: // Local Includes michael@0: #include "nsDocShellLoadInfo.h" michael@0: #include "nsCDefaultURIFixup.h" michael@0: #include "nsDocShellEnumerator.h" michael@0: #include "nsSHistory.h" michael@0: #include "nsDocShellEditorData.h" michael@0: michael@0: // Helper Classes michael@0: #include "nsError.h" michael@0: #include "nsEscape.h" michael@0: michael@0: // Interfaces Needed michael@0: #include "nsIUploadChannel.h" michael@0: #include "nsIUploadChannel2.h" michael@0: #include "nsIWebProgress.h" michael@0: #include "nsILayoutHistoryState.h" michael@0: #include "nsITimer.h" michael@0: #include "nsISHistoryInternal.h" michael@0: #include "nsIPrincipal.h" michael@0: #include "nsISHEntry.h" michael@0: #include "nsIWindowWatcher.h" michael@0: #include "nsIPromptFactory.h" michael@0: #include "nsITransportSecurityInfo.h" michael@0: #include "nsINSSErrorsService.h" michael@0: #include "nsIApplicationCacheChannel.h" michael@0: #include "nsIApplicationCacheContainer.h" michael@0: #include "nsStreamUtils.h" michael@0: #include "nsIController.h" michael@0: #include "nsPICommandUpdater.h" michael@0: #include "nsIDOMHTMLAnchorElement.h" michael@0: #include "nsIWebBrowserChrome3.h" michael@0: #include "nsITabChild.h" michael@0: #include "nsISiteSecurityService.h" michael@0: #include "nsStructuredCloneContainer.h" michael@0: #include "nsIStructuredCloneContainer.h" michael@0: #ifdef MOZ_PLACES michael@0: #include "nsIFaviconService.h" michael@0: #include "mozIAsyncFavicons.h" michael@0: #endif michael@0: #include "nsINetworkSeer.h" michael@0: michael@0: // Editor-related michael@0: #include "nsIEditingSession.h" michael@0: michael@0: #include "nsPIDOMWindow.h" michael@0: #include "nsGlobalWindow.h" michael@0: #include "nsPIWindowRoot.h" michael@0: #include "nsICachingChannel.h" michael@0: #include "nsIMultiPartChannel.h" michael@0: #include "nsIWyciwygChannel.h" michael@0: michael@0: // For reporting errors with the console service. michael@0: // These can go away if error reporting is propagated up past nsDocShell. michael@0: #include "nsIScriptError.h" michael@0: michael@0: // used to dispatch urls to default protocol handlers michael@0: #include "nsCExternalHandlerService.h" michael@0: #include "nsIExternalProtocolService.h" michael@0: michael@0: #include "nsFocusManager.h" michael@0: michael@0: #include "nsITextToSubURI.h" michael@0: michael@0: #include "nsIJARChannel.h" michael@0: michael@0: #include "prlog.h" michael@0: michael@0: #include "nsISelectionDisplay.h" michael@0: michael@0: #include "nsIGlobalHistory2.h" michael@0: michael@0: #include "nsIFrame.h" michael@0: #include "nsSubDocumentFrame.h" michael@0: michael@0: // for embedding michael@0: #include "nsIWebBrowserChromeFocus.h" michael@0: michael@0: #if NS_PRINT_PREVIEW michael@0: #include "nsIDocumentViewerPrint.h" michael@0: #include "nsIWebBrowserPrint.h" michael@0: #endif michael@0: michael@0: #include "nsContentUtils.h" michael@0: #include "nsCxPusher.h" michael@0: #include "nsIChannelPolicy.h" michael@0: #include "nsIContentSecurityPolicy.h" michael@0: #include "nsSandboxFlags.h" michael@0: #include "mozIThirdPartyUtil.h" michael@0: #include "nsXULAppAPI.h" michael@0: #include "nsDOMNavigationTiming.h" michael@0: #include "nsISecurityUITelemetry.h" michael@0: #include "nsIAppsService.h" michael@0: #include "nsDSURIContentListener.h" michael@0: #include "nsDocShellLoadTypes.h" michael@0: #include "nsDocShellTransferableHooks.h" michael@0: #include "nsICommandManager.h" michael@0: #include "nsIDOMNode.h" michael@0: #include "nsIDocShellTreeOwner.h" michael@0: #include "nsIHttpChannel.h" michael@0: #include "nsISHContainer.h" michael@0: #include "nsISHistory.h" michael@0: #include "nsISecureBrowserUI.h" michael@0: #include "nsIStringBundle.h" michael@0: #include "nsISupportsArray.h" michael@0: #include "nsIURIFixup.h" michael@0: #include "nsIURILoader.h" michael@0: #include "nsIWebBrowserFind.h" michael@0: #include "nsIWidget.h" michael@0: #include "mozilla/dom/EncodingUtils.h" michael@0: michael@0: static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID); michael@0: michael@0: #if defined(DEBUG_bryner) || defined(DEBUG_chb) michael@0: //#define DEBUG_DOCSHELL_FOCUS michael@0: #define DEBUG_PAGE_CACHE michael@0: #endif michael@0: michael@0: #ifdef XP_WIN michael@0: #include michael@0: #define getpid _getpid michael@0: #else michael@0: #include // for getpid() michael@0: #endif michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: michael@0: // True means sUseErrorPages has been added to preferences var cache. michael@0: static bool gAddedPreferencesVarCache = false; michael@0: michael@0: bool nsDocShell::sUseErrorPages = false; michael@0: michael@0: // Number of documents currently loading michael@0: static int32_t gNumberOfDocumentsLoading = 0; michael@0: michael@0: // Global count of existing docshells. michael@0: static int32_t gDocShellCount = 0; michael@0: michael@0: // Global count of docshells with the private attribute set michael@0: static uint32_t gNumberOfPrivateDocShells = 0; michael@0: michael@0: // Global reference to the URI fixup service. michael@0: nsIURIFixup *nsDocShell::sURIFixup = 0; michael@0: michael@0: // True means we validate window targets to prevent frameset michael@0: // spoofing. Initialize this to a non-bolean value so we know to check michael@0: // the pref on the creation of the first docshell. michael@0: static uint32_t gValidateOrigin = 0xffffffff; michael@0: michael@0: // Hint for native dispatch of events on how long to delay after michael@0: // all documents have loaded in milliseconds before favoring normal michael@0: // native event dispatch priorites over performance michael@0: // Can be overridden with docshell.event_starvation_delay_hint pref. michael@0: #define NS_EVENT_STARVATION_DELAY_HINT 2000 michael@0: michael@0: #ifdef PR_LOGGING michael@0: #ifdef DEBUG michael@0: static PRLogModuleInfo* gDocShellLog; michael@0: #endif michael@0: static PRLogModuleInfo* gDocShellLeakLog; michael@0: #endif michael@0: michael@0: const char kBrandBundleURL[] = "chrome://branding/locale/brand.properties"; michael@0: const char kAppstringsBundleURL[] = "chrome://global/locale/appstrings.properties"; michael@0: michael@0: static void michael@0: FavorPerformanceHint(bool perfOverStarvation) michael@0: { michael@0: nsCOMPtr appShell = do_GetService(kAppShellCID); michael@0: if (appShell) { michael@0: appShell->FavorPerformanceHint(perfOverStarvation, michael@0: Preferences::GetUint("docshell.event_starvation_delay_hint", michael@0: NS_EVENT_STARVATION_DELAY_HINT)); michael@0: } michael@0: } michael@0: michael@0: //***************************************************************************** michael@0: // support michael@0: //***************************************************************************** michael@0: michael@0: #define PREF_PINGS_ENABLED "browser.send_pings" michael@0: #define PREF_PINGS_MAX_PER_LINK "browser.send_pings.max_per_link" michael@0: #define PREF_PINGS_REQUIRE_SAME_HOST "browser.send_pings.require_same_host" michael@0: michael@0: // Check prefs to see if pings are enabled and if so what restrictions might michael@0: // be applied. michael@0: // michael@0: // @param maxPerLink michael@0: // This parameter returns the number of pings that are allowed per link click michael@0: // michael@0: // @param requireSameHost michael@0: // This parameter returns true if pings are restricted to the same host as michael@0: // the document in which the click occurs. If the same host restriction is michael@0: // imposed, then we still allow for pings to cross over to different michael@0: // protocols and ports for flexibility and because it is not possible to send michael@0: // a ping via FTP. michael@0: // michael@0: // @returns michael@0: // true if pings are enabled and false otherwise. michael@0: // michael@0: static bool michael@0: PingsEnabled(int32_t *maxPerLink, bool *requireSameHost) michael@0: { michael@0: bool allow = Preferences::GetBool(PREF_PINGS_ENABLED, false); michael@0: michael@0: *maxPerLink = 1; michael@0: *requireSameHost = true; michael@0: michael@0: if (allow) { michael@0: Preferences::GetInt(PREF_PINGS_MAX_PER_LINK, maxPerLink); michael@0: Preferences::GetBool(PREF_PINGS_REQUIRE_SAME_HOST, requireSameHost); michael@0: } michael@0: michael@0: return allow; michael@0: } michael@0: michael@0: static bool michael@0: CheckPingURI(nsIURI* uri, nsIContent* content) michael@0: { michael@0: if (!uri) michael@0: return false; michael@0: michael@0: // Check with nsIScriptSecurityManager michael@0: nsCOMPtr ssmgr = michael@0: do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID); michael@0: NS_ENSURE_TRUE(ssmgr, false); michael@0: michael@0: nsresult rv = michael@0: ssmgr->CheckLoadURIWithPrincipal(content->NodePrincipal(), uri, michael@0: nsIScriptSecurityManager::STANDARD); michael@0: if (NS_FAILED(rv)) { michael@0: return false; michael@0: } michael@0: michael@0: // Ignore non-HTTP(S) michael@0: bool match; michael@0: if ((NS_FAILED(uri->SchemeIs("http", &match)) || !match) && michael@0: (NS_FAILED(uri->SchemeIs("https", &match)) || !match)) { michael@0: return false; michael@0: } michael@0: michael@0: // Check with contentpolicy michael@0: int16_t shouldLoad = nsIContentPolicy::ACCEPT; michael@0: rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_PING, michael@0: uri, michael@0: content->NodePrincipal(), michael@0: content, michael@0: EmptyCString(), // mime hint michael@0: nullptr, //extra michael@0: &shouldLoad); michael@0: return NS_SUCCEEDED(rv) && NS_CP_ACCEPTED(shouldLoad); michael@0: } michael@0: michael@0: typedef void (* ForEachPingCallback)(void *closure, nsIContent *content, michael@0: nsIURI *uri, nsIIOService *ios); michael@0: michael@0: static void michael@0: ForEachPing(nsIContent *content, ForEachPingCallback callback, void *closure) michael@0: { michael@0: // NOTE: Using nsIDOMHTMLAnchorElement::GetPing isn't really worth it here michael@0: // since we'd still need to parse the resulting string. Instead, we michael@0: // just parse the raw attribute. It might be nice if the content node michael@0: // implemented an interface that exposed an enumeration of nsIURIs. michael@0: michael@0: // Make sure we are dealing with either an or element in the HTML michael@0: // or XHTML namespace. michael@0: if (!content->IsHTML()) michael@0: return; michael@0: nsIAtom *nameAtom = content->Tag(); michael@0: if (nameAtom != nsGkAtoms::a && nameAtom != nsGkAtoms::area) michael@0: return; michael@0: michael@0: nsCOMPtr pingAtom = do_GetAtom("ping"); michael@0: if (!pingAtom) michael@0: return; michael@0: michael@0: nsAutoString value; michael@0: content->GetAttr(kNameSpaceID_None, pingAtom, value); michael@0: if (value.IsEmpty()) michael@0: return; michael@0: michael@0: nsCOMPtr ios = do_GetIOService(); michael@0: if (!ios) michael@0: return; michael@0: michael@0: nsIDocument *doc = content->OwnerDoc(); michael@0: michael@0: // value contains relative URIs split on spaces (U+0020) michael@0: const char16_t *start = value.BeginReading(); michael@0: const char16_t *end = value.EndReading(); michael@0: const char16_t *iter = start; michael@0: for (;;) { michael@0: if (iter < end && *iter != ' ') { michael@0: ++iter; michael@0: } else { // iter is pointing at either end or a space michael@0: while (*start == ' ' && start < iter) michael@0: ++start; michael@0: if (iter != start) { michael@0: nsCOMPtr uri, baseURI = content->GetBaseURI(); michael@0: ios->NewURI(NS_ConvertUTF16toUTF8(Substring(start, iter)), michael@0: doc->GetDocumentCharacterSet().get(), michael@0: baseURI, getter_AddRefs(uri)); michael@0: if (CheckPingURI(uri, content)) { michael@0: callback(closure, content, uri, ios); michael@0: } michael@0: } michael@0: start = iter = iter + 1; michael@0: if (iter >= end) michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: // We wait this many milliseconds before killing the ping channel... michael@0: #define PING_TIMEOUT 10000 michael@0: michael@0: static void michael@0: OnPingTimeout(nsITimer *timer, void *closure) michael@0: { michael@0: nsILoadGroup *loadGroup = static_cast(closure); michael@0: if (loadGroup) michael@0: loadGroup->Cancel(NS_ERROR_ABORT); michael@0: } michael@0: michael@0: // Check to see if two URIs have the same host or not michael@0: static bool michael@0: IsSameHost(nsIURI *uri1, nsIURI *uri2) michael@0: { michael@0: nsAutoCString host1, host2; michael@0: uri1->GetAsciiHost(host1); michael@0: uri2->GetAsciiHost(host2); michael@0: return host1.Equals(host2); michael@0: } michael@0: michael@0: class nsPingListener MOZ_FINAL : public nsIStreamListener michael@0: , public nsIInterfaceRequestor michael@0: , public nsIChannelEventSink michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIREQUESTOBSERVER michael@0: NS_DECL_NSISTREAMLISTENER michael@0: NS_DECL_NSIINTERFACEREQUESTOR michael@0: NS_DECL_NSICHANNELEVENTSINK michael@0: michael@0: nsPingListener(bool requireSameHost, nsIContent* content, nsILoadGroup* loadGroup) michael@0: : mRequireSameHost(requireSameHost), michael@0: mContent(content), michael@0: mLoadGroup(loadGroup) michael@0: {} michael@0: michael@0: ~nsPingListener(); michael@0: michael@0: nsresult StartTimeout(); michael@0: michael@0: private: michael@0: bool mRequireSameHost; michael@0: nsCOMPtr mContent; michael@0: nsCOMPtr mLoadGroup; michael@0: nsCOMPtr mTimer; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(nsPingListener, nsIStreamListener, nsIRequestObserver, michael@0: nsIInterfaceRequestor, nsIChannelEventSink) michael@0: michael@0: nsPingListener::~nsPingListener() michael@0: { michael@0: if (mTimer) { michael@0: mTimer->Cancel(); michael@0: mTimer = nullptr; michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsPingListener::StartTimeout() michael@0: { michael@0: nsCOMPtr timer = do_CreateInstance(NS_TIMER_CONTRACTID); michael@0: michael@0: if (timer) { michael@0: nsresult rv = timer->InitWithFuncCallback(OnPingTimeout, mLoadGroup, michael@0: PING_TIMEOUT, michael@0: nsITimer::TYPE_ONE_SHOT); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: mTimer = timer; michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPingListener::OnStartRequest(nsIRequest *request, nsISupports *context) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPingListener::OnDataAvailable(nsIRequest *request, nsISupports *context, michael@0: nsIInputStream *stream, uint64_t offset, michael@0: uint32_t count) michael@0: { michael@0: uint32_t result; michael@0: return stream->ReadSegments(NS_DiscardSegment, nullptr, count, &result); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPingListener::OnStopRequest(nsIRequest *request, nsISupports *context, michael@0: nsresult status) michael@0: { michael@0: mLoadGroup = nullptr; michael@0: michael@0: if (mTimer) { michael@0: mTimer->Cancel(); michael@0: mTimer = nullptr; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPingListener::GetInterface(const nsIID &iid, void **result) michael@0: { michael@0: if (iid.Equals(NS_GET_IID(nsIChannelEventSink))) { michael@0: NS_ADDREF_THIS(); michael@0: *result = (nsIChannelEventSink *) this; michael@0: return NS_OK; michael@0: } michael@0: michael@0: return NS_ERROR_NO_INTERFACE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPingListener::AsyncOnChannelRedirect(nsIChannel *oldChan, nsIChannel *newChan, michael@0: uint32_t flags, michael@0: nsIAsyncVerifyRedirectCallback *callback) michael@0: { michael@0: nsCOMPtr newURI; michael@0: newChan->GetURI(getter_AddRefs(newURI)); michael@0: michael@0: if (!CheckPingURI(newURI, mContent)) michael@0: return NS_ERROR_ABORT; michael@0: michael@0: if (!mRequireSameHost) { michael@0: callback->OnRedirectVerifyCallback(NS_OK); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // XXXbz should this be using something more like the nsContentUtils michael@0: // same-origin checker? michael@0: nsCOMPtr oldURI; michael@0: oldChan->GetURI(getter_AddRefs(oldURI)); michael@0: NS_ENSURE_STATE(oldURI && newURI); michael@0: michael@0: if (!IsSameHost(oldURI, newURI)) michael@0: return NS_ERROR_ABORT; michael@0: michael@0: callback->OnRedirectVerifyCallback(NS_OK); michael@0: return NS_OK; michael@0: } michael@0: michael@0: struct SendPingInfo { michael@0: int32_t numPings; michael@0: int32_t maxPings; michael@0: bool requireSameHost; michael@0: nsIURI *target; michael@0: nsIURI *referrer; michael@0: }; michael@0: michael@0: static void michael@0: SendPing(void *closure, nsIContent *content, nsIURI *uri, nsIIOService *ios) michael@0: { michael@0: SendPingInfo *info = static_cast(closure); michael@0: if (info->numPings >= info->maxPings) michael@0: return; michael@0: michael@0: if (info->requireSameHost) { michael@0: // Make sure the referrer and the given uri share the same origin. We michael@0: // only require the same hostname. The scheme and port may differ. michael@0: if (!IsSameHost(uri, info->referrer)) michael@0: return; michael@0: } michael@0: michael@0: nsIDocument *doc = content->OwnerDoc(); michael@0: michael@0: nsCOMPtr chan; michael@0: ios->NewChannelFromURI(uri, getter_AddRefs(chan)); michael@0: if (!chan) michael@0: return; michael@0: michael@0: // Don't bother caching the result of this URI load. michael@0: chan->SetLoadFlags(nsIRequest::INHIBIT_CACHING); michael@0: michael@0: nsCOMPtr httpChan = do_QueryInterface(chan); michael@0: if (!httpChan) michael@0: return; michael@0: michael@0: // This is needed in order for 3rd-party cookie blocking to work. michael@0: nsCOMPtr httpInternal = do_QueryInterface(httpChan); michael@0: if (httpInternal) michael@0: httpInternal->SetDocumentURI(doc->GetDocumentURI()); michael@0: michael@0: michael@0: httpChan->SetRequestMethod(NS_LITERAL_CSTRING("POST")); michael@0: michael@0: // Remove extraneous request headers (to reduce request size) michael@0: httpChan->SetRequestHeader(NS_LITERAL_CSTRING("accept"), michael@0: EmptyCString(), false); michael@0: httpChan->SetRequestHeader(NS_LITERAL_CSTRING("accept-language"), michael@0: EmptyCString(), false); michael@0: httpChan->SetRequestHeader(NS_LITERAL_CSTRING("accept-encoding"), michael@0: EmptyCString(), false); michael@0: michael@0: // Always send a Ping-To header. michael@0: nsAutoCString pingTo; michael@0: if (NS_SUCCEEDED(info->target->GetSpec(pingTo))) michael@0: httpChan->SetRequestHeader(NS_LITERAL_CSTRING("Ping-To"), pingTo, false); michael@0: michael@0: nsCOMPtr sm = michael@0: do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID); michael@0: michael@0: if (sm && info->referrer) { michael@0: bool referrerIsSecure; michael@0: uint32_t flags = nsIProtocolHandler::URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT; michael@0: nsresult rv = NS_URIChainHasFlags(info->referrer, flags, &referrerIsSecure); michael@0: michael@0: // Default to sending less data if NS_URIChainHasFlags() fails. michael@0: referrerIsSecure = NS_FAILED(rv) || referrerIsSecure; michael@0: michael@0: bool sameOrigin = michael@0: NS_SUCCEEDED(sm->CheckSameOriginURI(info->referrer, uri, false)); michael@0: michael@0: // If both the address of the document containing the hyperlink being michael@0: // audited and "ping URL" have the same origin or the document containing michael@0: // the hyperlink being audited was not retrieved over an encrypted michael@0: // connection, send a Ping-From header. michael@0: if (sameOrigin || !referrerIsSecure) { michael@0: nsAutoCString pingFrom; michael@0: if (NS_SUCCEEDED(info->referrer->GetSpec(pingFrom))) michael@0: httpChan->SetRequestHeader(NS_LITERAL_CSTRING("Ping-From"), pingFrom, false); michael@0: } michael@0: michael@0: // If the document containing the hyperlink being audited was not retrieved michael@0: // over an encrypted connection and its address does not have the same michael@0: // origin as "ping URL", send a referrer. michael@0: if (!sameOrigin && !referrerIsSecure) michael@0: httpChan->SetReferrer(info->referrer); michael@0: } michael@0: michael@0: nsCOMPtr uploadChan = do_QueryInterface(httpChan); michael@0: if (!uploadChan) michael@0: return; michael@0: michael@0: NS_NAMED_LITERAL_CSTRING(uploadData, "PING"); michael@0: michael@0: nsCOMPtr uploadStream; michael@0: NS_NewPostDataStream(getter_AddRefs(uploadStream), false, uploadData); michael@0: if (!uploadStream) michael@0: return; michael@0: michael@0: uploadChan->ExplicitSetUploadStream(uploadStream, michael@0: NS_LITERAL_CSTRING("text/ping"), uploadData.Length(), michael@0: NS_LITERAL_CSTRING("POST"), false); michael@0: michael@0: // The channel needs to have a loadgroup associated with it, so that we can michael@0: // cancel the channel and any redirected channels it may create. michael@0: nsCOMPtr loadGroup = michael@0: do_CreateInstance(NS_LOADGROUP_CONTRACTID); michael@0: if (!loadGroup) michael@0: return; michael@0: chan->SetLoadGroup(loadGroup); michael@0: michael@0: // Construct a listener that merely discards any response. If successful at michael@0: // opening the channel, then it is not necessary to hold a reference to the michael@0: // channel. The networking subsystem will take care of that for us. michael@0: nsPingListener *pingListener = michael@0: new nsPingListener(info->requireSameHost, content, loadGroup); michael@0: if (!pingListener) michael@0: return; michael@0: michael@0: nsCOMPtr listener(pingListener); michael@0: michael@0: // Observe redirects as well: michael@0: nsCOMPtr callbacks = do_QueryInterface(listener); michael@0: NS_ASSERTION(callbacks, "oops"); michael@0: loadGroup->SetNotificationCallbacks(callbacks); michael@0: michael@0: chan->AsyncOpen(listener, nullptr); michael@0: michael@0: // Even if AsyncOpen failed, we still count this as a successful ping. It's michael@0: // possible that AsyncOpen may have failed after triggering some background michael@0: // process that may have written something to the network. michael@0: info->numPings++; michael@0: michael@0: // Prevent ping requests from stalling and never being garbage collected... michael@0: if (NS_FAILED(pingListener->StartTimeout())) { michael@0: // If we failed to setup the timer, then we should just cancel the channel michael@0: // because we won't be able to ensure that it goes away in a timely manner. michael@0: chan->Cancel(NS_ERROR_ABORT); michael@0: } michael@0: } michael@0: michael@0: // Spec: http://whatwg.org/specs/web-apps/current-work/#ping michael@0: static void michael@0: DispatchPings(nsIContent *content, nsIURI *target, nsIURI *referrer) michael@0: { michael@0: SendPingInfo info; michael@0: michael@0: if (!PingsEnabled(&info.maxPings, &info.requireSameHost)) michael@0: return; michael@0: if (info.maxPings == 0) michael@0: return; michael@0: michael@0: info.numPings = 0; michael@0: info.target = target; michael@0: info.referrer = referrer; michael@0: michael@0: ForEachPing(content, SendPing, &info); michael@0: } michael@0: michael@0: static nsDOMPerformanceNavigationType michael@0: ConvertLoadTypeToNavigationType(uint32_t aLoadType) michael@0: { michael@0: // Not initialized, assume it's normal load. michael@0: if (aLoadType == 0) { michael@0: aLoadType = LOAD_NORMAL; michael@0: } michael@0: michael@0: nsDOMPerformanceNavigationType result = dom::PerformanceNavigation::TYPE_RESERVED; michael@0: switch (aLoadType) { michael@0: case LOAD_NORMAL: michael@0: case LOAD_NORMAL_EXTERNAL: michael@0: case LOAD_NORMAL_BYPASS_CACHE: michael@0: case LOAD_NORMAL_BYPASS_PROXY: michael@0: case LOAD_NORMAL_BYPASS_PROXY_AND_CACHE: michael@0: case LOAD_NORMAL_REPLACE: michael@0: case LOAD_NORMAL_ALLOW_MIXED_CONTENT: michael@0: case LOAD_LINK: michael@0: case LOAD_STOP_CONTENT: michael@0: case LOAD_REPLACE_BYPASS_CACHE: michael@0: result = dom::PerformanceNavigation::TYPE_NAVIGATE; michael@0: break; michael@0: case LOAD_HISTORY: michael@0: result = dom::PerformanceNavigation::TYPE_BACK_FORWARD; michael@0: break; michael@0: case LOAD_RELOAD_NORMAL: michael@0: case LOAD_RELOAD_CHARSET_CHANGE: michael@0: case LOAD_RELOAD_BYPASS_CACHE: michael@0: case LOAD_RELOAD_BYPASS_PROXY: michael@0: case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE: michael@0: case LOAD_RELOAD_ALLOW_MIXED_CONTENT: michael@0: result = dom::PerformanceNavigation::TYPE_RELOAD; michael@0: break; michael@0: case LOAD_STOP_CONTENT_AND_REPLACE: michael@0: case LOAD_REFRESH: michael@0: case LOAD_BYPASS_HISTORY: michael@0: case LOAD_ERROR_PAGE: michael@0: case LOAD_PUSHSTATE: michael@0: result = dom::PerformanceNavigation::TYPE_RESERVED; michael@0: break; michael@0: default: michael@0: // NS_NOTREACHED("Unexpected load type value"); michael@0: result = dom::PerformanceNavigation::TYPE_RESERVED; michael@0: break; michael@0: } michael@0: michael@0: return result; michael@0: } michael@0: michael@0: static nsISHEntry* GetRootSHEntry(nsISHEntry *entry); michael@0: michael@0: static void michael@0: IncreasePrivateDocShellCount() michael@0: { michael@0: gNumberOfPrivateDocShells++; michael@0: if (gNumberOfPrivateDocShells > 1 || michael@0: XRE_GetProcessType() != GeckoProcessType_Content) { michael@0: return; michael@0: } michael@0: michael@0: mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton(); michael@0: cc->SendPrivateDocShellsExist(true); michael@0: } michael@0: michael@0: static void michael@0: DecreasePrivateDocShellCount() michael@0: { michael@0: MOZ_ASSERT(gNumberOfPrivateDocShells > 0); michael@0: gNumberOfPrivateDocShells--; michael@0: if (!gNumberOfPrivateDocShells) michael@0: { michael@0: if (XRE_GetProcessType() == GeckoProcessType_Content) { michael@0: mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton(); michael@0: cc->SendPrivateDocShellsExist(false); michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr obsvc = mozilla::services::GetObserverService(); michael@0: if (obsvc) { michael@0: obsvc->NotifyObservers(nullptr, "last-pb-context-exited", nullptr); michael@0: } michael@0: } michael@0: } michael@0: michael@0: //***************************************************************************** michael@0: //*** nsDocShell: Object Management michael@0: //***************************************************************************** michael@0: michael@0: static uint64_t gDocshellIDCounter = 0; michael@0: michael@0: // Note: operator new zeros our memory michael@0: nsDocShell::nsDocShell(): michael@0: nsDocLoader(), michael@0: mDefaultScrollbarPref(Scrollbar_Auto, Scrollbar_Auto), michael@0: mTreeOwner(nullptr), michael@0: mChromeEventHandler(nullptr), michael@0: mCharsetReloadState(eCharsetReloadInit), michael@0: mChildOffset(0), michael@0: mBusyFlags(BUSY_FLAGS_NONE), michael@0: mAppType(nsIDocShell::APP_TYPE_UNKNOWN), michael@0: mLoadType(0), michael@0: mMarginWidth(-1), michael@0: mMarginHeight(-1), michael@0: mItemType(typeContent), michael@0: mPreviousTransIndex(-1), michael@0: mLoadedTransIndex(-1), michael@0: mSandboxFlags(0), michael@0: mFullscreenAllowed(CHECK_ATTRIBUTES), michael@0: mCreated(false), michael@0: mAllowSubframes(true), michael@0: mAllowPlugins(true), michael@0: mAllowJavascript(true), michael@0: mAllowMetaRedirects(true), michael@0: mAllowImages(true), michael@0: mAllowMedia(true), michael@0: mAllowDNSPrefetch(true), michael@0: mAllowWindowControl(true), michael@0: mAllowContentRetargeting(true), michael@0: mCreatingDocument(false), michael@0: mUseErrorPages(false), michael@0: mObserveErrorPages(true), michael@0: mAllowAuth(true), michael@0: mAllowKeywordFixup(false), michael@0: mIsOffScreenBrowser(false), michael@0: mIsActive(true), michael@0: mIsAppTab(false), michael@0: mUseGlobalHistory(false), michael@0: mInPrivateBrowsing(false), michael@0: mUseRemoteTabs(false), michael@0: mDeviceSizeIsPageSize(false), michael@0: mCanExecuteScripts(false), michael@0: mFiredUnloadEvent(false), michael@0: mEODForCurrentDocument(false), michael@0: mURIResultedInDocument(false), michael@0: mIsBeingDestroyed(false), michael@0: mIsExecutingOnLoadHandler(false), michael@0: mIsPrintingOrPP(false), michael@0: mSavingOldViewer(false), michael@0: #ifdef DEBUG michael@0: mInEnsureScriptEnv(false), michael@0: #endif michael@0: mAffectPrivateSessionLifetime(true), michael@0: mInvisible(false), michael@0: mDefaultLoadFlags(nsIRequest::LOAD_NORMAL), michael@0: mFrameType(eFrameTypeRegular), michael@0: mOwnOrContainingAppId(nsIScriptSecurityManager::UNKNOWN_APP_ID), michael@0: mParentCharsetSource(0) michael@0: { michael@0: mHistoryID = ++gDocshellIDCounter; michael@0: if (gDocShellCount++ == 0) { michael@0: NS_ASSERTION(sURIFixup == nullptr, michael@0: "Huh, sURIFixup not null in first nsDocShell ctor!"); michael@0: michael@0: CallGetService(NS_URIFIXUP_CONTRACTID, &sURIFixup); michael@0: } michael@0: michael@0: #ifdef PR_LOGGING michael@0: #ifdef DEBUG michael@0: if (! gDocShellLog) michael@0: gDocShellLog = PR_NewLogModule("nsDocShell"); michael@0: #endif michael@0: if (nullptr == gDocShellLeakLog) michael@0: gDocShellLeakLog = PR_NewLogModule("nsDocShellLeak"); michael@0: if (gDocShellLeakLog) michael@0: PR_LOG(gDocShellLeakLog, PR_LOG_DEBUG, ("DOCSHELL %p created\n", this)); michael@0: #endif michael@0: michael@0: #ifdef DEBUG michael@0: // We're counting the number of |nsDocShells| to help find leaks michael@0: ++gNumberOfDocShells; michael@0: if (!PR_GetEnv("MOZ_QUIET")) { michael@0: printf_stderr("++DOCSHELL %p == %ld [pid = %d] [id = %llu]\n", michael@0: (void*) this, michael@0: gNumberOfDocShells, michael@0: getpid(), michael@0: SafeCast(mHistoryID)); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: nsDocShell::~nsDocShell() michael@0: { michael@0: Destroy(); michael@0: michael@0: nsCOMPtr michael@0: shPrivate(do_QueryInterface(mSessionHistory)); michael@0: if (shPrivate) { michael@0: shPrivate->SetRootDocShell(nullptr); michael@0: } michael@0: michael@0: if (--gDocShellCount == 0) { michael@0: NS_IF_RELEASE(sURIFixup); michael@0: } michael@0: michael@0: #ifdef PR_LOGGING michael@0: if (gDocShellLeakLog) michael@0: PR_LOG(gDocShellLeakLog, PR_LOG_DEBUG, ("DOCSHELL %p destroyed\n", this)); michael@0: #endif michael@0: michael@0: #ifdef DEBUG michael@0: // We're counting the number of |nsDocShells| to help find leaks michael@0: --gNumberOfDocShells; michael@0: if (!PR_GetEnv("MOZ_QUIET")) { michael@0: printf_stderr("--DOCSHELL %p == %ld [pid = %d] [id = %llu]\n", michael@0: (void*) this, michael@0: gNumberOfDocShells, michael@0: getpid(), michael@0: SafeCast(mHistoryID)); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: nsresult michael@0: nsDocShell::Init() michael@0: { michael@0: nsresult rv = nsDocLoader::Init(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: NS_ASSERTION(mLoadGroup, "Something went wrong!"); michael@0: michael@0: mContentListener = new nsDSURIContentListener(this); michael@0: NS_ENSURE_TRUE(mContentListener, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: rv = mContentListener->Init(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // We want to hold a strong ref to the loadgroup, so it better hold a weak michael@0: // ref to us... use an InterfaceRequestorProxy to do this. michael@0: nsCOMPtr proxy = michael@0: new InterfaceRequestorProxy(static_cast michael@0: (this)); michael@0: NS_ENSURE_TRUE(proxy, NS_ERROR_OUT_OF_MEMORY); michael@0: mLoadGroup->SetNotificationCallbacks(proxy); michael@0: michael@0: rv = nsDocLoader::AddDocLoaderAsChildOfRoot(this); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Add as |this| a progress listener to itself. A little weird, but michael@0: // simpler than reproducing all the listener-notification logic in michael@0: // overrides of the various methods via which nsDocLoader can be michael@0: // notified. Note that this holds an nsWeakPtr to ourselves, so it's ok. michael@0: return AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_DOCUMENT | michael@0: nsIWebProgress::NOTIFY_STATE_NETWORK); michael@0: michael@0: } michael@0: michael@0: void michael@0: nsDocShell::DestroyChildren() michael@0: { michael@0: nsCOMPtr shell; michael@0: nsTObserverArray::ForwardIterator iter(mChildList); michael@0: while (iter.HasMore()) { michael@0: shell = do_QueryObject(iter.GetNext()); michael@0: NS_ASSERTION(shell, "docshell has null child"); michael@0: michael@0: if (shell) { michael@0: shell->SetTreeOwner(nullptr); michael@0: } michael@0: } michael@0: michael@0: nsDocLoader::DestroyChildren(); michael@0: } michael@0: michael@0: //***************************************************************************** michael@0: // nsDocShell::nsISupports michael@0: //***************************************************************************** michael@0: michael@0: NS_IMPL_ADDREF_INHERITED(nsDocShell, nsDocLoader) michael@0: NS_IMPL_RELEASE_INHERITED(nsDocShell, nsDocLoader) michael@0: michael@0: NS_INTERFACE_MAP_BEGIN(nsDocShell) michael@0: NS_INTERFACE_MAP_ENTRY(nsIDocShell) michael@0: NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeItem) michael@0: NS_INTERFACE_MAP_ENTRY(nsIWebNavigation) michael@0: NS_INTERFACE_MAP_ENTRY(nsIBaseWindow) michael@0: NS_INTERFACE_MAP_ENTRY(nsIScrollable) michael@0: NS_INTERFACE_MAP_ENTRY(nsITextScroll) michael@0: NS_INTERFACE_MAP_ENTRY(nsIDocCharset) michael@0: NS_INTERFACE_MAP_ENTRY(nsIRefreshURI) michael@0: NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener) michael@0: NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) michael@0: NS_INTERFACE_MAP_ENTRY(nsIContentViewerContainer) michael@0: NS_INTERFACE_MAP_ENTRY(nsIWebPageDescriptor) michael@0: NS_INTERFACE_MAP_ENTRY(nsIAuthPromptProvider) michael@0: NS_INTERFACE_MAP_ENTRY(nsILoadContext) michael@0: NS_INTERFACE_MAP_ENTRY(nsIWebShellServices) michael@0: NS_INTERFACE_MAP_ENTRY(nsILinkHandler) michael@0: NS_INTERFACE_MAP_ENTRY(nsIClipboardCommands) michael@0: NS_INTERFACE_MAP_ENTRY(nsIDOMStorageManager) michael@0: NS_INTERFACE_MAP_END_INHERITING(nsDocLoader) michael@0: michael@0: ///***************************************************************************** michael@0: // nsDocShell::nsIInterfaceRequestor michael@0: //***************************************************************************** michael@0: NS_IMETHODIMP nsDocShell::GetInterface(const nsIID & aIID, void **aSink) michael@0: { michael@0: NS_PRECONDITION(aSink, "null out param"); michael@0: michael@0: *aSink = nullptr; michael@0: michael@0: if (aIID.Equals(NS_GET_IID(nsICommandManager))) { michael@0: NS_ENSURE_SUCCESS(EnsureCommandHandler(), NS_ERROR_FAILURE); michael@0: *aSink = mCommandManager; michael@0: } michael@0: else if (aIID.Equals(NS_GET_IID(nsIURIContentListener))) { michael@0: *aSink = mContentListener; michael@0: } michael@0: else if ((aIID.Equals(NS_GET_IID(nsIScriptGlobalObject)) || michael@0: aIID.Equals(NS_GET_IID(nsPIDOMWindow)) || michael@0: aIID.Equals(NS_GET_IID(nsIDOMWindow)) || michael@0: aIID.Equals(NS_GET_IID(nsIDOMWindowInternal))) && michael@0: NS_SUCCEEDED(EnsureScriptEnvironment())) { michael@0: return mScriptGlobal->QueryInterface(aIID, aSink); michael@0: } michael@0: else if (aIID.Equals(NS_GET_IID(nsIDOMDocument)) && michael@0: NS_SUCCEEDED(EnsureContentViewer())) { michael@0: mContentViewer->GetDOMDocument((nsIDOMDocument **) aSink); michael@0: return *aSink ? NS_OK : NS_NOINTERFACE; michael@0: } michael@0: else if (aIID.Equals(NS_GET_IID(nsIDocument)) && michael@0: NS_SUCCEEDED(EnsureContentViewer())) { michael@0: nsCOMPtr doc = mContentViewer->GetDocument(); michael@0: doc.forget(aSink); michael@0: return *aSink ? NS_OK : NS_NOINTERFACE; michael@0: } michael@0: else if (aIID.Equals(NS_GET_IID(nsIApplicationCacheContainer))) { michael@0: *aSink = nullptr; michael@0: michael@0: // Return application cache associated with this docshell, if any michael@0: michael@0: nsCOMPtr contentViewer; michael@0: GetContentViewer(getter_AddRefs(contentViewer)); michael@0: if (!contentViewer) michael@0: return NS_ERROR_NO_INTERFACE; michael@0: michael@0: nsCOMPtr domDoc; michael@0: contentViewer->GetDOMDocument(getter_AddRefs(domDoc)); michael@0: NS_ASSERTION(domDoc, "Should have a document."); michael@0: if (!domDoc) michael@0: return NS_ERROR_NO_INTERFACE; michael@0: michael@0: #if defined(PR_LOGGING) && defined(DEBUG) michael@0: PR_LOG(gDocShellLog, PR_LOG_DEBUG, michael@0: ("nsDocShell[%p]: returning app cache container %p", michael@0: this, domDoc.get())); michael@0: #endif michael@0: return domDoc->QueryInterface(aIID, aSink); michael@0: } michael@0: else if (aIID.Equals(NS_GET_IID(nsIPrompt)) && michael@0: NS_SUCCEEDED(EnsureScriptEnvironment())) { michael@0: nsresult rv; michael@0: nsCOMPtr wwatch = michael@0: do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Get the an auth prompter for our window so that the parenting michael@0: // of the dialogs works as it should when using tabs. michael@0: nsIPrompt *prompt; michael@0: rv = wwatch->GetNewPrompter(mScriptGlobal, &prompt); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: *aSink = prompt; michael@0: return NS_OK; michael@0: } michael@0: else if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) || michael@0: aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) { michael@0: return NS_SUCCEEDED( michael@0: GetAuthPrompt(PROMPT_NORMAL, aIID, aSink)) ? michael@0: NS_OK : NS_NOINTERFACE; michael@0: } michael@0: else if (aIID.Equals(NS_GET_IID(nsISHistory))) { michael@0: nsCOMPtr shistory; michael@0: nsresult michael@0: rv = michael@0: GetSessionHistory(getter_AddRefs(shistory)); michael@0: if (NS_SUCCEEDED(rv) && shistory) { michael@0: *aSink = shistory; michael@0: NS_ADDREF((nsISupports *) * aSink); michael@0: return NS_OK; michael@0: } michael@0: return NS_NOINTERFACE; michael@0: } michael@0: else if (aIID.Equals(NS_GET_IID(nsIWebBrowserFind))) { michael@0: nsresult rv = EnsureFind(); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: *aSink = mFind; michael@0: NS_ADDREF((nsISupports*)*aSink); michael@0: return NS_OK; michael@0: } michael@0: else if (aIID.Equals(NS_GET_IID(nsIEditingSession)) && NS_SUCCEEDED(EnsureEditorData())) { michael@0: nsCOMPtr editingSession; michael@0: mEditorData->GetEditingSession(getter_AddRefs(editingSession)); michael@0: if (editingSession) michael@0: { michael@0: *aSink = editingSession; michael@0: NS_ADDREF((nsISupports *)*aSink); michael@0: return NS_OK; michael@0: } michael@0: michael@0: return NS_NOINTERFACE; michael@0: } michael@0: else if (aIID.Equals(NS_GET_IID(nsIClipboardDragDropHookList)) michael@0: && NS_SUCCEEDED(EnsureTransferableHookData())) { michael@0: *aSink = mTransferableHookData; michael@0: NS_ADDREF((nsISupports *)*aSink); michael@0: return NS_OK; michael@0: } michael@0: else if (aIID.Equals(NS_GET_IID(nsISelectionDisplay))) { michael@0: nsIPresShell* shell = GetPresShell(); michael@0: if (shell) michael@0: return shell->QueryInterface(aIID,aSink); michael@0: } michael@0: else if (aIID.Equals(NS_GET_IID(nsIDocShellTreeOwner))) { michael@0: nsCOMPtr treeOwner; michael@0: nsresult rv = GetTreeOwner(getter_AddRefs(treeOwner)); michael@0: if (NS_SUCCEEDED(rv) && treeOwner) michael@0: return treeOwner->QueryInterface(aIID, aSink); michael@0: } michael@0: else if (aIID.Equals(NS_GET_IID(nsITabChild))) { michael@0: nsCOMPtr treeOwner; michael@0: nsresult rv = GetTreeOwner(getter_AddRefs(treeOwner)); michael@0: if (NS_SUCCEEDED(rv) && treeOwner) { michael@0: nsCOMPtr ir = do_QueryInterface(treeOwner); michael@0: if (ir) michael@0: return ir->GetInterface(aIID, aSink); michael@0: } michael@0: } michael@0: else if (aIID.Equals(NS_GET_IID(nsIContentFrameMessageManager))) { michael@0: nsCOMPtr tabChild = michael@0: do_GetInterface(static_cast(this)); michael@0: nsCOMPtr mm; michael@0: if (tabChild) { michael@0: tabChild-> michael@0: GetMessageManager(getter_AddRefs(mm)); michael@0: } else { michael@0: nsCOMPtr win = michael@0: do_GetInterface(static_cast(this)); michael@0: if (win) { michael@0: mm = do_QueryInterface(win->GetParentTarget()); michael@0: } michael@0: } michael@0: *aSink = mm.get(); michael@0: } michael@0: else { michael@0: return nsDocLoader::GetInterface(aIID, aSink); michael@0: } michael@0: michael@0: NS_IF_ADDREF(((nsISupports *) * aSink)); michael@0: return *aSink ? NS_OK : NS_NOINTERFACE; michael@0: } michael@0: michael@0: uint32_t michael@0: nsDocShell:: michael@0: ConvertDocShellLoadInfoToLoadType(nsDocShellInfoLoadType aDocShellLoadType) michael@0: { michael@0: uint32_t loadType = LOAD_NORMAL; michael@0: michael@0: switch (aDocShellLoadType) { michael@0: case nsIDocShellLoadInfo::loadNormal: michael@0: loadType = LOAD_NORMAL; michael@0: break; michael@0: case nsIDocShellLoadInfo::loadNormalReplace: michael@0: loadType = LOAD_NORMAL_REPLACE; michael@0: break; michael@0: case nsIDocShellLoadInfo::loadNormalExternal: michael@0: loadType = LOAD_NORMAL_EXTERNAL; michael@0: break; michael@0: case nsIDocShellLoadInfo::loadHistory: michael@0: loadType = LOAD_HISTORY; michael@0: break; michael@0: case nsIDocShellLoadInfo::loadNormalBypassCache: michael@0: loadType = LOAD_NORMAL_BYPASS_CACHE; michael@0: break; michael@0: case nsIDocShellLoadInfo::loadNormalBypassProxy: michael@0: loadType = LOAD_NORMAL_BYPASS_PROXY; michael@0: break; michael@0: case nsIDocShellLoadInfo::loadNormalBypassProxyAndCache: michael@0: loadType = LOAD_NORMAL_BYPASS_PROXY_AND_CACHE; michael@0: break; michael@0: case nsIDocShellLoadInfo::loadNormalAllowMixedContent: michael@0: loadType = LOAD_NORMAL_ALLOW_MIXED_CONTENT; michael@0: break; michael@0: case nsIDocShellLoadInfo::loadReloadNormal: michael@0: loadType = LOAD_RELOAD_NORMAL; michael@0: break; michael@0: case nsIDocShellLoadInfo::loadReloadCharsetChange: michael@0: loadType = LOAD_RELOAD_CHARSET_CHANGE; michael@0: break; michael@0: case nsIDocShellLoadInfo::loadReloadBypassCache: michael@0: loadType = LOAD_RELOAD_BYPASS_CACHE; michael@0: break; michael@0: case nsIDocShellLoadInfo::loadReloadBypassProxy: michael@0: loadType = LOAD_RELOAD_BYPASS_PROXY; michael@0: break; michael@0: case nsIDocShellLoadInfo::loadReloadBypassProxyAndCache: michael@0: loadType = LOAD_RELOAD_BYPASS_PROXY_AND_CACHE; michael@0: break; michael@0: case nsIDocShellLoadInfo::loadLink: michael@0: loadType = LOAD_LINK; michael@0: break; michael@0: case nsIDocShellLoadInfo::loadRefresh: michael@0: loadType = LOAD_REFRESH; michael@0: break; michael@0: case nsIDocShellLoadInfo::loadBypassHistory: michael@0: loadType = LOAD_BYPASS_HISTORY; michael@0: break; michael@0: case nsIDocShellLoadInfo::loadStopContent: michael@0: loadType = LOAD_STOP_CONTENT; michael@0: break; michael@0: case nsIDocShellLoadInfo::loadStopContentAndReplace: michael@0: loadType = LOAD_STOP_CONTENT_AND_REPLACE; michael@0: break; michael@0: case nsIDocShellLoadInfo::loadPushState: michael@0: loadType = LOAD_PUSHSTATE; michael@0: break; michael@0: case nsIDocShellLoadInfo::loadReplaceBypassCache: michael@0: loadType = LOAD_REPLACE_BYPASS_CACHE; michael@0: break; michael@0: case nsIDocShellLoadInfo::loadReloadMixedContent: michael@0: loadType = LOAD_RELOAD_ALLOW_MIXED_CONTENT; michael@0: break; michael@0: default: michael@0: NS_NOTREACHED("Unexpected nsDocShellInfoLoadType value"); michael@0: } michael@0: michael@0: return loadType; michael@0: } michael@0: michael@0: michael@0: nsDocShellInfoLoadType michael@0: nsDocShell::ConvertLoadTypeToDocShellLoadInfo(uint32_t aLoadType) michael@0: { michael@0: nsDocShellInfoLoadType docShellLoadType = nsIDocShellLoadInfo::loadNormal; michael@0: switch (aLoadType) { michael@0: case LOAD_NORMAL: michael@0: docShellLoadType = nsIDocShellLoadInfo::loadNormal; michael@0: break; michael@0: case LOAD_NORMAL_REPLACE: michael@0: docShellLoadType = nsIDocShellLoadInfo::loadNormalReplace; michael@0: break; michael@0: case LOAD_NORMAL_EXTERNAL: michael@0: docShellLoadType = nsIDocShellLoadInfo::loadNormalExternal; michael@0: break; michael@0: case LOAD_NORMAL_BYPASS_CACHE: michael@0: docShellLoadType = nsIDocShellLoadInfo::loadNormalBypassCache; michael@0: break; michael@0: case LOAD_NORMAL_BYPASS_PROXY: michael@0: docShellLoadType = nsIDocShellLoadInfo::loadNormalBypassProxy; michael@0: break; michael@0: case LOAD_NORMAL_BYPASS_PROXY_AND_CACHE: michael@0: docShellLoadType = nsIDocShellLoadInfo::loadNormalBypassProxyAndCache; michael@0: break; michael@0: case LOAD_NORMAL_ALLOW_MIXED_CONTENT: michael@0: docShellLoadType = nsIDocShellLoadInfo::loadNormalAllowMixedContent; michael@0: break; michael@0: case LOAD_HISTORY: michael@0: docShellLoadType = nsIDocShellLoadInfo::loadHistory; michael@0: break; michael@0: case LOAD_RELOAD_NORMAL: michael@0: docShellLoadType = nsIDocShellLoadInfo::loadReloadNormal; michael@0: break; michael@0: case LOAD_RELOAD_CHARSET_CHANGE: michael@0: docShellLoadType = nsIDocShellLoadInfo::loadReloadCharsetChange; michael@0: break; michael@0: case LOAD_RELOAD_BYPASS_CACHE: michael@0: docShellLoadType = nsIDocShellLoadInfo::loadReloadBypassCache; michael@0: break; michael@0: case LOAD_RELOAD_BYPASS_PROXY: michael@0: docShellLoadType = nsIDocShellLoadInfo::loadReloadBypassProxy; michael@0: break; michael@0: case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE: michael@0: docShellLoadType = nsIDocShellLoadInfo::loadReloadBypassProxyAndCache; michael@0: break; michael@0: case LOAD_LINK: michael@0: docShellLoadType = nsIDocShellLoadInfo::loadLink; michael@0: break; michael@0: case LOAD_REFRESH: michael@0: docShellLoadType = nsIDocShellLoadInfo::loadRefresh; michael@0: break; michael@0: case LOAD_BYPASS_HISTORY: michael@0: case LOAD_ERROR_PAGE: michael@0: docShellLoadType = nsIDocShellLoadInfo::loadBypassHistory; michael@0: break; michael@0: case LOAD_STOP_CONTENT: michael@0: docShellLoadType = nsIDocShellLoadInfo::loadStopContent; michael@0: break; michael@0: case LOAD_STOP_CONTENT_AND_REPLACE: michael@0: docShellLoadType = nsIDocShellLoadInfo::loadStopContentAndReplace; michael@0: break; michael@0: case LOAD_PUSHSTATE: michael@0: docShellLoadType = nsIDocShellLoadInfo::loadPushState; michael@0: break; michael@0: case LOAD_REPLACE_BYPASS_CACHE: michael@0: docShellLoadType = nsIDocShellLoadInfo::loadReplaceBypassCache; michael@0: break; michael@0: case LOAD_RELOAD_ALLOW_MIXED_CONTENT: michael@0: docShellLoadType = nsIDocShellLoadInfo::loadReloadMixedContent; michael@0: break; michael@0: default: michael@0: NS_NOTREACHED("Unexpected load type value"); michael@0: } michael@0: michael@0: return docShellLoadType; michael@0: } michael@0: michael@0: //***************************************************************************** michael@0: // nsDocShell::nsIDocShell michael@0: //***************************************************************************** michael@0: NS_IMETHODIMP michael@0: nsDocShell::LoadURI(nsIURI * aURI, michael@0: nsIDocShellLoadInfo * aLoadInfo, michael@0: uint32_t aLoadFlags, michael@0: bool aFirstParty) michael@0: { michael@0: NS_PRECONDITION(aLoadInfo || (aLoadFlags & EXTRA_LOAD_FLAGS) == 0, michael@0: "Unexpected flags"); michael@0: NS_PRECONDITION((aLoadFlags & 0xf) == 0, "Should not have these flags set"); michael@0: michael@0: // Note: we allow loads to get through here even if mFiredUnloadEvent is michael@0: // true; that case will get handled in LoadInternal or LoadHistoryEntry. michael@0: if (IsPrintingOrPP()) { michael@0: return NS_OK; // JS may not handle returning of an error code michael@0: } michael@0: nsCOMPtr referrer; michael@0: nsCOMPtr postStream; michael@0: nsCOMPtr headersStream; michael@0: nsCOMPtr owner; michael@0: bool inheritOwner = false; michael@0: bool ownerIsExplicit = false; michael@0: bool sendReferrer = true; michael@0: bool isSrcdoc = false; michael@0: nsCOMPtr shEntry; michael@0: nsXPIDLString target; michael@0: nsAutoString srcdoc; michael@0: nsCOMPtr sourceDocShell; michael@0: nsCOMPtr baseURI; michael@0: michael@0: uint32_t loadType = MAKE_LOAD_TYPE(LOAD_NORMAL, aLoadFlags); michael@0: michael@0: NS_ENSURE_ARG(aURI); michael@0: michael@0: if (!StartupTimeline::HasRecord(StartupTimeline::FIRST_LOAD_URI) && michael@0: mItemType == typeContent && !NS_IsAboutBlank(aURI)) { michael@0: StartupTimeline::RecordOnce(StartupTimeline::FIRST_LOAD_URI); michael@0: } michael@0: michael@0: // Extract the info from the DocShellLoadInfo struct... michael@0: if (aLoadInfo) { michael@0: aLoadInfo->GetReferrer(getter_AddRefs(referrer)); michael@0: michael@0: nsDocShellInfoLoadType lt = nsIDocShellLoadInfo::loadNormal; michael@0: aLoadInfo->GetLoadType(<); michael@0: // Get the appropriate loadType from nsIDocShellLoadInfo type michael@0: loadType = ConvertDocShellLoadInfoToLoadType(lt); michael@0: michael@0: aLoadInfo->GetOwner(getter_AddRefs(owner)); michael@0: aLoadInfo->GetInheritOwner(&inheritOwner); michael@0: aLoadInfo->GetOwnerIsExplicit(&ownerIsExplicit); michael@0: aLoadInfo->GetSHEntry(getter_AddRefs(shEntry)); michael@0: aLoadInfo->GetTarget(getter_Copies(target)); michael@0: aLoadInfo->GetPostDataStream(getter_AddRefs(postStream)); michael@0: aLoadInfo->GetHeadersStream(getter_AddRefs(headersStream)); michael@0: aLoadInfo->GetSendReferrer(&sendReferrer); michael@0: aLoadInfo->GetIsSrcdocLoad(&isSrcdoc); michael@0: aLoadInfo->GetSrcdocData(srcdoc); michael@0: aLoadInfo->GetSourceDocShell(getter_AddRefs(sourceDocShell)); michael@0: aLoadInfo->GetBaseURI(getter_AddRefs(baseURI)); michael@0: } michael@0: michael@0: #if defined(PR_LOGGING) && defined(DEBUG) michael@0: if (PR_LOG_TEST(gDocShellLog, PR_LOG_DEBUG)) { michael@0: nsAutoCString uristr; michael@0: aURI->GetAsciiSpec(uristr); michael@0: PR_LOG(gDocShellLog, PR_LOG_DEBUG, michael@0: ("nsDocShell[%p]: loading %s with flags 0x%08x", michael@0: this, uristr.get(), aLoadFlags)); michael@0: } michael@0: #endif michael@0: michael@0: if (!shEntry && michael@0: !LOAD_TYPE_HAS_FLAGS(loadType, LOAD_FLAGS_REPLACE_HISTORY)) { michael@0: // First verify if this is a subframe. michael@0: nsCOMPtr parentAsItem; michael@0: GetSameTypeParent(getter_AddRefs(parentAsItem)); michael@0: nsCOMPtr parentDS(do_QueryInterface(parentAsItem)); michael@0: uint32_t parentLoadType; michael@0: michael@0: if (parentDS && parentDS != static_cast(this)) { michael@0: /* OK. It is a subframe. Checkout the michael@0: * parent's loadtype. If the parent was loaded thro' a history michael@0: * mechanism, then get the SH entry for the child from the parent. michael@0: * This is done to restore frameset navigation while going back/forward. michael@0: * If the parent was loaded through any other loadType, set the michael@0: * child's loadType too accordingly, so that session history does not michael@0: * get confused. michael@0: */ michael@0: michael@0: // Get the parent's load type michael@0: parentDS->GetLoadType(&parentLoadType); michael@0: michael@0: // Get the ShEntry for the child from the parent michael@0: nsCOMPtr currentSH; michael@0: bool oshe = false; michael@0: parentDS->GetCurrentSHEntry(getter_AddRefs(currentSH), &oshe); michael@0: bool dynamicallyAddedChild = mDynamicallyCreated; michael@0: if (!dynamicallyAddedChild && !oshe && currentSH) { michael@0: currentSH->HasDynamicallyAddedChild(&dynamicallyAddedChild); michael@0: } michael@0: if (!dynamicallyAddedChild) { michael@0: // Only use the old SHEntry, if we're sure enough that michael@0: // it wasn't originally for some other frame. michael@0: parentDS->GetChildSHEntry(mChildOffset, getter_AddRefs(shEntry)); michael@0: } michael@0: michael@0: // Make some decisions on the child frame's loadType based on the michael@0: // parent's loadType. michael@0: if (mCurrentURI == nullptr) { michael@0: // This is a newly created frame. Check for exception cases first. michael@0: // By default the subframe will inherit the parent's loadType. michael@0: if (shEntry && (parentLoadType == LOAD_NORMAL || michael@0: parentLoadType == LOAD_LINK || michael@0: parentLoadType == LOAD_NORMAL_EXTERNAL)) { michael@0: // The parent was loaded normally. In this case, this *brand new* child really shouldn't michael@0: // have a SHEntry. If it does, it could be because the parent is replacing an michael@0: // existing frame with a new frame, in the onLoadHandler. We don't want this michael@0: // url to get into session history. Clear off shEntry, and set load type to michael@0: // LOAD_BYPASS_HISTORY. michael@0: bool inOnLoadHandler=false; michael@0: parentDS->GetIsExecutingOnLoadHandler(&inOnLoadHandler); michael@0: if (inOnLoadHandler) { michael@0: loadType = LOAD_NORMAL_REPLACE; michael@0: shEntry = nullptr; michael@0: } michael@0: } else if (parentLoadType == LOAD_REFRESH) { michael@0: // Clear shEntry. For refresh loads, we have to load michael@0: // what comes thro' the pipe, not what's in history. michael@0: shEntry = nullptr; michael@0: } else if ((parentLoadType == LOAD_BYPASS_HISTORY) || michael@0: (shEntry && michael@0: ((parentLoadType & LOAD_CMD_HISTORY) || michael@0: (parentLoadType == LOAD_RELOAD_NORMAL) || michael@0: (parentLoadType == LOAD_RELOAD_CHARSET_CHANGE)))) { michael@0: // If the parent url, bypassed history or was loaded from michael@0: // history, pass on the parent's loadType to the new child michael@0: // frame too, so that the child frame will also michael@0: // avoid getting into history. michael@0: loadType = parentLoadType; michael@0: } else if (parentLoadType == LOAD_ERROR_PAGE) { michael@0: // If the parent document is an error page, we don't michael@0: // want to update global/session history. However, michael@0: // this child frame is not an error page. michael@0: loadType = LOAD_BYPASS_HISTORY; michael@0: } else if ((parentLoadType == LOAD_RELOAD_BYPASS_CACHE) || michael@0: (parentLoadType == LOAD_RELOAD_BYPASS_PROXY) || michael@0: (parentLoadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE)) { michael@0: // the new frame should inherit the parent's load type so that it also michael@0: // bypasses the cache and/or proxy michael@0: loadType = parentLoadType; michael@0: } michael@0: } else { michael@0: // This is a pre-existing subframe. If the load was not originally initiated michael@0: // by session history, (if (!shEntry) condition succeeded) and mCurrentURI is not null, michael@0: // it is possible that a parent's onLoadHandler or even self's onLoadHandler is loading michael@0: // a new page in this child. Check parent's and self's busy flag and if it is set, michael@0: // we don't want this onLoadHandler load to get in to session history. michael@0: uint32_t parentBusy = BUSY_FLAGS_NONE; michael@0: uint32_t selfBusy = BUSY_FLAGS_NONE; michael@0: parentDS->GetBusyFlags(&parentBusy); michael@0: GetBusyFlags(&selfBusy); michael@0: if (parentBusy & BUSY_FLAGS_BUSY || michael@0: selfBusy & BUSY_FLAGS_BUSY) { michael@0: loadType = LOAD_NORMAL_REPLACE; michael@0: shEntry = nullptr; michael@0: } michael@0: } michael@0: } //parentDS michael@0: else { michael@0: // This is the root docshell. If we got here while michael@0: // executing an onLoad Handler,this load will not go michael@0: // into session history. michael@0: bool inOnLoadHandler=false; michael@0: GetIsExecutingOnLoadHandler(&inOnLoadHandler); michael@0: if (inOnLoadHandler) { michael@0: loadType = LOAD_NORMAL_REPLACE; michael@0: } michael@0: } michael@0: } // !shEntry michael@0: michael@0: if (shEntry) { michael@0: #ifdef DEBUG michael@0: PR_LOG(gDocShellLog, PR_LOG_DEBUG, michael@0: ("nsDocShell[%p]: loading from session history", this)); michael@0: #endif michael@0: michael@0: return LoadHistoryEntry(shEntry, loadType); michael@0: } michael@0: michael@0: // On history navigation via Back/Forward buttons, don't execute michael@0: // automatic JavaScript redirection such as |location.href = ...| or michael@0: // |window.open()| michael@0: // michael@0: // LOAD_NORMAL: window.open(...) etc. michael@0: // LOAD_STOP_CONTENT: location.href = ..., location.assign(...) michael@0: if ((loadType == LOAD_NORMAL || loadType == LOAD_STOP_CONTENT) && michael@0: ShouldBlockLoadingForBackButton()) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Perform the load... michael@0: michael@0: // We need an owner (a referring principal). michael@0: // michael@0: // If ownerIsExplicit is not set there are 4 possibilities: michael@0: // (1) If the system principal or an expanded principal was passed michael@0: // in and we're a typeContent docshell, inherit the principal michael@0: // from the current document instead. michael@0: // (2) In all other cases when the principal passed in is not null, michael@0: // use that principal. michael@0: // (3) If the caller has allowed inheriting from the current document, michael@0: // or if we're being called from system code (eg chrome JS or pure michael@0: // C++) then inheritOwner should be true and InternalLoad will get michael@0: // an owner from the current document. If none of these things are michael@0: // true, then michael@0: // (4) we pass a null owner into the channel, and an owner will be michael@0: // created later from the channel's internal data. michael@0: // michael@0: // If ownerIsExplicit *is* set, there are 4 possibilities michael@0: // (1) If the system principal or an expanded principal was passed in michael@0: // and we're a typeContent docshell, return an error. michael@0: // (2) In all other cases when the principal passed in is not null, michael@0: // use that principal. michael@0: // (3) If the caller has allowed inheriting from the current document, michael@0: // then inheritOwner should be true and InternalLoad will get an owner michael@0: // from the current document. If none of these things are true, then michael@0: // (4) we pass a null owner into the channel, and an owner will be michael@0: // created later from the channel's internal data. michael@0: // michael@0: // NOTE: This all only works because the only thing the owner is used michael@0: // for in InternalLoad is data:, javascript:, and about:blank michael@0: // URIs. For other URIs this would all be dead wrong! michael@0: michael@0: if (owner && mItemType != typeChrome) { michael@0: nsCOMPtr ownerPrincipal = do_QueryInterface(owner); michael@0: if (nsContentUtils::IsSystemOrExpandedPrincipal(ownerPrincipal)) { michael@0: if (ownerIsExplicit) { michael@0: return NS_ERROR_DOM_SECURITY_ERR; michael@0: } michael@0: owner = nullptr; michael@0: inheritOwner = true; michael@0: } michael@0: } michael@0: if (!owner && !inheritOwner && !ownerIsExplicit) { michael@0: // See if there's system or chrome JS code running michael@0: inheritOwner = nsContentUtils::IsSystemPrincipal( michael@0: nsContentUtils::GetSubjectPrincipal()); michael@0: } michael@0: michael@0: if (aLoadFlags & LOAD_FLAGS_DISALLOW_INHERIT_OWNER) { michael@0: inheritOwner = false; michael@0: owner = do_CreateInstance("@mozilla.org/nullprincipal;1"); michael@0: } michael@0: michael@0: uint32_t flags = 0; michael@0: michael@0: if (inheritOwner) michael@0: flags |= INTERNAL_LOAD_FLAGS_INHERIT_OWNER; michael@0: michael@0: if (!sendReferrer) michael@0: flags |= INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER; michael@0: michael@0: if (aLoadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) michael@0: flags |= INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP; michael@0: michael@0: if (aLoadFlags & LOAD_FLAGS_FIXUP_SCHEME_TYPOS) michael@0: flags |= INTERNAL_LOAD_FLAGS_FIXUP_SCHEME_TYPOS; michael@0: michael@0: if (aLoadFlags & LOAD_FLAGS_FIRST_LOAD) michael@0: flags |= INTERNAL_LOAD_FLAGS_FIRST_LOAD; michael@0: michael@0: if (aLoadFlags & LOAD_FLAGS_BYPASS_CLASSIFIER) michael@0: flags |= INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER; michael@0: michael@0: if (aLoadFlags & LOAD_FLAGS_FORCE_ALLOW_COOKIES) michael@0: flags |= INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES; michael@0: michael@0: if (isSrcdoc) michael@0: flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC; michael@0: michael@0: return InternalLoad(aURI, michael@0: referrer, michael@0: owner, michael@0: flags, michael@0: target.get(), michael@0: nullptr, // No type hint michael@0: NullString(), // No forced download michael@0: postStream, michael@0: headersStream, michael@0: loadType, michael@0: nullptr, // No SHEntry michael@0: aFirstParty, michael@0: srcdoc, michael@0: sourceDocShell, michael@0: baseURI, michael@0: nullptr, // No nsIDocShell michael@0: nullptr); // No nsIRequest michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::LoadStream(nsIInputStream *aStream, nsIURI * aURI, michael@0: const nsACString &aContentType, michael@0: const nsACString &aContentCharset, michael@0: nsIDocShellLoadInfo * aLoadInfo) michael@0: { michael@0: NS_ENSURE_ARG(aStream); michael@0: michael@0: mAllowKeywordFixup = false; michael@0: michael@0: // if the caller doesn't pass in a URI we need to create a dummy URI. necko michael@0: // currently requires a URI in various places during the load. Some consumers michael@0: // do as well. michael@0: nsCOMPtr uri = aURI; michael@0: if (!uri) { michael@0: // HACK ALERT michael@0: nsresult rv = NS_OK; michael@0: uri = do_CreateInstance(NS_SIMPLEURI_CONTRACTID, &rv); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: // Make sure that the URI spec "looks" like a protocol and path... michael@0: // For now, just use a bogus protocol called "internal" michael@0: rv = uri->SetSpec(NS_LITERAL_CSTRING("internal:load-stream")); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: } michael@0: michael@0: uint32_t loadType = LOAD_NORMAL; michael@0: if (aLoadInfo) { michael@0: nsDocShellInfoLoadType lt = nsIDocShellLoadInfo::loadNormal; michael@0: (void) aLoadInfo->GetLoadType(<); michael@0: // Get the appropriate LoadType from nsIDocShellLoadInfo type michael@0: loadType = ConvertDocShellLoadInfoToLoadType(lt); michael@0: } michael@0: michael@0: NS_ENSURE_SUCCESS(Stop(nsIWebNavigation::STOP_NETWORK), NS_ERROR_FAILURE); michael@0: michael@0: mLoadType = loadType; michael@0: michael@0: // build up a channel for this stream. michael@0: nsCOMPtr channel; michael@0: NS_ENSURE_SUCCESS(NS_NewInputStreamChannel michael@0: (getter_AddRefs(channel), uri, aStream, michael@0: aContentType, aContentCharset), michael@0: NS_ERROR_FAILURE); michael@0: michael@0: nsCOMPtr michael@0: uriLoader(do_GetService(NS_URI_LOADER_CONTRACTID)); michael@0: NS_ENSURE_TRUE(uriLoader, NS_ERROR_FAILURE); michael@0: michael@0: NS_ENSURE_SUCCESS(DoChannelLoad(channel, uriLoader, false), michael@0: NS_ERROR_FAILURE); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::CreateLoadInfo(nsIDocShellLoadInfo ** aLoadInfo) michael@0: { michael@0: nsDocShellLoadInfo *loadInfo = new nsDocShellLoadInfo(); michael@0: NS_ENSURE_TRUE(loadInfo, NS_ERROR_OUT_OF_MEMORY); michael@0: nsCOMPtr localRef(loadInfo); michael@0: michael@0: *aLoadInfo = localRef; michael@0: NS_ADDREF(*aLoadInfo); michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: /* michael@0: * Reset state to a new content model within the current document and the document michael@0: * viewer. Called by the document before initiating an out of band document.write(). michael@0: */ michael@0: NS_IMETHODIMP michael@0: nsDocShell::PrepareForNewContentModel() michael@0: { michael@0: mEODForCurrentDocument = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::FirePageHideNotification(bool aIsUnload) michael@0: { michael@0: if (mContentViewer && !mFiredUnloadEvent) { michael@0: // Keep an explicit reference since calling PageHide could release michael@0: // mContentViewer michael@0: nsCOMPtr kungFuDeathGrip(mContentViewer); michael@0: mFiredUnloadEvent = true; michael@0: michael@0: if (mTiming) { michael@0: mTiming->NotifyUnloadEventStart(); michael@0: } michael@0: michael@0: mContentViewer->PageHide(aIsUnload); michael@0: michael@0: if (mTiming) { michael@0: mTiming->NotifyUnloadEventEnd(); michael@0: } michael@0: michael@0: nsAutoTArray, 8> kids; michael@0: uint32_t n = mChildList.Length(); michael@0: kids.SetCapacity(n); michael@0: for (uint32_t i = 0; i < n; i++) { michael@0: kids.AppendElement(do_QueryInterface(ChildAt(i))); michael@0: } michael@0: michael@0: n = kids.Length(); michael@0: for (uint32_t i = 0; i < n; ++i) { michael@0: if (kids[i]) { michael@0: kids[i]->FirePageHideNotification(aIsUnload); michael@0: } michael@0: } michael@0: // Now make sure our editor, if any, is detached before we go michael@0: // any farther. michael@0: DetachEditorFromWindow(); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsDocShell::MaybeInitTiming() michael@0: { michael@0: if (mTiming) { michael@0: return; michael@0: } michael@0: michael@0: mTiming = new nsDOMNavigationTiming(); michael@0: mTiming->NotifyNavigationStart(); michael@0: } michael@0: michael@0: michael@0: // michael@0: // Bug 13871: Prevent frameset spoofing michael@0: // michael@0: // This routine answers: 'Is origin's document from same domain as michael@0: // target's document?' michael@0: // michael@0: // file: uris are considered the same domain for the purpose of michael@0: // frame navigation regardless of script accessibility (bug 420425) michael@0: // michael@0: /* static */ michael@0: bool michael@0: nsDocShell::ValidateOrigin(nsIDocShellTreeItem* aOriginTreeItem, michael@0: nsIDocShellTreeItem* aTargetTreeItem) michael@0: { michael@0: // We want to bypass this check for chrome callers, but only if there's michael@0: // JS on the stack. System callers still need to do it. michael@0: if (nsContentUtils::GetCurrentJSContext() && nsContentUtils::IsCallerChrome()) { michael@0: return true; michael@0: } michael@0: michael@0: // Get origin document principal michael@0: nsCOMPtr originDocument(do_GetInterface(aOriginTreeItem)); michael@0: NS_ENSURE_TRUE(originDocument, false); michael@0: michael@0: // Get target principal michael@0: nsCOMPtr targetDocument(do_GetInterface(aTargetTreeItem)); michael@0: NS_ENSURE_TRUE(targetDocument, false); michael@0: michael@0: bool equal; michael@0: nsresult rv = originDocument->NodePrincipal()->Equals(targetDocument->NodePrincipal(), michael@0: &equal); michael@0: if (NS_SUCCEEDED(rv) && equal) { michael@0: return true; michael@0: } michael@0: michael@0: // Not strictly equal, special case if both are file: uris michael@0: bool originIsFile = false; michael@0: bool targetIsFile = false; michael@0: nsCOMPtr originURI; michael@0: nsCOMPtr targetURI; michael@0: nsCOMPtr innerOriginURI; michael@0: nsCOMPtr innerTargetURI; michael@0: michael@0: rv = originDocument->NodePrincipal()->GetURI(getter_AddRefs(originURI)); michael@0: if (NS_SUCCEEDED(rv) && originURI) michael@0: innerOriginURI = NS_GetInnermostURI(originURI); michael@0: michael@0: rv = targetDocument->NodePrincipal()->GetURI(getter_AddRefs(targetURI)); michael@0: if (NS_SUCCEEDED(rv) && targetURI) michael@0: innerTargetURI = NS_GetInnermostURI(targetURI); michael@0: michael@0: return innerOriginURI && innerTargetURI && michael@0: NS_SUCCEEDED(innerOriginURI->SchemeIs("file", &originIsFile)) && michael@0: NS_SUCCEEDED(innerTargetURI->SchemeIs("file", &targetIsFile)) && michael@0: originIsFile && targetIsFile; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetEldestPresContext(nsPresContext** aPresContext) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aPresContext); michael@0: *aPresContext = nullptr; michael@0: michael@0: nsCOMPtr viewer = mContentViewer; michael@0: while (viewer) { michael@0: nsCOMPtr prevViewer; michael@0: viewer->GetPreviousViewer(getter_AddRefs(prevViewer)); michael@0: if (!prevViewer) { michael@0: return viewer->GetPresContext(aPresContext); michael@0: } michael@0: viewer = prevViewer; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetPresContext(nsPresContext ** aPresContext) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aPresContext); michael@0: *aPresContext = nullptr; michael@0: michael@0: if (!mContentViewer) michael@0: return NS_OK; michael@0: michael@0: return mContentViewer->GetPresContext(aPresContext); michael@0: } michael@0: michael@0: NS_IMETHODIMP_(nsIPresShell*) michael@0: nsDocShell::GetPresShell() michael@0: { michael@0: nsRefPtr presContext; michael@0: (void) GetPresContext(getter_AddRefs(presContext)); michael@0: return presContext ? presContext->GetPresShell() : nullptr; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetEldestPresShell(nsIPresShell** aPresShell) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: michael@0: NS_ENSURE_ARG_POINTER(aPresShell); michael@0: *aPresShell = nullptr; michael@0: michael@0: nsRefPtr presContext; michael@0: (void) GetEldestPresContext(getter_AddRefs(presContext)); michael@0: michael@0: if (presContext) { michael@0: NS_IF_ADDREF(*aPresShell = presContext->GetPresShell()); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetContentViewer(nsIContentViewer ** aContentViewer) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aContentViewer); michael@0: michael@0: *aContentViewer = mContentViewer; michael@0: NS_IF_ADDREF(*aContentViewer); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::SetChromeEventHandler(nsIDOMEventTarget* aChromeEventHandler) michael@0: { michael@0: // Weak reference. Don't addref. michael@0: nsCOMPtr handler = do_QueryInterface(aChromeEventHandler); michael@0: mChromeEventHandler = handler.get(); michael@0: michael@0: if (mScriptGlobal) { michael@0: mScriptGlobal->SetChromeEventHandler(mChromeEventHandler); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetChromeEventHandler(nsIDOMEventTarget** aChromeEventHandler) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aChromeEventHandler); michael@0: nsCOMPtr handler = mChromeEventHandler; michael@0: handler.forget(aChromeEventHandler); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* void setCurrentURI (in nsIURI uri); */ michael@0: NS_IMETHODIMP michael@0: nsDocShell::SetCurrentURI(nsIURI *aURI) michael@0: { michael@0: // Note that securityUI will set STATE_IS_INSECURE, even if michael@0: // the scheme of |aURI| is "https". michael@0: SetCurrentURI(aURI, nullptr, true, 0); michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: nsDocShell::SetCurrentURI(nsIURI *aURI, nsIRequest *aRequest, michael@0: bool aFireOnLocationChange, uint32_t aLocationFlags) michael@0: { michael@0: #ifdef PR_LOGGING michael@0: if (gDocShellLeakLog && PR_LOG_TEST(gDocShellLeakLog, PR_LOG_DEBUG)) { michael@0: nsAutoCString spec; michael@0: if (aURI) michael@0: aURI->GetSpec(spec); michael@0: PR_LogPrint("DOCSHELL %p SetCurrentURI %s\n", this, spec.get()); michael@0: } michael@0: #endif michael@0: michael@0: // We don't want to send a location change when we're displaying an error michael@0: // page, and we don't want to change our idea of "current URI" either michael@0: if (mLoadType == LOAD_ERROR_PAGE) { michael@0: return false; michael@0: } michael@0: michael@0: mCurrentURI = NS_TryToMakeImmutable(aURI); michael@0: michael@0: bool isRoot = false; // Is this the root docshell michael@0: bool isSubFrame = false; // Is this a subframe navigation? michael@0: michael@0: nsCOMPtr root; michael@0: michael@0: GetSameTypeRootTreeItem(getter_AddRefs(root)); michael@0: if (root.get() == static_cast(this)) michael@0: { michael@0: // This is the root docshell michael@0: isRoot = true; michael@0: } michael@0: if (mLSHE) { michael@0: mLSHE->GetIsSubFrame(&isSubFrame); michael@0: } michael@0: michael@0: if (!isSubFrame && !isRoot) { michael@0: /* michael@0: * We don't want to send OnLocationChange notifications when michael@0: * a subframe is being loaded for the first time, while michael@0: * visiting a frameset page michael@0: */ michael@0: return false; michael@0: } michael@0: michael@0: if (aFireOnLocationChange) { michael@0: FireOnLocationChange(this, aRequest, aURI, aLocationFlags); michael@0: } michael@0: return !aFireOnLocationChange; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetCharset(nsACString& aCharset) michael@0: { michael@0: aCharset.Truncate(); michael@0: michael@0: nsIPresShell* presShell = GetPresShell(); michael@0: NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE); michael@0: nsIDocument *doc = presShell->GetDocument(); michael@0: NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE); michael@0: aCharset = doc->GetDocumentCharacterSet(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GatherCharsetMenuTelemetry() michael@0: { michael@0: nsCOMPtr viewer; michael@0: GetContentViewer(getter_AddRefs(viewer)); michael@0: if (!viewer) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsIDocument* doc = viewer->GetDocument(); michael@0: if (!doc || doc->WillIgnoreCharsetOverride()) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_USED, true); michael@0: michael@0: bool isFileURL = false; michael@0: nsIURI* url = doc->GetOriginalURI(); michael@0: if (url) { michael@0: url->SchemeIs("file", &isFileURL); michael@0: } michael@0: michael@0: int32_t charsetSource = doc->GetDocumentCharacterSetSource(); michael@0: switch (charsetSource) { michael@0: case kCharsetFromTopLevelDomain: michael@0: // Unlabeled doc on a domain that we map to a fallback encoding michael@0: Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 7); michael@0: break; michael@0: case kCharsetFromFallback: michael@0: case kCharsetFromDocTypeDefault: michael@0: case kCharsetFromCache: michael@0: case kCharsetFromParentFrame: michael@0: case kCharsetFromHintPrevDoc: michael@0: // Changing charset on an unlabeled doc. michael@0: if (isFileURL) { michael@0: Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 0); michael@0: } else { michael@0: Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 1); michael@0: } michael@0: break; michael@0: case kCharsetFromAutoDetection: michael@0: // Changing charset on unlabeled doc where chardet fired michael@0: if (isFileURL) { michael@0: Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 2); michael@0: } else { michael@0: Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 3); michael@0: } michael@0: break; michael@0: case kCharsetFromMetaPrescan: michael@0: case kCharsetFromMetaTag: michael@0: case kCharsetFromChannel: michael@0: // Changing charset on a doc that had a charset label. michael@0: Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 4); michael@0: break; michael@0: case kCharsetFromParentForced: michael@0: case kCharsetFromUserForced: michael@0: // Changing charset on a document that already had an override. michael@0: Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 5); michael@0: break; michael@0: case kCharsetFromIrreversibleAutoDetection: michael@0: case kCharsetFromOtherComponent: michael@0: case kCharsetFromByteOrderMark: michael@0: case kCharsetUninitialized: michael@0: default: michael@0: // Bug. This isn't supposed to happen. michael@0: Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 6); michael@0: break; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::SetCharset(const nsACString& aCharset) michael@0: { michael@0: // set the charset override michael@0: return SetForcedCharset(aCharset); michael@0: } michael@0: michael@0: NS_IMETHODIMP nsDocShell::SetForcedCharset(const nsACString& aCharset) michael@0: { michael@0: if (aCharset.IsEmpty()) { michael@0: mForcedCharset.Truncate(); michael@0: return NS_OK; michael@0: } michael@0: nsAutoCString encoding; michael@0: if (!EncodingUtils::FindEncodingForLabel(aCharset, encoding)) { michael@0: // Reject unknown labels michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: if (!EncodingUtils::IsAsciiCompatible(encoding)) { michael@0: // Reject XSS hazards michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: mForcedCharset = encoding; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsDocShell::GetForcedCharset(nsACString& aResult) michael@0: { michael@0: aResult = mForcedCharset; michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsDocShell::SetParentCharset(const nsACString& aCharset, michael@0: int32_t aCharsetSource, michael@0: nsIPrincipal* aPrincipal) michael@0: { michael@0: mParentCharset = aCharset; michael@0: mParentCharsetSource = aCharsetSource; michael@0: mParentCharsetPrincipal = aPrincipal; michael@0: } michael@0: michael@0: void michael@0: nsDocShell::GetParentCharset(nsACString& aCharset, michael@0: int32_t* aCharsetSource, michael@0: nsIPrincipal** aPrincipal) michael@0: { michael@0: aCharset = mParentCharset; michael@0: *aCharsetSource = mParentCharsetSource; michael@0: NS_IF_ADDREF(*aPrincipal = mParentCharsetPrincipal); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetChannelIsUnsafe(bool *aUnsafe) michael@0: { michael@0: *aUnsafe = false; michael@0: michael@0: nsIChannel* channel = GetCurrentDocChannel(); michael@0: if (!channel) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsCOMPtr jarChannel = do_QueryInterface(channel); michael@0: if (!jarChannel) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: return jarChannel->GetIsUnsafe(aUnsafe); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetHasMixedActiveContentLoaded(bool* aHasMixedActiveContentLoaded) michael@0: { michael@0: nsCOMPtr doc(do_GetInterface(GetAsSupports(this))); michael@0: *aHasMixedActiveContentLoaded = doc && doc->GetHasMixedActiveContentLoaded(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetHasMixedActiveContentBlocked(bool* aHasMixedActiveContentBlocked) michael@0: { michael@0: nsCOMPtr doc(do_GetInterface(GetAsSupports(this))); michael@0: *aHasMixedActiveContentBlocked = doc && doc->GetHasMixedActiveContentBlocked(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetHasMixedDisplayContentLoaded(bool* aHasMixedDisplayContentLoaded) michael@0: { michael@0: nsCOMPtr doc(do_GetInterface(GetAsSupports(this))); michael@0: *aHasMixedDisplayContentLoaded = doc && doc->GetHasMixedDisplayContentLoaded(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetHasMixedDisplayContentBlocked(bool* aHasMixedDisplayContentBlocked) michael@0: { michael@0: nsCOMPtr doc(do_GetInterface(GetAsSupports(this))); michael@0: *aHasMixedDisplayContentBlocked = doc && doc->GetHasMixedDisplayContentBlocked(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetAllowPlugins(bool * aAllowPlugins) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aAllowPlugins); michael@0: michael@0: *aAllowPlugins = mAllowPlugins; michael@0: if (!mAllowPlugins) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool unsafe; michael@0: *aAllowPlugins = NS_SUCCEEDED(GetChannelIsUnsafe(&unsafe)) && !unsafe; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::SetAllowPlugins(bool aAllowPlugins) michael@0: { michael@0: mAllowPlugins = aAllowPlugins; michael@0: //XXX should enable or disable a plugin host michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetAllowJavascript(bool * aAllowJavascript) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aAllowJavascript); michael@0: michael@0: *aAllowJavascript = mAllowJavascript; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::SetAllowJavascript(bool aAllowJavascript) michael@0: { michael@0: mAllowJavascript = aAllowJavascript; michael@0: RecomputeCanExecuteScripts(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetUsePrivateBrowsing(bool* aUsePrivateBrowsing) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aUsePrivateBrowsing); michael@0: michael@0: *aUsePrivateBrowsing = mInPrivateBrowsing; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::SetUsePrivateBrowsing(bool aUsePrivateBrowsing) michael@0: { michael@0: nsContentUtils::ReportToConsoleNonLocalized( michael@0: NS_LITERAL_STRING("Only internal code is allowed to set the usePrivateBrowsing attribute"), michael@0: nsIScriptError::warningFlag, michael@0: NS_LITERAL_CSTRING("Internal API Used"), michael@0: mContentViewer ? mContentViewer->GetDocument() : nullptr); michael@0: michael@0: return SetPrivateBrowsing(aUsePrivateBrowsing); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::SetPrivateBrowsing(bool aUsePrivateBrowsing) michael@0: { michael@0: bool changed = aUsePrivateBrowsing != mInPrivateBrowsing; michael@0: if (changed) { michael@0: mInPrivateBrowsing = aUsePrivateBrowsing; michael@0: if (mAffectPrivateSessionLifetime) { michael@0: if (aUsePrivateBrowsing) { michael@0: IncreasePrivateDocShellCount(); michael@0: } else { michael@0: DecreasePrivateDocShellCount(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: nsTObserverArray::ForwardIterator iter(mChildList); michael@0: while (iter.HasMore()) { michael@0: nsCOMPtr shell = do_QueryObject(iter.GetNext()); michael@0: if (shell) { michael@0: shell->SetPrivateBrowsing(aUsePrivateBrowsing); michael@0: } michael@0: } michael@0: michael@0: if (changed) { michael@0: nsTObserverArray::ForwardIterator iter(mPrivacyObservers); michael@0: while (iter.HasMore()) { michael@0: nsWeakPtr ref = iter.GetNext(); michael@0: nsCOMPtr obs = do_QueryReferent(ref); michael@0: if (!obs) { michael@0: mPrivacyObservers.RemoveElement(ref); michael@0: } else { michael@0: obs->PrivateModeChanged(aUsePrivateBrowsing); michael@0: } michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetUseRemoteTabs(bool* aUseRemoteTabs) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aUseRemoteTabs); michael@0: michael@0: *aUseRemoteTabs = mUseRemoteTabs; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::SetRemoteTabs(bool aUseRemoteTabs) michael@0: { michael@0: #ifdef MOZ_CRASHREPORTER michael@0: if (aUseRemoteTabs) { michael@0: CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("DOMIPCEnabled"), michael@0: NS_LITERAL_CSTRING("1")); michael@0: } michael@0: #endif michael@0: michael@0: mUseRemoteTabs = aUseRemoteTabs; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::SetAffectPrivateSessionLifetime(bool aAffectLifetime) michael@0: { michael@0: bool change = aAffectLifetime != mAffectPrivateSessionLifetime; michael@0: if (change && mInPrivateBrowsing) { michael@0: if (aAffectLifetime) { michael@0: IncreasePrivateDocShellCount(); michael@0: } else { michael@0: DecreasePrivateDocShellCount(); michael@0: } michael@0: } michael@0: mAffectPrivateSessionLifetime = aAffectLifetime; michael@0: michael@0: nsTObserverArray::ForwardIterator iter(mChildList); michael@0: while (iter.HasMore()) { michael@0: nsCOMPtr shell = do_QueryObject(iter.GetNext()); michael@0: if (shell) { michael@0: shell->SetAffectPrivateSessionLifetime(aAffectLifetime); michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetAffectPrivateSessionLifetime(bool* aAffectLifetime) michael@0: { michael@0: *aAffectLifetime = mAffectPrivateSessionLifetime; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::AddWeakPrivacyTransitionObserver(nsIPrivacyTransitionObserver* aObserver) michael@0: { michael@0: nsWeakPtr weakObs = do_GetWeakReference(aObserver); michael@0: if (!weakObs) { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: return mPrivacyObservers.AppendElement(weakObs) ? NS_OK : NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::AddWeakReflowObserver(nsIReflowObserver* aObserver) michael@0: { michael@0: nsWeakPtr weakObs = do_GetWeakReference(aObserver); michael@0: if (!weakObs) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: return mReflowObservers.AppendElement(weakObs) ? NS_OK : NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::RemoveWeakReflowObserver(nsIReflowObserver* aObserver) michael@0: { michael@0: nsWeakPtr obs = do_GetWeakReference(aObserver); michael@0: return mReflowObservers.RemoveElement(obs) ? NS_OK : NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::NotifyReflowObservers(bool aInterruptible, michael@0: DOMHighResTimeStamp aStart, michael@0: DOMHighResTimeStamp aEnd) michael@0: { michael@0: nsTObserverArray::ForwardIterator iter(mReflowObservers); michael@0: while (iter.HasMore()) { michael@0: nsWeakPtr ref = iter.GetNext(); michael@0: nsCOMPtr obs = do_QueryReferent(ref); michael@0: if (!obs) { michael@0: mReflowObservers.RemoveElement(ref); michael@0: } else if (aInterruptible) { michael@0: obs->ReflowInterruptible(aStart, aEnd); michael@0: } else { michael@0: obs->Reflow(aStart, aEnd); michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsDocShell::GetAllowMetaRedirects(bool * aReturn) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aReturn); michael@0: michael@0: *aReturn = mAllowMetaRedirects; michael@0: if (!mAllowMetaRedirects) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool unsafe; michael@0: *aReturn = NS_SUCCEEDED(GetChannelIsUnsafe(&unsafe)) && !unsafe; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsDocShell::SetAllowMetaRedirects(bool aValue) michael@0: { michael@0: mAllowMetaRedirects = aValue; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsDocShell::GetAllowSubframes(bool * aAllowSubframes) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aAllowSubframes); michael@0: michael@0: *aAllowSubframes = mAllowSubframes; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsDocShell::SetAllowSubframes(bool aAllowSubframes) michael@0: { michael@0: mAllowSubframes = aAllowSubframes; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsDocShell::GetAllowImages(bool * aAllowImages) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aAllowImages); michael@0: michael@0: *aAllowImages = mAllowImages; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsDocShell::SetAllowImages(bool aAllowImages) michael@0: { michael@0: mAllowImages = aAllowImages; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsDocShell::GetAllowMedia(bool * aAllowMedia) michael@0: { michael@0: *aAllowMedia = mAllowMedia; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsDocShell::SetAllowMedia(bool aAllowMedia) michael@0: { michael@0: mAllowMedia = aAllowMedia; michael@0: michael@0: // Mute or unmute audio contexts attached to the inner window. michael@0: if (mScriptGlobal) { michael@0: nsPIDOMWindow* innerWin = mScriptGlobal->GetCurrentInnerWindow(); michael@0: if (innerWin) { michael@0: if (aAllowMedia) { michael@0: innerWin->UnmuteAudioContexts(); michael@0: } else { michael@0: innerWin->MuteAudioContexts(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsDocShell::GetAllowDNSPrefetch(bool * aAllowDNSPrefetch) michael@0: { michael@0: *aAllowDNSPrefetch = mAllowDNSPrefetch; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsDocShell::SetAllowDNSPrefetch(bool aAllowDNSPrefetch) michael@0: { michael@0: mAllowDNSPrefetch = aAllowDNSPrefetch; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsDocShell::GetAllowWindowControl(bool * aAllowWindowControl) michael@0: { michael@0: *aAllowWindowControl = mAllowWindowControl; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsDocShell::SetAllowWindowControl(bool aAllowWindowControl) michael@0: { michael@0: mAllowWindowControl = aAllowWindowControl; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetAllowContentRetargeting(bool* aAllowContentRetargeting) michael@0: { michael@0: *aAllowContentRetargeting = mAllowContentRetargeting; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::SetAllowContentRetargeting(bool aAllowContentRetargeting) michael@0: { michael@0: mAllowContentRetargeting = aAllowContentRetargeting; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetFullscreenAllowed(bool* aFullscreenAllowed) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aFullscreenAllowed); michael@0: michael@0: // Browsers and apps have their mFullscreenAllowed retrieved from their michael@0: // corresponding iframe in their parent upon creation. michael@0: if (mFullscreenAllowed != CHECK_ATTRIBUTES) { michael@0: *aFullscreenAllowed = (mFullscreenAllowed == PARENT_ALLOWS); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Assume false until we determine otherwise... michael@0: *aFullscreenAllowed = false; michael@0: michael@0: // For non-browsers/apps, check that the enclosing iframe element michael@0: // has the allowfullscreen attribute set to true. If any ancestor michael@0: // iframe does not have mozallowfullscreen=true, then fullscreen is michael@0: // prohibited. michael@0: nsCOMPtr win = do_GetInterface(GetAsSupports(this)); michael@0: if (!win) { michael@0: return NS_OK; michael@0: } michael@0: nsCOMPtr frameElement = do_QueryInterface(win->GetFrameElementInternal()); michael@0: if (frameElement && michael@0: frameElement->IsHTML(nsGkAtoms::iframe) && michael@0: !frameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::allowfullscreen) && michael@0: !frameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::mozallowfullscreen)) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // If we have no parent then we're the root docshell; no ancestor of the michael@0: // original docshell doesn't have a allowfullscreen attribute, so michael@0: // report fullscreen as allowed. michael@0: nsCOMPtr dsti = do_GetInterface(GetAsSupports(this)); michael@0: NS_ENSURE_TRUE(dsti, NS_OK); michael@0: michael@0: nsCOMPtr parentTreeItem; michael@0: dsti->GetParent(getter_AddRefs(parentTreeItem)); michael@0: if (!parentTreeItem) { michael@0: *aFullscreenAllowed = true; michael@0: return NS_OK; michael@0: } michael@0: // Otherwise, we have a parent, continue the checking for michael@0: // mozFullscreenAllowed in the parent docshell's ancestors. michael@0: nsCOMPtr parent = do_QueryInterface(parentTreeItem); michael@0: NS_ENSURE_TRUE(parent, NS_OK); michael@0: michael@0: return parent->GetFullscreenAllowed(aFullscreenAllowed); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::SetFullscreenAllowed(bool aFullscreenAllowed) michael@0: { michael@0: if (!nsIDocShell::GetIsBrowserOrApp()) { michael@0: // Only allow setting of fullscreenAllowed on content/process boundaries. michael@0: // At non-boundaries the fullscreenAllowed attribute is calculated based on michael@0: // whether all enclosing frames have the "mozFullscreenAllowed" attribute michael@0: // set to "true". fullscreenAllowed is set at the process boundaries to michael@0: // propagate the value of the parent's "mozFullscreenAllowed" attribute michael@0: // across process boundaries. michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: mFullscreenAllowed = (aFullscreenAllowed ? PARENT_ALLOWS : PARENT_PROHIBITS); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetMayEnableCharacterEncodingMenu(bool* aMayEnableCharacterEncodingMenu) michael@0: { michael@0: *aMayEnableCharacterEncodingMenu = false; michael@0: if (!mContentViewer) { michael@0: return NS_OK; michael@0: } michael@0: nsIDocument* doc = mContentViewer->GetDocument(); michael@0: if (!doc) { michael@0: return NS_OK; michael@0: } michael@0: if (doc->WillIgnoreCharsetOverride()) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: *aMayEnableCharacterEncodingMenu = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetDocShellEnumerator(int32_t aItemType, int32_t aDirection, nsISimpleEnumerator **outEnum) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(outEnum); michael@0: *outEnum = nullptr; michael@0: michael@0: nsRefPtr docShellEnum; michael@0: if (aDirection == ENUMERATE_FORWARDS) michael@0: docShellEnum = new nsDocShellForwardsEnumerator; michael@0: else michael@0: docShellEnum = new nsDocShellBackwardsEnumerator; michael@0: michael@0: if (!docShellEnum) return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: nsresult rv = docShellEnum->SetEnumDocShellType(aItemType); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = docShellEnum->SetEnumerationRootItem((nsIDocShellTreeItem *)this); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = docShellEnum->First(); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = docShellEnum->QueryInterface(NS_GET_IID(nsISimpleEnumerator), (void **)outEnum); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetAppType(uint32_t * aAppType) michael@0: { michael@0: *aAppType = mAppType; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::SetAppType(uint32_t aAppType) michael@0: { michael@0: mAppType = aAppType; michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetAllowAuth(bool * aAllowAuth) michael@0: { michael@0: *aAllowAuth = mAllowAuth; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::SetAllowAuth(bool aAllowAuth) michael@0: { michael@0: mAllowAuth = aAllowAuth; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetZoom(float *zoom) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(zoom); michael@0: *zoom = 1.0f; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::SetZoom(float zoom) michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetMarginWidth(int32_t * aWidth) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aWidth); michael@0: michael@0: *aWidth = mMarginWidth; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::SetMarginWidth(int32_t aWidth) michael@0: { michael@0: mMarginWidth = aWidth; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetMarginHeight(int32_t * aHeight) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aHeight); michael@0: michael@0: *aHeight = mMarginHeight; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::SetMarginHeight(int32_t aHeight) michael@0: { michael@0: mMarginHeight = aHeight; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetBusyFlags(uint32_t * aBusyFlags) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aBusyFlags); michael@0: michael@0: *aBusyFlags = mBusyFlags; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::TabToTreeOwner(bool aForward, bool* aTookFocus) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aTookFocus); michael@0: michael@0: nsCOMPtr chromeFocus = do_GetInterface(mTreeOwner); michael@0: if (chromeFocus) { michael@0: if (aForward) michael@0: *aTookFocus = NS_SUCCEEDED(chromeFocus->FocusNextElement()); michael@0: else michael@0: *aTookFocus = NS_SUCCEEDED(chromeFocus->FocusPrevElement()); michael@0: } else michael@0: *aTookFocus = false; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetSecurityUI(nsISecureBrowserUI **aSecurityUI) michael@0: { michael@0: NS_IF_ADDREF(*aSecurityUI = mSecurityUI); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::SetSecurityUI(nsISecureBrowserUI *aSecurityUI) michael@0: { michael@0: mSecurityUI = aSecurityUI; michael@0: mSecurityUI->SetDocShell(this); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetUseErrorPages(bool *aUseErrorPages) michael@0: { michael@0: *aUseErrorPages = UseErrorPages(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::SetUseErrorPages(bool aUseErrorPages) michael@0: { michael@0: // If mUseErrorPages is set explicitly, stop using sUseErrorPages. michael@0: if (mObserveErrorPages) { michael@0: mObserveErrorPages = false; michael@0: } michael@0: mUseErrorPages = aUseErrorPages; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetPreviousTransIndex(int32_t *aPreviousTransIndex) michael@0: { michael@0: *aPreviousTransIndex = mPreviousTransIndex; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetLoadedTransIndex(int32_t *aLoadedTransIndex) michael@0: { michael@0: *aLoadedTransIndex = mLoadedTransIndex; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::HistoryPurged(int32_t aNumEntries) michael@0: { michael@0: // These indices are used for fastback cache eviction, to determine michael@0: // which session history entries are candidates for content viewer michael@0: // eviction. We need to adjust by the number of entries that we michael@0: // just purged from history, so that we look at the right session history michael@0: // entries during eviction. michael@0: mPreviousTransIndex = std::max(-1, mPreviousTransIndex - aNumEntries); michael@0: mLoadedTransIndex = std::max(0, mLoadedTransIndex - aNumEntries); michael@0: michael@0: nsTObserverArray::ForwardIterator iter(mChildList); michael@0: while (iter.HasMore()) { michael@0: nsCOMPtr shell = do_QueryObject(iter.GetNext()); michael@0: if (shell) { michael@0: shell->HistoryPurged(aNumEntries); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsDocShell::HistoryTransactionRemoved(int32_t aIndex) michael@0: { michael@0: // These indices are used for fastback cache eviction, to determine michael@0: // which session history entries are candidates for content viewer michael@0: // eviction. We need to adjust by the number of entries that we michael@0: // just purged from history, so that we look at the right session history michael@0: // entries during eviction. michael@0: if (aIndex == mPreviousTransIndex) { michael@0: mPreviousTransIndex = -1; michael@0: } else if (aIndex < mPreviousTransIndex) { michael@0: --mPreviousTransIndex; michael@0: } michael@0: if (mLoadedTransIndex == aIndex) { michael@0: mLoadedTransIndex = 0; michael@0: } else if (aIndex < mLoadedTransIndex) { michael@0: --mLoadedTransIndex; michael@0: } michael@0: michael@0: nsTObserverArray::ForwardIterator iter(mChildList); michael@0: while (iter.HasMore()) { michael@0: nsCOMPtr shell = do_QueryObject(iter.GetNext()); michael@0: if (shell) { michael@0: static_cast(shell.get())-> michael@0: HistoryTransactionRemoved(aIndex); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsIDOMStorageManager* michael@0: nsDocShell::TopSessionStorageManager() michael@0: { michael@0: nsresult rv; michael@0: michael@0: nsCOMPtr topItem; michael@0: rv = GetSameTypeRootTreeItem(getter_AddRefs(topItem)); michael@0: if (NS_FAILED(rv)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: if (!topItem) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsDocShell* topDocShell = static_cast(topItem.get()); michael@0: if (topDocShell != this) { michael@0: return topDocShell->TopSessionStorageManager(); michael@0: } michael@0: michael@0: if (!mSessionStorageManager) { michael@0: mSessionStorageManager = michael@0: do_CreateInstance("@mozilla.org/dom/sessionStorage-manager;1"); michael@0: } michael@0: michael@0: return mSessionStorageManager; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetSessionStorageForPrincipal(nsIPrincipal* aPrincipal, michael@0: const nsAString& aDocumentURI, michael@0: bool aCreate, michael@0: nsIDOMStorage** aStorage) michael@0: { michael@0: nsCOMPtr manager = TopSessionStorageManager(); michael@0: if (!manager) { michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: nsCOMPtr thirdPartyUtil = michael@0: do_GetService(THIRDPARTYUTIL_CONTRACTID); michael@0: if (!thirdPartyUtil) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsCOMPtr doc(do_GetInterface(GetAsSupports(this))); michael@0: nsCOMPtr firstPartyIsolationURI; michael@0: nsresult rv = thirdPartyUtil->GetFirstPartyIsolationURI(nullptr, doc, michael@0: getter_AddRefs(firstPartyIsolationURI)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (aCreate) { michael@0: return manager->CreateStorageForFirstParty(firstPartyIsolationURI, michael@0: aPrincipal, aDocumentURI, michael@0: mInPrivateBrowsing, aStorage); michael@0: } michael@0: michael@0: return manager->GetStorageForFirstParty(firstPartyIsolationURI, aPrincipal, michael@0: mInPrivateBrowsing, aStorage); michael@0: } michael@0: michael@0: // Bacause it is not called from anywhere, nsDocShell::AddSessionStorage() michael@0: // does not need to be modified to isolate DOM Storage to the first party URI. michael@0: nsresult michael@0: nsDocShell::AddSessionStorage(nsIPrincipal* aPrincipal, michael@0: nsIDOMStorage* aStorage) michael@0: { michael@0: nsCOMPtr pistorage = do_QueryInterface(aStorage); michael@0: nsIPrincipal* storagePrincipal = pistorage->GetPrincipal(); michael@0: if (storagePrincipal != aPrincipal) { michael@0: NS_ERROR("Wanting to add a sessionStorage for different principal"); michael@0: return NS_ERROR_DOM_SECURITY_ERR; michael@0: } michael@0: michael@0: nsCOMPtr manager = TopSessionStorageManager(); michael@0: if (!manager) { michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: return manager->CloneStorage(aStorage); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetCurrentDocumentChannel(nsIChannel** aResult) michael@0: { michael@0: NS_IF_ADDREF(*aResult = GetCurrentDocChannel()); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsIChannel* michael@0: nsDocShell::GetCurrentDocChannel() michael@0: { michael@0: if (mContentViewer) { michael@0: nsIDocument* doc = mContentViewer->GetDocument(); michael@0: if (doc) { michael@0: return doc->GetChannel(); michael@0: } michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::AddWeakScrollObserver(nsIScrollObserver* aObserver) michael@0: { michael@0: nsWeakPtr weakObs = do_GetWeakReference(aObserver); michael@0: if (!weakObs) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: return mScrollObservers.AppendElement(weakObs) ? NS_OK : NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::RemoveWeakScrollObserver(nsIScrollObserver* aObserver) michael@0: { michael@0: nsWeakPtr obs = do_GetWeakReference(aObserver); michael@0: return mScrollObservers.RemoveElement(obs) ? NS_OK : NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::NotifyScrollObservers() michael@0: { michael@0: nsTObserverArray::ForwardIterator iter(mScrollObservers); michael@0: while (iter.HasMore()) { michael@0: nsWeakPtr ref = iter.GetNext(); michael@0: nsCOMPtr obs = do_QueryReferent(ref); michael@0: if (obs) { michael@0: obs->ScrollPositionChanged(); michael@0: } else { michael@0: mScrollObservers.RemoveElement(ref); michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: //***************************************************************************** michael@0: // nsDocShell::nsIDocShellTreeItem michael@0: //***************************************************************************** michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetName(nsAString& aName) michael@0: { michael@0: aName = mName; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::SetName(const nsAString& aName) michael@0: { michael@0: mName = aName; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::NameEquals(const char16_t *aName, bool *_retval) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aName); michael@0: NS_ENSURE_ARG_POINTER(_retval); michael@0: *_retval = mName.Equals(aName); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* virtual */ int32_t michael@0: nsDocShell::ItemType() michael@0: { michael@0: return mItemType; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetItemType(int32_t * aItemType) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aItemType); michael@0: michael@0: *aItemType = ItemType(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::SetItemType(int32_t aItemType) michael@0: { michael@0: NS_ENSURE_ARG((aItemType == typeChrome) || (typeContent == aItemType)); michael@0: michael@0: // Only allow setting the type on root docshells. Those would be the ones michael@0: // that have the docloader service as mParent or have no mParent at all. michael@0: nsCOMPtr docLoaderService = michael@0: do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID); michael@0: NS_ENSURE_TRUE(docLoaderService, NS_ERROR_UNEXPECTED); michael@0: michael@0: NS_ENSURE_STATE(!mParent || mParent == docLoaderService); michael@0: michael@0: mItemType = aItemType; michael@0: michael@0: // disable auth prompting for anything but content michael@0: mAllowAuth = mItemType == typeContent; michael@0: michael@0: nsRefPtr presContext = nullptr; michael@0: GetPresContext(getter_AddRefs(presContext)); michael@0: if (presContext) { michael@0: presContext->UpdateIsChrome(); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetParent(nsIDocShellTreeItem ** aParent) michael@0: { michael@0: if (!mParent) { michael@0: *aParent = nullptr; michael@0: } else { michael@0: CallQueryInterface(mParent, aParent); michael@0: } michael@0: // Note that in the case when the parent is not an nsIDocShellTreeItem we michael@0: // don't want to throw; we just want to return null. michael@0: return NS_OK; michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsDocShell::GetParentDocshell() michael@0: { michael@0: nsCOMPtr docshell = do_QueryInterface(GetAsSupports(mParent)); michael@0: return docshell.forget().downcast(); michael@0: } michael@0: michael@0: void michael@0: nsDocShell::RecomputeCanExecuteScripts() michael@0: { michael@0: bool old = mCanExecuteScripts; michael@0: nsRefPtr parent = GetParentDocshell(); michael@0: michael@0: // If we have no tree owner, that means that we've been detached from the michael@0: // docshell tree (this is distinct from having no parent dochshell, which michael@0: // is the case for root docshells). It would be nice to simply disallow michael@0: // script in detached docshells, but bug 986542 demonstrates that this michael@0: // behavior breaks at least one website. michael@0: // michael@0: // So instead, we use our previous value, unless mAllowJavascript has been michael@0: // explicitly set to false. michael@0: if (!mTreeOwner) { michael@0: mCanExecuteScripts = mCanExecuteScripts && mAllowJavascript; michael@0: // If scripting has been explicitly disabled on our docshell, we're done. michael@0: } else if (!mAllowJavascript) { michael@0: mCanExecuteScripts = false; michael@0: // If we have a parent, inherit. michael@0: } else if (parent) { michael@0: mCanExecuteScripts = parent->mCanExecuteScripts; michael@0: // Otherwise, we're the root of the tree, and we haven't explicitly disabled michael@0: // script. Allow. michael@0: } else { michael@0: mCanExecuteScripts = true; michael@0: } michael@0: michael@0: // Inform our active DOM window. michael@0: // michael@0: // This will pass the outer, which will be in the scope of the active inner. michael@0: if (mScriptGlobal && mScriptGlobal->GetGlobalJSObject()) { michael@0: xpc::Scriptability& scriptability = michael@0: xpc::Scriptability::Get(mScriptGlobal->GetGlobalJSObject()); michael@0: scriptability.SetDocShellAllowsScript(mCanExecuteScripts); michael@0: } michael@0: michael@0: // If our value has changed, our children might be affected. Recompute their michael@0: // value as well. michael@0: if (old != mCanExecuteScripts) { michael@0: nsTObserverArray::ForwardIterator iter(mChildList); michael@0: while (iter.HasMore()) { michael@0: static_cast(iter.GetNext())->RecomputeCanExecuteScripts(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsDocShell::SetDocLoaderParent(nsDocLoader * aParent) michael@0: { michael@0: bool wasFrame = IsFrame(); michael@0: michael@0: nsDocLoader::SetDocLoaderParent(aParent); michael@0: michael@0: nsCOMPtr priorityGroup = do_QueryInterface(mLoadGroup); michael@0: if (wasFrame != IsFrame() && priorityGroup) { michael@0: priorityGroup->AdjustPriority(wasFrame ? -1 : 1); michael@0: } michael@0: michael@0: // Curse ambiguous nsISupports inheritance! michael@0: nsISupports* parent = GetAsSupports(aParent); michael@0: michael@0: // If parent is another docshell, we inherit all their flags for michael@0: // allowing plugins, scripting etc. michael@0: bool value; michael@0: nsCOMPtr parentAsDocShell(do_QueryInterface(parent)); michael@0: if (parentAsDocShell) michael@0: { michael@0: if (NS_SUCCEEDED(parentAsDocShell->GetAllowPlugins(&value))) michael@0: { michael@0: SetAllowPlugins(value); michael@0: } michael@0: if (NS_SUCCEEDED(parentAsDocShell->GetAllowJavascript(&value))) michael@0: { michael@0: SetAllowJavascript(value); michael@0: } michael@0: if (NS_SUCCEEDED(parentAsDocShell->GetAllowMetaRedirects(&value))) michael@0: { michael@0: SetAllowMetaRedirects(value); michael@0: } michael@0: if (NS_SUCCEEDED(parentAsDocShell->GetAllowSubframes(&value))) michael@0: { michael@0: SetAllowSubframes(value); michael@0: } michael@0: if (NS_SUCCEEDED(parentAsDocShell->GetAllowImages(&value))) michael@0: { michael@0: SetAllowImages(value); michael@0: } michael@0: SetAllowMedia(parentAsDocShell->GetAllowMedia()); michael@0: if (NS_SUCCEEDED(parentAsDocShell->GetAllowWindowControl(&value))) michael@0: { michael@0: SetAllowWindowControl(value); michael@0: } michael@0: SetAllowContentRetargeting( michael@0: parentAsDocShell->GetAllowContentRetargeting()); michael@0: if (NS_SUCCEEDED(parentAsDocShell->GetIsActive(&value))) michael@0: { michael@0: SetIsActive(value); michael@0: } michael@0: if (NS_FAILED(parentAsDocShell->GetAllowDNSPrefetch(&value))) { michael@0: value = false; michael@0: } michael@0: SetAllowDNSPrefetch(value); michael@0: value = parentAsDocShell->GetAffectPrivateSessionLifetime(); michael@0: SetAffectPrivateSessionLifetime(value); michael@0: uint32_t flags; michael@0: if (NS_SUCCEEDED(parentAsDocShell->GetDefaultLoadFlags(&flags))) michael@0: { michael@0: SetDefaultLoadFlags(flags); michael@0: } michael@0: michael@0: } michael@0: michael@0: nsCOMPtr parentAsLoadContext(do_QueryInterface(parent)); michael@0: if (parentAsLoadContext && michael@0: NS_SUCCEEDED(parentAsLoadContext->GetUsePrivateBrowsing(&value))) michael@0: { michael@0: SetPrivateBrowsing(value); michael@0: } michael@0: michael@0: nsCOMPtr parentURIListener(do_GetInterface(parent)); michael@0: if (parentURIListener) michael@0: mContentListener->SetParentContentListener(parentURIListener); michael@0: michael@0: // Our parent has changed. Recompute scriptability. michael@0: RecomputeCanExecuteScripts(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetSameTypeParent(nsIDocShellTreeItem ** aParent) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aParent); michael@0: *aParent = nullptr; michael@0: michael@0: if (nsIDocShell::GetIsBrowserOrApp()) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsCOMPtr parent = michael@0: do_QueryInterface(GetAsSupports(mParent)); michael@0: if (!parent) michael@0: return NS_OK; michael@0: michael@0: if (parent->ItemType() == mItemType) { michael@0: parent.swap(*aParent); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetSameTypeParentIgnoreBrowserAndAppBoundaries(nsIDocShell** aParent) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aParent); michael@0: *aParent = nullptr; michael@0: michael@0: nsCOMPtr parent = michael@0: do_QueryInterface(GetAsSupports(mParent)); michael@0: if (!parent) michael@0: return NS_OK; michael@0: michael@0: if (parent->ItemType() == mItemType) { michael@0: nsCOMPtr parentDS = do_QueryInterface(parent); michael@0: parentDS.forget(aParent); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetRootTreeItem(nsIDocShellTreeItem ** aRootTreeItem) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aRootTreeItem); michael@0: *aRootTreeItem = static_cast(this); michael@0: michael@0: nsCOMPtr parent; michael@0: NS_ENSURE_SUCCESS(GetParent(getter_AddRefs(parent)), NS_ERROR_FAILURE); michael@0: while (parent) { michael@0: *aRootTreeItem = parent; michael@0: NS_ENSURE_SUCCESS((*aRootTreeItem)->GetParent(getter_AddRefs(parent)), michael@0: NS_ERROR_FAILURE); michael@0: } michael@0: NS_ADDREF(*aRootTreeItem); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetSameTypeRootTreeItem(nsIDocShellTreeItem ** aRootTreeItem) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aRootTreeItem); michael@0: *aRootTreeItem = static_cast(this); michael@0: michael@0: nsCOMPtr parent; michael@0: NS_ENSURE_SUCCESS(GetSameTypeParent(getter_AddRefs(parent)), michael@0: NS_ERROR_FAILURE); michael@0: while (parent) { michael@0: *aRootTreeItem = parent; michael@0: NS_ENSURE_SUCCESS((*aRootTreeItem)-> michael@0: GetSameTypeParent(getter_AddRefs(parent)), michael@0: NS_ERROR_FAILURE); michael@0: } michael@0: NS_ADDREF(*aRootTreeItem); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* static */ michael@0: bool michael@0: nsDocShell::CanAccessItem(nsIDocShellTreeItem* aTargetItem, michael@0: nsIDocShellTreeItem* aAccessingItem, michael@0: bool aConsiderOpener) michael@0: { michael@0: NS_PRECONDITION(aTargetItem, "Must have target item!"); michael@0: michael@0: if (!gValidateOrigin || !aAccessingItem) { michael@0: // Good to go michael@0: return true; michael@0: } michael@0: michael@0: // XXXbz should we care if aAccessingItem or the document therein is michael@0: // chrome? Should those get extra privileges? michael@0: michael@0: // For historical context, see: michael@0: // michael@0: // Bug 13871: Prevent frameset spoofing michael@0: // Bug 103638: Targets with same name in different windows open in wrong michael@0: // window with javascript michael@0: // Bug 408052: Adopt "ancestor" frame navigation policy michael@0: michael@0: // Now do a security check. michael@0: // michael@0: // Disallow navigation if the two frames are not part of the same app, or if michael@0: // they have different is-in-browser-element states. michael@0: // michael@0: // Allow navigation if michael@0: // 1) aAccessingItem can script aTargetItem or one of its ancestors in michael@0: // the frame hierarchy or michael@0: // 2) aTargetItem is a top-level frame and aAccessingItem is its descendant michael@0: // 3) aTargetItem is a top-level frame and aAccessingItem can target michael@0: // its opener per rule (1) or (2). michael@0: michael@0: if (aTargetItem == aAccessingItem) { michael@0: // A frame is allowed to navigate itself. michael@0: return true; michael@0: } michael@0: michael@0: nsCOMPtr targetDS = do_QueryInterface(aTargetItem); michael@0: nsCOMPtr accessingDS = do_QueryInterface(aAccessingItem); michael@0: if (!!targetDS != !!accessingDS) { michael@0: // We must be able to convert both or neither to nsIDocShell. michael@0: return false; michael@0: } michael@0: michael@0: if (targetDS && accessingDS && michael@0: (targetDS->GetIsInBrowserElement() != michael@0: accessingDS->GetIsInBrowserElement() || michael@0: targetDS->GetAppId() != accessingDS->GetAppId())) { michael@0: return false; michael@0: } michael@0: michael@0: nsCOMPtr accessingRoot; michael@0: aAccessingItem->GetSameTypeRootTreeItem(getter_AddRefs(accessingRoot)); michael@0: michael@0: if (aTargetItem == accessingRoot) { michael@0: // A frame can navigate its root. michael@0: return true; michael@0: } michael@0: michael@0: // Check if aAccessingItem can navigate one of aTargetItem's ancestors. michael@0: nsCOMPtr target = aTargetItem; michael@0: do { michael@0: if (ValidateOrigin(aAccessingItem, target)) { michael@0: return true; michael@0: } michael@0: michael@0: nsCOMPtr parent; michael@0: target->GetSameTypeParent(getter_AddRefs(parent)); michael@0: parent.swap(target); michael@0: } while (target); michael@0: michael@0: nsCOMPtr targetRoot; michael@0: aTargetItem->GetSameTypeRootTreeItem(getter_AddRefs(targetRoot)); michael@0: michael@0: if (aTargetItem != targetRoot) { michael@0: // target is a subframe, not in accessor's frame hierarchy, and all its michael@0: // ancestors have origins different from that of the accessor. Don't michael@0: // allow access. michael@0: return false; michael@0: } michael@0: michael@0: if (!aConsiderOpener) { michael@0: // All done here michael@0: return false; michael@0: } michael@0: michael@0: nsCOMPtr targetWindow = do_GetInterface(aTargetItem); michael@0: if (!targetWindow) { michael@0: NS_ERROR("This should not happen, really"); michael@0: return false; michael@0: } michael@0: michael@0: nsCOMPtr targetOpener; michael@0: targetWindow->GetOpener(getter_AddRefs(targetOpener)); michael@0: nsCOMPtr openerWebNav(do_GetInterface(targetOpener)); michael@0: nsCOMPtr openerItem(do_QueryInterface(openerWebNav)); michael@0: michael@0: if (!openerItem) { michael@0: return false; michael@0: } michael@0: michael@0: return CanAccessItem(openerItem, aAccessingItem, false); michael@0: } michael@0: michael@0: static bool michael@0: ItemIsActive(nsIDocShellTreeItem *aItem) michael@0: { michael@0: nsCOMPtr window(do_GetInterface(aItem)); michael@0: michael@0: if (window) { michael@0: bool isClosed; michael@0: michael@0: if (NS_SUCCEEDED(window->GetClosed(&isClosed)) && !isClosed) { michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::FindItemWithName(const char16_t * aName, michael@0: nsISupports * aRequestor, michael@0: nsIDocShellTreeItem * aOriginalRequestor, michael@0: nsIDocShellTreeItem ** _retval) michael@0: { michael@0: NS_ENSURE_ARG(aName); michael@0: NS_ENSURE_ARG_POINTER(_retval); michael@0: michael@0: // If we don't find one, we return NS_OK and a null result michael@0: *_retval = nullptr; michael@0: michael@0: if (!*aName) michael@0: return NS_OK; michael@0: michael@0: if (aRequestor) { michael@0: // If aRequestor is not null we don't need to check special names, so michael@0: // just hand straight off to the search by actual name function. michael@0: return DoFindItemWithName(aName, aRequestor, aOriginalRequestor, michael@0: _retval); michael@0: } else { michael@0: michael@0: // This is the entry point into the target-finding algorithm. Check michael@0: // for special names. This should only be done once, hence the check michael@0: // for a null aRequestor. michael@0: michael@0: nsCOMPtr foundItem; michael@0: nsDependentString name(aName); michael@0: if (name.LowerCaseEqualsLiteral("_self")) { michael@0: foundItem = this; michael@0: } michael@0: else if (name.LowerCaseEqualsLiteral("_blank")) michael@0: { michael@0: // Just return null. Caller must handle creating a new window with michael@0: // a blank name himself. michael@0: return NS_OK; michael@0: } michael@0: else if (name.LowerCaseEqualsLiteral("_parent")) michael@0: { michael@0: GetSameTypeParent(getter_AddRefs(foundItem)); michael@0: if(!foundItem) michael@0: foundItem = this; michael@0: } michael@0: else if (name.LowerCaseEqualsLiteral("_top")) michael@0: { michael@0: GetSameTypeRootTreeItem(getter_AddRefs(foundItem)); michael@0: NS_ASSERTION(foundItem, "Must have this; worst case it's us!"); michael@0: } michael@0: // _main is an IE target which should be case-insensitive but isn't michael@0: // see bug 217886 for details michael@0: else if (name.LowerCaseEqualsLiteral("_content") || michael@0: name.EqualsLiteral("_main")) michael@0: { michael@0: // Must pass our same type root as requestor to the michael@0: // treeowner to make sure things work right. michael@0: nsCOMPtr root; michael@0: GetSameTypeRootTreeItem(getter_AddRefs(root)); michael@0: if (mTreeOwner) { michael@0: NS_ASSERTION(root, "Must have this; worst case it's us!"); michael@0: mTreeOwner->FindItemWithName(aName, root, aOriginalRequestor, michael@0: getter_AddRefs(foundItem)); michael@0: } michael@0: #ifdef DEBUG michael@0: else { michael@0: NS_ERROR("Someone isn't setting up the tree owner. " michael@0: "You might like to try that. " michael@0: "Things will.....you know, work."); michael@0: // Note: _content should always exist. If we don't have one michael@0: // hanging off the treeowner, just create a named window.... michael@0: // so don't return here, in case we did that and can now find michael@0: // it. michael@0: // XXXbz should we be using |root| instead of creating michael@0: // a new window? michael@0: } michael@0: #endif michael@0: } else { michael@0: // Do the search for item by an actual name. michael@0: DoFindItemWithName(aName, aRequestor, aOriginalRequestor, michael@0: getter_AddRefs(foundItem)); michael@0: } michael@0: michael@0: if (foundItem && !CanAccessItem(foundItem, aOriginalRequestor)) { michael@0: foundItem = nullptr; michael@0: } michael@0: michael@0: // DoFindItemWithName only returns active items and we don't check if michael@0: // the item is active for the special cases. michael@0: if (foundItem) { michael@0: foundItem.swap(*_retval); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsDocShell::DoFindItemWithName(const char16_t* aName, michael@0: nsISupports* aRequestor, michael@0: nsIDocShellTreeItem* aOriginalRequestor, michael@0: nsIDocShellTreeItem** _retval) michael@0: { michael@0: // First we check our name. michael@0: if (mName.Equals(aName) && ItemIsActive(this) && michael@0: CanAccessItem(this, aOriginalRequestor)) { michael@0: NS_ADDREF(*_retval = this); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // This QI may fail, but the places where we want to compare, comparing michael@0: // against nullptr serves the same purpose. michael@0: nsCOMPtr reqAsTreeItem(do_QueryInterface(aRequestor)); michael@0: michael@0: // Second we check our children making sure not to ask a child if michael@0: // it is the aRequestor. michael@0: #ifdef DEBUG michael@0: nsresult rv = michael@0: #endif michael@0: FindChildWithName(aName, true, true, reqAsTreeItem, michael@0: aOriginalRequestor, _retval); michael@0: NS_ASSERTION(NS_SUCCEEDED(rv), michael@0: "FindChildWithName should not be failing here."); michael@0: if (*_retval) michael@0: return NS_OK; michael@0: michael@0: // Third if we have a parent and it isn't the requestor then we michael@0: // should ask it to do the search. If it is the requestor we michael@0: // should just stop here and let the parent do the rest. If we michael@0: // don't have a parent, then we should ask the michael@0: // docShellTreeOwner to do the search. michael@0: nsCOMPtr parentAsTreeItem = michael@0: do_QueryInterface(GetAsSupports(mParent)); michael@0: if (parentAsTreeItem) { michael@0: if (parentAsTreeItem == reqAsTreeItem) michael@0: return NS_OK; michael@0: michael@0: if (parentAsTreeItem->ItemType() == mItemType) { michael@0: return parentAsTreeItem-> michael@0: FindItemWithName(aName, michael@0: static_cast michael@0: (this), michael@0: aOriginalRequestor, michael@0: _retval); michael@0: } michael@0: } michael@0: michael@0: // If the parent is null or not of the same type fall through and ask tree michael@0: // owner. michael@0: michael@0: // This may fail, but comparing against null serves the same purpose michael@0: nsCOMPtr michael@0: reqAsTreeOwner(do_QueryInterface(aRequestor)); michael@0: michael@0: if (mTreeOwner && mTreeOwner != reqAsTreeOwner) { michael@0: return mTreeOwner-> michael@0: FindItemWithName(aName, this, aOriginalRequestor, _retval); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: nsDocShell::IsSandboxedFrom(nsIDocShell* aTargetDocShell) michael@0: { michael@0: // If no target then not sandboxed. michael@0: if (!aTargetDocShell) { michael@0: return false; michael@0: } michael@0: michael@0: // We cannot be sandboxed from ourselves. michael@0: if (aTargetDocShell == this) { michael@0: return false; michael@0: } michael@0: michael@0: // Default the sandbox flags to our flags, so that if we can't retrieve the michael@0: // active document, we will still enforce our own. michael@0: uint32_t sandboxFlags = mSandboxFlags; michael@0: if (mContentViewer) { michael@0: nsCOMPtr doc = mContentViewer->GetDocument(); michael@0: if (doc) { michael@0: sandboxFlags = doc->GetSandboxFlags(); michael@0: } michael@0: } michael@0: michael@0: // If no flags, we are not sandboxed at all. michael@0: if (!sandboxFlags) { michael@0: return false; michael@0: } michael@0: michael@0: // If aTargetDocShell has an ancestor, it is not top level. michael@0: nsCOMPtr ancestorOfTarget; michael@0: aTargetDocShell->GetSameTypeParent(getter_AddRefs(ancestorOfTarget)); michael@0: if (ancestorOfTarget) { michael@0: do { michael@0: // We are not sandboxed if we are an ancestor of target. michael@0: if (ancestorOfTarget == this) { michael@0: return false; michael@0: } michael@0: nsCOMPtr tempTreeItem; michael@0: ancestorOfTarget->GetSameTypeParent(getter_AddRefs(tempTreeItem)); michael@0: tempTreeItem.swap(ancestorOfTarget); michael@0: } while (ancestorOfTarget); michael@0: michael@0: // Otherwise, we are sandboxed from aTargetDocShell. michael@0: return true; michael@0: } michael@0: michael@0: // aTargetDocShell is top level, are we the "one permitted sandboxed michael@0: // navigator", i.e. did we open aTargetDocShell? michael@0: nsCOMPtr permittedNavigator; michael@0: aTargetDocShell-> michael@0: GetOnePermittedSandboxedNavigator(getter_AddRefs(permittedNavigator)); michael@0: if (permittedNavigator == this) { michael@0: return false; michael@0: } michael@0: michael@0: // If SANDBOXED_TOPLEVEL_NAVIGATION flag is not on, we are not sandboxed michael@0: // from our top. michael@0: if (!(sandboxFlags & SANDBOXED_TOPLEVEL_NAVIGATION)) { michael@0: nsCOMPtr rootTreeItem; michael@0: GetSameTypeRootTreeItem(getter_AddRefs(rootTreeItem)); michael@0: if (SameCOMIdentity(aTargetDocShell, rootTreeItem)) { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: // Otherwise, we are sandboxed from aTargetDocShell. michael@0: return true; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetTreeOwner(nsIDocShellTreeOwner ** aTreeOwner) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aTreeOwner); michael@0: michael@0: *aTreeOwner = mTreeOwner; michael@0: NS_IF_ADDREF(*aTreeOwner); michael@0: return NS_OK; michael@0: } michael@0: michael@0: #ifdef DEBUG_DOCSHELL_FOCUS michael@0: static void michael@0: PrintDocTree(nsIDocShellTreeItem * aParentNode, int aLevel) michael@0: { michael@0: for (int32_t i=0;iGetChildCount(&childWebshellCount); michael@0: nsCOMPtr parentAsDocShell(do_QueryInterface(aParentNode)); michael@0: int32_t type = aParentNode->ItemType(); michael@0: nsCOMPtr presShell = parentAsDocShell->GetPresShell(); michael@0: nsRefPtr presContext; michael@0: parentAsDocShell->GetPresContext(getter_AddRefs(presContext)); michael@0: nsIDocument *doc = presShell->GetDocument(); michael@0: michael@0: nsCOMPtr domwin(doc->GetWindow()); michael@0: michael@0: nsCOMPtr widget; michael@0: nsViewManager* vm = presShell->GetViewManager(); michael@0: if (vm) { michael@0: vm->GetWidget(getter_AddRefs(widget)); michael@0: } michael@0: dom::Element* rootElement = doc->GetRootElement(); michael@0: michael@0: printf("DS %p Ty %s Doc %p DW %p EM %p CN %p\n", michael@0: (void*)parentAsDocShell.get(), michael@0: type==nsIDocShellTreeItem::typeChrome?"Chr":"Con", michael@0: (void*)doc, (void*)domwin.get(), michael@0: (void*)presContext->EventStateManager(), (void*)rootElement); michael@0: michael@0: if (childWebshellCount > 0) { michael@0: for (int32_t i=0;i child; michael@0: aParentNode->GetChildAt(i, getter_AddRefs(child)); michael@0: PrintDocTree(child, aLevel+1); michael@0: } michael@0: } michael@0: } michael@0: michael@0: static void michael@0: PrintDocTree(nsIDocShellTreeItem * aParentNode) michael@0: { michael@0: NS_ASSERTION(aParentNode, "Pointer is null!"); michael@0: michael@0: nsCOMPtr parentItem; michael@0: aParentNode->GetParent(getter_AddRefs(parentItem)); michael@0: while (parentItem) { michael@0: nsCOMPtrtmp; michael@0: parentItem->GetParent(getter_AddRefs(tmp)); michael@0: if (!tmp) { michael@0: break; michael@0: } michael@0: parentItem = tmp; michael@0: } michael@0: michael@0: if (!parentItem) { michael@0: parentItem = aParentNode; michael@0: } michael@0: michael@0: PrintDocTree(parentItem, 0); michael@0: } michael@0: #endif michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::SetTreeOwner(nsIDocShellTreeOwner * aTreeOwner) michael@0: { michael@0: #ifdef DEBUG_DOCSHELL_FOCUS michael@0: nsCOMPtr item(do_QueryInterface(aTreeOwner)); michael@0: if (item) { michael@0: PrintDocTree(item); michael@0: } michael@0: #endif michael@0: michael@0: // Don't automatically set the progress based on the tree owner for frames michael@0: if (!IsFrame()) { michael@0: nsCOMPtr webProgress = michael@0: do_QueryInterface(GetAsSupports(this)); michael@0: michael@0: if (webProgress) { michael@0: nsCOMPtr michael@0: oldListener(do_QueryInterface(mTreeOwner)); michael@0: nsCOMPtr michael@0: newListener(do_QueryInterface(aTreeOwner)); michael@0: michael@0: if (oldListener) { michael@0: webProgress->RemoveProgressListener(oldListener); michael@0: } michael@0: michael@0: if (newListener) { michael@0: webProgress->AddProgressListener(newListener, michael@0: nsIWebProgress::NOTIFY_ALL); michael@0: } michael@0: } michael@0: } michael@0: michael@0: mTreeOwner = aTreeOwner; // Weak reference per API michael@0: michael@0: nsTObserverArray::ForwardIterator iter(mChildList); michael@0: while (iter.HasMore()) { michael@0: nsCOMPtr child = do_QueryObject(iter.GetNext()); michael@0: NS_ENSURE_TRUE(child, NS_ERROR_FAILURE); michael@0: michael@0: if (child->ItemType() == mItemType) michael@0: child->SetTreeOwner(aTreeOwner); michael@0: } michael@0: michael@0: // Our tree owner has changed. Recompute scriptability. michael@0: // michael@0: // Note that this is near-redundant with the recomputation in michael@0: // SetDocLoaderParent(), but not so for the root DocShell, where the call to michael@0: // SetTreeOwner() happens after the initial AddDocLoaderAsChildOfRoot(), michael@0: // and we never set another parent. Given that this is neither expensive nor michael@0: // performance-critical, let's be safe and unconditionally recompute this michael@0: // state whenever dependent state changes. michael@0: RecomputeCanExecuteScripts(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::SetChildOffset(uint32_t aChildOffset) michael@0: { michael@0: mChildOffset = aChildOffset; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetHistoryID(uint64_t* aID) michael@0: { michael@0: *aID = mHistoryID; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetIsInUnload(bool* aIsInUnload) michael@0: { michael@0: *aIsInUnload = mFiredUnloadEvent; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetChildCount(int32_t * aChildCount) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aChildCount); michael@0: *aChildCount = mChildList.Length(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::AddChild(nsIDocShellTreeItem * aChild) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aChild); michael@0: michael@0: nsRefPtr childAsDocLoader = GetAsDocLoader(aChild); michael@0: NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED); michael@0: michael@0: // Make sure we're not creating a loop in the docshell tree michael@0: nsDocLoader* ancestor = this; michael@0: do { michael@0: if (childAsDocLoader == ancestor) { michael@0: return NS_ERROR_ILLEGAL_VALUE; michael@0: } michael@0: ancestor = ancestor->GetParent(); michael@0: } while (ancestor); michael@0: michael@0: // Make sure to remove the child from its current parent. michael@0: nsDocLoader* childsParent = childAsDocLoader->GetParent(); michael@0: if (childsParent) { michael@0: childsParent->RemoveChildLoader(childAsDocLoader); michael@0: } michael@0: michael@0: // Make sure to clear the treeowner in case this child is a different type michael@0: // from us. michael@0: aChild->SetTreeOwner(nullptr); michael@0: michael@0: nsresult res = AddChildLoader(childAsDocLoader); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: NS_ASSERTION(!mChildList.IsEmpty(), michael@0: "child list must not be empty after a successful add"); michael@0: michael@0: nsCOMPtr childDocShell = do_QueryInterface(aChild); michael@0: bool dynamic = false; michael@0: childDocShell->GetCreatedDynamically(&dynamic); michael@0: if (!dynamic) { michael@0: nsCOMPtr currentSH; michael@0: bool oshe = false; michael@0: GetCurrentSHEntry(getter_AddRefs(currentSH), &oshe); michael@0: if (currentSH) { michael@0: currentSH->HasDynamicallyAddedChild(&dynamic); michael@0: } michael@0: } michael@0: childDocShell->SetChildOffset(dynamic ? -1 : mChildList.Length() - 1); michael@0: michael@0: /* Set the child's global history if the parent has one */ michael@0: if (mUseGlobalHistory) { michael@0: childDocShell->SetUseGlobalHistory(true); michael@0: } michael@0: michael@0: if (aChild->ItemType() != mItemType) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: aChild->SetTreeOwner(mTreeOwner); michael@0: michael@0: nsCOMPtr childAsDocShell(do_QueryInterface(aChild)); michael@0: if (!childAsDocShell) michael@0: return NS_OK; michael@0: michael@0: // charset, style-disabling, and zoom will be inherited in SetupNewViewer() michael@0: michael@0: // Now take this document's charset and set the child's parentCharset field michael@0: // to it. We'll later use that field, in the loading process, for the michael@0: // charset choosing algorithm. michael@0: // If we fail, at any point, we just return NS_OK. michael@0: // This code has some performance impact. But this will be reduced when michael@0: // the current charset will finally be stored as an Atom, avoiding the michael@0: // alias resolution extra look-up. michael@0: michael@0: // we are NOT going to propagate the charset is this Chrome's docshell michael@0: if (mItemType == nsIDocShellTreeItem::typeChrome) michael@0: return NS_OK; michael@0: michael@0: // get the parent's current charset michael@0: if (!mContentViewer) michael@0: return NS_OK; michael@0: nsIDocument* doc = mContentViewer->GetDocument(); michael@0: if (!doc) michael@0: return NS_OK; michael@0: michael@0: bool isWyciwyg = false; michael@0: michael@0: if (mCurrentURI) { michael@0: // Check if the url is wyciwyg michael@0: mCurrentURI->SchemeIs("wyciwyg", &isWyciwyg); michael@0: } michael@0: michael@0: if (!isWyciwyg) { michael@0: // If this docshell is loaded from a wyciwyg: URI, don't michael@0: // advertise our charset since it does not in any way reflect michael@0: // the actual source charset, which is what we're trying to michael@0: // expose here. michael@0: michael@0: const nsACString &parentCS = doc->GetDocumentCharacterSet(); michael@0: int32_t charsetSource = doc->GetDocumentCharacterSetSource(); michael@0: // set the child's parentCharset michael@0: childAsDocShell->SetParentCharset(parentCS, michael@0: charsetSource, michael@0: doc->NodePrincipal()); michael@0: } michael@0: michael@0: // printf("### 1 >>> Adding child. Parent CS = %s. ItemType = %d.\n", NS_LossyConvertUTF16toASCII(parentCS).get(), mItemType); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::RemoveChild(nsIDocShellTreeItem * aChild) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aChild); michael@0: michael@0: nsRefPtr childAsDocLoader = GetAsDocLoader(aChild); michael@0: NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED); michael@0: michael@0: nsresult rv = RemoveChildLoader(childAsDocLoader); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: aChild->SetTreeOwner(nullptr); michael@0: michael@0: return nsDocLoader::AddDocLoaderAsChildOfRoot(childAsDocLoader); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetChildAt(int32_t aIndex, nsIDocShellTreeItem ** aChild) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aChild); michael@0: michael@0: #ifdef DEBUG michael@0: if (aIndex < 0) { michael@0: NS_WARNING("Negative index passed to GetChildAt"); michael@0: } else if (static_cast(aIndex) >= mChildList.Length()) { michael@0: NS_WARNING("Too large an index passed to GetChildAt"); michael@0: } michael@0: #endif michael@0: michael@0: nsIDocumentLoader* child = ChildAt(aIndex); michael@0: NS_ENSURE_TRUE(child, NS_ERROR_UNEXPECTED); michael@0: michael@0: return CallQueryInterface(child, aChild); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::FindChildWithName(const char16_t * aName, michael@0: bool aRecurse, bool aSameType, michael@0: nsIDocShellTreeItem * aRequestor, michael@0: nsIDocShellTreeItem * aOriginalRequestor, michael@0: nsIDocShellTreeItem ** _retval) michael@0: { michael@0: NS_ENSURE_ARG(aName); michael@0: NS_ENSURE_ARG_POINTER(_retval); michael@0: michael@0: *_retval = nullptr; // if we don't find one, we return NS_OK and a null result michael@0: michael@0: if (!*aName) michael@0: return NS_OK; michael@0: michael@0: nsXPIDLString childName; michael@0: nsTObserverArray::ForwardIterator iter(mChildList); michael@0: while (iter.HasMore()) { michael@0: nsCOMPtr child = do_QueryObject(iter.GetNext()); michael@0: NS_ENSURE_TRUE(child, NS_ERROR_FAILURE); michael@0: int32_t childType = child->ItemType(); michael@0: michael@0: if (aSameType && (childType != mItemType)) michael@0: continue; michael@0: michael@0: bool childNameEquals = false; michael@0: child->NameEquals(aName, &childNameEquals); michael@0: if (childNameEquals && ItemIsActive(child) && michael@0: CanAccessItem(child, aOriginalRequestor)) { michael@0: child.swap(*_retval); michael@0: break; michael@0: } michael@0: michael@0: if (childType != mItemType) //Only ask it to check children if it is same type michael@0: continue; michael@0: michael@0: if (aRecurse && (aRequestor != child)) // Only ask the child if it isn't the requestor michael@0: { michael@0: // See if child contains the shell with the given name michael@0: #ifdef DEBUG michael@0: nsresult rv = michael@0: #endif michael@0: child->FindChildWithName(aName, true, michael@0: aSameType, michael@0: static_cast michael@0: (this), michael@0: aOriginalRequestor, michael@0: _retval); michael@0: NS_ASSERTION(NS_SUCCEEDED(rv), michael@0: "FindChildWithName should not fail here"); michael@0: if (*_retval) // found it michael@0: return NS_OK; michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetChildSHEntry(int32_t aChildOffset, nsISHEntry ** aResult) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: michael@0: NS_ENSURE_ARG_POINTER(aResult); michael@0: *aResult = nullptr; michael@0: michael@0: michael@0: // A nsISHEntry for a child is *only* available when the parent is in michael@0: // the progress of loading a document too... michael@0: michael@0: if (mLSHE) { michael@0: /* Before looking for the subframe's url, check michael@0: * the expiration status of the parent. If the parent michael@0: * has expired from cache, then subframes will not be michael@0: * loaded from history in certain situations. michael@0: */ michael@0: bool parentExpired=false; michael@0: mLSHE->GetExpirationStatus(&parentExpired); michael@0: michael@0: /* Get the parent's Load Type so that it can be set on the child too. michael@0: * By default give a loadHistory value michael@0: */ michael@0: uint32_t loadType = nsIDocShellLoadInfo::loadHistory; michael@0: mLSHE->GetLoadType(&loadType); michael@0: // If the user did a shift-reload on this frameset page, michael@0: // we don't want to load the subframes from history. michael@0: if (loadType == nsIDocShellLoadInfo::loadReloadBypassCache || michael@0: loadType == nsIDocShellLoadInfo::loadReloadBypassProxy || michael@0: loadType == nsIDocShellLoadInfo::loadReloadBypassProxyAndCache || michael@0: loadType == nsIDocShellLoadInfo::loadRefresh) michael@0: return rv; michael@0: michael@0: /* If the user pressed reload and the parent frame has expired michael@0: * from cache, we do not want to load the child frame from history. michael@0: */ michael@0: if (parentExpired && (loadType == nsIDocShellLoadInfo::loadReloadNormal)) { michael@0: // The parent has expired. Return null. michael@0: *aResult = nullptr; michael@0: return rv; michael@0: } michael@0: michael@0: nsCOMPtr container(do_QueryInterface(mLSHE)); michael@0: if (container) { michael@0: // Get the child subframe from session history. michael@0: rv = container->GetChildAt(aChildOffset, aResult); michael@0: if (*aResult) michael@0: (*aResult)->SetLoadType(loadType); michael@0: } michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::AddChildSHEntry(nsISHEntry * aCloneRef, nsISHEntry * aNewEntry, michael@0: int32_t aChildOffset, uint32_t loadType, michael@0: bool aCloneChildren) michael@0: { michael@0: nsresult rv; michael@0: michael@0: if (mLSHE && loadType != LOAD_PUSHSTATE) { michael@0: /* You get here if you are currently building a michael@0: * hierarchy ie.,you just visited a frameset page michael@0: */ michael@0: nsCOMPtr container(do_QueryInterface(mLSHE, &rv)); michael@0: if (container) { michael@0: rv = container->AddChild(aNewEntry, aChildOffset); michael@0: } michael@0: } michael@0: else if (!aCloneRef) { michael@0: /* This is an initial load in some subframe. Just append it if we can */ michael@0: nsCOMPtr container(do_QueryInterface(mOSHE, &rv)); michael@0: if (container) { michael@0: rv = container->AddChild(aNewEntry, aChildOffset); michael@0: } michael@0: } michael@0: else if (mSessionHistory) { michael@0: /* You are currently in the rootDocShell. michael@0: * You will get here when a subframe has a new url michael@0: * to load and you have walked up the tree all the michael@0: * way to the top to clone the current SHEntry hierarchy michael@0: * and replace the subframe where a new url was loaded with michael@0: * a new entry. michael@0: */ michael@0: int32_t index = -1; michael@0: nsCOMPtr currentHE; michael@0: mSessionHistory->GetIndex(&index); michael@0: if (index < 0) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: rv = mSessionHistory->GetEntryAtIndex(index, false, michael@0: getter_AddRefs(currentHE)); michael@0: NS_ENSURE_TRUE(currentHE, NS_ERROR_FAILURE); michael@0: michael@0: nsCOMPtr currentEntry(do_QueryInterface(currentHE)); michael@0: if (currentEntry) { michael@0: uint32_t cloneID = 0; michael@0: nsCOMPtr nextEntry; michael@0: aCloneRef->GetID(&cloneID); michael@0: rv = CloneAndReplace(currentEntry, this, cloneID, aNewEntry, michael@0: aCloneChildren, getter_AddRefs(nextEntry)); michael@0: michael@0: if (NS_SUCCEEDED(rv)) { michael@0: nsCOMPtr michael@0: shPrivate(do_QueryInterface(mSessionHistory)); michael@0: NS_ENSURE_TRUE(shPrivate, NS_ERROR_FAILURE); michael@0: rv = shPrivate->AddEntry(nextEntry, true); michael@0: } michael@0: } michael@0: } michael@0: else { michael@0: /* Just pass this along */ michael@0: nsCOMPtr parent = michael@0: do_QueryInterface(GetAsSupports(mParent), &rv); michael@0: if (parent) { michael@0: rv = parent->AddChildSHEntry(aCloneRef, aNewEntry, aChildOffset, michael@0: loadType, aCloneChildren); michael@0: } michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsDocShell::DoAddChildSHEntry(nsISHEntry* aNewEntry, int32_t aChildOffset, michael@0: bool aCloneChildren) michael@0: { michael@0: /* You will get here when you are in a subframe and michael@0: * a new url has been loaded on you. michael@0: * The mOSHE in this subframe will be the previous url's michael@0: * mOSHE. This mOSHE will be used as the identification michael@0: * for this subframe in the CloneAndReplace function. michael@0: */ michael@0: michael@0: // In this case, we will end up calling AddEntry, which increases the michael@0: // current index by 1 michael@0: nsCOMPtr rootSH; michael@0: GetRootSessionHistory(getter_AddRefs(rootSH)); michael@0: if (rootSH) { michael@0: rootSH->GetIndex(&mPreviousTransIndex); michael@0: } michael@0: michael@0: nsresult rv; michael@0: nsCOMPtr parent = michael@0: do_QueryInterface(GetAsSupports(mParent), &rv); michael@0: if (parent) { michael@0: rv = parent->AddChildSHEntry(mOSHE, aNewEntry, aChildOffset, mLoadType, michael@0: aCloneChildren); michael@0: } michael@0: michael@0: michael@0: if (rootSH) { michael@0: rootSH->GetIndex(&mLoadedTransIndex); michael@0: #ifdef DEBUG_PAGE_CACHE michael@0: printf("Previous index: %d, Loaded index: %d\n\n", mPreviousTransIndex, michael@0: mLoadedTransIndex); michael@0: #endif michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::SetUseGlobalHistory(bool aUseGlobalHistory) michael@0: { michael@0: nsresult rv; michael@0: michael@0: mUseGlobalHistory = aUseGlobalHistory; michael@0: michael@0: if (!aUseGlobalHistory) { michael@0: mGlobalHistory = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // No need to initialize mGlobalHistory if IHistory is available. michael@0: nsCOMPtr history = services::GetHistoryService(); michael@0: if (history) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (mGlobalHistory) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: mGlobalHistory = do_GetService(NS_GLOBALHISTORY2_CONTRACTID, &rv); michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetUseGlobalHistory(bool *aUseGlobalHistory) michael@0: { michael@0: *aUseGlobalHistory = mUseGlobalHistory; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::RemoveFromSessionHistory() michael@0: { michael@0: nsCOMPtr internalHistory; michael@0: nsCOMPtr sessionHistory; michael@0: nsCOMPtr root; michael@0: GetSameTypeRootTreeItem(getter_AddRefs(root)); michael@0: if (root) { michael@0: nsCOMPtr rootAsWebnav = michael@0: do_QueryInterface(root); michael@0: if (rootAsWebnav) { michael@0: rootAsWebnav->GetSessionHistory(getter_AddRefs(sessionHistory)); michael@0: internalHistory = do_QueryInterface(sessionHistory); michael@0: } michael@0: } michael@0: if (!internalHistory) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: int32_t index = 0; michael@0: sessionHistory->GetIndex(&index); michael@0: nsAutoTArray ids; michael@0: ids.AppendElement(mHistoryID); michael@0: internalHistory->RemoveEntries(ids, index); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::SetCreatedDynamically(bool aDynamic) michael@0: { michael@0: mDynamicallyCreated = aDynamic; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetCreatedDynamically(bool* aDynamic) michael@0: { michael@0: *aDynamic = mDynamicallyCreated; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetCurrentSHEntry(nsISHEntry** aEntry, bool* aOSHE) michael@0: { michael@0: *aOSHE = false; michael@0: *aEntry = nullptr; michael@0: if (mLSHE) { michael@0: NS_ADDREF(*aEntry = mLSHE); michael@0: } else if (mOSHE) { michael@0: NS_ADDREF(*aEntry = mOSHE); michael@0: *aOSHE = true; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsIScriptGlobalObject* michael@0: nsDocShell::GetScriptGlobalObject() michael@0: { michael@0: NS_ENSURE_SUCCESS(EnsureScriptEnvironment(), nullptr); michael@0: return mScriptGlobal; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::SetDeviceSizeIsPageSize(bool aValue) michael@0: { michael@0: if (mDeviceSizeIsPageSize != aValue) { michael@0: mDeviceSizeIsPageSize = aValue; michael@0: nsRefPtr presContext; michael@0: GetPresContext(getter_AddRefs(presContext)); michael@0: if (presContext) { michael@0: presContext->MediaFeatureValuesChanged(presContext->eAlwaysRebuildStyle); michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetDeviceSizeIsPageSize(bool* aValue) michael@0: { michael@0: *aValue = mDeviceSizeIsPageSize; michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsDocShell::ClearFrameHistory(nsISHEntry* aEntry) michael@0: { michael@0: nsCOMPtr shcontainer = do_QueryInterface(aEntry); michael@0: nsCOMPtr rootSH; michael@0: GetRootSessionHistory(getter_AddRefs(rootSH)); michael@0: nsCOMPtr history = do_QueryInterface(rootSH); michael@0: if (!history || !shcontainer) { michael@0: return; michael@0: } michael@0: michael@0: int32_t count = 0; michael@0: shcontainer->GetChildCount(&count); michael@0: nsAutoTArray ids; michael@0: for (int32_t i = 0; i < count; ++i) { michael@0: nsCOMPtr child; michael@0: shcontainer->GetChildAt(i, getter_AddRefs(child)); michael@0: if (child) { michael@0: uint64_t id = 0; michael@0: child->GetDocshellID(&id); michael@0: ids.AppendElement(id); michael@0: } michael@0: } michael@0: int32_t index = 0; michael@0: rootSH->GetIndex(&index); michael@0: history->RemoveEntries(ids, index); michael@0: } michael@0: michael@0: //------------------------------------- michael@0: //-- Helper Method for Print discovery michael@0: //------------------------------------- michael@0: bool michael@0: nsDocShell::IsPrintingOrPP(bool aDisplayErrorDialog) michael@0: { michael@0: if (mIsPrintingOrPP && aDisplayErrorDialog) { michael@0: DisplayLoadError(NS_ERROR_DOCUMENT_IS_PRINTMODE, nullptr, nullptr, nullptr); michael@0: } michael@0: michael@0: return mIsPrintingOrPP; michael@0: } michael@0: michael@0: bool michael@0: nsDocShell::IsNavigationAllowed(bool aDisplayPrintErrorDialog) michael@0: { michael@0: bool isAllowed = !IsPrintingOrPP(aDisplayPrintErrorDialog) && !mFiredUnloadEvent; michael@0: if (!isAllowed) { michael@0: return false; michael@0: } michael@0: if (!mContentViewer) { michael@0: return true; michael@0: } michael@0: bool firingBeforeUnload; michael@0: mContentViewer->GetBeforeUnloadFiring(&firingBeforeUnload); michael@0: return !firingBeforeUnload; michael@0: } michael@0: michael@0: //***************************************************************************** michael@0: // nsDocShell::nsIWebNavigation michael@0: //***************************************************************************** michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetCanGoBack(bool * aCanGoBack) michael@0: { michael@0: if (!IsNavigationAllowed(false)) { michael@0: *aCanGoBack = false; michael@0: return NS_OK; // JS may not handle returning of an error code michael@0: } michael@0: nsresult rv; michael@0: nsCOMPtr rootSH; michael@0: rv = GetRootSessionHistory(getter_AddRefs(rootSH)); michael@0: nsCOMPtr webnav(do_QueryInterface(rootSH)); michael@0: NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE); michael@0: rv = webnav->GetCanGoBack(aCanGoBack); michael@0: return rv; michael@0: michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetCanGoForward(bool * aCanGoForward) michael@0: { michael@0: if (!IsNavigationAllowed(false)) { michael@0: *aCanGoForward = false; michael@0: return NS_OK; // JS may not handle returning of an error code michael@0: } michael@0: nsresult rv; michael@0: nsCOMPtr rootSH; michael@0: rv = GetRootSessionHistory(getter_AddRefs(rootSH)); michael@0: nsCOMPtr webnav(do_QueryInterface(rootSH)); michael@0: NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE); michael@0: rv = webnav->GetCanGoForward(aCanGoForward); michael@0: return rv; michael@0: michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GoBack() michael@0: { michael@0: if (!IsNavigationAllowed()) { michael@0: return NS_OK; // JS may not handle returning of an error code michael@0: } michael@0: nsresult rv; michael@0: nsCOMPtr rootSH; michael@0: rv = GetRootSessionHistory(getter_AddRefs(rootSH)); michael@0: nsCOMPtr webnav(do_QueryInterface(rootSH)); michael@0: NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE); michael@0: rv = webnav->GoBack(); michael@0: return rv; michael@0: michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GoForward() michael@0: { michael@0: if (!IsNavigationAllowed()) { michael@0: return NS_OK; // JS may not handle returning of an error code michael@0: } michael@0: nsresult rv; michael@0: nsCOMPtr rootSH; michael@0: rv = GetRootSessionHistory(getter_AddRefs(rootSH)); michael@0: nsCOMPtr webnav(do_QueryInterface(rootSH)); michael@0: NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE); michael@0: rv = webnav->GoForward(); michael@0: return rv; michael@0: michael@0: } michael@0: michael@0: NS_IMETHODIMP nsDocShell::GotoIndex(int32_t aIndex) michael@0: { michael@0: if (!IsNavigationAllowed()) { michael@0: return NS_OK; // JS may not handle returning of an error code michael@0: } michael@0: nsresult rv; michael@0: nsCOMPtr rootSH; michael@0: rv = GetRootSessionHistory(getter_AddRefs(rootSH)); michael@0: nsCOMPtr webnav(do_QueryInterface(rootSH)); michael@0: NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE); michael@0: rv = webnav->GotoIndex(aIndex); michael@0: return rv; michael@0: michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::LoadURI(const char16_t * aURI, michael@0: uint32_t aLoadFlags, michael@0: nsIURI * aReferringURI, michael@0: nsIInputStream * aPostStream, michael@0: nsIInputStream * aHeaderStream) michael@0: { michael@0: return LoadURIWithBase(aURI, aLoadFlags, aReferringURI, aPostStream, michael@0: aHeaderStream, nullptr); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::LoadURIWithBase(const char16_t * aURI, michael@0: uint32_t aLoadFlags, michael@0: nsIURI * aReferringURI, michael@0: nsIInputStream * aPostStream, michael@0: nsIInputStream * aHeaderStream, michael@0: nsIURI * aBaseURI) michael@0: { michael@0: NS_ASSERTION((aLoadFlags & 0xf) == 0, "Unexpected flags"); michael@0: michael@0: if (!IsNavigationAllowed()) { michael@0: return NS_OK; // JS may not handle returning of an error code michael@0: } michael@0: nsCOMPtr uri; michael@0: nsCOMPtr postStream(aPostStream); michael@0: nsresult rv = NS_OK; michael@0: michael@0: // Create a URI from our string; if that succeeds, we want to michael@0: // change aLoadFlags to not include the ALLOW_THIRD_PARTY_FIXUP michael@0: // flag. michael@0: michael@0: NS_ConvertUTF16toUTF8 uriString(aURI); michael@0: // Cleanup the empty spaces that might be on each end. michael@0: uriString.Trim(" "); michael@0: // Eliminate embedded newlines, which single-line text fields now allow: michael@0: uriString.StripChars("\r\n"); michael@0: NS_ENSURE_TRUE(!uriString.IsEmpty(), NS_ERROR_FAILURE); michael@0: michael@0: rv = NS_NewURI(getter_AddRefs(uri), uriString); michael@0: if (uri) { michael@0: aLoadFlags &= ~LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP; michael@0: } michael@0: michael@0: if (sURIFixup) { michael@0: // Call the fixup object. This will clobber the rv from NS_NewURI michael@0: // above, but that's fine with us. Note that we need to do this even michael@0: // if NS_NewURI returned a URI, because fixup handles nested URIs, etc michael@0: // (things like view-source:mozilla.org for example). michael@0: uint32_t fixupFlags = 0; michael@0: if (aLoadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) { michael@0: fixupFlags |= nsIURIFixup::FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP; michael@0: } michael@0: if (aLoadFlags & LOAD_FLAGS_FIXUP_SCHEME_TYPOS) { michael@0: fixupFlags |= nsIURIFixup::FIXUP_FLAG_FIX_SCHEME_TYPOS; michael@0: } michael@0: nsCOMPtr fixupStream; michael@0: rv = sURIFixup->CreateFixupURI(uriString, fixupFlags, michael@0: getter_AddRefs(fixupStream), michael@0: getter_AddRefs(uri)); michael@0: if (fixupStream) { michael@0: // CreateFixupURI only returns a post data stream if it succeeded michael@0: // and changed the URI, in which case we should override the michael@0: // passed-in post data. michael@0: postStream = fixupStream; michael@0: } michael@0: } michael@0: // else no fixup service so just use the URI we created and see michael@0: // what happens michael@0: michael@0: if (NS_ERROR_MALFORMED_URI == rv) { michael@0: DisplayLoadError(rv, uri, aURI, nullptr); michael@0: } michael@0: michael@0: if (NS_FAILED(rv) || !uri) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: PopupControlState popupState; michael@0: if (aLoadFlags & LOAD_FLAGS_ALLOW_POPUPS) { michael@0: popupState = openAllowed; michael@0: aLoadFlags &= ~LOAD_FLAGS_ALLOW_POPUPS; michael@0: } else { michael@0: popupState = openOverridden; michael@0: } michael@0: nsAutoPopupStatePusher statePusher(popupState); michael@0: michael@0: // Don't pass certain flags that aren't needed and end up confusing michael@0: // ConvertLoadTypeToDocShellLoadInfo. We do need to ensure that they are michael@0: // passed to LoadURI though, since it uses them. michael@0: uint32_t extraFlags = (aLoadFlags & EXTRA_LOAD_FLAGS); michael@0: aLoadFlags &= ~EXTRA_LOAD_FLAGS; michael@0: michael@0: nsCOMPtr loadInfo; michael@0: rv = CreateLoadInfo(getter_AddRefs(loadInfo)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: /* michael@0: * If the user "Disables Protection on This Page", we have to make sure to michael@0: * remember the users decision when opening links in child tabs [Bug 906190] michael@0: */ michael@0: uint32_t loadType; michael@0: if (aLoadFlags & LOAD_FLAGS_ALLOW_MIXED_CONTENT) { michael@0: loadType = MAKE_LOAD_TYPE(LOAD_NORMAL_ALLOW_MIXED_CONTENT, aLoadFlags); michael@0: } else { michael@0: loadType = MAKE_LOAD_TYPE(LOAD_NORMAL, aLoadFlags); michael@0: } michael@0: michael@0: loadInfo->SetLoadType(ConvertLoadTypeToDocShellLoadInfo(loadType)); michael@0: loadInfo->SetPostDataStream(postStream); michael@0: loadInfo->SetReferrer(aReferringURI); michael@0: loadInfo->SetHeadersStream(aHeaderStream); michael@0: loadInfo->SetBaseURI(aBaseURI); michael@0: michael@0: rv = LoadURI(uri, loadInfo, extraFlags, true); michael@0: michael@0: // Save URI string in case it's needed later when michael@0: // sending to search engine service in EndPageLoad() michael@0: mOriginalUriString = uriString; michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::DisplayLoadError(nsresult aError, nsIURI *aURI, michael@0: const char16_t *aURL, michael@0: nsIChannel* aFailedChannel) michael@0: { michael@0: // Get prompt and string bundle servcies michael@0: nsCOMPtr prompter; michael@0: nsCOMPtr stringBundle; michael@0: GetPromptAndStringBundle(getter_AddRefs(prompter), michael@0: getter_AddRefs(stringBundle)); michael@0: michael@0: NS_ENSURE_TRUE(stringBundle, NS_ERROR_FAILURE); michael@0: NS_ENSURE_TRUE(prompter, NS_ERROR_FAILURE); michael@0: michael@0: nsAutoString error; michael@0: const uint32_t kMaxFormatStrArgs = 3; michael@0: nsAutoString formatStrs[kMaxFormatStrArgs]; michael@0: uint32_t formatStrCount = 0; michael@0: bool addHostPort = false; michael@0: nsresult rv = NS_OK; michael@0: nsAutoString messageStr; michael@0: nsAutoCString cssClass; michael@0: nsAutoCString errorPage; michael@0: michael@0: errorPage.AssignLiteral("neterror"); michael@0: michael@0: // Turn the error code into a human readable error message. michael@0: if (NS_ERROR_UNKNOWN_PROTOCOL == aError) { michael@0: NS_ENSURE_ARG_POINTER(aURI); michael@0: michael@0: // Extract the schemes into a comma delimited list. michael@0: nsAutoCString scheme; michael@0: aURI->GetScheme(scheme); michael@0: CopyASCIItoUTF16(scheme, formatStrs[0]); michael@0: nsCOMPtr nestedURI = do_QueryInterface(aURI); michael@0: while (nestedURI) { michael@0: nsCOMPtr tempURI; michael@0: nsresult rv2; michael@0: rv2 = nestedURI->GetInnerURI(getter_AddRefs(tempURI)); michael@0: if (NS_SUCCEEDED(rv2) && tempURI) { michael@0: tempURI->GetScheme(scheme); michael@0: formatStrs[0].Append(NS_LITERAL_STRING(", ")); michael@0: AppendASCIItoUTF16(scheme, formatStrs[0]); michael@0: } michael@0: nestedURI = do_QueryInterface(tempURI); michael@0: } michael@0: formatStrCount = 1; michael@0: error.AssignLiteral("unknownProtocolFound"); michael@0: } michael@0: else if (NS_ERROR_FILE_NOT_FOUND == aError) { michael@0: NS_ENSURE_ARG_POINTER(aURI); michael@0: error.AssignLiteral("fileNotFound"); michael@0: } michael@0: else if (NS_ERROR_UNKNOWN_HOST == aError) { michael@0: NS_ENSURE_ARG_POINTER(aURI); michael@0: // Get the host michael@0: nsAutoCString host; michael@0: nsCOMPtr innermostURI = NS_GetInnermostURI(aURI); michael@0: innermostURI->GetHost(host); michael@0: CopyUTF8toUTF16(host, formatStrs[0]); michael@0: formatStrCount = 1; michael@0: error.AssignLiteral("dnsNotFound"); michael@0: } michael@0: else if(NS_ERROR_CONNECTION_REFUSED == aError) { michael@0: NS_ENSURE_ARG_POINTER(aURI); michael@0: addHostPort = true; michael@0: error.AssignLiteral("connectionFailure"); michael@0: } michael@0: else if(NS_ERROR_NET_INTERRUPT == aError) { michael@0: NS_ENSURE_ARG_POINTER(aURI); michael@0: addHostPort = true; michael@0: error.AssignLiteral("netInterrupt"); michael@0: } michael@0: else if (NS_ERROR_NET_TIMEOUT == aError) { michael@0: NS_ENSURE_ARG_POINTER(aURI); michael@0: // Get the host michael@0: nsAutoCString host; michael@0: aURI->GetHost(host); michael@0: CopyUTF8toUTF16(host, formatStrs[0]); michael@0: formatStrCount = 1; michael@0: error.AssignLiteral("netTimeout"); michael@0: } michael@0: else if (NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION == aError) { michael@0: // CSP error michael@0: cssClass.AssignLiteral("neterror"); michael@0: error.AssignLiteral("cspFrameAncestorBlocked"); michael@0: } michael@0: else if (NS_ERROR_GET_MODULE(aError) == NS_ERROR_MODULE_SECURITY) { michael@0: nsCOMPtr nsserr = michael@0: do_GetService(NS_NSS_ERRORS_SERVICE_CONTRACTID); michael@0: michael@0: uint32_t errorClass; michael@0: if (!nsserr || michael@0: NS_FAILED(nsserr->GetErrorClass(aError, &errorClass))) { michael@0: errorClass = nsINSSErrorsService::ERROR_CLASS_SSL_PROTOCOL; michael@0: } michael@0: michael@0: nsCOMPtr securityInfo; michael@0: nsCOMPtr tsi; michael@0: if (aFailedChannel) michael@0: aFailedChannel->GetSecurityInfo(getter_AddRefs(securityInfo)); michael@0: tsi = do_QueryInterface(securityInfo); michael@0: if (tsi) { michael@0: // Usually we should have aFailedChannel and get a detailed message michael@0: tsi->GetErrorMessage(getter_Copies(messageStr)); michael@0: } michael@0: else { michael@0: // No channel, let's obtain the generic error message michael@0: if (nsserr) { michael@0: nsserr->GetErrorMessage(aError, messageStr); michael@0: } michael@0: } michael@0: if (!messageStr.IsEmpty()) { michael@0: if (errorClass == nsINSSErrorsService::ERROR_CLASS_BAD_CERT) { michael@0: error.AssignLiteral("nssBadCert"); michael@0: michael@0: // if this is a Strict-Transport-Security host and the cert michael@0: // is bad, don't allow overrides (STS Spec section 7.3). michael@0: nsCOMPtr sss = michael@0: do_GetService(NS_SSSERVICE_CONTRACTID, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: uint32_t flags = michael@0: mInPrivateBrowsing ? nsISocketProvider::NO_PERMANENT_STORAGE : 0; michael@0: michael@0: bool isStsHost = false; michael@0: rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, michael@0: aURI, flags, &isStsHost); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: uint32_t bucketId; michael@0: if (isStsHost) { michael@0: cssClass.AssignLiteral("badStsCert"); michael@0: //measuring STS separately allows us to measure click through michael@0: //rates easily michael@0: bucketId = nsISecurityUITelemetry::WARNING_BAD_CERT_TOP_STS; michael@0: } else { michael@0: bucketId = nsISecurityUITelemetry::WARNING_BAD_CERT_TOP; michael@0: } michael@0: michael@0: michael@0: if (Preferences::GetBool( michael@0: "browser.xul.error_pages.expert_bad_cert", false)) { michael@0: cssClass.AssignLiteral("expertBadCert"); michael@0: } michael@0: michael@0: // See if an alternate cert error page is registered michael@0: nsAdoptingCString alternateErrorPage = michael@0: Preferences::GetCString( michael@0: "security.alternate_certificate_error_page"); michael@0: if (alternateErrorPage) michael@0: errorPage.Assign(alternateErrorPage); michael@0: michael@0: if (!IsFrame() && errorPage.EqualsIgnoreCase("certerror")) michael@0: mozilla::Telemetry::Accumulate(mozilla::Telemetry::SECURITY_UI, bucketId); michael@0: michael@0: } else { michael@0: error.AssignLiteral("nssFailure2"); michael@0: } michael@0: } michael@0: } else if (NS_ERROR_PHISHING_URI == aError || NS_ERROR_MALWARE_URI == aError) { michael@0: nsAutoCString host; michael@0: aURI->GetHost(host); michael@0: CopyUTF8toUTF16(host, formatStrs[0]); michael@0: formatStrCount = 1; michael@0: michael@0: // Malware and phishing detectors may want to use an alternate error michael@0: // page, but if the pref's not set, we'll fall back on the standard page michael@0: nsAdoptingCString alternateErrorPage = michael@0: Preferences::GetCString("urlclassifier.alternate_error_page"); michael@0: if (alternateErrorPage) michael@0: errorPage.Assign(alternateErrorPage); michael@0: michael@0: uint32_t bucketId; michael@0: if (NS_ERROR_PHISHING_URI == aError) { michael@0: error.AssignLiteral("phishingBlocked"); michael@0: bucketId = IsFrame() ? nsISecurityUITelemetry::WARNING_PHISHING_PAGE_FRAME : michael@0: nsISecurityUITelemetry::WARNING_PHISHING_PAGE_TOP ; michael@0: } else { michael@0: error.AssignLiteral("malwareBlocked"); michael@0: bucketId = IsFrame() ? nsISecurityUITelemetry::WARNING_MALWARE_PAGE_FRAME : michael@0: nsISecurityUITelemetry::WARNING_MALWARE_PAGE_TOP ; michael@0: } michael@0: michael@0: if (errorPage.EqualsIgnoreCase("blocked")) michael@0: mozilla::Telemetry::Accumulate(mozilla::Telemetry::SECURITY_UI, michael@0: bucketId); michael@0: michael@0: cssClass.AssignLiteral("blacklist"); michael@0: } else if (NS_ERROR_CONTENT_CRASHED == aError) { michael@0: errorPage.AssignLiteral("tabcrashed"); michael@0: error.AssignLiteral("tabcrashed"); michael@0: michael@0: nsCOMPtr handler = mChromeEventHandler; michael@0: if (handler) { michael@0: nsCOMPtr element = do_QueryInterface(handler); michael@0: element->GetAttribute(NS_LITERAL_STRING("crashedPageTitle"), messageStr); michael@0: } michael@0: michael@0: // DisplayLoadError requires a non-empty messageStr to proceed and call LoadErrorPage. michael@0: // If the page doesn't have a title, we will use a blank space which will be trimmed michael@0: // and thus treated as empty by the front-end. michael@0: if (messageStr.IsEmpty()) { michael@0: messageStr.Assign(NS_LITERAL_STRING(" ")); michael@0: } michael@0: } michael@0: else { michael@0: // Errors requiring simple formatting michael@0: switch (aError) { michael@0: case NS_ERROR_MALFORMED_URI: michael@0: // URI is malformed michael@0: error.AssignLiteral("malformedURI"); michael@0: break; michael@0: case NS_ERROR_REDIRECT_LOOP: michael@0: // Doc failed to load because the server generated too many redirects michael@0: error.AssignLiteral("redirectLoop"); michael@0: break; michael@0: case NS_ERROR_UNKNOWN_SOCKET_TYPE: michael@0: // Doc failed to load because PSM is not installed michael@0: error.AssignLiteral("unknownSocketType"); michael@0: break; michael@0: case NS_ERROR_NET_RESET: michael@0: // Doc failed to load because the server kept reseting the connection michael@0: // before we could read any data from it michael@0: error.AssignLiteral("netReset"); michael@0: break; michael@0: case NS_ERROR_DOCUMENT_NOT_CACHED: michael@0: // Doc failed to load because the cache does not contain a copy of michael@0: // the document. michael@0: error.AssignLiteral("notCached"); michael@0: break; michael@0: case NS_ERROR_OFFLINE: michael@0: // Doc failed to load because we are offline. michael@0: error.AssignLiteral("netOffline"); michael@0: break; michael@0: case NS_ERROR_DOCUMENT_IS_PRINTMODE: michael@0: // Doc navigation attempted while Printing or Print Preview michael@0: error.AssignLiteral("isprinting"); michael@0: break; michael@0: case NS_ERROR_PORT_ACCESS_NOT_ALLOWED: michael@0: // Port blocked for security reasons michael@0: addHostPort = true; michael@0: error.AssignLiteral("deniedPortAccess"); michael@0: break; michael@0: case NS_ERROR_UNKNOWN_PROXY_HOST: michael@0: // Proxy hostname could not be resolved. michael@0: error.AssignLiteral("proxyResolveFailure"); michael@0: break; michael@0: case NS_ERROR_PROXY_CONNECTION_REFUSED: michael@0: // Proxy connection was refused. michael@0: error.AssignLiteral("proxyConnectFailure"); michael@0: break; michael@0: case NS_ERROR_INVALID_CONTENT_ENCODING: michael@0: // Bad Content Encoding. michael@0: error.AssignLiteral("contentEncodingError"); michael@0: break; michael@0: case NS_ERROR_REMOTE_XUL: michael@0: { michael@0: error.AssignLiteral("remoteXUL"); michael@0: break; michael@0: } michael@0: case NS_ERROR_UNSAFE_CONTENT_TYPE: michael@0: // Channel refused to load from an unrecognized content type. michael@0: error.AssignLiteral("unsafeContentType"); michael@0: break; michael@0: case NS_ERROR_CORRUPTED_CONTENT: michael@0: // Broken Content Detected. e.g. Content-MD5 check failure. michael@0: error.AssignLiteral("corruptedContentError"); michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: } michael@0: michael@0: // Test if the error should be displayed michael@0: if (error.IsEmpty()) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Test if the error needs to be formatted michael@0: if (!messageStr.IsEmpty()) { michael@0: // already obtained message michael@0: } michael@0: else { michael@0: if (addHostPort) { michael@0: // Build up the host:port string. michael@0: nsAutoCString hostport; michael@0: if (aURI) { michael@0: aURI->GetHostPort(hostport); michael@0: } else { michael@0: hostport.AssignLiteral("?"); michael@0: } michael@0: CopyUTF8toUTF16(hostport, formatStrs[formatStrCount++]); michael@0: } michael@0: michael@0: nsAutoCString spec; michael@0: rv = NS_ERROR_NOT_AVAILABLE; michael@0: if (aURI) { michael@0: // displaying "file://" is aesthetically unpleasing and could even be michael@0: // confusing to the user michael@0: bool isFileURI = false; michael@0: rv = aURI->SchemeIs("file", &isFileURI); michael@0: if (NS_SUCCEEDED(rv) && isFileURI) michael@0: aURI->GetPath(spec); michael@0: else michael@0: aURI->GetSpec(spec); michael@0: michael@0: nsAutoCString charset; michael@0: // unescape and convert from origin charset michael@0: aURI->GetOriginCharset(charset); michael@0: nsCOMPtr textToSubURI( michael@0: do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: rv = textToSubURI->UnEscapeURIForUI(charset, spec, formatStrs[formatStrCount]); michael@0: } michael@0: } else { michael@0: spec.AssignLiteral("?"); michael@0: } michael@0: if (NS_FAILED(rv)) michael@0: CopyUTF8toUTF16(spec, formatStrs[formatStrCount]); michael@0: rv = NS_OK; michael@0: ++formatStrCount; michael@0: michael@0: const char16_t *strs[kMaxFormatStrArgs]; michael@0: for (uint32_t i = 0; i < formatStrCount; i++) { michael@0: strs[i] = formatStrs[i].get(); michael@0: } michael@0: nsXPIDLString str; michael@0: rv = stringBundle->FormatStringFromName( michael@0: error.get(), michael@0: strs, formatStrCount, getter_Copies(str)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: messageStr.Assign(str.get()); michael@0: } michael@0: michael@0: // Display the error as a page or an alert prompt michael@0: NS_ENSURE_FALSE(messageStr.IsEmpty(), NS_ERROR_FAILURE); michael@0: michael@0: if (UseErrorPages()) { michael@0: // Display an error page michael@0: LoadErrorPage(aURI, aURL, errorPage.get(), error.get(), michael@0: messageStr.get(), cssClass.get(), aFailedChannel); michael@0: } michael@0: else michael@0: { michael@0: // The prompter reqires that our private window has a document (or it michael@0: // asserts). Satisfy that assertion now since GetDoc will force michael@0: // creation of one if it hasn't already been created. michael@0: if (mScriptGlobal) { michael@0: unused << mScriptGlobal->GetDoc(); michael@0: } michael@0: michael@0: // Display a message box michael@0: prompter->Alert(nullptr, messageStr.get()); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::LoadErrorPage(nsIURI *aURI, const char16_t *aURL, michael@0: const char *aErrorPage, michael@0: const char16_t *aErrorType, michael@0: const char16_t *aDescription, michael@0: const char *aCSSClass, michael@0: nsIChannel* aFailedChannel) michael@0: { michael@0: #if defined(PR_LOGGING) && defined(DEBUG) michael@0: if (PR_LOG_TEST(gDocShellLog, PR_LOG_DEBUG)) { michael@0: nsAutoCString spec; michael@0: aURI->GetSpec(spec); michael@0: michael@0: nsAutoCString chanName; michael@0: if (aFailedChannel) michael@0: aFailedChannel->GetName(chanName); michael@0: else michael@0: chanName.AssignLiteral(""); michael@0: michael@0: PR_LOG(gDocShellLog, PR_LOG_DEBUG, michael@0: ("nsDocShell[%p]::LoadErrorPage(\"%s\", \"%s\", {...}, [%s])\n", this, michael@0: spec.get(), NS_ConvertUTF16toUTF8(aURL).get(), chanName.get())); michael@0: } michael@0: #endif michael@0: mFailedChannel = aFailedChannel; michael@0: mFailedURI = aURI; michael@0: mFailedLoadType = mLoadType; michael@0: michael@0: if (mLSHE) { michael@0: // Abandon mLSHE's BFCache entry and create a new one. This way, if michael@0: // we go back or forward to another SHEntry with the same doc michael@0: // identifier, the error page won't persist. michael@0: mLSHE->AbandonBFCacheEntry(); michael@0: } michael@0: michael@0: nsAutoCString url; michael@0: nsAutoCString charset; michael@0: if (aURI) michael@0: { michael@0: nsresult rv = aURI->GetSpec(url); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = aURI->GetOriginCharset(charset); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: else if (aURL) michael@0: { michael@0: CopyUTF16toUTF8(aURL, url); michael@0: } michael@0: else michael@0: { michael@0: return NS_ERROR_INVALID_POINTER; michael@0: } michael@0: michael@0: // Create a URL to pass all the error information through to the page. michael@0: michael@0: #undef SAFE_ESCAPE michael@0: #define SAFE_ESCAPE(cstring, escArg1, escArg2) \ michael@0: { \ michael@0: char* s = nsEscape(escArg1, escArg2); \ michael@0: if (!s) \ michael@0: return NS_ERROR_OUT_OF_MEMORY; \ michael@0: cstring.Adopt(s); \ michael@0: } michael@0: nsCString escapedUrl, escapedCharset, escapedError, escapedDescription, michael@0: escapedCSSClass; michael@0: SAFE_ESCAPE(escapedUrl, url.get(), url_Path); michael@0: SAFE_ESCAPE(escapedCharset, charset.get(), url_Path); michael@0: SAFE_ESCAPE(escapedError, michael@0: NS_ConvertUTF16toUTF8(aErrorType).get(), url_Path); michael@0: SAFE_ESCAPE(escapedDescription, michael@0: NS_ConvertUTF16toUTF8(aDescription).get(), url_Path); michael@0: if (aCSSClass) { michael@0: SAFE_ESCAPE(escapedCSSClass, aCSSClass, url_Path); michael@0: } michael@0: nsCString errorPageUrl("about:"); michael@0: errorPageUrl.AppendASCII(aErrorPage); michael@0: errorPageUrl.AppendLiteral("?e="); michael@0: michael@0: errorPageUrl.AppendASCII(escapedError.get()); michael@0: errorPageUrl.AppendLiteral("&u="); michael@0: errorPageUrl.AppendASCII(escapedUrl.get()); michael@0: if (!escapedCSSClass.IsEmpty()) { michael@0: errorPageUrl.AppendLiteral("&s="); michael@0: errorPageUrl.AppendASCII(escapedCSSClass.get()); michael@0: } michael@0: errorPageUrl.AppendLiteral("&c="); michael@0: errorPageUrl.AppendASCII(escapedCharset.get()); michael@0: michael@0: nsAutoCString frameType(FrameTypeToString(mFrameType)); michael@0: errorPageUrl.AppendLiteral("&f="); michael@0: errorPageUrl.AppendASCII(frameType.get()); michael@0: michael@0: // Append the manifest URL if the error comes from an app. michael@0: nsString manifestURL; michael@0: nsresult rv = GetAppManifestURL(manifestURL); michael@0: if (manifestURL.Length() > 0) { michael@0: nsCString manifestParam; michael@0: SAFE_ESCAPE(manifestParam, michael@0: NS_ConvertUTF16toUTF8(manifestURL).get(), michael@0: url_Path); michael@0: errorPageUrl.AppendLiteral("&m="); michael@0: errorPageUrl.AppendASCII(manifestParam.get()); michael@0: } michael@0: michael@0: // netError.xhtml's getDescription only handles the "d" parameter at the michael@0: // end of the URL, so append it last. michael@0: errorPageUrl.AppendLiteral("&d="); michael@0: errorPageUrl.AppendASCII(escapedDescription.get()); michael@0: michael@0: nsCOMPtr errorPageURI; michael@0: rv = NS_NewURI(getter_AddRefs(errorPageURI), errorPageUrl); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return InternalLoad(errorPageURI, nullptr, nullptr, michael@0: INTERNAL_LOAD_FLAGS_INHERIT_OWNER, nullptr, nullptr, michael@0: NullString(), nullptr, nullptr, LOAD_ERROR_PAGE, michael@0: nullptr, true, NullString(), this, nullptr, nullptr, michael@0: nullptr); michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::Reload(uint32_t aReloadFlags) michael@0: { michael@0: if (!IsNavigationAllowed()) { michael@0: return NS_OK; // JS may not handle returning of an error code michael@0: } michael@0: nsresult rv; michael@0: NS_ASSERTION(((aReloadFlags & 0xf) == 0), michael@0: "Reload command not updated to use load flags!"); michael@0: NS_ASSERTION((aReloadFlags & EXTRA_LOAD_FLAGS) == 0, michael@0: "Don't pass these flags to Reload"); michael@0: michael@0: uint32_t loadType = MAKE_LOAD_TYPE(LOAD_RELOAD_NORMAL, aReloadFlags); michael@0: NS_ENSURE_TRUE(IsValidLoadType(loadType), NS_ERROR_INVALID_ARG); michael@0: michael@0: // Send notifications to the HistoryListener if any, about the impending reload michael@0: nsCOMPtr rootSH; michael@0: rv = GetRootSessionHistory(getter_AddRefs(rootSH)); michael@0: nsCOMPtr shistInt(do_QueryInterface(rootSH)); michael@0: bool canReload = true; michael@0: if (rootSH) { michael@0: shistInt->NotifyOnHistoryReload(mCurrentURI, aReloadFlags, &canReload); michael@0: } michael@0: michael@0: if (!canReload) michael@0: return NS_OK; michael@0: michael@0: /* If you change this part of code, make sure bug 45297 does not re-occur */ michael@0: if (mOSHE) { michael@0: rv = LoadHistoryEntry(mOSHE, loadType); michael@0: } michael@0: else if (mLSHE) { // In case a reload happened before the current load is done michael@0: rv = LoadHistoryEntry(mLSHE, loadType); michael@0: } michael@0: else { michael@0: nsCOMPtr doc(do_GetInterface(GetAsSupports(this))); michael@0: michael@0: // Do not inherit owner from document michael@0: uint32_t flags = INTERNAL_LOAD_FLAGS_NONE; michael@0: nsAutoString srcdoc; michael@0: nsIPrincipal* principal = nullptr; michael@0: nsAutoString contentTypeHint; michael@0: nsCOMPtr baseURI; michael@0: if (doc) { michael@0: principal = doc->NodePrincipal(); michael@0: doc->GetContentType(contentTypeHint); michael@0: michael@0: if (doc->IsSrcdocDocument()) { michael@0: doc->GetSrcdocData(srcdoc); michael@0: flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC; michael@0: baseURI = doc->GetBaseURI(); michael@0: } michael@0: } michael@0: rv = InternalLoad(mCurrentURI, michael@0: mReferrerURI, michael@0: principal, michael@0: flags, michael@0: nullptr, // No window target michael@0: NS_LossyConvertUTF16toASCII(contentTypeHint).get(), michael@0: NullString(), // No forced download michael@0: nullptr, // No post data michael@0: nullptr, // No headers data michael@0: loadType, // Load type michael@0: nullptr, // No SHEntry michael@0: true, michael@0: srcdoc, // srcdoc argument for iframe michael@0: this, // For reloads we are the source michael@0: baseURI, michael@0: nullptr, // No nsIDocShell michael@0: nullptr); // No nsIRequest michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::Stop(uint32_t aStopFlags) michael@0: { michael@0: // Revoke any pending event related to content viewer restoration michael@0: mRestorePresentationEvent.Revoke(); michael@0: michael@0: if (mLoadType == LOAD_ERROR_PAGE) { michael@0: if (mLSHE) { michael@0: // Since error page loads never unset mLSHE, do so now michael@0: SetHistoryEntry(&mOSHE, mLSHE); michael@0: SetHistoryEntry(&mLSHE, nullptr); michael@0: } michael@0: michael@0: mFailedChannel = nullptr; michael@0: mFailedURI = nullptr; michael@0: } michael@0: michael@0: if (nsIWebNavigation::STOP_CONTENT & aStopFlags) { michael@0: // Stop the document loading michael@0: if (mContentViewer) { michael@0: nsCOMPtr cv = mContentViewer; michael@0: cv->Stop(); michael@0: } michael@0: } michael@0: michael@0: if (nsIWebNavigation::STOP_NETWORK & aStopFlags) { michael@0: // Suspend any timers that were set for this loader. We'll clear michael@0: // them out for good in CreateContentViewer. michael@0: if (mRefreshURIList) { michael@0: SuspendRefreshURIs(); michael@0: mSavedRefreshURIList.swap(mRefreshURIList); michael@0: mRefreshURIList = nullptr; michael@0: } michael@0: michael@0: // XXXbz We could also pass |this| to nsIURILoader::Stop. That will michael@0: // just call Stop() on us as an nsIDocumentLoader... We need fewer michael@0: // redundant apis! michael@0: Stop(); michael@0: } michael@0: michael@0: nsTObserverArray::ForwardIterator iter(mChildList); michael@0: while (iter.HasMore()) { michael@0: nsCOMPtr shellAsNav(do_QueryObject(iter.GetNext())); michael@0: if (shellAsNav) michael@0: shellAsNav->Stop(aStopFlags); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetDocument(nsIDOMDocument ** aDocument) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aDocument); michael@0: NS_ENSURE_SUCCESS(EnsureContentViewer(), NS_ERROR_FAILURE); michael@0: michael@0: return mContentViewer->GetDOMDocument(aDocument); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetCurrentURI(nsIURI ** aURI) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aURI); michael@0: michael@0: if (mCurrentURI) { michael@0: return NS_EnsureSafeToReturn(mCurrentURI, aURI); michael@0: } michael@0: michael@0: *aURI = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetReferringURI(nsIURI ** aURI) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aURI); michael@0: michael@0: *aURI = mReferrerURI; michael@0: NS_IF_ADDREF(*aURI); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::SetSessionHistory(nsISHistory * aSessionHistory) michael@0: { michael@0: michael@0: NS_ENSURE_TRUE(aSessionHistory, NS_ERROR_FAILURE); michael@0: // make sure that we are the root docshell and michael@0: // set a handle to root docshell in SH. michael@0: michael@0: nsCOMPtr root; michael@0: /* Get the root docshell. If *this* is the root docshell michael@0: * then save a handle to *this* in SH. SH needs it to do michael@0: * traversions thro' its entries michael@0: */ michael@0: GetSameTypeRootTreeItem(getter_AddRefs(root)); michael@0: NS_ENSURE_TRUE(root, NS_ERROR_FAILURE); michael@0: if (root.get() == static_cast(this)) { michael@0: mSessionHistory = aSessionHistory; michael@0: nsCOMPtr michael@0: shPrivate(do_QueryInterface(mSessionHistory)); michael@0: NS_ENSURE_TRUE(shPrivate, NS_ERROR_FAILURE); michael@0: shPrivate->SetRootDocShell(this); michael@0: return NS_OK; michael@0: } michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetSessionHistory(nsISHistory ** aSessionHistory) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aSessionHistory); michael@0: *aSessionHistory = mSessionHistory; michael@0: NS_IF_ADDREF(*aSessionHistory); michael@0: return NS_OK; michael@0: } michael@0: michael@0: //***************************************************************************** michael@0: // nsDocShell::nsIWebPageDescriptor michael@0: //***************************************************************************** michael@0: NS_IMETHODIMP michael@0: nsDocShell::LoadPage(nsISupports *aPageDescriptor, uint32_t aDisplayType) michael@0: { michael@0: nsCOMPtr shEntryIn(do_QueryInterface(aPageDescriptor)); michael@0: michael@0: // Currently, the opaque 'page descriptor' is an nsISHEntry... michael@0: if (!shEntryIn) { michael@0: return NS_ERROR_INVALID_POINTER; michael@0: } michael@0: michael@0: // Now clone shEntryIn, since we might end up modifying it later on, and we michael@0: // want a page descriptor to be reusable. michael@0: nsCOMPtr shEntry; michael@0: nsresult rv = shEntryIn->Clone(getter_AddRefs(shEntry)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Give our cloned shEntry a new bfcache entry so this load is independent michael@0: // of all other loads. (This is important, in particular, for bugs 582795 michael@0: // and 585298.) michael@0: rv = shEntry->AbandonBFCacheEntry(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // michael@0: // load the page as view-source michael@0: // michael@0: if (nsIWebPageDescriptor::DISPLAY_AS_SOURCE == aDisplayType) { michael@0: nsCOMPtr oldUri, newUri; michael@0: nsCString spec, newSpec; michael@0: michael@0: // Create a new view-source URI and replace the original. michael@0: rv = shEntry->GetURI(getter_AddRefs(oldUri)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: oldUri->GetSpec(spec); michael@0: newSpec.AppendLiteral("view-source:"); michael@0: newSpec.Append(spec); michael@0: michael@0: rv = NS_NewURI(getter_AddRefs(newUri), newSpec); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: shEntry->SetURI(newUri); michael@0: } michael@0: michael@0: rv = LoadHistoryEntry(shEntry, LOAD_HISTORY); michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetCurrentDescriptor(nsISupports **aPageDescriptor) michael@0: { michael@0: NS_PRECONDITION(aPageDescriptor, "Null out param?"); michael@0: michael@0: *aPageDescriptor = nullptr; michael@0: michael@0: nsISHEntry* src = mOSHE ? mOSHE : mLSHE; michael@0: if (src) { michael@0: nsCOMPtr dest; michael@0: michael@0: nsresult rv = src->Clone(getter_AddRefs(dest)); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: // null out inappropriate cloned attributes... michael@0: dest->SetParent(nullptr); michael@0: dest->SetIsSubFrame(false); michael@0: michael@0: return CallQueryInterface(dest, aPageDescriptor); michael@0: } michael@0: michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: michael@0: //***************************************************************************** michael@0: // nsDocShell::nsIBaseWindow michael@0: //***************************************************************************** michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::InitWindow(nativeWindow parentNativeWindow, michael@0: nsIWidget * parentWidget, int32_t x, int32_t y, michael@0: int32_t cx, int32_t cy) michael@0: { michael@0: SetParentWidget(parentWidget); michael@0: SetPositionAndSize(x, y, cx, cy, false); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::Create() michael@0: { michael@0: if (mCreated) { michael@0: // We've already been created michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome, michael@0: "Unexpected item type in docshell"); michael@0: michael@0: NS_ENSURE_TRUE(Preferences::GetRootBranch(), NS_ERROR_FAILURE); michael@0: mCreated = true; michael@0: michael@0: mAllowSubframes = michael@0: Preferences::GetBool("browser.frames.enabled", mAllowSubframes); michael@0: michael@0: if (gValidateOrigin == 0xffffffff) { michael@0: // Check pref to see if we should prevent frameset spoofing michael@0: gValidateOrigin = michael@0: Preferences::GetBool("browser.frame.validate_origin", true); michael@0: } michael@0: michael@0: // Should we use XUL error pages instead of alerts if possible? michael@0: mUseErrorPages = michael@0: Preferences::GetBool("browser.xul.error_pages.enabled", mUseErrorPages); michael@0: michael@0: if(!gAddedPreferencesVarCache) { michael@0: Preferences::AddBoolVarCache(&sUseErrorPages, michael@0: "browser.xul.error_pages.enabled", michael@0: mUseErrorPages); michael@0: gAddedPreferencesVarCache = true; michael@0: } michael@0: michael@0: mDeviceSizeIsPageSize = michael@0: Preferences::GetBool("docshell.device_size_is_page_size", michael@0: mDeviceSizeIsPageSize); michael@0: michael@0: nsCOMPtr serv = services::GetObserverService(); michael@0: if (serv) { michael@0: const char* msg = mItemType == typeContent ? michael@0: NS_WEBNAVIGATION_CREATE : NS_CHROME_WEBNAVIGATION_CREATE; michael@0: serv->NotifyObservers(GetAsSupports(this), msg, nullptr); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::Destroy() michael@0: { michael@0: NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome, michael@0: "Unexpected item type in docshell"); michael@0: michael@0: if (!mIsBeingDestroyed) { michael@0: nsCOMPtr serv = services::GetObserverService(); michael@0: if (serv) { michael@0: const char* msg = mItemType == typeContent ? michael@0: NS_WEBNAVIGATION_DESTROY : NS_CHROME_WEBNAVIGATION_DESTROY; michael@0: serv->NotifyObservers(GetAsSupports(this), msg, nullptr); michael@0: } michael@0: } michael@0: michael@0: mIsBeingDestroyed = true; michael@0: michael@0: // Remove our pref observers michael@0: if (mObserveErrorPages) { michael@0: mObserveErrorPages = false; michael@0: } michael@0: michael@0: // Make sure to blow away our mLoadingURI just in case. No loads michael@0: // from inside this pagehide. michael@0: mLoadingURI = nullptr; michael@0: michael@0: // Fire unload event before we blow anything away. michael@0: (void) FirePageHideNotification(true); michael@0: michael@0: // Clear pointers to any detached nsEditorData that's lying michael@0: // around in shistory entries. Breaks cycle. See bug 430921. michael@0: if (mOSHE) michael@0: mOSHE->SetEditorData(nullptr); michael@0: if (mLSHE) michael@0: mLSHE->SetEditorData(nullptr); michael@0: michael@0: // Note: mContentListener can be null if Init() failed and we're being michael@0: // called from the destructor. michael@0: if (mContentListener) { michael@0: mContentListener->DropDocShellreference(); michael@0: mContentListener->SetParentContentListener(nullptr); michael@0: // Note that we do NOT set mContentListener to null here; that michael@0: // way if someone tries to do a load in us after this point michael@0: // the nsDSURIContentListener will block it. All of which michael@0: // means that we should do this before calling Stop(), of michael@0: // course. michael@0: } michael@0: michael@0: // Stop any URLs that are currently being loaded... michael@0: Stop(nsIWebNavigation::STOP_ALL); michael@0: michael@0: mEditorData = nullptr; michael@0: michael@0: mTransferableHookData = nullptr; michael@0: michael@0: // Save the state of the current document, before destroying the window. michael@0: // This is needed to capture the state of a frameset when the new document michael@0: // causes the frameset to be destroyed... michael@0: PersistLayoutHistoryState(); michael@0: michael@0: // Remove this docshell from its parent's child list michael@0: nsCOMPtr docShellParentAsItem = michael@0: do_QueryInterface(GetAsSupports(mParent)); michael@0: if (docShellParentAsItem) michael@0: docShellParentAsItem->RemoveChild(this); michael@0: michael@0: if (mContentViewer) { michael@0: mContentViewer->Close(nullptr); michael@0: mContentViewer->Destroy(); michael@0: mContentViewer = nullptr; michael@0: } michael@0: michael@0: nsDocLoader::Destroy(); michael@0: michael@0: mParentWidget = nullptr; michael@0: mCurrentURI = nullptr; michael@0: michael@0: if (mScriptGlobal) { michael@0: mScriptGlobal->DetachFromDocShell(); michael@0: mScriptGlobal = nullptr; michael@0: } michael@0: michael@0: if (mSessionHistory) { michael@0: // We want to destroy these content viewers now rather than michael@0: // letting their destruction wait for the session history michael@0: // entries to get garbage collected. (Bug 488394) michael@0: nsCOMPtr shPrivate = michael@0: do_QueryInterface(mSessionHistory); michael@0: if (shPrivate) { michael@0: shPrivate->EvictAllContentViewers(); michael@0: } michael@0: mSessionHistory = nullptr; michael@0: } michael@0: michael@0: SetTreeOwner(nullptr); michael@0: michael@0: mOnePermittedSandboxedNavigator = nullptr; michael@0: michael@0: // required to break ref cycle michael@0: mSecurityUI = nullptr; michael@0: michael@0: // Cancel any timers that were set for this docshell; this is needed michael@0: // to break the cycle between us and the timers. michael@0: CancelRefreshURITimers(); michael@0: michael@0: if (mInPrivateBrowsing) { michael@0: mInPrivateBrowsing = false; michael@0: if (mAffectPrivateSessionLifetime) { michael@0: DecreasePrivateDocShellCount(); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetUnscaledDevicePixelsPerCSSPixel(double *aScale) michael@0: { michael@0: if (mParentWidget) { michael@0: *aScale = mParentWidget->GetDefaultScale().scale; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsCOMPtr ownerWindow(do_QueryInterface(mTreeOwner)); michael@0: if (ownerWindow) { michael@0: return ownerWindow->GetUnscaledDevicePixelsPerCSSPixel(aScale); michael@0: } michael@0: michael@0: *aScale = 1.0; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::SetPosition(int32_t x, int32_t y) michael@0: { michael@0: mBounds.x = x; michael@0: mBounds.y = y; michael@0: michael@0: if (mContentViewer) michael@0: NS_ENSURE_SUCCESS(mContentViewer->Move(x, y), NS_ERROR_FAILURE); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetPosition(int32_t * aX, int32_t * aY) michael@0: { michael@0: int32_t dummyHolder; michael@0: return GetPositionAndSize(aX, aY, &dummyHolder, &dummyHolder); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::SetSize(int32_t aCX, int32_t aCY, bool aRepaint) michael@0: { michael@0: int32_t x = 0, y = 0; michael@0: GetPosition(&x, &y); michael@0: return SetPositionAndSize(x, y, aCX, aCY, aRepaint); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetSize(int32_t * aCX, int32_t * aCY) michael@0: { michael@0: int32_t dummyHolder; michael@0: return GetPositionAndSize(&dummyHolder, &dummyHolder, aCX, aCY); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::SetPositionAndSize(int32_t x, int32_t y, int32_t cx, michael@0: int32_t cy, bool fRepaint) michael@0: { michael@0: mBounds.x = x; michael@0: mBounds.y = y; michael@0: mBounds.width = cx; michael@0: mBounds.height = cy; michael@0: michael@0: // Hold strong ref, since SetBounds can make us null out mContentViewer michael@0: nsCOMPtr viewer = mContentViewer; michael@0: if (viewer) { michael@0: //XXX Border figured in here or is that handled elsewhere? michael@0: NS_ENSURE_SUCCESS(viewer->SetBounds(mBounds), NS_ERROR_FAILURE); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetPositionAndSize(int32_t * x, int32_t * y, int32_t * cx, michael@0: int32_t * cy) michael@0: { michael@0: if (mParentWidget) { michael@0: // ensure size is up-to-date if window has changed resolution michael@0: nsIntRect r; michael@0: mParentWidget->GetClientBounds(r); michael@0: SetPositionAndSize(mBounds.x, mBounds.y, r.width, r.height, false); michael@0: } michael@0: michael@0: // We should really consider just getting this information from michael@0: // our window instead of duplicating the storage and code... michael@0: if (cx || cy) { michael@0: // Caller wants to know our size; make sure to give them up to michael@0: // date information. michael@0: nsCOMPtr doc(do_GetInterface(GetAsSupports(mParent))); michael@0: if (doc) { michael@0: doc->FlushPendingNotifications(Flush_Layout); michael@0: } michael@0: } michael@0: michael@0: DoGetPositionAndSize(x, y, cx, cy); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsDocShell::DoGetPositionAndSize(int32_t * x, int32_t * y, int32_t * cx, michael@0: int32_t * cy) michael@0: { michael@0: if (x) michael@0: *x = mBounds.x; michael@0: if (y) michael@0: *y = mBounds.y; michael@0: if (cx) michael@0: *cx = mBounds.width; michael@0: if (cy) michael@0: *cy = mBounds.height; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::Repaint(bool aForce) michael@0: { michael@0: nsCOMPtr presShell =GetPresShell(); michael@0: NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE); michael@0: michael@0: nsViewManager* viewManager = presShell->GetViewManager(); michael@0: NS_ENSURE_TRUE(viewManager, NS_ERROR_FAILURE); michael@0: michael@0: viewManager->InvalidateAllViews(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetParentWidget(nsIWidget ** parentWidget) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(parentWidget); michael@0: michael@0: *parentWidget = mParentWidget; michael@0: NS_IF_ADDREF(*parentWidget); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::SetParentWidget(nsIWidget * aParentWidget) michael@0: { michael@0: mParentWidget = aParentWidget; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetParentNativeWindow(nativeWindow * parentNativeWindow) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(parentNativeWindow); michael@0: michael@0: if (mParentWidget) michael@0: *parentNativeWindow = mParentWidget->GetNativeData(NS_NATIVE_WIDGET); michael@0: else michael@0: *parentNativeWindow = nullptr; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::SetParentNativeWindow(nativeWindow parentNativeWindow) michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetNativeHandle(nsAString& aNativeHandle) michael@0: { michael@0: // the nativeHandle should be accessed from nsIXULWindow michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetVisibility(bool * aVisibility) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aVisibility); michael@0: michael@0: *aVisibility = false; michael@0: michael@0: if (!mContentViewer) michael@0: return NS_OK; michael@0: michael@0: nsCOMPtr presShell = GetPresShell(); michael@0: if (!presShell) michael@0: return NS_OK; michael@0: michael@0: // get the view manager michael@0: nsViewManager* vm = presShell->GetViewManager(); michael@0: NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE); michael@0: michael@0: // get the root view michael@0: nsView *view = vm->GetRootView(); // views are not ref counted michael@0: NS_ENSURE_TRUE(view, NS_ERROR_FAILURE); michael@0: michael@0: // if our root view is hidden, we are not visible michael@0: if (view->GetVisibility() == nsViewVisibility_kHide) michael@0: return NS_OK; michael@0: michael@0: // otherwise, we must walk up the document and view trees checking michael@0: // for a hidden view, unless we're an off screen browser, which michael@0: // would make this test meaningless. michael@0: michael@0: nsCOMPtr treeItem = this; michael@0: nsCOMPtr parentItem; michael@0: treeItem->GetParent(getter_AddRefs(parentItem)); michael@0: while (parentItem) { michael@0: nsCOMPtr docShell(do_QueryInterface(treeItem)); michael@0: presShell = docShell->GetPresShell(); michael@0: michael@0: nsCOMPtr parentDS = do_QueryInterface(parentItem); michael@0: nsCOMPtr pPresShell = parentDS->GetPresShell(); michael@0: michael@0: // Null-check for crash in bug 267804 michael@0: if (!pPresShell) { michael@0: NS_NOTREACHED("parent docshell has null pres shell"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsIContent *shellContent = michael@0: pPresShell->GetDocument()->FindContentForSubDocument(presShell->GetDocument()); michael@0: NS_ASSERTION(shellContent, "subshell not in the map"); michael@0: michael@0: nsIFrame* frame = shellContent ? shellContent->GetPrimaryFrame() : nullptr; michael@0: bool isDocShellOffScreen = false; michael@0: docShell->GetIsOffScreenBrowser(&isDocShellOffScreen); michael@0: if (frame && michael@0: !frame->IsVisibleConsideringAncestors(nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY) && michael@0: !isDocShellOffScreen) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: treeItem = parentItem; michael@0: treeItem->GetParent(getter_AddRefs(parentItem)); michael@0: } michael@0: michael@0: nsCOMPtr treeOwnerAsWin(do_QueryInterface(mTreeOwner)); michael@0: if (!treeOwnerAsWin) { michael@0: *aVisibility = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Check with the tree owner as well to give embedders a chance to michael@0: // expose visibility as well. michael@0: return treeOwnerAsWin->GetVisibility(aVisibility); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::SetIsOffScreenBrowser(bool aIsOffScreen) michael@0: { michael@0: mIsOffScreenBrowser = aIsOffScreen; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::GetIsOffScreenBrowser(bool *aIsOffScreen) michael@0: { michael@0: *aIsOffScreen = mIsOffScreenBrowser; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDocShell::SetIsActive(bool aIsActive) michael@0: { michael@0: // We disallow setting active on chrome docshells. michael@0: if (mItemType == nsIDocShellTreeItem::typeChrome) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: // Keep track ourselves. michael@0: mIsActive = aIsActive; michael@0: michael@0: // Tell the PresShell about it. michael@0: nsCOMPtr pshell = GetPresShell(); michael@0: if (pshell) michael@0: pshell->SetIsActive(aIsActive); michael@0: michael@0: // Tell the window about it michael@0: if (mScriptGlobal) { michael@0: mScriptGlobal->SetIsBackground(!aIsActive); michael@0: if (nsCOMPtr doc = mScriptGlobal->GetExtantDoc()) { michael@0: doc->PostVisibilityUpdateEvent(); michael@0: } michael@0: } michael@0: michael@0: // Recursively tell all of our children, but don't tell