/* -*- 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 #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 #include "nsDocShell.h" #include "nsIWebShell.h" #include "nsWebShell.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 "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 "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 "nsILayoutHistoryState.h" #include "nsTextFormatter.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" #define SH_IN_FRAMES 1 #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 static NS_DEFINE_CID(kGlobalHistoryCID, NS_GLOBALHISTORY_CID); static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID); //---------------------------------------------------------------------- //---------------------------------------------------------------------- // 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_ICONTENTVIEWERCONTAINER_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(kIDOMHTMLDocumentIID, NS_IDOMHTMLDOCUMENT_IID); static NS_DEFINE_CID(kCDOMRangeCID, NS_RANGE_CID); // XXX not sure static NS_DEFINE_IID(kILinkHandlerIID, NS_ILINKHANDLER_IID); //---------------------------------------------------------------------- // 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(); mThreadEventQueue = nsnull; InitFrameData(); mItemType = typeContent; mCharsetReloadState = eCharsetReloadInit; mHistoryState = nsnull; mFiredUnloadEvent = PR_FALSE; mBounds.SetRect(0, 0, 0, 0); } nsWebShell::~nsWebShell() { Destroy(); // Stop any pending document loads and destroy the loader... if (nsnull != mDocLoader) { mDocLoader->Stop(); mDocLoader->RemoveObserver((nsIDocumentLoaderObserver*)this); mDocLoader->SetContainer(nsnull); mDocLoader->Destroy(); 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(mThreadEventQueue); mContentViewer=nsnull; mDeviceContext=nsnull; NS_IF_RELEASE(mContainer); if (mScriptGlobal) { mScriptGlobal->SetDocShell(nsnull); mScriptGlobal = nsnull; } if (mScriptContext) { mScriptContext->SetOwner(nsnull); mScriptContext = nsnull; } InitFrameData(); #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 (!mFiredUnloadEvent) { mFiredUnloadEvent = PR_TRUE; 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(); 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(nsIWebShellContainer) NS_INTERFACE_MAP_ENTRY(nsILinkHandler) NS_INTERFACE_MAP_ENTRY(nsIClipboardCommands) #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(nsIRefreshURI) 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(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; } if (!*aInstancePtr || NS_FAILED(rv)) return nsDocShell::GetInterface(aIID,aInstancePtr); else return rv; } NS_IMETHODIMP nsWebShell::SetupNewViewer(nsIContentViewer* aViewer) { NS_ENSURE_SUCCESS(FireUnloadEvent(), NS_ERROR_FAILURE); NS_ENSURE_SUCCESS(nsDocShell::SetupNewViewer(aViewer), NS_ERROR_FAILURE); // Set mFiredUnloadEvent = PR_FALSE so that the unload handler for the // *new* document will fire. mFiredUnloadEvent = PR_FALSE; // 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) { #ifdef SH_IN_FRAMES return nsDocShell::Embed(aContentViewer, aCommand, aExtraInfo); #else return SetupNewViewer(aContentViewer); #endif /* SH_IN_FRAMES */ } NS_IMETHODIMP nsWebShell::SetContainer(nsIWebShellContainer* aContainer) { NS_IF_RELEASE(mContainer); mContainer = aContainer; NS_IF_ADDREF(mContainer); 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; } 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; } /** * Document Load methods */ NS_IMETHODIMP nsWebShell::GetDocumentLoader(nsIDocumentLoader*& aResult) { aResult = mDocLoader; NS_IF_ADDREF(mDocLoader); return (nsnull != mDocLoader) ? NS_OK : NS_ERROR_FAILURE; } //---------------------------------------- // History methods NS_IMETHODIMP nsWebShell::GoTo(PRInt32 aIndex) { NS_ENSURE_STATE(mSessionHistory); NS_ENSURE_TRUE(!IsFrame(), NS_ERROR_FAILURE); nsCOMPtr entry; NS_ENSURE_SUCCESS(mSessionHistory->GetEntryAtIndex(aIndex, PR_TRUE, getter_AddRefs(entry)), NS_ERROR_FAILURE); NS_ENSURE_TRUE(entry, NS_ERROR_FAILURE); UpdateCurrentSessionHistory(); #ifdef SH_IN_FRAMES NS_ENSURE_SUCCESS(LoadHistoryEntry(entry, nsIDocShellLoadInfo::loadHistory), NS_ERROR_FAILURE); #else NS_ENSURE_SUCCESS(LoadHistoryEntry(entry), NS_ERROR_FAILURE); #endif return NS_OK; } NS_IMETHODIMP nsWebShell::GetHistoryLength(PRInt32& aResult) { NS_ENSURE_STATE(mSessionHistory); NS_ENSURE_TRUE(!IsFrame(), NS_ERROR_FAILURE); NS_ENSURE_SUCCESS(mSessionHistory->GetCount(&aResult), NS_ERROR_FAILURE); return NS_OK; } NS_IMETHODIMP nsWebShell::GetHistoryIndex(PRInt32& aResult) { NS_ENSURE_STATE(mSessionHistory); NS_ENSURE_TRUE(!IsFrame(), NS_ERROR_FAILURE); NS_ENSURE_SUCCESS(mSessionHistory->GetIndex(&aResult), NS_ERROR_FAILURE); return NS_OK; } NS_IMETHODIMP nsWebShell::GetURL(PRInt32 aIndex, const PRUnichar** aURLResult) { NS_ENSURE_STATE(mSessionHistory); NS_ENSURE_TRUE(!IsFrame(), NS_ERROR_FAILURE); nsCOMPtr entry; NS_ENSURE_SUCCESS(mSessionHistory->GetEntryAtIndex(aIndex, PR_TRUE, getter_AddRefs(entry)), NS_ERROR_FAILURE); NS_ENSURE_TRUE(entry, NS_ERROR_FAILURE); nsCOMPtr uri; entry->GetURI(getter_AddRefs(uri)); NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE); nsXPIDLCString spec; uri->GetSpec(getter_Copies(spec)); *aURLResult = NS_ConvertASCIItoUCS2(spec).ToNewUnicode(); return NS_OK; } //---------------------------------------- //---------------------------------------------------------------------- // WebShell container implementation 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 ) { muDV->SetHintCharacterSet(NS_ConvertASCIItoUCS2(aCharset).GetUnicode()); muDV->SetHintCharacterSetSource((PRInt32)aSource); if(eCharsetReloadRequested != mCharsetReloadState) { mCharsetReloadState = eCharsetReloadRequested; LoadURI(NS_ConvertASCIItoUCS2(aURL).GetUnicode()); } } } } return NS_OK; } NS_IMETHODIMP nsWebShell::ReloadDocument(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 ) { muDV->SetHintCharacterSet(NS_ConvertASCIItoUCS2(aCharset).GetUnicode()); muDV->SetHintCharacterSetSource((PRInt32)aSource); if(eCharsetReloadRequested != mCharsetReloadState) { mCharsetReloadState = eCharsetReloadRequested; return Reload(nsIWebNavigation::loadReloadNormal); } } } } 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) { nsCAutoString target; target.AssignWithConversion(aTargetSpec); switch(aVerb) { case eLinkVerb_New: target = "_blank"; // Fall into replace case case eLinkVerb_Undefined: // Fall through, this seems like the most reasonable action 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 nsCOMPtr uri; NS_NewURI(getter_AddRefs(uri), aURLSpec, nsnull); nsCOMPtr owner; GetCurrentDocumentOwner(getter_AddRefs(owner)); #ifdef SH_IN_FRAMES InternalLoad(uri, mCurrentURI, owner, target, aPostDataStream, nsIDocShellLoadInfo::loadLink, nsnull); #else InternalLoad(uri, mCurrentURI, owner, target, aPostDataStream, nsIDocShellLoadInfo::loadLink); #endif /* SH_IN_FRAMES */ } break; case eLinkVerb_Embed: // XXX TODO Should be similar to the HTML IMG ALT attribute handling // in NS 4.x default: NS_ABORT_IF_FALSE(0,"unexpected link verb"); } } NS_IMETHODIMP nsWebShell::OnOverLink(nsIContent* aContent, const PRUnichar* aURLSpec, const PRUnichar* aTargetSpec) { nsCOMPtr browserChrome(do_GetInterface(mTreeOwner)); if(browserChrome) browserChrome->SetOverLink(aURLSpec); return NS_OK; } NS_IMETHODIMP nsWebShell::GetLinkState(const char* aLinkURI, nsLinkState& aState) { aState = eLinkState_Unvisited; if(mGlobalHistory) { PRInt64 lastVisitDate; NS_ENSURE_SUCCESS(mGlobalHistory->GetLastVisitDate(aLinkURI, &lastVisitDate), NS_ERROR_FAILURE); // 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) { nsresult rv = NS_ERROR_FAILURE; 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(mContainer && dlObserver) dlObserver->OnStartDocumentLoad(mDocLoader, aURL, aCommand); } return rv; } NS_IMETHODIMP nsWebShell::OnEndDocumentLoad(nsIDocumentLoader* loader, nsIChannel* channel, nsresult aStatus) { if(loader != mDocLoader) return NS_OK; nsresult rv = NS_OK; if(!channel) return NS_ERROR_NULL_POINTER; nsCOMPtr aURL; NS_ENSURE_SUCCESS(channel->GetURI(getter_AddRefs(aURL)), NS_ERROR_FAILURE); // 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); // Clear the LSHE reference in docshell to indicate document loading // is done one way or another. LSHE = nsnull; if(mScriptGlobal && !mEODForCurrentDocument && NS_SUCCEEDED(aStatus)) { if(mContentViewer) { nsCOMPtr presContext; GetPresContext(getter_AddRefs(presContext)); if(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); } } } mEODForCurrentDocument = PR_TRUE; 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 OnEndDocumentLoad of the DocLoaderobserver */ if(dlObserver && aURL) dlObserver->OnEndDocumentLoad(mDocLoader, channel, aStatus); if(mDocLoader == loader && NS_FAILED(aStatus)) { nsXPIDLCString host; nsresult hostResult = aURL->GetHost(getter_Copies(host)); if (NS_SUCCEEDED(hostResult) && host) { CBufDescriptor buf((const char *)host, PR_TRUE, PL_strlen(host) + 1); nsCAutoString hostStr(buf); PRInt32 dotLoc = hostStr.FindChar('.'); nsXPIDLCString scheme; aURL->GetScheme(getter_Copies(scheme)); PRUint32 schemeLen = PL_strlen((const char*)scheme); CBufDescriptor schemeBuf((const char*)scheme, PR_TRUE, schemeLen+1, schemeLen); nsCAutoString schemeStr(schemeBuf); if(aStatus == NS_ERROR_UNKNOWN_HOST || aStatus == NS_ERROR_CONNECTION_REFUSED || aStatus == NS_ERROR_NET_TIMEOUT) { PRBool keywordsEnabled = PR_FALSE; NS_ENSURE_SUCCESS(mPrefs->GetBoolPref("keyword.enabled", &keywordsEnabled), NS_ERROR_FAILURE); // we should only perform a keyword search under the following conditions: // (1) the url scheme is http (or https) // (2) the url does not have a protocol scheme // If we don't enforce such a policy, then we end up doing keyword searchs on urls // we don't intend like imap, file, mailbox, etc. This could lead to a security // problem where we send data to the keyword server that we shouldn't be. // Someone needs to clean up keywords in general so we can determine on a per url basis // if we want keywords enabled...this is just a bandaid... static const char httpSchemeName[] = "http"; if (schemeStr.IsEmpty() || !schemeStr.Find(httpSchemeName) ) keywordsEnabled = PR_TRUE; // keep keywors enabled for this url else keywordsEnabled = PR_FALSE; // all other cases, disable keywords for this url. if(keywordsEnabled && (-1 == dotLoc)) { // only send non-qualified hosts to the keyword server nsAutoString keywordSpec; keywordSpec.AssignWithConversion("keyword:"); keywordSpec.Append(NS_ConvertUTF8toUCS2(host)); return LoadURI(keywordSpec.GetUnicode()); } // 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; 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()) { NS_ENSURE_SUCCESS(aURL->SetHost(retryHost.GetBuffer()), NS_ERROR_FAILURE); nsXPIDLCString aSpec; NS_ENSURE_SUCCESS(aURL->GetSpec(getter_Copies(aSpec)), NS_ERROR_FAILURE); // reload the url return LoadURI(NS_ConvertASCIItoUCS2(aSpec).GetUnicode()); } // retry // throw a DNS failure dialog nsCOMPtr prompter; nsCOMPtr stringBundle; GetPromptAndStringBundle(getter_AddRefs(prompter), getter_AddRefs(stringBundle)); NS_ENSURE_TRUE(stringBundle, NS_ERROR_FAILURE); nsXPIDLString messageStr; NS_ENSURE_SUCCESS(stringBundle->GetStringFromName( NS_ConvertASCIItoUCS2("dnsNotFound").GetUnicode(), getter_Copies(messageStr)), NS_ERROR_FAILURE); if (host) { PRUnichar *msg = nsTextFormatter::smprintf(messageStr, (const char*)host); if (!msg) return NS_ERROR_OUT_OF_MEMORY; prompter->Alert(nsnull, msg); nsTextFormatter::smprintf_free(msg); } } else if(aStatus == NS_ERROR_CONNECTION_REFUSED) {// Doc failed to load because we couldn't connect to the server. // throw a connection failure dialog PRInt32 port = -1; NS_ENSURE_SUCCESS(aURL->GetPort(&port), NS_ERROR_FAILURE); nsCOMPtr prompter; nsCOMPtr stringBundle; GetPromptAndStringBundle(getter_AddRefs(prompter), getter_AddRefs(stringBundle)); NS_ENSURE_TRUE(stringBundle, NS_ERROR_FAILURE); nsXPIDLString messageStr; NS_ENSURE_SUCCESS(stringBundle->GetStringFromName( NS_ConvertASCIItoUCS2("connectionFailure").GetUnicode(), getter_Copies(messageStr)), NS_ERROR_FAILURE); // build up the host:port string. nsCAutoString combo(host); if (port > 0) { combo.Append(':'); combo.AppendInt(port); } PRUnichar *msg = nsTextFormatter::smprintf(messageStr, combo.GetBuffer()); if (!msg) return NS_ERROR_OUT_OF_MEMORY; prompter->Alert(nsnull, msg); nsTextFormatter::smprintf_free(msg); } else if(aStatus == NS_ERROR_NET_TIMEOUT) {// Doc failed to load because the socket function timed out. // throw a timeout dialog nsCOMPtr prompter; nsCOMPtr stringBundle; GetPromptAndStringBundle(getter_AddRefs(prompter), getter_AddRefs(stringBundle)); NS_ENSURE_TRUE(stringBundle, NS_ERROR_FAILURE); nsXPIDLString messageStr; NS_ENSURE_SUCCESS(stringBundle->GetStringFromName( NS_ConvertASCIItoUCS2("netTimeout").GetUnicode(), getter_Copies(messageStr)), NS_ERROR_FAILURE); PRUnichar *msg = nsTextFormatter::smprintf(messageStr, (const char*)host); if (!msg) return NS_ERROR_OUT_OF_MEMORY; prompter->Alert(nsnull, msg); nsTextFormatter::smprintf_free(msg); } // end NS_ERROR_NET_TIMEOUT } // if we have a host } // end mDocLoader == loader 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. nsLoadFlags loadFlags = 0; channel -> GetLoadAttributes (&loadFlags); PRBool equals = PR_FALSE; if (NS_SUCCEEDED(aURL->Equals(mCurrentURI, &equals)) && equals && !(loadFlags & nsIChannel::LOAD_REPLACE)) 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; } //---------------------------------------------------- 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 selCon = do_QueryInterface(presShell); if (NS_FAILED(rv) || !selCon) return rv; nsCOMPtr selection; rv = selCon->GetSelection(nsISelectionController::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; } //***************************************************************************** // 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); 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; //Fire unload event before we blow anything away. rv = FireUnloadEvent(); nsDocShell::Destroy(); SetContainer(nsnull); return NS_OK; } 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; } #ifdef DETECT_WEBSHELL_LEAKS unsigned long nsWebShell::gNumberOfWebShells = 0; #endif