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