/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: ft=cpp tw=78 sw=4 et ts=4 sts=4 cin * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is the Mozilla browser. * * The Initial Developer of the Original Code is * Netscape Communications, Inc. * Portions created by the Initial Developer are Copyright (C) 1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Travis Bogard * Pierre Phaneuf * Peter Annema * Dan Rosen * Mats Palmgren * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #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 "nsIDocument.h" #include "nsIDOMDocument.h" #include "nsIDOMNSDocument.h" #include "nsIDOMElement.h" #include "nsIDOMStorage.h" #include "nsPIDOMStorage.h" #include "nsIDocumentViewer.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 "nsIMarkupDocumentViewer.h" #include "nsXPIDLString.h" #include "nsReadableUtils.h" #include "nsIDOMEventTarget.h" #include "nsIDOMChromeWindow.h" #include "nsIDOMWindowInternal.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 "nsIUploadChannel.h" #include "nsISecurityEventSink.h" #include "nsIScriptSecurityManager.h" #include "nsIJSContextStack.h" #include "nsIScriptObjectPrincipal.h" #include "nsDocumentCharsetInfoCID.h" #include "nsICanvasFrame.h" #include "nsIScrollableFrame.h" #include "nsContentPolicyUtils.h" // NS_CheckContentLoadPolicy(...) #include "nsICategoryManager.h" #include "nsXPCOMCID.h" #include "nsISeekableStream.h" #include "nsAutoPtr.h" #include "nsIPrefService.h" #include "nsIPrefBranch.h" #include "nsIPrefBranch2.h" #include "nsIWritablePropertyBag2.h" #include "nsIAppShell.h" #include "nsWidgetsCID.h" #include "nsDOMJSUtils.h" #include "nsIInterfaceRequestorUtils.h" #include "nsIView.h" #include "nsIViewManager.h" #include "nsIScrollableView.h" #include "nsIScriptChannel.h" #include "nsIURIClassifier.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 "nsDOMError.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 "nsIApplicationCacheContainer.h" #include "nsIPermissionManager.h" // Editor-related #include "nsIEditingSession.h" #include "nsPIDOMWindow.h" #include "nsIDOMDocument.h" #include "nsICachingChannel.h" #include "nsICacheVisitor.h" #include "nsICacheEntryDescriptor.h" #include "nsIMultiPartChannel.h" #include "nsIWyciwygChannel.h" // The following are for bug #13871: Prevent frameset spoofing #include "nsIHTMLDocument.h" // For reporting errors with the console service. // These can go away if error reporting is propagated up past nsDocShell. #include "nsIConsoleService.h" #include "nsIScriptError.h" // used to dispatch urls to default protocol handlers #include "nsCExternalHandlerService.h" #include "nsIExternalProtocolService.h" #include "nsIFocusController.h" #include "nsITextToSubURI.h" #include "nsIJARChannel.h" #include "prlog.h" #include "prmem.h" #include "nsISelectionDisplay.h" #include "nsIGlobalHistory2.h" #include "nsIGlobalHistory3.h" #ifdef DEBUG_DOCSHELL_FOCUS #include "nsIEventStateManager.h" #endif #include "nsIFrame.h" // for embedding #include "nsIWebBrowserChromeFocus.h" #include "nsPluginError.h" 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 #include "nsContentErrors.h" #include "nsIFocusEventSuppressor.h" // Number of documents currently loading static PRInt32 gNumberOfDocumentsLoading = 0; // Global count of existing docshells. static PRInt32 gDocShellCount = 0; // Global reference to the URI fixup service. nsIURIFixup *nsDocShell::sURIFixup = 0; // True means we validate window targets to prevent frameset // spoofing. Initialize this to a non-bolean value so we know to check // the pref on the creation of the first docshell. static PRBool gValidateOrigin = (PRBool)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 // This is needed for displaying an error message // when navigation is attempted on a document when printing // The value arbitrary as long as it doesn't conflict with // any of the other values in the errors in DisplayLoadError #define NS_ERROR_DOCUMENT_IS_PRINTMODE NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_GENERAL,2001) #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(PRBool perfOverStarvation, PRUint32 starvationDelay) { nsCOMPtr appShell = do_GetService(kAppShellCID); if (appShell) appShell->FavorPerformanceHint(perfOverStarvation, starvationDelay); } //***************************************************************************** //*** nsDocShellFocusController //***************************************************************************** class nsDocShellFocusController { public: static nsDocShellFocusController* GetInstance() { return &mDocShellFocusControllerSingleton; } virtual ~nsDocShellFocusController(){} void Focus(nsIDocShell* aDS); void ClosingDown(nsIDocShell* aDS); protected: nsDocShellFocusController(){} nsIDocShell* mFocusedDocShell; // very weak reference private: static nsDocShellFocusController mDocShellFocusControllerSingleton; }; nsDocShellFocusController nsDocShellFocusController::mDocShellFocusControllerSingleton; //***************************************************************************** //*** nsDocShell: Object Management //***************************************************************************** nsDocShell::nsDocShell(): nsDocLoader(), mAllowSubframes(PR_TRUE), mAllowPlugins(PR_TRUE), mAllowJavascript(PR_TRUE), mAllowMetaRedirects(PR_TRUE), mAllowImages(PR_TRUE), mFocusDocFirst(PR_FALSE), mHasFocus(PR_FALSE), mCreatingDocument(PR_FALSE), mUseErrorPages(PR_FALSE), mObserveErrorPages(PR_TRUE), mAllowAuth(PR_TRUE), mAllowKeywordFixup(PR_FALSE), mFiredUnloadEvent(PR_FALSE), mEODForCurrentDocument(PR_FALSE), mURIResultedInDocument(PR_FALSE), mIsBeingDestroyed(PR_FALSE), mIsExecutingOnLoadHandler(PR_FALSE), mIsPrintingOrPP(PR_FALSE), mIsOffScreenBrowser(PR_FALSE), mSavingOldViewer(PR_FALSE), mAppType(nsIDocShell::APP_TYPE_UNKNOWN), mChildOffset(0), mBusyFlags(BUSY_FLAGS_NONE), mMarginWidth(0), mMarginHeight(0), mItemType(typeContent), mDefaultScrollbarPref(Scrollbar_Auto, Scrollbar_Auto), mPreviousTransIndex(-1), mLoadedTransIndex(-1), mTreeOwner(nsnull), mChromeEventHandler(nsnull) #ifdef DEBUG , mInEnsureScriptEnv(PR_FALSE) #endif { if (gDocShellCount++ == 0) { NS_ASSERTION(sURIFixup == nsnull, "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 (nsnull == gDocShellLeakLog) gDocShellLeakLog = PR_NewLogModule("nsDocShellLeak"); if (gDocShellLeakLog) PR_LOG(gDocShellLeakLog, PR_LOG_DEBUG, ("DOCSHELL %p created\n", this)); #endif } nsDocShell::~nsDocShell() { nsDocShellFocusController* dsfc = nsDocShellFocusController::GetInstance(); if (dsfc) { dsfc->ClosingDown(this); } Destroy(); if (--gDocShellCount == 0) { NS_IF_RELEASE(sURIFixup); } #ifdef PR_LOGGING if (gDocShellLeakLog) PR_LOG(gDocShellLeakLog, PR_LOG_DEBUG, ("DOCSHELL %p destroyed\n", this)); #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); if (!mStorages.Init()) return NS_ERROR_OUT_OF_MEMORY; // 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; PRInt32 n = mChildList.Count(); for (PRInt32 i = 0; i < n; i++) { shell = do_QueryInterface(ChildAt(i)); NS_ASSERTION(shell, "docshell has null child"); if (shell) { shell->SetTreeOwner(nsnull); } } 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_END_INHERITING(nsDocLoader) ///***************************************************************************** // nsDocShell::nsIInterfaceRequestor //***************************************************************************** NS_IMETHODIMP nsDocShell::GetInterface(const nsIID & aIID, void **aSink) { NS_PRECONDITION(aSink, "null out param"); *aSink = nsnull; 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(nsIDOMWindowInternal)) || aIID.Equals(NS_GET_IID(nsPIDOMWindow)) || aIID.Equals(NS_GET_IID(nsIDOMWindow))) && 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(nsIApplicationCacheContainer))) { *aSink = nsnull; // Return the toplevel document as an // nsIApplicationCacheContainer. nsCOMPtr rootItem; GetSameTypeRootTreeItem(getter_AddRefs(rootItem)); nsCOMPtr rootDocShell = do_QueryInterface(rootItem); if (!rootDocShell) return NS_ERROR_NO_INTERFACE; nsCOMPtr contentViewer; rootDocShell->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; 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))) { nsCOMPtr shell; nsresult rv = GetPresShell(getter_AddRefs(shell)); if (NS_SUCCEEDED(rv) && 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 { return nsDocLoader::GetInterface(aIID, aSink); } NS_IF_ADDREF(((nsISupports *) * aSink)); return *aSink ? NS_OK : NS_NOINTERFACE; } PRUint32 nsDocShell:: ConvertDocShellLoadInfoToLoadType(nsDocShellInfoLoadType aDocShellLoadType) { PRUint32 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; default: NS_NOTREACHED("Unexpected nsDocShellInfoLoadType value"); } return loadType; } nsDocShellInfoLoadType nsDocShell::ConvertLoadTypeToDocShellLoadInfo(PRUint32 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; default: NS_NOTREACHED("Unexpected load type value"); } return docShellLoadType; } //***************************************************************************** // nsDocShell::nsIDocShell //***************************************************************************** NS_IMETHODIMP nsDocShell::LoadURI(nsIURI * aURI, nsIDocShellLoadInfo * aLoadInfo, PRUint32 aLoadFlags, PRBool aFirstParty) { // 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; PRBool inheritOwner = PR_FALSE; PRBool sendReferrer = PR_TRUE; nsCOMPtr shEntry; nsXPIDLString target; PRUint32 loadType = MAKE_LOAD_TYPE(LOAD_NORMAL, aLoadFlags); NS_ENSURE_ARG(aURI); // 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->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)) { nsCAutoString 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)); PRUint32 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 parent->GetChildSHEntry(mChildOffset, getter_AddRefs(shEntry)); // Make some decisions on the child frame's loadType based on the // parent's loadType. if (mCurrentURI == nsnull) { // 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 laod type to // LOAD_BYPASS_HISTORY. PRBool inOnLoadHandler=PR_FALSE; parentDS->GetIsExecutingOnLoadHandler(&inOnLoadHandler); if (inOnLoadHandler) { loadType = LOAD_NORMAL_REPLACE; shEntry = nsnull; } } 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 = nsnull; } else if ((parentLoadType == LOAD_BYPASS_HISTORY) || (parentLoadType == LOAD_ERROR_PAGE) || (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 { // 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. PRUint32 parentBusy = BUSY_FLAGS_NONE; PRUint32 selfBusy = BUSY_FLAGS_NONE; parentDS->GetBusyFlags(&parentBusy); GetBusyFlags(&selfBusy); if (((parentBusy & BUSY_FLAGS_BUSY) || (selfBusy & BUSY_FLAGS_BUSY)) && shEntry) { loadType = LOAD_NORMAL_REPLACE; shEntry = nsnull; } } } // 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. PRBool inOnLoadHandler=PR_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 rv = LoadHistoryEntry(shEntry, loadType); } // Perform the load... else { // We need an owner (a referring principal). 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. // // 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); PRBool isSystem; rv = secMan->IsSystemPrincipal(ownerPrincipal, &isSystem); NS_ENSURE_SUCCESS(rv, rv); if (isSystem) { owner = nsnull; inheritOwner = PR_TRUE; } } if (!owner && !inheritOwner) { // See if there's system or chrome JS code running rv = secMan->SubjectPrincipalIsSystem(&inheritOwner); if (NS_FAILED(rv)) { // Set it back to false inheritOwner = PR_FALSE; } } PRUint32 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; rv = InternalLoad(aURI, referrer, owner, flags, target.get(), nsnull, // No type hint postStream, headersStream, loadType, nsnull, // No SHEntry aFirstParty, nsnull, // No nsIDocShell nsnull); // No nsIRequest } return rv; } NS_IMETHODIMP nsDocShell::LoadStream(nsIInputStream *aStream, nsIURI * aURI, const nsACString &aContentType, const nsACString &aContentCharset, nsIDocShellLoadInfo * aLoadInfo) { NS_ENSURE_ARG(aStream); mAllowKeywordFixup = PR_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; } PRUint32 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, PR_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 = PR_FALSE; return NS_OK; } NS_IMETHODIMP nsDocShell::FirePageHideNotification(PRBool aIsUnload) { if (mContentViewer && !mFiredUnloadEvent) { // Keep an explicit reference since calling PageHide could release // mContentViewer nsCOMPtr kungFuDeathGrip(mContentViewer); mFiredUnloadEvent = PR_TRUE; mContentViewer->PageHide(aIsUnload); nsAutoTArray, 8> kids; PRInt32 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; } // // 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 */ PRBool nsDocShell::ValidateOrigin(nsIDocShellTreeItem* aOriginTreeItem, nsIDocShellTreeItem* aTargetTreeItem) { nsCOMPtr securityManager = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID); NS_ENSURE_TRUE(securityManager, PR_FALSE); nsCOMPtr subjectPrincipal; nsresult rv = securityManager->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal)); NS_ENSURE_SUCCESS(rv, PR_FALSE); if (subjectPrincipal) { // We're called from JS, check if UniversalBrowserWrite is // enabled. PRBool ubwEnabled = PR_FALSE; rv = securityManager->IsCapabilityEnabled("UniversalBrowserWrite", &ubwEnabled); NS_ENSURE_SUCCESS(rv, PR_FALSE); if (ubwEnabled) { return PR_TRUE; } } // Get origin document principal nsCOMPtr originDOMDocument = do_GetInterface(aOriginTreeItem); nsCOMPtr originDocument(do_QueryInterface(originDOMDocument)); NS_ENSURE_TRUE(originDocument, PR_FALSE); // Get target principal nsCOMPtr targetDOMDocument = do_GetInterface(aTargetTreeItem); nsCOMPtr targetDocument(do_QueryInterface(targetDOMDocument)); NS_ENSURE_TRUE(targetDocument, PR_FALSE); PRBool equal; rv = originDocument->NodePrincipal()-> Equals(targetDocument->NodePrincipal(), &equal); if (NS_SUCCEEDED(rv) && equal) { return PR_TRUE; } // Not strictly equal, special case if both are file: uris PRBool originIsFile = PR_FALSE; PRBool targetIsFile = PR_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) { nsresult rv = NS_OK; NS_ENSURE_ARG_POINTER(aPresContext); *aPresContext = nsnull; nsCOMPtr viewer = mContentViewer; while (viewer) { nsCOMPtr prevViewer; viewer->GetPreviousViewer(getter_AddRefs(prevViewer)); if (prevViewer) viewer = prevViewer; else { nsCOMPtr docv(do_QueryInterface(viewer)); if (docv) rv = docv->GetPresContext(aPresContext); break; } } return rv; } NS_IMETHODIMP nsDocShell::GetPresContext(nsPresContext ** aPresContext) { NS_ENSURE_ARG_POINTER(aPresContext); *aPresContext = nsnull; if (!mContentViewer) return NS_OK; nsCOMPtr docv(do_QueryInterface(mContentViewer)); NS_ENSURE_TRUE(docv, NS_ERROR_NO_INTERFACE); return docv->GetPresContext(aPresContext); } NS_IMETHODIMP nsDocShell::GetPresShell(nsIPresShell ** aPresShell) { nsresult rv = NS_OK; NS_ENSURE_ARG_POINTER(aPresShell); *aPresShell = nsnull; nsCOMPtr presContext; (void) GetPresContext(getter_AddRefs(presContext)); if (presContext) { NS_IF_ADDREF(*aPresShell = presContext->GetPresShell()); } return rv; } NS_IMETHODIMP nsDocShell::GetEldestPresShell(nsIPresShell** aPresShell) { nsresult rv = NS_OK; NS_ENSURE_ARG_POINTER(aPresShell); *aPresShell = nsnull; nsCOMPtr 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) { nsCOMPtr piTarget = do_QueryInterface(aChromeEventHandler); // Weak reference. Don't addref. mChromeEventHandler = piTarget; nsCOMPtr win(do_QueryInterface(mScriptGlobal)); if (win) { win->SetChromeEventHandler(piTarget); } 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; } /* [noscript] void setCurrentURI (in nsIURI uri); */ NS_IMETHODIMP nsDocShell::SetCurrentURI(nsIURI *aURI) { SetCurrentURI(aURI, nsnull, PR_TRUE); return NS_OK; } PRBool nsDocShell::SetCurrentURI(nsIURI *aURI, nsIRequest *aRequest, PRBool aFireOnLocationChange) { #ifdef PR_LOGGING if (gDocShellLeakLog && PR_LOG_TEST(gDocShellLeakLog, PR_LOG_DEBUG)) { nsCAutoString 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 PR_FALSE; } mCurrentURI = NS_TryToMakeImmutable(aURI); PRBool isRoot = PR_FALSE; // Is this the root docshell PRBool isSubFrame = PR_FALSE; // Is this a subframe navigation? nsCOMPtr root; GetSameTypeRootTreeItem(getter_AddRefs(root)); if (root.get() == static_cast(this)) { // This is the root docshell isRoot = PR_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 PR_FALSE; } if (aFireOnLocationChange) { FireOnLocationChange(this, aRequest, aURI); } return !aFireOnLocationChange; } NS_IMETHODIMP nsDocShell::GetCharset(char** aCharset) { NS_ENSURE_ARG_POINTER(aCharset); *aCharset = nsnull; nsCOMPtr presShell; GetPresShell(getter_AddRefs(presShell)); 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) { NS_ENSURE_SUCCESS(muDV->SetDefaultCharacterSet(nsDependentCString(aCharset)), NS_ERROR_FAILURE); } } // set the charset override nsCOMPtr dcInfo; GetDocumentCharsetInfo(getter_AddRefs(dcInfo)); if (dcInfo) { nsCOMPtr csAtom; csAtom = do_GetAtom(aCharset); dcInfo->SetForcedCharset(csAtom); } return NS_OK; } NS_IMETHODIMP nsDocShell::GetDocumentCharsetInfo(nsIDocumentCharsetInfo ** aDocumentCharsetInfo) { NS_ENSURE_ARG_POINTER(aDocumentCharsetInfo); // if the mDocumentCharsetInfo does not exist already, we create it now if (!mDocumentCharsetInfo) { mDocumentCharsetInfo = do_CreateInstance(NS_DOCUMENTCHARSETINFO_CONTRACTID); if (!mDocumentCharsetInfo) return NS_ERROR_FAILURE; } *aDocumentCharsetInfo = mDocumentCharsetInfo; NS_IF_ADDREF(*aDocumentCharsetInfo); return NS_OK; } NS_IMETHODIMP nsDocShell::SetDocumentCharsetInfo(nsIDocumentCharsetInfo * aDocumentCharsetInfo) { mDocumentCharsetInfo = aDocumentCharsetInfo; return NS_OK; } NS_IMETHODIMP nsDocShell::GetChannelIsUnsafe(PRBool *aUnsafe) { *aUnsafe = PR_FALSE; nsCOMPtr channel; GetCurrentDocumentChannel(getter_AddRefs(channel)); if (!channel) { return NS_OK; } nsCOMPtr jarChannel = do_QueryInterface(channel); if (!jarChannel) { return NS_OK; } return jarChannel->GetIsUnsafe(aUnsafe); } NS_IMETHODIMP nsDocShell::GetAllowPlugins(PRBool * aAllowPlugins) { NS_ENSURE_ARG_POINTER(aAllowPlugins); *aAllowPlugins = mAllowPlugins; if (!mAllowPlugins) { return NS_OK; } PRBool unsafe; *aAllowPlugins = NS_SUCCEEDED(GetChannelIsUnsafe(&unsafe)) && !unsafe; return NS_OK; } NS_IMETHODIMP nsDocShell::SetAllowPlugins(PRBool aAllowPlugins) { mAllowPlugins = aAllowPlugins; //XXX should enable or disable a plugin host return NS_OK; } NS_IMETHODIMP nsDocShell::GetAllowJavascript(PRBool * aAllowJavascript) { NS_ENSURE_ARG_POINTER(aAllowJavascript); *aAllowJavascript = mAllowJavascript; if (!mAllowJavascript) { return NS_OK; } PRBool unsafe; *aAllowJavascript = NS_SUCCEEDED(GetChannelIsUnsafe(&unsafe)) && !unsafe; return NS_OK; } NS_IMETHODIMP nsDocShell::SetAllowJavascript(PRBool aAllowJavascript) { mAllowJavascript = aAllowJavascript; return NS_OK; } NS_IMETHODIMP nsDocShell::GetAllowMetaRedirects(PRBool * aReturn) { NS_ENSURE_ARG_POINTER(aReturn); *aReturn = mAllowMetaRedirects; if (!mAllowMetaRedirects) { return NS_OK; } PRBool unsafe; *aReturn = NS_SUCCEEDED(GetChannelIsUnsafe(&unsafe)) && !unsafe; return NS_OK; } NS_IMETHODIMP nsDocShell::SetAllowMetaRedirects(PRBool aValue) { mAllowMetaRedirects = aValue; return NS_OK; } NS_IMETHODIMP nsDocShell::GetAllowSubframes(PRBool * aAllowSubframes) { NS_ENSURE_ARG_POINTER(aAllowSubframes); *aAllowSubframes = mAllowSubframes; return NS_OK; } NS_IMETHODIMP nsDocShell::SetAllowSubframes(PRBool aAllowSubframes) { mAllowSubframes = aAllowSubframes; return NS_OK; } NS_IMETHODIMP nsDocShell::GetAllowImages(PRBool * aAllowImages) { NS_ENSURE_ARG_POINTER(aAllowImages); *aAllowImages = mAllowImages; return NS_OK; } NS_IMETHODIMP nsDocShell::SetAllowImages(PRBool aAllowImages) { mAllowImages = aAllowImages; return NS_OK; } NS_IMETHODIMP nsDocShell::GetDocShellEnumerator(PRInt32 aItemType, PRInt32 aDirection, nsISimpleEnumerator **outEnum) { NS_ENSURE_ARG_POINTER(outEnum); *outEnum = nsnull; 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(PRUint32 * aAppType) { *aAppType = mAppType; return NS_OK; } NS_IMETHODIMP nsDocShell::SetAppType(PRUint32 aAppType) { mAppType = aAppType; return NS_OK; } NS_IMETHODIMP nsDocShell::GetAllowAuth(PRBool * aAllowAuth) { *aAllowAuth = mAllowAuth; return NS_OK; } NS_IMETHODIMP nsDocShell::SetAllowAuth(PRBool 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(PRInt32 * aWidth) { NS_ENSURE_ARG_POINTER(aWidth); *aWidth = mMarginWidth; return NS_OK; } NS_IMETHODIMP nsDocShell::SetMarginWidth(PRInt32 aWidth) { mMarginWidth = aWidth; return NS_OK; } NS_IMETHODIMP nsDocShell::GetMarginHeight(PRInt32 * aHeight) { NS_ENSURE_ARG_POINTER(aHeight); *aHeight = mMarginHeight; return NS_OK; } NS_IMETHODIMP nsDocShell::SetMarginHeight(PRInt32 aHeight) { mMarginHeight = aHeight; return NS_OK; } NS_IMETHODIMP nsDocShell::GetBusyFlags(PRUint32 * aBusyFlags) { NS_ENSURE_ARG_POINTER(aBusyFlags); *aBusyFlags = mBusyFlags; return NS_OK; } NS_IMETHODIMP nsDocShell::TabToTreeOwner(PRBool aForward, PRBool* 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 = PR_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(PRBool *aUseErrorPages) { *aUseErrorPages = mUseErrorPages; return NS_OK; } NS_IMETHODIMP nsDocShell::SetUseErrorPages(PRBool aUseErrorPages) { // If mUseErrorPages is set explicitly, stop observing the pref. if (mObserveErrorPages) { nsCOMPtr prefs(do_QueryInterface(mPrefs)); if (prefs) { prefs->RemoveObserver("browser.xul.error_pages.enabled", this); mObserveErrorPages = PR_FALSE; } } mUseErrorPages = aUseErrorPages; return NS_OK; } NS_IMETHODIMP nsDocShell::GetPreviousTransIndex(PRInt32 *aPreviousTransIndex) { *aPreviousTransIndex = mPreviousTransIndex; return NS_OK; } NS_IMETHODIMP nsDocShell::GetLoadedTransIndex(PRInt32 *aLoadedTransIndex) { *aLoadedTransIndex = mLoadedTransIndex; return NS_OK; } NS_IMETHODIMP nsDocShell::HistoryPurged(PRInt32 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 = PR_MAX(-1, mPreviousTransIndex - aNumEntries); mLoadedTransIndex = PR_MAX(0, mLoadedTransIndex - aNumEntries); PRInt32 count = mChildList.Count(); for (PRInt32 i = 0; i < count; ++i) { nsCOMPtr shell = do_QueryInterface(ChildAt(i)); if (shell) { shell->HistoryPurged(aNumEntries); } } return NS_OK; } NS_IMETHODIMP nsDocShell::GetSessionStorageForURI(nsIURI* aURI, nsIDOMStorage** aStorage) { NS_ENSURE_ARG_POINTER(aStorage); *aStorage = nsnull; nsCOMPtr topItem; nsresult rv = GetSameTypeRootTreeItem(getter_AddRefs(topItem)); if (NS_FAILED(rv)) return rv; if (!topItem) return NS_ERROR_FAILURE; nsCOMPtr topDocShell = do_QueryInterface(topItem); if (topDocShell != this) return topDocShell->GetSessionStorageForURI(aURI, aStorage); nsCAutoString currentDomain; rv = aURI->GetAsciiHost(currentDomain); NS_ENSURE_SUCCESS(rv, rv); if (currentDomain.IsEmpty()) return NS_OK; if (!mStorages.Get(currentDomain, aStorage)) { nsCOMPtr newstorage = do_CreateInstance("@mozilla.org/dom/storage;1"); if (!newstorage) return NS_ERROR_OUT_OF_MEMORY; nsCOMPtr pistorage = do_QueryInterface(newstorage); if (!pistorage) return NS_ERROR_FAILURE; pistorage->Init(aURI, NS_ConvertUTF8toUTF16(currentDomain), PR_FALSE); if (!mStorages.Put(currentDomain, newstorage)) return NS_ERROR_OUT_OF_MEMORY; *aStorage = newstorage; NS_ADDREF(*aStorage); } return NS_OK; } nsresult nsDocShell::AddSessionStorage(const nsACString& aDomain, nsIDOMStorage* aStorage) { NS_ENSURE_ARG_POINTER(aStorage); if (aDomain.IsEmpty()) 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) { if (!mStorages.Put(aDomain, aStorage)) return NS_ERROR_OUT_OF_MEMORY; } else { return topDocShell->AddSessionStorage(aDomain, aStorage); } } return NS_OK; } NS_IMETHODIMP nsDocShell::GetCurrentDocumentChannel(nsIChannel** aResult) { *aResult = nsnull; if (!mContentViewer) return NS_OK; nsCOMPtr domDoc; nsresult rv = mContentViewer->GetDOMDocument(getter_AddRefs(domDoc)); if (NS_FAILED(rv)) return rv; nsCOMPtr doc(do_QueryInterface(domDoc)); if (doc) { *aResult = doc->GetChannel(); NS_IF_ADDREF(*aResult); } return NS_OK; } //***************************************************************************** // 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, PRBool *_retval) { NS_ENSURE_ARG_POINTER(aName); NS_ENSURE_ARG_POINTER(_retval); *_retval = mName.Equals(aName); return NS_OK; } NS_IMETHODIMP nsDocShell::GetItemType(PRInt32 * aItemType) { NS_ENSURE_ARG_POINTER(aItemType); *aItemType = mItemType; return NS_OK; } NS_IMETHODIMP nsDocShell::SetItemType(PRInt32 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; return NS_OK; } NS_IMETHODIMP nsDocShell::GetParent(nsIDocShellTreeItem ** aParent) { if (!mParent) { *aParent = nsnull; } 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. nsCOMPtr parentAsDocShell(do_QueryInterface(parent)); if (parentAsDocShell) { PRBool value; 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); } } 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 = nsnull; nsCOMPtr parent = do_QueryInterface(GetAsSupports(mParent)); if (!parent) return NS_OK; PRInt32 parentType; NS_ENSURE_SUCCESS(parent->GetItemType(&parentType), NS_ERROR_FAILURE); if (parentType == mItemType) { parent.swap(*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 */ PRBool nsDocShell::CanAccessItem(nsIDocShellTreeItem* aTargetItem, nsIDocShellTreeItem* aAccessingItem, PRBool aConsiderOpener) { NS_PRECONDITION(aTargetItem, "Must have target item!"); if (!gValidateOrigin || !aAccessingItem) { // Good to go return PR_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 // // 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 PR_TRUE; } nsCOMPtr accessingRoot; aAccessingItem->GetSameTypeRootTreeItem(getter_AddRefs(accessingRoot)); if (aTargetItem == accessingRoot) { // A frame can navigate its root. return PR_TRUE; } // Check if aAccessingItem can navigate one of aTargetItem's ancestors. nsCOMPtr target = aTargetItem; do { if (ValidateOrigin(aAccessingItem, target)) { return PR_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 PR_FALSE; } if (!aConsiderOpener) { // All done here return PR_FALSE; } nsCOMPtr targetWindow(do_GetInterface(aTargetItem)); nsCOMPtr targetInternal(do_QueryInterface(targetWindow)); if (!targetInternal) { NS_ERROR("This should not happen, really"); return PR_FALSE; } nsCOMPtr targetOpener; targetInternal->GetOpener(getter_AddRefs(targetOpener)); nsCOMPtr openerWebNav(do_GetInterface(targetOpener)); nsCOMPtr openerItem(do_QueryInterface(openerWebNav)); if (!openerItem) { return PR_FALSE; } return CanAccessItem(openerItem, aAccessingItem, PR_FALSE); } static PRBool ItemIsActive(nsIDocShellTreeItem *aItem) { nsCOMPtr tmp(do_GetInterface(aItem)); nsCOMPtr window(do_QueryInterface(tmp)); if (window) { PRBool isClosed; if (NS_SUCCEEDED(window->GetClosed(&isClosed)) && !isClosed) { return PR_TRUE; } } return PR_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 = nsnull; 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 = nsnull; } 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. 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 nsnull 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, PR_TRUE, PR_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; PRInt32 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 (PRInt32 i=0;iGetChildCount(&childWebshellCount); nsCOMPtr parentAsDocShell(do_QueryInterface(aParentNode)); PRInt32 type; aParentNode->GetItemType(&type); nsCOMPtr presShell; parentAsDocShell->GetPresShell(getter_AddRefs(presShell)); nsCOMPtr presContext; parentAsDocShell->GetPresContext(getter_AddRefs(presContext)); nsIDocument *doc = presShell->GetDocument(); nsCOMPtr domwin(doc->GetWindow()); nsCOMPtr widget; nsIViewManager* vm = presShell->GetViewManager(); if (vm) { vm->GetWidget(getter_AddRefs(widget)); } nsIContent* rootContent = doc->GetRootContent(); 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*)rootContent); if (childWebshellCount > 0) { for (PRInt32 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 PRInt32 i, n = mChildList.Count(); for (i = 0; i < n; i++) { nsCOMPtr child = do_QueryInterface(ChildAt(i)); NS_ENSURE_TRUE(child, NS_ERROR_FAILURE); PRInt32 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(PRUint32 aChildOffset) { mChildOffset = aChildOffset; return NS_OK; } NS_IMETHODIMP nsDocShell::GetIsInUnload(PRBool* aIsInUnload) { *aIsInUnload = mFiredUnloadEvent; return NS_OK; } //***************************************************************************** // nsDocShell::nsIDocShellTreeNode //***************************************************************************** NS_IMETHODIMP nsDocShell::GetChildCount(PRInt32 * 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(nsnull); nsresult res = AddChildLoader(childAsDocLoader); NS_ENSURE_SUCCESS(res, res); NS_ASSERTION(mChildList.Count() > 0, "child list must not be empty after a successful add"); // Set the child's index in the parent's children list // XXX What if the parent had different types of children? // XXX in that case docshell hierarchy and SH hierarchy won't match. { nsCOMPtr childDocShell = do_QueryInterface(aChild); if (childDocShell) { // If there are frameloaders in the finalization list, reduce // the offset so that the SH hierarchy is more likely to match the // docshell hierarchy nsCOMPtr domDoc = do_GetInterface(GetAsSupports(this)); nsCOMPtr doc = do_QueryInterface(domDoc); PRUint32 offset = mChildList.Count() - 1; if (doc) { PRUint32 oldChildCount = offset; // Current child count - 1 for (PRUint32 i = 0; i < oldChildCount; ++i) { nsCOMPtr child = do_QueryInterface(ChildAt(i)); if (doc->FrameLoaderScheduledToBeFinalized(child)) { --offset; } } } childDocShell->SetChildOffset(offset); } } /* Set the child's global history if the parent has one */ if (mGlobalHistory) { nsCOMPtr dsHistoryChild(do_QueryInterface(aChild)); if (dsHistoryChild) dsHistoryChild->SetUseGlobalHistory(PR_TRUE); } PRInt32 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 parentCharset field of the // child's DocumentCharsetInfo 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 child's docCSInfo object nsCOMPtr dcInfo = NULL; res = childAsDocShell->GetDocumentCharsetInfo(getter_AddRefs(dcInfo)); if (NS_FAILED(res) || (!dcInfo)) return NS_OK; // get the parent's current charset nsCOMPtr docv(do_QueryInterface(mContentViewer)); if (!docv) return NS_OK; nsCOMPtr doc; res = docv->GetDocument(getter_AddRefs(doc)); if (NS_FAILED(res) || (!doc)) return NS_OK; const nsACString &parentCS = doc->GetDocumentCharacterSet(); PRBool isWyciwyg = PR_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 = dcInfo->SetParentCharset(parentCSAtom); if (NS_FAILED(res)) return NS_OK; PRInt32 charsetSource = doc->GetDocumentCharacterSetSource(); // set the child's parentCharset res = dcInfo->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(nsnull); return nsDocLoader::AddDocLoaderAsChildOfRoot(childAsDocLoader); } NS_IMETHODIMP nsDocShell::GetChildAt(PRInt32 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, PRBool aRecurse, PRBool aSameType, nsIDocShellTreeItem * aRequestor, nsIDocShellTreeItem * aOriginalRequestor, nsIDocShellTreeItem ** _retval) { NS_ENSURE_ARG(aName); NS_ENSURE_ARG_POINTER(_retval); *_retval = nsnull; // if we don't find one, we return NS_OK and a null result if (!*aName) return NS_OK; nsXPIDLString childName; PRInt32 i, n = mChildList.Count(); for (i = 0; i < n; i++) { nsCOMPtr child = do_QueryInterface(ChildAt(i)); NS_ENSURE_TRUE(child, NS_ERROR_FAILURE); PRInt32 childType; child->GetItemType(&childType); if (aSameType && (childType != mItemType)) continue; PRBool childNameEquals = PR_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, PR_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(PRInt32 aChildOffset, nsISHEntry ** aResult) { nsresult rv = NS_OK; NS_ENSURE_ARG_POINTER(aResult); *aResult = nsnull; // 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. */ PRBool parentExpired=PR_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 */ PRUint32 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 = nsnull; 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, PRInt32 aChildOffset) { nsresult rv; if (mLSHE) { /* 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. */ PRInt32 index = -1; nsCOMPtr currentHE; mSessionHistory->GetIndex(&index); if (index < 0) return NS_ERROR_FAILURE; rv = mSessionHistory->GetEntryAtIndex(index, PR_FALSE, getter_AddRefs(currentHE)); NS_ENSURE_TRUE(currentHE, NS_ERROR_FAILURE); nsCOMPtr currentEntry(do_QueryInterface(currentHE)); if (currentEntry) { PRUint32 cloneID = 0; nsCOMPtr nextEntry; aCloneRef->GetID(&cloneID); rv = CloneAndReplace(currentEntry, this, cloneID, aNewEntry, getter_AddRefs(nextEntry)); if (NS_SUCCEEDED(rv)) { nsCOMPtr shPrivate(do_QueryInterface(mSessionHistory)); NS_ENSURE_TRUE(shPrivate, NS_ERROR_FAILURE); rv = shPrivate->AddEntry(nextEntry, PR_TRUE); } } } else { /* Just pass this along */ nsCOMPtr parent = do_QueryInterface(GetAsSupports(mParent), &rv); if (parent) { rv = parent->AddChildSHEntry(aCloneRef, aNewEntry, aChildOffset); } } return rv; } nsresult nsDocShell::DoAddChildSHEntry(nsISHEntry* aNewEntry, PRInt32 aChildOffset) { /* 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); } 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(PRBool aUseGlobalHistory) { nsresult rv; if (!aUseGlobalHistory) { mGlobalHistory = nsnull; return NS_OK; } if (mGlobalHistory) { return NS_OK; } mGlobalHistory = do_GetService(NS_GLOBALHISTORY2_CONTRACTID, &rv); return rv; } NS_IMETHODIMP nsDocShell::GetUseGlobalHistory(PRBool *aUseGlobalHistory) { *aUseGlobalHistory = (mGlobalHistory != nsnull); return NS_OK; } //------------------------------------- //-- Helper Method for Print discovery //------------------------------------- PRBool nsDocShell::IsPrintingOrPP(PRBool aDisplayErrorDialog) { if (mIsPrintingOrPP && aDisplayErrorDialog) { DisplayLoadError(NS_ERROR_DOCUMENT_IS_PRINTMODE, nsnull, nsnull); } return mIsPrintingOrPP; } PRBool nsDocShell::IsNavigationAllowed(PRBool aDisplayPrintErrorDialog) { return !IsPrintingOrPP(aDisplayPrintErrorDialog) && !mFiredUnloadEvent; } //***************************************************************************** // nsDocShell::nsIWebNavigation //***************************************************************************** NS_IMETHODIMP nsDocShell::GetCanGoBack(PRBool * aCanGoBack) { if (!IsNavigationAllowed(PR_FALSE)) { *aCanGoBack = PR_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(PRBool * aCanGoForward) { if (!IsNavigationAllowed(PR_FALSE)) { *aCanGoForward = PR_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(PRInt32 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, PRUint32 aLoadFlags, nsIURI * aReferringURI, nsIInputStream * aPostStream, nsIInputStream * aHeaderStream) { 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). PRUint32 fixupFlags = 0; if (aLoadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) { fixupFlags |= nsIURIFixup::FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP; } 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. PRUint32 extraFlags = (aLoadFlags & EXTRA_LOAD_FLAGS); aLoadFlags &= ~EXTRA_LOAD_FLAGS; nsCOMPtr loadInfo; rv = CreateLoadInfo(getter_AddRefs(loadInfo)); if (NS_FAILED(rv)) return rv; PRUint32 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, PR_TRUE); 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 PRUint32 kMaxFormatStrArgs = 3; nsAutoString formatStrs[kMaxFormatStrArgs]; PRUint32 formatStrCount = 0; PRBool addHostPort = PR_FALSE; nsresult rv = NS_OK; nsAutoString messageStr; nsCAutoString cssClass; nsCAutoString 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 nsCAutoString 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 nsCAutoString 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 = PR_TRUE; error.AssignLiteral("connectionFailure"); } else if(NS_ERROR_NET_INTERRUPT == aError) { NS_ENSURE_ARG_POINTER(aURI); addHostPort = PR_TRUE; error.AssignLiteral("netInterrupt"); } else if (NS_ERROR_NET_TIMEOUT == aError) { NS_ENSURE_ARG_POINTER(aURI); // Get the host nsCAutoString host; aURI->GetHost(host); CopyUTF8toUTF16(host, formatStrs[0]); formatStrCount = 1; error.AssignLiteral("netTimeout"); } else if (NS_ERROR_GET_MODULE(aError) == NS_ERROR_MODULE_SECURITY) { nsCOMPtr nsserr = do_GetService(NS_NSS_ERRORS_SERVICE_CONTRACTID); PRUint32 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"); PRBool expert = PR_FALSE; mPrefs->GetBoolPref("browser.xul.error_pages.expert_bad_cert", &expert); if (expert) { cssClass.AssignLiteral("expertBadCert"); } } else { error.AssignLiteral("nssFailure2"); } } } else if (NS_ERROR_PHISHING_URI == aError || NS_ERROR_MALWARE_URI == aError) { nsCAutoString 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 nsXPIDLCString alternateErrorPage; mPrefs->GetCharPref("urlclassifier.alternate_error_page", getter_Copies(alternateErrorPage)); if (alternateErrorPage) errorPage.Assign(alternateErrorPage); if (NS_ERROR_PHISHING_URI == aError) error.AssignLiteral("phishingBlocked"); else error.AssignLiteral("malwareBlocked"); 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 we are offline and the cache does not // contain a copy of the document. 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 = PR_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_UNSAFE_CONTENT_TYPE: // Channel refused to load from an unrecognized content type. error.AssignLiteral("unsafeContentType"); 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. nsCAutoString hostport; if (aURI) { aURI->GetHostPort(hostport); } else { hostport.AssignLiteral("?"); } CopyUTF8toUTF16(hostport, formatStrs[formatStrCount++]); } nsCAutoString spec; rv = NS_ERROR_NOT_AVAILABLE; if (aURI) { // displaying "file://" is aesthetically unpleasing and could even be // confusing to the user PRBool isFileURI = PR_FALSE; rv = aURI->SchemeIs("file", &isFileURI); if (NS_SUCCEEDED(rv) && isFileURI) aURI->GetPath(spec); else aURI->GetSpec(spec); nsCAutoString 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 (PRUint32 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); // Note: For now, display an alert instead of an error page if we have no // URI object. Missing URI objects are handled badly by session history. if (mUseErrorPages && aURI && aFailedChannel) { // 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(nsnull, 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)) { nsCAutoString spec; aURI->GetSpec(spec); nsCAutoString 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 // Create an shistory entry for the old load, if we have a channel if (aFailedChannel) { mURIResultedInDocument = PR_TRUE; OnLoadingSite(aFailedChannel, PR_TRUE, PR_FALSE); } else if (aURI) { mURIResultedInDocument = PR_TRUE; OnNewURI(aURI, nsnull, mLoadType, PR_TRUE, PR_FALSE); } // Be sure to have a correct mLSHE, it may have been cleared by // EndPageLoad. See bug 302115. if (mSessionHistory && !mLSHE) { PRInt32 idx; mSessionHistory->GetRequestedIndex(&idx); if (idx == -1) mSessionHistory->GetIndex(&idx); nsCOMPtr entry; mSessionHistory->GetEntryAtIndex(idx, PR_FALSE, getter_AddRefs(entry)); mLSHE = do_QueryInterface(entry); } nsCAutoString url; nsCAutoString charset; if (aURI) { // Set our current URI SetCurrentURI(aURI); nsresult rv = aURI->GetSpec(url); rv |= aURI->GetOriginCharset(charset); NS_ENSURE_SUCCESS(rv, rv); } else if (aURL) { CopyUTF16toUTF8(aURL, url); } else { return NS_ERROR_INVALID_POINTER; } // Create a URL to pass all the error information through to the page. char *escapedUrl = nsEscape(url.get(), url_Path); char *escapedCharset = nsEscape(charset.get(), url_Path); char *escapedError = nsEscape(NS_ConvertUTF16toUTF8(aErrorType).get(), url_Path); char *escapedDescription = nsEscape(NS_ConvertUTF16toUTF8(aDescription).get(), url_Path); char *escapedCSSClass = nsEscape(aCSSClass, url_Path); nsCString errorPageUrl("about:"); errorPageUrl.AppendASCII(aErrorPage); errorPageUrl.AppendLiteral("?e="); errorPageUrl.AppendASCII(escapedError); errorPageUrl.AppendLiteral("&u="); errorPageUrl.AppendASCII(escapedUrl); if (escapedCSSClass && escapedCSSClass[0]) { errorPageUrl.AppendASCII("&s="); errorPageUrl.AppendASCII(escapedCSSClass); } errorPageUrl.AppendLiteral("&c="); errorPageUrl.AppendASCII(escapedCharset); errorPageUrl.AppendLiteral("&d="); errorPageUrl.AppendASCII(escapedDescription); nsMemory::Free(escapedDescription); nsMemory::Free(escapedError); nsMemory::Free(escapedUrl); nsMemory::Free(escapedCharset); nsMemory::Free(escapedCSSClass); nsCOMPtr errorPageURI; nsresult rv = NS_NewURI(getter_AddRefs(errorPageURI), errorPageUrl); NS_ENSURE_SUCCESS(rv, rv); return InternalLoad(errorPageURI, nsnull, nsnull, PR_TRUE, nsnull, nsnull, nsnull, nsnull, LOAD_ERROR_PAGE, nsnull, PR_TRUE, nsnull, nsnull); } NS_IMETHODIMP nsDocShell::Reload(PRUint32 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!"); PRUint32 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)); PRBool canReload = PR_TRUE; if (rootSH) { nsCOMPtr listener; shistInt->GetListener(getter_AddRefs(listener)); if (listener) { listener->OnHistoryReload(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 domDoc(do_GetInterface(GetAsSupports(this))); nsCOMPtr doc(do_QueryInterface(domDoc)); nsIPrincipal* principal = nsnull; 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 nsnull, // No window target NS_LossyConvertUTF16toASCII(contentTypeHint).get(), nsnull, // No post data nsnull, // No headers data loadType, // Load type nsnull, // No SHEntry PR_TRUE, nsnull, // No nsIDocShell nsnull); // No nsIRequest } return rv; } NS_IMETHODIMP nsDocShell::Stop(PRUint32 aStopFlags) { // Revoke any pending event related to content viewer restoration mRestorePresentationEvent.Revoke(); 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 = nsnull; } if (mClassifier) { mClassifier->Cancel(); mClassifier = nsnull; } // 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(); } PRInt32 n; PRInt32 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::SetDocument(nsIDOMDocument* aDocument, const PRUnichar* aContentType) { //XXX First Checkin NS_ERROR("Not Yet Implemented"); return NS_ERROR_FAILURE; } */ 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 = nsnull; 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, PRUint32 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); // // 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 = nsnull; 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(nsnull); dest->SetIsSubFrame(PR_FALSE); return CallQueryInterface(dest, aPageDescriptor); } return NS_ERROR_NOT_AVAILABLE; } //***************************************************************************** // nsDocShell::nsIBaseWindow //***************************************************************************** NS_IMETHODIMP nsDocShell::InitWindow(nativeWindow parentNativeWindow, nsIWidget * parentWidget, PRInt32 x, PRInt32 y, PRInt32 cx, PRInt32 cy) { NS_ENSURE_ARG(parentWidget); // DocShells must get a widget for a parent SetParentWidget(parentWidget); SetPositionAndSize(x, y, cx, cy, PR_FALSE); return NS_OK; } NS_IMETHODIMP nsDocShell::Create() { NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome, "Unexpected item type in docshell"); nsresult rv = NS_ERROR_FAILURE; mPrefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); PRBool tmpbool; rv = mPrefs->GetBoolPref("browser.frames.enabled", &tmpbool); if (NS_SUCCEEDED(rv)) mAllowSubframes = tmpbool; if (gValidateOrigin == (PRBool)0xffffffff) { // Check pref to see if we should prevent frameset spoofing rv = mPrefs->GetBoolPref("browser.frame.validate_origin", &tmpbool); if (NS_SUCCEEDED(rv)) { gValidateOrigin = tmpbool; } else { gValidateOrigin = PR_TRUE; } } // Should we use XUL error pages instead of alerts if possible? rv = mPrefs->GetBoolPref("browser.xul.error_pages.enabled", &tmpbool); if (NS_SUCCEEDED(rv)) mUseErrorPages = tmpbool; nsCOMPtr prefs(do_QueryInterface(mPrefs, &rv)); if (NS_SUCCEEDED(rv) && mObserveErrorPages) { prefs->AddObserver("browser.xul.error_pages.enabled", this, PR_FALSE); } nsCOMPtr serv = do_GetService(NS_OBSERVERSERVICE_CONTRACTID); if (serv) { const char* msg = mItemType == typeContent ? NS_WEBNAVIGATION_CREATE : NS_CHROME_WEBNAVIGATION_CREATE; serv->NotifyObservers(GetAsSupports(this), msg, nsnull); } return NS_OK; } NS_IMETHODIMP nsDocShell::Destroy() { NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome, "Unexpected item type in docshell"); if (!mIsBeingDestroyed) { nsCOMPtr serv = do_GetService(NS_OBSERVERSERVICE_CONTRACTID); if (serv) { const char* msg = mItemType == typeContent ? NS_WEBNAVIGATION_DESTROY : NS_CHROME_WEBNAVIGATION_DESTROY; serv->NotifyObservers(GetAsSupports(this), msg, nsnull); } } mIsBeingDestroyed = PR_TRUE; // Remove our pref observers if (mObserveErrorPages) { nsCOMPtr prefs(do_QueryInterface(mPrefs)); if (prefs) { prefs->RemoveObserver("browser.xul.error_pages.enabled", this); mObserveErrorPages = PR_FALSE; } } // Make sure to blow away our mLoadingURI just in case. No loads // from inside this pagehide. mLoadingURI = nsnull; // Fire unload event before we blow anything away. (void) FirePageHideNotification(PR_TRUE); // Clear pointers to any detached nsEditorData that's lying // around in shistory entries. Breaks cycle. See bug 430921. if (mOSHE) mOSHE->SetEditorData(nsnull); if (mLSHE) mLSHE->SetEditorData(nsnull); // Note: mContentListener can be null if Init() failed and we're being // called from the destructor. if (mContentListener) { mContentListener->DropDocShellreference(); mContentListener->SetParentContentListener(nsnull); // 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 = nsnull; mTransferableHookData = nsnull; // 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); nsCOMPtr suppressor; if (mContentViewer) { suppressor = do_GetService(NS_NSIFOCUSEVENTSUPPRESSORSERVICE_CONTRACTID); NS_ENSURE_STATE(suppressor); suppressor->Suppress(); mContentViewer->Close(nsnull); mContentViewer->Destroy(); mContentViewer = nsnull; } nsDocLoader::Destroy(); mParentWidget = nsnull; mCurrentURI = nsnull; if (mScriptGlobal) { nsCOMPtr win(do_QueryInterface(mScriptGlobal)); win->SetDocShell(nsnull); mScriptGlobal = nsnull; } mSessionHistory = nsnull; SetTreeOwner(nsnull); // required to break ref cycle mSecurityUI = nsnull; // Cancel any timers that were set for this docshell; this is needed // to break the cycle between us and the timers. CancelRefreshURITimers(); if (suppressor) { suppressor->Unsuppress(); } return NS_OK; } NS_IMETHODIMP nsDocShell::SetPosition(PRInt32 x, PRInt32 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(PRInt32 * aX, PRInt32 * aY) { PRInt32 dummyHolder; return GetPositionAndSize(aX, aY, &dummyHolder, &dummyHolder); } NS_IMETHODIMP nsDocShell::SetSize(PRInt32 aCX, PRInt32 aCY, PRBool aRepaint) { PRInt32 x = 0, y = 0; GetPosition(&x, &y); return SetPositionAndSize(x, y, aCX, aCY, aRepaint); } NS_IMETHODIMP nsDocShell::GetSize(PRInt32 * aCX, PRInt32 * aCY) { PRInt32 dummyHolder; return GetPositionAndSize(&dummyHolder, &dummyHolder, aCX, aCY); } NS_IMETHODIMP nsDocShell::SetPositionAndSize(PRInt32 x, PRInt32 y, PRInt32 cx, PRInt32 cy, PRBool 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(PRInt32 * x, PRInt32 * y, PRInt32 * cx, PRInt32 * cy) { // We should really consider just getting this information from // our window instead of duplicating the storage and code... nsCOMPtr document(do_GetInterface(GetAsSupports(mParent))); nsCOMPtr doc(do_QueryInterface(document)); if (doc) { doc->FlushPendingNotifications(Flush_Layout); } DoGetPositionAndSize(x, y, cx, cy); return NS_OK; } void nsDocShell::DoGetPositionAndSize(PRInt32 * x, PRInt32 * y, PRInt32 * cx, PRInt32 * 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(PRBool aForce) { nsCOMPtr context; GetPresContext(getter_AddRefs(context)); NS_ENSURE_TRUE(context, NS_ERROR_FAILURE); nsIViewManager* viewManager = context->GetViewManager(); NS_ENSURE_TRUE(viewManager, NS_ERROR_FAILURE); // what about aForce ? NS_ENSURE_SUCCESS(viewManager->UpdateAllViews(0), NS_ERROR_FAILURE); 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 = nsnull; return NS_OK; } NS_IMETHODIMP nsDocShell::SetParentNativeWindow(nativeWindow parentNativeWindow) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsDocShell::GetVisibility(PRBool * aVisibility) { NS_ENSURE_ARG_POINTER(aVisibility); if (!mContentViewer) { *aVisibility = PR_FALSE; return NS_OK; } // get the pres shell nsCOMPtr presShell; NS_ENSURE_SUCCESS(GetPresShell(getter_AddRefs(presShell)), NS_ERROR_FAILURE); NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE); // get the view manager nsIViewManager* vm = presShell->GetViewManager(); NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE); // get the root view nsIView *view = nsnull; // views are not ref counted NS_ENSURE_SUCCESS(vm->GetRootView(view), NS_ERROR_FAILURE); NS_ENSURE_TRUE(view, NS_ERROR_FAILURE); // if our root view is hidden, we are not visible if (view->GetVisibility() == nsViewVisibility_kHide) { *aVisibility = PR_FALSE; 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)); docShell->GetPresShell(getter_AddRefs(presShell)); nsCOMPtr parentDS = do_QueryInterface(parentItem); nsCOMPtr pPresShell; parentDS->GetPresShell(getter_AddRefs(pPresShell)); // Null-check for crash in bug 267804 if (!pPresShell) { NS_NOTREACHED("docshell has null pres shell"); *aVisibility = PR_FALSE; return NS_OK; } nsIContent *shellContent = pPresShell->GetDocument()->FindContentForSubDocument(presShell->GetDocument()); NS_ASSERTION(shellContent, "subshell not in the map"); nsIFrame* frame = pPresShell->GetPrimaryFrameFor(shellContent); PRBool isDocShellOffScreen = PR_FALSE; docShell->GetIsOffScreenBrowser(&isDocShellOffScreen); if (frame && !frame->AreAncestorViewsVisible() && !isDocShellOffScreen) { *aVisibility = PR_FALSE; return NS_OK; } treeItem = parentItem; treeItem->GetParent(getter_AddRefs(parentItem)); } nsCOMPtr treeOwnerAsWin(do_QueryInterface(mTreeOwner)); if (!treeOwnerAsWin) { *aVisibility = PR_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(PRBool aIsOffScreen) { mIsOffScreenBrowser = aIsOffScreen; return NS_OK; } NS_IMETHODIMP nsDocShell::GetIsOffScreenBrowser(PRBool *aIsOffScreen) { *aIsOffScreen = mIsOffScreenBrowser; return NS_OK; } NS_IMETHODIMP nsDocShell::SetVisibility(PRBool aVisibility) { if (!mContentViewer) return NS_OK; if (aVisibility) { mContentViewer->Show(); } else { mContentViewer->Hide(); } return NS_OK; } NS_IMETHODIMP nsDocShell::GetEnabled(PRBool *aEnabled) { NS_ENSURE_ARG_POINTER(aEnabled); *aEnabled = PR_TRUE; return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsDocShell::SetEnabled(PRBool aEnabled) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsDocShell::GetBlurSuppression(PRBool *aBlurSuppression) { NS_ENSURE_ARG_POINTER(aBlurSuppression); *aBlurSuppression = PR_FALSE; return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsDocShell::SetBlurSuppression(PRBool aBlurSuppression) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsDocShell::GetMainWidget(nsIWidget ** aMainWidget) { // We don't create our own widget, so simply return the parent one. return GetParentWidget(aMainWidget); } NS_IMETHODIMP nsDocShell::SetFocus() { #ifdef DEBUG_DOCSHELL_FOCUS printf("nsDocShell::SetFocus %p\n", (void*)this); #endif // Tell itself (and the DocShellFocusController) who has focus // this way focus gets removed from the currently focused DocShell SetHasFocus(PR_TRUE); return NS_OK; } NS_IMETHODIMP nsDocShell::GetTitle(PRUnichar ** aTitle) { NS_ENSURE_ARG_POINTER(aTitle); *aTitle = ToNewUnicode(mTitle); return NS_OK; } NS_IMETHODIMP nsDocShell::SetTitle(const PRUnichar * aTitle) { // Store local title mTitle = aTitle; nsCOMPtr parent; GetSameTypeParent(getter_AddRefs(parent)); // When title is set on the top object it should then be passed to the // tree owner. if (!parent) { nsCOMPtr treeOwnerAsWin(do_QueryInterface(mTreeOwner)); if (treeOwnerAsWin) treeOwnerAsWin->SetTitle(aTitle); } if (mGlobalHistory && mCurrentURI && mLoadType != LOAD_ERROR_PAGE) { mGlobalHistory->SetPageTitle(mCurrentURI, nsDependentString(aTitle)); } // Update SessionHistory with the document's title. If the // page was loaded from history or the page bypassed history, // there is no need to update the title. There is no need to // go to mSessionHistory to update the title. Setting it in mOSHE // would suffice. if (mOSHE && (mLoadType != LOAD_BYPASS_HISTORY) && (mLoadType != LOAD_HISTORY) && (mLoadType != LOAD_ERROR_PAGE)) { mOSHE->SetTitle(mTitle); } return NS_OK; } //***************************************************************************** // nsDocShell::nsIScrollable //***************************************************************************** NS_IMETHODIMP nsDocShell::GetCurScrollPos(PRInt32 scrollOrientation, PRInt32 * curPos) { NS_ENSURE_ARG_POINTER(curPos); nsIScrollableView* scrollView; NS_ENSURE_SUCCESS(GetRootScrollableView(&scrollView), NS_ERROR_FAILURE); if (!scrollView) { return NS_ERROR_FAILURE; } nscoord x, y; NS_ENSURE_SUCCESS(scrollView->GetScrollPosition(x, y), NS_ERROR_FAILURE); switch (scrollOrientation) { case ScrollOrientation_X: *curPos = x; return NS_OK; case ScrollOrientation_Y: *curPos = y; return NS_OK; default: NS_ENSURE_TRUE(PR_FALSE, NS_ERROR_INVALID_ARG); } return NS_ERROR_FAILURE; } NS_IMETHODIMP nsDocShell::SetCurScrollPos(PRInt32 scrollOrientation, PRInt32 curPos) { nsIScrollableView* scrollView; NS_ENSURE_SUCCESS(GetRootScrollableView(&scrollView), NS_ERROR_FAILURE); if (!scrollView) { return NS_ERROR_FAILURE; } PRInt32 other; PRInt32 x; PRInt32 y; GetCurScrollPos(scrollOrientation, &other); switch (scrollOrientation) { case ScrollOrientation_X: x = curPos; y = other; break; case ScrollOrientation_Y: x = other; y = curPos; break; default: NS_ENSURE_TRUE(PR_FALSE, NS_ERROR_INVALID_ARG); x = 0; y = 0; // fix compiler warning, not actually executed } NS_ENSURE_SUCCESS(scrollView->ScrollTo(x, y, NS_VMREFRESH_IMMEDIATE), NS_ERROR_FAILURE); return NS_OK; } NS_IMETHODIMP nsDocShell::SetCurScrollPosEx(PRInt32 curHorizontalPos, PRInt32 curVerticalPos) { nsIScrollableView* scrollView; NS_ENSURE_SUCCESS(GetRootScrollableView(&scrollView), NS_ERROR_FAILURE); if (!scrollView) { return NS_ERROR_FAILURE; } NS_ENSURE_SUCCESS(scrollView->ScrollTo(curHorizontalPos, curVerticalPos, NS_VMREFRESH_IMMEDIATE), NS_ERROR_FAILURE); return NS_OK; } // XXX This is wrong NS_IMETHODIMP nsDocShell::GetScrollRange(PRInt32 scrollOrientation, PRInt32 * minPos, PRInt32 * maxPos) { NS_ENSURE_ARG_POINTER(minPos && maxPos); nsIScrollableView* scrollView; NS_ENSURE_SUCCESS(GetRootScrollableView(&scrollView), NS_ERROR_FAILURE); if (!scrollView) { return NS_ERROR_FAILURE; } PRInt32 cx; PRInt32 cy; NS_ENSURE_SUCCESS(scrollView->GetContainerSize(&cx, &cy), NS_ERROR_FAILURE); *minPos = 0; switch (scrollOrientation) { case ScrollOrientation_X: *maxPos = cx; return NS_OK; case ScrollOrientation_Y: *maxPos = cy; return NS_OK; default: NS_ENSURE_TRUE(PR_FALSE, NS_ERROR_INVALID_ARG); } return NS_ERROR_FAILURE; } NS_IMETHODIMP nsDocShell::SetScrollRange(PRInt32 scrollOrientation, PRInt32 minPos, PRInt32 maxPos) { //XXX First Check /* Retrieves or Sets the valid ranges for the thumb. When maxPos is set to something less than the current thumb position, curPos is set = to maxPos. @return NS_OK - Setting or Getting completed successfully. NS_ERROR_INVALID_ARG - returned when curPos is not within the minPos and maxPos. */ return NS_ERROR_FAILURE; } NS_IMETHODIMP nsDocShell::SetScrollRangeEx(PRInt32 minHorizontalPos, PRInt32 maxHorizontalPos, PRInt32 minVerticalPos, PRInt32 maxVerticalPos) { //XXX First Check /* Retrieves or Sets the valid ranges for the thumb. When maxPos is set to something less than the current thumb position, curPos is set = to maxPos. @return NS_OK - Setting or Getting completed successfully. NS_ERROR_INVALID_ARG - returned when curPos is not within the minPos and maxPos. */ return NS_ERROR_FAILURE; } // This returns setting for all documents in this webshell NS_IMETHODIMP nsDocShell::GetDefaultScrollbarPreferences(PRInt32 scrollOrientation, PRInt32 * scrollbarPref) { NS_ENSURE_ARG_POINTER(scrollbarPref); switch (scrollOrientation) { case ScrollOrientation_X: *scrollbarPref = mDefaultScrollbarPref.x; return NS_OK; case ScrollOrientation_Y: *scrollbarPref = mDefaultScrollbarPref.y; return NS_OK; default: NS_ENSURE_TRUE(PR_FALSE, NS_ERROR_INVALID_ARG); } return NS_ERROR_FAILURE; } // Set scrolling preference for all documents in this shell // // There are three possible values stored in the shell: // 1) nsIScrollable::Scrollbar_Never = no scrollbar // 2) nsIScrollable::Scrollbar_Auto = scrollbar appears if the document // being displayed would normally have scrollbar // 3) nsIScrollable::Scrollbar_Always = scrollbar always appears // // One important client is nsHTMLFrameInnerFrame::CreateWebShell() NS_IMETHODIMP nsDocShell::SetDefaultScrollbarPreferences(PRInt32 scrollOrientation, PRInt32 scrollbarPref) { switch (scrollOrientation) { case ScrollOrientation_X: mDefaultScrollbarPref.x = scrollbarPref; return NS_OK; case ScrollOrientation_Y: mDefaultScrollbarPref.y = scrollbarPref; return NS_OK; default: NS_ENSURE_TRUE(PR_FALSE, NS_ERROR_INVALID_ARG); } return NS_ERROR_FAILURE; } NS_IMETHODIMP nsDocShell::GetScrollbarVisibility(PRBool * verticalVisible, PRBool * horizontalVisible) { nsIScrollableView* scrollView; NS_ENSURE_SUCCESS(GetRootScrollableView(&scrollView), NS_ERROR_FAILURE); if (!scrollView) return NS_ERROR_FAILURE; // We should now call nsLayoutUtils::GetScrollableFrameFor, // but we can't because of stupid linkage! nsIFrame* scrollFrame = static_cast(scrollView->View()->GetParent()->GetClientData()); if (!scrollFrame) return NS_ERROR_FAILURE; nsIScrollableFrame* scrollable = nsnull; CallQueryInterface(scrollFrame, &scrollable); if (!scrollable) return NS_ERROR_FAILURE; nsMargin scrollbars = scrollable->GetActualScrollbarSizes(); if (verticalVisible) *verticalVisible = scrollbars.left != 0 || scrollbars.right != 0; if (horizontalVisible) *horizontalVisible = scrollbars.top != 0 || scrollbars.bottom != 0; return NS_OK; } //***************************************************************************** // nsDocShell::nsITextScroll //***************************************************************************** NS_IMETHODIMP nsDocShell::ScrollByLines(PRInt32 numLines) { nsIScrollableView* scrollView; NS_ENSURE_SUCCESS(GetRootScrollableView(&scrollView), NS_ERROR_FAILURE); if (!scrollView) { return NS_ERROR_FAILURE; } NS_ENSURE_SUCCESS(scrollView->ScrollByLines(0, numLines), NS_ERROR_FAILURE); return NS_OK; } NS_IMETHODIMP nsDocShell::ScrollByPages(PRInt32 numPages) { nsIScrollableView* scrollView; NS_ENSURE_SUCCESS(GetRootScrollableView(&scrollView), NS_ERROR_FAILURE); if (!scrollView) { return NS_ERROR_FAILURE; } NS_ENSURE_SUCCESS(scrollView->ScrollByPages(0, numPages), NS_ERROR_FAILURE); return NS_OK; } //***************************************************************************** // nsDocShell::nsIScriptGlobalObjectOwner //***************************************************************************** nsIScriptGlobalObject* nsDocShell::GetScriptGlobalObject() { NS_ENSURE_SUCCESS(EnsureScriptEnvironment(), nsnull); return mScriptGlobal; } //***************************************************************************** // nsDocShell::nsIRefreshURI //***************************************************************************** NS_IMETHODIMP nsDocShell::RefreshURI(nsIURI * aURI, PRInt32 aDelay, PRBool aRepeat, PRBool aMetaRefresh) { NS_ENSURE_ARG(aURI); /* Check if Meta refresh/redirects are permitted. Some * embedded applications may not want to do this. * Must do this before sending out NOTIFY_REFRESH events * because listeners may have side effects (e.g. displaying a * button to manually trigger the refresh later). */ PRBool allowRedirects = PR_TRUE; GetAllowMetaRedirects(&allowRedirects); if (!allowRedirects) return NS_OK; // If any web progress listeners are listening for NOTIFY_REFRESH events, // give them a chance to block this refresh. PRBool sameURI; nsresult rv = aURI->Equals(mCurrentURI, &sameURI); if (NS_FAILED(rv)) sameURI = PR_FALSE; if (!RefreshAttempted(this, aURI, aDelay, sameURI)) return NS_OK; nsRefreshTimer *refreshTimer = new nsRefreshTimer(); NS_ENSURE_TRUE(refreshTimer, NS_ERROR_OUT_OF_MEMORY); PRUint32 busyFlags = 0; GetBusyFlags(&busyFlags); nsCOMPtr dataRef = refreshTimer; // Get the ref count to 1 refreshTimer->mDocShell = this; refreshTimer->mURI = aURI; refreshTimer->mDelay = aDelay; refreshTimer->mRepeat = aRepeat; refreshTimer->mMetaRefresh = aMetaRefresh; if (!mRefreshURIList) { NS_ENSURE_SUCCESS(NS_NewISupportsArray(getter_AddRefs(mRefreshURIList)), NS_ERROR_FAILURE); } if (busyFlags & BUSY_FLAGS_BUSY) { // We are busy loading another page. Don't create the // timer right now. Instead queue up the request and trigger the // timer in EndPageLoad(). mRefreshURIList->AppendElement(refreshTimer); } else { // There is no page loading going on right now. Create the // timer and fire it right away. nsCOMPtr timer = do_CreateInstance("@mozilla.org/timer;1"); NS_ENSURE_TRUE(timer, NS_ERROR_FAILURE); mRefreshURIList->AppendElement(timer); // owning timer ref timer->InitWithCallback(refreshTimer, aDelay, nsITimer::TYPE_ONE_SHOT); } return NS_OK; } nsresult nsDocShell::ForceRefreshURIFromTimer(nsIURI * aURI, PRInt32 aDelay, PRBool aMetaRefresh, nsITimer* aTimer) { NS_PRECONDITION(aTimer, "Must have a timer here"); // Remove aTimer from mRefreshURIList if needed if (mRefreshURIList) { PRUint32 n = 0; mRefreshURIList->Count(&n); for (PRUint32 i = 0; i < n; ++i) { nsCOMPtr timer = do_QueryElementAt(mRefreshURIList, i); if (timer == aTimer) { mRefreshURIList->RemoveElementAt(i); break; } } } return ForceRefreshURI(aURI, aDelay, aMetaRefresh); } NS_IMETHODIMP nsDocShell::ForceRefreshURI(nsIURI * aURI, PRInt32 aDelay, PRBool aMetaRefresh) { NS_ENSURE_ARG(aURI); nsCOMPtr loadInfo; CreateLoadInfo(getter_AddRefs(loadInfo)); NS_ENSURE_TRUE(loadInfo, NS_ERROR_OUT_OF_MEMORY); /* We do need to pass in a referrer, but we don't want it to * be sent to the server. */ loadInfo->SetSendReferrer(PR_FALSE); /* for most refreshes the current URI is an appropriate * internal referrer */ loadInfo->SetReferrer(mCurrentURI); /* Check if this META refresh causes a redirection * to another site. */ PRBool equalUri = PR_FALSE; nsresult rv = aURI->Equals(mCurrentURI, &equalUri); if (NS_SUCCEEDED(rv) && (!equalUri) && aMetaRefresh) { /* It is a META refresh based redirection. Now check if it happened within the threshold time we have in mind(15000 ms as defined by REFRESH_REDIRECT_TIMER). If so, pass a REPLACE flag to LoadURI(). */ if (aDelay <= REFRESH_REDIRECT_TIMER) { loadInfo->SetLoadType(nsIDocShellLoadInfo::loadNormalReplace); /* for redirects we mimic HTTP, which passes the * original referrer */ nsCOMPtr internalReferrer; GetReferringURI(getter_AddRefs(internalReferrer)); if (internalReferrer) { loadInfo->SetReferrer(internalReferrer); } } else loadInfo->SetLoadType(nsIDocShellLoadInfo::loadRefresh); /* * LoadURI(...) will cancel all refresh timers... This causes the * Timer and its refreshData instance to be released... */ LoadURI(aURI, loadInfo, nsIWebNavigation::LOAD_FLAGS_NONE, PR_TRUE); return NS_OK; } else loadInfo->SetLoadType(nsIDocShellLoadInfo::loadRefresh); LoadURI(aURI, loadInfo, nsIWebNavigation::LOAD_FLAGS_NONE, PR_TRUE); return NS_OK; } nsresult nsDocShell::SetupRefreshURIFromHeader(nsIURI * aBaseURI, const nsACString & aHeader) { // Refresh headers are parsed with the following format in mind // // By the time we are here, the following is true: // header = "REFRESH" // content = "5; URL=http://uri" // note the URL attribute is // optional, if it is absent, the currently loaded url is used. // Also note that the seconds and URL separator can be either // a ';' or a ','. The ',' separator should be illegal but CNN // is using it. // // We need to handle the following strings, where // - X is a set of digits // - URI is either a relative or absolute URI // // Note that URI should start with "url=" but we allow omission // // "" || ";" || "," // empty string. use the currently loaded URI // and refresh immediately. // "X" || "X;" || "X," // Refresh the currently loaded URI in X seconds. // "X; URI" || "X, URI" // Refresh using URI as the destination in X seconds. // "URI" || "; URI" || ", URI" // Refresh immediately using URI as the destination. // // Currently, anything immediately following the URI, if // separated by any char in the set "'\"\t\r\n " will be // ignored. So "10; url=go.html ; foo=bar" will work, // and so will "10; url='go.html'; foo=bar". However, // "10; url=go.html; foo=bar" will result in the uri // "go.html;" since ';' and ',' are valid uri characters. // // Note that we need to remove any tokens wrapping the URI. // These tokens currently include spaces, double and single // quotes. // when done, seconds is 0 or the given number of seconds // uriAttrib is empty or the URI specified nsCAutoString uriAttrib; PRInt32 seconds = 0; PRBool specifiesSeconds = PR_FALSE; nsACString::const_iterator iter, tokenStart, doneIterating; aHeader.BeginReading(iter); aHeader.EndReading(doneIterating); // skip leading whitespace while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter)) ++iter; tokenStart = iter; // skip leading + and - if (iter != doneIterating && (*iter == '-' || *iter == '+')) ++iter; // parse number while (iter != doneIterating && (*iter >= '0' && *iter <= '9')) { seconds = seconds * 10 + (*iter - '0'); specifiesSeconds = PR_TRUE; ++iter; } if (iter != doneIterating) { // if we started with a '-', number is negative if (*tokenStart == '-') seconds = -seconds; // skip to next ';' or ',' nsACString::const_iterator iterAfterDigit = iter; while (iter != doneIterating && !(*iter == ';' || *iter == ',')) { if (specifiesSeconds) { // Non-whitespace characters here mean that the string is // malformed but tolerate sites that specify a decimal point, // even though meta refresh only works on whole seconds. if (iter == iterAfterDigit && !nsCRT::IsAsciiSpace(*iter) && *iter != '.') { // The characters between the seconds and the next // section are just garbage! // e.g. content="2a0z+,URL=http://www.mozilla.org/" // Just ignore this redirect. return NS_ERROR_FAILURE; } else if (nsCRT::IsAsciiSpace(*iter)) { // We've had at least one whitespace so tolerate the mistake // and drop through. // e.g. content="10 foo" ++iter; break; } } ++iter; } // skip any remaining whitespace while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter)) ++iter; // skip ';' or ',' if (iter != doneIterating && (*iter == ';' || *iter == ',')) { ++iter; } // skip whitespace while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter)) ++iter; } // possible start of URI tokenStart = iter; // skip "url = " to real start of URI if (iter != doneIterating && (*iter == 'u' || *iter == 'U')) { ++iter; if (iter != doneIterating && (*iter == 'r' || *iter == 'R')) { ++iter; if (iter != doneIterating && (*iter == 'l' || *iter == 'L')) { ++iter; // skip whitespace while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter)) ++iter; if (iter != doneIterating && *iter == '=') { ++iter; // skip whitespace while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter)) ++iter; // found real start of URI tokenStart = iter; } } } } // skip a leading '"' or '\''. PRBool isQuotedURI = PR_FALSE; if (tokenStart != doneIterating && (*tokenStart == '"' || *tokenStart == '\'')) { isQuotedURI = PR_TRUE; ++tokenStart; } // set iter to start of URI iter = tokenStart; // tokenStart here points to the beginning of URI // grab the rest of the URI while (iter != doneIterating) { if (isQuotedURI && (*iter == '"' || *iter == '\'')) break; ++iter; } // move iter one back if the last character is a '"' or '\'' if (iter != tokenStart && isQuotedURI) { --iter; if (!(*iter == '"' || *iter == '\'')) ++iter; } // URI is whatever's contained from tokenStart to iter. // note: if tokenStart == doneIterating, so is iter. nsresult rv = NS_OK; nsCOMPtr uri; PRBool specifiesURI = PR_FALSE; if (tokenStart == iter) { uri = aBaseURI; } else { uriAttrib = Substring(tokenStart, iter); // NS_NewURI takes care of any whitespace surrounding the URL rv = NS_NewURI(getter_AddRefs(uri), uriAttrib, nsnull, aBaseURI); specifiesURI = PR_TRUE; } // No URI or seconds were specified if (!specifiesSeconds && !specifiesURI) { // Do nothing because the alternative is to spin around in a refresh // loop forever! return NS_ERROR_FAILURE; } if (NS_SUCCEEDED(rv)) { nsCOMPtr securityManager(do_GetService (NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv)); if (NS_SUCCEEDED(rv)) { rv = securityManager-> CheckLoadURI(aBaseURI, uri, nsIScriptSecurityManager:: LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT); if (NS_SUCCEEDED(rv)) { // Since we can't travel back in time yet, just pretend // negative numbers do nothing at all. if (seconds < 0) return NS_ERROR_FAILURE; rv = RefreshURI(uri, seconds * 1000, PR_FALSE, PR_TRUE); } } } return rv; } NS_IMETHODIMP nsDocShell::SetupRefreshURI(nsIChannel * aChannel) { nsresult rv; nsCOMPtr httpChannel(do_QueryInterface(aChannel, &rv)); if (NS_SUCCEEDED(rv)) { nsCAutoString refreshHeader; rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("refresh"), refreshHeader); if (!refreshHeader.IsEmpty()) { SetupReferrerFromChannel(aChannel); rv = SetupRefreshURIFromHeader(mCurrentURI, refreshHeader); if (NS_SUCCEEDED(rv)) { return NS_REFRESHURI_HEADER_FOUND; } } } return rv; } static void DoCancelRefreshURITimers(nsISupportsArray* aTimerList) { if (!aTimerList) return; PRUint32 n=0; aTimerList->Count(&n); while (n) { nsCOMPtr timer(do_QueryElementAt(aTimerList, --n)); aTimerList->RemoveElementAt(n); // bye bye owning timer ref if (timer) timer->Cancel(); } } NS_IMETHODIMP nsDocShell::CancelRefreshURITimers() { DoCancelRefreshURITimers(mRefreshURIList); DoCancelRefreshURITimers(mSavedRefreshURIList); mRefreshURIList = nsnull; mSavedRefreshURIList = nsnull; return NS_OK; } NS_IMETHODIMP nsDocShell::GetRefreshPending(PRBool* _retval) { if (!mRefreshURIList) { *_retval = PR_FALSE; return NS_OK; } PRUint32 count; nsresult rv = mRefreshURIList->Count(&count); if (NS_SUCCEEDED(rv)) *_retval = (count != 0); return rv; } NS_IMETHODIMP nsDocShell::SuspendRefreshURIs() { if (mRefreshURIList) { PRUint32 n = 0; mRefreshURIList->Count(&n); for (PRUint32 i = 0; i < n; ++i) { nsCOMPtr timer = do_QueryElementAt(mRefreshURIList, i); if (!timer) continue; // this must be a nsRefreshURI already // Replace this timer object with a nsRefreshTimer object. nsCOMPtr callback; timer->GetCallback(getter_AddRefs(callback)); timer->Cancel(); nsCOMPtr rt = do_QueryInterface(callback); NS_ASSERTION(rt, "RefreshURIList timer callbacks should only be RefreshTimer objects"); mRefreshURIList->ReplaceElementAt(rt, i); } } // Suspend refresh URIs for our child shells as well. PRInt32 n = mChildList.Count(); for (PRInt32 i = 0; i < n; ++i) { nsCOMPtr shell = do_QueryInterface(ChildAt(i)); if (shell) shell->SuspendRefreshURIs(); } return NS_OK; } NS_IMETHODIMP nsDocShell::ResumeRefreshURIs() { RefreshURIFromQueue(); // Resume refresh URIs for our child shells as well. PRInt32 n = mChildList.Count(); for (PRInt32 i = 0; i < n; ++i) { nsCOMPtr shell = do_QueryInterface(ChildAt(i)); if (shell) shell->ResumeRefreshURIs(); } return NS_OK; } nsresult nsDocShell::RefreshURIFromQueue() { if (!mRefreshURIList) return NS_OK; PRUint32 n = 0; mRefreshURIList->Count(&n); while (n) { nsCOMPtr element; mRefreshURIList->GetElementAt(--n, getter_AddRefs(element)); nsCOMPtr refreshInfo(do_QueryInterface(element)); if (refreshInfo) { // This is the nsRefreshTimer object, waiting to be // setup in a timer object and fired. // Create the timer and trigger it. PRUint32 delay = static_cast(static_cast(refreshInfo))->GetDelay(); nsCOMPtr timer = do_CreateInstance("@mozilla.org/timer;1"); if (timer) { // Replace the nsRefreshTimer element in the queue with // its corresponding timer object, so that in case another // load comes through before the timer can go off, the timer will // get cancelled in CancelRefreshURITimer() mRefreshURIList->ReplaceElementAt(timer, n); timer->InitWithCallback(refreshInfo, delay, nsITimer::TYPE_ONE_SHOT); } } } // while return NS_OK; } //***************************************************************************** // nsDocShell::nsIContentViewerContainer //***************************************************************************** NS_IMETHODIMP nsDocShell::Embed(nsIContentViewer * aContentViewer, const char *aCommand, nsISupports * aExtraInfo) { // Save the LayoutHistoryState of the previous document, before // setting up new document PersistLayoutHistoryState(); nsresult rv = SetupNewViewer(aContentViewer); // If we are loading a wyciwyg url from history, change the base URI for // the document to the original http url that created the document.write(). // This makes sure that all relative urls in a document.written page loaded // via history work properly. if (mCurrentURI && (mLoadType & LOAD_CMD_HISTORY || mLoadType == LOAD_RELOAD_NORMAL || mLoadType == LOAD_RELOAD_CHARSET_CHANGE)){ PRBool isWyciwyg = PR_FALSE; // Check if the url is wyciwyg rv = mCurrentURI->SchemeIs("wyciwyg", &isWyciwyg); if (isWyciwyg && NS_SUCCEEDED(rv)) SetBaseUrlForWyciwyg(aContentViewer); } // XXX What if SetupNewViewer fails? if (mLSHE) { // Restore the editing state, if it's stored in session history. if (mLSHE->HasDetachedEditor()) { ReattachEditorToWindow(mLSHE); } SetHistoryEntry(&mOSHE, mLSHE); } PRBool updateHistory = PR_TRUE; // Determine if this type of load should update history switch (mLoadType) { case LOAD_NORMAL_REPLACE: case LOAD_STOP_CONTENT_AND_REPLACE: case LOAD_RELOAD_BYPASS_CACHE: case LOAD_RELOAD_BYPASS_PROXY: case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE: updateHistory = PR_FALSE; break; default: break; } if (!updateHistory) SetLayoutHistoryState(nsnull); return NS_OK; } /* void setIsPrinting (in boolean aIsPrinting); */ NS_IMETHODIMP nsDocShell::SetIsPrinting(PRBool aIsPrinting) { mIsPrintingOrPP = aIsPrinting; return NS_OK; } //***************************************************************************** // nsDocShell::nsIWebProgressListener //***************************************************************************** NS_IMETHODIMP nsDocShell::OnProgressChange(nsIWebProgress * aProgress, nsIRequest * aRequest, PRInt32 aCurSelfProgress, PRInt32 aMaxSelfProgress, PRInt32 aCurTotalProgress, PRInt32 aMaxTotalProgress) { return NS_OK; } NS_IMETHODIMP nsDocShell::OnStateChange(nsIWebProgress * aProgress, nsIRequest * aRequest, PRUint32 aStateFlags, nsresult aStatus) { nsresult rv; // Update the busy cursor if ((~aStateFlags & (STATE_START | STATE_IS_NETWORK)) == 0) { nsCOMPtr wcwgChannel(do_QueryInterface(aRequest)); nsCOMPtr webProgress = do_QueryInterface(GetAsSupports(this)); // Was the wyciwyg document loaded on this docshell? if (wcwgChannel && !mLSHE && (mItemType == typeContent) && aProgress == webProgress.get()) { nsCOMPtr uri; wcwgChannel->GetURI(getter_AddRefs(uri)); PRBool equalUri = PR_TRUE; // Store the wyciwyg url in session history, only if it is // being loaded fresh for the first time. We don't want // multiple entries for successive loads if (mCurrentURI && NS_SUCCEEDED(uri->Equals(mCurrentURI, &equalUri)) && !equalUri) { // This is a document.write(). Get the made-up url // from the channel and store it in session history. rv = AddToSessionHistory(uri, wcwgChannel, getter_AddRefs(mLSHE)); SetCurrentURI(uri, aRequest, PR_TRUE); // Save history state of the previous page rv = PersistLayoutHistoryState(); if (mOSHE) SetHistoryEntry(&mOSHE, mLSHE); } } // Page has begun to load mBusyFlags = BUSY_FLAGS_BUSY | BUSY_FLAGS_BEFORE_PAGE_LOAD; nsCOMPtr mainWidget; GetMainWidget(getter_AddRefs(mainWidget)); if (mainWidget) { mainWidget->SetCursor(eCursor_spinning); } } else if ((~aStateFlags & (STATE_TRANSFERRING | STATE_IS_DOCUMENT)) == 0) { // Page is loading mBusyFlags = BUSY_FLAGS_BUSY | BUSY_FLAGS_PAGE_LOADING; } else if ((aStateFlags & STATE_STOP) && (aStateFlags & STATE_IS_NETWORK)) { // Page has finished loading mBusyFlags = BUSY_FLAGS_NONE; nsCOMPtr mainWidget; GetMainWidget(getter_AddRefs(mainWidget)); if (mainWidget) { mainWidget->SetCursor(eCursor_standard); } } if ((~aStateFlags & (STATE_IS_DOCUMENT | STATE_STOP)) == 0) { nsCOMPtr webProgress = do_QueryInterface(GetAsSupports(this)); // Is the document stop notification for this document? if (aProgress == webProgress.get()) { nsCOMPtr channel(do_QueryInterface(aRequest)); EndPageLoad(aProgress, channel, aStatus); } } // note that redirect state changes will go through here as well, but it // is better to handle those in OnRedirectStateChange where more // information is available. return NS_OK; } NS_IMETHODIMP nsDocShell::OnLocationChange(nsIWebProgress * aProgress, nsIRequest * aRequest, nsIURI * aURI) { NS_NOTREACHED("notification excluded in AddProgressListener(...)"); return NS_OK; } void nsDocShell::OnRedirectStateChange(nsIChannel* aOldChannel, nsIChannel* aNewChannel, PRUint32 aRedirectFlags, PRUint32 aStateFlags) { NS_ASSERTION(aStateFlags & STATE_REDIRECTING, "Calling OnRedirectStateChange when there is no redirect"); if (!(aStateFlags & STATE_IS_DOCUMENT)) return; // not a toplevel document // If this load is being checked by the URI classifier, we need to // query the classifier again for the new URI. if (mClassifier) { mClassifier->OnRedirect(aOldChannel, aNewChannel); } nsCOMPtr history3(do_QueryInterface(mGlobalHistory)); nsresult result = NS_ERROR_NOT_IMPLEMENTED; if (history3) { // notify global history of this redirect result = history3->AddDocumentRedirect(aOldChannel, aNewChannel, aRedirectFlags, !IsFrame()); } if (result == NS_ERROR_NOT_IMPLEMENTED) { // when there is no GlobalHistory3, or it doesn't implement // AddToplevelRedirect, we fall back to GlobalHistory2. Just notify // that the redirecting page was a redirect so it will be link colored // but not visible. nsCOMPtr oldURI; aOldChannel->GetURI(getter_AddRefs(oldURI)); if (! oldURI) return; // nothing to tell anybody about AddToGlobalHistory(oldURI, PR_TRUE, aOldChannel); } } NS_IMETHODIMP nsDocShell::OnStatusChange(nsIWebProgress * aWebProgress, nsIRequest * aRequest, nsresult aStatus, const PRUnichar * aMessage) { NS_NOTREACHED("notification excluded in AddProgressListener(...)"); return NS_OK; } NS_IMETHODIMP nsDocShell::OnSecurityChange(nsIWebProgress * aWebProgress, nsIRequest * aRequest, PRUint32 state) { NS_NOTREACHED("notification excluded in AddProgressListener(...)"); return NS_OK; } nsresult nsDocShell::EndPageLoad(nsIWebProgress * aProgress, nsIChannel * aChannel, nsresult aStatus) { // // one of many safeguards that prevent death and destruction if // someone is so very very rude as to bring this window down // during this load handler. // nsCOMPtr kungFuDeathGrip(this); // We're done with the URI classifier for this channel mClassifier = nsnull; // // Notify the ContentViewer that the Document has finished loading... // // This will cause any OnLoad(...) handlers to fire, if it is a HTML // document... // if (!mEODForCurrentDocument && mContentViewer) { mIsExecutingOnLoadHandler = PR_TRUE; mContentViewer->LoadComplete(aStatus); mIsExecutingOnLoadHandler = PR_FALSE; mEODForCurrentDocument = PR_TRUE; // If all documents have completed their loading // favor native event dispatch priorities // over performance if (--gNumberOfDocumentsLoading == 0) { // Hint to use normal native event dispatch priorities FavorPerformanceHint(PR_FALSE, NS_EVENT_STARVATION_DELAY_HINT); } } /* Check if the httpChannel has any cache-control related response headers, * like no-store, no-cache. If so, update SHEntry so that * when a user goes back/forward to this page, we appropriately do * form value restoration or load from server. */ nsCOMPtr httpChannel(do_QueryInterface(aChannel)); if (!httpChannel) // HttpChannel could be hiding underneath a Multipart channel. GetHttpChannel(aChannel, getter_AddRefs(httpChannel)); if (httpChannel) { // figure out if SH should be saving layout state. PRBool discardLayoutState = ShouldDiscardLayoutState(httpChannel); if (mLSHE && discardLayoutState && (mLoadType & LOAD_CMD_NORMAL) && (mLoadType != LOAD_BYPASS_HISTORY) && (mLoadType != LOAD_ERROR_PAGE)) mLSHE->SetSaveLayoutStateFlag(PR_FALSE); } // Clear mLSHE after calling the onLoadHandlers. This way, if the // onLoadHandler tries to load something different in // itself or one of its children, we can deal with it appropriately. if (mLSHE) { mLSHE->SetLoadType(nsIDocShellLoadInfo::loadHistory); // Clear the mLSHE reference to indicate document loading is done one // way or another. SetHistoryEntry(&mLSHE, nsnull); } // if there's a refresh header in the channel, this method // will set it up for us. RefreshURIFromQueue(); return NS_OK; } //***************************************************************************** // nsDocShell: Content Viewer Management //***************************************************************************** NS_IMETHODIMP nsDocShell::EnsureContentViewer() { if (mContentViewer) return NS_OK; if (mIsBeingDestroyed) return NS_ERROR_FAILURE; nsIPrincipal* principal = nsnull; nsCOMPtr piDOMWindow(do_QueryInterface(mScriptGlobal)); if (piDOMWindow) { principal = piDOMWindow->GetOpenerScriptPrincipal(); } if (!principal) { principal = GetInheritedPrincipal(PR_FALSE); } nsresult rv = CreateAboutBlankContentViewer(principal); if (NS_SUCCEEDED(rv)) { nsCOMPtr domDoc; mContentViewer->GetDOMDocument(getter_AddRefs(domDoc)); nsCOMPtr doc(do_QueryInterface(domDoc)); NS_ASSERTION(doc, "Should have doc if CreateAboutBlankContentViewer " "succeeded!"); doc->SetIsInitialDocument(PR_TRUE); } return rv; } nsresult nsDocShell::CreateAboutBlankContentViewer(nsIPrincipal* aPrincipal) { nsCOMPtr blankDoc; nsCOMPtr viewer; nsresult rv = NS_ERROR_FAILURE; /* mCreatingDocument should never be true at this point. However, it's a theoretical possibility. We want to know about it and make it stop, and this sounds like a job for an assertion. */ NS_ASSERTION(!mCreatingDocument, "infinite(?) loop creating document averted"); if (mCreatingDocument) return NS_ERROR_FAILURE; mCreatingDocument = PR_TRUE; // mContentViewer->PermitUnload may release |this| docshell. nsCOMPtr kungFuDeathGrip(this); if (mContentViewer) { // We've got a content viewer already. Make sure the user // permits us to discard the current document and replace it // with about:blank. And also ensure we fire the unload events // in the current document. PRBool okToUnload; rv = mContentViewer->PermitUnload(&okToUnload); if (NS_SUCCEEDED(rv) && !okToUnload) { // The user chose not to unload the page, interrupt the load. return NS_ERROR_FAILURE; } mSavingOldViewer = CanSavePresentation(LOAD_NORMAL, nsnull, nsnull); // Make sure to blow away our mLoadingURI just in case. No loads // from inside this pagehide. mLoadingURI = nsnull; // Notify the current document that it is about to be unloaded!! // // It is important to fire the unload() notification *before* any state // is changed within the DocShell - otherwise, javascript will get the // wrong information :-( // (void) FirePageHideNotification(!mSavingOldViewer); } // Now make sure we don't think we're in the middle of firing unload after // this point. This will make us fire unload when the about:blank document // unloads... but that's ok, more or less. Would be nice if it fired load // too, of course. mFiredUnloadEvent = PR_FALSE; // one helper factory, please nsCOMPtr catMan(do_GetService(NS_CATEGORYMANAGER_CONTRACTID)); if (!catMan) return NS_ERROR_FAILURE; nsXPIDLCString contractId; rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", "text/html", getter_Copies(contractId)); if (NS_FAILED(rv)) return rv; nsCOMPtr docFactory(do_GetService(contractId)); if (docFactory) { // generate (about:blank) document to load docFactory->CreateBlankDocument(mLoadGroup, aPrincipal, getter_AddRefs(blankDoc)); if (blankDoc) { blankDoc->SetContainer(static_cast(this)); // create a content viewer for us and the new document docFactory->CreateInstanceForDocument(NS_ISUPPORTS_CAST(nsIDocShell *, this), blankDoc, "view", getter_AddRefs(viewer)); // hook 'em up if (viewer) { viewer->SetContainer(static_cast(this)); nsCOMPtr domdoc(do_QueryInterface(blankDoc)); Embed(viewer, "", 0); viewer->SetDOMDocument(domdoc); SetCurrentURI(blankDoc->GetDocumentURI(), nsnull, PR_TRUE); rv = NS_OK; } } } mCreatingDocument = PR_FALSE; // The transient about:blank viewer doesn't have a session history entry. SetHistoryEntry(&mOSHE, nsnull); return rv; } PRBool nsDocShell::CanSavePresentation(PRUint32 aLoadType, nsIRequest *aNewRequest, nsIDocument *aNewDocument) { if (!mOSHE) return PR_FALSE; // no entry to save into // Only save presentation for "normal" loads and link loads. Anything else // probably wants to refetch the page, so caching the old presentation // would be incorrect. if (aLoadType != LOAD_NORMAL && aLoadType != LOAD_HISTORY && aLoadType != LOAD_LINK && aLoadType != LOAD_STOP_CONTENT && aLoadType != LOAD_STOP_CONTENT_AND_REPLACE && aLoadType != LOAD_ERROR_PAGE) return PR_FALSE; // If the session history entry has the saveLayoutState flag set to false, // then we should not cache the presentation. PRBool canSaveState; mOSHE->GetSaveLayoutStateFlag(&canSaveState); if (canSaveState == PR_FALSE) return PR_FALSE; // If the document is not done loading, don't cache it. nsCOMPtr pWin = do_QueryInterface(mScriptGlobal); if (!pWin || pWin->IsLoading()) return PR_FALSE; if (pWin->WouldReuseInnerWindow(aNewDocument)) return PR_FALSE; // Avoid doing the work of saving the presentation state in the case where // the content viewer cache is disabled. if (nsSHistory::GetMaxTotalViewers() == 0) return PR_FALSE; // Don't cache the content viewer if we're in a subframe and the subframe // pref is disabled. PRBool cacheFrames = PR_FALSE; mPrefs->GetBoolPref("browser.sessionhistory.cache_subframes", &cacheFrames); if (!cacheFrames) { nsCOMPtr root; GetSameTypeParent(getter_AddRefs(root)); if (root && root != this) { return PR_FALSE; // this is a subframe load } } // If the document does not want its presentation cached, then don't. nsCOMPtr doc = do_QueryInterface(pWin->GetExtantDocument()); if (!doc || !doc->CanSavePresentation(aNewRequest)) return PR_FALSE; return PR_TRUE; } void nsDocShell::ReattachEditorToWindow(nsISHEntry *aSHEntry) { NS_ASSERTION(!mEditorData, "Why reattach an editor when we already have one?"); NS_ASSERTION(aSHEntry && aSHEntry->HasDetachedEditor(), "Reattaching when there's not a detached editor."); if (mEditorData || !aSHEntry) return; mEditorData = aSHEntry->ForgetEditorData(); if (mEditorData) { nsresult res = mEditorData->ReattachToWindow(this); NS_ASSERTION(NS_SUCCEEDED(res), "Failed to reattach editing session"); } } void nsDocShell::DetachEditorFromWindow(nsISHEntry *aSHEntry) { if (!mEditorData) return; NS_ASSERTION(!aSHEntry || !aSHEntry->HasDetachedEditor(), "Detaching editor when it's already detached."); nsresult res = mEditorData->DetachFromWindow(); NS_ASSERTION(NS_SUCCEEDED(res), "Failed to detach editor"); if (NS_SUCCEEDED(res)) { // Make aSHEntry hold the owning ref to the editor data. if (aSHEntry) aSHEntry->SetEditorData(mEditorData.forget()); else mEditorData = nsnull; } #ifdef DEBUG { PRBool isEditable; GetEditable(&isEditable); NS_ASSERTION(!isEditable, "Window is still editable after detaching editor."); } #endif // DEBUG } void nsDocShell::DetachEditorFromWindow() { if (mOSHE) DetachEditorFromWindow(mOSHE); } nsresult nsDocShell::CaptureState() { if (!mOSHE || mOSHE == mLSHE) { // No entry to save into, or we're replacing the existing entry. return NS_ERROR_FAILURE; } nsCOMPtr privWin = do_QueryInterface(mScriptGlobal); if (!privWin) return NS_ERROR_FAILURE; nsCOMPtr windowState; nsresult rv = privWin->SaveWindowState(getter_AddRefs(windowState)); NS_ENSURE_SUCCESS(rv, rv); #ifdef DEBUG_PAGE_CACHE nsCOMPtr uri; mOSHE->GetURI(getter_AddRefs(uri)); nsCAutoString spec; if (uri) uri->GetSpec(spec); printf("Saving presentation into session history\n"); printf(" SH URI: %s\n", spec.get()); #endif rv = mOSHE->SetWindowState(windowState); NS_ENSURE_SUCCESS(rv, rv); // Suspend refresh URIs and save off the timer queue rv = mOSHE->SetRefreshURIList(mSavedRefreshURIList); NS_ENSURE_SUCCESS(rv, rv); // Capture the current content viewer bounds. nsCOMPtr shell; nsDocShell::GetPresShell(getter_AddRefs(shell)); if (shell) { nsIViewManager *vm = shell->GetViewManager(); if (vm) { nsIView *rootView = nsnull; vm->GetRootView(rootView); if (rootView) { nsIWidget *widget = rootView->GetWidget(); if (widget) { nsRect bounds(0, 0, 0, 0); widget->GetBounds(bounds); rv = mOSHE->SetViewerBounds(bounds); } } } } // Capture the docshell hierarchy. mOSHE->ClearChildShells(); PRInt32 childCount = mChildList.Count(); for (PRInt32 i = 0; i < childCount; ++i) { nsCOMPtr childShell = do_QueryInterface(ChildAt(i)); NS_ASSERTION(childShell, "null child shell"); mOSHE->AddChildShell(childShell); } return NS_OK; } NS_IMETHODIMP nsDocShell::RestorePresentationEvent::Run() { if (mDocShell && NS_FAILED(mDocShell->RestoreFromHistory())) NS_WARNING("RestoreFromHistory failed"); return NS_OK; } NS_IMETHODIMP nsDocShell::BeginRestore(nsIContentViewer *aContentViewer, PRBool aTop) { nsresult rv; if (!aContentViewer) { rv = EnsureContentViewer(); NS_ENSURE_SUCCESS(rv, rv); aContentViewer = mContentViewer; } // Dispatch events for restoring the presentation. We try to simulate // the progress notifications loading the document would cause, so we add // the document's channel to the loadgroup to initiate stateChange // notifications. nsCOMPtr domDoc; aContentViewer->GetDOMDocument(getter_AddRefs(domDoc)); nsCOMPtr doc = do_QueryInterface(domDoc); if (doc) { nsIChannel *channel = doc->GetChannel(); if (channel) { mEODForCurrentDocument = PR_FALSE; mIsRestoringDocument = PR_TRUE; mLoadGroup->AddRequest(channel, nsnull); mIsRestoringDocument = PR_FALSE; } } if (!aTop) { // This point corresponds to us having gotten OnStartRequest or // STATE_START, so do the same thing that CreateContentViewer does at // this point to ensure that unload/pagehide events for this document // will fire when it's unloaded again. mFiredUnloadEvent = PR_FALSE; // For non-top frames, there is no notion of making sure that the // previous document is in the domwindow when STATE_START notifications // happen. We can just call BeginRestore for all of the child shells // now. rv = BeginRestoreChildren(); NS_ENSURE_SUCCESS(rv, rv); } return NS_OK; } nsresult nsDocShell::BeginRestoreChildren() { PRInt32 n = mChildList.Count(); for (PRInt32 i = 0; i < n; ++i) { nsCOMPtr child = do_QueryInterface(ChildAt(i)); if (child) { nsresult rv = child->BeginRestore(nsnull, PR_FALSE); NS_ENSURE_SUCCESS(rv, rv); } } return NS_OK; } NS_IMETHODIMP nsDocShell::FinishRestore() { // First we call finishRestore() on our children. In the simulated load, // all of the child frames finish loading before the main document. PRInt32 n = mChildList.Count(); for (PRInt32 i = 0; i < n; ++i) { nsCOMPtr child = do_QueryInterface(ChildAt(i)); if (child) { child->FinishRestore(); } } if (mOSHE && mOSHE->HasDetachedEditor()) { ReattachEditorToWindow(mOSHE); } if (mContentViewer) { nsCOMPtr domDoc; mContentViewer->GetDOMDocument(getter_AddRefs(domDoc)); nsCOMPtr doc = do_QueryInterface(domDoc); if (doc) { // Finally, we remove the request from the loadgroup. This will // cause onStateChange(STATE_STOP) to fire, which will fire the // pageshow event to the chrome. nsIChannel *channel = doc->GetChannel(); if (channel) { mIsRestoringDocument = PR_TRUE; mLoadGroup->RemoveRequest(channel, nsnull, NS_OK); mIsRestoringDocument = PR_FALSE; } } } return NS_OK; } NS_IMETHODIMP nsDocShell::GetRestoringDocument(PRBool *aRestoring) { *aRestoring = mIsRestoringDocument; return NS_OK; } nsresult nsDocShell::RestorePresentation(nsISHEntry *aSHEntry, PRBool *aRestoring) { NS_ASSERTION(mLoadType & LOAD_CMD_HISTORY, "RestorePresentation should only be called for history loads"); nsCOMPtr viewer; aSHEntry->GetContentViewer(getter_AddRefs(viewer)); #ifdef DEBUG_PAGE_CACHE nsCOMPtr uri; aSHEntry->GetURI(getter_AddRefs(uri)); nsCAutoString spec; if (uri) uri->GetSpec(spec); #endif *aRestoring = PR_FALSE; if (!viewer) { #ifdef DEBUG_PAGE_CACHE printf("no saved presentation for uri: %s\n", spec.get()); #endif return NS_OK; } // We need to make sure the content viewer's container is this docshell. // In subframe navigation, it's possible for the docshell that the // content viewer was originally loaded into to be replaced with a // different one. We don't currently support restoring the presentation // in that case. nsCOMPtr container; viewer->GetContainer(getter_AddRefs(container)); if (!::SameCOMIdentity(container, GetAsSupports(this))) { #ifdef DEBUG_PAGE_CACHE printf("No valid container, clearing presentation\n"); #endif aSHEntry->SetContentViewer(nsnull); return NS_ERROR_FAILURE; } NS_ASSERTION(mContentViewer != viewer, "Restoring existing presentation"); #ifdef DEBUG_PAGE_CACHE printf("restoring presentation from session history: %s\n", spec.get()); #endif SetHistoryEntry(&mLSHE, aSHEntry); // Add the request to our load group. We do this before swapping out // the content viewers so that consumers of STATE_START can access // the old document. We only deal with the toplevel load at this time -- // to be consistent with normal document loading, subframes cannot start // loading until after data arrives, which is after STATE_START completes. BeginRestore(viewer, PR_TRUE); // Post an event that will remove the request after we've returned // to the event loop. This mimics the way it is called by nsIChannel // implementations. // Revoke any pending restore (just in case) NS_ASSERTION(!mRestorePresentationEvent.IsPending(), "should only have one RestorePresentationEvent"); mRestorePresentationEvent.Revoke(); nsRefPtr evt = new RestorePresentationEvent(this); nsresult rv = NS_DispatchToCurrentThread(evt); if (NS_SUCCEEDED(rv)) { mRestorePresentationEvent = evt.get(); // The rest of the restore processing will happen on our event // callback. *aRestoring = PR_TRUE; } return rv; } nsresult nsDocShell::RestoreFromHistory() { mRestorePresentationEvent.Forget(); // This section of code follows the same ordering as CreateContentViewer. if (!mLSHE) return NS_ERROR_FAILURE; nsCOMPtr viewer; mLSHE->GetContentViewer(getter_AddRefs(viewer)); if (!viewer) return NS_ERROR_FAILURE; if (mSavingOldViewer) { // We determined that it was safe to cache the document presentation // at the time we initiated the new load. We need to check whether // it's still safe to do so, since there may have been DOM mutations // or new requests initiated. nsCOMPtr domDoc; viewer->GetDOMDocument(getter_AddRefs(domDoc)); nsCOMPtr doc = do_QueryInterface(domDoc); nsIRequest *request = nsnull; if (doc) request = doc->GetChannel(); mSavingOldViewer = CanSavePresentation(mLoadType, request, doc); } nsCOMPtr oldMUDV(do_QueryInterface(mContentViewer)); nsCOMPtr newMUDV(do_QueryInterface(viewer)); float textZoom = 1.0f; float pageZoom = 1.0f; if (oldMUDV && newMUDV) { oldMUDV->GetTextZoom(&textZoom); oldMUDV->GetFullZoom(&pageZoom); } // Protect against mLSHE going away via a load triggered from // pagehide or unload. nsCOMPtr origLSHE = mLSHE; // Make sure to blow away our mLoadingURI just in case. No loads // from inside this pagehide. mLoadingURI = nsnull; // Notify the old content viewer that it's being hidden. FirePageHideNotification(!mSavingOldViewer); // If mLSHE was changed as a result of the pagehide event, then // something else was loaded. Don't finish restoring. if (mLSHE != origLSHE) return NS_OK; // Set mFiredUnloadEvent = PR_FALSE so that the unload handler for the // *new* document will fire. mFiredUnloadEvent = PR_FALSE; mURIResultedInDocument = PR_TRUE; nsCOMPtr rootSH; GetRootSessionHistory(getter_AddRefs(rootSH)); if (rootSH) { nsCOMPtr hist = do_QueryInterface(rootSH); rootSH->GetIndex(&mPreviousTransIndex); hist->UpdateIndex(); rootSH->GetIndex(&mLoadedTransIndex); #ifdef DEBUG_PAGE_CACHE printf("Previous index: %d, Loaded index: %d\n\n", mPreviousTransIndex, mLoadedTransIndex); #endif } // Rather than call Embed(), we will retrieve the viewer from the session // history entry and swap it in. // XXX can we refactor this so that we can just call Embed()? PersistLayoutHistoryState(); nsresult rv; if (mContentViewer) { if (mSavingOldViewer && NS_FAILED(CaptureState())) { if (mOSHE) { mOSHE->SyncPresentationState(); } mSavingOldViewer = PR_FALSE; } } mSavedRefreshURIList = nsnull; // In cases where we use a transient about:blank viewer between loads, // we never show the transient viewer, so _its_ previous viewer is never // unhooked from the view hierarchy. Destroy any such previous viewer now, // before we grab the root view sibling, so that we don't grab a view // that's about to go away. if (mContentViewer) { nsCOMPtr previousViewer; mContentViewer->GetPreviousViewer(getter_AddRefs(previousViewer)); if (previousViewer) { mContentViewer->SetPreviousViewer(nsnull); previousViewer->Destroy(); } } // Save off the root view's parent and sibling so that we can insert the // new content viewer's root view at the same position. Also save the // bounds of the root view's widget. nsIView *rootViewSibling = nsnull, *rootViewParent = nsnull; nsRect newBounds(0, 0, 0, 0); nsCOMPtr oldPresShell; nsDocShell::GetPresShell(getter_AddRefs(oldPresShell)); if (oldPresShell) { nsIViewManager *vm = oldPresShell->GetViewManager(); if (vm) { nsIView *oldRootView = nsnull; vm->GetRootView(oldRootView); if (oldRootView) { rootViewSibling = oldRootView->GetNextSibling(); rootViewParent = oldRootView->GetParent(); nsIWidget *widget = oldRootView->GetWidget(); if (widget) { widget->GetBounds(newBounds); } } } } // Transfer ownership to mContentViewer. By ensuring that either the // docshell or the session history, but not both, have references to the // content viewer, we prevent the viewer from being torn down after // Destroy() is called. if (mContentViewer) { mContentViewer->Close(mSavingOldViewer ? mOSHE.get() : nsnull); viewer->SetPreviousViewer(mContentViewer); } mContentViewer.swap(viewer); viewer = nsnull; // force a release to complete ownership transfer // Grab all of the related presentation from the SHEntry now. // Clearing the viewer from the SHEntry will clear all of this state. nsCOMPtr windowState; mLSHE->GetWindowState(getter_AddRefs(windowState)); mLSHE->SetWindowState(nsnull); PRBool sticky; mLSHE->GetSticky(&sticky); nsCOMPtr domDoc; mContentViewer->GetDOMDocument(getter_AddRefs(domDoc)); nsCOMArray childShells; PRInt32 i = 0; nsCOMPtr child; while (NS_SUCCEEDED(mLSHE->ChildShellAt(i++, getter_AddRefs(child))) && child) { childShells.AppendObject(child); } // get the previous content viewer size nsRect oldBounds(0, 0, 0, 0); mLSHE->GetViewerBounds(oldBounds); // Restore the refresh URI list. The refresh timers will be restarted // when EndPageLoad() is called. nsCOMPtr refreshURIList; mLSHE->GetRefreshURIList(getter_AddRefs(refreshURIList)); // Reattach to the window object. rv = mContentViewer->Open(windowState, mLSHE); // Now remove it from the cached presentation. mLSHE->SetContentViewer(nsnull); mEODForCurrentDocument = PR_FALSE; #ifdef DEBUG { nsCOMPtr refreshURIs; mLSHE->GetRefreshURIList(getter_AddRefs(refreshURIs)); nsCOMPtr childShell; mLSHE->ChildShellAt(0, getter_AddRefs(childShell)); NS_ASSERTION(!refreshURIs && !childShell, "SHEntry should have cleared presentation state"); } #endif // Restore the sticky state of the viewer. The viewer has set this state // on the history entry in Destroy() just before marking itself non-sticky, // to avoid teardown of the presentation. mContentViewer->SetSticky(sticky); // Now that we have switched documents, forget all of our children. DestroyChildren(); NS_ENSURE_SUCCESS(rv, rv); // mLSHE is now our currently-loaded document. SetHistoryEntry(&mOSHE, mLSHE); // XXX special wyciwyg handling in Embed()? // We aren't going to restore any items from the LayoutHistoryState, // but we don't want them to stay around in case the page is reloaded. SetLayoutHistoryState(nsnull); // This is the end of our Embed() replacement mSavingOldViewer = PR_FALSE; mEODForCurrentDocument = PR_FALSE; // Tell the event loop to favor plevents over user events, see comments // in CreateContentViewer. if (++gNumberOfDocumentsLoading == 1) FavorPerformanceHint(PR_TRUE, NS_EVENT_STARVATION_DELAY_HINT); if (oldMUDV && newMUDV) { newMUDV->SetTextZoom(textZoom); newMUDV->SetFullZoom(pageZoom); } nsCOMPtr document = do_QueryInterface(domDoc); if (document) { // Use the uri from the mLSHE we had when we entered this function // (which need not match the document's URI if anchors are involved), // since that's the history entry we're loading. Note that if we use // origLSHE we don't have to worry about whether the entry in question // is still mLSHE or whether it's now mOSHE. nsCOMPtr uri; origLSHE->GetURI(getter_AddRefs(uri)); SetCurrentURI(uri, document->GetChannel(), PR_TRUE); } // This is the end of our CreateContentViewer() replacement. // Now we simulate a load. First, we restore the state of the javascript // window object. nsCOMPtr privWin = do_GetInterface(static_cast(this)); NS_ASSERTION(privWin, "could not get nsPIDOMWindow interface"); rv = privWin->RestoreWindowState(windowState); NS_ENSURE_SUCCESS(rv, rv); // Now, dispatch a title change event which would happen as the // is parsed. document->NotifyPossibleTitleChange(PR_FALSE); // Now we simulate appending child docshells for subframes. for (i = 0; i < childShells.Count(); ++i) { nsIDocShellTreeItem *childItem = childShells.ObjectAt(i); nsCOMPtr childShell = do_QueryInterface(childItem); // Make sure to not clobber the state of the child. Since AddChild // always clobbers it, save it off first. PRBool allowPlugins; childShell->GetAllowPlugins(&allowPlugins); PRBool allowJavascript; childShell->GetAllowJavascript(&allowJavascript); PRBool allowRedirects; childShell->GetAllowMetaRedirects(&allowRedirects); PRBool allowSubframes; childShell->GetAllowSubframes(&allowSubframes); PRBool allowImages; childShell->GetAllowImages(&allowImages); AddChild(childItem); childShell->SetAllowPlugins(allowPlugins); childShell->SetAllowJavascript(allowJavascript); childShell->SetAllowMetaRedirects(allowRedirects); childShell->SetAllowSubframes(allowSubframes); childShell->SetAllowImages(allowImages); rv = childShell->BeginRestore(nsnull, PR_FALSE); NS_ENSURE_SUCCESS(rv, rv); } nsCOMPtr shell; nsDocShell::GetPresShell(getter_AddRefs(shell)); nsIViewManager *newVM = shell ? shell->GetViewManager() : nsnull; nsIView *newRootView = nsnull; if (newVM) newVM->GetRootView(newRootView); // Insert the new root view at the correct location in the view tree. if (rootViewParent) { nsIViewManager *parentVM = rootViewParent->GetViewManager(); if (parentVM && newRootView) { // InsertChild(parent, child, sib, PR_TRUE) inserts the child after // sib in content order, which is before sib in view order. BUT // when sib is null it inserts at the end of the the document // order, i.e., first in view order. But when oldRootSibling is // null, the old root as at the end of the view list --- last in // content order --- and we want to call InsertChild(parent, child, // nsnull, PR_FALSE) in that case. parentVM->InsertChild(rootViewParent, newRootView, rootViewSibling, rootViewSibling ? PR_TRUE : PR_FALSE); NS_ASSERTION(newRootView->GetNextSibling() == rootViewSibling, "error in InsertChild"); } } // Now that all of the child docshells have been put into place, we can // restart the timers for the window and all of the child frames. privWin->ResumeTimeouts(); // Restore the refresh URI list. The refresh timers will be restarted // when EndPageLoad() is called. mRefreshURIList = refreshURIList; // Meta-refresh timers have been restarted for this shell, but not // for our children. Walk the child shells and restart their timers. PRInt32 n = mChildList.Count(); for (i = 0; i < n; ++i) { nsCOMPtr child = do_QueryInterface(ChildAt(i)); if (child) child->ResumeRefreshURIs(); } // Make sure this presentation is the same size as the previous // presentation. If this is not the same size we showed it at last time, // then we need to resize the widget. // XXXbryner This interacts poorly with Firefox's infobar. If the old // presentation had the infobar visible, then we will resize the new // presentation to that smaller size. However, firing the locationchanged // event will hide the infobar, which will immediately resize the window // back to the larger size. A future optimization might be to restore // the presentation at the "wrong" size, then fire the locationchanged // event and check whether the docshell's new size is the same as the // cached viewer size (skipping the resize if they are equal). if (newRootView) { nsIWidget *widget = newRootView->GetWidget(); if (widget && !newBounds.IsEmpty() && newBounds != oldBounds) { #ifdef DEBUG_PAGE_CACHE printf("resize widget(%d, %d, %d, %d)\n", newBounds.x, newBounds.y, newBounds.width, newBounds.height); #endif widget->Resize(newBounds.x, newBounds.y, newBounds.width, newBounds.height, PR_FALSE); } } // Simulate the completion of the load. nsDocShell::FinishRestore(); // Restart plugins, and paint the content. if (shell) shell->Thaw(); return privWin->FireDelayedDOMEvents(); } NS_IMETHODIMP nsDocShell::CreateContentViewer(const char *aContentType, nsIRequest * request, nsIStreamListener ** aContentHandler) { *aContentHandler = nsnull; // Can we check the content type of the current content viewer // and reuse it without destroying it and re-creating it? NS_ASSERTION(mLoadGroup, "Someone ignored return from Init()?"); // Instantiate the content viewer object nsCOMPtr viewer; nsresult rv = NewContentViewerObj(aContentType, request, mLoadGroup, aContentHandler, getter_AddRefs(viewer)); if (NS_FAILED(rv)) return NS_ERROR_FAILURE; // Notify the current document that it is about to be unloaded!! // // It is important to fire the unload() notification *before* any state // is changed within the DocShell - otherwise, javascript will get the // wrong information :-( // if (mSavingOldViewer) { // We determined that it was safe to cache the document presentation // at the time we initiated the new load. We need to check whether // it's still safe to do so, since there may have been DOM mutations // or new requests initiated. nsCOMPtr domDoc; viewer->GetDOMDocument(getter_AddRefs(domDoc)); nsCOMPtr doc = do_QueryInterface(domDoc); mSavingOldViewer = CanSavePresentation(mLoadType, request, doc); } NS_ASSERTION(!mLoadingURI, "Re-entering unload?"); nsCOMPtr aOpenedChannel = do_QueryInterface(request); if (aOpenedChannel) { aOpenedChannel->GetURI(getter_AddRefs(mLoadingURI)); } FirePageHideNotification(!mSavingOldViewer); mLoadingURI = nsnull; // Set mFiredUnloadEvent = PR_FALSE so that the unload handler for the // *new* document will fire. mFiredUnloadEvent = PR_FALSE; // we've created a new document so go ahead and call // OnLoadingSite(), but don't fire OnLocationChange() // notifications before we've called Embed(). See bug 284993. mURIResultedInDocument = PR_TRUE; PRBool onLocationChangeNeeded = OnLoadingSite(aOpenedChannel, PR_FALSE); // let's try resetting the load group if we need to... nsCOMPtr currentLoadGroup; NS_ENSURE_SUCCESS(aOpenedChannel-> GetLoadGroup(getter_AddRefs(currentLoadGroup)), NS_ERROR_FAILURE); if (currentLoadGroup != mLoadGroup) { nsLoadFlags loadFlags = 0; //Cancel any URIs that are currently loading... /// XXX: Need to do this eventually Stop(); // // Retarget the document to this loadgroup... // /* First attach the channel to the right loadgroup * and then remove from the old loadgroup. This * puts the notifications in the right order and * we don't null-out mLSHE in OnStateChange() for * all redirected urls */ aOpenedChannel->SetLoadGroup(mLoadGroup); // Mark the channel as being a document URI... aOpenedChannel->GetLoadFlags(&loadFlags); loadFlags |= nsIChannel::LOAD_DOCUMENT_URI; aOpenedChannel->SetLoadFlags(loadFlags); mLoadGroup->AddRequest(request, nsnull); if (currentLoadGroup) currentLoadGroup->RemoveRequest(request, nsnull, NS_BINDING_RETARGETED); // Update the notification callbacks, so that progress and // status information are sent to the right docshell... aOpenedChannel->SetNotificationCallbacks(this); } NS_ENSURE_SUCCESS(Embed(viewer, "", (nsISupports *) nsnull), NS_ERROR_FAILURE); mSavedRefreshURIList = nsnull; mSavingOldViewer = PR_FALSE; mEODForCurrentDocument = PR_FALSE; // if this document is part of a multipart document, // the ID can be used to distinguish it from the other parts. nsCOMPtr multiPartChannel(do_QueryInterface(request)); if (multiPartChannel) { nsCOMPtr shell; rv = GetPresShell(getter_AddRefs(shell)); if (NS_SUCCEEDED(rv) && shell) { nsIDocument *doc = shell->GetDocument(); if (doc) { PRUint32 partID; multiPartChannel->GetPartID(&partID); doc->SetPartID(partID); } } } // Give hint to native plevent dispatch mechanism. If a document // is loading the native plevent dispatch mechanism should favor // performance over normal native event dispatch priorities. if (++gNumberOfDocumentsLoading == 1) { // Hint to favor performance for the plevent notification mechanism. // We want the pages to load as fast as possible even if its means // native messages might be starved. FavorPerformanceHint(PR_TRUE, NS_EVENT_STARVATION_DELAY_HINT); } if (onLocationChangeNeeded) { FireOnLocationChange(this, request, mCurrentURI); } return NS_OK; } nsresult nsDocShell::NewContentViewerObj(const char *aContentType, nsIRequest * request, nsILoadGroup * aLoadGroup, nsIStreamListener ** aContentHandler, nsIContentViewer ** aViewer) { nsCOMPtr aOpenedChannel = do_QueryInterface(request); nsresult rv; nsCOMPtr catMan(do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv)); if (NS_FAILED(rv)) return rv; nsXPIDLCString contractId; rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", aContentType, getter_Copies(contractId)); // Create an instance of the document-loader-factory nsCOMPtr docLoaderFactory; if (NS_SUCCEEDED(rv)) docLoaderFactory = do_GetService(contractId.get()); if (!docLoaderFactory) { return NS_ERROR_FAILURE; } // Now create an instance of the content viewer // nsLayoutDLF makes the determination if it should be a "view-source" instead of "view" NS_ENSURE_SUCCESS(docLoaderFactory->CreateInstance("view", aOpenedChannel, aLoadGroup, aContentType, static_cast(this), nsnull, aContentHandler, aViewer), NS_ERROR_FAILURE); (*aViewer)->SetContainer(static_cast(this)); return NS_OK; } NS_IMETHODIMP nsDocShell::SetupNewViewer(nsIContentViewer * aNewViewer) { // // Copy content viewer state from previous or parent content viewer. // // The following logic is mirrored in nsHTMLDocument::StartDocumentLoad! // // Do NOT to maintain a reference to the old content viewer outside // of this "copying" block, or it will not be destroyed until the end of // this routine and all