/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsMsgWindow.h" #include "nsIURILoader.h" #include "nsCURILoader.h" #include "nsIDocShell.h" #include "nsIDocShellTreeItem.h" #include "nsIDOMElement.h" #include "mozIDOMWindow.h" #include "nsTransactionManagerCID.h" #include "nsIComponentManager.h" #include "nsILoadGroup.h" #include "nsIMsgMailNewsUrl.h" #include "nsIInterfaceRequestor.h" #include "nsIInterfaceRequestorUtils.h" #include "nsIWebProgress.h" #include "nsIWebProgressListener.h" #include "nsPIDOMWindow.h" #include "nsIPrompt.h" #include "nsICharsetConverterManager.h" #include "nsIChannel.h" #include "nsIRequestObserver.h" #include "netCore.h" #include "prmem.h" #include "plbase64.h" #include "nsMsgI18N.h" #include "nsIWebNavigation.h" #include "nsMsgContentPolicy.h" #include "nsComponentManagerUtils.h" #include "nsServiceManagerUtils.h" #include "nsIAuthPrompt.h" #include "nsMsgUtils.h" // used to dispatch urls to default protocol handlers #include "nsCExternalHandlerService.h" #include "nsIExternalProtocolService.h" static NS_DEFINE_CID(kTransactionManagerCID, NS_TRANSACTIONMANAGER_CID); NS_IMPL_ISUPPORTS(nsMsgWindow, nsIMsgWindow, nsIURIContentListener, nsISupportsWeakReference, nsIMsgWindowTest) nsMsgWindow::nsMsgWindow() { mCharsetOverride = false; m_stopped = false; } nsMsgWindow::~nsMsgWindow() { CloseWindow(); } nsresult nsMsgWindow::Init() { // register ourselves as a content listener with the uri dispatcher service nsresult rv; nsCOMPtr dispatcher = do_GetService(NS_URI_LOADER_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); rv = dispatcher->RegisterContentListener(this); if (NS_FAILED(rv)) return rv; // create Undo/Redo Transaction Manager mTransactionManager = do_CreateInstance(kTransactionManagerCID, &rv); NS_ENSURE_SUCCESS(rv, rv); return mTransactionManager->SetMaxTransactionCount(-1); } NS_IMETHODIMP nsMsgWindow::GetMessageWindowDocShell(nsIDocShell ** aDocShell) { *aDocShell = nullptr; nsCOMPtr docShell(do_QueryReferent(mMessageWindowDocShellWeak)); if (!docShell) { // if we don't have a docshell, then we need to look up the message pane docshell nsCOMPtr rootShell(do_QueryReferent(mRootDocShellWeak)); if (rootShell) { nsCOMPtr msgDocShellItem; if(rootShell) rootShell->FindChildWithName(NS_LITERAL_STRING("messagepane"), true, false, nullptr, nullptr, getter_AddRefs(msgDocShellItem)); NS_ENSURE_TRUE(msgDocShellItem, NS_ERROR_FAILURE); docShell = do_QueryInterface(msgDocShellItem); // we don't own mMessageWindowDocShell so don't try to keep a reference to it! mMessageWindowDocShellWeak = do_GetWeakReference(docShell); } } docShell.swap(*aDocShell); return NS_OK; } NS_IMETHODIMP nsMsgWindow::CloseWindow() { nsresult rv = NS_OK; nsCOMPtr dispatcher = do_GetService(NS_URI_LOADER_CONTRACTID, &rv); if (dispatcher) // on shut down it's possible dispatcher will be null. rv = dispatcher->UnRegisterContentListener(this); mMsgWindowCommands = nullptr; mStatusFeedback = nullptr; StopUrls(); nsCOMPtr messagePaneDocShell(do_QueryReferent(mMessageWindowDocShellWeak)); if (messagePaneDocShell) { nsCOMPtr listener(do_GetInterface(messagePaneDocShell)); if (listener) listener->SetParentContentListener(nullptr); SetRootDocShell(nullptr); mMessageWindowDocShellWeak = nullptr; } // in case nsMsgWindow leaks, make sure other stuff doesn't leak. mTransactionManager = nullptr; return NS_OK; } NS_IMETHODIMP nsMsgWindow::GetStatusFeedback(nsIMsgStatusFeedback **aStatusFeedback) { NS_ENSURE_ARG_POINTER(aStatusFeedback); NS_IF_ADDREF(*aStatusFeedback = mStatusFeedback); return NS_OK; } NS_IMETHODIMP nsMsgWindow::SetStatusFeedback(nsIMsgStatusFeedback * aStatusFeedback) { mStatusFeedback = aStatusFeedback; nsCOMPtr messageWindowDocShell; GetMessageWindowDocShell(getter_AddRefs(messageWindowDocShell)); // register our status feedback object as a web progress listener nsCOMPtr webProgress(do_GetInterface(messageWindowDocShell)); if (webProgress && mStatusFeedback && messageWindowDocShell) { nsCOMPtr webProgressListener = do_QueryInterface(mStatusFeedback); webProgress->AddProgressListener(webProgressListener, nsIWebProgress::NOTIFY_ALL); } return NS_OK; } NS_IMETHODIMP nsMsgWindow::SetWindowCommands(nsIMsgWindowCommands * aMsgWindowCommands) { mMsgWindowCommands = aMsgWindowCommands; return NS_OK; } NS_IMETHODIMP nsMsgWindow::GetWindowCommands(nsIMsgWindowCommands **aMsgWindowCommands) { NS_ENSURE_ARG_POINTER(aMsgWindowCommands); NS_IF_ADDREF(*aMsgWindowCommands = mMsgWindowCommands); return NS_OK; } NS_IMETHODIMP nsMsgWindow::GetMsgHeaderSink(nsIMsgHeaderSink * *aMsgHdrSink) { NS_ENSURE_ARG_POINTER(aMsgHdrSink); NS_IF_ADDREF(*aMsgHdrSink = mMsgHeaderSink); return NS_OK; } NS_IMETHODIMP nsMsgWindow::SetMsgHeaderSink(nsIMsgHeaderSink * aMsgHdrSink) { mMsgHeaderSink = aMsgHdrSink; return NS_OK; } NS_IMETHODIMP nsMsgWindow::GetTransactionManager(nsITransactionManager * *aTransactionManager) { NS_ENSURE_ARG_POINTER(aTransactionManager); NS_IF_ADDREF(*aTransactionManager = mTransactionManager); return NS_OK; } NS_IMETHODIMP nsMsgWindow::SetTransactionManager(nsITransactionManager * aTransactionManager) { mTransactionManager = aTransactionManager; return NS_OK; } NS_IMETHODIMP nsMsgWindow::GetOpenFolder(nsIMsgFolder * *aOpenFolder) { NS_ENSURE_ARG_POINTER(aOpenFolder); NS_IF_ADDREF(*aOpenFolder = mOpenFolder); return NS_OK; } NS_IMETHODIMP nsMsgWindow::SetOpenFolder(nsIMsgFolder * aOpenFolder) { mOpenFolder = aOpenFolder; return NS_OK; } NS_IMETHODIMP nsMsgWindow::GetRootDocShell(nsIDocShell * *aDocShell) { if (mRootDocShellWeak) CallQueryReferent(mRootDocShellWeak.get(), aDocShell); else *aDocShell = nullptr; return NS_OK; } NS_IMETHODIMP nsMsgWindow::GetAuthPrompt(nsIAuthPrompt * *aAuthPrompt) { NS_ENSURE_ARG_POINTER(aAuthPrompt); // testing only if (mAuthPrompt) { NS_ADDREF(*aAuthPrompt = mAuthPrompt); return NS_OK; } if (!mRootDocShellWeak) return NS_ERROR_FAILURE; nsresult rv; nsCOMPtr docShell(do_QueryReferent(mRootDocShellWeak.get(), &rv)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr prompt = do_GetInterface(docShell, &rv); NS_ENSURE_SUCCESS(rv, rv); prompt.swap(*aAuthPrompt); return rv; } NS_IMETHODIMP nsMsgWindow::SetAuthPrompt(nsIAuthPrompt* aAuthPrompt) { mAuthPrompt = aAuthPrompt; return NS_OK; } NS_IMETHODIMP nsMsgWindow::SetRootDocShell(nsIDocShell * aDocShell) { nsresult rv; nsCOMPtr contentPolicyListener = do_GetService(NS_MSGCONTENTPOLICY_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); // remove the content policy webProgressListener from the root doc shell // we're currently holding, so we don't keep listening for loads that // we don't care about if (mRootDocShellWeak) { nsCOMPtr oldWebProgress = do_QueryReferent(mRootDocShellWeak, &rv); if (NS_SUCCEEDED(rv)) { rv = oldWebProgress->RemoveProgressListener(contentPolicyListener); NS_ASSERTION(NS_SUCCEEDED(rv), "failed to remove old progress listener"); } } // Query for the doc shell and release it mRootDocShellWeak = nullptr; if (aDocShell) { mRootDocShellWeak = do_GetWeakReference(aDocShell); nsCOMPtr messagePaneDocShell; GetMessageWindowDocShell(getter_AddRefs(messagePaneDocShell)); nsCOMPtr listener(do_GetInterface(messagePaneDocShell)); if (listener) listener->SetParentContentListener(this); // set the contentPolicy webProgressListener on the root docshell for this // window so that it can allow JavaScript for non-message content nsCOMPtr docShellProgress = do_QueryInterface(aDocShell, &rv); NS_ENSURE_SUCCESS(rv, rv); rv = docShellProgress->AddProgressListener(contentPolicyListener, nsIWebProgress::NOTIFY_LOCATION); NS_ENSURE_SUCCESS(rv, rv); } return NS_OK; } NS_IMETHODIMP nsMsgWindow::GetMailCharacterSet(nsACString& aMailCharacterSet) { aMailCharacterSet = mMailCharacterSet; return NS_OK; } NS_IMETHODIMP nsMsgWindow::SetMailCharacterSet(const nsACString& aMailCharacterSet) { mMailCharacterSet.Assign(aMailCharacterSet); // Convert to a canonical charset name instead of using the charset name from the message header as is. // This is needed for charset menu item to have a check mark correctly. nsresult rv; nsCOMPtr ccm = do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); return ccm->GetCharsetAlias(PromiseFlatCString(aMailCharacterSet).get(), mMailCharacterSet); } NS_IMETHODIMP nsMsgWindow::GetCharsetOverride(bool *aCharsetOverride) { NS_ENSURE_ARG_POINTER(aCharsetOverride); *aCharsetOverride = mCharsetOverride; return NS_OK; } NS_IMETHODIMP nsMsgWindow::SetCharsetOverride(bool aCharsetOverride) { mCharsetOverride = aCharsetOverride; return NS_OK; } NS_IMETHODIMP nsMsgWindow::GetDomWindow(mozIDOMWindowProxy **aWindow) { NS_ENSURE_ARG_POINTER(aWindow); if (mDomWindow) CallQueryReferent(mDomWindow.get(), aWindow); else *aWindow = nullptr; return NS_OK; } NS_IMETHODIMP nsMsgWindow::SetDomWindow(mozIDOMWindowProxy * aWindow) { NS_ENSURE_ARG_POINTER(aWindow); mDomWindow = do_GetWeakReference(aWindow); nsCOMPtr win = nsPIDOMWindowOuter::From(aWindow); nsIDocShell *docShell = nullptr; if (win) docShell = win->GetDocShell(); nsCOMPtr docShellAsItem(do_QueryInterface(docShell)); if(docShellAsItem) { nsCOMPtr rootAsItem; docShellAsItem->GetSameTypeRootTreeItem(getter_AddRefs(rootAsItem)); nsCOMPtr rootAsShell(do_QueryInterface(rootAsItem)); SetRootDocShell(rootAsShell); // force ourselves to figure out the message pane nsCOMPtr messageWindowDocShell; GetMessageWindowDocShell(getter_AddRefs(messageWindowDocShell)); } return NS_OK; } NS_IMETHODIMP nsMsgWindow::SetNotificationCallbacks(nsIInterfaceRequestor * aNotificationCallbacks) { mNotificationCallbacks = aNotificationCallbacks; return NS_OK; } NS_IMETHODIMP nsMsgWindow::GetNotificationCallbacks(nsIInterfaceRequestor **aNotificationCallbacks) { NS_ENSURE_ARG_POINTER(aNotificationCallbacks); NS_IF_ADDREF(*aNotificationCallbacks = mNotificationCallbacks); return NS_OK; } NS_IMETHODIMP nsMsgWindow::StopUrls() { m_stopped = true; nsCOMPtr webnav(do_QueryReferent(mRootDocShellWeak)); return webnav ? webnav->Stop(nsIWebNavigation::STOP_NETWORK) : NS_ERROR_FAILURE; } // nsIURIContentListener support NS_IMETHODIMP nsMsgWindow::OnStartURIOpen(nsIURI* aURI, bool* aAbortOpen) { return NS_OK; } NS_IMETHODIMP nsMsgWindow::DoContent(const nsACString& aContentType, bool aIsContentPreferred, nsIRequest *request, nsIStreamListener **aContentHandler, bool *aAbortProcess) { if (!aContentType.IsEmpty()) { // forward the DoContent call to our docshell nsCOMPtr messageWindowDocShell; GetMessageWindowDocShell(getter_AddRefs(messageWindowDocShell)); nsCOMPtr ctnListener = do_QueryInterface(messageWindowDocShell); if (ctnListener) { nsCOMPtr aChannel = do_QueryInterface(request); if (!aChannel) return NS_ERROR_FAILURE; // get the url for the channel...let's hope it is a mailnews url so we can set our msg hdr sink on it.. // right now, this is the only way I can think of to force the msg hdr sink into the mime converter so it can // get too it later... nsCOMPtr uri; aChannel->GetURI(getter_AddRefs(uri)); if (uri) { nsCOMPtr mailnewsUrl(do_QueryInterface(uri)); if (mailnewsUrl) mailnewsUrl->SetMsgWindow(this); } return ctnListener->DoContent(aContentType, aIsContentPreferred, request, aContentHandler, aAbortProcess); } } return NS_OK; } NS_IMETHODIMP nsMsgWindow::IsPreferred(const char * aContentType, char ** aDesiredContentType, bool * aCanHandleContent) { *aCanHandleContent = false; return NS_OK; } NS_IMETHODIMP nsMsgWindow::CanHandleContent(const char * aContentType, bool aIsContentPreferred, char ** aDesiredContentType, bool * aCanHandleContent) { // the mail window knows nothing about the default content types // its docshell can handle...ask the content area if it can handle // the content type... nsCOMPtr messageWindowDocShell; GetMessageWindowDocShell(getter_AddRefs(messageWindowDocShell)); nsCOMPtr ctnListener (do_GetInterface(messageWindowDocShell)); if (ctnListener) return ctnListener->CanHandleContent(aContentType, aIsContentPreferred, aDesiredContentType, aCanHandleContent); else *aCanHandleContent = false; return NS_OK; } NS_IMETHODIMP nsMsgWindow::GetParentContentListener(nsIURIContentListener** aParent) { NS_ENSURE_ARG_POINTER(aParent); *aParent = nullptr; return NS_OK; } NS_IMETHODIMP nsMsgWindow::SetParentContentListener(nsIURIContentListener* aParent) { return NS_OK; } NS_IMETHODIMP nsMsgWindow::GetLoadCookie(nsISupports ** aLoadCookie) { NS_ENSURE_ARG_POINTER(aLoadCookie); *aLoadCookie = nullptr; return NS_OK; } NS_IMETHODIMP nsMsgWindow::SetLoadCookie(nsISupports * aLoadCookie) { return NS_OK; } NS_IMETHODIMP nsMsgWindow::GetPromptDialog(nsIPrompt **aPrompt) { NS_ENSURE_ARG_POINTER(aPrompt); // testing only if (mPromptDialog) { NS_ADDREF(*aPrompt = mPromptDialog); return NS_OK; } nsresult rv; nsCOMPtr rootShell(do_QueryReferent(mRootDocShellWeak, &rv)); if (rootShell) { nsCOMPtr dialog; dialog = do_GetInterface(rootShell, &rv); dialog.swap(*aPrompt); } return rv; } NS_IMETHODIMP nsMsgWindow::SetPromptDialog(nsIPrompt* aPromptDialog) { mPromptDialog = aPromptDialog; return NS_OK; } NS_IMETHODIMP nsMsgWindow::DisplayHTMLInMessagePane(const nsAString& title, const nsAString& body, bool clearMsgHdr) { if (clearMsgHdr && mMsgWindowCommands) mMsgWindowCommands->ClearMsgPane(); nsString htmlStr; htmlStr.Append(NS_LITERAL_STRING("")); htmlStr.Append(body); htmlStr.Append(NS_LITERAL_STRING("")); char *encodedHtml = PL_Base64Encode(NS_ConvertUTF16toUTF8(htmlStr).get(), 0, nullptr); if (!encodedHtml) return NS_ERROR_OUT_OF_MEMORY; nsCString dataSpec; dataSpec = "data:text/html;base64,"; dataSpec += encodedHtml; PR_FREEIF(encodedHtml); nsCOMPtr docShell; GetMessageWindowDocShell(getter_AddRefs(docShell)); NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE); nsCOMPtr webNav(do_QueryInterface(docShell)); NS_ENSURE_TRUE(webNav, NS_ERROR_FAILURE); return webNav->LoadURI(NS_ConvertASCIItoUTF16(dataSpec).get(), nsIWebNavigation::LOAD_FLAGS_NONE, nullptr, nullptr, nullptr); } NS_IMPL_GETSET(nsMsgWindow, Stopped, bool, m_stopped)