/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vi:tw=2:ts=2:et:sw=2: * * The contents of this file are subject to the Netscape 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/NPL/ * * 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 Mozilla Communicator client code. * * The Initial Developer of the Original Code is Netscape Communications * Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): * Travis Bogard * Pierre Phaneuf */ #ifdef XP_OS2_VACPP // XXX every other file that pulls in _os2.h has no problem with HTMX there; // this one does; the problem may lie with the order of the headers below, // which is why this fix is here instead of in _os2.h typedef unsigned long HMTX; #endif #include "nsDocShell.h" #include "nsIWebShell.h" #include "nsIWebBrowserChrome.h" #include "nsIInterfaceRequestor.h" #include "nsIDocumentLoader.h" #include "nsIDocumentLoaderObserver.h" #include "nsIDocumentLoaderFactory.h" #include "nsIContentViewer.h" #include "nsIDocumentViewer.h" #include "nsIMarkupDocumentViewer.h" #include "nsIClipboardCommands.h" #include "nsILinkHandler.h" #include "nsIStreamListener.h" #include "nsIPrompt.h" #include "nsNetUtil.h" #include "nsIProtocolHandler.h" #include "nsIDNSService.h" #include "nsIRefreshURI.h" #include "nsIScriptGlobalObject.h" #include "nsIScriptGlobalObjectOwner.h" #include "nsIProgressEventSink.h" #include "nsDOMEvent.h" #include "nsIPresContext.h" #include "nsIComponentManager.h" #include "nsIServiceManager.h" #include "nsIEventQueueService.h" #include "nsCRT.h" #include "nsVoidArray.h" #include "nsString.h" #include "nsWidgetsCID.h" #include "nsGfxCIID.h" #include "plevent.h" #include "prprf.h" #include "nsIPluginHost.h" #include "nsplugin.h" //#include "nsPluginsCID.h" #include "nsIPluginManager.h" #include "nsIPref.h" #include "nsITimer.h" #include "nsITimerCallback.h" #include "nsIContent.h" #include "prlog.h" #include "nsCOMPtr.h" #include "nsIPresShell.h" #include "nsIWebShellServices.h" #include "nsIGlobalHistory.h" #include "prmem.h" #include "nsXPIDLString.h" #include "nsDOMError.h" #include "nsIDOMHTMLElement.h" #include "nsIDOMHTMLDocument.h" #include "nsLayoutCID.h" #include "nsIDOMRange.h" #include "nsIURIContentListener.h" #include "nsIDOMDocument.h" #include "nsTimer.h" #include "nsIBaseWindow.h" #include "nsIDocShell.h" #include "nsIDocShellTreeItem.h" #include "nsIDocShellTreeNode.h" #include "nsIDocShellTreeOwner.h" #include "nsCURILoader.h" #include "nsIDOMWindow.h" #include "nsEscape.h" #include "nsIPlatformCharset.h" #include "nsICharsetConverterManager.h" #include "nsISocketTransportService.h" #include "nsIHTTPChannel.h" // add this to the ick include list...we need it to QI for post data interface #include "nsHTTPEnums.h" #include "nsILocaleService.h" #include "nsIStringBundle.h" static NS_DEFINE_CID(kLocaleServiceCID, NS_LOCALESERVICE_CID); static NS_DEFINE_CID(kPrefServiceCID, NS_PREF_CID); static NS_DEFINE_CID(kCStringBundleServiceCID, NS_STRINGBUNDLESERVICE_CID); static NS_DEFINE_CID(kPlatformCharsetCID, NS_PLATFORMCHARSET_CID); static NS_DEFINE_CID(kCharsetConverterManagerCID, NS_ICHARSETCONVERTERMANAGER_CID); #include "nsIIOService.h" #include "nsIURL.h" #include "nsIProtocolHandler.h" //XXX for nsIPostData; this is wrong; we shouldn't see the nsIDocument type #include "nsIDocument.h" #ifdef DEBUG #undef NOISY_LINKS #undef NOISY_WEBSHELL_LEAKS #else #undef NOISY_LINKS #undef NOISY_WEBSHELL_LEAKS #endif #define NOISY_WEBSHELL_LEAKS #ifdef NOISY_WEBSHELL_LEAKS #undef DETECT_WEBSHELL_LEAKS #define DETECT_WEBSHELL_LEAKS #endif #ifdef NS_DEBUG /** * Note: the log module is created during initialization which * means that you cannot perform logging before then. */ static PRLogModuleInfo* gLogModule = PR_NewLogModule("webshell"); #endif #define WEB_TRACE_CALLS 0x1 #define WEB_TRACE_HISTORY 0x2 #define WEB_LOG_TEST(_lm,_bit) (PRIntn((_lm)->level) & (_bit)) #ifdef NS_DEBUG #define WEB_TRACE(_bit,_args) \ PR_BEGIN_MACRO \ if (WEB_LOG_TEST(gLogModule,_bit)) { \ PR_LogPrint _args; \ } \ PR_END_MACRO #else #define WEB_TRACE(_bit,_args) #endif #if OLD_EVENT_QUEUE /* The following is not used for the GTK version of the browser. * It is still lurking around for Motif */ PLEventQueue* gWebShell_UnixEventQueue; void nsWebShell_SetUnixEventQueue(PLEventQueue* aEventQueue) { gWebShell_UnixEventQueue = aEventQueue; } #endif /* OLD_EVENT_QUEUE */ static NS_DEFINE_CID(kGlobalHistoryCID, NS_GLOBALHISTORY_CID); static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID); //---------------------------------------------------------------------- typedef enum { eCharsetReloadInit, eCharsetReloadRequested, eCharsetReloadStopOrigional } eCharsetReloadState; class nsWebShell : public nsDocShell, public nsIWebShell, public nsIWebShellContainer, public nsIWebShellServices, public nsILinkHandler, public nsIDocumentLoaderObserver, public nsIProgressEventSink, // should go away (nsIDocLoaderObs) public nsIRefreshURI, public nsIURIContentListener, public nsIClipboardCommands { public: nsWebShell(); virtual ~nsWebShell(); NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW // nsISupports NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIURICONTENTLISTENER // nsIInterfaceRequestor NS_DECL_NSIINTERFACEREQUESTOR NS_DECL_NSIDOCUMENTLOADEROBSERVER NS_IMETHOD SetupNewViewer(nsIContentViewer* aViewer); // nsIContentViewerContainer NS_IMETHOD Embed(nsIContentViewer* aDocViewer, const char* aCommand, nsISupports* aExtraInfo); // nsIWebShell NS_IMETHOD SetContainer(nsIWebShellContainer* aContainer); NS_IMETHOD GetContainer(nsIWebShellContainer*& aResult); NS_IMETHOD GetTopLevelWindow(nsIWebShellContainer** aWebShellWindow); NS_IMETHOD GetRootWebShell(nsIWebShell*& aResult); NS_IMETHOD SetParent(nsIWebShell* aParent); NS_IMETHOD GetParent(nsIWebShell*& aParent); NS_IMETHOD GetReferrer(nsIURI **aReferrer); // Document load api's NS_IMETHOD GetDocumentLoader(nsIDocumentLoader*& aResult); NS_IMETHOD LoadURL(const PRUnichar *aURLSpec, nsIInputStream* aPostDataStream=nsnull, PRBool aModifyHistory=PR_TRUE, nsLoadFlags aType = nsIChannel::LOAD_NORMAL, const PRUint32 localIP = 0, nsISupports * aHistoryState = nsnull, const PRUnichar* aReferrer=nsnull); NS_IMETHOD LoadURL(const PRUnichar *aURLSpec, const char* aCommand, nsIInputStream* aPostDataStream=nsnull, PRBool aModifyHistory=PR_TRUE, nsLoadFlags aType = nsIChannel::LOAD_NORMAL, const PRUint32 localIP = 0, nsISupports * aHistoryState=nsnull, const PRUnichar* aReferrer=nsnull, const char * aWindowTarget = nsnull); NS_IMETHOD LoadURI(nsIURI * aUri, const char * aCommand, nsIInputStream* aPostDataStream=nsnull, PRBool aModifyHistory=PR_TRUE, nsLoadFlags aType = nsIChannel::LOAD_NORMAL, const PRUint32 aLocalIP=0, nsISupports * aHistoryState=nsnull, const PRUnichar* aReferrer=nsnull, const char * aWindowTarget = nsnull); NS_IMETHOD GetCanGoBack(PRBool* aCanGoBack); NS_IMETHOD GetCanGoForward(PRBool* aCanGoForward); NS_IMETHOD GoBack(); NS_IMETHOD GoForward(); NS_IMETHOD LoadURI(const PRUnichar* aURI); NS_IMETHOD InternalLoad(nsIURI* aURI, nsIURI* aReferrer, nsIInputStream* aPostData, loadType aLoadType); NS_IMETHOD Stop(void); void SetReferrer(const PRUnichar* aReferrer); // History api's NS_IMETHOD GoTo(PRInt32 aHistoryIndex); NS_IMETHOD GetHistoryLength(PRInt32& aResult); NS_IMETHOD GetHistoryIndex(PRInt32& aResult); NS_IMETHOD GetURL(PRInt32 aHistoryIndex, const PRUnichar** aURLResult); // nsIWebShellContainer NS_IMETHOD WillLoadURL(nsIWebShell* aShell, const PRUnichar* aURL, nsLoadType aReason); NS_IMETHOD BeginLoadURL(nsIWebShell* aShell, const PRUnichar* aURL); NS_IMETHOD ProgressLoadURL(nsIWebShell* aShell, const PRUnichar* aURL, PRInt32 aProgress, PRInt32 aProgressMax); NS_IMETHOD EndLoadURL(nsIWebShell* aShell, const PRUnichar* aURL, nsresult aStatus); NS_IMETHOD GetHistoryState(nsISupports** aLayoutHistoryState); NS_IMETHOD SetHistoryState(nsISupports* aLayoutHistoryState); NS_IMETHOD FireUnloadEvent(void); // nsIWebShellServices NS_IMETHOD LoadDocument(const char* aURL, const char* aCharset= nsnull , nsCharsetSource aSource = kCharsetUninitialized); NS_IMETHOD ReloadDocument(const char* aCharset= nsnull , nsCharsetSource aSource = kCharsetUninitialized, const char* aCmd=nsnull); NS_IMETHOD StopDocumentLoad(void); NS_IMETHOD SetRendering(PRBool aRender); // nsILinkHandler NS_IMETHOD OnLinkClick(nsIContent* aContent, nsLinkVerb aVerb, const PRUnichar* aURLSpec, const PRUnichar* aTargetSpec, nsIInputStream* aPostDataStream = 0); NS_IMETHOD OnOverLink(nsIContent* aContent, const PRUnichar* aURLSpec, const PRUnichar* aTargetSpec); NS_IMETHOD GetLinkState(const PRUnichar* aURLSpec, nsLinkState& aState); NS_IMETHOD RefreshURL(const char* aURL, PRInt32 millis, PRBool repeat); // nsIRefreshURL interface methods... NS_IMETHOD RefreshURI(nsIURI* aURI, PRInt32 aMillis, PRBool aRepeat); NS_IMETHOD CancelRefreshURITimers(void); // nsIProgressEventSink NS_DECL_NSIPROGRESSEVENTSINK // nsIClipboardCommands NS_IMETHOD CanCutSelection (PRBool* aResult); NS_IMETHOD CanCopySelection (PRBool* aResult); NS_IMETHOD CanPasteSelection(PRBool* aResult); NS_IMETHOD CutSelection (void); NS_IMETHOD CopySelection (void); NS_IMETHOD PasteSelection(void); NS_IMETHOD SelectAll(void); NS_IMETHOD SelectNone(void); NS_IMETHOD FindNext(const PRUnichar * aSearchStr, PRBool aMatchCase, PRBool aSearchDown, PRBool &aIsFound); // nsIBaseWindow NS_IMETHOD Create(); NS_IMETHOD Destroy(); NS_IMETHOD SetPositionAndSize(PRInt32 x, PRInt32 y, PRInt32 cx, PRInt32 cy, PRBool fRepaint); NS_IMETHOD GetPositionAndSize(PRInt32* x, PRInt32* y, PRInt32* cx, PRInt32* cy); // nsIDocShell NS_IMETHOD SetDocument(nsIDOMDocument *aDOMDoc, nsIDOMElement *aRootNode); // nsWebShell nsIEventQueue* GetEventQueue(void); void HandleLinkClickEvent(nsIContent *aContent, nsLinkVerb aVerb, const PRUnichar* aURLSpec, const PRUnichar* aTargetSpec, nsIInputStream* aPostDataStream = 0); void ShowHistory(); static void RefreshURLCallback(nsITimer* aTimer, void* aClosure); static nsEventStatus PR_CALLBACK HandleEvent(nsGUIEvent *aEvent); nsresult CreatePluginHost(PRBool aAllowPlugins); nsresult DestroyPluginHost(void); NS_IMETHOD SetSessionHistory(nsISessionHistory * aSHist); NS_IMETHOD GetSessionHistory(nsISessionHistory *& aResult); NS_IMETHOD SetIsInSHist(PRBool aIsFrame); NS_IMETHOD GetIsInSHist(PRBool& aIsFrame); NS_IMETHOD SetURL(const PRUnichar* aURL); protected: void GetRootWebShellEvenIfChrome(nsIWebShell** aResult); void InitFrameData(); nsresult CheckForTrailingSlash(nsIURI* aURL); nsresult InitDialogVars(void); nsIEventQueue* mThreadEventQueue; nsIWebShellContainer* mContainer; nsIDocumentLoader* mDocLoader; nsCOMPtr mPrompter; nsCOMPtr mStringBundle; nsString mDefaultCharacterSet; nsVoidArray mHistory; PRInt32 mHistoryIndex; PRBool mFiredUnloadEvent; nsIGlobalHistory* mHistoryService; nsISessionHistory * mSHist; nsRect mBounds; nsString mOverURL; nsString mOverTarget; PRPackedBool mIsInSHist; PRPackedBool mFailedToLoadHistoryService; nsScrollPreference mScrollPref; nsVoidArray mRefreshments; eCharsetReloadState mCharsetReloadState; nsISupports* mHistoryState; // Weak reference. Session history owns this. nsresult FireUnloadForChildren(); nsresult DoLoadURL(nsIURI * aUri, const char* aCommand, nsIInputStream* aPostDataStream, nsLoadFlags aType, const PRUint32 aLocalIP, const PRUnichar* aReferrer, const char * aWindowTarget, PRBool aKickOffLoad = PR_TRUE); nsresult PrepareToLoadURI(nsIURI * aUri, nsIInputStream * aPostStream, PRBool aModifyHistory, nsLoadFlags aType, const PRUint32 aLocalIP, nsISupports * aHistoryState, const PRUnichar * aReferrer); nsresult CreateViewer(nsIChannel* aChannel, const char* aContentType, const char* aCommand, nsIStreamListener** aResult); static nsIPluginHost *mPluginHost; static nsIPluginManager *mPluginManager; static PRUint32 mPluginInitCnt; PRBool mProcessedEndDocumentLoad; PRBool mViewSource; MOZ_TIMER_DECLARE(mTotalTime) #ifdef DETECT_WEBSHELL_LEAKS private: // We're counting the number of |nsWebShells| to help find leaks static unsigned long gNumberOfWebShells; public: static unsigned long TotalWebShellsInExistence() { return gNumberOfWebShells; } #endif }; #ifdef DETECT_WEBSHELL_LEAKS unsigned long nsWebShell::gNumberOfWebShells = 0; extern "C" NS_WEB unsigned long NS_TotalWebShellsInExistence() { return nsWebShell::TotalWebShellsInExistence(); } #endif //---------------------------------------------------------------------- // Class IID's static NS_DEFINE_IID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID); static NS_DEFINE_IID(kChildCID, NS_CHILD_CID); static NS_DEFINE_IID(kDocLoaderServiceCID, NS_DOCUMENTLOADER_SERVICE_CID); static NS_DEFINE_IID(kWebShellCID, NS_WEB_SHELL_CID); // IID's static NS_DEFINE_IID(kIContentViewerContainerIID, NS_ICONTENT_VIEWER_CONTAINER_IID); static NS_DEFINE_IID(kIProgressEventSinkIID, NS_IPROGRESSEVENTSINK_IID); static NS_DEFINE_IID(kIDocumentLoaderIID, NS_IDOCUMENTLOADER_IID); static NS_DEFINE_IID(kIFactoryIID, NS_IFACTORY_IID); static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID); static NS_DEFINE_IID(kRefreshURIIID, NS_IREFRESHURI_IID); static NS_DEFINE_IID(kIWidgetIID, NS_IWIDGET_IID); static NS_DEFINE_IID(kIPluginManagerIID, NS_IPLUGINMANAGER_IID); static NS_DEFINE_IID(kIPluginHostIID, NS_IPLUGINHOST_IID); static NS_DEFINE_IID(kCPluginManagerCID, NS_PLUGINMANAGER_CID); static NS_DEFINE_IID(kIDocumentViewerIID, NS_IDOCUMENT_VIEWER_IID); static NS_DEFINE_IID(kITimerCallbackIID, NS_ITIMERCALLBACK_IID); static NS_DEFINE_IID(kIWebShellContainerIID, NS_IWEB_SHELL_CONTAINER_IID); static NS_DEFINE_IID(kIClipboardCommandsIID, NS_ICLIPBOARDCOMMANDS_IID); static NS_DEFINE_IID(kIEventQueueServiceIID, NS_IEVENTQUEUESERVICE_IID); static NS_DEFINE_IID(kISessionHistoryIID, NS_ISESSIONHISTORY_IID); static NS_DEFINE_IID(kIDOMHTMLDocumentIID, NS_IDOMHTMLDOCUMENT_IID); static NS_DEFINE_CID(kCDOMRangeCID, NS_RANGE_CID); // XXX not sure static NS_DEFINE_IID(kILinkHandlerIID, NS_ILINKHANDLER_IID); nsIPluginHost *nsWebShell::mPluginHost = nsnull; nsIPluginManager *nsWebShell::mPluginManager = nsnull; PRUint32 nsWebShell::mPluginInitCnt = 0; nsresult nsWebShell::CreatePluginHost(PRBool aAllowPlugins) { nsresult rv = NS_OK; if ((PR_TRUE == aAllowPlugins) && (0 == mPluginInitCnt)) { if (nsnull == mPluginManager) { // use the service manager to obtain the plugin manager. rv = nsServiceManager::GetService(kCPluginManagerCID, kIPluginManagerIID, (nsISupports**)&mPluginManager); if (NS_OK == rv) { if (NS_OK == mPluginManager->QueryInterface(kIPluginHostIID, (void **)&mPluginHost)) { mPluginHost->Init(); } } } } mPluginInitCnt++; return rv; } nsresult nsWebShell::DestroyPluginHost(void) { mPluginInitCnt--; NS_ASSERTION(!(mPluginInitCnt < 0), "underflow in plugin host destruction"); if (0 == mPluginInitCnt) { if (nsnull != mPluginHost) { mPluginHost->Destroy(); mPluginHost->Release(); mPluginHost = NULL; } // use the service manager to release the plugin manager. if (nsnull != mPluginManager) { nsServiceManager::ReleaseService(kCPluginManagerCID, mPluginManager); mPluginManager = NULL; } } return NS_OK; } //---------------------------------------------------------------------- // Note: operator new zeros our memory nsWebShell::nsWebShell() : nsDocShell() { #ifdef DETECT_WEBSHELL_LEAKS // We're counting the number of |nsWebShells| to help find leaks ++gNumberOfWebShells; #endif #ifdef NOISY_WEBSHELL_LEAKS printf("WEBSHELL+ = %ld\n", gNumberOfWebShells); #endif NS_INIT_REFCNT(); mHistoryIndex = -1; mScrollPref = nsScrollPreference_kAuto; mThreadEventQueue = nsnull; InitFrameData(); mItemType = typeContent; mSHist = nsnull; mIsInSHist = PR_FALSE; mFailedToLoadHistoryService = PR_FALSE; mDefaultCharacterSet = ""; mProcessedEndDocumentLoad = PR_FALSE; mCharsetReloadState = eCharsetReloadInit; mViewSource=PR_FALSE; mHistoryService = nsnull; mHistoryState = nsnull; mFiredUnloadEvent = PR_FALSE; mBounds.SetRect(0, 0, 0, 0); } nsWebShell::~nsWebShell() { if (nsnull != mHistoryService) { nsServiceManager::ReleaseService(kGlobalHistoryCID, mHistoryService); mHistoryService = nsnull; } // Stop any pending document loads and destroy the loader... if (nsnull != mDocLoader) { mDocLoader->Stop(); mDocLoader->RemoveObserver((nsIDocumentLoaderObserver*)this); mDocLoader->SetContainer(nsnull); NS_RELEASE(mDocLoader); } // Cancel any timers that were set for this loader. CancelRefreshURITimers(); ++mRefCnt; // following releases can cause this destructor to be called // recursively if the refcount is allowed to remain 0 NS_IF_RELEASE(mSHist); NS_IF_RELEASE(mThreadEventQueue); mContentViewer=nsnull; mDeviceContext=nsnull; NS_IF_RELEASE(mContainer); if (mScriptGlobal) { mScriptGlobal->SetDocShell(nsnull); mScriptGlobal = nsnull; } if (mScriptContext) { mScriptContext->SetOwner(nsnull); mScriptContext = nsnull; } InitFrameData(); // Free up history memory PRInt32 i, n = mHistory.Count(); for (i = 0; i < n; i++) { nsString* s = (nsString*) mHistory.ElementAt(i); delete s; } DestroyPluginHost(); #ifdef DETECT_WEBSHELL_LEAKS // We're counting the number of |nsWebShells| to help find leaks --gNumberOfWebShells; #endif #ifdef NOISY_WEBSHELL_LEAKS printf("WEBSHELL- = %ld\n", gNumberOfWebShells); #endif } void nsWebShell::InitFrameData() { SetMarginWidth(-1); SetMarginHeight(-1); } nsresult nsWebShell::FireUnloadForChildren() { nsresult rv = NS_OK; PRInt32 i, n = mChildren.Count(); for (i = 0; i < n; i++) { nsIDocShell* shell = (nsIDocShell*) mChildren.ElementAt(i); nsCOMPtr webShell(do_QueryInterface(shell)); rv = webShell->FireUnloadEvent(); } return rv; } NS_IMETHODIMP nsWebShell::FireUnloadEvent() { nsresult rv = NS_OK; if (mScriptGlobal) { nsIDocumentViewer* docViewer; if (mContentViewer && NS_SUCCEEDED(mContentViewer->QueryInterface(kIDocumentViewerIID, (void**)&docViewer))) { nsIPresContext *presContext; if (NS_SUCCEEDED(docViewer->GetPresContext(presContext))) { nsEventStatus status = nsEventStatus_eIgnore; nsMouseEvent event; event.eventStructType = NS_EVENT; event.message = NS_PAGE_UNLOAD; rv = mScriptGlobal->HandleDOMEvent(presContext, &event, nsnull, NS_EVENT_FLAG_INIT, &status); NS_RELEASE(presContext); } NS_RELEASE(docViewer); } } //Fire child unloads now while our data is intact. rv = FireUnloadForChildren(); mFiredUnloadEvent = PR_TRUE; return rv; } NS_IMPL_ADDREF_INHERITED(nsWebShell, nsDocShell) NS_IMPL_RELEASE_INHERITED(nsWebShell, nsDocShell) NS_INTERFACE_MAP_BEGIN(nsWebShell) #if 0 // inherits from nsDocShell: NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWebShell) #endif NS_INTERFACE_MAP_ENTRY(nsIWebShell) NS_INTERFACE_MAP_ENTRY(nsIWebShellServices) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIContentViewerContainer, nsIWebShell) NS_INTERFACE_MAP_ENTRY(nsIDocumentLoaderObserver) NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink) NS_INTERFACE_MAP_ENTRY(nsIWebShellContainer) NS_INTERFACE_MAP_ENTRY(nsILinkHandler) NS_INTERFACE_MAP_ENTRY(nsIRefreshURI) NS_INTERFACE_MAP_ENTRY(nsIClipboardCommands) NS_INTERFACE_MAP_ENTRY(nsIURIContentListener) #if 0 // inherits from nsDocShell: NS_INTERFACE_MAP_ENTRY(nsIScriptGlobalObjectOwner) NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor) NS_INTERFACE_MAP_ENTRY(nsIBaseWindow) NS_INTERFACE_MAP_ENTRY(nsIDocShell) NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeItem) NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeNode) NS_INTERFACE_MAP_ENTRY(nsIWebNavigation) NS_INTERFACE_MAP_ENTRY(nsIScrollable) #endif NS_INTERFACE_MAP_END_INHERITING(nsDocShell) NS_IMETHODIMP nsWebShell::GetInterface(const nsIID &aIID, void** aInstancePtr) { NS_ENSURE_ARG_POINTER(aInstancePtr); nsresult rv = NS_OK; *aInstancePtr = nsnull; if(aIID.Equals(NS_GET_IID(nsILinkHandler))) { *aInstancePtr = NS_STATIC_CAST(nsILinkHandler*, this); NS_ADDREF((nsISupports*)*aInstancePtr); return NS_OK; } else if(aIID.Equals(NS_GET_IID(nsIScriptGlobalObjectOwner))) { *aInstancePtr = NS_STATIC_CAST(nsIScriptGlobalObjectOwner*, this); NS_ADDREF((nsISupports*)*aInstancePtr); return NS_OK; } else if (aIID.Equals(NS_GET_IID(nsIURIContentListener))) { *aInstancePtr = NS_STATIC_CAST(nsIURIContentListener*, this); NS_ADDREF((nsISupports*)*aInstancePtr); return NS_OK; } else if(aIID.Equals(NS_GET_IID(nsIScriptGlobalObject))) { NS_ENSURE_SUCCESS(EnsureScriptEnvironment(), NS_ERROR_FAILURE); *aInstancePtr = mScriptGlobal; NS_ADDREF((nsISupports*)*aInstancePtr); return NS_OK; } else if(aIID.Equals(NS_GET_IID(nsIDOMWindow))) { NS_ENSURE_SUCCESS(EnsureScriptEnvironment(), NS_ERROR_FAILURE); NS_ENSURE_SUCCESS(mScriptGlobal->QueryInterface(NS_GET_IID(nsIDOMWindow), aInstancePtr), NS_ERROR_FAILURE); return NS_OK; } else if(mPluginManager) //XXX this seems a little wrong. MMP rv = mPluginManager->QueryInterface(aIID, aInstancePtr); if (!*aInstancePtr || NS_FAILED(rv)) return nsDocShell::GetInterface(aIID,aInstancePtr); else return rv; } NS_IMETHODIMP nsWebShell::SetupNewViewer(nsIContentViewer* aViewer) { NS_ENSURE_SUCCESS(nsDocShell::SetupNewViewer(aViewer), NS_ERROR_FAILURE); // If the history state has been set by session history, // set it on the pres shell now that we have a content // viewer. if(mContentViewer && mHistoryState) { nsCOMPtr docv(do_QueryInterface(mContentViewer)); if(docv) { nsCOMPtr shell; docv->GetPresShell(*getter_AddRefs(shell)); if(shell) shell->SetHistoryState((nsILayoutHistoryState*)mHistoryState); } } return NS_OK; } NS_IMETHODIMP nsWebShell::Embed(nsIContentViewer* aContentViewer, const char* aCommand, nsISupports* aExtraInfo) { return SetupNewViewer(aContentViewer); } NS_IMETHODIMP nsWebShell::SetContainer(nsIWebShellContainer* aContainer) { NS_IF_RELEASE(mContainer); mContainer = aContainer; NS_IF_ADDREF(mContainer); // uri dispatching change.....if you set a container for a webshell // and that container is a content listener itself....then use // it as our parent container. nsresult rv = NS_OK; nsCOMPtr contentListener = do_QueryInterface(mContainer, &rv); if (NS_SUCCEEDED(rv) && contentListener) SetParentContentListener(contentListener); // if the container is getting set to null, then our parent must be going away // so clear out our knowledge of the content listener represented by the container if (!aContainer) SetParentContentListener(nsnull); return NS_OK; } NS_IMETHODIMP nsWebShell::GetContainer(nsIWebShellContainer*& aResult) { aResult = mContainer; NS_IF_ADDREF(mContainer); return NS_OK; } NS_IMETHODIMP nsWebShell::GetTopLevelWindow(nsIWebShellContainer** aTopLevelWindow) { NS_ENSURE_ARG_POINTER(aTopLevelWindow); *aTopLevelWindow = nsnull; nsCOMPtr rootWebShell; GetRootWebShellEvenIfChrome(getter_AddRefs(rootWebShell)); if(!rootWebShell) return NS_OK; nsCOMPtr rootContainer; rootWebShell->GetContainer(*aTopLevelWindow); return NS_OK; } NS_IMETHODIMP nsWebShell::SetSessionHistory(nsISessionHistory* aSHist) { NS_IF_RELEASE(mSHist); mSHist = aSHist; NS_IF_ADDREF(aSHist); return NS_OK; } NS_IMETHODIMP nsWebShell::GetSessionHistory(nsISessionHistory *& aResult) { aResult = mSHist; NS_IF_ADDREF(mSHist); return NS_OK; } nsEventStatus PR_CALLBACK nsWebShell::HandleEvent(nsGUIEvent *aEvent) { return nsEventStatus_eIgnore; } NS_IMETHODIMP nsWebShell::GetRootWebShell(nsIWebShell*& aResult) { nsCOMPtr top; GetSameTypeRootTreeItem(getter_AddRefs(top)); nsCOMPtr topAsWebShell(do_QueryInterface(top)); aResult = topAsWebShell; NS_IF_ADDREF(aResult); return NS_OK; } void nsWebShell::GetRootWebShellEvenIfChrome(nsIWebShell** aResult) { nsCOMPtr top; GetRootTreeItem(getter_AddRefs(top)); nsCOMPtr topAsWebShell(do_QueryInterface(top)); *aResult = topAsWebShell; NS_IF_ADDREF(*aResult); } NS_IMETHODIMP nsWebShell::SetParent(nsIWebShell* aParent) { nsCOMPtr parentAsTreeItem(do_QueryInterface(aParent)); mParent = parentAsTreeItem.get(); return NS_OK; } NS_IMETHODIMP nsWebShell::GetParent(nsIWebShell*& aParent) { nsCOMPtr parent; NS_ENSURE_SUCCESS(GetSameTypeParent(getter_AddRefs(parent)), NS_ERROR_FAILURE); if(parent) parent->QueryInterface(NS_GET_IID(nsIWebShell), (void**)&aParent); else aParent = nsnull; return NS_OK; } NS_IMETHODIMP nsWebShell::GetReferrer(nsIURI **aReferrer) { *aReferrer = mReferrerURI; NS_IF_ADDREF(*aReferrer); return NS_OK; } void nsWebShell::SetReferrer(const PRUnichar* aReferrer) { NS_NewURI(getter_AddRefs(mReferrerURI), aReferrer, nsnull); } NS_IMETHODIMP nsWebShell::SetURL(const PRUnichar* aURL) { nsCOMPtr uri; NS_ENSURE_SUCCESS(NS_NewURI(getter_AddRefs(uri), aURL, nsnull), NS_ERROR_FAILURE); SetCurrentURI(uri); return NS_OK; } NS_IMETHODIMP nsWebShell::GetIsInSHist(PRBool& aResult) { aResult = mIsInSHist; return NS_OK; } NS_IMETHODIMP nsWebShell::SetIsInSHist(PRBool aIsInSHist) { mIsInSHist = aIsInSHist; return NS_OK; } /** * Document Load methods */ NS_IMETHODIMP nsWebShell::GetDocumentLoader(nsIDocumentLoader*& aResult) { aResult = mDocLoader; NS_IF_ADDREF(mDocLoader); return (nsnull != mDocLoader) ? NS_OK : NS_ERROR_FAILURE; } #define FILE_PROTOCOL "file://" static void convertFileToURL(const nsString &aIn, nsString &aOut) { aOut = aIn; #ifdef XP_PC // Check for \ in the url-string (PC) if(kNotFound != aIn.FindChar(PRUnichar('\\'))) { #else #if XP_UNIX // Check if it starts with / or \ (UNIX) const PRUnichar * up = aIn.GetUnicode(); if((PRUnichar('/') == *up) || (PRUnichar('\\') == *up)) { #else if (0) { // Do nothing (All others for now) #endif #endif #ifdef XP_PC // Translate '\' to '/' aOut.ReplaceChar(PRUnichar('\\'), PRUnichar('/')); aOut.ReplaceChar(PRUnichar(':'), PRUnichar('|')); #endif // Build the file URL aOut.Insert(FILE_PROTOCOL,0); } } NS_IMETHODIMP nsWebShell::LoadURL(const PRUnichar *aURLSpec, nsIInputStream* aPostDataStream, PRBool aModifyHistory, nsLoadFlags aType, const PRUint32 aLocalIP, nsISupports * aHistoryState, const PRUnichar* aReferrer) { const char *cmd = mViewSource ? "view-source" : "view" ; return LoadURL(aURLSpec, cmd, aPostDataStream, aModifyHistory,aType, aLocalIP, aHistoryState, aReferrer); } static PRBool EqualBaseURLs(nsIURI* url1, nsIURI* url2) { nsXPIDLCString spec1; nsXPIDLCString spec2; char * anchor1 = nsnull, * anchor2=nsnull; PRBool rv = PR_FALSE; if (url1 && url2) { // XXX We need to make these strcmps case insensitive. url1->GetSpec(getter_Copies(spec1)); url2->GetSpec(getter_Copies(spec2)); /* Don't look at the ref-part */ anchor1 = PL_strrchr(spec1, '#'); anchor2 = PL_strrchr(spec2, '#'); if (anchor1) *anchor1 = '\0'; if (anchor2) *anchor2 = '\0'; if (0 == PL_strcmp(spec1,spec2)) { rv = PR_TRUE; } } // url1 && url2 return rv; } nsresult nsWebShell::DoLoadURL(nsIURI * aUri, const char* aCommand, nsIInputStream* aPostDataStream, nsLoadFlags aType, const PRUint32 aLocalIP, const PRUnichar* aReferrer, const char * aWindowTarget, PRBool aKickOffLoad) { if (!aUri) return NS_ERROR_NULL_POINTER; nsXPIDLCString urlSpec; nsresult rv = NS_OK; rv = aUri->GetSpec(getter_Copies(urlSpec)); if (NS_FAILED(rv)) return rv; // If it's a normal reload that uses the cache, look at the destination anchor // and see if it's an element within the current document // We don't have a reload loadtype yet in necko. So, check for just history // loadtype if ((aType == nsISessionHistory::LOAD_HISTORY || aType == nsIChannel::LOAD_NORMAL) && (nsnull != mContentViewer) && (nsnull == aPostDataStream)) { nsCOMPtr docViewer; if (NS_SUCCEEDED(mContentViewer->QueryInterface(kIDocumentViewerIID, getter_AddRefs(docViewer)))) { // Get the document object nsCOMPtr doc; docViewer->GetDocument(*getter_AddRefs(doc)); // Get the URL for the document nsCOMPtr docURL = getter_AddRefs(doc->GetDocumentURL()); if (aUri && docURL && EqualBaseURLs(docURL, aUri)) { // See if there's a destination anchor nsXPIDLCString ref; nsCOMPtr aUrl = do_QueryInterface(aUri); if (aUrl) rv = aUrl->GetRef(getter_Copies(ref)); nsCOMPtr presShell; rv = docViewer->GetPresShell(*getter_AddRefs(presShell)); if (NS_SUCCEEDED(rv) && presShell) { /* Pass OnStartDocument notifications to the docloaderobserver * so that urlbar, forward/back buttons will * behave properly when going to named anchors */ nsIInterfaceRequestor * interfaceRequestor = NS_STATIC_CAST(nsIInterfaceRequestor *, this); nsCOMPtr dummyChannel; // creating a channel is expensive...don't create it unless we know we have to // so move the creation down into each of the if clauses... if (nsnull != (const char *) ref) { rv = NS_OpenURI(getter_AddRefs(dummyChannel), aUri, nsnull, interfaceRequestor); if (NS_FAILED(rv)) return rv; if (!mProcessedEndDocumentLoad) rv = OnStartDocumentLoad(mDocLoader, aUri, "load"); // Go to the anchor in the current document rv = presShell->GoToAnchor(nsAutoString(ref)); // Set the URL & referrer if the anchor was successfully visited if (NS_SUCCEEDED(rv)) { SetCurrentURI(aUri); SetReferrer(aReferrer); } // Pass on status of scrolling/anchor visit to docloaderobserver if (!mProcessedEndDocumentLoad) { rv = OnEndDocumentLoad(mDocLoader, dummyChannel, rv); } return rv; } else if (aType == nsISessionHistory::LOAD_HISTORY) { rv = NS_OpenURI(getter_AddRefs(dummyChannel), aUri, nsnull, interfaceRequestor); if (NS_FAILED(rv)) return rv; rv = OnStartDocumentLoad(mDocLoader, aUri, "load"); // Go to the top of the current document nsCOMPtr viewMgr; rv = presShell->GetViewManager(getter_AddRefs(viewMgr)); if (NS_SUCCEEDED(rv) && viewMgr) { nsIScrollableView* view; rv = viewMgr->GetRootScrollableView(&view); if (NS_SUCCEEDED(rv) && view) rv = view->ScrollTo(0, 0, NS_VMREFRESH_IMMEDIATE); if(NS_SUCCEEDED(rv)) { SetCurrentURI(aUri); SetReferrer(aReferrer); } mProcessedEndDocumentLoad = PR_FALSE; // Pass on status of scrolling/anchor visit to docloaderobserver rv = OnEndDocumentLoad(mDocLoader, dummyChannel, rv); return rv; } } #if 0 mProcessedEndDocumentLoad = PR_FALSE; // Pass on status of scrolling/anchor visit to docloaderobserver rv = OnEndDocumentLoad(mDocLoader, dummyChannel, rv); return rv; #endif /* 0 */ } // NS_SUCCEEDED(rv) && presShell } // EqualBaseURLs(docURL, url) } } // Stop loading the current document (if any...). This call may result in // firing an EndLoadURL notification for the old document... if (aKickOffLoad) StopLoad(); // Tell web-shell-container we are loading a new url if (nsnull != mContainer) { nsAutoString uniSpec (urlSpec); rv = mContainer->BeginLoadURL(this, uniSpec.GetUnicode()); if (NS_FAILED(rv)) { return rv; } } mProcessedEndDocumentLoad = PR_FALSE; /* WebShell was primarily passing the buck when it came to streamObserver. * So, pass on the observer which is already a streamObserver to DocLoder. */ if (aKickOffLoad) { #ifdef MOZ_PERF_METRICS { char* url; nsresult rv = NS_OK; rv = aUri->GetSpec(&url); if (NS_SUCCEEDED(rv)) { MOZ_TIMER_LOG(("*** Timing layout processes on url: '%s', webshell: %p\n", url, this)); delete [] url; } } MOZ_TIMER_DEBUGLOG(("Reset and start: nsWebShell::DoLoadURL(), this=%p\n", this)); MOZ_TIMER_RESET(mTotalTime); MOZ_TIMER_START(mTotalTime); #endif nsCOMPtr pURILoader = do_GetService(NS_URI_LOADER_PROGID, &rv); if (NS_FAILED(rv)) return rv; nsXPIDLCString aUrlScheme; aUri->GetScheme(getter_Copies(aUrlScheme)); nsCOMPtr loadGroup; pURILoader->GetLoadGroupForContext(NS_STATIC_CAST(nsISupports *, (nsIWebShell *) this), getter_AddRefs(loadGroup)); // first, create a channel for the protocol.... nsCOMPtr pNetService = do_GetService(kIOServiceCID, &rv); if (NS_SUCCEEDED(rv)) { nsCOMPtr pChannel; nsCOMPtr requestor (do_QueryInterface(NS_STATIC_CAST(nsIContentViewerContainer*, (nsIWebShell*)this))); // Create a referrer URI nsCOMPtr referrer; if (aReferrer) { nsAutoString tempReferrer(aReferrer); char* referrerStr = tempReferrer.ToNewCString(); pNetService->NewURI(referrerStr, nsnull, getter_AddRefs(referrer)); Recycle(referrerStr); } rv = pNetService->NewChannelFromURI(aCommand, aUri, loadGroup, requestor, aType, referrer /* referring uri */, 0, 0, getter_AddRefs(pChannel)); if (NS_FAILED(rv)) { if (rv == NS_ERROR_DOM_RETVAL_UNDEFINED) // if causing the channel changed the return NS_OK; // dom and there is nothing else to do else return rv; // uhoh we were unable to get a channel to handle the url!!! } // Mark the channel as being a document URI... nsLoadFlags loadAttribs = 0; pChannel->GetLoadAttributes(&loadAttribs); loadAttribs |= nsIChannel::LOAD_DOCUMENT_URI; pChannel->SetLoadAttributes(loadAttribs); nsCOMPtr httpChannel(do_QueryInterface(pChannel)); if (httpChannel) { // figure out if we need to set the post data stream on the channel... // right now, this is only done for http channels..... if (aPostDataStream) { httpChannel->SetRequestMethod(HM_POST); httpChannel->SetPostDataStream(aPostDataStream); } // Set the referrer explicitly if (referrer) { // Referrer is currenly only set for link clicks here. httpChannel->SetReferrer(referrer, nsIHTTPChannel::REFERRER_LINK_CLICK); } } // now let's pass the channel into the uri loader nsURILoadCommand loadCmd = nsIURILoader::viewNormal; if (nsCRT::strcasecmp(aCommand, "view-link-click") == 0) loadCmd = nsIURILoader::viewUserClick; else if (nsCRT::strcasecmp(aCommand, "view-source") == 0) loadCmd = nsIURILoader::viewSource; rv = pURILoader->OpenURI(pChannel, loadCmd, aWindowTarget /* window target */, NS_STATIC_CAST(nsIContentViewerContainer*, (nsIWebShell*)this)); } } // Fix for bug 1646. Change the notion of current url and referrer only after // the document load succeeds (but only if we're not targeting another window). if (NS_SUCCEEDED(rv) && !aWindowTarget) { SetCurrentURI(aUri); SetReferrer(aReferrer); } return rv; } NS_IMETHODIMP nsWebShell::GetCanGoBack(PRBool* aCanGoBack) { #ifdef DOCSHELL_LOAD return nsDocShell::GetCanGoBack(aCanGoBack); #else /*!DOCSHELL_LOAD*/ *aCanGoBack = (mHistoryIndex - 1) > - 1 ? PR_TRUE : PR_FALSE; return NS_OK; #endif /*!DOCSHELL_LOAD*/ } NS_IMETHODIMP nsWebShell::GetCanGoForward(PRBool* aCanGoForward) { #ifdef DOCSHELL_LOAD return nsDocShell::GetCanGoForward(aCanGoForward); #else /*!DOCSHELL_LOAD*/ *aCanGoForward = mHistoryIndex < mHistory.Count() - 1 ? PR_TRUE : PR_FALSE; return NS_OK; #endif /*!DOCSHELL_LOAD*/ } NS_IMETHODIMP nsWebShell::GoBack() { #ifdef DOCSHELL_LOAD return nsDocShell::GoBack(); #else /*!DOCSHELL_LOAD*/ NS_ENSURE_SUCCESS(GoTo(mHistoryIndex - 1), NS_ERROR_FAILURE); return NS_OK; #endif /*!DOCSHELL_LOAD*/ } NS_IMETHODIMP nsWebShell::GoForward() { #ifdef DOCSHELL_LOAD return nsDocShell::GoForward(); #else /*!DOCSHELL_LOAD*/ NS_ENSURE_SUCCESS(GoTo(mHistoryIndex + 1), NS_ERROR_FAILURE); return NS_OK; #endif /*!DOCSHELL_LOAD*/ } NS_IMETHODIMP nsWebShell::LoadURI(const PRUnichar* aURI) { #ifdef DOCSHELL_LOAD return nsDocShell::LoadURI(aURI); #else /*!DOCSHELL_LOAD*/ return LoadURL(aURI); #endif /*!DOCSHELL_LOAD*/ } NS_IMETHODIMP nsWebShell::InternalLoad(nsIURI* aURI, nsIURI* aReferrer, nsIInputStream* aPostData, loadType aLoadType) { nsXPIDLCString url; aURI->GetSpec(getter_Copies(url)); nsXPIDLCString referrer; if(aReferrer) aReferrer->GetSpec(getter_Copies(referrer)); return LoadURL(nsAutoString(url).GetUnicode(), nsnull, PR_FALSE, 0, 0, nsnull, nsAutoString(referrer).GetUnicode()); } // nsIURIContentListener support NS_IMETHODIMP nsWebShell::GetProtocolHandler(nsIURI *aURI, nsIProtocolHandler **aProtocolHandler) { // Farm this off to our content listener NS_ENSURE_SUCCESS(EnsureContentListener(), NS_ERROR_FAILURE); return mContentListener->GetProtocolHandler(aURI, aProtocolHandler); } NS_IMETHODIMP nsWebShell::IsPreferred(const char * aContentType, nsURILoadCommand aCommand, const char * aWindowTarget, char ** aDesiredContentType, PRBool * aCanHandleContent) { NS_ENSURE_SUCCESS(EnsureContentListener(), NS_ERROR_FAILURE); return mContentListener->IsPreferred(aContentType, aCommand, aWindowTarget, aDesiredContentType, aCanHandleContent); } NS_IMETHODIMP nsWebShell::CanHandleContent(const char * aContentType, nsURILoadCommand aCommand, const char * aWindowTarget, char ** aDesiredContentType, PRBool * aCanHandleContent) { NS_ENSURE_SUCCESS(EnsureContentListener(), NS_ERROR_FAILURE); return mContentListener->CanHandleContent(aContentType, aCommand, aWindowTarget, aDesiredContentType, aCanHandleContent); } NS_IMETHODIMP nsWebShell::DoContent(const char * aContentType, nsURILoadCommand aCommand, const char * aWindowTarget, nsIChannel * aOpenedChannel, nsIStreamListener ** aContentHandler, PRBool * aAbortProcess) { NS_ENSURE_ARG(aOpenedChannel); nsresult rv = NS_OK; if (aAbortProcess) *aAbortProcess = PR_FALSE; nsXPIDLCString strCommand; // go to the uri loader and ask it to convert the uri load command into a old // world style string NS_WITH_SERVICE(nsIURILoader, pURILoader, NS_URI_LOADER_PROGID, &rv); if (NS_SUCCEEDED(rv)) pURILoader->GetStringForCommand(aCommand, getter_Copies(strCommand)); // determine if the channel has just been retargeted to us... nsLoadFlags loadAttribs = 0; aOpenedChannel->GetLoadAttributes(&loadAttribs); // first, run any uri preparation stuff that we would have run normally // had we gone through OpenURI nsCOMPtr aUri; aOpenedChannel->GetURI(getter_AddRefs(aUri)); if (loadAttribs & nsIChannel::LOAD_RETARGETED_DOCUMENT_URI) { PrepareToLoadURI(aUri, nsnull, PR_TRUE, nsIChannel::LOAD_NORMAL, 0, nsnull, nsnull); // mscott: when I called DoLoadURL I found that we ran into problems because // we currently don't have channel retargeting yet. Basically, what happens is that // DoLoadURL calls StopBeforeRequestingURL and this cancels the current load group // however since we can't retarget yet, we were basically canceling our very // own load group!!! So the request would get canceled out from under us... // after retargeting we may be able to safely call DoLoadURL. DoLoadURL(aUri, strCommand, nsnull, nsIChannel::LOAD_NORMAL, 0, nsnull, nsnull, PR_FALSE); SetFocus(); // force focus to get set on the retargeted window... } OnLoadingSite(aUri); return CreateContentViewer(aContentType, aCommand, aOpenedChannel, aContentHandler); } nsresult nsWebShell::PrepareToLoadURI(nsIURI * aUri, nsIInputStream * aPostStream, PRBool aModifyHistory, nsLoadFlags aType, const PRUint32 aLocalIP, nsISupports * aHistoryState, const PRUnichar * aReferrer) { nsresult rv; CancelRefreshURITimers(); nsXPIDLCString scheme, CUriSpec; if (!aUri) return NS_ERROR_NULL_POINTER; rv = aUri->GetScheme(getter_Copies(scheme)); if (NS_FAILED(rv)) return rv; rv = aUri->GetSpec(getter_Copies(CUriSpec)); if (NS_FAILED(rv)) return rv; nsAutoString uriSpec(CUriSpec); nsXPIDLCString spec; rv = aUri->GetSpec(getter_Copies(spec)); if (NS_FAILED(rv)) return rv; nsString* url = new nsString(uriSpec); if (aModifyHistory) { // Discard part of history that is no longer reachable PRInt32 i, n = mHistory.Count(); i = mHistoryIndex + 1; while (--n >= i) { nsString* u = (nsString*) mHistory.ElementAt(n); delete u; mHistory.RemoveElementAt(n); } // Tack on new url mHistory.AppendElement(url); mHistoryIndex++; } else { // Replace the current history index with this URL nsString* u = (nsString*) mHistory.ElementAt(mHistoryIndex); if (nsnull != u) { delete u; } mHistory.ReplaceElementAt(url, mHistoryIndex); } ShowHistory(); // Give web-shell-container right of refusal if (nsnull != mContainer) { nsAutoString str(spec); rv = mContainer->WillLoadURL(this, str.GetUnicode(), nsLoadURL); if (NS_FAILED(rv)) { return rv; } } return rv; } NS_IMETHODIMP nsWebShell::LoadURI(nsIURI * aUri, const char * aCommand, nsIInputStream* aPostDataStream, PRBool aModifyHistory, nsLoadFlags aType, const PRUint32 aLocalIP, nsISupports * aHistoryState, const PRUnichar* aReferrer, const char * aWindowTarget) { nsresult rv = PrepareToLoadURI(aUri, aPostDataStream, aModifyHistory, aType, aLocalIP, aHistoryState, aReferrer); if (NS_SUCCEEDED(rv)) rv = DoLoadURL(aUri, aCommand, aPostDataStream, aType, aLocalIP, aReferrer, aWindowTarget); return rv; } static nsresult convertURLToFileCharset(nsString& aIn, nsCString& aOut) { nsresult rv=NS_OK; aOut = ""; // for file url, we need to convert the nsString to the file system // charset before we pass to NS_NewURI static nsAutoString fsCharset(""); // find out the file system charset first if(0 == fsCharset.Length()) { fsCharset = "ISO-8859-1"; // set the fallback first. NS_WITH_SERVICE(nsIPlatformCharset, plat, kPlatformCharsetCID, &rv); NS_ASSERTION(NS_SUCCEEDED(rv), "cannot get nsIPlatformCharset"); if(NS_SUCCEEDED(rv)) { rv = plat->GetCharset(kPlatformCharsetSel_FileName, fsCharset); NS_ASSERTION(NS_SUCCEEDED(rv), "nsIPlatformCharset GetCharset failed"); } } // We probably should cache ccm here. // get a charset converter from the manager NS_WITH_SERVICE(nsICharsetConverterManager, ccm, kCharsetConverterManagerCID, &rv); NS_ASSERTION(NS_SUCCEEDED(rv), "cannot get CharsetConverterManager"); nsCOMPtr fsEncoder; if(NS_SUCCEEDED(rv)){ rv = ccm->GetUnicodeEncoder(&fsCharset, getter_AddRefs(fsEncoder)); NS_ASSERTION(NS_SUCCEEDED(rv), "cannot get encoder"); if(NS_SUCCEEDED(rv)){ PRInt32 bufLen = 0; rv = fsEncoder->GetMaxLength(aIn.GetUnicode(), aIn.Length(), &bufLen); NS_ASSERTION(NS_SUCCEEDED(rv), "GetMaxLength failed"); aOut.SetCapacity(bufLen+1); PRInt32 srclen = aIn.Length(); rv = fsEncoder->Convert(aIn.GetUnicode(), &srclen, (char*)aOut.GetBuffer(), &bufLen); if(NS_SUCCEEDED(rv)) { ((char*)aOut.GetBuffer())[bufLen]='\0'; aOut.SetLength(bufLen); } } } return rv; } NS_IMETHODIMP nsWebShell::LoadURL(const PRUnichar *aURLSpec, const char* aCommand, nsIInputStream* aPostDataStream, PRBool aModifyHistory, nsLoadFlags aType, const PRUint32 aLocalIP, nsISupports * aHistoryState, const PRUnichar* aReferrer, const char * aWindowTarget) { nsresult rv = NS_OK; PRBool isMail= PR_FALSE; // XXX mailto: hack PRBool keywordsEnabled = PR_FALSE; /* TODO This doesnt belong here... The app should be doing all this URL play. The webshell should not have a clue about whats "mailto" If you insist that this should be here, then put in URL parsing optimizations here. -Gagan */ mViewSource = (0==PL_strcmp("view-source", aCommand)); nsAutoString urlStr(aURLSpec); // first things first. try to create a uri out of the string. nsCOMPtr uri; nsXPIDLCString spec; if(0==urlStr.Find("file:",0)) { // if this is file url, we need to convert the URI // from Unicode to the FS charset nsCAutoString inFSCharset; rv = convertURLToFileCharset(urlStr, inFSCharset); NS_ASSERTION(NS_SUCCEEDED(rv), "convertURLToFielCharset failed"); if (NS_SUCCEEDED(rv)) rv = NS_NewURI(getter_AddRefs(uri), inFSCharset.GetBuffer(), nsnull); } else { rv = NS_NewURI(getter_AddRefs(uri), urlStr, nsnull); } if (NS_FAILED(rv)) { // no dice. nsAutoString urlSpec; urlStr.Trim(" "); // see if we've got a file url. convertFileToURL(urlStr, urlSpec); nsCAutoString inFSCharset; rv = convertURLToFileCharset(urlSpec, inFSCharset); NS_ASSERTION(NS_SUCCEEDED(rv), "convertURLToFielCharset failed"); if (NS_SUCCEEDED(rv)) rv = NS_NewURI(getter_AddRefs(uri), inFSCharset.GetBuffer(), nsnull); if (NS_FAILED(rv)) { NS_ASSERTION(mPrefs, "the webshell's pref service wasn't initialized"); rv = mPrefs->GetBoolPref("keyword.enabled", &keywordsEnabled); if (NS_FAILED(rv)) return rv; PRInt32 qMarkLoc = -1, spaceLoc = -1; rv = NS_ERROR_FAILURE; if (keywordsEnabled) { // These are keyword formatted strings // "what is mozilla" // "what is mozilla?" // "?mozilla" // "?What is mozilla" // These are not keyword formatted strings // "www.blah.com" - anything with a dot in it // "nonQualifiedHost:80" - anything with a colon in it // "nonQualifiedHost?" // "nonQualifiedHost?args" // "nonQualifiedHost?some args" if (urlStr.FindChar('.') == -1 && urlStr.FindChar(':') == -1) { qMarkLoc = urlStr.FindChar('?'); spaceLoc = urlStr.FindChar(' '); PRBool keyword = PR_FALSE; if (qMarkLoc == 0) { keyword = PR_TRUE; } else if ( (spaceLoc > 0) && ( (qMarkLoc == -1) || (spaceLoc < qMarkLoc) )) { keyword = PR_TRUE; } if (keyword) { nsCAutoString keywordSpec("keyword:"); char *utf8Spec = urlStr.ToNewUTF8String(); if(utf8Spec) { char* escapedUTF8Spec = nsEscape(utf8Spec, url_Path); if(escapedUTF8Spec) { keywordSpec.Append(escapedUTF8Spec); rv = NS_NewURI(getter_AddRefs(uri), keywordSpec.GetBuffer(), nsnull); nsAllocator::Free(escapedUTF8Spec); } // escapedUTF8Spec nsAllocator::Free(utf8Spec); } // utf8Spec } // keyword } // FindChar } // keywordEnable PRInt32 colon = -1; if (NS_FAILED(rv)) { PRInt32 fSlash = urlSpec.FindChar('/'); PRUnichar port; // if no scheme (protocol) is found, assume http. if ( ((colon=urlSpec.FindChar(':')) == -1) // no colon at all || ( (fSlash > -1) && (colon > fSlash) ) // the only colon comes after the first slash || ( (colon < urlSpec.Length()-1) // the first char after the first colon is a digit (i.e. a port) && ((port=urlSpec.CharAt(colon+1)) <= '9') && (port > '0') )) { // find host name PRInt32 hostPos = urlSpec.FindCharInSet("./:"); if (hostPos == -1) { hostPos = urlSpec.Length(); } // extract host name nsAutoString hostSpec; urlSpec.Left(hostSpec, hostPos); // insert url spec corresponding to host name if (hostSpec.EqualsIgnoreCase("ftp")) { urlSpec.Insert("ftp://", 0, 6); } else { urlSpec.Insert("http://", 0, 7); } } // end if colon // XXX mailto: hack if ((urlSpec.Find("mailto:", PR_TRUE)) >= 0) isMail = PR_TRUE; if (!isMail) { rv = NS_NewURI(getter_AddRefs(uri), urlSpec, nsnull); if (NS_ERROR_UNKNOWN_PROTOCOL == rv) { // we weren't able to find a protocol handler rv = InitDialogVars(); if (NS_FAILED(rv)) return rv; nsXPIDLString messageStr; nsAutoString name("protocolNotFound"); rv = mStringBundle->GetStringFromName(name.GetUnicode(), getter_Copies(messageStr)); if (NS_FAILED(rv)) return rv; NS_ASSERTION(colon != -1, "we shouldn't have gotten this far if we didn't have a colon"); // extract the scheme nsAutoString scheme; urlSpec.Left(scheme, colon); nsAutoString dnsMsg(scheme); dnsMsg.Append(' '); dnsMsg.Append(messageStr); (void)mPrompter->Alert(dnsMsg.GetUnicode()); } // end unknown protocol } // end !isMail } } } if (!uri) return rv; rv = uri->GetSpec(getter_Copies(spec)); if (NS_FAILED(rv)) return rv; // Get hold of Root webshell nsCOMPtr root; nsCOMPtr shist; PRBool isLoadingHistory=PR_FALSE; // Is SH currently loading an entry from history? rv = GetRootWebShell(*getter_AddRefs(root)); // Get hold of session History if (NS_SUCCEEDED(rv) && root) { root->GetSessionHistory(*getter_AddRefs(shist)); } if (shist) shist->GetLoadingFlag(&isLoadingHistory); /* * Save the history state for the current index iff this loadurl() request * is not from SH. When the request comes from SH, aModifyHistory will * be false and nsSessionHistory.cpp takes of this. */ if (shist) { PRInt32 indix; shist->GetCurrentIndex(&indix); if (indix >= 0 && (aModifyHistory)) { nsCOMPtr historyState; rv = GetHistoryState(getter_AddRefs(historyState)); if (NS_SUCCEEDED(rv) && historyState) shist->SetHistoryObjectForIndex(indix, historyState); } } /* Set the History state object for the current page in the * presentation shell. If it is a new page being visited, * aHistoryState is null. If the load is coming from * session History, it will be set to the cached history object by * session History. */ SetHistoryState(aHistoryState); /* If the current Uri is the same as the one to be loaded * don't add it to session history. This avoids multiple * entries for the same url in the go menu */ nsCOMPtr currentURI; nsresult res = GetCurrentURI(getter_AddRefs(currentURI)); if (NS_SUCCEEDED(res) && currentURI) { nsXPIDLCString currentUriSpec; rv = currentURI->GetSpec(getter_Copies(currentUriSpec)); if (NS_FAILED(rv)) return rv; nsAutoString currentURIString(currentUriSpec); if (currentURIString == spec) { /* The url to be loaded is the same as the * url already in the page. Don't add it to session history */ aModifyHistory = PR_FALSE; } } /* Add the page to session history */ if (aModifyHistory && shist && (!isMail)) { PRInt32 ret; nsCAutoString referrer(aReferrer); ret = shist->Add(spec, referrer, this); } nsCOMPtr parent; res = GetParent(*getter_AddRefs(parent)); nsCOMPtr newURI; if ((isLoadingHistory)) { /* if LoadURL() got called from SH, AND If we are going "Back/Forward" * to a frame page,SH will change the current uri to the right value * for smoother redraw. */ res = GetCurrentURI(getter_AddRefs(newURI)); } else{ /* If the call is not from SH, use the url passed by the caller * so that things like JS will work right. This is for bug # 1646. * May regress in other situations. * What a hack */ nsAutoString urlstr = (const char *) spec; res = NS_NewURI(getter_AddRefs(newURI), urlstr, nsnull); } if (NS_SUCCEEDED(res)) { // now that we have a uri, call the REAL LoadURI method which requires a nsIURI. return LoadURI(newURI, aCommand, aPostDataStream, aModifyHistory, aType, aLocalIP, aHistoryState, aReferrer, aWindowTarget); } return rv; } NS_IMETHODIMP nsWebShell::Stop(void) { // Cancel any timers that were set for this loader. CancelRefreshURITimers(); return nsDocShell::Stop(); } //---------------------------------------- // History methods NS_IMETHODIMP nsWebShell::GoTo(PRInt32 aHistoryIndex) { nsresult rv = NS_ERROR_ILLEGAL_VALUE; if ((aHistoryIndex >= 0) && (aHistoryIndex < mHistory.Count())) { nsString* s = (nsString*) mHistory.ElementAt(aHistoryIndex); // Give web-shell-container right of refusal nsAutoString urlSpec(*s); if (nsnull != mContainer) { rv = mContainer->WillLoadURL(this, urlSpec.GetUnicode(), nsLoadHistory); if (NS_FAILED(rv)) { return rv; } } #ifdef DEBUG printf("Goto %d\n", aHistoryIndex); #endif mHistoryIndex = aHistoryIndex; ShowHistory(); // convert the uri spec into a url and then pass it to DoLoadURL nsCOMPtr uri; rv = NS_NewURI(getter_AddRefs(uri), urlSpec, nsnull); if (NS_FAILED(rv)) return rv; rv = DoLoadURL(uri, // URL string "view", // Command nsnull, // Post Data nsISessionHistory::LOAD_HISTORY, // the reload type 0, // load attributes nsnull,nsnull); // referrer } return rv; } NS_IMETHODIMP nsWebShell::GetHistoryLength(PRInt32& aResult) { aResult = mHistory.Count(); return NS_OK; } NS_IMETHODIMP nsWebShell::GetHistoryIndex(PRInt32& aResult) { aResult = mHistoryIndex; return NS_OK; } NS_IMETHODIMP nsWebShell::GetURL(PRInt32 aHistoryIndex, const PRUnichar** aURLResult) { nsresult rv = NS_ERROR_ILLEGAL_VALUE; // XXX Ownership rules for the string passed back from this // method are not XPCOM compliant. If they were correct, // the caller would deallocate the string. if ((aHistoryIndex >= 0) && (aHistoryIndex <= mHistory.Count() - 1)) { nsString* s = (nsString*) mHistory.ElementAt(aHistoryIndex); if (nsnull != s) { *aURLResult = s->GetUnicode(); } rv = NS_OK; } return rv; } void nsWebShell::ShowHistory() { #if defined(NS_DEBUG) if (WEB_LOG_TEST(gLogModule, WEB_TRACE_HISTORY)) { PRInt32 i, n = mHistory.Count(); for (i = 0; i < n; i++) { if (i == mHistoryIndex) { printf("**"); } else { printf(" "); } nsString* u = (nsString*) mHistory.ElementAt(i); fputs(*u, stdout); printf("\n"); } } #endif } //---------------------------------------- //---------------------------------------------------------------------- // WebShell container implementation NS_IMETHODIMP nsWebShell::WillLoadURL(nsIWebShell* aShell, const PRUnichar* aURL, nsLoadType aReason) { nsresult rv = NS_OK; if (nsnull != mContainer) { rv = mContainer->WillLoadURL(aShell, aURL, aReason); } return rv; } NS_IMETHODIMP nsWebShell::BeginLoadURL(nsIWebShell* aShell, const PRUnichar* aURL) { if (nsnull != mContainer) { // XXX: do not propagate this notification up from any frames... return mContainer->BeginLoadURL(aShell, aURL); } return NS_OK; } NS_IMETHODIMP nsWebShell::ProgressLoadURL(nsIWebShell* aShell, const PRUnichar* aURL, PRInt32 aProgress, PRInt32 aProgressMax) { nsresult rv = NS_OK; if (nsnull != mContainer) { rv = mContainer->ProgressLoadURL(aShell, aURL, aProgress, aProgressMax); } return rv; } NS_IMETHODIMP nsWebShell::EndLoadURL(nsIWebShell* aShell, const PRUnichar* aURL, nsresult aStatus) { nsresult rv = NS_OK; if (nsnull != mContainer) { // XXX: do not propagate this notification up from any frames... return mContainer->EndLoadURL(aShell, aURL, aStatus); } return rv; } NS_IMETHODIMP nsWebShell::GetHistoryState(nsISupports** aLayoutHistoryState) { nsresult rv = NS_OK; // XXX Need to think about what to do for framesets. // For now, return an error if this webshell // contains a frame or a frameset document. // The main content area will always have a parent. It is // enough to check the children count to verify frames. if (mChildren.Count() > 0) { return NS_ERROR_NOT_IMPLEMENTED; } // Ask the pres shell for the history state if (mContentViewer) { nsCOMPtr docv = do_QueryInterface(mContentViewer); if (nsnull != docv) { nsCOMPtr shell; rv = docv->GetPresShell(*getter_AddRefs(shell)); if (NS_SUCCEEDED(rv)) { rv = shell->GetHistoryState((nsILayoutHistoryState**) aLayoutHistoryState); } } } return rv; } NS_IMETHODIMP nsWebShell::SetHistoryState(nsISupports* aLayoutHistoryState) { mHistoryState = aLayoutHistoryState; return NS_OK; } //---------------------------------------------------------------------- // Web Shell Services API NS_IMETHODIMP nsWebShell::LoadDocument(const char* aURL, const char* aCharset, nsCharsetSource aSource) { // XXX hack. kee the aCharset and aSource wait to pick it up nsCOMPtr cv; NS_ENSURE_SUCCESS(GetContentViewer(getter_AddRefs(cv)), NS_ERROR_FAILURE); if (cv) { nsCOMPtr muDV = do_QueryInterface(cv); if (muDV) { nsCharsetSource hint; muDV->GetHintCharacterSetSource((PRInt32 *)(&hint)); if( aSource > hint ) { nsAutoString inputCharSet(aCharset); muDV->SetHintCharacterSet(inputCharSet.GetUnicode()); muDV->SetHintCharacterSetSource((PRInt32)aSource); if(eCharsetReloadRequested != mCharsetReloadState) { mCharsetReloadState = eCharsetReloadRequested; nsAutoString url(aURL); LoadURL(url.GetUnicode()); } } } } return NS_OK; } NS_IMETHODIMP nsWebShell::ReloadDocument(const char* aCharset, nsCharsetSource aSource, const char* aCmd) { // XXX hack. kee the aCharset and aSource wait to pick it up nsCOMPtr cv; NS_ENSURE_SUCCESS(GetContentViewer(getter_AddRefs(cv)), NS_ERROR_FAILURE); if (cv) { nsCOMPtr muDV = do_QueryInterface(cv); if (muDV) { nsCharsetSource hint; muDV->GetHintCharacterSetSource((PRInt32 *)(&hint)); if( aSource > hint ) { nsAutoString inputCharSet(aCharset); muDV->SetHintCharacterSet(inputCharSet.GetUnicode()); muDV->SetHintCharacterSetSource((PRInt32)aSource); if(eCharsetReloadRequested != mCharsetReloadState) { mCharsetReloadState = eCharsetReloadRequested; return Reload(nsIChannel::LOAD_NORMAL); } } } } return NS_OK; } NS_IMETHODIMP nsWebShell::StopDocumentLoad(void) { if(eCharsetReloadRequested != mCharsetReloadState) { Stop(); } return NS_OK; } NS_IMETHODIMP nsWebShell::SetRendering(PRBool aRender) { if(eCharsetReloadRequested != mCharsetReloadState) { if (mContentViewer) { mContentViewer->SetEnableRendering(aRender); } } return NS_OK; } //---------------------------------------------------------------------- // WebShell link handling struct OnLinkClickEvent : public PLEvent { OnLinkClickEvent(nsWebShell* aHandler, nsIContent* aContent, nsLinkVerb aVerb, const PRUnichar* aURLSpec, const PRUnichar* aTargetSpec, nsIInputStream* aPostDataStream = 0); ~OnLinkClickEvent(); void HandleEvent() { mHandler->HandleLinkClickEvent(mContent, mVerb, mURLSpec->GetUnicode(), mTargetSpec->GetUnicode(), mPostDataStream); } nsWebShell* mHandler; nsString* mURLSpec; nsString* mTargetSpec; nsIInputStream* mPostDataStream; nsIContent* mContent; nsLinkVerb mVerb; }; static void PR_CALLBACK HandlePLEvent(OnLinkClickEvent* aEvent) { aEvent->HandleEvent(); } static void PR_CALLBACK DestroyPLEvent(OnLinkClickEvent* aEvent) { delete aEvent; } OnLinkClickEvent::OnLinkClickEvent(nsWebShell* aHandler, nsIContent *aContent, nsLinkVerb aVerb, const PRUnichar* aURLSpec, const PRUnichar* aTargetSpec, nsIInputStream* aPostDataStream) { nsIEventQueue* eventQueue; mHandler = aHandler; NS_ADDREF(aHandler); mURLSpec = new nsString(aURLSpec); mTargetSpec = new nsString(aTargetSpec); mPostDataStream = aPostDataStream; NS_IF_ADDREF(mPostDataStream); mContent = aContent; NS_IF_ADDREF(mContent); mVerb = aVerb; PL_InitEvent(this, nsnull, (PLHandleEventProc) ::HandlePLEvent, (PLDestroyEventProc) ::DestroyPLEvent); eventQueue = aHandler->GetEventQueue(); eventQueue->PostEvent(this); NS_RELEASE(eventQueue); } OnLinkClickEvent::~OnLinkClickEvent() { NS_IF_RELEASE(mContent); NS_IF_RELEASE(mHandler); NS_IF_RELEASE(mPostDataStream); if (nsnull != mURLSpec) delete mURLSpec; if (nsnull != mTargetSpec) delete mTargetSpec; } //---------------------------------------- NS_IMETHODIMP nsWebShell::OnLinkClick(nsIContent* aContent, nsLinkVerb aVerb, const PRUnichar* aURLSpec, const PRUnichar* aTargetSpec, nsIInputStream* aPostDataStream) { OnLinkClickEvent* ev; nsresult rv = NS_OK; ev = new OnLinkClickEvent(this, aContent, aVerb, aURLSpec, aTargetSpec, aPostDataStream); if (nsnull == ev) { rv = NS_ERROR_OUT_OF_MEMORY; } return rv; } nsIEventQueue* nsWebShell::GetEventQueue(void) { NS_PRECONDITION(nsnull != mThreadEventQueue, "EventQueue for thread is null"); NS_ADDREF(mThreadEventQueue); return mThreadEventQueue; } void nsWebShell::HandleLinkClickEvent(nsIContent *aContent, nsLinkVerb aVerb, const PRUnichar* aURLSpec, const PRUnichar* aTargetSpec, nsIInputStream* aPostDataStream) { nsAutoString target(aTargetSpec); switch(aVerb) { case eLinkVerb_New: target.Assign("_blank"); // Fall into replace case case eLinkVerb_Replace: { // for now, just hack the verb to be view-link-clicked // and down in the load document code we'll detect this and // set the correct uri loader command nsXPIDLCString spec; mCurrentURI->GetSpec(getter_Copies(spec)); nsAutoString specString(spec); LoadURL(aURLSpec, "view-link-click", aPostDataStream, PR_TRUE, nsIChannel::LOAD_NORMAL, 0, nsnull, specString.GetUnicode(), nsCAutoString(aTargetSpec)); } break; case eLinkVerb_Embed: default: ; // XXX Need to do this } } NS_IMETHODIMP nsWebShell::OnOverLink(nsIContent* aContent, const PRUnichar* aURLSpec, const PRUnichar* aTargetSpec) { if (!mOverURL.Equals(aURLSpec) || !mOverTarget.Equals(aTargetSpec)) { #ifdef NOISY_LINKS fputs("Was '", stdout); fputs(mOverURL, stdout); fputs("' '", stdout); fputs(mOverTarget, stdout); fputs("'\n", stdout); fputs("Over link '", stdout); fputs(aURLSpec, stdout); fputs("' '", stdout); fputs(aTargetSpec, stdout); fputs("'\n", stdout); #endif /* NS_DEBUG */ mOverURL = aURLSpec; mOverTarget = aTargetSpec; nsCOMPtr browserChrome(do_GetInterface(mTreeOwner)); if (browserChrome) browserChrome->SetOverLink(aURLSpec); } return NS_OK; } NS_IMETHODIMP nsWebShell::GetLinkState(const PRUnichar* aURLSpec, nsLinkState& aState) { aState = eLinkState_Unvisited; nsresult rv; // XXX: GlobalHistory is going to be moved out of the webshell into a more appropriate place. if ((nsnull == mHistoryService) && !mFailedToLoadHistoryService) { rv = nsServiceManager::GetService(kGlobalHistoryCID, NS_GET_IID(nsIGlobalHistory), (nsISupports**) &mHistoryService); if (NS_FAILED(rv)) { mFailedToLoadHistoryService = PR_TRUE; } } if (mHistoryService) { // XXX aURLSpec should really be a char*, not a PRUnichar*. nsAutoString urlStr(aURLSpec); char buf[256]; char* url = buf; if (urlStr.Length() >= PRInt32(sizeof buf)) { url = new char[urlStr.Length() + 1]; } PRInt64 lastVisitDate; if (url) { urlStr.ToCString(url, urlStr.Length() + 1); rv = mHistoryService->GetLastVisitDate(url, &lastVisitDate); if (url != buf) delete[] url; } else { rv = NS_ERROR_OUT_OF_MEMORY; } //XXX: Moved to destructor nsServiceManager::ReleaseService(kGlobalHistoryCID, mHistoryService); if (NS_FAILED(rv)) return rv; // a last-visit-date of zero means we've never seen it before; so // if it's not zero, we must've seen it. if (! LL_IS_ZERO(lastVisitDate)) aState = eLinkState_Visited; // XXX how to tell if eLinkState_OutOfDate? } return NS_OK; } //---------------------------------------------------------------------- NS_IMETHODIMP nsWebShell::OnStartDocumentLoad(nsIDocumentLoader* loader, nsIURI* aURL, const char* aCommand) { nsIDocumentViewer* docViewer; nsresult rv = NS_ERROR_FAILURE; if ((mScriptGlobal) && (loader == mDocLoader)) { if (nsnull != mContentViewer && NS_OK == mContentViewer->QueryInterface(kIDocumentViewerIID, (void**)&docViewer)) { nsIPresContext *presContext; if (NS_OK == docViewer->GetPresContext(presContext)) { nsEventStatus status = nsEventStatus_eIgnore; nsMouseEvent event; event.eventStructType = NS_EVENT; event.message = NS_PAGE_UNLOAD; rv = mScriptGlobal->HandleDOMEvent(presContext, &event, nsnull, NS_EVENT_FLAG_INIT, &status); NS_RELEASE(presContext); } NS_RELEASE(docViewer); } } if (loader == mDocLoader) { nsCOMPtr dlObserver; if (!mDocLoaderObserver && mParent) { /* If this is a frame (in which case it would have a parent && doesn't * have a documentloaderObserver, get it from the rootWebShell */ nsCOMPtr rootItem; GetSameTypeRootTreeItem(getter_AddRefs(rootItem)); nsCOMPtr rootDocShell(do_QueryInterface(rootItem)); if (rootDocShell) rootDocShell->GetDocLoaderObserver(getter_AddRefs(dlObserver)); } else { dlObserver = do_QueryInterface(mDocLoaderObserver); // we need this to addref } /* * Fire the OnStartDocumentLoad of the webshell observer */ if ((nsnull != mContainer) && (nsnull != dlObserver)) { dlObserver->OnStartDocumentLoad(mDocLoader, aURL, aCommand); } } return rv; } NS_IMETHODIMP nsWebShell::OnEndDocumentLoad(nsIDocumentLoader* loader, nsIChannel* channel, nsresult aStatus) { #ifdef MOZ_PERF_METRICS MOZ_TIMER_DEBUGLOG(("Stop: nsWebShell::OnEndDocumentLoad(), this=%p\n", this)); MOZ_TIMER_STOP(mTotalTime); MOZ_TIMER_LOG(("Total (Layout + Page Load) Time (webshell=%p): ", this)); MOZ_TIMER_PRINT(mTotalTime); #endif nsresult rv = NS_ERROR_FAILURE; if (!channel) { return NS_ERROR_NULL_POINTER; } nsCOMPtr aURL; rv = channel->GetURI(getter_AddRefs(aURL)); if (NS_FAILED(rv)) return rv; // clean up reload state for meta charset if(eCharsetReloadRequested == mCharsetReloadState) mCharsetReloadState = eCharsetReloadStopOrigional; else mCharsetReloadState = eCharsetReloadInit; /* one of many safeguards that prevent death and destruction if someone is so very very rude as to bring this window down during this load handler. */ nsCOMPtr kungFuDeathGrip(this); //if (!mProcessedEndDocumentLoad) { if (loader == mDocLoader) { mProcessedEndDocumentLoad = PR_TRUE; if (mScriptGlobal && !mEODForCurrentDocument) { nsIDocumentViewer* docViewer; if (nsnull != mContentViewer && NS_OK == mContentViewer->QueryInterface(kIDocumentViewerIID, (void**)&docViewer)) { nsIPresContext *presContext; if (NS_OK == docViewer->GetPresContext(presContext)) { nsEventStatus status = nsEventStatus_eIgnore; nsMouseEvent event; event.eventStructType = NS_EVENT; event.message = NS_PAGE_LOAD; rv = mScriptGlobal->HandleDOMEvent(presContext, &event, nsnull, NS_EVENT_FLAG_INIT, &status); NS_RELEASE(presContext); } NS_RELEASE(docViewer); } } mEODForCurrentDocument = PR_TRUE; // Fire the EndLoadURL of the web shell container if (nsnull != aURL) { nsAutoString urlString; char* spec; rv = aURL->GetSpec(&spec); if (NS_SUCCEEDED(rv)) { urlString = spec; if (nsnull != mContainer) { rv = mContainer->EndLoadURL(this, urlString.GetUnicode(), 0); } nsCRT::free(spec); } } nsCOMPtr dlObserver; if (!mDocLoaderObserver && mParent) { /* If this is a frame (in which case it would have a parent && doesn't * have a documentloaderObserver, get it from the rootWebShell */ nsCOMPtr rootItem; GetSameTypeRootTreeItem(getter_AddRefs(rootItem)); nsCOMPtr rootDocShell(do_QueryInterface(rootItem)); if (rootDocShell) rootDocShell->GetDocLoaderObserver(getter_AddRefs(dlObserver)); } else { /* Take care of the Trailing slash situation */ if (mSHist) CheckForTrailingSlash(aURL); dlObserver = do_QueryInterface(mDocLoaderObserver); // we need this to addref } /* * Fire the OnEndDocumentLoad of the DocLoaderobserver */ if (dlObserver && (nsnull != aURL)) { dlObserver->OnEndDocumentLoad(mDocLoader, channel, aStatus); } if (mDocLoader == loader && NS_FAILED(aStatus)) { nsAutoString errorMsg; nsXPIDLCString host; rv = aURL->GetHost(getter_Copies(host)); if (NS_FAILED(rv)) return rv; CBufDescriptor buf((const char *)host, PR_TRUE, PL_strlen(host) + 1); nsCAutoString hostStr(buf); PRInt32 dotLoc = hostStr.FindChar('.'); if (aStatus == NS_ERROR_UNKNOWN_HOST || aStatus == NS_ERROR_CONNECTION_REFUSED || aStatus == NS_ERROR_NET_TIMEOUT) { // First see if we should throw it to the keyword server. NS_ASSERTION(mPrefs, "the webshell's pref service wasn't initialized"); PRBool keywordsEnabled = PR_FALSE; rv = mPrefs->GetBoolPref("keyword.enabled", &keywordsEnabled); if (NS_FAILED(rv)) return rv; if (keywordsEnabled && (-1 == dotLoc)) { // only send non-qualified hosts to the keyword server nsAutoString keywordSpec("keyword:"); keywordSpec.Append(host); return LoadURL(keywordSpec.GetUnicode(), "view"); } // end keywordsEnabled } // Doc failed to load because the host was not found. if (aStatus == NS_ERROR_UNKNOWN_HOST) { // Try our www.*.com trick. nsCAutoString retryHost; nsXPIDLCString scheme; rv = aURL->GetScheme(getter_Copies(scheme)); if (NS_FAILED(rv)) return rv; PRUint32 schemeLen = PL_strlen((const char*)scheme); CBufDescriptor schemeBuf((const char*)scheme, PR_TRUE, schemeLen+1, schemeLen); nsCAutoString schemeStr(schemeBuf); if (schemeStr.Find("http") == 0) { if (-1 == dotLoc) { retryHost = "www."; retryHost += hostStr; retryHost += ".com"; } else { PRInt32 hostLen = hostStr.Length(); if ( ((hostLen - dotLoc) == 3) || ((hostLen - dotLoc) == 4) ) { retryHost = "www."; retryHost += hostStr; } } } if (!retryHost.IsEmpty()) { rv = aURL->SetHost(retryHost.GetBuffer()); if (NS_FAILED(rv)) return rv; nsXPIDLCString aSpec; rv = aURL->GetSpec(getter_Copies(aSpec)); if (NS_FAILED(rv)) return rv; nsAutoString newURL(aSpec); // reload the url return LoadURL(newURL.GetUnicode(), "view"); } // retry // throw a DNS failure dialog rv = InitDialogVars(); if (NS_FAILED(rv)) return rv; nsXPIDLString messageStr; nsAutoString name("dnsNotFound"); rv = mStringBundle->GetStringFromName(name.GetUnicode(), getter_Copies(messageStr)); if (NS_FAILED(rv)) return rv; errorMsg = host; errorMsg.Append(' '); errorMsg.Append(messageStr); (void)mPrompter->Alert(errorMsg.GetUnicode()); } else // unknown host // Doc failed to load because we couldn't connect to the server. if (aStatus == NS_ERROR_CONNECTION_REFUSED) { // throw a connection failure dialog PRInt32 port = -1; rv = aURL->GetPort(&port); if (NS_FAILED(rv)) return rv; rv = InitDialogVars(); if (NS_FAILED(rv)) return rv; nsXPIDLString messageStr; nsAutoString name("connectionFailure"); rv = mStringBundle->GetStringFromName(name.GetUnicode(), getter_Copies(messageStr)); if (NS_FAILED(rv)) return rv; errorMsg = messageStr; errorMsg.Append(' '); errorMsg.Append(host); if (port > 0) { errorMsg.Append(':'); errorMsg.Append(port); } errorMsg.Append('.'); (void)mPrompter->Alert(errorMsg.GetUnicode()); } else // end NS_ERROR_CONNECTION_REFUSED // Doc failed to load because the socket function timed out. if (aStatus == NS_ERROR_NET_TIMEOUT) { // throw a timeout dialog rv = InitDialogVars(); if (NS_FAILED(rv)) return rv; nsXPIDLString messageStr; nsAutoString name("netTimeout"); rv = mStringBundle->GetStringFromName(name.GetUnicode(), getter_Copies(messageStr)); if (NS_FAILED(rv)) return rv; errorMsg = messageStr; errorMsg.Append(' '); errorMsg.Append(host); errorMsg.Append('.'); (void)mPrompter->Alert(errorMsg.GetUnicode()); } // end NS_ERROR_NET_TIMEOUT } // end mDocLoader == loader } //!mProcessedEndDocumentLoad else { rv = NS_OK; } return rv; } NS_IMETHODIMP nsWebShell::OnStartURLLoad(nsIDocumentLoader* loader, nsIChannel* channel) { nsresult rv; nsCOMPtr aURL; rv = channel->GetURI(getter_AddRefs(aURL)); if (NS_FAILED(rv)) return rv; // Stop loading of the earlier document completely when the document url // load starts. Now we know that this url is valid and available. PRBool equals = PR_FALSE; if (NS_SUCCEEDED(aURL->Equals(mCurrentURI, &equals)) && equals) Stop(); /* *Fire the OnStartDocumentLoad of the webshell observer */ if ((nsnull != mContainer) && (nsnull != mDocLoaderObserver)) { mDocLoaderObserver->OnStartURLLoad(mDocLoader, channel); } return NS_OK; } NS_IMETHODIMP nsWebShell::OnProgressURLLoad(nsIDocumentLoader* loader, nsIChannel* channel, PRUint32 aProgress, PRUint32 aProgressMax) { /* *Fire the OnStartDocumentLoad of the webshell observer and container... */ if ((nsnull != mContainer) && (nsnull != mDocLoaderObserver)) { mDocLoaderObserver->OnProgressURLLoad(mDocLoader, channel, aProgress, aProgressMax); } return NS_OK; } NS_IMETHODIMP nsWebShell::OnStatusURLLoad(nsIDocumentLoader* loader, nsIChannel* channel, nsString& aMsg) { /* *Fire the OnStartDocumentLoad of the webshell observer and container... */ if ((nsnull != mContainer) && (nsnull != mDocLoaderObserver)) { mDocLoaderObserver->OnStatusURLLoad(mDocLoader, channel, aMsg); } return NS_OK; } NS_IMETHODIMP nsWebShell::OnEndURLLoad(nsIDocumentLoader* loader, nsIChannel* channel, nsresult aStatus) { #if 0 const char* spec; aURL->GetSpec(&spec); printf("nsWebShell::OnEndURLLoad:%p: loader=%p url=%s status=%d\n", this, loader, spec, aStatus); #endif /* *Fire the OnEndDocumentLoad of the webshell observer */ if ((nsnull != mContainer) && (nsnull != mDocLoaderObserver)) { mDocLoaderObserver->OnEndURLLoad(mDocLoader, channel, aStatus); } return NS_OK; } /* For use with redirect/refresh url api */ class refreshData : public nsITimerCallback { public: refreshData(); NS_DECL_ISUPPORTS // nsITimerCallback interface NS_IMETHOD_(void) Notify(nsITimer *timer); nsIWebShell* mShell; nsString mUrlSpec; PRBool mRepeat; PRInt32 mDelay; protected: virtual ~refreshData(); }; refreshData::refreshData() { NS_INIT_REFCNT(); mShell = nsnull; } refreshData::~refreshData() { NS_IF_RELEASE(mShell); } NS_IMPL_ISUPPORTS(refreshData, kITimerCallbackIID); NS_IMETHODIMP_(void) refreshData::Notify(nsITimer *aTimer) { NS_PRECONDITION((nsnull != mShell), "Null pointer..."); if (nsnull != mShell) { mShell->LoadURL(mUrlSpec.GetUnicode(), nsnull, PR_TRUE, nsIChannel::LOAD_NORMAL); } /* * LoadURL(...) will cancel all refresh timers... This causes the Timer and * its refreshData instance to be released... */ } NS_IMETHODIMP nsWebShell::RefreshURI(nsIURI* aURI, PRInt32 millis, PRBool repeat) { nsresult rv = NS_OK; if (nsnull == aURI) { NS_PRECONDITION((aURI != nsnull), "Null pointer"); rv = NS_ERROR_NULL_POINTER; goto done; } char* spec; aURI->GetSpec(&spec); rv = RefreshURL(spec, millis, repeat); nsCRT::free(spec); done: return rv; } NS_IMETHODIMP nsWebShell::RefreshURL(const char* aURI, PRInt32 millis, PRBool repeat) { nsresult rv = NS_OK; nsITimer *timer=nsnull; refreshData *data; if (nsnull == aURI) { NS_PRECONDITION((aURI != nsnull), "Null pointer"); rv = NS_ERROR_NULL_POINTER; goto done; } NS_NEWXPCOM(data, refreshData); if (nsnull == data) { rv = NS_ERROR_OUT_OF_MEMORY; goto done; } // Set the reference count to one... NS_ADDREF(data); data->mShell = this; NS_ADDREF(data->mShell); data->mUrlSpec = aURI; data->mDelay = millis; data->mRepeat = repeat; /* Create the timer. */ if (NS_OK == NS_NewTimer(&timer)) { /* Add the timer to our array. */ NS_LOCK_INSTANCE(); mRefreshments.AppendElement(timer); timer->Init(data, millis); NS_UNLOCK_INSTANCE(); } NS_RELEASE(data); done: return rv; } NS_IMETHODIMP nsWebShell::CancelRefreshURITimers(void) { PRInt32 i; nsITimer* timer; /* Right now all we can do is cancel all the timers for this webshell. */ NS_LOCK_INSTANCE(); /* Walk the list backwards to avoid copying the array as it shrinks.. */ for (i = mRefreshments.Count()-1; (0 <= i); i--) { timer=(nsITimer*)mRefreshments.ElementAt(i); /* Remove the entry from the list before releasing the timer.*/ mRefreshments.RemoveElementAt(i); if (timer) { timer->Cancel(); NS_RELEASE(timer); } } NS_UNLOCK_INSTANCE(); return NS_OK; } //---------------------------------------------------------------------- /* * There are cases where netlib does things like add a trailing slash * to the url being retrieved. We need to watch out for such * changes and update the currently loading url's entry in the history * list. UpdateHistoryEntry() does this. * * Assumptions: * * 1) aURL is the URL that was inserted into the history list in LoadURL() * 2) The load of aURL is in progress and this function is being called * from one of the functions in nsIStreamListener implemented by nsWebShell. */ nsresult nsWebShell::CheckForTrailingSlash(nsIURI* aURL) { PRInt32 curIndex=0; nsresult rv; /* Get current history index and url for it */ rv = mSHist->GetCurrentIndex(&curIndex); /* Get the url that netlib passed us */ char* spec; aURL->GetSpec(&spec); //Set it in session history if (NS_SUCCEEDED(rv) && !mTitle.IsEmpty()) { mSHist->SetTitleForIndex(curIndex, mTitle.GetUnicode()); // Replace the top most history entry with the new url mSHist->SetURLForIndex(curIndex, spec); } nsCRT::free(spec); return NS_OK; } //---------------------------------------------------- NS_IMETHODIMP nsWebShell::CanCutSelection(PRBool* aResult) { nsresult rv = NS_OK; if (nsnull == aResult) { rv = NS_ERROR_NULL_POINTER; } else { *aResult = PR_FALSE; } return rv; } NS_IMETHODIMP nsWebShell::CanCopySelection(PRBool* aResult) { nsresult rv = NS_OK; if (nsnull == aResult) { rv = NS_ERROR_NULL_POINTER; } else { *aResult = PR_FALSE; } return rv; } NS_IMETHODIMP nsWebShell::CanPasteSelection(PRBool* aResult) { nsresult rv = NS_OK; if (nsnull == aResult) { rv = NS_ERROR_NULL_POINTER; } else { *aResult = PR_FALSE; } return rv; } NS_IMETHODIMP nsWebShell::CutSelection(void) { return NS_ERROR_FAILURE; } NS_IMETHODIMP nsWebShell::CopySelection(void) { return NS_ERROR_FAILURE; } NS_IMETHODIMP nsWebShell::PasteSelection(void) { return NS_ERROR_FAILURE; } NS_IMETHODIMP nsWebShell::SelectAll(void) { nsresult rv; nsCOMPtr docViewer; rv = mContentViewer->QueryInterface(kIDocumentViewerIID, getter_AddRefs(docViewer)); if (NS_FAILED(rv) || !docViewer) return rv; nsCOMPtr presShell; rv = docViewer->GetPresShell(*getter_AddRefs(presShell)); if (NS_FAILED(rv) || !presShell) return rv; nsCOMPtr selection; rv = presShell->GetSelection(SELECTION_NORMAL, getter_AddRefs(selection)); if (NS_FAILED(rv) || !selection) return rv; // Get the document object nsCOMPtr doc; rv = docViewer->GetDocument(*getter_AddRefs(doc)); if (NS_FAILED(rv) || !doc) return rv; nsCOMPtr htmldoc; rv = doc->QueryInterface(kIDOMHTMLDocumentIID, (void**)&htmldoc); if (NS_FAILED(rv) || !htmldoc) return rv; nsCOMPtrbodyElement; rv = htmldoc->GetBody(getter_AddRefs(bodyElement)); if (NS_FAILED(rv) || !bodyElement) return rv; nsCOMPtrbodyNode = do_QueryInterface(bodyElement); if (!bodyNode) return NS_ERROR_NO_INTERFACE; #if 0 rv = selection->Collapse(bodyNode, 0); if (NS_FAILED(rv)) return rv; nsCOMPtrrange; rv = selection->GetRangeAt(0, getter_AddRefs(range)); if (NS_FAILED(rv) || !range) return rv; #endif rv = selection->ClearSelection(); if (NS_FAILED(rv)) return rv; nsCOMPtr range; rv = nsComponentManager::CreateInstance(kCDOMRangeCID, nsnull, NS_GET_IID(nsIDOMRange), getter_AddRefs(range)); rv = range->SelectNodeContents(bodyNode); if (NS_FAILED(rv)) return rv; rv = selection->AddRange(range); return rv; } NS_IMETHODIMP nsWebShell::SelectNone(void) { return NS_ERROR_FAILURE; } //---------------------------------------------------- NS_IMETHODIMP nsWebShell::FindNext(const PRUnichar * aSearchStr, PRBool aMatchCase, PRBool aSearchDown, PRBool &aIsFound) { return NS_ERROR_FAILURE; } // Methods from nsIProgressEventSink NS_IMETHODIMP nsWebShell::OnProgress(nsIChannel* channel, nsISupports* ctxt, PRUint32 aProgress, PRUint32 aProgressMax) { if (nsnull != mDocLoaderObserver) { return mDocLoaderObserver->OnProgressURLLoad( mDocLoader, channel, aProgress, aProgressMax); } return NS_OK; } NS_IMETHODIMP nsWebShell::OnStatus(nsIChannel* channel, nsISupports* ctxt, const PRUnichar* aMsg) { if (nsnull != mDocLoaderObserver) { nsAutoString temp(aMsg); nsresult rv = mDocLoaderObserver->OnStatusURLLoad( mDocLoader, channel, temp); return rv; } return NS_OK; } #define DIALOG_STRING_URI "chrome://global/locale/appstrings.properties" nsresult nsWebShell::InitDialogVars(void) { nsresult rv = NS_OK; if (!mPrompter) { // Get an nsIPrompt so we can throw dialogs. nsCOMPtr topLevelWindow; GetTopLevelWindow(getter_AddRefs(topLevelWindow)); if (topLevelWindow) mPrompter = do_QueryInterface(topLevelWindow); } if (!mStringBundle) { // Get a string bundle so we can retrieve dialog strings nsCOMPtr locale; NS_WITH_SERVICE(nsILocaleService, localeServ, kLocaleServiceCID, &rv); if (NS_FAILED(rv)) return rv; rv = localeServ->GetSystemLocale(getter_AddRefs(locale)); if (NS_FAILED(rv)) return rv; NS_WITH_SERVICE(nsIStringBundleService, service, kCStringBundleServiceCID, &rv); if (NS_FAILED(rv)) return rv; rv = service->CreateBundle(DIALOG_STRING_URI, locale, getter_AddRefs(mStringBundle)); } return rv; } //***************************************************************************** // nsWebShell::nsIBaseWindow //***************************************************************************** NS_IMETHODIMP nsWebShell::Create() { // Cache the PL_EventQueue of the current UI thread... // // Since this call must be made on the UI thread, we know the Event Queue // will be associated with the current thread... // nsCOMPtr eventService(do_GetService(kEventQueueServiceCID)); NS_ENSURE_TRUE(eventService, NS_ERROR_FAILURE); NS_ENSURE_SUCCESS(eventService->GetThreadEventQueue(NS_CURRENT_THREAD, &mThreadEventQueue), NS_ERROR_FAILURE); //XXX make sure plugins have started up. this really needs to //be associated with the nsIContentViewerContainer interfaces, //not the nsIWebShell interfaces. this is a hack. MMP CreatePluginHost(mAllowPlugins); WEB_TRACE(WEB_TRACE_CALLS, ("nsWebShell::Init: this=%p", this)); // HACK....force the uri loader to give us a load cookie for this webshell...then get it's // doc loader and store it...as more of the docshell lands, we'll be able to get rid // of this hack... nsCOMPtr uriLoader = do_GetService(NS_URI_LOADER_PROGID); uriLoader->GetDocumentLoaderForContext(NS_STATIC_CAST( nsISupports*, (nsIWebShell *) this), &mDocLoader); // Set the webshell as the default IContentViewerContainer for the loader... mDocLoader->SetContainer(NS_STATIC_CAST(nsIContentViewerContainer*, (nsIWebShell*)this)); //Register ourselves as an observer for the new doc loader mDocLoader->AddObserver((nsIDocumentLoaderObserver*)this); return nsDocShell::Create(); } NS_IMETHODIMP nsWebShell::Destroy() { nsresult rv = NS_OK; if (!mFiredUnloadEvent) { //Fire unload event before we blow anything away. rv = FireUnloadEvent(); } // Stop any URLs that are currently being loaded... Stop(); mDocLoader->Destroy(); SetContainer(nsnull); SetDocLoaderObserver(nsnull); // Remove this docshell from its parent's child list nsCOMPtr docShellParentAsNode(do_QueryInterface(mParent)); if (docShellParentAsNode) { docShellParentAsNode->RemoveChild(this); } if (nsnull != mDocLoader) { mDocLoader->SetContainer(nsnull); } mContentViewer = nsnull; // Destroy our child web shells and release references to them DestroyChildren(); return rv; } NS_IMETHODIMP nsWebShell::SetPositionAndSize(PRInt32 x, PRInt32 y, PRInt32 cx, PRInt32 cy, PRBool fRepaint) { mBounds.SetRect(x, y, cx, cy); return nsDocShell::SetPositionAndSize(x, y, cx, cy, fRepaint); } NS_IMETHODIMP nsWebShell::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; } //***************************************************************************** // nsWebShell::nsIDocShell //***************************************************************************** NS_IMETHODIMP nsWebShell::SetDocument(nsIDOMDocument *aDOMDoc, nsIDOMElement *aRootNode) { // The tricky part is bypassing the normal load process and just putting a document into // the webshell. This is particularly nasty, since webshells don't normally even know // about their documents // (1) Create a document viewer nsCOMPtr documentViewer; nsCOMPtr docFactory; static NS_DEFINE_CID(kLayoutDocumentLoaderFactoryCID, NS_LAYOUT_DOCUMENT_LOADER_FACTORY_CID); NS_ENSURE_SUCCESS(nsComponentManager::CreateInstance(kLayoutDocumentLoaderFactoryCID, nsnull, NS_GET_IID(nsIDocumentLoaderFactory), (void**)getter_AddRefs(docFactory)), NS_ERROR_FAILURE); nsCOMPtr doc = do_QueryInterface(aDOMDoc); if (!doc) { return NS_ERROR_NULL_POINTER; } NS_ENSURE_SUCCESS(docFactory->CreateInstanceForDocument(NS_STATIC_CAST(nsIContentViewerContainer*, (nsIWebShell*)this), doc, "view", getter_AddRefs(documentViewer)), NS_ERROR_FAILURE); // (2) Feed the webshell to the content viewer NS_ENSURE_SUCCESS(documentViewer->SetContainer((nsIWebShell*)this), NS_ERROR_FAILURE); // (3) Tell the content viewer container to embed the content viewer. // (This step causes everything to be set up for an initial flow.) NS_ENSURE_SUCCESS(SetupNewViewer(documentViewer), NS_ERROR_FAILURE); // XXX: It would be great to get rid of this dummy channel! const nsAutoString uriString = "about:blank"; nsCOMPtr uri; NS_ENSURE_SUCCESS(NS_NewURI(getter_AddRefs(uri), uriString), NS_ERROR_FAILURE); if (!uri) { return NS_ERROR_OUT_OF_MEMORY; } nsCOMPtr dummyChannel; NS_ENSURE_SUCCESS(NS_OpenURI(getter_AddRefs(dummyChannel), uri, nsnull), NS_ERROR_FAILURE); // (4) fire start document load notification nsCOMPtr outStreamListener; // a valid pointer is required for the returned stream listener // XXX: warning: magic cookie! should get string "view delayedContentLoad" // from somewhere, maybe nsIHTMLDocument? NS_ENSURE_SUCCESS(doc->StartDocumentLoad("view delayedContentLoad", dummyChannel, nsnull, NS_STATIC_CAST(nsIContentViewerContainer*, (nsIWebShell*)this), getter_AddRefs(outStreamListener)), NS_ERROR_FAILURE); NS_ENSURE_SUCCESS(OnStartDocumentLoad(mDocLoader, uri, "load"), NS_ERROR_FAILURE); // (5) hook up the document and its content nsCOMPtr rootContent = do_QueryInterface(aRootNode); if (!doc) { return NS_ERROR_OUT_OF_MEMORY; } NS_ENSURE_SUCCESS(rootContent->SetDocument(doc, PR_FALSE), NS_ERROR_FAILURE); doc->SetRootContent(rootContent); rootContent->SetDocument(doc, PR_TRUE); // (6) reflow the document PRInt32 i; PRInt32 ns = doc->GetNumberOfShells(); for (i = 0; i < ns; i++) { nsCOMPtr shell(dont_AddRef(doc->GetShellAt(i))); if (shell) { // Make shell an observer for next time NS_ENSURE_SUCCESS(shell->BeginObservingDocument(), NS_ERROR_FAILURE); // Resize-reflow this time nsCOMPtr docViewer = do_QueryInterface(documentViewer); if (!docViewer) { return NS_ERROR_OUT_OF_MEMORY; } nsCOMPtr presContext; NS_ENSURE_SUCCESS(docViewer->GetPresContext(*(getter_AddRefs(presContext))), NS_ERROR_FAILURE); if (!presContext) { return NS_ERROR_OUT_OF_MEMORY; } float p2t; presContext->GetScaledPixelsToTwips(&p2t); nsRect r; GetPositionAndSize(&r.x, &r.y, &r.width, &r.height); NS_ENSURE_SUCCESS(shell->InitialReflow(NSToCoordRound(r.width * p2t), NSToCoordRound(r.height * p2t)), NS_ERROR_FAILURE); // Now trigger a refresh nsCOMPtr vm; NS_ENSURE_SUCCESS(shell->GetViewManager(getter_AddRefs(vm)), NS_ERROR_FAILURE); if (vm) { PRBool enabled; documentViewer->GetEnableRendering(&enabled); if (enabled) { vm->EnableRefresh(NS_VMREFRESH_IMMEDIATE); } NS_ENSURE_SUCCESS(vm->SetWindowDimensions(NSToCoordRound(r.width * p2t), NSToCoordRound(r.height * p2t)), NS_ERROR_FAILURE); } } } // (7) fire end document load notification mProcessedEndDocumentLoad = PR_FALSE; nsresult rv = NS_OK; NS_ENSURE_SUCCESS(OnEndDocumentLoad(mDocLoader, dummyChannel, rv), NS_ERROR_FAILURE); NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); // test the resulting out-param separately return NS_OK; } NS_IMETHODIMP nsWebShell::GetParentContentListener(nsIURIContentListener** aParent) { return GetParentURIContentListener(aParent); } NS_IMETHODIMP nsWebShell::SetParentContentListener(nsIURIContentListener* aParent) { return SetParentURIContentListener(aParent); } NS_IMETHODIMP nsWebShell::GetLoadCookie(nsISupports ** aLoadCookie) { NS_ENSURE_SUCCESS(EnsureContentListener(), NS_ERROR_FAILURE); return mContentListener->GetLoadCookie(aLoadCookie); } NS_IMETHODIMP nsWebShell::SetLoadCookie(nsISupports * aLoadCookie) { NS_ENSURE_SUCCESS(EnsureContentListener(), NS_ERROR_FAILURE); return mContentListener->SetLoadCookie(aLoadCookie); } //---------------------------------------------------------------------- // Factory code for creating nsWebShell's class nsWebShellFactory : public nsIFactory { public: nsWebShellFactory(); virtual ~nsWebShellFactory(); NS_DECL_ISUPPORTS // nsIFactory methods NS_IMETHOD CreateInstance(nsISupports *aOuter, const nsIID &aIID, void **aResult); NS_IMETHOD LockFactory(PRBool aLock); }; nsWebShellFactory::nsWebShellFactory() { NS_INIT_REFCNT(); } nsWebShellFactory::~nsWebShellFactory() { } NS_IMPL_ADDREF(nsWebShellFactory); NS_IMPL_RELEASE(nsWebShellFactory); NS_INTERFACE_MAP_BEGIN(nsWebShellFactory) NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_ENTRY(nsIFactory) NS_INTERFACE_MAP_END nsresult nsWebShellFactory::CreateInstance(nsISupports *aOuter, const nsIID &aIID, void **aResult) { nsresult rv; nsWebShell *inst; NS_ENSURE_ARG_POINTER(aResult); NS_ENSURE_NO_AGGREGATION(aOuter); *aResult = NULL; NS_NEWXPCOM(inst, nsWebShell); NS_ENSURE_TRUE(inst, NS_ERROR_OUT_OF_MEMORY); NS_ADDREF(inst); rv = inst->QueryInterface(aIID, aResult); NS_RELEASE(inst); return rv; } nsresult nsWebShellFactory::LockFactory(PRBool aLock) { // Not implemented in simplest case. return NS_OK; } extern "C" NS_WEB nsresult NS_NewWebShellFactory(nsIFactory** aFactory) { nsresult rv = NS_OK; nsIFactory* inst = new nsWebShellFactory(); if (nsnull == inst) { rv = NS_ERROR_OUT_OF_MEMORY; } else { NS_ADDREF(inst); } *aFactory = inst; return rv; }