mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 22:32:46 +00:00
8422 lines
272 KiB
C++
8422 lines
272 KiB
C++
/* -*- 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 <travis@netscape.com>
|
|
* Pierre Phaneuf <pp@ludusdesign.com>
|
|
* Peter Annema <disttsc@bart.nl>
|
|
* Dan Rosen <dr@netscape.com>
|
|
*
|
|
* 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 ***** */
|
|
|
|
#include "nsIBrowserDOMWindow.h"
|
|
#include "nsIComponentManager.h"
|
|
#include "nsIContent.h"
|
|
#include "nsIDocument.h"
|
|
#include "nsIDOMDocument.h"
|
|
#include "nsIDOMNSDocument.h"
|
|
#include "nsIDOMElement.h"
|
|
#include "nsIDocumentViewer.h"
|
|
#include "nsIDocumentLoaderFactory.h"
|
|
#include "nsCURILoader.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 "nsIChromeEventHandler.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 "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 "nsIWritablePropertyBag2.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"
|
|
|
|
// Helper Classes
|
|
#include "nsDOMError.h"
|
|
#include "nsEscape.h"
|
|
|
|
// Interfaces Needed
|
|
#include "nsIUploadChannel.h"
|
|
#include "nsIDataChannel.h"
|
|
#include "nsIProgressEventSink.h"
|
|
#include "nsIWebProgress.h"
|
|
#include "nsILayoutHistoryState.h"
|
|
#include "nsITimer.h"
|
|
#include "nsISHistoryInternal.h"
|
|
#include "nsIPrincipal.h"
|
|
#include "nsIHistoryEntry.h"
|
|
#include "nsISHistoryListener.h"
|
|
#include "nsIWindowWatcher.h"
|
|
|
|
// Editor-related
|
|
#include "nsIEditingSession.h"
|
|
|
|
#include "nsPIDOMWindow.h"
|
|
#include "nsIDOMDocument.h"
|
|
#include "nsICachingChannel.h"
|
|
#include "nsICacheVisitor.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 "prlog.h"
|
|
#include "prmem.h"
|
|
|
|
#include "nsISelectionDisplay.h"
|
|
|
|
#include "nsIGlobalHistory2.h"
|
|
|
|
#ifdef DEBUG_DOCSHELL_FOCUS
|
|
#include "nsIEventStateManager.h"
|
|
#endif
|
|
|
|
#include "nsIFrame.h"
|
|
|
|
// for embedding
|
|
#include "nsIWebBrowserChromeFocus.h"
|
|
|
|
#include "nsPluginError.h"
|
|
|
|
static NS_DEFINE_IID(kDeviceContextCID, NS_DEVICE_CONTEXT_CID);
|
|
static NS_DEFINE_CID(kSimpleURICID, NS_SIMPLEURI_CID);
|
|
static NS_DEFINE_CID(kDocumentCharsetInfoCID, NS_DOCUMENTCHARSETINFO_CID);
|
|
static NS_DEFINE_CID(kDOMScriptObjectFactoryCID,
|
|
NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
|
|
|
|
#if defined(DEBUG_bryner) || defined(DEBUG_chb)
|
|
//#define DEBUG_DOCSHELL_FOCUS
|
|
#define DEBUG_PAGE_CACHE
|
|
#endif
|
|
|
|
#include "plevent.h"
|
|
#include "nsGUIEvent.h"
|
|
#include "nsEventQueueUtils.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 plevents 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
|
|
static PRLogModuleInfo* gDocShellLog;
|
|
#endif
|
|
|
|
//*****************************************************************************
|
|
//*** 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),
|
|
mAllowAuth(PR_TRUE),
|
|
mFiredUnloadEvent(PR_FALSE),
|
|
mEODForCurrentDocument(PR_FALSE),
|
|
mURIResultedInDocument(PR_FALSE),
|
|
mIsBeingDestroyed(PR_FALSE),
|
|
mIsExecutingOnLoadHandler(PR_FALSE),
|
|
mIsPrintingOrPP(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),
|
|
mEditorData(nsnull),
|
|
mTreeOwner(nsnull),
|
|
mChromeEventHandler(nsnull)
|
|
{
|
|
if (gDocShellCount++ == 0) {
|
|
NS_ASSERTION(sURIFixup == nsnull,
|
|
"Huh, sURIFixup not null in first nsDocShell ctor!");
|
|
|
|
CallGetService(NS_URIFIXUP_CONTRACTID, &sURIFixup);
|
|
}
|
|
|
|
#ifdef PR_LOGGING
|
|
if (! gDocShellLog)
|
|
gDocShellLog = PR_NewLogModule("nsDocShell");
|
|
#endif
|
|
}
|
|
|
|
nsDocShell::~nsDocShell()
|
|
{
|
|
nsDocShellFocusController* dsfc = nsDocShellFocusController::GetInstance();
|
|
if (dsfc) {
|
|
dsfc->ClosingDown(this);
|
|
}
|
|
Destroy();
|
|
|
|
if (--gDocShellCount == 0) {
|
|
NS_IF_RELEASE(sURIFixup);
|
|
}
|
|
}
|
|
|
|
nsresult
|
|
nsDocShell::Init()
|
|
{
|
|
nsresult rv = nsDocLoader::Init();
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
NS_ASSERTION(mLoadGroup, "Something went wrong!");
|
|
|
|
mContentListener = new nsDSURIContentListener(this);
|
|
NS_ENSURE_TRUE(mContentListener, NS_ERROR_OUT_OF_MEMORY);
|
|
|
|
rv = mContentListener->Init();
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// We want to hold a strong ref to the loadgroup, so it better hold a weak
|
|
// ref to us... use an InterfaceRequestorProxy to do this.
|
|
nsCOMPtr<InterfaceRequestorProxy> proxy =
|
|
new InterfaceRequestorProxy(NS_STATIC_CAST(nsIInterfaceRequestor*,
|
|
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<nsIDocShellTreeItem> shell;
|
|
PRInt32 n = mChildList.Count();
|
|
for (PRInt32 i = 0; i < n; i++) {
|
|
shell = do_QueryInterface(ChildAt(i));
|
|
NS_WARN_IF_FALSE(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_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(nsIPrompt)) &&
|
|
NS_SUCCEEDED(EnsureScriptEnvironment())) {
|
|
nsresult rv;
|
|
nsCOMPtr<nsIWindowWatcher> wwatch =
|
|
do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsCOMPtr<nsIDOMWindow> 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))) {
|
|
return NS_SUCCEEDED(
|
|
GetAuthPrompt(PROMPT_NORMAL, (nsIAuthPrompt **) aSink)) ?
|
|
NS_OK : NS_NOINTERFACE;
|
|
|
|
}
|
|
else if (aIID.Equals(NS_GET_IID(nsISHistory))) {
|
|
nsCOMPtr<nsISHistory> shistory;
|
|
nsresult
|
|
rv =
|
|
GetSessionHistory(getter_AddRefs(shistory));
|
|
if (NS_SUCCEEDED(rv) && shistory) {
|
|
*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<nsIEditingSession> 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<nsIPresShell> 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<nsIDocShellTreeOwner> 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::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;
|
|
}
|
|
|
|
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_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;
|
|
}
|
|
|
|
return docShellLoadType;
|
|
}
|
|
|
|
//*****************************************************************************
|
|
// nsDocShell::nsIDocShell
|
|
//*****************************************************************************
|
|
NS_IMETHODIMP
|
|
nsDocShell::LoadURI(nsIURI * aURI,
|
|
nsIDocShellLoadInfo * aLoadInfo,
|
|
PRUint32 aLoadFlags,
|
|
PRBool aFirstParty)
|
|
{
|
|
nsresult rv;
|
|
nsCOMPtr<nsIURI> referrer;
|
|
nsCOMPtr<nsIInputStream> postStream;
|
|
nsCOMPtr<nsIInputStream> headersStream;
|
|
nsCOMPtr<nsISupports> owner;
|
|
PRBool inheritOwner = PR_FALSE;
|
|
PRBool sendReferrer = PR_TRUE;
|
|
nsCOMPtr<nsISHEntry> 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);
|
|
}
|
|
|
|
#ifdef PR_LOGGING
|
|
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<nsIDocShellTreeItem> parentAsItem;
|
|
GetSameTypeParent(getter_AddRefs(parentAsItem));
|
|
nsCOMPtr<nsIDocShell> parentDS(do_QueryInterface(parentAsItem));
|
|
PRUint32 parentLoadType;
|
|
|
|
if (parentDS && parentDS != NS_STATIC_CAST(nsIDocShell *, this)) {
|
|
/* OK. It is a subframe. Checkout the
|
|
* parent's loadtype. If the parent was loaded thro' a history
|
|
* mechanism, then get the SH entry for the child from the parent.
|
|
* This is done to restore frameset navigation while going back/forward.
|
|
* If the parent was loaded through any other loadType, set the
|
|
* child's loadType too accordingly, so that session history does not
|
|
* get confused.
|
|
*/
|
|
|
|
// Get the parent's load type
|
|
parentDS->GetLoadType(&parentLoadType);
|
|
|
|
nsCOMPtr<nsIDocShellHistory> 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) {
|
|
PR_LOG(gDocShellLog, PR_LOG_DEBUG,
|
|
("nsDocShell[%p]: loading from session history", this));
|
|
|
|
rv = LoadHistoryEntry(shEntry, loadType);
|
|
}
|
|
// Perform the load...
|
|
else {
|
|
// We need an owner (a referring principal). 3 possibilities:
|
|
// (1) If a principal was passed in, that's what we'll use.
|
|
// (2) If the caller has allowed inheriting from the current document,
|
|
// or if we're being called from chrome (if there's system JS on the stack),
|
|
// then inheritOwner should be true and InternalLoad will get an owner
|
|
// from the current document. If none of these things are true, then
|
|
// (3) we pass a null owner into the channel, and an owner will be
|
|
// created later from the URL.
|
|
if (!owner && !inheritOwner) {
|
|
// See if there's system or chrome JS code running
|
|
nsCOMPtr<nsIScriptSecurityManager> secMan;
|
|
|
|
secMan = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
nsCOMPtr<nsIPrincipal> sysPrin;
|
|
nsCOMPtr<nsIPrincipal> subjectPrin;
|
|
|
|
// Just to compare, not to use!
|
|
rv = secMan->GetSystemPrincipal(getter_AddRefs(sysPrin));
|
|
if (NS_SUCCEEDED(rv)) {
|
|
rv = secMan->GetSubjectPrincipal(getter_AddRefs(subjectPrin));
|
|
}
|
|
// If there's no subject principal, there's no JS running, so we're in system code.
|
|
if (NS_SUCCEEDED(rv) &&
|
|
(!subjectPrin || sysPrin.get() == subjectPrin.get())) {
|
|
inheritOwner = PR_TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
PRUint32 flags = 0;
|
|
|
|
if (inheritOwner)
|
|
flags |= INTERNAL_LOAD_FLAGS_INHERIT_OWNER;
|
|
|
|
if (!sendReferrer)
|
|
flags |= INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER;
|
|
|
|
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);
|
|
|
|
// if the caller doesn't pass in a URI we need to create a dummy URI. necko
|
|
// currently requires a URI in various places during the load. Some consumers
|
|
// do as well.
|
|
nsCOMPtr<nsIURI> uri = aURI;
|
|
if (!uri) {
|
|
// HACK ALERT
|
|
nsresult rv = NS_OK;
|
|
uri = do_CreateInstance(kSimpleURICID, &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<nsIChannel> channel;
|
|
NS_ENSURE_SUCCESS(NS_NewInputStreamChannel
|
|
(getter_AddRefs(channel), uri, aStream,
|
|
aContentType, aContentCharset),
|
|
NS_ERROR_FAILURE);
|
|
|
|
nsCOMPtr<nsIURILoader>
|
|
uriLoader(do_GetService(NS_URI_LOADER_CONTRACTID));
|
|
NS_ENSURE_TRUE(uriLoader, NS_ERROR_FAILURE);
|
|
|
|
NS_ENSURE_SUCCESS(DoChannelLoad(channel, uriLoader), 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<nsIDocShellLoadInfo> 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) {
|
|
mFiredUnloadEvent = PR_TRUE;
|
|
|
|
mContentViewer->PageHide(aIsUnload);
|
|
|
|
PRInt32 i, n = mChildList.Count();
|
|
for (i = 0; i < n; i++) {
|
|
nsCOMPtr<nsIDocShell> shell(do_QueryInterface(ChildAt(i)));
|
|
if (shell) {
|
|
shell->FirePageHideNotification(aIsUnload);
|
|
}
|
|
}
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
//
|
|
// Bug 13871: Prevent frameset spoofing
|
|
// Check if origin document uri is the equivalent to target's principal.
|
|
// This takes into account subdomain checking if document.domain is set for
|
|
// Nav 4.x compatability.
|
|
//
|
|
// The following was derived from nsCodeBasePrincipal::Equals but in addition
|
|
// to the host PL_strcmp, it accepts a subdomain (nsHTMLDocument::SetDomain)
|
|
// if the document.domain was set.
|
|
//
|
|
static PRBool
|
|
SameOrSubdomainOfTarget(nsIURI* aOriginURI, nsIURI* aTargetURI,
|
|
PRBool aDocumentDomainSet)
|
|
{
|
|
nsCAutoString targetScheme;
|
|
nsresult rv = aTargetURI->GetScheme(targetScheme);
|
|
NS_ENSURE_SUCCESS(rv, PR_TRUE);
|
|
|
|
nsCAutoString originScheme;
|
|
rv = aOriginURI->GetScheme(originScheme);
|
|
NS_ENSURE_SUCCESS(rv, PR_TRUE);
|
|
|
|
if (strcmp(targetScheme.get(), originScheme.get()))
|
|
return PR_FALSE; // Different schemes - check fails
|
|
|
|
if (! strcmp(targetScheme.get(), "file"))
|
|
return PR_TRUE; // All file: urls are considered to have the same origin.
|
|
|
|
if (! strcmp(targetScheme.get(), "imap") ||
|
|
! strcmp(targetScheme.get(), "mailbox") ||
|
|
! strcmp(targetScheme.get(), "news"))
|
|
{
|
|
|
|
// Each message is a distinct trust domain; use the whole spec for comparison
|
|
nsCAutoString targetSpec;
|
|
rv =aTargetURI->GetAsciiSpec(targetSpec);
|
|
NS_ENSURE_SUCCESS(rv, PR_TRUE);
|
|
|
|
nsCAutoString originSpec;
|
|
rv = aOriginURI->GetAsciiSpec(originSpec);
|
|
NS_ENSURE_SUCCESS(rv, PR_TRUE);
|
|
|
|
return (! strcmp(targetSpec.get(), originSpec.get())); // True if full spec is same, false otherwise
|
|
}
|
|
|
|
// Compare ports.
|
|
int targetPort, originPort;
|
|
rv = aTargetURI->GetPort(&targetPort);
|
|
NS_ENSURE_SUCCESS(rv, PR_TRUE);
|
|
|
|
rv = aOriginURI->GetPort(&originPort);
|
|
NS_ENSURE_SUCCESS(rv, PR_TRUE);
|
|
|
|
if (targetPort != originPort)
|
|
return PR_FALSE; // Different port - check fails
|
|
|
|
// Need to check the hosts
|
|
nsCAutoString targetHost;
|
|
rv = aTargetURI->GetHost(targetHost);
|
|
NS_ENSURE_SUCCESS(rv, PR_TRUE);
|
|
|
|
nsCAutoString originHost;
|
|
rv = aOriginURI->GetHost(originHost);
|
|
NS_ENSURE_SUCCESS(rv, PR_TRUE);
|
|
|
|
if (!strcmp(targetHost.get(), originHost.get()))
|
|
return PR_TRUE; // Hosts are the same - check passed
|
|
|
|
// If document.domain was set, do the relaxed check
|
|
// Right align hostnames and compare - ensure preceeding char is . or /
|
|
if (aDocumentDomainSet)
|
|
{
|
|
int targetHostLen = targetHost.Length();
|
|
int originHostLen = originHost.Length();
|
|
int prefixChar = originHostLen-targetHostLen-1;
|
|
|
|
return ((originHostLen > targetHostLen) &&
|
|
(! strcmp((originHost.get()+prefixChar+1), targetHost.get())) &&
|
|
(originHost.CharAt(prefixChar) == '.' || originHost.CharAt(prefixChar) == '/'));
|
|
}
|
|
|
|
return PR_FALSE; // document.domain not set and hosts not same - check failed
|
|
}
|
|
|
|
//
|
|
// Bug 13871: Prevent frameset spoofing
|
|
//
|
|
// This routine answers: 'Is origin's document from same domain as
|
|
// target's document?'
|
|
// Be optimistic that domain is same - error cases all answer 'yes'.
|
|
//
|
|
// We have to compare the URI of the actual document loaded in the
|
|
// origin, ignoring any document.domain that was set, with the
|
|
// principal URI of the target (including any document.domain that was
|
|
// set). This puts control of loading in the hands of the target,
|
|
// which is more secure. (per Nav 4.x)
|
|
//
|
|
/* static */
|
|
PRBool
|
|
nsDocShell::ValidateOrigin(nsIDocShellTreeItem* aOriginTreeItem,
|
|
nsIDocShellTreeItem* aTargetTreeItem)
|
|
{
|
|
nsCOMPtr<nsIScriptSecurityManager> securityManager =
|
|
do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
|
|
NS_ENSURE_TRUE(securityManager, PR_FALSE);
|
|
|
|
nsCOMPtr<nsIPrincipal> subjectPrincipal;
|
|
nsresult rv =
|
|
securityManager->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
|
|
NS_ENSURE_SUCCESS(rv, PR_TRUE);
|
|
|
|
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 uri (ignoring document.domain)
|
|
nsCOMPtr<nsIWebNavigation> originWebNav =
|
|
do_QueryInterface(aOriginTreeItem);
|
|
NS_ENSURE_TRUE(originWebNav, PR_TRUE);
|
|
|
|
nsCOMPtr<nsIURI> originDocumentURI;
|
|
rv = originWebNav->GetCurrentURI(getter_AddRefs(originDocumentURI));
|
|
NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && originDocumentURI, PR_TRUE);
|
|
|
|
// This may be wyciwyg URI... if so, we need to extract the actual
|
|
// URI from it.
|
|
if (sURIFixup) {
|
|
PRBool isWyciwyg = PR_FALSE;
|
|
rv = originDocumentURI->SchemeIs("wyciwyg", &isWyciwyg);
|
|
if (isWyciwyg && NS_SUCCEEDED(rv)) {
|
|
nsCOMPtr<nsIURI> temp;
|
|
sURIFixup->CreateExposableURI(originDocumentURI,
|
|
getter_AddRefs(temp));
|
|
originDocumentURI = temp;
|
|
}
|
|
}
|
|
|
|
// Get target principal uri (including document.domain)
|
|
nsCOMPtr<nsIDOMDocument> targetDOMDocument =
|
|
do_GetInterface(aTargetTreeItem);
|
|
nsCOMPtr<nsIDocument> targetDocument(do_QueryInterface(targetDOMDocument));
|
|
NS_ENSURE_TRUE(targetDocument, PR_TRUE);
|
|
|
|
nsIPrincipal *targetPrincipal = targetDocument->GetPrincipal();
|
|
NS_ENSURE_TRUE(targetPrincipal, PR_TRUE);
|
|
|
|
nsCOMPtr<nsIURI> targetPrincipalURI;
|
|
rv = targetPrincipal->GetURI(getter_AddRefs(targetPrincipalURI));
|
|
NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && targetPrincipalURI, PR_TRUE);
|
|
|
|
// Find out if document.domain was set for HTML documents
|
|
PRBool documentDomainSet = PR_FALSE;
|
|
nsCOMPtr<nsIHTMLDocument> targetHTMLDocument =
|
|
do_QueryInterface(targetDocument);
|
|
|
|
// If we don't have an HTML document, fall through with
|
|
// documentDomainSet false
|
|
if (targetHTMLDocument) {
|
|
documentDomainSet = targetHTMLDocument->WasDomainSet();
|
|
}
|
|
|
|
// Is origin same principal or a subdomain of target's
|
|
// document.domain Compare actual URI of origin document, not origin
|
|
// principal's URI. (Per Nav 4.x)
|
|
return SameOrSubdomainOfTarget(originDocumentURI, targetPrincipalURI,
|
|
documentDomainSet);
|
|
}
|
|
|
|
nsresult nsDocShell::FindTarget(const PRUnichar *aWindowTarget,
|
|
PRBool *aIsNewWindow,
|
|
nsIDocShell **aResult)
|
|
{
|
|
nsresult rv;
|
|
|
|
*aResult = nsnull;
|
|
*aIsNewWindow = PR_FALSE;
|
|
|
|
// Try to locate the target window...
|
|
nsCOMPtr<nsIDocShellTreeItem> treeItem;
|
|
FindItemWithName(aWindowTarget, nsnull, this, getter_AddRefs(treeItem));
|
|
|
|
PRInt32 linkPref = nsIBrowserDOMWindow::OPEN_DEFAULTWINDOW;
|
|
// XXXbz this should live inside FindItemWithName, I think...
|
|
if (!treeItem) {
|
|
mPrefs->GetIntPref("browser.link.open_newwindow", &linkPref);
|
|
if (linkPref == nsIBrowserDOMWindow::OPEN_CURRENTWINDOW) {
|
|
// force new window to go to _top
|
|
FindItemWithName(NS_LITERAL_STRING("_top").get(),
|
|
nsnull, this, getter_AddRefs(treeItem));
|
|
NS_ASSERTION(treeItem,
|
|
"We better get a treeitem when asking for '_top' "
|
|
"with |this| as the original requestor");
|
|
}
|
|
}
|
|
|
|
if (!treeItem)
|
|
{
|
|
/// XXXbz this should really happen in FindItemWithName too, I think...
|
|
nsCOMPtr<nsIDOMWindow> newWindow;
|
|
nsCOMPtr<nsIDOMWindowInternal> parentWindow;
|
|
|
|
// This DocShell is the parent window
|
|
parentWindow = do_GetInterface(GetAsSupports(this));
|
|
if (!parentWindow) {
|
|
NS_ASSERTION(0, "Can't get nsIDOMWindowInternal from nsDocShell!");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
if (linkPref == nsIBrowserDOMWindow::OPEN_NEWTAB) {
|
|
|
|
// is it a popup?
|
|
|
|
PRBool allowTab = PR_TRUE;
|
|
nsCOMPtr<nsPIDOMWindow> pWindow = do_QueryInterface(mScriptGlobal);
|
|
if (pWindow) {
|
|
// skip the window search-by-name of GetOpenAllow
|
|
// by using _self. we don't care about that at this point.
|
|
OpenAllowValue allow = pWindow->GetOpenAllow(
|
|
NS_LITERAL_STRING("_self"));
|
|
if (allow == allowNot || allow == allowSelf)
|
|
allowTab = PR_FALSE;
|
|
}
|
|
|
|
// try to get our tab-opening interface
|
|
|
|
if (allowTab) {
|
|
nsCOMPtr<nsIBrowserDOMWindow> bwin;
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> rootItem;
|
|
GetRootTreeItem(getter_AddRefs(rootItem));
|
|
nsCOMPtr<nsIDOMWindow> rootWin(do_GetInterface(rootItem));
|
|
nsCOMPtr<nsIDOMChromeWindow> chromeWin(
|
|
do_QueryInterface(rootWin));
|
|
if (chromeWin)
|
|
chromeWin->GetBrowserDOMWindow(getter_AddRefs(bwin));
|
|
|
|
// open a new tab
|
|
if (bwin) {
|
|
rv = bwin->OpenURI(0, 0, nsIBrowserDOMWindow::OPEN_NEWTAB,
|
|
nsIBrowserDOMWindow::OPEN_NEW,
|
|
getter_AddRefs(newWindow));
|
|
|
|
nsCOMPtr<nsIScriptGlobalObject> newObj =
|
|
do_GetInterface(newWindow);
|
|
if (newObj)
|
|
newObj->SetOpenerWindow(parentWindow);
|
|
}
|
|
}
|
|
// else fall through to the normal Open method, from which
|
|
// the appropriate measures will be taken when the popup fails
|
|
}
|
|
|
|
if (!newWindow) {
|
|
nsAutoString name(aWindowTarget);
|
|
// XXXbz this should be handled somewhere else.... and in fact, it
|
|
// may be safe to just pass those through here. Check.
|
|
if (name.LowerCaseEqualsLiteral("_blank") ||
|
|
name.LowerCaseEqualsLiteral("_new")) {
|
|
name.Truncate();
|
|
}
|
|
rv = parentWindow->Open(EmptyString(), // URL to load
|
|
name, // Window name
|
|
EmptyString(), // Window features
|
|
getter_AddRefs(newWindow));
|
|
}
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
// Get the DocShell from the new window...
|
|
nsCOMPtr<nsIScriptGlobalObject> sgo;
|
|
sgo = do_QueryInterface(newWindow, &rv);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
// This will AddRef() aResult...
|
|
*aResult = sgo->GetDocShell();
|
|
|
|
// If all went well, indicate that a new window has been created.
|
|
if (*aResult) {
|
|
NS_ADDREF(*aResult);
|
|
|
|
*aIsNewWindow = PR_TRUE;
|
|
|
|
// if we just open a new window for this link, charset from current docshell
|
|
// should be kept, as what we did in js openNewWindowWith(url)
|
|
nsCOMPtr<nsIMarkupDocumentViewer> muCV, target_muCV;
|
|
nsCOMPtr<nsIContentViewer> cv, target_cv;
|
|
this->GetContentViewer(getter_AddRefs(cv));
|
|
(*aResult)->GetContentViewer(getter_AddRefs(target_cv));
|
|
if (cv && target_cv) {
|
|
muCV = do_QueryInterface(cv);
|
|
target_muCV = do_QueryInterface(target_cv);
|
|
if (muCV && target_muCV) {
|
|
nsCAutoString defaultCharset;
|
|
nsCAutoString prevDocCharset;
|
|
rv = muCV->GetDefaultCharacterSet(defaultCharset);
|
|
if(NS_SUCCEEDED(rv)) {
|
|
target_muCV->SetDefaultCharacterSet(defaultCharset);
|
|
}
|
|
rv = muCV->GetPrevDocCharacterSet(prevDocCharset);
|
|
if(NS_SUCCEEDED(rv)) {
|
|
target_muCV->SetPrevDocCharacterSet(prevDocCharset);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
else
|
|
{
|
|
if (treeItem)
|
|
{
|
|
NS_ASSERTION(!*aResult, "aResult should be null if treeItem is set!");
|
|
treeItem->QueryInterface(NS_GET_IID(nsIDocShell), (void **)aResult);
|
|
}
|
|
else
|
|
{
|
|
NS_IF_ADDREF(*aResult);
|
|
}
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::GetEldestPresContext(nsPresContext** aPresContext)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
|
|
NS_ENSURE_ARG_POINTER(aPresContext);
|
|
*aPresContext = nsnull;
|
|
|
|
nsCOMPtr<nsIContentViewer> viewer = mContentViewer;
|
|
while (viewer) {
|
|
nsCOMPtr<nsIContentViewer> prevViewer;
|
|
viewer->GetPreviousViewer(getter_AddRefs(prevViewer));
|
|
if (prevViewer)
|
|
viewer = prevViewer;
|
|
else {
|
|
nsCOMPtr<nsIDocumentViewer> 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<nsIDocumentViewer> 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<nsPresContext> 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<nsPresContext> presContext;
|
|
(void) GetEldestPresContext(getter_AddRefs(presContext));
|
|
|
|
if (presContext) {
|
|
NS_IF_ADDREF(*aPresShell = presContext->GetPresShell());
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::GetContentViewer(nsIContentViewer ** aContentViewer)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aContentViewer);
|
|
|
|
*aContentViewer = mContentViewer;
|
|
NS_IF_ADDREF(*aContentViewer);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::SetChromeEventHandler(nsIChromeEventHandler * aChromeEventHandler)
|
|
{
|
|
// Weak reference. Don't addref.
|
|
mChromeEventHandler = aChromeEventHandler;
|
|
|
|
NS_ASSERTION(!mScriptGlobal,
|
|
"SetChromeEventHandler() called after the script global "
|
|
"object was created! This means that the script global "
|
|
"object in this docshell won't get the right chrome event "
|
|
"handler. You really don't want to see this assert, FIX "
|
|
"YOUR CODE!");
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::GetChromeEventHandler(nsIChromeEventHandler ** aChromeEventHandler)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aChromeEventHandler);
|
|
|
|
*aChromeEventHandler = mChromeEventHandler;
|
|
NS_IF_ADDREF(*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)
|
|
{
|
|
// 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 = aURI; //This assignment addrefs
|
|
PRBool isRoot = PR_FALSE; // Is this the root docshell
|
|
PRBool isSubFrame = PR_FALSE; // Is this a subframe navigation?
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> root;
|
|
|
|
GetSameTypeRootTreeItem(getter_AddRefs(root));
|
|
if (root.get() == NS_STATIC_CAST(nsIDocShellTreeItem *, 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<nsIPresShell> 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<nsIContentViewer> viewer;
|
|
GetContentViewer(getter_AddRefs(viewer));
|
|
if (viewer) {
|
|
nsCOMPtr<nsIMarkupDocumentViewer> muDV(do_QueryInterface(viewer));
|
|
if (muDV) {
|
|
NS_ENSURE_SUCCESS(muDV->SetDefaultCharacterSet(nsDependentCString(aCharset)),
|
|
NS_ERROR_FAILURE);
|
|
}
|
|
}
|
|
|
|
// set the charset override
|
|
nsCOMPtr<nsIDocumentCharsetInfo> dcInfo;
|
|
GetDocumentCharsetInfo(getter_AddRefs(dcInfo));
|
|
if (dcInfo) {
|
|
nsCOMPtr<nsIAtom> 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(kDocumentCharsetInfoCID);
|
|
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::GetAllowPlugins(PRBool * aAllowPlugins)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aAllowPlugins);
|
|
|
|
*aAllowPlugins = mAllowPlugins;
|
|
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;
|
|
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;
|
|
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<nsDocShellEnumerator> 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);
|
|
NS_ENSURE_SUCCESS(EnsureDeviceContext(), NS_ERROR_FAILURE);
|
|
|
|
NS_ENSURE_SUCCESS(mDeviceContext->GetZoom(*zoom), NS_ERROR_FAILURE);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::SetZoom(float zoom)
|
|
{
|
|
NS_ENSURE_SUCCESS(EnsureDeviceContext(), NS_ERROR_FAILURE);
|
|
mDeviceContext->SetZoom(zoom);
|
|
|
|
// get the pres shell
|
|
nsCOMPtr<nsIPresShell> 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 *rootView = nsnull; // views are not ref counted
|
|
vm->GetRootView(rootView);
|
|
if (rootView)
|
|
vm->UpdateView(rootView, 0);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
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<nsIWebBrowserChromeFocus> 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)
|
|
{
|
|
mUseErrorPages = aUseErrorPages;
|
|
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<nsIDocumentLoader> docLoaderService =
|
|
do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID);
|
|
NS_ENSURE_TRUE(docLoaderService, NS_ERROR_UNEXPECTED);
|
|
|
|
NS_ENSURE_STATE(!mParent || mParent == docLoaderService);
|
|
|
|
mItemType = aItemType;
|
|
|
|
// disable auth prompting for anything but content
|
|
mAllowAuth = mItemType == typeContent;
|
|
|
|
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<nsIDocShell> 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<nsIURIContentListener> 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<nsIDocShellTreeItem> 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 = NS_STATIC_CAST(nsIDocShellTreeItem *, this);
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> 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 = NS_STATIC_CAST(nsIDocShellTreeItem *, this);
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> parent;
|
|
NS_ENSURE_SUCCESS(GetSameTypeParent(getter_AddRefs(parent)),
|
|
NS_ERROR_FAILURE);
|
|
while (parent) {
|
|
*aRootTreeItem = parent;
|
|
NS_ENSURE_SUCCESS((*aRootTreeItem)->
|
|
GetSameTypeParent(getter_AddRefs(parent)),
|
|
NS_ERROR_FAILURE);
|
|
}
|
|
NS_ADDREF(*aRootTreeItem);
|
|
return NS_OK;
|
|
}
|
|
|
|
/* 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?
|
|
|
|
// Now do a security check
|
|
// Bug 13871: Prevent frameset spoofing
|
|
// See BugSplat 336170, 338737 and XP_FindNamedContextInList in
|
|
// the classic codebase
|
|
// Nav's behaviour was:
|
|
// - pref controlled: "browser.frame.validate_origin"
|
|
// (gValidateOrigin)
|
|
// - allow load if host of target or target's parent is same
|
|
// as host of origin
|
|
// - allow load if target is a top level window
|
|
|
|
// We are going to be a little more restrictive, with the
|
|
// following algorithm:
|
|
// - pref controlled in the same way
|
|
// - allow access if the two treeitems are in the same tree
|
|
// - allow access if the aTargetItem or one of its ancestors
|
|
// has the same origin as aAccessingItem
|
|
// - allow access if the target is a toplevel window and we can
|
|
// access its opener. Note that we only allow one level of
|
|
// recursion there.
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> targetRoot;
|
|
aTargetItem->GetSameTypeRootTreeItem(getter_AddRefs(targetRoot));
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> accessingRoot;
|
|
aAccessingItem->GetSameTypeRootTreeItem(getter_AddRefs(accessingRoot));
|
|
|
|
if (targetRoot == accessingRoot) {
|
|
return PR_TRUE;
|
|
}
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> target = aTargetItem;
|
|
do {
|
|
if (ValidateOrigin(aAccessingItem, target)) {
|
|
return PR_TRUE;
|
|
}
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> parent;
|
|
target->GetSameTypeParent(getter_AddRefs(parent));
|
|
parent.swap(target);
|
|
} while (target);
|
|
|
|
if (aTargetItem != targetRoot) {
|
|
// target is a subframe, not in accessor's frame hierarchy, and all its
|
|
// ancestors have origins different from that of the accessor. Don't
|
|
// allow access.
|
|
return PR_FALSE;
|
|
}
|
|
|
|
if (!aConsiderOpener) {
|
|
// All done here
|
|
return PR_FALSE;
|
|
}
|
|
|
|
nsCOMPtr<nsIDOMWindow> targetWindow(do_GetInterface(aTargetItem));
|
|
nsCOMPtr<nsIDOMWindowInternal> targetInternal(do_QueryInterface(targetWindow));
|
|
if (!targetInternal) {
|
|
NS_ERROR("This should not happen, really");
|
|
return PR_FALSE;
|
|
}
|
|
|
|
nsCOMPtr<nsIDOMWindowInternal> targetOpener;
|
|
targetInternal->GetOpener(getter_AddRefs(targetOpener));
|
|
nsCOMPtr<nsIWebNavigation> openerWebNav(do_GetInterface(targetOpener));
|
|
nsCOMPtr<nsIDocShellTreeItem> openerItem(do_QueryInterface(openerWebNav));
|
|
|
|
if (!openerItem) {
|
|
return PR_FALSE;
|
|
}
|
|
|
|
return CanAccessItem(openerItem, aAccessingItem, PR_FALSE);
|
|
}
|
|
|
|
static PRBool
|
|
ItemIsActive(nsIDocShellTreeItem *aItem)
|
|
{
|
|
nsCOMPtr<nsIDOMWindow> tmp(do_GetInterface(aItem));
|
|
nsCOMPtr<nsIDOMWindowInternal> 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<nsIDocShellTreeItem> 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") ||
|
|
name.LowerCaseEqualsLiteral("_new"))
|
|
{
|
|
// 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));
|
|
if(!foundItem)
|
|
foundItem = this;
|
|
}
|
|
// _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"))
|
|
{
|
|
if (mTreeOwner)
|
|
mTreeOwner->GetPrimaryContentShell(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.
|
|
}
|
|
#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<nsIDocShellTreeItem> reqAsTreeItem(do_QueryInterface(aRequestor));
|
|
|
|
// Second we check our children making sure not to ask a child if
|
|
// it is the aRequestor.
|
|
#ifdef DEBUG
|
|
nsresult rv =
|
|
#endif
|
|
FindChildWithName(aName, 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<nsIDocShellTreeItem> 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,
|
|
NS_STATIC_CAST(nsIDocShellTreeItem*,
|
|
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<nsIDocShellTreeOwner>
|
|
reqAsTreeOwner(do_QueryInterface(aRequestor));
|
|
|
|
if (mTreeOwner && mTreeOwner != reqAsTreeOwner) {
|
|
return mTreeOwner->
|
|
FindItemWithName(aName,
|
|
NS_STATIC_CAST(nsIDocShellTreeItem*,
|
|
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(nsIDocShellTreeNode * aParentNode, int aLevel)
|
|
{
|
|
for (PRInt32 i=0;i<aLevel;i++) printf(" ");
|
|
|
|
PRInt32 childWebshellCount;
|
|
aParentNode->GetChildCount(&childWebshellCount);
|
|
nsCOMPtr<nsIDocShell> parentAsDocShell(do_QueryInterface(aParentNode));
|
|
nsCOMPtr<nsIDocShellTreeItem> parentAsItem(do_QueryInterface(aParentNode));
|
|
PRInt32 type;
|
|
parentAsItem->GetItemType(&type);
|
|
nsCOMPtr<nsIPresShell> presShell;
|
|
parentAsDocShell->GetPresShell(getter_AddRefs(presShell));
|
|
nsCOMPtr<nsPresContext> presContext;
|
|
parentAsDocShell->GetPresContext(getter_AddRefs(presContext));
|
|
nsIDocument *doc = presShell->GetDocument();
|
|
|
|
nsCOMPtr<nsIScriptGlobalObject> sgo;
|
|
doc->GetScriptGlobalObject(getter_AddRefs(sgo));
|
|
nsCOMPtr<nsIDOMWindowInternal> domwin(do_QueryInterface(sgo));
|
|
|
|
nsCOMPtr<nsIWidget> widget;
|
|
nsIViewManager* vm = presShell->GetViewManager();
|
|
if (vm) {
|
|
vm->GetWidget(getter_AddRefs(widget));
|
|
}
|
|
nsCOMPtr<nsIContent> rootContent;
|
|
doc->GetRootContent(getter_AddRefs(rootContent));
|
|
|
|
printf("DS %p Ty %s Doc %p DW %p EM %p CN %p\n",
|
|
parentAsDocShell.get(),
|
|
type==nsIDocShellTreeItem::typeChrome?"Chr":"Con",
|
|
doc.get(), domwin.get(),
|
|
presContext->EventStateManager(), rootContent.get());
|
|
|
|
if (childWebshellCount > 0) {
|
|
for (PRInt32 i=0;i<childWebshellCount;i++) {
|
|
nsCOMPtr<nsIDocShellTreeItem> child;
|
|
aParentNode->GetChildAt(i, getter_AddRefs(child));
|
|
nsCOMPtr<nsIDocShellTreeNode> childAsNode(do_QueryInterface(child));
|
|
PrintDocTree(childAsNode, aLevel+1);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
PrintDocTree(nsIDocShellTreeNode * aParentNode)
|
|
{
|
|
NS_ASSERTION(aParentNode, "Pointer is null!");
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> item(do_QueryInterface(aParentNode));
|
|
nsCOMPtr<nsIDocShellTreeItem> parentItem;
|
|
item->GetParent(getter_AddRefs(parentItem));
|
|
while (parentItem) {
|
|
nsCOMPtr<nsIDocShellTreeItem>tmp;
|
|
parentItem->GetParent(getter_AddRefs(tmp));
|
|
if (!tmp) {
|
|
break;
|
|
}
|
|
parentItem = tmp;
|
|
}
|
|
|
|
if (!parentItem) {
|
|
parentItem = do_QueryInterface(aParentNode);
|
|
}
|
|
|
|
if (parentItem) {
|
|
nsCOMPtr<nsIDocShellTreeNode> parentAsNode(do_QueryInterface(parentItem));
|
|
PrintDocTree(parentAsNode, 0);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::SetTreeOwner(nsIDocShellTreeOwner * aTreeOwner)
|
|
{
|
|
#ifdef DEBUG_DOCSHELL_FOCUS
|
|
nsCOMPtr<nsIDocShellTreeNode> node(do_QueryInterface(aTreeOwner));
|
|
if (node) {
|
|
PrintDocTree(node);
|
|
}
|
|
#endif
|
|
|
|
// Don't automatically set the progress based on the tree owner for frames
|
|
if (!IsFrame()) {
|
|
nsCOMPtr<nsIWebProgress> webProgress =
|
|
do_QueryInterface(GetAsSupports(this));
|
|
|
|
if (webProgress) {
|
|
nsCOMPtr<nsIWebProgressListener>
|
|
oldListener(do_QueryInterface(mTreeOwner));
|
|
nsCOMPtr<nsIWebProgressListener>
|
|
newListener(do_QueryInterface(aTreeOwner));
|
|
|
|
if (oldListener) {
|
|
webProgress->RemoveProgressListener(oldListener);
|
|
}
|
|
|
|
if (newListener) {
|
|
webProgress->AddProgressListener(newListener,
|
|
nsIWebProgress::NOTIFY_ALL);
|
|
}
|
|
}
|
|
}
|
|
|
|
mTreeOwner = aTreeOwner; // Weak reference per API
|
|
|
|
PRInt32 i, n = mChildList.Count();
|
|
for (i = 0; i < n; i++) {
|
|
nsCOMPtr<nsIDocShellTreeItem> 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(PRInt32 aChildOffset)
|
|
{
|
|
mChildOffset = aChildOffset;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::GetChildOffset(PRInt32 * aChildOffset)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aChildOffset);
|
|
*aChildOffset = mChildOffset;
|
|
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<nsDocLoader> childAsDocLoader = GetAsDocLoader(aChild);
|
|
NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED);
|
|
|
|
// Make sure we're not creating a loop in the docshell tree
|
|
nsDocLoader* ancestor = this;
|
|
do {
|
|
if (childAsDocLoader == ancestor) {
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
}
|
|
ancestor = ancestor->GetParent();
|
|
} while (ancestor);
|
|
|
|
// Make sure to remove the child from its current parent.
|
|
nsDocLoader* childsParent = childAsDocLoader->GetParent();
|
|
if (childsParent) {
|
|
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);
|
|
|
|
// 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.
|
|
aChild->SetChildOffset(mChildList.Count() - 1);
|
|
|
|
/* Set the child's global history if the parent has one */
|
|
if (mGlobalHistory) {
|
|
nsCOMPtr<nsIDocShellHistory>
|
|
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<nsIDocShell> childAsDocShell(do_QueryInterface(aChild));
|
|
if (!childAsDocShell)
|
|
return NS_OK;
|
|
|
|
// charset, style-disabling, and zoom will be inherited in SetupNewViewer()
|
|
|
|
// Now take this document's charset and set the 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<nsIDocumentCharsetInfo> dcInfo = NULL;
|
|
res = childAsDocShell->GetDocumentCharsetInfo(getter_AddRefs(dcInfo));
|
|
if (NS_FAILED(res) || (!dcInfo))
|
|
return NS_OK;
|
|
|
|
// get the parent's current charset
|
|
nsCOMPtr<nsIDocumentViewer> docv(do_QueryInterface(mContentViewer));
|
|
if (!docv)
|
|
return NS_OK;
|
|
nsCOMPtr<nsIDocument> 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<nsIAtom> 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_LossyConvertUCS2toASCII(parentCS).get(), mItemType);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::RemoveChild(nsIDocShellTreeItem * aChild)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aChild);
|
|
|
|
nsRefPtr<nsDocLoader> 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);
|
|
|
|
NS_WARN_IF_FALSE(aIndex >=0 && aIndex < mChildList.Count(),
|
|
"index of child element is out of range!");
|
|
|
|
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<nsIDocShellTreeItem> 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
|
|
nsCOMPtr<nsIDocShellTreeNode>
|
|
childAsNode(do_QueryInterface(child));
|
|
if (childAsNode) {
|
|
#ifdef DEBUG
|
|
nsresult rv =
|
|
#endif
|
|
childAsNode->FindChildWithName(aName, PR_TRUE,
|
|
aSameType,
|
|
NS_STATIC_CAST(nsIDocShellTreeItem*,
|
|
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<nsISHContainer> container(do_QueryInterface(mLSHE));
|
|
if (container) {
|
|
// Get the child subframe from session history.
|
|
rv = container->GetChildAt(aChildOffset, aResult);
|
|
if (*aResult)
|
|
(*aResult)->SetLoadType(loadType);
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::AddChildSHEntry(nsISHEntry * aCloneRef, nsISHEntry * aNewEntry,
|
|
PRInt32 aChildOffset)
|
|
{
|
|
nsresult rv;
|
|
|
|
if (mLSHE) {
|
|
/* You get here if you are currently building a
|
|
* hierarchy ie.,you just visited a frameset page
|
|
*/
|
|
nsCOMPtr<nsISHContainer> container(do_QueryInterface(mLSHE, &rv));
|
|
if (container) {
|
|
rv = container->AddChild(aNewEntry, aChildOffset);
|
|
}
|
|
}
|
|
else if (!aCloneRef) {
|
|
/* This is an initial load in some subframe. Just append it if we can */
|
|
nsCOMPtr<nsISHContainer> container(do_QueryInterface(mOSHE, &rv));
|
|
if (container) {
|
|
rv = container->AddChild(aNewEntry, aChildOffset);
|
|
}
|
|
}
|
|
else 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<nsIHistoryEntry> 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<nsISHEntry> currentEntry(do_QueryInterface(currentHE));
|
|
if (currentEntry) {
|
|
PRUint32 cloneID = 0;
|
|
nsCOMPtr<nsISHEntry> nextEntry;
|
|
aCloneRef->GetID(&cloneID);
|
|
rv = CloneAndReplace(currentEntry, this, cloneID, aNewEntry,
|
|
getter_AddRefs(nextEntry));
|
|
|
|
if (NS_SUCCEEDED(rv)) {
|
|
nsCOMPtr<nsISHistoryInternal>
|
|
shPrivate(do_QueryInterface(mSessionHistory));
|
|
NS_ENSURE_TRUE(shPrivate, NS_ERROR_FAILURE);
|
|
rv = shPrivate->AddEntry(nextEntry, PR_TRUE);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
/* Just pass this along */
|
|
nsCOMPtr<nsIDocShellHistory> 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.
|
|
*/
|
|
|
|
nsresult rv;
|
|
nsCOMPtr<nsIDocShellHistory> parent =
|
|
do_QueryInterface(GetAsSupports(mParent), &rv);
|
|
if (parent) {
|
|
rv = parent->AddChildSHEntry(mOSHE, aNewEntry, aChildOffset);
|
|
}
|
|
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;
|
|
}
|
|
|
|
//*****************************************************************************
|
|
// nsDocShell::nsIWebNavigation
|
|
//*****************************************************************************
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::GetCanGoBack(PRBool * aCanGoBack)
|
|
{
|
|
if (IsPrintingOrPP(PR_FALSE)) {
|
|
*aCanGoBack = PR_FALSE;
|
|
return NS_OK; // JS may not handle returning of an error code
|
|
}
|
|
nsresult rv;
|
|
nsCOMPtr<nsISHistory> rootSH;
|
|
rv = GetRootSessionHistory(getter_AddRefs(rootSH));
|
|
nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
|
|
NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
|
|
rv = webnav->GetCanGoBack(aCanGoBack);
|
|
return rv;
|
|
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::GetCanGoForward(PRBool * aCanGoForward)
|
|
{
|
|
if (IsPrintingOrPP(PR_FALSE)) {
|
|
*aCanGoForward = PR_FALSE;
|
|
return NS_OK; // JS may not handle returning of an error code
|
|
}
|
|
nsresult rv;
|
|
nsCOMPtr<nsISHistory> rootSH;
|
|
rv = GetRootSessionHistory(getter_AddRefs(rootSH));
|
|
nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
|
|
NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
|
|
rv = webnav->GetCanGoForward(aCanGoForward);
|
|
return rv;
|
|
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::GoBack()
|
|
{
|
|
if (IsPrintingOrPP()) {
|
|
return NS_OK; // JS may not handle returning of an error code
|
|
}
|
|
nsresult rv;
|
|
nsCOMPtr<nsISHistory> rootSH;
|
|
rv = GetRootSessionHistory(getter_AddRefs(rootSH));
|
|
nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
|
|
NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
|
|
rv = webnav->GoBack();
|
|
return rv;
|
|
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::GoForward()
|
|
{
|
|
if (IsPrintingOrPP()) {
|
|
return NS_OK; // JS may not handle returning of an error code
|
|
}
|
|
nsresult rv;
|
|
nsCOMPtr<nsISHistory> rootSH;
|
|
rv = GetRootSessionHistory(getter_AddRefs(rootSH));
|
|
nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
|
|
NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
|
|
rv = webnav->GoForward();
|
|
return rv;
|
|
|
|
}
|
|
|
|
NS_IMETHODIMP nsDocShell::GotoIndex(PRInt32 aIndex)
|
|
{
|
|
if (IsPrintingOrPP()) {
|
|
return NS_OK; // JS may not handle returning of an error code
|
|
}
|
|
nsresult rv;
|
|
nsCOMPtr<nsISHistory> rootSH;
|
|
rv = GetRootSessionHistory(getter_AddRefs(rootSH));
|
|
nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
|
|
NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
|
|
rv = webnav->GotoIndex(aIndex);
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::LoadURI(const PRUnichar * aURI,
|
|
PRUint32 aLoadFlags,
|
|
nsIURI * aReferringURI,
|
|
nsIInputStream * aPostStream,
|
|
nsIInputStream * aHeaderStream)
|
|
{
|
|
if (IsPrintingOrPP()) {
|
|
return NS_OK; // JS may not handle returning of an error code
|
|
}
|
|
nsCOMPtr<nsIURI> uri;
|
|
nsresult rv = NS_OK;
|
|
// Create the fixup object if necessary
|
|
if (!sURIFixup) {
|
|
// No fixup service so try and create a URI and see what happens
|
|
nsAutoString 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);
|
|
} else {
|
|
// Call the fixup object
|
|
rv = sURIFixup->CreateFixupURI(NS_ConvertUCS2toUTF8(aURI),
|
|
nsIURIFixup::FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP,
|
|
getter_AddRefs(uri));
|
|
}
|
|
|
|
if (NS_ERROR_MALFORMED_URI == rv) {
|
|
DisplayLoadError(rv, uri, aURI);
|
|
}
|
|
|
|
if (NS_FAILED(rv) || !uri)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
nsCOMPtr<nsIDocShellLoadInfo> 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, 0, PR_TRUE);
|
|
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::DisplayLoadError(nsresult aError, nsIURI *aURI,
|
|
const PRUnichar *aURL,
|
|
nsIChannel* aFailedChannel)
|
|
{
|
|
// Get prompt and string bundle servcies
|
|
nsCOMPtr<nsIPrompt> prompter;
|
|
nsCOMPtr<nsIStringBundle> stringBundle;
|
|
GetPromptAndStringBundle(getter_AddRefs(prompter),
|
|
getter_AddRefs(stringBundle));
|
|
|
|
NS_ENSURE_TRUE(stringBundle, NS_ERROR_FAILURE);
|
|
NS_ENSURE_TRUE(prompter, NS_ERROR_FAILURE);
|
|
|
|
nsAutoString error;
|
|
const PRUint32 kMaxFormatStrArgs = 2;
|
|
nsAutoString formatStrs[kMaxFormatStrArgs];
|
|
PRUint32 formatStrCount = 0;
|
|
nsresult rv = NS_OK;
|
|
|
|
// 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);
|
|
CopyASCIItoUCS2(scheme, formatStrs[0]);
|
|
formatStrCount = 1;
|
|
error.AssignLiteral("protocolNotFound");
|
|
}
|
|
else if (NS_ERROR_FILE_NOT_FOUND == aError) {
|
|
NS_ENSURE_ARG_POINTER(aURI);
|
|
nsCAutoString spec;
|
|
aURI->GetPath(spec);
|
|
nsCAutoString charset;
|
|
// unescape and convert from origin charset
|
|
aURI->GetOriginCharset(charset);
|
|
nsCOMPtr<nsITextToSubURI> textToSubURI(
|
|
do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv));
|
|
if (NS_SUCCEEDED(rv))
|
|
// UnEscapeURIForUI always succeeds
|
|
textToSubURI->UnEscapeURIForUI(charset, spec, formatStrs[0]);
|
|
else
|
|
CopyUTF8toUTF16(spec, formatStrs[0]);
|
|
rv = NS_OK;
|
|
formatStrCount = 1;
|
|
error.AssignLiteral("fileNotFound");
|
|
}
|
|
else if (NS_ERROR_UNKNOWN_HOST == aError) {
|
|
NS_ENSURE_ARG_POINTER(aURI);
|
|
// Get the host
|
|
nsCAutoString host;
|
|
aURI->GetHost(host);
|
|
CopyUTF8toUTF16(host, formatStrs[0]);
|
|
formatStrCount = 1;
|
|
error.AssignLiteral("dnsNotFound");
|
|
}
|
|
else if(NS_ERROR_CONNECTION_REFUSED == aError) {
|
|
NS_ENSURE_ARG_POINTER(aURI);
|
|
// Build up the host:port string.
|
|
nsCAutoString hostport;
|
|
aURI->GetHostPort(hostport);
|
|
CopyUTF8toUTF16(hostport, formatStrs[0]);
|
|
formatStrCount = 1;
|
|
error.AssignLiteral("connectionFailure");
|
|
}
|
|
else if(NS_ERROR_NET_INTERRUPT == aError) {
|
|
NS_ENSURE_ARG_POINTER(aURI);
|
|
// Build up the host:port string.
|
|
nsCAutoString hostport;
|
|
aURI->GetHostPort(hostport);
|
|
CopyUTF8toUTF16(hostport, formatStrs[0]);
|
|
formatStrCount = 1;
|
|
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 {
|
|
// 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 falied 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
|
|
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;
|
|
}
|
|
}
|
|
|
|
// Test if the error should be displayed
|
|
if (error.IsEmpty()) {
|
|
return NS_OK;
|
|
}
|
|
|
|
// Test if the error needs to be formatted
|
|
nsAutoString messageStr;
|
|
if (formatStrCount > 0) {
|
|
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());
|
|
}
|
|
else
|
|
{
|
|
nsXPIDLString str;
|
|
rv = stringBundle->GetStringFromName(
|
|
error.get(),
|
|
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) {
|
|
// Display an error page
|
|
LoadErrorPage(aURI, aURL, error.get(), messageStr.get(),
|
|
aFailedChannel);
|
|
}
|
|
else
|
|
{
|
|
// Display a message box
|
|
prompter->Alert(nsnull, messageStr.get());
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::LoadErrorPage(nsIURI *aURI, const PRUnichar *aURL,
|
|
const PRUnichar *aErrorType,
|
|
const PRUnichar *aDescription,
|
|
nsIChannel* aFailedChannel)
|
|
{
|
|
#ifdef PR_LOGGING
|
|
if (PR_LOG_TEST(gDocShellLog, PR_LOG_DEBUG)) {
|
|
nsCAutoString spec;
|
|
aURI->GetSpec(spec);
|
|
|
|
nsCAutoString chanName;
|
|
if (aFailedChannel)
|
|
aFailedChannel->GetName(chanName);
|
|
else
|
|
chanName.AssignLiteral("<no channel>");
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
|
|
nsCString errorPageUrl("about:neterror?e=");
|
|
|
|
errorPageUrl.AppendASCII(escapedError);
|
|
errorPageUrl.AppendLiteral("&u=");
|
|
errorPageUrl.AppendASCII(escapedUrl);
|
|
errorPageUrl.AppendLiteral("&c=");
|
|
errorPageUrl.AppendASCII(escapedCharset);
|
|
errorPageUrl.AppendLiteral("&d=");
|
|
errorPageUrl.AppendASCII(escapedDescription);
|
|
|
|
nsMemory::Free(escapedDescription);
|
|
nsMemory::Free(escapedError);
|
|
nsMemory::Free(escapedUrl);
|
|
nsMemory::Free(escapedCharset);
|
|
|
|
nsCOMPtr<nsIURI> 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 (IsPrintingOrPP()) {
|
|
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!");
|
|
|
|
// XXXTAB Convert reload type to our type
|
|
LoadType type = LOAD_RELOAD_NORMAL;
|
|
if (aReloadFlags & LOAD_FLAGS_BYPASS_CACHE &&
|
|
aReloadFlags & LOAD_FLAGS_BYPASS_PROXY)
|
|
type = LOAD_RELOAD_BYPASS_PROXY_AND_CACHE;
|
|
else if (aReloadFlags & LOAD_FLAGS_CHARSET_CHANGE)
|
|
type = LOAD_RELOAD_CHARSET_CHANGE;
|
|
// Send notifications to the HistoryListener if any, about the impending reload
|
|
nsCOMPtr<nsISHistory> rootSH;
|
|
rv = GetRootSessionHistory(getter_AddRefs(rootSH));
|
|
nsCOMPtr<nsISHistoryInternal> shistInt(do_QueryInterface(rootSH));
|
|
PRBool canReload = PR_TRUE;
|
|
if (rootSH) {
|
|
nsCOMPtr<nsISHistoryListener> 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, type);
|
|
}
|
|
else if (mLSHE) { // In case a reload happened before the current load is done
|
|
rv = LoadHistoryEntry(mLSHE, type);
|
|
}
|
|
else {
|
|
nsAutoString contentTypeHint;
|
|
nsCOMPtr<nsIDOMWindow> window(do_GetInterface((nsIDocShell*)this));
|
|
if (window) {
|
|
nsCOMPtr<nsIDOMDocument> document;
|
|
window->GetDocument(getter_AddRefs(document));
|
|
nsCOMPtr<nsIDOMNSDocument> doc(do_QueryInterface(document));
|
|
if (doc) {
|
|
doc->GetContentType(contentTypeHint);
|
|
}
|
|
}
|
|
|
|
rv = InternalLoad(mCurrentURI,
|
|
mReferrerURI,
|
|
nsnull, // No owner
|
|
INTERNAL_LOAD_FLAGS_INHERIT_OWNER, // Inherit owner from document
|
|
nsnull, // No window target
|
|
NS_LossyConvertUCS2toASCII(contentTypeHint).get(),
|
|
nsnull, // No post data
|
|
nsnull, // No headers data
|
|
type, // Load type
|
|
nsnull, // No SHEntry
|
|
PR_TRUE,
|
|
nsnull, // No nsIDocShell
|
|
nsnull); // No nsIRequest
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::Stop(PRUint32 aStopFlags)
|
|
{
|
|
if (nsIWebNavigation::STOP_CONTENT & aStopFlags) {
|
|
// Revoke any pending plevents related to content viewer restoration
|
|
nsCOMPtr<nsIEventQueue> uiThreadQueue;
|
|
NS_GetMainEventQ(getter_AddRefs(uiThreadQueue));
|
|
if (uiThreadQueue)
|
|
uiThreadQueue->RevokeEvents(this);
|
|
|
|
// 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;
|
|
}
|
|
|
|
// 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<nsIWebNavigation> 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);
|
|
|
|
*aURI = mCurrentURI;
|
|
NS_IF_ADDREF(*aURI);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::GetReferringURI(nsIURI ** aURI)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aURI);
|
|
|
|
*aURI = mReferrerURI;
|
|
NS_IF_ADDREF(*aURI);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::SetSessionHistory(nsISHistory * aSessionHistory)
|
|
{
|
|
|
|
NS_ENSURE_TRUE(aSessionHistory, NS_ERROR_FAILURE);
|
|
// make sure that we are the root docshell and
|
|
// set a handle to root docshell in SH.
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> root;
|
|
/* Get the root docshell. If *this* is the root docshell
|
|
* then save a handle to *this* in SH. SH needs it to do
|
|
* traversions thro' its entries
|
|
*/
|
|
GetSameTypeRootTreeItem(getter_AddRefs(root));
|
|
NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
|
|
if (root.get() == NS_STATIC_CAST(nsIDocShellTreeItem *, this)) {
|
|
mSessionHistory = aSessionHistory;
|
|
nsCOMPtr<nsISHistoryInternal>
|
|
shPrivate(do_QueryInterface(mSessionHistory));
|
|
NS_ENSURE_TRUE(shPrivate, NS_ERROR_FAILURE);
|
|
shPrivate->SetRootDocShell(this);
|
|
return NS_OK;
|
|
}
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::GetSessionHistory(nsISHistory ** aSessionHistory)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aSessionHistory);
|
|
*aSessionHistory = mSessionHistory;
|
|
NS_IF_ADDREF(*aSessionHistory);
|
|
return NS_OK;
|
|
}
|
|
|
|
//*****************************************************************************
|
|
// nsDocShell::nsIWebPageDescriptor
|
|
//*****************************************************************************
|
|
NS_IMETHODIMP
|
|
nsDocShell::LoadPage(nsISupports *aPageDescriptor, PRUint32 aDisplayType)
|
|
{
|
|
nsCOMPtr<nsISHEntry> shEntryIn(do_QueryInterface(aPageDescriptor));
|
|
|
|
// Currently, the opaque 'page descriptor' is an nsISHEntry...
|
|
if (!shEntryIn) {
|
|
return NS_ERROR_INVALID_POINTER;
|
|
}
|
|
|
|
// Now clone shEntryIn, since we might end up modifying it later on, and we
|
|
// want a page descriptor to be reusable.
|
|
nsCOMPtr<nsISHEntry> shEntry;
|
|
nsresult rv = shEntryIn->Clone(getter_AddRefs(shEntry));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
//
|
|
// load the page as view-source
|
|
//
|
|
if (nsIWebPageDescriptor::DISPLAY_AS_SOURCE == aDisplayType) {
|
|
nsCOMPtr<nsIURI> oldUri, newUri;
|
|
nsCString spec, newSpec;
|
|
|
|
// Create a new view-source URI and replace the original.
|
|
rv = shEntry->GetURI(getter_AddRefs(oldUri));
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
oldUri->GetSpec(spec);
|
|
newSpec.AppendLiteral("view-source:");
|
|
newSpec.Append(spec);
|
|
|
|
rv = NS_NewURI(getter_AddRefs(newUri), newSpec);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
shEntry->SetURI(newUri);
|
|
}
|
|
|
|
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<nsISHEntry> 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()
|
|
{
|
|
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;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::Destroy()
|
|
{
|
|
mIsBeingDestroyed = PR_TRUE;
|
|
|
|
//Fire unload event before we blow anything away.
|
|
(void) FirePageHideNotification(PR_TRUE);
|
|
|
|
// Stop any URLs that are currently being loaded...
|
|
Stop(nsIWebNavigation::STOP_ALL);
|
|
|
|
delete mEditorData;
|
|
mEditorData = 0;
|
|
|
|
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<nsIDocShellTreeNode> docShellParentAsNode =
|
|
do_QueryInterface(GetAsSupports(mParent));
|
|
if (docShellParentAsNode)
|
|
docShellParentAsNode->RemoveChild(this);
|
|
|
|
if (mContentViewer) {
|
|
mContentViewer->Close(nsnull);
|
|
mContentViewer->Destroy();
|
|
mContentViewer = nsnull;
|
|
}
|
|
|
|
nsDocLoader::Destroy();
|
|
|
|
mParentWidget = nsnull;
|
|
mCurrentURI = nsnull;
|
|
|
|
if (mScriptGlobal) {
|
|
mScriptGlobal->SetDocShell(nsnull);
|
|
mScriptGlobal->SetGlobalObjectOwner(nsnull);
|
|
mScriptGlobal = nsnull;
|
|
}
|
|
|
|
mSessionHistory = nsnull;
|
|
SetTreeOwner(nsnull);
|
|
|
|
// Note: mContentListener can be null if Init() failed and we're being
|
|
// called from the destructor.
|
|
if (mContentListener) {
|
|
mContentListener->DropDocShellreference();
|
|
mContentListener->SetParentContentListener(nsnull);
|
|
}
|
|
|
|
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;
|
|
|
|
if (mContentViewer) {
|
|
//XXX Border figured in here or is that handled elsewhere?
|
|
NS_ENSURE_SUCCESS(mContentViewer->SetBounds(mBounds), NS_ERROR_FAILURE);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::GetPositionAndSize(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;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::Repaint(PRBool aForce)
|
|
{
|
|
nsCOMPtr<nsPresContext> 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;
|
|
|
|
if (!mParentWidget) {
|
|
// If the parent widget is set to null we don't want to hold
|
|
// on to the current device context any more since it is
|
|
// associated with the parent widget we no longer own. We'll
|
|
// need to create a new device context if one is needed again.
|
|
|
|
mDeviceContext = nsnull;
|
|
}
|
|
|
|
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<nsIPresShell> 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.
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> treeItem = this;
|
|
nsCOMPtr<nsIDocShellTreeItem> parentItem;
|
|
treeItem->GetParent(getter_AddRefs(parentItem));
|
|
while (parentItem) {
|
|
nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(treeItem));
|
|
docShell->GetPresShell(getter_AddRefs(presShell));
|
|
|
|
nsCOMPtr<nsIDocShell> parentDS = do_QueryInterface(parentItem);
|
|
nsCOMPtr<nsIPresShell> 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, &frame);
|
|
if (frame && !frame->AreAncestorViewsVisible()) {
|
|
*aVisibility = PR_FALSE;
|
|
return NS_OK;
|
|
}
|
|
|
|
treeItem = parentItem;
|
|
treeItem->GetParent(getter_AddRefs(parentItem));
|
|
}
|
|
|
|
*aVisibility = PR_TRUE;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::SetVisibility(PRBool aVisibility)
|
|
{
|
|
if (!mContentViewer)
|
|
return NS_OK;
|
|
if (aVisibility) {
|
|
NS_ENSURE_SUCCESS(EnsureContentViewer(), NS_ERROR_FAILURE);
|
|
mContentViewer->Show();
|
|
}
|
|
else if (mContentViewer)
|
|
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", (nsIDocShell*)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<nsIDocShellTreeItem> parent;
|
|
GetSameTypeParent(getter_AddRefs(parent));
|
|
|
|
// When title is set on the top object it should then be passed to the
|
|
// tree owner.
|
|
if (!parent) {
|
|
nsCOMPtr<nsIBaseWindow>
|
|
treeOwnerAsWin(do_QueryInterface(mTreeOwner));
|
|
if (treeOwnerAsWin)
|
|
treeOwnerAsWin->SetTitle(aTitle);
|
|
}
|
|
|
|
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 =
|
|
NS_STATIC_CAST(nsIFrame*, 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);
|
|
|
|
nsRefreshTimer *refreshTimer = new nsRefreshTimer();
|
|
NS_ENSURE_TRUE(refreshTimer, NS_ERROR_OUT_OF_MEMORY);
|
|
PRUint32 busyFlags = 0;
|
|
GetBusyFlags(&busyFlags);
|
|
|
|
nsCOMPtr<nsISupports> 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<nsITimer> 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::SetupRefreshURIFromHeader(nsIURI * aBaseURI,
|
|
const nsACString & aHeader)
|
|
{
|
|
// Refresh headers are parsed with the following format in mind
|
|
// <META HTTP-EQUIV=REFRESH CONTENT="5; URL=http://uri">
|
|
// By the time we are here, the following is true:
|
|
// header = "REFRESH"
|
|
// content = "5; URL=http://uri" // note the URL attribute is
|
|
// optional, if it is absent, the currently loaded url is used.
|
|
// Also note that the seconds and URL separator can be either
|
|
// a ';' or a ','. The ',' separator should be illegal but CNN
|
|
// is using it.
|
|
//
|
|
// We need to handle the following strings, where
|
|
// - X is a set of digits
|
|
// - URI is either a relative or absolute URI
|
|
//
|
|
// Note that URI should start with "url=" but we allow omission
|
|
//
|
|
// "" || ";" || ","
|
|
// empty string. use the currently loaded URI
|
|
// and refresh immediately.
|
|
// "X" || "X;" || "X,"
|
|
// Refresh the currently loaded URI in X seconds.
|
|
// "X; URI" || "X, URI"
|
|
// Refresh using URI as the destination in X seconds.
|
|
// "URI" || "; URI" || ", URI"
|
|
// Refresh immediately using URI as the destination.
|
|
//
|
|
// Currently, anything immediately following the URI, if
|
|
// separated by any char in the set "'\"\t\r\n " will be
|
|
// ignored. So "10; url=go.html ; foo=bar" will work,
|
|
// and so will "10; url='go.html'; foo=bar". However,
|
|
// "10; url=go.html; foo=bar" will result in the uri
|
|
// "go.html;" since ';' and ',' are valid uri characters.
|
|
//
|
|
// Note that we need to remove any tokens wrapping the URI.
|
|
// These tokens currently include spaces, double and single
|
|
// quotes.
|
|
|
|
// when done, seconds is 0 or the given number of seconds
|
|
// uriAttrib is empty or the URI specified
|
|
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<nsIURI> 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<nsIScriptSecurityManager>
|
|
securityManager(do_GetService
|
|
(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv));
|
|
if (NS_SUCCEEDED(rv)) {
|
|
rv = securityManager->CheckLoadURI(aBaseURI, uri,
|
|
nsIScriptSecurityManager::
|
|
DISALLOW_FROM_MAIL);
|
|
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<nsIHttpChannel> 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);
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::CancelRefreshURITimers()
|
|
{
|
|
if (!mRefreshURIList)
|
|
return NS_OK;
|
|
|
|
PRUint32 n=0;
|
|
mRefreshURIList->Count(&n);
|
|
|
|
while (n) {
|
|
nsCOMPtr<nsISupports> element;
|
|
mRefreshURIList->GetElementAt(--n, getter_AddRefs(element));
|
|
nsCOMPtr<nsITimer> timer(do_QueryInterface(element));
|
|
|
|
mRefreshURIList->RemoveElementAt(n); // bye bye owning timer ref
|
|
|
|
if (timer)
|
|
timer->Cancel();
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::SuspendRefreshURIs()
|
|
{
|
|
if (mRefreshURIList) {
|
|
PRUint32 n = 0;
|
|
mRefreshURIList->Count(&n);
|
|
|
|
for (PRUint32 i = 0; i < n; ++i) {
|
|
nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i);
|
|
if (!timer)
|
|
continue; // this must be a nsRefreshURI already
|
|
|
|
// Replace this timer object with a nsRefreshTimer object.
|
|
nsCOMPtr<nsITimerCallback> callback;
|
|
timer->GetCallback(getter_AddRefs(callback));
|
|
|
|
timer->Cancel();
|
|
|
|
nsCOMPtr<nsRefreshTimer> 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<nsIDocShell> 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<nsIDocShell> 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<nsISupports> element;
|
|
mRefreshURIList->GetElementAt(--n, getter_AddRefs(element));
|
|
nsCOMPtr<nsRefreshTimer> 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 = refreshInfo->GetDelay();
|
|
nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1");
|
|
if (timer) {
|
|
// Replace the nsRefreshTimer element in the queue with
|
|
// its corresponding timer object, so that in case another
|
|
// load comes through before the timer can go off, the timer will
|
|
// get cancelled in CancelRefreshURITimer()
|
|
mRefreshURIList->ReplaceElementAt(timer, n);
|
|
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)
|
|
SetHistoryEntry(&mOSHE, mLSHE);
|
|
|
|
PRBool updateHistory = PR_TRUE;
|
|
|
|
// Determine if this type of load should update history
|
|
switch (mLoadType) {
|
|
case LOAD_RELOAD_CHARSET_CHANGE: //don't perserve history in charset reload
|
|
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<nsIWyciwygChannel> wcwgChannel(do_QueryInterface(aRequest));
|
|
nsCOMPtr<nsIWebProgress> webProgress =
|
|
do_QueryInterface(GetAsSupports(this));
|
|
|
|
// Was the wyciwyg document loaded on this docshell?
|
|
if (wcwgChannel && !mLSHE && (mItemType == typeContent) && aProgress == webProgress.get()) {
|
|
nsCOMPtr<nsIURI> 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<nsIWidget> mainWidget;
|
|
GetMainWidget(getter_AddRefs(mainWidget));
|
|
if (mainWidget) {
|
|
mainWidget->SetCursor(eCursor_spinning);
|
|
}
|
|
}
|
|
else if ((~aStateFlags & (STATE_TRANSFERRING | STATE_IS_DOCUMENT)) == 0) {
|
|
// Page is loading
|
|
mBusyFlags = BUSY_FLAGS_BUSY | BUSY_FLAGS_PAGE_LOADING;
|
|
}
|
|
else if ((aStateFlags & STATE_STOP) && (aStateFlags & STATE_IS_NETWORK)) {
|
|
// Page has finished loading
|
|
mBusyFlags = BUSY_FLAGS_NONE;
|
|
nsCOMPtr<nsIWidget> mainWidget;
|
|
GetMainWidget(getter_AddRefs(mainWidget));
|
|
if (mainWidget) {
|
|
mainWidget->SetCursor(eCursor_standard);
|
|
}
|
|
}
|
|
if ((~aStateFlags & (STATE_IS_DOCUMENT | STATE_STOP)) == 0) {
|
|
nsCOMPtr<nsIWebProgress> webProgress =
|
|
do_QueryInterface(GetAsSupports(this));
|
|
// Is the document stop notification for this document?
|
|
if (aProgress == webProgress.get()) {
|
|
nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
|
|
EndPageLoad(aProgress, channel, aStatus);
|
|
}
|
|
}
|
|
else if ((~aStateFlags & (STATE_IS_DOCUMENT | STATE_REDIRECTING)) == 0) {
|
|
// XXX Is it enough if I check just for the above 2 flags for redirection
|
|
nsCOMPtr<nsIWebProgress> webProgress =
|
|
do_QueryInterface(GetAsSupports(this));
|
|
|
|
// Is the document stop notification for this document?
|
|
if (aProgress == webProgress.get()) {
|
|
nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
|
|
if (channel) {
|
|
// Get the uri from the channel
|
|
nsCOMPtr<nsIURI> uri;
|
|
nsCOMPtr<nsIURI> referrer;
|
|
channel->GetURI(getter_AddRefs(uri));
|
|
// Get the referrer uri from the channel
|
|
nsCOMPtr<nsIHttpChannel> httpchannel(do_QueryInterface(aRequest));
|
|
if (httpchannel)
|
|
httpchannel->GetReferrer(getter_AddRefs(referrer));
|
|
// Add the original url to global History so that
|
|
// visited url color changes happen.
|
|
if (uri)
|
|
AddToGlobalHistory(uri, PR_TRUE, referrer);
|
|
} // channel
|
|
} // aProgress
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::OnLocationChange(nsIWebProgress * aProgress,
|
|
nsIRequest * aRequest, nsIURI * aURI)
|
|
{
|
|
NS_NOTREACHED("notification excluded in AddProgressListener(...)");
|
|
return NS_OK;
|
|
}
|
|
|
|
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<nsIDocShell> kungFuDeathGrip(this);
|
|
//
|
|
// 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
|
|
PL_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<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
|
|
if (!httpChannel) // HttpChannel could be hiding underneath a Multipart channel.
|
|
GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
|
|
|
|
if (httpChannel) {
|
|
// figure out if SH should be saving layout state.
|
|
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;
|
|
|
|
return CreateAboutBlankContentViewer();
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::EnsureDeviceContext()
|
|
{
|
|
if (mDeviceContext)
|
|
return NS_OK;
|
|
|
|
mDeviceContext = do_CreateInstance(kDeviceContextCID);
|
|
NS_ENSURE_TRUE(mDeviceContext, NS_ERROR_FAILURE);
|
|
|
|
nsCOMPtr<nsIWidget> widget;
|
|
GetMainWidget(getter_AddRefs(widget));
|
|
NS_ENSURE_TRUE(widget, NS_ERROR_FAILURE);
|
|
|
|
mDeviceContext->Init(widget->GetNativeData(NS_NATIVE_WIDGET));
|
|
float dev2twip;
|
|
dev2twip = mDeviceContext->DevUnitsToTwips();
|
|
mDeviceContext->SetDevUnitsToAppUnits(dev2twip);
|
|
float twip2dev;
|
|
twip2dev = mDeviceContext->TwipsToDevUnits();
|
|
mDeviceContext->SetAppUnitsToDevUnits(twip2dev);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::CreateAboutBlankContentViewer()
|
|
{
|
|
nsCOMPtr<nsIDocument> blankDoc;
|
|
nsCOMPtr<nsIContentViewer> viewer;
|
|
nsresult rv = NS_ERROR_FAILURE;
|
|
|
|
/* mCreatingDocument should never be true at this point. However, it's
|
|
a theoretical possibility. We want to know about it and make it stop,
|
|
and this sounds like a job for an assertion. */
|
|
NS_ASSERTION(!mCreatingDocument, "infinite(?) loop creating document averted");
|
|
if (mCreatingDocument)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
mCreatingDocument = PR_TRUE;
|
|
|
|
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;
|
|
}
|
|
|
|
// 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(PR_TRUE);
|
|
}
|
|
|
|
// one helper factory, please
|
|
nsCOMPtr<nsICategoryManager> 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<nsIDocumentLoaderFactory> docFactory(do_GetService(contractId));
|
|
if (docFactory) {
|
|
// generate (about:blank) document to load
|
|
docFactory->CreateBlankDocument(mLoadGroup, getter_AddRefs(blankDoc));
|
|
if (blankDoc) {
|
|
blankDoc->SetContainer(NS_STATIC_CAST(nsIDocShell *, 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(NS_STATIC_CAST(nsIContentViewerContainer *,this));
|
|
nsCOMPtr<nsIDOMDocument> domdoc(do_QueryInterface(blankDoc));
|
|
Embed(viewer, "", 0);
|
|
viewer->SetDOMDocument(domdoc);
|
|
|
|
SetCurrentURI(blankDoc->GetDocumentURI(), nsnull, PR_TRUE);
|
|
rv = NS_OK;
|
|
}
|
|
}
|
|
}
|
|
mCreatingDocument = PR_FALSE;
|
|
return rv;
|
|
}
|
|
|
|
PRBool
|
|
nsDocShell::CanSavePresentation(PRUint32 aLoadType, nsIRequest *aNewRequest)
|
|
{
|
|
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<nsPIDOMWindow> pWin = do_QueryInterface(mScriptGlobal);
|
|
if (!pWin || pWin->IsLoading())
|
|
return PR_FALSE;
|
|
|
|
// Avoid doing the work of saving the presentation state in the case where
|
|
// the content viewer cache is disabled.
|
|
PRInt32 maxViewers = 0;
|
|
mPrefs->GetIntPref("browser.sessionhistory.max_viewers", &maxViewers);
|
|
if (maxViewers == 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<nsIDocShellTreeItem> 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<nsIDocument> doc = do_QueryInterface(pWin->GetExtantDocument());
|
|
if (!doc || !doc->CanSavePresentation(aNewRequest))
|
|
return PR_FALSE;
|
|
|
|
return PR_TRUE;
|
|
}
|
|
|
|
nsresult
|
|
nsDocShell::CaptureState()
|
|
{
|
|
if (!mOSHE || mOSHE == mLSHE) {
|
|
// No entry to save into, or we're replacing the existing entry.
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
nsCOMPtr<nsPIDOMWindow> privWin = do_QueryInterface(mScriptGlobal);
|
|
if (!privWin)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
nsCOMPtr<nsISupports> windowState;
|
|
nsresult rv = privWin->SaveWindowState(getter_AddRefs(windowState));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
#ifdef DEBUG_PAGE_CACHE
|
|
nsCOMPtr<nsIURI> 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<nsIPresShell> 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<nsIDocShellTreeItem> childShell = do_QueryInterface(ChildAt(i));
|
|
NS_ASSERTION(childShell, "null child shell");
|
|
|
|
mOSHE->AddChildShell(childShell);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
class RestorePresentationEvent : public PLEvent
|
|
{
|
|
public:
|
|
RestorePresentationEvent(nsDocShell *aShell);
|
|
|
|
nsRefPtr<nsDocShell> mDocShell;
|
|
};
|
|
|
|
PR_STATIC_CALLBACK(void*)
|
|
HandleRestorePresentationEvent(PLEvent *aEvent)
|
|
{
|
|
RestorePresentationEvent *event =
|
|
NS_STATIC_CAST(RestorePresentationEvent*, aEvent);
|
|
|
|
nsresult rv = event->mDocShell->RestoreFromHistory();
|
|
NS_ASSERTION(NS_SUCCEEDED(rv), "RestoreFromHistory failed");
|
|
return nsnull;
|
|
}
|
|
|
|
PR_STATIC_CALLBACK(void)
|
|
DestroyRestorePresentationEvent(PLEvent *aEvent)
|
|
{
|
|
delete NS_STATIC_CAST(RestorePresentationEvent*, aEvent);
|
|
}
|
|
|
|
RestorePresentationEvent::RestorePresentationEvent(nsDocShell *aShell)
|
|
: mDocShell(aShell)
|
|
{
|
|
PL_InitEvent(this, mDocShell, ::HandleRestorePresentationEvent,
|
|
::DestroyRestorePresentationEvent);
|
|
}
|
|
|
|
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<nsIDOMDocument> domDoc;
|
|
aContentViewer->GetDOMDocument(getter_AddRefs(domDoc));
|
|
nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
|
|
if (doc) {
|
|
nsIChannel *channel = doc->GetChannel();
|
|
if (channel) {
|
|
mIsRestoringDocument = PR_TRUE;
|
|
mLoadGroup->AddRequest(channel, nsnull);
|
|
mIsRestoringDocument = PR_FALSE;
|
|
}
|
|
}
|
|
|
|
if (!aTop) {
|
|
// 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<nsIDocShell> 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<nsIDocShell> child = do_QueryInterface(ChildAt(i));
|
|
if (child) {
|
|
child->FinishRestore();
|
|
}
|
|
}
|
|
|
|
if (mContentViewer) {
|
|
nsCOMPtr<nsIDOMDocument> domDoc;
|
|
mContentViewer->GetDOMDocument(getter_AddRefs(domDoc));
|
|
|
|
nsCOMPtr<nsIDocument> 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<nsIContentViewer> viewer;
|
|
aSHEntry->GetContentViewer(getter_AddRefs(viewer));
|
|
|
|
#ifdef DEBUG_PAGE_CACHE
|
|
nsCOMPtr<nsIURI> 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<nsISupports> 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 a PLEvent that will remove the request after we've returned
|
|
// to the event loop. This mimics the way it is called by nsIChannel
|
|
// implementations.
|
|
|
|
nsCOMPtr<nsIEventQueue> uiThreadQueue;
|
|
NS_GetMainEventQ(getter_AddRefs(uiThreadQueue));
|
|
NS_ENSURE_TRUE(uiThreadQueue, NS_ERROR_UNEXPECTED);
|
|
|
|
PLEvent *evt = new RestorePresentationEvent(this);
|
|
NS_ENSURE_TRUE(evt, NS_ERROR_OUT_OF_MEMORY);
|
|
|
|
nsresult rv = uiThreadQueue->PostEvent(evt);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
// The rest of the restore processing will happen on our PLEvent
|
|
// callback.
|
|
*aRestoring = PR_TRUE;
|
|
} else {
|
|
PL_DestroyEvent(evt);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsDocShell::RestoreFromHistory()
|
|
{
|
|
// This section of code follows the same ordering as CreateContentViewer.
|
|
if (!mLSHE)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
nsCOMPtr<nsIContentViewer> viewer;
|
|
mLSHE->GetContentViewer(getter_AddRefs(viewer));
|
|
if (!viewer)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
if (mSavingOldViewer) {
|
|
// We determined that it was safe to cache the document presentation
|
|
// at the time we initiated the new load. We need to check whether
|
|
// it's still safe to do so, since there may have been DOM mutations
|
|
// or new requests initiated.
|
|
nsCOMPtr<nsIDOMDocument> domDoc;
|
|
viewer->GetDOMDocument(getter_AddRefs(domDoc));
|
|
nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
|
|
nsIRequest *request = nsnull;
|
|
if (doc)
|
|
request = doc->GetChannel();
|
|
mSavingOldViewer = CanSavePresentation(mLoadType, request);
|
|
}
|
|
|
|
nsCOMPtr<nsIMarkupDocumentViewer> oldMUDV(do_QueryInterface(mContentViewer));
|
|
nsCOMPtr<nsIMarkupDocumentViewer> newMUDV(do_QueryInterface(viewer));
|
|
float zoom = 1.0;
|
|
if (oldMUDV && newMUDV)
|
|
oldMUDV->GetTextZoom(&zoom);
|
|
|
|
// Notify the old content viewer that it's being hidden.
|
|
FirePageHideNotification(!mSavingOldViewer);
|
|
// Set mFiredUnloadEvent = PR_FALSE so that the unload handler for the
|
|
// *new* document will fire.
|
|
mFiredUnloadEvent = PR_FALSE;
|
|
|
|
mURIResultedInDocument = PR_TRUE;
|
|
nsCOMPtr<nsISHistory> rootSH;
|
|
GetRootSessionHistory(getter_AddRefs(rootSH));
|
|
if (rootSH) {
|
|
nsCOMPtr<nsISHistoryInternal> hist = do_QueryInterface(rootSH);
|
|
hist->UpdateIndex();
|
|
}
|
|
|
|
// 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()?
|
|
// XXX PersistLayoutHistoryState() ?
|
|
nsresult rv;
|
|
if (mContentViewer) {
|
|
if (mSavingOldViewer && NS_FAILED(CaptureState())) {
|
|
if (mOSHE) {
|
|
mOSHE->SyncPresentationState();
|
|
}
|
|
mSavingOldViewer = PR_FALSE;
|
|
}
|
|
}
|
|
|
|
mSavedRefreshURIList = nsnull;
|
|
|
|
// 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<nsIPresShell> 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);
|
|
mContentViewer->Destroy();
|
|
}
|
|
|
|
mContentViewer.swap(viewer);
|
|
viewer = nsnull; // force a release to complete ownership transfer
|
|
|
|
// Grab the window state up here so we can pass it to Open.
|
|
nsCOMPtr<nsISupports> windowState;
|
|
mLSHE->GetWindowState(getter_AddRefs(windowState));
|
|
|
|
mLSHE->SetWindowState(nsnull);
|
|
|
|
// Reattach to the window object.
|
|
rv = mContentViewer->Open(windowState);
|
|
|
|
// Now remove it from the cached presentation.
|
|
mLSHE->SetContentViewer(nsnull);
|
|
mEODForCurrentDocument = PR_FALSE;
|
|
|
|
// 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.
|
|
PRBool sticky;
|
|
mLSHE->GetSticky(&sticky);
|
|
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()?
|
|
// XXX SetLayoutHistoryState(null)?
|
|
// 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)
|
|
PL_FavorPerformanceHint(PR_TRUE, NS_EVENT_STARVATION_DELAY_HINT);
|
|
|
|
|
|
if (oldMUDV && newMUDV)
|
|
newMUDV->SetTextZoom(zoom);
|
|
|
|
nsCOMPtr<nsIDOMDocument> domDoc;
|
|
mContentViewer->GetDOMDocument(getter_AddRefs(domDoc));
|
|
nsCOMPtr<nsIDocument> document = do_QueryInterface(domDoc);
|
|
if (document) {
|
|
SetCurrentURI(document->GetDocumentURI(),
|
|
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<nsPIDOMWindow> privWin =
|
|
do_GetInterface(NS_STATIC_CAST(nsIInterfaceRequestor*, 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 happed as the
|
|
// <head> is parsed.
|
|
nsCOMPtr<nsIDOMNSDocument> nsDoc = do_QueryInterface(document);
|
|
if (nsDoc) {
|
|
const nsAFlatString &title = document->GetDocumentTitle();
|
|
nsDoc->SetTitle(title);
|
|
}
|
|
|
|
// Now we simulate appending child docshells for subframes.
|
|
PRInt32 i = 0;
|
|
nsCOMPtr<nsIDocShellTreeItem> child;
|
|
while (NS_SUCCEEDED(mLSHE->ChildShellAt(i++, getter_AddRefs(child))) &&
|
|
child) {
|
|
AddChild(child);
|
|
|
|
nsCOMPtr<nsIDocShell> childShell = do_QueryInterface(child);
|
|
rv = childShell->BeginRestore(nsnull, PR_FALSE);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
// And release the references in the history entry.
|
|
mLSHE->ClearChildShells();
|
|
|
|
// get the previous content viewer size
|
|
nsRect oldBounds(0, 0, 0, 0);
|
|
mLSHE->GetViewerBounds(oldBounds);
|
|
|
|
nsCOMPtr<nsIPresShell> 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");
|
|
}
|
|
}
|
|
|
|
// Restore the refresh URI list. The refresh timers will be restarted
|
|
// when EndPageLoad() is called.
|
|
mLSHE->GetRefreshURIList(getter_AddRefs(mRefreshURIList));
|
|
mLSHE->SetRefreshURIList(nsnull);
|
|
|
|
// 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<nsIDocShell> 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 NS_OK;
|
|
}
|
|
|
|
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<nsIContentViewer> 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.
|
|
mSavingOldViewer = CanSavePresentation(mLoadType, request);
|
|
}
|
|
|
|
FirePageHideNotification(!mSavingOldViewer);
|
|
|
|
// 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;
|
|
|
|
nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(request);
|
|
|
|
PRBool onLocationChangeNeeded = OnLoadingSite(aOpenedChannel, PR_FALSE);
|
|
|
|
// let's try resetting the load group if we need to...
|
|
nsCOMPtr<nsILoadGroup> currentLoadGroup;
|
|
NS_ENSURE_SUCCESS(aOpenedChannel->
|
|
GetLoadGroup(getter_AddRefs(currentLoadGroup)),
|
|
NS_ERROR_FAILURE);
|
|
|
|
if (currentLoadGroup != mLoadGroup) {
|
|
nsLoadFlags loadFlags = 0;
|
|
|
|
//Cancel any URIs that are currently loading...
|
|
/// XXX: Need to do this eventually Stop();
|
|
//
|
|
// Retarget the document to this loadgroup...
|
|
//
|
|
/* First attach the channel to the right loadgroup
|
|
* and then remove from the old loadgroup. This
|
|
* puts the notifications in the right order and
|
|
* we don't null-out mLSHE in OnStateChange() for
|
|
* all redirected urls
|
|
*/
|
|
aOpenedChannel->SetLoadGroup(mLoadGroup);
|
|
|
|
// Mark the channel as being a document URI...
|
|
aOpenedChannel->GetLoadFlags(&loadFlags);
|
|
loadFlags |= nsIChannel::LOAD_DOCUMENT_URI;
|
|
|
|
aOpenedChannel->SetLoadFlags(loadFlags);
|
|
|
|
mLoadGroup->AddRequest(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<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(request));
|
|
if (multiPartChannel) {
|
|
nsCOMPtr<nsIPresShell> 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.
|
|
PL_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<nsIChannel> aOpenedChannel = do_QueryInterface(request);
|
|
|
|
nsresult rv;
|
|
nsCOMPtr<nsICategoryManager> 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<nsIDocumentLoaderFactory> 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,
|
|
NS_STATIC_CAST
|
|
(nsIContentViewerContainer
|
|
*, this), nsnull,
|
|
aContentHandler,
|
|
aViewer),
|
|
NS_ERROR_FAILURE);
|
|
|
|
(*aViewer)->SetContainer(NS_STATIC_CAST(nsIContentViewerContainer *, 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 <SCRIPT>s and event handlers fail! (bug 20315)
|
|
//
|
|
// In this block of code, if we get an error result, we return it
|
|
// but if we get a null pointer, that's perfectly legal for parent
|
|
// and parentContentViewer.
|
|
//
|
|
|
|
PRInt32 x = 0;
|
|
PRInt32 y = 0;
|
|
PRInt32 cx = 0;
|
|
PRInt32 cy = 0;
|
|
|
|
// This will get the size from the current content viewer or from the
|
|
// Init settings
|
|
GetPositionAndSize(&x, &y, &cx, &cy);
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
|
|
NS_ENSURE_SUCCESS(GetSameTypeParent(getter_AddRefs(parentAsItem)),
|
|
NS_ERROR_FAILURE);
|
|
nsCOMPtr<nsIDocShell> parent(do_QueryInterface(parentAsItem));
|
|
|
|
nsCAutoString defaultCharset;
|
|
nsCAutoString forceCharset;
|
|
nsCAutoString hintCharset;
|
|
PRInt32 hintCharsetSource;
|
|
nsCAutoString prevDocCharset;
|
|
float textZoom;
|
|
PRBool styleDisabled;
|
|
// |newMUDV| also serves as a flag to set the data from the above vars
|
|
nsCOMPtr<nsIMarkupDocumentViewer> newMUDV;
|
|
|
|
if (mContentViewer || parent) {
|
|
nsCOMPtr<nsIMarkupDocumentViewer> oldMUDV;
|
|
if (mContentViewer) {
|
|
// Get any interesting state from old content viewer
|
|
// XXX: it would be far better to just reuse the document viewer ,
|
|
// since we know we're just displaying the same document as before
|
|
oldMUDV = do_QueryInterface(mContentViewer);
|
|
|
|
// Tell the old content viewer to hibernate in session history when
|
|
// it is destroyed.
|
|
|
|
if (mSavingOldViewer && NS_FAILED(CaptureState())) {
|
|
if (mOSHE) {
|
|
mOSHE->SyncPresentationState();
|
|
}
|
|
mSavingOldViewer = PR_FALSE;
|
|
}
|
|
}
|
|
else {
|
|
// No old content viewer, so get state from parent's content viewer
|
|
nsCOMPtr<nsIContentViewer> parentContentViewer;
|
|
parent->GetContentViewer(getter_AddRefs(parentContentViewer));
|
|
oldMUDV = do_QueryInterface(parentContentViewer);
|
|
}
|
|
|
|
if (oldMUDV) {
|
|
nsresult rv;
|
|
|
|
newMUDV = do_QueryInterface(aNewViewer,&rv);
|
|
if (newMUDV) {
|
|
NS_ENSURE_SUCCESS(oldMUDV->
|
|
GetDefaultCharacterSet(defaultCharset),
|
|
NS_ERROR_FAILURE);
|
|
NS_ENSURE_SUCCESS(oldMUDV->
|
|
GetForceCharacterSet(forceCharset),
|
|
NS_ERROR_FAILURE);
|
|
NS_ENSURE_SUCCESS(oldMUDV->
|
|
GetHintCharacterSet(hintCharset),
|
|
NS_ERROR_FAILURE);
|
|
NS_ENSURE_SUCCESS(oldMUDV->
|
|
GetHintCharacterSetSource(&hintCharsetSource),
|
|
NS_ERROR_FAILURE);
|
|
NS_ENSURE_SUCCESS(oldMUDV->
|
|
GetTextZoom(&textZoom),
|
|
NS_ERROR_FAILURE);
|
|
NS_ENSURE_SUCCESS(oldMUDV->
|
|
GetAuthorStyleDisabled(&styleDisabled),
|
|
NS_ERROR_FAILURE);
|
|
NS_ENSURE_SUCCESS(oldMUDV->
|
|
GetPrevDocCharacterSet(prevDocCharset),
|
|
NS_ERROR_FAILURE);
|
|
}
|
|
}
|
|
}
|
|
|
|
// It is necessary to obtain the focus controller to utilize its ability
|
|
// to suppress focus. This is necessary to fix Win32-only bugs related to
|
|
// a loss of focus when mContentViewer is set to null. The internal window
|
|
// is destroyed, and the OS focuses the parent window. This call ends up
|
|
// notifying the focus controller that the outer window should focus
|
|
// and this hoses us on any link traversal.
|
|
//
|
|
// Please do not touch any of the focus controller code here without
|
|
// testing bugs #28580 and 50509. These are immensely important bugs,
|
|
// so PLEASE take care not to regress them if you decide to alter this
|
|
// code later -- hyatt
|
|
nsIFocusController *focusController = nsnull;
|
|
if (mScriptGlobal) {
|
|
nsCOMPtr<nsPIDOMWindow> ourWindow = do_QueryInterface(mScriptGlobal);
|
|
focusController = ourWindow->GetRootFocusController();
|
|
if (focusController) {
|
|
// Suppress the command dispatcher.
|
|
focusController->SetSuppressFocus(PR_TRUE,
|
|
"Win32-Only Link Traversal Issue");
|
|
// Remove focus from the element that has it
|
|
nsCOMPtr<nsIDOMWindowInternal> focusedWindow;
|
|
focusController->GetFocusedWindow(getter_AddRefs(focusedWindow));
|
|
|
|
// We want to null out the last focused element if the document containing
|
|
// it is going away. If the last focused element is in a descendent
|
|
// window of our domwindow, its document will be destroyed when we
|
|
// destroy our children. So, check for this case and null out the
|
|
// last focused element. See bug 70484.
|
|
|
|
PRBool isSubWindow = PR_FALSE;
|
|
nsCOMPtr<nsIDOMWindow> curwin;
|
|
if (focusedWindow)
|
|
focusedWindow->GetParent(getter_AddRefs(curwin));
|
|
while (curwin) {
|
|
if (curwin == ourWindow) {
|
|
isSubWindow = PR_TRUE;
|
|
break;
|
|
}
|
|
|
|
// don't use nsCOMPtr here to avoid extra addref
|
|
// when assigning to curwin
|
|
nsIDOMWindow* temp;
|
|
curwin->GetParent(&temp);
|
|
if (curwin == temp) {
|
|
NS_RELEASE(temp);
|
|
break;
|
|
}
|
|
curwin = dont_AddRef(temp);
|
|
}
|
|
|
|
if (ourWindow == focusedWindow || isSubWindow)
|
|
focusController->ResetElementFocus();
|
|
}
|
|
}
|
|
|
|
nscolor bgcolor = NS_RGBA(0, 0, 0, 0);
|
|
PRBool bgSet = PR_FALSE;
|
|
|
|
// Ensure that the content viewer is destroyed *after* the GC - bug 71515
|
|
nsCOMPtr<nsIContentViewer> kungfuDeathGrip = mContentViewer;
|
|
if (mContentViewer) {
|
|
// Stop any activity that may be happening in the old document before
|
|
// releasing it...
|
|
mContentViewer->Stop();
|
|
|
|
// Try to extract the default background color from the old
|
|
// view manager, so we can use it for the next document.
|
|
nsCOMPtr<nsIDocumentViewer> docviewer =
|
|
do_QueryInterface(mContentViewer);
|
|
|
|
if (docviewer) {
|
|
nsCOMPtr<nsIPresShell> shell;
|
|
docviewer->GetPresShell(getter_AddRefs(shell));
|
|
|
|
if (shell) {
|
|
nsIViewManager* vm = shell->GetViewManager();
|
|
|
|
if (vm) {
|
|
vm->GetDefaultBackgroundColor(&bgcolor);
|
|
// If the background color is not known, don't propagate it.
|
|
bgSet = NS_GET_A(bgcolor) != 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
mContentViewer->Close(mSavingOldViewer ? mOSHE.get() : nsnull);
|
|
aNewViewer->SetPreviousViewer(mContentViewer);
|
|
|
|
mContentViewer = nsnull;
|
|
}
|
|
|
|
mContentViewer = aNewViewer;
|
|
|
|
nsCOMPtr<nsIWidget> widget;
|
|
NS_ENSURE_SUCCESS(GetMainWidget(getter_AddRefs(widget)), NS_ERROR_FAILURE);
|
|
|
|
if (widget) {
|
|
NS_ENSURE_SUCCESS(EnsureDeviceContext(), NS_ERROR_FAILURE);
|
|
}
|
|
|
|
nsRect bounds(x, y, cx, cy);
|
|
|
|
if (NS_FAILED(mContentViewer->Init(widget, mDeviceContext, bounds))) {
|
|
mContentViewer = nsnull;
|
|
NS_ERROR("ContentViewer Initialization failed");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
// If we have old state to copy, set the old state onto the new content
|
|
// viewer
|
|
if (newMUDV) {
|
|
NS_ENSURE_SUCCESS(newMUDV->SetDefaultCharacterSet(defaultCharset),
|
|
NS_ERROR_FAILURE);
|
|
NS_ENSURE_SUCCESS(newMUDV->SetForceCharacterSet(forceCharset),
|
|
NS_ERROR_FAILURE);
|
|
NS_ENSURE_SUCCESS(newMUDV->SetHintCharacterSet(hintCharset),
|
|
NS_ERROR_FAILURE);
|
|
NS_ENSURE_SUCCESS(newMUDV->
|
|
SetHintCharacterSetSource(hintCharsetSource),
|
|
NS_ERROR_FAILURE);
|
|
NS_ENSURE_SUCCESS(newMUDV->SetPrevDocCharacterSet(prevDocCharset),
|
|
NS_ERROR_FAILURE);
|
|
NS_ENSURE_SUCCESS(newMUDV->SetTextZoom(textZoom),
|
|
NS_ERROR_FAILURE);
|
|
NS_ENSURE_SUCCESS(newMUDV->SetAuthorStyleDisabled(styleDisabled),
|
|
NS_ERROR_FAILURE);
|
|
}
|
|
|
|
// End copying block (Don't mess with the old content/document viewer
|
|
// beyond here!!)
|
|
|
|
// See the book I wrote above regarding why the focus controller is
|
|
// being used here. -- hyatt
|
|
|
|
/* Note it's important that focus suppression be turned off no earlier
|
|
because in cases where the docshell is lazily creating an about:blank
|
|
document, mContentViewer->Init finally puts a reference to that
|
|
document into the DOM window, which prevents an infinite recursion
|
|
attempting to lazily create the document as focus is unsuppressed
|
|
(bug 110856). */
|
|
if (focusController)
|
|
focusController->SetSuppressFocus(PR_FALSE,
|
|
"Win32-Only Link Traversal Issue");
|
|
|
|
if (bgSet && widget) {
|
|
// Stuff the bgcolor from the last view manager into the new
|
|
// view manager. This improves page load continuity.
|
|
nsCOMPtr<nsIDocumentViewer> docviewer =
|
|
do_QueryInterface(mContentViewer);
|
|
|
|
if (docviewer) {
|
|
nsCOMPtr<nsIPresShell> shell;
|
|
docviewer->GetPresShell(getter_AddRefs(shell));
|
|
|
|
if (shell) {
|
|
nsIViewManager* vm = shell->GetViewManager();
|
|
|
|
if (vm) {
|
|
vm->SetDefaultBackgroundColor(bgcolor);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// XXX: It looks like the LayoutState gets restored again in Embed()
|
|
// right after the call to SetupNewViewer(...)
|
|
|
|
// We don't show the mContentViewer yet, since we want to draw the old page
|
|
// until we have enough of the new page to show. Just return with the new
|
|
// viewer still set to hidden.
|
|
|
|
// Now that we have switched documents, forget all of our children
|
|
DestroyChildren();
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
nsresult
|
|
nsDocShell::CheckLoadingPermissions()
|
|
{
|
|
// This method checks whether the caller may load content into
|
|
// this docshell. Even though we've done our best to hide windows
|
|
// from code that doesn't have the right to access them, it's
|
|
// still possible for an evil site to open a window and access
|
|
// frames in the new window through window.frames[] (which is
|
|
// allAccess for historic reasons), so we still need to do this
|
|
// check on load.
|
|
nsresult rv = NS_OK, sameOrigin = NS_OK;
|
|
|
|
if (!gValidateOrigin || !IsFrame()) {
|
|
// Origin validation was turned off, or we're not a frame.
|
|
// Permit all loads.
|
|
|
|
return rv;
|
|
}
|
|
|
|
// We're a frame. Check that the caller has write permission to
|
|
// the parent before allowing it to load anything into this
|
|
// docshell.
|
|
|
|
nsCOMPtr<nsIScriptSecurityManager> securityManager =
|
|
do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
PRBool ubwEnabled = PR_FALSE;
|
|
rv = securityManager->IsCapabilityEnabled("UniversalBrowserWrite",
|
|
&ubwEnabled);
|
|
if (NS_FAILED(rv) || ubwEnabled) {
|
|
return rv;
|
|
}
|
|
|
|
nsCOMPtr<nsIPrincipal> subjPrincipal;
|
|
rv = securityManager->GetSubjectPrincipal(getter_AddRefs(subjPrincipal));
|
|
NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && subjPrincipal, rv);
|
|
|
|
// Check if the caller is from the same origin as this docshell,
|
|
// or any of it's ancestors.
|
|
nsCOMPtr<nsIDocShellTreeItem> item(this);
|
|
do {
|
|
nsCOMPtr<nsIScriptGlobalObject> sgo(do_GetInterface(item));
|
|
nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(sgo));
|
|
|
|
nsIPrincipal *p;
|
|
if (!sop || !(p = sop->GetPrincipal())) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
// Compare origins
|
|
sameOrigin =
|
|
securityManager->CheckSameOriginPrincipal(subjPrincipal, p);
|
|
if (NS_SUCCEEDED(sameOrigin)) {
|
|
// Same origin, permit load
|
|
|
|
return sameOrigin;
|
|
}
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> tmp;
|
|
item->GetSameTypeParent(getter_AddRefs(tmp));
|
|
item.swap(tmp);
|
|
} while (item);
|
|
|
|
// The caller is not from the same origin as this item, or any if
|
|
// this items ancestors. Only permit loading content if both are
|
|
// part of the same window, assuming we can find the window of the
|
|
// caller.
|
|
|
|
nsCOMPtr<nsIJSContextStack> stack =
|
|
do_GetService("@mozilla.org/js/xpc/ContextStack;1");
|
|
if (!stack) {
|
|
// No context stack available. Should never happen, but in
|
|
// case it does, return the sameOrigin error from the security
|
|
// check above.
|
|
|
|
return sameOrigin;
|
|
}
|
|
|
|
JSContext *cx = nsnull;
|
|
stack->Peek(&cx);
|
|
|
|
if (!cx) {
|
|
// No caller docshell reachable, return the sameOrigin error
|
|
// from the security check above.
|
|
|
|
return sameOrigin;
|
|
}
|
|
|
|
nsIScriptContext *currentCX = GetScriptContextFromJSContext(cx);
|
|
nsCOMPtr<nsIDocShellTreeItem> callerTreeItem;
|
|
nsIScriptGlobalObject *sgo;
|
|
if (currentCX &&
|
|
(sgo = currentCX->GetGlobalObject()) &&
|
|
(callerTreeItem = do_QueryInterface(sgo->GetDocShell()))) {
|
|
nsCOMPtr<nsIDocShellTreeItem> callerRoot;
|
|
callerTreeItem->GetSameTypeRootTreeItem(getter_AddRefs(callerRoot));
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> ourRoot;
|
|
GetSameTypeRootTreeItem(getter_AddRefs(ourRoot));
|
|
|
|
if (ourRoot == callerRoot) {
|
|
// The running JS is in the same window as the target
|
|
// frame, permit load.
|
|
sameOrigin = NS_OK;
|
|
}
|
|
}
|
|
|
|
return sameOrigin;
|
|
}
|
|
|
|
//*****************************************************************************
|
|
// nsDocShell: Site Loading
|
|
//*****************************************************************************
|
|
NS_IMETHODIMP
|
|
nsDocShell::InternalLoad(nsIURI * aURI,
|
|
nsIURI * aReferrer,
|
|
nsISupports * aOwner,
|
|
PRUint32 aFlags,
|
|
const PRUnichar *aWindowTarget,
|
|
const char* aTypeHint,
|
|
nsIInputStream * aPostData,
|
|
nsIInputStream * aHeadersData,
|
|
PRUint32 aLoadType,
|
|
nsISHEntry * aSHEntry,
|
|
PRBool aFirstParty,
|
|
nsIDocShell** aDocShell,
|
|
nsIRequest** aRequest)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
|
|
// Initialize aDocShell/aRequest
|
|
if (aDocShell) {
|
|
*aDocShell = nsnull;
|
|
}
|
|
if (aRequest) {
|
|
*aRequest = nsnull;
|
|
}
|
|
|
|
if (!aURI) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
|
|
NS_ENSURE_TRUE(IsValidLoadType(aLoadType), NS_ERROR_INVALID_ARG);
|
|
|
|
// wyciwyg urls can only be loaded through history. Any normal load of
|
|
// wyciwyg through docshell is illegal. Disallow such loads.
|
|
if (aLoadType & LOAD_CMD_NORMAL) {
|
|
PRBool isWyciwyg = PR_FALSE;
|
|
rv = aURI->SchemeIs("wyciwyg", &isWyciwyg);
|
|
if ((isWyciwyg && NS_SUCCEEDED(rv)) || NS_FAILED(rv))
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
PRBool bIsJavascript = PR_FALSE;
|
|
if (NS_FAILED(aURI->SchemeIs("javascript", &bIsJavascript))) {
|
|
bIsJavascript = PR_FALSE;
|
|
}
|
|
|
|
//
|
|
// First, notify any nsIContentPolicy listeners about the document load.
|
|
// Only abort the load if a content policy listener explicitly vetos it!
|
|
//
|
|
nsCOMPtr<nsIDOMElement> requestingElement;
|
|
// Use nsPIDOMWindow since we _want_ to cross the chrome boundary if needed
|
|
nsCOMPtr<nsPIDOMWindow> privateWin(do_QueryInterface(mScriptGlobal));
|
|
if (privateWin)
|
|
requestingElement = privateWin->GetFrameElementInternal();
|
|
|
|
PRInt16 shouldLoad = nsIContentPolicy::ACCEPT;
|
|
PRUint32 contentType;
|
|
if (IsFrame()) {
|
|
NS_ASSERTION(requestingElement, "A frame but no DOM element!?");
|
|
contentType = nsIContentPolicy::TYPE_SUBDOCUMENT;
|
|
} else {
|
|
contentType = nsIContentPolicy::TYPE_DOCUMENT;
|
|
}
|
|
|
|
nsISupports* context = requestingElement;
|
|
if (!context) {
|
|
context = mScriptGlobal;
|
|
}
|
|
rv = NS_CheckContentLoadPolicy(contentType,
|
|
aURI,
|
|
aReferrer,
|
|
context,
|
|
EmptyCString(), //mime guess
|
|
nsnull, //extra
|
|
&shouldLoad);
|
|
|
|
if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
|
|
if (NS_SUCCEEDED(rv) && shouldLoad == nsIContentPolicy::REJECT_TYPE) {
|
|
return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
|
|
}
|
|
|
|
return NS_ERROR_CONTENT_BLOCKED;
|
|
}
|
|
|
|
nsCOMPtr<nsISupports> owner(aOwner);
|
|
//
|
|
// Get an owner from the current document if necessary
|
|
//
|
|
if (!owner && (aFlags & INTERNAL_LOAD_FLAGS_INHERIT_OWNER))
|
|
GetCurrentDocumentOwner(getter_AddRefs(owner));
|
|
|
|
//
|
|
// Resolve the window target before going any further...
|
|
// If the load has been targeted to another DocShell, then transfer the
|
|
// load to it...
|
|
//
|
|
if (aWindowTarget && *aWindowTarget) {
|
|
PRBool bIsNewWindow;
|
|
nsCOMPtr<nsIDocShell> targetDocShell;
|
|
nsAutoString name(aWindowTarget);
|
|
|
|
//
|
|
// This is a hack to prevent top-level windows from ever being
|
|
// created. It really doesn't belong here, but until there is a
|
|
// way for embeddors to get involved in window targeting, this is
|
|
// as good a place as any...
|
|
// XXXbz no, it's not.... fixme!!!!
|
|
//
|
|
PRInt32 linkPref = nsIBrowserDOMWindow::OPEN_DEFAULTWINDOW;
|
|
mPrefs->GetIntPref("browser.link.open_newwindow", &linkPref);
|
|
if (linkPref == nsIBrowserDOMWindow::OPEN_CURRENTWINDOW) {
|
|
PRBool bIsChromeOrResource = PR_FALSE;
|
|
if (mCurrentURI)
|
|
mCurrentURI->SchemeIs("chrome", &bIsChromeOrResource);
|
|
if (!bIsChromeOrResource) {
|
|
aURI->SchemeIs("chrome", &bIsChromeOrResource);
|
|
if (!bIsChromeOrResource) {
|
|
aURI->SchemeIs("resource", &bIsChromeOrResource);
|
|
}
|
|
}
|
|
if (!bIsChromeOrResource) {
|
|
if (name.LowerCaseEqualsLiteral("_blank") ||
|
|
name.LowerCaseEqualsLiteral("_new")) {
|
|
name.AssignLiteral("_top");
|
|
}
|
|
// _main is an IE target which should be case-insensitive but isn't
|
|
// see bug 217886 for details
|
|
else if (!name.LowerCaseEqualsLiteral("_parent") &&
|
|
!name.LowerCaseEqualsLiteral("_self") &&
|
|
!name.LowerCaseEqualsLiteral("_content") &&
|
|
!name.EqualsLiteral("_main")) {
|
|
nsCOMPtr<nsIDocShellTreeItem> targetTreeItem;
|
|
FindItemWithName(name.get(),
|
|
nsnull,
|
|
this,
|
|
getter_AddRefs(targetTreeItem));
|
|
if (targetTreeItem)
|
|
targetDocShell = do_QueryInterface(targetTreeItem);
|
|
else
|
|
name.AssignLiteral("_top");
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Locate the target DocShell.
|
|
// This may involve creating a new toplevel window - if necessary.
|
|
//
|
|
if (!targetDocShell) {
|
|
rv = FindTarget(name.get(), &bIsNewWindow,
|
|
getter_AddRefs(targetDocShell));
|
|
}
|
|
|
|
NS_ASSERTION(targetDocShell, "No Target docshell could be found!");
|
|
//
|
|
// Transfer the load to the target DocShell... Pass nsnull as the
|
|
// window target name from to prevent recursive retargeting!
|
|
//
|
|
if (targetDocShell) {
|
|
rv = targetDocShell->InternalLoad(aURI,
|
|
aReferrer,
|
|
owner,
|
|
aFlags,
|
|
nsnull, // No window target
|
|
aTypeHint,
|
|
aPostData,
|
|
aHeadersData,
|
|
aLoadType,
|
|
aSHEntry,
|
|
aFirstParty,
|
|
aDocShell,
|
|
aRequest);
|
|
if (rv == NS_ERROR_NO_CONTENT) {
|
|
if (bIsNewWindow) {
|
|
//
|
|
// At this point, a new window has been created, but the
|
|
// URI did not have any data associated with it...
|
|
//
|
|
// So, the best we can do, is to tear down the new window
|
|
// that was just created!
|
|
//
|
|
nsCOMPtr<nsIDocShellTreeItem> treeItem;
|
|
nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
|
|
|
|
treeItem = do_QueryInterface(targetDocShell);
|
|
treeItem->GetTreeOwner(getter_AddRefs(treeOwner));
|
|
if (treeOwner) {
|
|
nsCOMPtr<nsIBaseWindow> treeOwnerAsWin;
|
|
|
|
treeOwnerAsWin = do_QueryInterface(treeOwner);
|
|
if (treeOwnerAsWin) {
|
|
treeOwnerAsWin->Destroy();
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// NS_ERROR_NO_CONTENT should not be returned to the
|
|
// caller... This is an internal error code indicating that
|
|
// the URI had no data associated with it - probably a
|
|
// helper-app style protocol (ie. mailto://)
|
|
//
|
|
rv = NS_OK;
|
|
}
|
|
else if (bIsNewWindow) {
|
|
// XXX: Once new windows are created hidden, the new
|
|
// window will need to be made visible... For now,
|
|
// do nothing.
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
//
|
|
// Load is being targetted at this docshell so return an error if the
|
|
// docshell is in the process of being destroyed.
|
|
//
|
|
if (mIsBeingDestroyed) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
// Before going any further vet loads initiated by external programs.
|
|
if (aLoadType == LOAD_NORMAL_EXTERNAL) {
|
|
// Disallow external chrome: loads targetted at content windows
|
|
PRBool isChrome = PR_FALSE;
|
|
if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome)) && isChrome) {
|
|
NS_WARNING("blocked external chrome: url -- use '-chrome' option");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
// clear the decks to prevent context bleed-through (bug 298255)
|
|
rv = CreateAboutBlankContentViewer();
|
|
if (NS_FAILED(rv))
|
|
return NS_ERROR_FAILURE;
|
|
|
|
// reset loadType so we don't have to add lots of tests for
|
|
// LOAD_NORMAL_EXTERNAL after this point
|
|
aLoadType = LOAD_NORMAL;
|
|
}
|
|
|
|
rv = CheckLoadingPermissions();
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
mURIResultedInDocument = PR_FALSE; // reset the clock...
|
|
|
|
//
|
|
// First:
|
|
// Check to see if the new URI is an anchor in the existing document.
|
|
// Skip this check if we're doing some sort of abnormal load, if the
|
|
// new load is a non-history load and has postdata, or if we're doing
|
|
// a history load and the page identifiers of mOSHE and aSHEntry
|
|
// don't match.
|
|
//
|
|
PRBool allowScroll = PR_TRUE;
|
|
if (!aSHEntry) {
|
|
allowScroll = (aPostData == nsnull);
|
|
} else if (mOSHE) {
|
|
PRUint32 ourPageIdent;
|
|
mOSHE->GetPageIdentifier(&ourPageIdent);
|
|
PRUint32 otherPageIdent;
|
|
aSHEntry->GetPageIdentifier(&otherPageIdent);
|
|
allowScroll = (ourPageIdent == otherPageIdent);
|
|
#ifdef DEBUG
|
|
if (allowScroll) {
|
|
nsCOMPtr<nsIInputStream> currentPostData;
|
|
mOSHE->GetPostData(getter_AddRefs(currentPostData));
|
|
NS_ASSERTION(currentPostData == aPostData,
|
|
"Different POST data for entries for the same page?");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if ((aLoadType == LOAD_NORMAL ||
|
|
aLoadType == LOAD_STOP_CONTENT ||
|
|
LOAD_TYPE_HAS_FLAGS(aLoadType, LOAD_FLAGS_REPLACE_HISTORY) ||
|
|
aLoadType == LOAD_HISTORY ||
|
|
aLoadType == LOAD_LINK) && allowScroll) {
|
|
PRBool wasAnchor = PR_FALSE;
|
|
nscoord cx, cy;
|
|
NS_ENSURE_SUCCESS(ScrollIfAnchor(aURI, &wasAnchor, aLoadType, &cx, &cy), NS_ERROR_FAILURE);
|
|
if (wasAnchor) {
|
|
mLoadType = aLoadType;
|
|
mURIResultedInDocument = PR_TRUE;
|
|
|
|
/* we need to assign mLSHE to aSHEntry right here, so that on History loads,
|
|
* SetCurrentURI() called from OnNewURI() will send proper
|
|
* onLocationChange() notifications to the browser to update
|
|
* back/forward buttons.
|
|
*/
|
|
SetHistoryEntry(&mLSHE, aSHEntry);
|
|
|
|
/* This is a anchor traversal with in the same page.
|
|
* call OnNewURI() so that, this traversal will be
|
|
* recorded in session and global history.
|
|
*/
|
|
OnNewURI(aURI, nsnull, mLoadType, PR_TRUE);
|
|
nsCOMPtr<nsIInputStream> postData;
|
|
PRUint32 pageIdent = PR_UINT32_MAX;
|
|
|
|
if (mOSHE) {
|
|
/* save current position of scroller(s) (bug 59774) */
|
|
mOSHE->SetScrollPosition(cx, cy);
|
|
// Get the postdata and page ident from the current page, if
|
|
// the new load is being done via normal means. Note that
|
|
// "normal means" can be checked for just by checking for
|
|
// LOAD_CMD_NORMAL, given the loadType and allowScroll check
|
|
// above -- it filters out some LOAD_CMD_NORMAL cases that we
|
|
// wouldn't want here.
|
|
if (aLoadType & LOAD_CMD_NORMAL) {
|
|
mOSHE->GetPostData(getter_AddRefs(postData));
|
|
mOSHE->GetPageIdentifier(&pageIdent);
|
|
}
|
|
}
|
|
|
|
/* Assign mOSHE to mLSHE. This will either be a new entry created
|
|
* by OnNewURI() for normal loads or aSHEntry for history loads.
|
|
*/
|
|
if (mLSHE) {
|
|
SetHistoryEntry(&mOSHE, mLSHE);
|
|
// Save the postData obtained from the previous page
|
|
// in to the session history entry created for the
|
|
// anchor page, so that any history load of the anchor
|
|
// page will restore the appropriate postData.
|
|
if (postData)
|
|
mOSHE->SetPostData(postData);
|
|
|
|
// Propagate our page ident to the new mOSHE so that
|
|
// we'll know it just differed by a scroll on the page.
|
|
if (pageIdent != PR_UINT32_MAX)
|
|
mOSHE->SetPageIdentifier(pageIdent);
|
|
}
|
|
|
|
/* restore previous position of scroller(s), if we're moving
|
|
* back in history (bug 59774)
|
|
*/
|
|
if (mOSHE && (aLoadType == LOAD_HISTORY || aLoadType == LOAD_RELOAD_NORMAL))
|
|
{
|
|
nscoord bx, by;
|
|
mOSHE->GetScrollPosition(&bx, &by);
|
|
SetCurScrollPosEx(bx, by);
|
|
}
|
|
|
|
/* Clear out mLSHE so that further anchor visits get
|
|
* recorded in SH and SH won't misbehave.
|
|
*/
|
|
SetHistoryEntry(&mLSHE, nsnull);
|
|
/* Set the title for the SH entry for this target url. so that
|
|
* SH menus in go/back/forward buttons won't be empty for this.
|
|
*/
|
|
if (mSessionHistory) {
|
|
PRInt32 index = -1;
|
|
mSessionHistory->GetIndex(&index);
|
|
nsCOMPtr<nsIHistoryEntry> hEntry;
|
|
mSessionHistory->GetEntryAtIndex(index, PR_FALSE,
|
|
getter_AddRefs(hEntry));
|
|
NS_ENSURE_TRUE(hEntry, NS_ERROR_FAILURE);
|
|
nsCOMPtr<nsISHEntry> shEntry(do_QueryInterface(hEntry));
|
|
if (shEntry)
|
|
shEntry->SetTitle(mTitle);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
// Check if the page doesn't want to be unloaded. The javascript:
|
|
// protocol handler deals with this for javascript: URLs.
|
|
if (!bIsJavascript && mContentViewer) {
|
|
PRBool okToUnload;
|
|
rv = mContentViewer->PermitUnload(&okToUnload);
|
|
|
|
if (NS_SUCCEEDED(rv) && !okToUnload) {
|
|
// The user chose not to unload the page, interrupt the
|
|
// load.
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
// Check for saving the presentation here, before calling Stop().
|
|
// This is necessary so that we can catch any pending requests.
|
|
// Since the new request has not been created yet, we pass null for the
|
|
// new request parameter.
|
|
PRBool savePresentation = CanSavePresentation(aLoadType, nsnull);
|
|
|
|
// Don't stop current network activity for javascript: URL's since
|
|
// they might not result in any data, and thus nothing should be
|
|
// stopped in those cases. In the case where they do result in
|
|
// data, the javascript: URL channel takes care of stopping
|
|
// current network activity.
|
|
if (!bIsJavascript) {
|
|
// Stop any current network activity.
|
|
// Also stop content if this is a zombie doc. otherwise
|
|
// the onload will be delayed by other loads initiated in the
|
|
// background by the first document that
|
|
// didn't fully load before the next load was initiated.
|
|
// If not a zombie, don't stop content until data
|
|
// starts arriving from the new URI...
|
|
|
|
nsCOMPtr<nsIContentViewer> zombieViewer;
|
|
if (mContentViewer) {
|
|
mContentViewer->GetPreviousViewer(getter_AddRefs(zombieViewer));
|
|
}
|
|
|
|
if (zombieViewer ||
|
|
LOAD_TYPE_HAS_FLAGS(aLoadType, LOAD_FLAGS_STOP_CONTENT)) {
|
|
rv = Stop(nsIWebNavigation::STOP_ALL);
|
|
} else {
|
|
rv = Stop(nsIWebNavigation::STOP_NETWORK);
|
|
}
|
|
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
}
|
|
|
|
mLoadType = aLoadType;
|
|
|
|
// mLSHE should be assigned to aSHEntry, only after Stop() has
|
|
// been called. But when loading an error page, do not clear the
|
|
// mLSHE for the real page.
|
|
if (mLoadType != LOAD_ERROR_PAGE)
|
|
SetHistoryEntry(&mLSHE, aSHEntry);
|
|
|
|
mSavingOldViewer = savePresentation;
|
|
|
|
// If we have a saved content viewer in history, restore and show it now.
|
|
if (aSHEntry && (mLoadType & LOAD_CMD_HISTORY)) {
|
|
nsCOMPtr<nsISHEntry> oldEntry = mOSHE;
|
|
PRBool restoring;
|
|
rv = RestorePresentation(aSHEntry, &restoring);
|
|
if (restoring)
|
|
return rv;
|
|
|
|
// We failed to restore the presentation, so clean up.
|
|
// Both the old and new history entries could potentially be in
|
|
// an inconsistent state.
|
|
if (NS_FAILED(rv)) {
|
|
if (oldEntry)
|
|
oldEntry->SyncPresentationState();
|
|
|
|
aSHEntry->SyncPresentationState();
|
|
}
|
|
}
|
|
|
|
nsCOMPtr<nsIRequest> req;
|
|
rv = DoURILoad(aURI, aReferrer,
|
|
!(aFlags & INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER),
|
|
owner, aTypeHint, aPostData, aHeadersData, aFirstParty,
|
|
aDocShell, getter_AddRefs(req));
|
|
if (req && aRequest)
|
|
NS_ADDREF(*aRequest = req);
|
|
|
|
if (NS_FAILED(rv)) {
|
|
nsCOMPtr<nsIChannel> chan(do_QueryInterface(req));
|
|
DisplayLoadError(rv, aURI, nsnull, chan);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
void
|
|
nsDocShell::GetCurrentDocumentOwner(nsISupports ** aOwner)
|
|
{
|
|
*aOwner = nsnull;
|
|
nsCOMPtr<nsIDocument> document;
|
|
//-- Get the current document
|
|
if (mContentViewer) {
|
|
nsCOMPtr<nsIDocumentViewer>
|
|
docViewer(do_QueryInterface(mContentViewer));
|
|
if (!docViewer)
|
|
return;
|
|
docViewer->GetDocument(getter_AddRefs(document));
|
|
}
|
|
else //-- If there's no document loaded yet, look at the parent (frameset)
|
|
{
|
|
nsCOMPtr<nsIDocShellTreeItem> parentItem;
|
|
GetSameTypeParent(getter_AddRefs(parentItem));
|
|
if (!parentItem)
|
|
return;
|
|
nsCOMPtr<nsIDOMWindowInternal>
|
|
parentWindow(do_GetInterface(parentItem));
|
|
if (!parentWindow)
|
|
return;
|
|
nsCOMPtr<nsIDOMDocument> parentDomDoc;
|
|
parentWindow->GetDocument(getter_AddRefs(parentDomDoc));
|
|
if (!parentDomDoc)
|
|
return;
|
|
document = do_QueryInterface(parentDomDoc);
|
|
}
|
|
|
|
//-- Get the document's principal
|
|
if (document) {
|
|
*aOwner = document->GetPrincipal();
|
|
}
|
|
|
|
NS_IF_ADDREF(*aOwner);
|
|
}
|
|
|
|
nsresult
|
|
nsDocShell::DoURILoad(nsIURI * aURI,
|
|
nsIURI * aReferrerURI,
|
|
PRBool aSendReferrer,
|
|
nsISupports * aOwner,
|
|
const char * aTypeHint,
|
|
nsIInputStream * aPostData,
|
|
nsIInputStream * aHeadersData,
|
|
PRBool aFirstParty,
|
|
nsIDocShell ** aDocShell,
|
|
nsIRequest ** aRequest)
|
|
{
|
|
nsresult rv;
|
|
nsCOMPtr<nsIURILoader> uriLoader;
|
|
|
|
uriLoader = do_GetService(NS_URI_LOADER_CONTRACTID, &rv);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL;
|
|
if (aFirstParty) {
|
|
// tag first party URL loads
|
|
loadFlags |= nsIChannel::LOAD_INITIAL_DOCUMENT_URI;
|
|
}
|
|
|
|
if (mLoadType == LOAD_ERROR_PAGE) {
|
|
// Error pages are LOAD_BACKGROUND
|
|
loadFlags |= nsIChannel::LOAD_BACKGROUND;
|
|
}
|
|
|
|
// open a channel for the url
|
|
nsCOMPtr<nsIChannel> channel;
|
|
|
|
rv = NS_NewChannel(getter_AddRefs(channel),
|
|
aURI,
|
|
nsnull,
|
|
nsnull,
|
|
NS_STATIC_CAST(nsIInterfaceRequestor *, this),
|
|
loadFlags);
|
|
if (NS_FAILED(rv)) {
|
|
if (rv == NS_ERROR_UNKNOWN_PROTOCOL) {
|
|
// This is a uri with a protocol scheme we don't know how
|
|
// to handle. Embedders might still be interested in
|
|
// handling the load, though, so we fire a notification
|
|
// before throwing the load away.
|
|
PRBool abort = PR_FALSE;
|
|
nsresult rv2 = mContentListener->OnStartURIOpen(aURI, &abort);
|
|
if (NS_SUCCEEDED(rv2) && abort) {
|
|
// Hey, they're handling the load for us! How convenient!
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
// Make sure to give the caller a channel if we managed to create one
|
|
// This is important for correct error page/session history interaction
|
|
if (aRequest)
|
|
NS_ADDREF(*aRequest = channel);
|
|
|
|
channel->SetOriginalURI(aURI);
|
|
if (aTypeHint && *aTypeHint) {
|
|
channel->SetContentType(nsDependentCString(aTypeHint));
|
|
mContentTypeHint = aTypeHint;
|
|
}
|
|
else {
|
|
mContentTypeHint.Truncate();
|
|
}
|
|
|
|
//hack
|
|
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
|
|
nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal(do_QueryInterface(channel));
|
|
if (httpChannelInternal) {
|
|
if (aFirstParty) {
|
|
httpChannelInternal->SetDocumentURI(aURI);
|
|
} else {
|
|
httpChannelInternal->SetDocumentURI(aReferrerURI);
|
|
}
|
|
}
|
|
|
|
nsCOMPtr<nsIWritablePropertyBag2> props(do_QueryInterface(channel));
|
|
if (props)
|
|
{
|
|
// save true referrer for those who need it (e.g. xpinstall whitelisting)
|
|
// Currently only http and ftp channels support this.
|
|
props->SetPropertyAsInterface(NS_LITERAL_STRING("docshell.internalReferrer"),
|
|
aReferrerURI);
|
|
}
|
|
|
|
//
|
|
// If this is a HTTP channel, then set up the HTTP specific information
|
|
// (ie. POST data, referrer, ...)
|
|
//
|
|
if (httpChannel) {
|
|
nsCOMPtr<nsICachingChannel> cacheChannel(do_QueryInterface(httpChannel));
|
|
/* Get the cache Key from SH */
|
|
nsCOMPtr<nsISupports> cacheKey;
|
|
if (mLSHE) {
|
|
mLSHE->GetCacheKey(getter_AddRefs(cacheKey));
|
|
}
|
|
else if (mOSHE) // for reload cases
|
|
mOSHE->GetCacheKey(getter_AddRefs(cacheKey));
|
|
|
|
// figure out if we need to set the post data stream on the channel...
|
|
// right now, this is only done for http channels.....
|
|
if (aPostData) {
|
|
// XXX it's a bit of a hack to rewind the postdata stream here but
|
|
// it has to be done in case the post data is being reused multiple
|
|
// times.
|
|
nsCOMPtr<nsISeekableStream>
|
|
postDataSeekable(do_QueryInterface(aPostData));
|
|
if (postDataSeekable) {
|
|
rv = postDataSeekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
|
|
NS_ASSERTION(uploadChannel, "http must support nsIUploadChannel");
|
|
|
|
// we really need to have a content type associated with this stream!!
|
|
uploadChannel->SetUploadStream(aPostData, EmptyCString(), -1);
|
|
/* If there is a valid postdata *and* it is a History Load,
|
|
* set up the cache key on the channel, to retrieve the
|
|
* data *only* from the cache. If it is a normal reload, the
|
|
* cache is free to go to the server for updated postdata.
|
|
*/
|
|
if (cacheChannel && cacheKey) {
|
|
if (mLoadType == LOAD_HISTORY || mLoadType == LOAD_RELOAD_CHARSET_CHANGE) {
|
|
cacheChannel->SetCacheKey(cacheKey);
|
|
PRUint32 loadFlags;
|
|
if (NS_SUCCEEDED(channel->GetLoadFlags(&loadFlags)))
|
|
channel->SetLoadFlags(loadFlags | nsICachingChannel::LOAD_ONLY_FROM_CACHE);
|
|
}
|
|
else if (mLoadType == LOAD_RELOAD_NORMAL)
|
|
cacheChannel->SetCacheKey(cacheKey);
|
|
}
|
|
}
|
|
else {
|
|
/* If there is no postdata, set the cache key on the channel, and
|
|
* do not set the LOAD_ONLY_FROM_CACHE flag, so that the channel
|
|
* will be free to get it from net if it is not found in cache.
|
|
* New cache may use it creatively on CGI pages with GET
|
|
* method and even on those that say "no-cache"
|
|
*/
|
|
if (mLoadType == LOAD_HISTORY || mLoadType == LOAD_RELOAD_NORMAL
|
|
|| mLoadType == LOAD_RELOAD_CHARSET_CHANGE) {
|
|
if (cacheChannel && cacheKey)
|
|
cacheChannel->SetCacheKey(cacheKey);
|
|
}
|
|
}
|
|
if (aHeadersData) {
|
|
rv = AddHeadersToChannel(aHeadersData, httpChannel);
|
|
}
|
|
// Set the referrer explicitly
|
|
if (aReferrerURI && aSendReferrer) {
|
|
// Referrer is currenly only set for link clicks here.
|
|
httpChannel->SetReferrer(aReferrerURI);
|
|
}
|
|
}
|
|
//
|
|
// Set the owner of the channel - only for javascript and data channels.
|
|
//
|
|
// XXX: Is seems wrong that the owner is ignored - even if one is
|
|
// supplied) unless the URI is javascript or data.
|
|
//
|
|
// (Currently chrome URIs set the owner when they are created!
|
|
// So setting a NULL owner would be bad!)
|
|
//
|
|
PRBool isJSOrData = PR_FALSE;
|
|
aURI->SchemeIs("javascript", &isJSOrData);
|
|
if (!isJSOrData) {
|
|
aURI->SchemeIs("data", &isJSOrData);
|
|
}
|
|
if (isJSOrData) {
|
|
channel->SetOwner(aOwner);
|
|
}
|
|
|
|
rv = DoChannelLoad(channel, uriLoader);
|
|
|
|
//
|
|
// If the channel load failed, we failed and nsIWebProgress just ain't
|
|
// gonna happen.
|
|
//
|
|
if (NS_SUCCEEDED(rv)) {
|
|
if (aDocShell) {
|
|
*aDocShell = this;
|
|
NS_ADDREF(*aDocShell);
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
static NS_METHOD
|
|
AppendSegmentToString(nsIInputStream *in,
|
|
void *closure,
|
|
const char *fromRawSegment,
|
|
PRUint32 toOffset,
|
|
PRUint32 count,
|
|
PRUint32 *writeCount)
|
|
{
|
|
// aFromSegment now contains aCount bytes of data.
|
|
|
|
nsCAutoString *buf = NS_STATIC_CAST(nsCAutoString *, closure);
|
|
buf->Append(fromRawSegment, count);
|
|
|
|
// Indicate that we have consumed all of aFromSegment
|
|
*writeCount = count;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::AddHeadersToChannel(nsIInputStream *aHeadersData,
|
|
nsIChannel *aGenericChannel)
|
|
{
|
|
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aGenericChannel);
|
|
NS_ENSURE_STATE(httpChannel);
|
|
|
|
PRUint32 numRead;
|
|
nsCAutoString headersString;
|
|
nsresult rv = aHeadersData->ReadSegments(AppendSegmentToString,
|
|
&headersString,
|
|
PR_UINT32_MAX,
|
|
&numRead);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// used during the manipulation of the String from the InputStream
|
|
nsCAutoString headerName;
|
|
nsCAutoString headerValue;
|
|
PRInt32 crlf;
|
|
PRInt32 colon;
|
|
|
|
//
|
|
// Iterate over the headersString: for each "\r\n" delimited chunk,
|
|
// add the value as a header to the nsIHttpChannel
|
|
//
|
|
|
|
static const char kWhitespace[] = "\b\t\r\n ";
|
|
while (PR_TRUE) {
|
|
crlf = headersString.Find("\r\n");
|
|
if (crlf == kNotFound)
|
|
return NS_OK;
|
|
|
|
const nsCSubstring &oneHeader = StringHead(headersString, crlf);
|
|
|
|
colon = oneHeader.FindChar(':');
|
|
if (colon == kNotFound)
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
headerName = StringHead(oneHeader, colon);
|
|
headerValue = Substring(oneHeader, colon + 1);
|
|
|
|
headerName.Trim(kWhitespace);
|
|
headerValue.Trim(kWhitespace);
|
|
|
|
headersString.Cut(0, crlf + 2);
|
|
|
|
//
|
|
// FINALLY: we can set the header!
|
|
//
|
|
|
|
rv = httpChannel->SetRequestHeader(headerName, headerValue, PR_TRUE);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
NS_NOTREACHED("oops");
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
nsresult nsDocShell::DoChannelLoad(nsIChannel * aChannel,
|
|
nsIURILoader * aURILoader)
|
|
{
|
|
nsresult rv;
|
|
// Mark the channel as being a document URI...
|
|
nsLoadFlags loadFlags = 0;
|
|
(void) aChannel->GetLoadFlags(&loadFlags);
|
|
loadFlags |= nsIChannel::LOAD_DOCUMENT_URI;
|
|
|
|
// Load attributes depend on load type...
|
|
switch (mLoadType) {
|
|
case LOAD_HISTORY:
|
|
loadFlags |= nsIRequest::VALIDATE_NEVER;
|
|
break;
|
|
|
|
case LOAD_RELOAD_CHARSET_CHANGE:
|
|
loadFlags |= nsIRequest::LOAD_FROM_CACHE;
|
|
break;
|
|
|
|
|
|
case LOAD_RELOAD_NORMAL:
|
|
loadFlags |= nsIRequest::VALIDATE_ALWAYS;
|
|
break;
|
|
|
|
case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
|
|
case LOAD_REFRESH:
|
|
loadFlags |= nsIRequest::LOAD_BYPASS_CACHE;
|
|
break;
|
|
|
|
case LOAD_NORMAL:
|
|
case LOAD_LINK:
|
|
// Set cache checking flags
|
|
PRInt32 prefSetting;
|
|
if (NS_SUCCEEDED
|
|
(mPrefs->
|
|
GetIntPref("browser.cache.check_doc_frequency",
|
|
&prefSetting))) {
|
|
switch (prefSetting) {
|
|
case 0:
|
|
loadFlags |= nsIRequest::VALIDATE_ONCE_PER_SESSION;
|
|
break;
|
|
case 1:
|
|
loadFlags |= nsIRequest::VALIDATE_ALWAYS;
|
|
break;
|
|
case 2:
|
|
loadFlags |= nsIRequest::VALIDATE_NEVER;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
(void) aChannel->SetLoadFlags(loadFlags);
|
|
|
|
rv = aURILoader->OpenURI(aChannel,
|
|
(mLoadType == LOAD_LINK),
|
|
this);
|
|
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::ScrollIfAnchor(nsIURI * aURI, PRBool * aWasAnchor,
|
|
PRUint32 aLoadType, nscoord *cx, nscoord *cy)
|
|
{
|
|
NS_ASSERTION(aURI, "null uri arg");
|
|
NS_ASSERTION(aWasAnchor, "null anchor arg");
|
|
|
|
if (aURI == nsnull || aWasAnchor == nsnull) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
*aWasAnchor = PR_FALSE;
|
|
|
|
if (!mCurrentURI) {
|
|
return NS_OK;
|
|
}
|
|
|
|
nsCOMPtr<nsIPresShell> shell;
|
|
nsresult rv = GetPresShell(getter_AddRefs(shell));
|
|
if (NS_FAILED(rv) || !shell) {
|
|
// If we failed to get the shell, or if there is no shell,
|
|
// nothing left to do here.
|
|
|
|
return rv;
|
|
}
|
|
|
|
// NOTE: we assume URIs are absolute for comparison purposes
|
|
|
|
nsCAutoString currentSpec;
|
|
NS_ENSURE_SUCCESS(mCurrentURI->GetSpec(currentSpec),
|
|
NS_ERROR_FAILURE);
|
|
|
|
nsCAutoString newSpec;
|
|
NS_ENSURE_SUCCESS(aURI->GetSpec(newSpec), NS_ERROR_FAILURE);
|
|
|
|
// Search for hash marks in the current URI and the new URI and
|
|
// take a copy of everything to the left of the hash for
|
|
// comparison.
|
|
|
|
const char kHash = '#';
|
|
|
|
// Split the new URI into a left and right part
|
|
// (assume we're parsing it out right
|
|
nsACString::const_iterator urlStart, urlEnd, refStart, refEnd;
|
|
newSpec.BeginReading(urlStart);
|
|
newSpec.EndReading(refEnd);
|
|
|
|
PRInt32 hashNew = newSpec.FindChar(kHash);
|
|
if (hashNew == 0) {
|
|
return NS_OK; // Strange URI
|
|
}
|
|
|
|
if (hashNew > 0) {
|
|
// found it
|
|
urlEnd = urlStart;
|
|
urlEnd.advance(hashNew);
|
|
|
|
refStart = urlEnd;
|
|
++refStart; // advanced past '#'
|
|
|
|
}
|
|
else {
|
|
// no hash at all
|
|
urlEnd = refStart = refEnd;
|
|
}
|
|
const nsACString& sNewLeft = Substring(urlStart, urlEnd);
|
|
const nsACString& sNewRef = Substring(refStart, refEnd);
|
|
|
|
// Split the current URI in a left and right part
|
|
nsACString::const_iterator currentLeftStart, currentLeftEnd;
|
|
currentSpec.BeginReading(currentLeftStart);
|
|
|
|
PRInt32 hashCurrent = currentSpec.FindChar(kHash);
|
|
if (hashCurrent == 0) {
|
|
return NS_OK; // Strange URI
|
|
}
|
|
|
|
if (hashCurrent > 0) {
|
|
currentLeftEnd = currentLeftStart;
|
|
currentLeftEnd.advance(hashCurrent);
|
|
}
|
|
else {
|
|
currentSpec.EndReading(currentLeftEnd);
|
|
}
|
|
|
|
// If we have no new anchor, we do not want to scroll, unless there is a
|
|
// current anchor and we are doing a history load. So return if we have no
|
|
// new anchor, and there is no current anchor or the load is not a history
|
|
// load.
|
|
NS_ASSERTION(hashNew != 0 && hashCurrent != 0,
|
|
"What happened to the early returns above?");
|
|
if (hashNew == kNotFound &&
|
|
(hashCurrent == kNotFound || aLoadType != LOAD_HISTORY)) {
|
|
return NS_OK;
|
|
}
|
|
|
|
// Compare the URIs.
|
|
//
|
|
// NOTE: this is a case sensitive comparison because some parts of the
|
|
// URI are case sensitive, and some are not. i.e. the domain name
|
|
// is case insensitive but the the paths are not.
|
|
//
|
|
// This means that comparing "http://www.ABC.com/" to "http://www.abc.com/"
|
|
// will fail this test.
|
|
|
|
if (!Substring(currentLeftStart, currentLeftEnd).Equals(sNewLeft)) {
|
|
return NS_OK; // URIs not the same
|
|
}
|
|
|
|
// Now we know we are dealing with an anchor
|
|
*aWasAnchor = PR_TRUE;
|
|
|
|
// Both the new and current URIs refer to the same page. We can now
|
|
// browse to the hash stored in the new URI.
|
|
//
|
|
// But first let's capture positions of scroller(s) that can
|
|
// (and usually will) be modified by GoToAnchor() call.
|
|
|
|
GetCurScrollPos(ScrollOrientation_X, cx);
|
|
GetCurScrollPos(ScrollOrientation_Y, cy);
|
|
|
|
if (!sNewRef.IsEmpty()) {
|
|
// anchor is there, but if it's a load from history,
|
|
// we don't have any anchor jumping to do
|
|
PRBool scroll = aLoadType != LOAD_HISTORY &&
|
|
aLoadType != LOAD_RELOAD_NORMAL;
|
|
|
|
char *str = ToNewCString(sNewRef);
|
|
if (!str) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
// nsUnescape modifies the string that is passed into it.
|
|
nsUnescape(str);
|
|
|
|
// We assume that the bytes are in UTF-8, as it says in the
|
|
// spec:
|
|
// http://www.w3.org/TR/html4/appendix/notes.html#h-B.2.1
|
|
|
|
// We try the UTF-8 string first, and then try the document's
|
|
// charset (see below). If the string is not UTF-8,
|
|
// conversion will fail and give us an empty Unicode string.
|
|
// In that case, we should just fall through to using the
|
|
// page's charset.
|
|
rv = NS_ERROR_FAILURE;
|
|
NS_ConvertUTF8toUCS2 uStr(str);
|
|
if (!uStr.IsEmpty()) {
|
|
rv = shell->GoToAnchor(NS_ConvertUTF8toUCS2(str), scroll);
|
|
}
|
|
nsMemory::Free(str);
|
|
|
|
// Above will fail if the anchor name is not UTF-8. Need to
|
|
// convert from document charset to unicode.
|
|
if (NS_FAILED(rv)) {
|
|
|
|
// Get a document charset
|
|
NS_ENSURE_TRUE(mContentViewer, NS_ERROR_FAILURE);
|
|
nsCOMPtr<nsIDocumentViewer>
|
|
docv(do_QueryInterface(mContentViewer));
|
|
NS_ENSURE_TRUE(docv, NS_ERROR_FAILURE);
|
|
nsCOMPtr<nsIDocument> doc;
|
|
rv = docv->GetDocument(getter_AddRefs(doc));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
const nsACString &aCharset = doc->GetDocumentCharacterSet();
|
|
|
|
nsCOMPtr<nsITextToSubURI> textToSubURI =
|
|
do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// Unescape and convert to unicode
|
|
nsXPIDLString uStr;
|
|
|
|
rv = textToSubURI->UnEscapeAndConvert(PromiseFlatCString(aCharset).get(),
|
|
PromiseFlatCString(sNewRef).get(),
|
|
getter_Copies(uStr));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// Ignore return value of GoToAnchor, since it will return an error
|
|
// if there is no such anchor in the document, which is actually a
|
|
// success condition for us (we want to update the session history
|
|
// with the new URI no matter whether we actually scrolled
|
|
// somewhere).
|
|
shell->GoToAnchor(uStr, scroll);
|
|
}
|
|
}
|
|
else {
|
|
|
|
// Tell the shell it's at an anchor, without scrolling.
|
|
shell->GoToAnchor(EmptyString(), PR_FALSE);
|
|
|
|
// An empty anchor was found, but if it's a load from history,
|
|
// we don't have to jump to the top of the page. Scrollbar
|
|
// position will be restored by the caller, based on positions
|
|
// stored in session history.
|
|
if (aLoadType == LOAD_HISTORY || aLoadType == LOAD_RELOAD_NORMAL)
|
|
return rv;
|
|
//An empty anchor. Scroll to the top of the page.
|
|
rv = SetCurScrollPosEx(0, 0);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
void
|
|
nsDocShell::SetupReferrerFromChannel(nsIChannel * aChannel)
|
|
{
|
|
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
|
|
if (httpChannel) {
|
|
nsCOMPtr<nsIURI> referrer;
|
|
nsresult rv = httpChannel->GetReferrer(getter_AddRefs(referrer));
|
|
if (NS_SUCCEEDED(rv)) {
|
|
SetReferrerURI(referrer);
|
|
}
|
|
}
|
|
}
|
|
|
|
PRBool
|
|
nsDocShell::OnNewURI(nsIURI * aURI, nsIChannel * aChannel,
|
|
PRUint32 aLoadType, PRBool aFireOnLocationChange,
|
|
PRBool aAddToGlobalHistory)
|
|
{
|
|
NS_ASSERTION(aURI, "uri is null");
|
|
#ifdef PR_LOGGING
|
|
if (PR_LOG_TEST(gDocShellLog, PR_LOG_DEBUG)) {
|
|
nsCAutoString spec;
|
|
aURI->GetSpec(spec);
|
|
|
|
nsCAutoString chanName;
|
|
if (aChannel)
|
|
aChannel->GetName(chanName);
|
|
else
|
|
chanName.AssignLiteral("<no channel>");
|
|
|
|
PR_LOG(gDocShellLog, PR_LOG_DEBUG,
|
|
("nsDocShell[%p]::OnNewURI(\"%s\", [%s], 0x%x)\n", this, spec.get(),
|
|
chanName.get(), aLoadType));
|
|
}
|
|
#endif
|
|
|
|
PRBool updateHistory = PR_TRUE;
|
|
PRBool equalUri = PR_FALSE;
|
|
PRBool shAvailable = PR_TRUE;
|
|
|
|
// Get the post data from the channel
|
|
nsCOMPtr<nsIInputStream> inputStream;
|
|
if (aChannel) {
|
|
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
|
|
|
|
// Check if the HTTPChannel is hiding under a multiPartChannel
|
|
if (!httpChannel) {
|
|
GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
|
|
}
|
|
|
|
if (httpChannel) {
|
|
nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
|
|
if (uploadChannel) {
|
|
uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
|
|
}
|
|
}
|
|
}
|
|
/* Create SH Entry (mLSHE) only if there is a SessionHistory object (mSessionHistory) in
|
|
* the current frame or in the root docshell
|
|
*/
|
|
nsCOMPtr<nsISHistory> rootSH = mSessionHistory;
|
|
if (!rootSH) {
|
|
// Get the handle to SH from the root docshell
|
|
GetRootSessionHistory(getter_AddRefs(rootSH));
|
|
if (!rootSH)
|
|
shAvailable = PR_FALSE;
|
|
} // rootSH
|
|
|
|
|
|
// Determine if this type of load should update history.
|
|
if (aLoadType == LOAD_BYPASS_HISTORY ||
|
|
aLoadType == LOAD_ERROR_PAGE ||
|
|
aLoadType & LOAD_CMD_HISTORY ||
|
|
aLoadType & LOAD_CMD_RELOAD)
|
|
updateHistory = PR_FALSE;
|
|
|
|
// Check if the url to be loaded is the same as the one already loaded.
|
|
if (mCurrentURI)
|
|
aURI->Equals(mCurrentURI, &equalUri);
|
|
|
|
PR_LOG(gDocShellLog, PR_LOG_DEBUG,
|
|
(" shAvailable=%i updateHistory=%i equalURI=%i\n",
|
|
shAvailable, updateHistory, equalUri));
|
|
|
|
/* If the url to be loaded is the same as the one already there,
|
|
* and the original loadType is LOAD_NORMAL, LOAD_LINK, or
|
|
* LOAD_STOP_CONTENT, set loadType to LOAD_NORMAL_REPLACE so that
|
|
* AddToSessionHistory() won't mess with the current SHEntry and
|
|
* if this page has any frame children, it also will be handled
|
|
* properly. see bug 83684
|
|
*
|
|
* XXX Hopefully changing the loadType at this time will not hurt
|
|
* anywhere. The other way to take care of sequentially repeating
|
|
* frameset pages is to add new methods to nsIDocShellTreeItem.
|
|
* Hopefully I don't have to do that.
|
|
*/
|
|
if (equalUri &&
|
|
(mLoadType == LOAD_NORMAL ||
|
|
mLoadType == LOAD_LINK ||
|
|
mLoadType == LOAD_STOP_CONTENT) &&
|
|
!inputStream)
|
|
{
|
|
mLoadType = LOAD_NORMAL_REPLACE;
|
|
}
|
|
|
|
// If this is a refresh to the currently loaded url, we don't
|
|
// have to update session or global history.
|
|
if (mLoadType == LOAD_REFRESH && !inputStream && equalUri) {
|
|
SetHistoryEntry(&mLSHE, mOSHE);
|
|
}
|
|
|
|
|
|
/* If the user pressed shift-reload, cache will create a new cache key
|
|
* for the page. Save the new cacheKey in Session History.
|
|
* see bug 90098
|
|
*/
|
|
if (aChannel && aLoadType == LOAD_RELOAD_BYPASS_CACHE ||
|
|
aLoadType == LOAD_RELOAD_BYPASS_PROXY ||
|
|
aLoadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE) {
|
|
NS_ASSERTION(!updateHistory,
|
|
"We shouldn't be updating history for forced reloads!");
|
|
|
|
nsCOMPtr<nsICachingChannel> cacheChannel(do_QueryInterface(aChannel));
|
|
nsCOMPtr<nsISupports> cacheKey;
|
|
// Get the Cache Key and store it in SH.
|
|
if (cacheChannel)
|
|
cacheChannel->GetCacheKey(getter_AddRefs(cacheKey));
|
|
// If we already have a loading history entry, store the new cache key
|
|
// in it. Otherwise, since we're doing a reload and won't be updating
|
|
// our history entry, store the cache key in our current history entry.
|
|
if (mLSHE)
|
|
mLSHE->SetCacheKey(cacheKey);
|
|
else if (mOSHE)
|
|
mOSHE->SetCacheKey(cacheKey);
|
|
}
|
|
|
|
if (updateHistory && shAvailable) {
|
|
// Update session history if necessary...
|
|
if (!mLSHE && (mItemType == typeContent) && mURIResultedInDocument) {
|
|
/* This is a fresh page getting loaded for the first time
|
|
*.Create a Entry for it and add it to SH, if this is the
|
|
* rootDocShell
|
|
*/
|
|
(void) AddToSessionHistory(aURI, aChannel, getter_AddRefs(mLSHE));
|
|
}
|
|
|
|
// Update Global history
|
|
if (aAddToGlobalHistory) {
|
|
// Get the referrer uri from the channel
|
|
nsCOMPtr<nsIURI> referrer;
|
|
nsCOMPtr<nsIHttpChannel> httpchannel(do_QueryInterface(aChannel));
|
|
if (httpchannel)
|
|
httpchannel->GetReferrer(getter_AddRefs(referrer));
|
|
AddToGlobalHistory(aURI, PR_FALSE, referrer);
|
|
}
|
|
}
|
|
|
|
// If this was a history load, update the index in
|
|
// SH.
|
|
if (rootSH && (mLoadType & LOAD_CMD_HISTORY)) {
|
|
nsCOMPtr<nsISHistoryInternal> shInternal(do_QueryInterface(rootSH));
|
|
if (shInternal)
|
|
shInternal->UpdateIndex();
|
|
}
|
|
PRBool onLocationChangeNeeded = SetCurrentURI(aURI, aChannel,
|
|
aFireOnLocationChange);
|
|
// Make sure to store the referrer from the channel, if any
|
|
SetupReferrerFromChannel(aChannel);
|
|
return onLocationChangeNeeded;
|
|
}
|
|
|
|
PRBool
|
|
nsDocShell::OnLoadingSite(nsIChannel * aChannel, PRBool aFireOnLocationChange,
|
|
PRBool aAddToGlobalHistory)
|
|
{
|
|
nsCOMPtr<nsIURI> uri;
|
|
// If this a redirect, use the final url (uri)
|
|
// else use the original url
|
|
//
|
|
// The better way would be to trust the OnRedirect() that necko gives us.
|
|
// But this notification happen after the necko notification and hence
|
|
// overrides it. Until OnRedirect() gets settles out, let us do this.
|
|
nsLoadFlags loadFlags = 0;
|
|
aChannel->GetLoadFlags(&loadFlags);
|
|
if (loadFlags & nsIChannel::LOAD_REPLACE)
|
|
aChannel->GetURI(getter_AddRefs(uri));
|
|
else
|
|
aChannel->GetOriginalURI(getter_AddRefs(uri));
|
|
NS_ENSURE_TRUE(uri, PR_FALSE);
|
|
|
|
return OnNewURI(uri, aChannel, mLoadType, aFireOnLocationChange,
|
|
aAddToGlobalHistory);
|
|
|
|
}
|
|
|
|
void
|
|
nsDocShell::SetReferrerURI(nsIURI * aURI)
|
|
{
|
|
mReferrerURI = aURI; // This assigment addrefs
|
|
}
|
|
|
|
//*****************************************************************************
|
|
// nsDocShell: Session History
|
|
//*****************************************************************************
|
|
PRBool
|
|
nsDocShell::ShouldAddToSessionHistory(nsIURI * aURI)
|
|
{
|
|
// I believe none of the about: urls should go in the history. But then
|
|
// that could just be me... If the intent is only deny about:blank then we
|
|
// should just do a spec compare, rather than two gets of the scheme and
|
|
// then the path. -Gagan
|
|
nsresult rv;
|
|
nsCAutoString buf;
|
|
|
|
rv = aURI->GetScheme(buf);
|
|
if (NS_FAILED(rv))
|
|
return PR_FALSE;
|
|
|
|
if (buf.Equals("about")) {
|
|
rv = aURI->GetPath(buf);
|
|
if (NS_FAILED(rv))
|
|
return PR_FALSE;
|
|
|
|
if (buf.Equals("blank")) {
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
return PR_TRUE;
|
|
}
|
|
|
|
nsresult
|
|
nsDocShell::AddToSessionHistory(nsIURI * aURI,
|
|
nsIChannel * aChannel, nsISHEntry ** aNewEntry)
|
|
{
|
|
#ifdef PR_LOGGING
|
|
if (PR_LOG_TEST(gDocShellLog, PR_LOG_DEBUG)) {
|
|
nsCAutoString spec;
|
|
aURI->GetSpec(spec);
|
|
|
|
nsCAutoString chanName;
|
|
if (aChannel)
|
|
aChannel->GetName(chanName);
|
|
else
|
|
chanName.AssignLiteral("<no channel>");
|
|
|
|
PR_LOG(gDocShellLog, PR_LOG_DEBUG,
|
|
("nsDocShell[%p]::AddToSessionHistory(\"%s\", [%s])\n", this, spec.get(),
|
|
chanName.get()));
|
|
}
|
|
#endif
|
|
|
|
nsresult rv = NS_OK;
|
|
nsCOMPtr<nsISHEntry> entry;
|
|
PRBool shouldPersist;
|
|
|
|
shouldPersist = ShouldAddToSessionHistory(aURI);
|
|
|
|
// Get a handle to the root docshell
|
|
nsCOMPtr<nsIDocShellTreeItem> root;
|
|
GetSameTypeRootTreeItem(getter_AddRefs(root));
|
|
/*
|
|
* If this is a LOAD_FLAGS_REPLACE_HISTORY in a subframe, we use
|
|
* the existing SH entry in the page and replace the url and
|
|
* other vitalities.
|
|
*/
|
|
if (LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY) &&
|
|
root != NS_STATIC_CAST(nsIDocShellTreeItem *, this)) {
|
|
// This is a subframe
|
|
entry = mOSHE;
|
|
nsCOMPtr<nsISHContainer> shContainer(do_QueryInterface(entry));
|
|
if (shContainer) {
|
|
PRInt32 childCount = 0;
|
|
shContainer->GetChildCount(&childCount);
|
|
// Remove all children of this entry
|
|
for (PRInt32 i = childCount - 1; i >= 0; i--) {
|
|
nsCOMPtr<nsISHEntry> child;
|
|
shContainer->GetChildAt(i, getter_AddRefs(child));
|
|
shContainer->RemoveChild(child);
|
|
} // for
|
|
} // shContainer
|
|
}
|
|
|
|
// Create a new entry if necessary.
|
|
if (!entry) {
|
|
entry = do_CreateInstance(NS_SHENTRY_CONTRACTID);
|
|
|
|
if (!entry) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
|
|
// Get the post data & referrer
|
|
nsCOMPtr<nsIInputStream> inputStream;
|
|
nsCOMPtr<nsIURI> referrerURI;
|
|
nsCOMPtr<nsISupports> cacheKey;
|
|
nsCOMPtr<nsISupports> cacheToken;
|
|
PRBool expired = PR_FALSE;
|
|
PRBool discardLayoutState = PR_FALSE;
|
|
if (aChannel) {
|
|
nsCOMPtr<nsICachingChannel>
|
|
cacheChannel(do_QueryInterface(aChannel));
|
|
/* If there is a caching channel, get the Cache Key and store it
|
|
* in SH.
|
|
*/
|
|
if (cacheChannel) {
|
|
cacheChannel->GetCacheKey(getter_AddRefs(cacheKey));
|
|
cacheChannel->GetCacheToken(getter_AddRefs(cacheToken));
|
|
}
|
|
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
|
|
|
|
// Check if the httpChannel is hiding under a multipartChannel
|
|
if (!httpChannel) {
|
|
GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
|
|
}
|
|
if (httpChannel) {
|
|
nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
|
|
if (uploadChannel) {
|
|
uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
|
|
}
|
|
httpChannel->GetReferrer(getter_AddRefs(referrerURI));
|
|
|
|
discardLayoutState = ShouldDiscardLayoutState(httpChannel);
|
|
}
|
|
}
|
|
|
|
//Title is set in nsDocShell::SetTitle()
|
|
entry->Create(aURI, // uri
|
|
EmptyString(), // Title
|
|
inputStream, // Post data stream
|
|
nsnull, // LayoutHistory state
|
|
cacheKey, // CacheKey
|
|
mContentTypeHint); // Content-type
|
|
entry->SetReferrerURI(referrerURI);
|
|
/* If cache got a 'no-store', ask SH not to store
|
|
* HistoryLayoutState. By default, SH will set this
|
|
* flag to PR_TRUE and save HistoryLayoutState.
|
|
*/
|
|
if (discardLayoutState) {
|
|
entry->SetSaveLayoutStateFlag(PR_FALSE);
|
|
}
|
|
if (cacheToken) {
|
|
// Check if the page has expired from cache
|
|
nsCOMPtr<nsICacheEntryInfo> cacheEntryInfo(do_QueryInterface(cacheToken));
|
|
if (cacheEntryInfo) {
|
|
PRUint32 expTime;
|
|
cacheEntryInfo->GetExpirationTime(&expTime);
|
|
PRUint32 now = PRTimeToSeconds(PR_Now());
|
|
if (expTime <= now)
|
|
expired = PR_TRUE;
|
|
|
|
}
|
|
}
|
|
if (expired == PR_TRUE)
|
|
entry->SetExpirationStatus(PR_TRUE);
|
|
|
|
|
|
if (root == NS_STATIC_CAST(nsIDocShellTreeItem *, this) && mSessionHistory) {
|
|
// This is the root docshell
|
|
if (LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
|
|
// Replace current entry in session history.
|
|
PRInt32 index = 0;
|
|
mSessionHistory->GetIndex(&index);
|
|
nsCOMPtr<nsISHistoryInternal> shPrivate(do_QueryInterface(mSessionHistory));
|
|
// Replace the current entry with the new entry
|
|
if (shPrivate)
|
|
rv = shPrivate->ReplaceEntry(index, entry);
|
|
}
|
|
else {
|
|
// Add to session history
|
|
nsCOMPtr<nsISHistoryInternal>
|
|
shPrivate(do_QueryInterface(mSessionHistory));
|
|
NS_ENSURE_TRUE(shPrivate, NS_ERROR_FAILURE);
|
|
rv = shPrivate->AddEntry(entry, shouldPersist);
|
|
}
|
|
}
|
|
else {
|
|
// This is a subframe.
|
|
if (!mOSHE || !LOAD_TYPE_HAS_FLAGS(mLoadType,
|
|
LOAD_FLAGS_REPLACE_HISTORY))
|
|
rv = DoAddChildSHEntry(entry, mChildOffset);
|
|
}
|
|
|
|
// Return the new SH entry...
|
|
if (aNewEntry) {
|
|
*aNewEntry = nsnull;
|
|
if (NS_SUCCEEDED(rv)) {
|
|
*aNewEntry = entry;
|
|
NS_ADDREF(*aNewEntry);
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::LoadHistoryEntry(nsISHEntry * aEntry, PRUint32 aLoadType)
|
|
{
|
|
nsresult rv;
|
|
nsCOMPtr<nsIURI> uri;
|
|
nsCOMPtr<nsIInputStream> postData;
|
|
nsCOMPtr<nsIURI> referrerURI;
|
|
nsCAutoString contentType;
|
|
|
|
NS_ENSURE_TRUE(aEntry, NS_ERROR_FAILURE);
|
|
|
|
NS_ENSURE_SUCCESS(aEntry->GetURI(getter_AddRefs(uri)), NS_ERROR_FAILURE);
|
|
NS_ENSURE_SUCCESS(aEntry->GetReferrerURI(getter_AddRefs(referrerURI)),
|
|
NS_ERROR_FAILURE);
|
|
NS_ENSURE_SUCCESS(aEntry->GetPostData(getter_AddRefs(postData)),
|
|
NS_ERROR_FAILURE);
|
|
NS_ENSURE_SUCCESS(aEntry->GetContentType(contentType), NS_ERROR_FAILURE);
|
|
|
|
PRBool isJavaScript, isViewSource, isData;
|
|
if ((NS_SUCCEEDED(uri->SchemeIs("javascript", &isJavaScript)) &&
|
|
isJavaScript) ||
|
|
(NS_SUCCEEDED(uri->SchemeIs("view-source", &isViewSource)) &&
|
|
isViewSource) ||
|
|
(NS_SUCCEEDED(uri->SchemeIs("data", &isData)) && isData)) {
|
|
// We're loading a javascript: or data: URL from session
|
|
// history. Replace the current document with about:blank to
|
|
// prevent anything from the current document from leaking
|
|
// into any JavaScript code in the URL.
|
|
rv = CreateAboutBlankContentViewer();
|
|
|
|
if (NS_FAILED(rv)) {
|
|
// The creation of the intermittent about:blank content
|
|
// viewer failed for some reason (potentially because the
|
|
// user prevented it). Interrupt the history load.
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
/* If there is a valid postdata *and* the user pressed
|
|
* reload or shift-reload, take user's permission before we
|
|
* repost the data to the server.
|
|
*/
|
|
if ((aLoadType & LOAD_CMD_RELOAD) && postData) {
|
|
|
|
nsCOMPtr<nsIPrompt> prompter;
|
|
PRBool repost;
|
|
nsCOMPtr<nsIStringBundle> stringBundle;
|
|
GetPromptAndStringBundle(getter_AddRefs(prompter),
|
|
getter_AddRefs(stringBundle));
|
|
|
|
if (stringBundle && prompter) {
|
|
nsXPIDLString messageStr;
|
|
nsresult rv = stringBundle->GetStringFromName(NS_LITERAL_STRING("repostConfirm").get(),
|
|
getter_Copies(messageStr));
|
|
|
|
if (NS_SUCCEEDED(rv) && messageStr) {
|
|
prompter->Confirm(nsnull, messageStr, &repost);
|
|
/* If the user pressed cancel in the dialog, return. We're
|
|
* done here.
|
|
*/
|
|
if (!repost)
|
|
return NS_BINDING_ABORTED;
|
|
}
|
|
}
|
|
}
|
|
|
|
rv = InternalLoad(uri,
|
|
referrerURI,
|
|
nsnull, // No owner
|
|
INTERNAL_LOAD_FLAGS_NONE, // Do not inherit owner from document (security-critical!)
|
|
nsnull, // No window target
|
|
contentType.get(), // Type hint
|
|
postData, // Post data stream
|
|
nsnull, // No headers stream
|
|
aLoadType, // Load type
|
|
aEntry, // SHEntry
|
|
PR_TRUE,
|
|
nsnull, // No nsIDocShell
|
|
nsnull); // No nsIRequest
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP nsDocShell::GetShouldSaveLayoutState(PRBool* aShould)
|
|
{
|
|
*aShould = PR_FALSE;
|
|
if (mOSHE) {
|
|
// Don't capture historystate and save it in history
|
|
// if the page asked not to do so.
|
|
mOSHE->GetSaveLayoutStateFlag(aShould);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsDocShell::PersistLayoutHistoryState()
|
|
{
|
|
nsresult rv = NS_OK;
|
|
|
|
if (mOSHE) {
|
|
PRBool shouldSave;
|
|
GetShouldSaveLayoutState(&shouldSave);
|
|
if (!shouldSave)
|
|
return NS_OK;
|
|
|
|
nsCOMPtr<nsIPresShell> shell;
|
|
rv = GetPresShell(getter_AddRefs(shell));
|
|
if (NS_SUCCEEDED(rv) && shell) {
|
|
nsCOMPtr<nsILayoutHistoryState> layoutState;
|
|
rv = shell->CaptureHistoryState(getter_AddRefs(layoutState),
|
|
PR_TRUE);
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
/* static */ nsresult
|
|
nsDocShell::WalkHistoryEntries(nsISHEntry *aRootEntry,
|
|
nsDocShell *aRootShell,
|
|
WalkHistoryEntriesFunc aCallback,
|
|
void *aData)
|
|
{
|
|
NS_ENSURE_TRUE(aRootEntry, NS_ERROR_FAILURE);
|
|
|
|
nsCOMPtr<nsISHContainer> container(do_QueryInterface(aRootEntry));
|
|
if (!container)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
PRInt32 childCount;
|
|
container->GetChildCount(&childCount);
|
|
for (PRInt32 i = 0; i < childCount; i++) {
|
|
nsCOMPtr<nsISHEntry> childEntry;
|
|
container->GetChildAt(i, getter_AddRefs(childEntry));
|
|
if (!childEntry) {
|
|
// childEntry can be null for valid reasons, for example if the
|
|
// docshell at index i never loaded anything useful.
|
|
continue;
|
|
}
|
|
|
|
nsDocShell *childShell = nsnull;
|
|
if (aRootShell) {
|
|
// Walk the children of aRootShell and see if one of them
|
|
// has srcChild as a SHEntry.
|
|
|
|
PRInt32 childCount = aRootShell->mChildList.Count();
|
|
for (PRInt32 j = 0; j < childCount; ++j) {
|
|
nsDocShell *child =
|
|
NS_STATIC_CAST(nsDocShell*, aRootShell->ChildAt(j));
|
|
|
|
if (child->HasHistoryEntry(childEntry)) {
|
|
childShell = child;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
nsresult rv = aCallback(childEntry, childShell, i, aData);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
// callback data for WalkHistoryEntries
|
|
struct CloneAndReplaceData
|
|
{
|
|
CloneAndReplaceData(PRUint32 aCloneID, nsISHEntry *aReplaceEntry,
|
|
nsISHEntry *aDestTreeParent)
|
|
: cloneID(aCloneID),
|
|
replaceEntry(aReplaceEntry),
|
|
destTreeParent(aDestTreeParent) { }
|
|
|
|
PRUint32 cloneID;
|
|
nsISHEntry *replaceEntry;
|
|
nsISHEntry *destTreeParent;
|
|
nsCOMPtr<nsISHEntry> resultEntry;
|
|
};
|
|
|
|
/* static */ nsresult
|
|
nsDocShell::CloneAndReplaceChild(nsISHEntry *aEntry, nsDocShell *aShell,
|
|
PRInt32 aEntryIndex, void *aData)
|
|
{
|
|
nsresult result = NS_OK;
|
|
nsCOMPtr<nsISHEntry> dest;
|
|
|
|
CloneAndReplaceData *data = NS_STATIC_CAST(CloneAndReplaceData*, aData);
|
|
PRUint32 cloneID = data->cloneID;
|
|
nsISHEntry *replaceEntry = data->replaceEntry;
|
|
|
|
PRUint32 srcID;
|
|
aEntry->GetID(&srcID);
|
|
|
|
if (srcID == cloneID) {
|
|
// Just replace the entry, and don't walk the children.
|
|
dest = replaceEntry;
|
|
dest->SetIsSubFrame(PR_TRUE);
|
|
} else {
|
|
// Clone the SHEntry...
|
|
result = aEntry->Clone(getter_AddRefs(dest));
|
|
if (NS_FAILED(result))
|
|
return result;
|
|
|
|
// This entry is for a subframe navigation
|
|
dest->SetIsSubFrame(PR_TRUE);
|
|
|
|
// Walk the children
|
|
CloneAndReplaceData childData(cloneID, replaceEntry, dest);
|
|
result = WalkHistoryEntries(aEntry, aShell,
|
|
CloneAndReplaceChild, &childData);
|
|
if (NS_FAILED(result))
|
|
return result;
|
|
|
|
if (aShell)
|
|
aShell->SwapHistoryEntries(aEntry, dest);
|
|
}
|
|
|
|
nsCOMPtr<nsISHContainer> container =
|
|
do_QueryInterface(data->destTreeParent);
|
|
if (container)
|
|
container->AddChild(dest, aEntryIndex);
|
|
|
|
data->resultEntry = dest;
|
|
return result;
|
|
}
|
|
|
|
/* static */ nsresult
|
|
nsDocShell::CloneAndReplace(nsISHEntry *aSrcEntry,
|
|
nsDocShell *aSrcShell,
|
|
PRUint32 aCloneID,
|
|
nsISHEntry *aReplaceEntry,
|
|
nsISHEntry **aResultEntry)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aResultEntry);
|
|
NS_ENSURE_TRUE(aReplaceEntry, NS_ERROR_FAILURE);
|
|
|
|
CloneAndReplaceData data(aCloneID, aReplaceEntry, nsnull);
|
|
nsresult rv = CloneAndReplaceChild(aSrcEntry, aSrcShell, 0, &data);
|
|
|
|
data.resultEntry.swap(*aResultEntry);
|
|
return rv;
|
|
}
|
|
|
|
|
|
void
|
|
nsDocShell::SwapHistoryEntries(nsISHEntry *aOldEntry, nsISHEntry *aNewEntry)
|
|
{
|
|
if (aOldEntry == mOSHE)
|
|
mOSHE = aNewEntry;
|
|
|
|
if (aOldEntry == mLSHE)
|
|
mLSHE = aNewEntry;
|
|
}
|
|
|
|
|
|
struct SwapEntriesData
|
|
{
|
|
nsDocShell *ignoreShell; // constant; the shell to ignore
|
|
nsISHEntry *destTreeRoot; // constant; the root of the dest tree
|
|
nsISHEntry *destTreeParent; // constant; the node under destTreeRoot
|
|
// whose children will correspond to aEntry
|
|
};
|
|
|
|
|
|
nsresult
|
|
nsDocShell::SetChildHistoryEntry(nsISHEntry *aEntry, nsDocShell *aShell,
|
|
PRInt32 aEntryIndex, void *aData)
|
|
{
|
|
SwapEntriesData *data = NS_STATIC_CAST(SwapEntriesData*, aData);
|
|
nsDocShell *ignoreShell = data->ignoreShell;
|
|
|
|
if (!aShell || aShell == ignoreShell)
|
|
return NS_OK;
|
|
|
|
nsISHEntry *destTreeRoot = data->destTreeRoot;
|
|
|
|
// aEntry is a clone of the child of destTreeParent with the same index.
|
|
nsCOMPtr<nsISHEntry> destEntry;
|
|
nsCOMPtr<nsISHContainer> container =
|
|
do_QueryInterface(data->destTreeParent);
|
|
|
|
if (container) {
|
|
container->GetChildAt(aEntryIndex, getter_AddRefs(destEntry));
|
|
NS_ASSERTION(destEntry, "oops, history trees are out of sync");
|
|
|
|
#ifdef DEBUG
|
|
PRUint32 id1, id2;
|
|
aEntry->GetID(&id1);
|
|
destEntry->GetID(&id2);
|
|
NS_ASSERTION(id1 == id2, "oops, history trees are out of sync");
|
|
#endif
|
|
} else {
|
|
destEntry = destTreeRoot;
|
|
}
|
|
|
|
aShell->SwapHistoryEntries(aEntry, destEntry);
|
|
|
|
// Now handle the children of aEntry.
|
|
SwapEntriesData childData = { ignoreShell, destTreeRoot, destEntry };
|
|
return WalkHistoryEntries(aEntry, aShell,
|
|
SetChildHistoryEntry, &childData);
|
|
}
|
|
|
|
|
|
static nsISHEntry*
|
|
GetRootSHEntry(nsISHEntry *aEntry)
|
|
{
|
|
nsCOMPtr<nsISHEntry> rootEntry = aEntry;
|
|
nsISHEntry *result = nsnull;
|
|
while (rootEntry) {
|
|
result = rootEntry;
|
|
result->GetParent(getter_AddRefs(rootEntry));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
void
|
|
nsDocShell::SetHistoryEntry(nsCOMPtr<nsISHEntry> *aPtr, nsISHEntry *aEntry)
|
|
{
|
|
// We need to sync up the docshell and session history trees for
|
|
// subframe navigation. If the load was in a subframe, we forward up to
|
|
// the root docshell, which will then recursively sync up all docshells
|
|
// to their corresponding entries in the new session history tree.
|
|
// If we don't do this, then we can cache a content viewer on the wrong
|
|
// cloned entry, and subsequently restore it at the wrong time.
|
|
|
|
nsISHEntry *newRootEntry = GetRootSHEntry(aEntry);
|
|
if (newRootEntry) {
|
|
// newRootEntry is now the new root entry.
|
|
// Find the old root entry as well.
|
|
|
|
nsISHEntry *oldRootEntry = GetRootSHEntry(*aPtr);
|
|
if (oldRootEntry) {
|
|
nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
|
|
GetSameTypeParent(getter_AddRefs(parentAsItem));
|
|
nsCOMPtr<nsIDocShell> rootShell = do_QueryInterface(parentAsItem);
|
|
if (rootShell) { // if we're the root just set it, nothing to swap
|
|
SwapEntriesData data = { this, newRootEntry };
|
|
nsIDocShell *rootIDocShell =
|
|
NS_STATIC_CAST(nsIDocShell*, rootShell);
|
|
nsDocShell *rootDocShell = NS_STATIC_CAST(nsDocShell*,
|
|
rootIDocShell);
|
|
|
|
nsresult rv = SetChildHistoryEntry(oldRootEntry, rootDocShell,
|
|
0, &data);
|
|
NS_ASSERTION(NS_SUCCEEDED(rv), "SetChildHistoryEntry failed");
|
|
}
|
|
}
|
|
}
|
|
|
|
*aPtr = aEntry;
|
|
}
|
|
|
|
|
|
nsresult
|
|
nsDocShell::GetRootSessionHistory(nsISHistory ** aReturn)
|
|
{
|
|
nsresult rv;
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> root;
|
|
//Get the root docshell
|
|
rv = GetSameTypeRootTreeItem(getter_AddRefs(root));
|
|
// QI to nsIWebNavigation
|
|
nsCOMPtr<nsIWebNavigation> rootAsWebnav(do_QueryInterface(root));
|
|
if (rootAsWebnav) {
|
|
// Get the handle to SH from the root docshell
|
|
rv = rootAsWebnav->GetSessionHistory(aReturn);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsDocShell::GetHttpChannel(nsIChannel * aChannel, nsIHttpChannel ** aReturn)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aReturn);
|
|
if (!aChannel)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(aChannel));
|
|
if (multiPartChannel) {
|
|
nsCOMPtr<nsIChannel> baseChannel;
|
|
multiPartChannel->GetBaseChannel(getter_AddRefs(baseChannel));
|
|
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(baseChannel));
|
|
*aReturn = httpChannel;
|
|
NS_IF_ADDREF(*aReturn);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
PRBool
|
|
nsDocShell::ShouldDiscardLayoutState(nsIHttpChannel * aChannel)
|
|
{
|
|
// By default layout State will be saved.
|
|
if (!aChannel)
|
|
return PR_FALSE;
|
|
|
|
// figure out if SH should be saving layout state
|
|
nsCOMPtr<nsISupports> securityInfo;
|
|
PRBool noStore = PR_FALSE, noCache = PR_FALSE;
|
|
aChannel->GetSecurityInfo(getter_AddRefs(securityInfo));
|
|
aChannel->IsNoStoreResponse(&noStore);
|
|
aChannel->IsNoCacheResponse(&noCache);
|
|
|
|
return (noStore || (noCache && securityInfo));
|
|
}
|
|
|
|
//*****************************************************************************
|
|
// nsDocShell: nsIEditorDocShell
|
|
//*****************************************************************************
|
|
|
|
NS_IMETHODIMP nsDocShell::GetEditor(nsIEditor * *aEditor)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aEditor);
|
|
nsresult rv = EnsureEditorData();
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
return mEditorData->GetEditor(aEditor);
|
|
}
|
|
|
|
NS_IMETHODIMP nsDocShell::SetEditor(nsIEditor * aEditor)
|
|
{
|
|
nsresult rv = EnsureEditorData();
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
return mEditorData->SetEditor(aEditor);
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP nsDocShell::GetEditable(PRBool *aEditable)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aEditable);
|
|
*aEditable = mEditorData && mEditorData->GetEditable();
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP nsDocShell::GetHasEditingSession(PRBool *aHasEditingSession)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aHasEditingSession);
|
|
|
|
if (mEditorData)
|
|
{
|
|
nsCOMPtr<nsIEditingSession> editingSession;
|
|
mEditorData->GetEditingSession(getter_AddRefs(editingSession));
|
|
*aHasEditingSession = (editingSession.get() != nsnull);
|
|
}
|
|
else
|
|
{
|
|
*aHasEditingSession = PR_FALSE;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsDocShell::MakeEditable(PRBool inWaitForUriLoad)
|
|
{
|
|
nsresult rv = EnsureEditorData();
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
return mEditorData->MakeEditable(inWaitForUriLoad);
|
|
}
|
|
|
|
nsresult
|
|
nsDocShell::AddToGlobalHistory(nsIURI * aURI, PRBool aRedirect, nsIURI * aReferrer)
|
|
{
|
|
if (mItemType != typeContent || !mGlobalHistory)
|
|
return NS_OK;
|
|
|
|
PRBool visited;
|
|
nsresult rv = mGlobalHistory->IsVisited(aURI, &visited);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
rv = mGlobalHistory->AddURI(aURI, aRedirect, !IsFrame(), aReferrer);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
if (!visited) {
|
|
nsCOMPtr<nsIObserverService> obsService =
|
|
do_GetService("@mozilla.org/observer-service;1");
|
|
if (obsService) {
|
|
obsService->NotifyObservers(aURI, NS_LINK_VISITED_EVENT_TOPIC, nsnull);
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
//*****************************************************************************
|
|
// nsDocShell: Helper Routines
|
|
//*****************************************************************************
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::SetLoadType(PRUint32 aLoadType)
|
|
{
|
|
mLoadType = aLoadType;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::GetLoadType(PRUint32 * aLoadType)
|
|
{
|
|
*aLoadType = mLoadType;
|
|
return NS_OK;
|
|
}
|
|
|
|
#define DIALOG_STRING_URI "chrome://global/locale/appstrings.properties"
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::GetPromptAndStringBundle(nsIPrompt ** aPrompt,
|
|
nsIStringBundle ** aStringBundle)
|
|
{
|
|
NS_ENSURE_SUCCESS(GetInterface(NS_GET_IID(nsIPrompt), (void **) aPrompt),
|
|
NS_ERROR_FAILURE);
|
|
|
|
nsCOMPtr<nsIStringBundleService>
|
|
stringBundleService(do_GetService(NS_STRINGBUNDLE_CONTRACTID));
|
|
NS_ENSURE_TRUE(stringBundleService, NS_ERROR_FAILURE);
|
|
|
|
NS_ENSURE_SUCCESS(stringBundleService->
|
|
CreateBundle(DIALOG_STRING_URI,
|
|
aStringBundle),
|
|
NS_ERROR_FAILURE);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::GetChildOffset(nsIDOMNode * aChild, nsIDOMNode * aParent,
|
|
PRInt32 * aOffset)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aChild || aParent);
|
|
|
|
nsCOMPtr<nsIDOMNodeList> childNodes;
|
|
NS_ENSURE_SUCCESS(aParent->GetChildNodes(getter_AddRefs(childNodes)),
|
|
NS_ERROR_FAILURE);
|
|
NS_ENSURE_TRUE(childNodes, NS_ERROR_FAILURE);
|
|
|
|
PRInt32 i = 0;
|
|
|
|
for (; PR_TRUE; i++) {
|
|
nsCOMPtr<nsIDOMNode> childNode;
|
|
NS_ENSURE_SUCCESS(childNodes->Item(i, getter_AddRefs(childNode)),
|
|
NS_ERROR_FAILURE);
|
|
NS_ENSURE_TRUE(childNode, NS_ERROR_FAILURE);
|
|
|
|
if (childNode.get() == aChild) {
|
|
*aOffset = i;
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::GetRootScrollableView(nsIScrollableView ** aOutScrollView)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aOutScrollView);
|
|
|
|
nsCOMPtr<nsIPresShell> shell;
|
|
NS_ENSURE_SUCCESS(GetPresShell(getter_AddRefs(shell)), NS_ERROR_FAILURE);
|
|
NS_ENSURE_TRUE(shell, NS_ERROR_NULL_POINTER);
|
|
|
|
NS_ENSURE_SUCCESS(shell->GetViewManager()->GetRootScrollableView(aOutScrollView),
|
|
NS_ERROR_FAILURE);
|
|
|
|
if (*aOutScrollView == nsnull) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::EnsureScriptEnvironment()
|
|
{
|
|
if (mScriptGlobal)
|
|
return NS_OK;
|
|
|
|
if (mIsBeingDestroyed) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
nsCOMPtr<nsIDOMScriptObjectFactory> factory =
|
|
do_GetService(kDOMScriptObjectFactoryCID);
|
|
NS_ENSURE_TRUE(factory, NS_ERROR_FAILURE);
|
|
|
|
factory->NewScriptGlobalObject(mItemType == typeChrome,
|
|
getter_AddRefs(mScriptGlobal));
|
|
NS_ENSURE_TRUE(mScriptGlobal, NS_ERROR_FAILURE);
|
|
|
|
mScriptGlobal->SetDocShell(NS_STATIC_CAST(nsIDocShell *, this));
|
|
mScriptGlobal->
|
|
SetGlobalObjectOwner(NS_STATIC_CAST
|
|
(nsIScriptGlobalObjectOwner *, this));
|
|
|
|
nsCOMPtr<nsIScriptContext> context;
|
|
factory->NewScriptContext(mScriptGlobal, getter_AddRefs(context));
|
|
NS_ENSURE_TRUE(context, NS_ERROR_FAILURE);
|
|
|
|
// Note that mScriptGlobal has taken a reference to the script
|
|
// context, so we don't have to.
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::EnsureEditorData()
|
|
{
|
|
if (!mEditorData && !mIsBeingDestroyed)
|
|
{
|
|
mEditorData = new nsDocShellEditorData(this);
|
|
if (!mEditorData) return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
return mEditorData ? NS_OK : NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
nsresult
|
|
nsDocShell::EnsureTransferableHookData()
|
|
{
|
|
if (!mTransferableHookData) {
|
|
mTransferableHookData = new nsTransferableHookData();
|
|
if (!mTransferableHookData) return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP nsDocShell::EnsureFind()
|
|
{
|
|
nsresult rv;
|
|
if (!mFind)
|
|
{
|
|
mFind = do_CreateInstance("@mozilla.org/embedcomp/find;1", &rv);
|
|
if (NS_FAILED(rv)) return rv;
|
|
}
|
|
|
|
// we promise that the nsIWebBrowserFind that we return has been set
|
|
// up to point to the focused, or content window, so we have to
|
|
// set that up each time.
|
|
|
|
nsIScriptGlobalObject* scriptGO = GetScriptGlobalObject();
|
|
NS_ENSURE_TRUE(scriptGO, NS_ERROR_UNEXPECTED);
|
|
|
|
// default to our window
|
|
nsCOMPtr<nsIDOMWindow> rootWindow = do_QueryInterface(scriptGO);
|
|
nsCOMPtr<nsIDOMWindow> windowToSearch = rootWindow;
|
|
|
|
// if we can, search the focused window
|
|
nsCOMPtr<nsPIDOMWindow> ourWindow = do_QueryInterface(scriptGO);
|
|
nsIFocusController *focusController = nsnull;
|
|
if (ourWindow)
|
|
focusController = ourWindow->GetRootFocusController();
|
|
if (focusController)
|
|
{
|
|
nsCOMPtr<nsIDOMWindowInternal> focusedWindow;
|
|
focusController->GetFocusedWindow(getter_AddRefs(focusedWindow));
|
|
if (focusedWindow)
|
|
windowToSearch = focusedWindow;
|
|
}
|
|
|
|
nsCOMPtr<nsIWebBrowserFindInFrames> findInFrames = do_QueryInterface(mFind);
|
|
if (!findInFrames) return NS_ERROR_NO_INTERFACE;
|
|
|
|
rv = findInFrames->SetRootSearchFrame(rootWindow);
|
|
if (NS_FAILED(rv)) return rv;
|
|
rv = findInFrames->SetCurrentSearchFrame(windowToSearch);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
PRBool
|
|
nsDocShell::IsFrame()
|
|
{
|
|
nsCOMPtr<nsIDocShellTreeItem> parent =
|
|
do_QueryInterface(GetAsSupports(mParent));
|
|
if (parent) {
|
|
PRInt32 parentType = ~mItemType; // Not us
|
|
parent->GetItemType(&parentType);
|
|
if (parentType == mItemType) // This is a frame
|
|
return PR_TRUE;
|
|
}
|
|
|
|
return PR_FALSE;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::GetHasFocus(PRBool *aHasFocus)
|
|
{
|
|
*aHasFocus = mHasFocus;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::SetHasFocus(PRBool aHasFocus)
|
|
{
|
|
#ifdef DEBUG_DOCSHELL_FOCUS
|
|
printf(">>>>>>>>>> nsDocShell::SetHasFocus: %p %s\n", this, aHasFocus?"Yes":"No");
|
|
#endif
|
|
|
|
mHasFocus = aHasFocus;
|
|
|
|
nsDocShellFocusController* dsfc = nsDocShellFocusController::GetInstance();
|
|
if (dsfc && aHasFocus) {
|
|
dsfc->Focus(this);
|
|
}
|
|
|
|
if (!aHasFocus) {
|
|
// We may be in a situation where the focus outline was shown
|
|
// on this document because the user tabbed into it, but the focus
|
|
// is now switching to another document via a click. In this case,
|
|
// we need to make sure the focus outline is removed from this document.
|
|
SetCanvasHasFocus(PR_FALSE);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
//-------------------------------------------------------
|
|
// Tells the HTMLFrame/CanvasFrame that is now has focus
|
|
NS_IMETHODIMP
|
|
nsDocShell::SetCanvasHasFocus(PRBool aCanvasHasFocus)
|
|
{
|
|
if (mEditorData && mEditorData->GetEditable())
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
|
|
nsCOMPtr<nsIPresShell> presShell;
|
|
GetPresShell(getter_AddRefs(presShell));
|
|
if (!presShell) return NS_ERROR_FAILURE;
|
|
|
|
nsIDocument *doc = presShell->GetDocument();
|
|
if (!doc) return NS_ERROR_FAILURE;
|
|
|
|
nsIContent *rootContent = doc->GetRootContent();
|
|
if (!rootContent) return NS_ERROR_FAILURE;
|
|
|
|
nsIFrame* frame;
|
|
presShell->GetPrimaryFrameFor(rootContent, &frame);
|
|
if (frame) {
|
|
frame = frame->GetParent();
|
|
if (frame) {
|
|
nsICanvasFrame* canvasFrame;
|
|
if (NS_SUCCEEDED(frame->QueryInterface(NS_GET_IID(nsICanvasFrame), (void**)&canvasFrame))) {
|
|
canvasFrame->SetHasFocus(aCanvasHasFocus);
|
|
|
|
nsIViewManager* vm = presShell->GetViewManager();
|
|
if (vm) {
|
|
vm->UpdateAllViews(NS_VMREFRESH_NO_SYNC);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
}
|
|
}
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::GetCanvasHasFocus(PRBool *aCanvasHasFocus)
|
|
{
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
/* boolean IsBeingDestroyed (); */
|
|
NS_IMETHODIMP
|
|
nsDocShell::IsBeingDestroyed(PRBool *aDoomed)
|
|
{
|
|
NS_ENSURE_ARG(aDoomed);
|
|
*aDoomed = mIsBeingDestroyed;
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::GetIsExecutingOnLoadHandler(PRBool *aResult)
|
|
{
|
|
NS_ENSURE_ARG(aResult);
|
|
*aResult = mIsExecutingOnLoadHandler;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::GetLayoutHistoryState(nsILayoutHistoryState **aLayoutHistoryState)
|
|
{
|
|
if (mOSHE)
|
|
mOSHE->GetLayoutHistoryState(aLayoutHistoryState);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::SetLayoutHistoryState(nsILayoutHistoryState *aLayoutHistoryState)
|
|
{
|
|
if (mOSHE)
|
|
mOSHE->SetLayoutHistoryState(aLayoutHistoryState);
|
|
return NS_OK;
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//*** nsRefreshTimer: Object Management
|
|
//*****************************************************************************
|
|
|
|
nsRefreshTimer::nsRefreshTimer()
|
|
: mDelay(0), mRepeat(PR_FALSE), mMetaRefresh(PR_FALSE)
|
|
{
|
|
}
|
|
|
|
nsRefreshTimer::~nsRefreshTimer()
|
|
{
|
|
}
|
|
|
|
//*****************************************************************************
|
|
// nsRefreshTimer::nsISupports
|
|
//*****************************************************************************
|
|
|
|
NS_IMPL_THREADSAFE_ADDREF(nsRefreshTimer)
|
|
NS_IMPL_THREADSAFE_RELEASE(nsRefreshTimer)
|
|
|
|
NS_INTERFACE_MAP_BEGIN(nsRefreshTimer)
|
|
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITimerCallback)
|
|
NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
|
|
NS_INTERFACE_MAP_END_THREADSAFE
|
|
|
|
///*****************************************************************************
|
|
// nsRefreshTimer::nsITimerCallback
|
|
//*****************************************************************************
|
|
NS_IMETHODIMP
|
|
nsRefreshTimer::Notify(nsITimer * aTimer)
|
|
{
|
|
NS_ASSERTION(mDocShell, "DocShell is somehow null");
|
|
|
|
if (mDocShell && aTimer) {
|
|
/* Check if Meta refresh/redirects are permitted. Some
|
|
* embedded applications may not want to do this.
|
|
*/
|
|
PRBool allowRedirects = PR_TRUE;
|
|
mDocShell->GetAllowMetaRedirects(&allowRedirects);
|
|
if (!allowRedirects)
|
|
return NS_OK;
|
|
// Get the delay count
|
|
PRUint32 delay = 0;
|
|
aTimer->GetDelay(&delay);
|
|
// Get the current uri from the docshell.
|
|
nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell));
|
|
nsCOMPtr<nsIURI> currURI;
|
|
if (webNav) {
|
|
webNav->GetCurrentURI(getter_AddRefs(currURI));
|
|
}
|
|
nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
|
|
mDocShell->CreateLoadInfo(getter_AddRefs(loadInfo));
|
|
NS_ENSURE_TRUE(loadInfo, NS_OK);
|
|
|
|
/* 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(currURI);
|
|
|
|
/* Check if this META refresh causes a redirection
|
|
* to another site.
|
|
*/
|
|
PRBool equalUri = PR_FALSE;
|
|
nsresult rv = mURI->Equals(currURI, &equalUri);
|
|
if (NS_SUCCEEDED(rv) && (!equalUri) && mMetaRefresh) {
|
|
|
|
/* 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 (delay <= REFRESH_REDIRECT_TIMER) {
|
|
loadInfo->SetLoadType(nsIDocShellLoadInfo::loadNormalReplace);
|
|
|
|
/* for redirects we mimic HTTP, which passes the
|
|
* original referrer
|
|
*/
|
|
nsCOMPtr<nsIURI> internalReferrer;
|
|
nsCOMPtr<nsIWebNavigation> webNav =
|
|
do_QueryInterface(mDocShell);
|
|
if (webNav) {
|
|
webNav->GetReferringURI(getter_AddRefs(internalReferrer));
|
|
if (internalReferrer) {
|
|
loadInfo->SetReferrer(internalReferrer);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
loadInfo->SetLoadType(nsIDocShellLoadInfo::loadRefresh);
|
|
/*
|
|
* LoadURL(...) will cancel all refresh timers... This causes the Timer and
|
|
* its refreshData instance to be released...
|
|
*/
|
|
mDocShell->LoadURI(mURI, loadInfo,
|
|
nsIWebNavigation::LOAD_FLAGS_NONE, PR_TRUE);
|
|
return NS_OK;
|
|
|
|
}
|
|
else
|
|
loadInfo->SetLoadType(nsIDocShellLoadInfo::loadRefresh);
|
|
mDocShell->LoadURI(mURI, loadInfo, nsIWebNavigation::LOAD_FLAGS_NONE, PR_TRUE);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//*** nsDocShellFocusController: Object Management
|
|
//*****************************************************************************
|
|
void
|
|
nsDocShellFocusController::Focus(nsIDocShell* aDocShell)
|
|
{
|
|
#ifdef DEBUG_DOCSHELL_FOCUS
|
|
printf("****** nsDocShellFocusController Focus To: %p Blur To: %p\n", aDocShell, mFocusedDocShell);
|
|
#endif
|
|
|
|
if (aDocShell != mFocusedDocShell) {
|
|
if (mFocusedDocShell) {
|
|
mFocusedDocShell->SetHasFocus(PR_FALSE);
|
|
}
|
|
mFocusedDocShell = aDocShell;
|
|
}
|
|
|
|
}
|
|
|
|
//--------------------------------------------------
|
|
// This is need for when the document with focus goes away
|
|
void
|
|
nsDocShellFocusController::ClosingDown(nsIDocShell* aDocShell)
|
|
{
|
|
if (aDocShell == mFocusedDocShell) {
|
|
mFocusedDocShell = nsnull;
|
|
}
|
|
}
|
|
|
|
//*****************************************************************************
|
|
// nsDocShell::InterfaceRequestorProxy
|
|
//*****************************************************************************
|
|
nsDocShell::InterfaceRequestorProxy::InterfaceRequestorProxy(nsIInterfaceRequestor* p)
|
|
{
|
|
if (p) {
|
|
mWeakPtr = do_GetWeakReference(p);
|
|
}
|
|
}
|
|
|
|
nsDocShell::InterfaceRequestorProxy::~InterfaceRequestorProxy()
|
|
{
|
|
mWeakPtr = nsnull;
|
|
}
|
|
|
|
NS_IMPL_THREADSAFE_ISUPPORTS1(nsDocShell::InterfaceRequestorProxy, nsIInterfaceRequestor)
|
|
|
|
NS_IMETHODIMP
|
|
nsDocShell::InterfaceRequestorProxy::GetInterface(const nsIID & aIID, void **aSink)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aSink);
|
|
nsCOMPtr<nsIInterfaceRequestor> ifReq = do_QueryReferent(mWeakPtr);
|
|
if (ifReq) {
|
|
return ifReq->GetInterface(aIID, aSink);
|
|
}
|
|
*aSink = nsnull;
|
|
return NS_NOINTERFACE;
|
|
}
|
|
|
|
nsresult
|
|
nsDocShell::SetBaseUrlForWyciwyg(nsIContentViewer * aContentViewer)
|
|
{
|
|
if (!aContentViewer)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
nsCOMPtr<nsIURI> baseURI;
|
|
nsCOMPtr<nsIDocument> document;
|
|
nsresult rv = NS_ERROR_NOT_AVAILABLE;
|
|
|
|
if (sURIFixup)
|
|
rv = sURIFixup->CreateExposableURI(mCurrentURI,
|
|
getter_AddRefs(baseURI));
|
|
|
|
// Get the current document and set the base uri
|
|
if (baseURI) {
|
|
nsCOMPtr<nsIDocumentViewer> docViewer(do_QueryInterface(aContentViewer));
|
|
if (docViewer) {
|
|
rv = docViewer->GetDocument(getter_AddRefs(document));
|
|
if (document)
|
|
rv = document->SetBaseURI(baseURI);
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
//*****************************************************************************
|
|
// nsDocShell::nsIAuthPromptProvider
|
|
//*****************************************************************************
|
|
|
|
nsresult
|
|
nsDocShell::GetAuthPrompt(PRUint32 aPromptReason, nsIAuthPrompt **aResult)
|
|
{
|
|
// a priority prompt request will override a false mAllowAuth setting
|
|
PRBool priorityPrompt = (aPromptReason == PROMPT_PROXY);
|
|
|
|
if (!mAllowAuth && !priorityPrompt)
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
|
|
// we're either allowing auth, or it's a proxy request
|
|
nsresult rv;
|
|
nsCOMPtr<nsIWindowWatcher> wwatch =
|
|
do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
rv = EnsureScriptEnvironment();
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsCOMPtr<nsIDOMWindow> 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.
|
|
|
|
return wwatch->GetNewAuthPrompter(window, aResult);
|
|
}
|