1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "nsMsgWindow.h"
7 #include "nsIURILoader.h"
8 #include "nsCURILoader.h"
9 #include "nsIDocShell.h"
10 #include "nsIDocShellTreeItem.h"
11 #include "mozIDOMWindow.h"
12 #include "nsTransactionManagerCID.h"
13 #include "nsIComponentManager.h"
14 #include "nsILoadGroup.h"
15 #include "nsIMsgMailNewsUrl.h"
16 #include "nsIInterfaceRequestor.h"
17 #include "nsIInterfaceRequestorUtils.h"
18 #include "nsIWebProgress.h"
19 #include "nsIWebProgressListener.h"
20 #include "nsPIDOMWindow.h"
21 #include "nsIPrompt.h"
22 #include "nsICharsetConverterManager.h"
23 #include "nsIChannel.h"
24 #include "nsIRequestObserver.h"
25 #include "netCore.h"
26 #include "prmem.h"
27 #include "plbase64.h"
28 #include "nsMsgI18N.h"
29 #include "nsIWebNavigation.h"
30 #include "nsContentUtils.h"
31 #include "nsComponentManagerUtils.h"
32 #include "nsServiceManagerUtils.h"
33 #include "nsIAuthPrompt.h"
34 #include "nsMsgUtils.h"
35 #include "mozilla/dom/Document.h"
36 #include "mozilla/TransactionManager.h"
37 #include "mozilla/dom/LoadURIOptionsBinding.h"
38 #include "mozilla/dom/Element.h"
39 #include "mozilla/dom/XULFrameElement.h"
40 #include "mozilla/Components.h"
41 #include "nsFrameLoader.h"
42 
NS_IMPL_ISUPPORTS(nsMsgWindow,nsIMsgWindow,nsIURIContentListener,nsISupportsWeakReference,nsIMsgWindowTest)43 NS_IMPL_ISUPPORTS(nsMsgWindow, nsIMsgWindow, nsIURIContentListener,
44                   nsISupportsWeakReference, nsIMsgWindowTest)
45 
46 nsMsgWindow::nsMsgWindow() {
47   mCharsetOverride = false;
48   m_stopped = false;
49 }
50 
~nsMsgWindow()51 nsMsgWindow::~nsMsgWindow() { CloseWindow(); }
52 
Init()53 nsresult nsMsgWindow::Init() {
54   // create Undo/Redo Transaction Manager
55   mTransactionManager = new mozilla::TransactionManager();
56   return mTransactionManager->SetMaxTransactionCount(-1);
57 }
58 
GetMessageWindowDocShell(nsIDocShell ** aDocShell)59 NS_IMETHODIMP nsMsgWindow::GetMessageWindowDocShell(nsIDocShell** aDocShell) {
60   *aDocShell = nullptr;
61 
62   nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mMessageWindowDocShellWeak));
63   nsCOMPtr<nsIDocShell> rootShell(do_QueryReferent(mRootDocShellWeak));
64   if (rootShell) {
65     // There seem to be some issues with shutdown (see Bug 1610406).
66     // This workaround should prevent the GetElementById() call dying horribly
67     // but really, we shouldn't even get here in such cases.
68     bool doomed;
69     rootShell->IsBeingDestroyed(&doomed);
70     if (doomed) {
71       return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
72     }
73 
74     RefPtr<mozilla::dom::Element> el =
75         rootShell->GetDocument()->GetElementById(u"messagepane"_ns);
76     RefPtr<mozilla::dom::XULFrameElement> frame =
77         mozilla::dom::XULFrameElement::FromNodeOrNull(el);
78     NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
79     RefPtr<mozilla::dom::Document> doc = frame->GetContentDocument();
80     NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
81     docShell = doc->GetDocShell();
82     NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
83 
84     // we don't own mMessageWindowDocShell so don't try to keep a reference to
85     // it!
86     mMessageWindowDocShellWeak = do_GetWeakReference(docShell);
87   }
88 
89   NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
90   docShell.forget(aDocShell);
91   return NS_OK;
92 }
93 
CloseWindow()94 NS_IMETHODIMP nsMsgWindow::CloseWindow() {
95   mMsgWindowCommands = nullptr;
96   mStatusFeedback = nullptr;
97 
98   StopUrls();
99 
100   nsCOMPtr<nsIDocShell> messagePaneDocShell(
101       do_QueryReferent(mMessageWindowDocShellWeak));
102   if (messagePaneDocShell) {
103     nsCOMPtr<nsIURIContentListener> listener(
104         do_GetInterface(messagePaneDocShell));
105     if (listener) listener->SetParentContentListener(nullptr);
106     SetRootDocShell(nullptr);
107     mMessageWindowDocShellWeak = nullptr;
108   }
109 
110   // in case nsMsgWindow leaks, make sure other stuff doesn't leak.
111   mTransactionManager = nullptr;
112   return NS_OK;
113 }
114 
GetStatusFeedback(nsIMsgStatusFeedback ** aStatusFeedback)115 NS_IMETHODIMP nsMsgWindow::GetStatusFeedback(
116     nsIMsgStatusFeedback** aStatusFeedback) {
117   NS_ENSURE_ARG_POINTER(aStatusFeedback);
118   NS_IF_ADDREF(*aStatusFeedback = mStatusFeedback);
119   return NS_OK;
120 }
121 
SetStatusFeedback(nsIMsgStatusFeedback * aStatusFeedback)122 NS_IMETHODIMP nsMsgWindow::SetStatusFeedback(
123     nsIMsgStatusFeedback* aStatusFeedback) {
124   mStatusFeedback = aStatusFeedback;
125   nsCOMPtr<nsIDocShell> messageWindowDocShell;
126   GetMessageWindowDocShell(getter_AddRefs(messageWindowDocShell));
127 
128   // register our status feedback object as a web progress listener
129   nsCOMPtr<nsIWebProgress> webProgress(do_GetInterface(messageWindowDocShell));
130   if (webProgress && mStatusFeedback && messageWindowDocShell) {
131     nsCOMPtr<nsIWebProgressListener> webProgressListener =
132         do_QueryInterface(mStatusFeedback);
133     webProgress->AddProgressListener(webProgressListener,
134                                      nsIWebProgress::NOTIFY_ALL);
135   }
136   return NS_OK;
137 }
138 
SetWindowCommands(nsIMsgWindowCommands * aMsgWindowCommands)139 NS_IMETHODIMP nsMsgWindow::SetWindowCommands(
140     nsIMsgWindowCommands* aMsgWindowCommands) {
141   mMsgWindowCommands = aMsgWindowCommands;
142   return NS_OK;
143 }
144 
GetWindowCommands(nsIMsgWindowCommands ** aMsgWindowCommands)145 NS_IMETHODIMP nsMsgWindow::GetWindowCommands(
146     nsIMsgWindowCommands** aMsgWindowCommands) {
147   NS_ENSURE_ARG_POINTER(aMsgWindowCommands);
148   NS_IF_ADDREF(*aMsgWindowCommands = mMsgWindowCommands);
149   return NS_OK;
150 }
151 
GetMsgHeaderSink(nsIMsgHeaderSink ** aMsgHdrSink)152 NS_IMETHODIMP nsMsgWindow::GetMsgHeaderSink(nsIMsgHeaderSink** aMsgHdrSink) {
153   NS_ENSURE_ARG_POINTER(aMsgHdrSink);
154   NS_IF_ADDREF(*aMsgHdrSink = mMsgHeaderSink);
155   return NS_OK;
156 }
157 
SetMsgHeaderSink(nsIMsgHeaderSink * aMsgHdrSink)158 NS_IMETHODIMP nsMsgWindow::SetMsgHeaderSink(nsIMsgHeaderSink* aMsgHdrSink) {
159   mMsgHeaderSink = aMsgHdrSink;
160   return NS_OK;
161 }
162 
GetTransactionManager(nsITransactionManager ** aTransactionManager)163 NS_IMETHODIMP nsMsgWindow::GetTransactionManager(
164     nsITransactionManager** aTransactionManager) {
165   NS_ENSURE_ARG_POINTER(aTransactionManager);
166   NS_IF_ADDREF(*aTransactionManager = mTransactionManager);
167   return NS_OK;
168 }
169 
SetTransactionManager(nsITransactionManager * aTransactionManager)170 NS_IMETHODIMP nsMsgWindow::SetTransactionManager(
171     nsITransactionManager* aTransactionManager) {
172   mTransactionManager = aTransactionManager;
173   return NS_OK;
174 }
175 
GetOpenFolder(nsIMsgFolder ** aOpenFolder)176 NS_IMETHODIMP nsMsgWindow::GetOpenFolder(nsIMsgFolder** aOpenFolder) {
177   NS_ENSURE_ARG_POINTER(aOpenFolder);
178   NS_IF_ADDREF(*aOpenFolder = mOpenFolder);
179   return NS_OK;
180 }
181 
SetOpenFolder(nsIMsgFolder * aOpenFolder)182 NS_IMETHODIMP nsMsgWindow::SetOpenFolder(nsIMsgFolder* aOpenFolder) {
183   mOpenFolder = aOpenFolder;
184   return NS_OK;
185 }
186 
GetRootDocShell(nsIDocShell ** aDocShell)187 NS_IMETHODIMP nsMsgWindow::GetRootDocShell(nsIDocShell** aDocShell) {
188   if (mRootDocShellWeak)
189     CallQueryReferent(mRootDocShellWeak.get(), aDocShell);
190   else
191     *aDocShell = nullptr;
192   return NS_OK;
193 }
194 
GetAuthPrompt(nsIAuthPrompt ** aAuthPrompt)195 NS_IMETHODIMP nsMsgWindow::GetAuthPrompt(nsIAuthPrompt** aAuthPrompt) {
196   NS_ENSURE_ARG_POINTER(aAuthPrompt);
197 
198   // testing only
199   if (mAuthPrompt) {
200     NS_ADDREF(*aAuthPrompt = mAuthPrompt);
201     return NS_OK;
202   }
203 
204   if (!mRootDocShellWeak) return NS_ERROR_FAILURE;
205 
206   nsresult rv;
207   nsCOMPtr<nsIDocShell> docShell(
208       do_QueryReferent(mRootDocShellWeak.get(), &rv));
209   NS_ENSURE_SUCCESS(rv, rv);
210 
211   nsCOMPtr<nsIAuthPrompt> prompt = do_GetInterface(docShell, &rv);
212   NS_ENSURE_SUCCESS(rv, rv);
213 
214   prompt.forget(aAuthPrompt);
215 
216   return rv;
217 }
218 
SetAuthPrompt(nsIAuthPrompt * aAuthPrompt)219 NS_IMETHODIMP nsMsgWindow::SetAuthPrompt(nsIAuthPrompt* aAuthPrompt) {
220   mAuthPrompt = aAuthPrompt;
221   return NS_OK;
222 }
223 
SetRootDocShell(nsIDocShell * aDocShell)224 NS_IMETHODIMP nsMsgWindow::SetRootDocShell(nsIDocShell* aDocShell) {
225   // Query for the doc shell and release it
226   mRootDocShellWeak = nullptr;
227   if (aDocShell) {
228     mRootDocShellWeak = do_GetWeakReference(aDocShell);
229 
230     nsCOMPtr<nsIDocShell> messagePaneDocShell;
231     GetMessageWindowDocShell(getter_AddRefs(messagePaneDocShell));
232     nsCOMPtr<nsIURIContentListener> listener(
233         do_GetInterface(messagePaneDocShell));
234     if (listener) listener->SetParentContentListener(this);
235   }
236   return NS_OK;
237 }
238 
GetMailCharacterSet(nsACString & aMailCharacterSet)239 NS_IMETHODIMP nsMsgWindow::GetMailCharacterSet(nsACString& aMailCharacterSet) {
240   aMailCharacterSet = mMailCharacterSet;
241   return NS_OK;
242 }
243 
SetMailCharacterSet(const nsACString & aMailCharacterSet)244 NS_IMETHODIMP nsMsgWindow::SetMailCharacterSet(
245     const nsACString& aMailCharacterSet) {
246   mMailCharacterSet.Assign(aMailCharacterSet);
247 
248   // Convert to a canonical charset name instead of using the charset name from
249   // the message header as is. This is needed for charset menu item to have a
250   // check mark correctly.
251   nsresult rv;
252   nsCOMPtr<nsICharsetConverterManager> ccm =
253       do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
254   NS_ENSURE_SUCCESS(rv, rv);
255 
256   return ccm->GetCharsetAlias(PromiseFlatCString(aMailCharacterSet).get(),
257                               mMailCharacterSet);
258 }
259 
GetCharsetOverride(bool * aCharsetOverride)260 NS_IMETHODIMP nsMsgWindow::GetCharsetOverride(bool* aCharsetOverride) {
261   NS_ENSURE_ARG_POINTER(aCharsetOverride);
262   *aCharsetOverride = mCharsetOverride;
263   return NS_OK;
264 }
265 
SetCharsetOverride(bool aCharsetOverride)266 NS_IMETHODIMP nsMsgWindow::SetCharsetOverride(bool aCharsetOverride) {
267   mCharsetOverride = aCharsetOverride;
268   return NS_OK;
269 }
270 
GetDomWindow(mozIDOMWindowProxy ** aWindow)271 NS_IMETHODIMP nsMsgWindow::GetDomWindow(mozIDOMWindowProxy** aWindow) {
272   NS_ENSURE_ARG_POINTER(aWindow);
273   if (mDomWindow)
274     CallQueryReferent(mDomWindow.get(), aWindow);
275   else
276     *aWindow = nullptr;
277   return NS_OK;
278 }
279 
SetDomWindow(mozIDOMWindowProxy * aWindow)280 NS_IMETHODIMP nsMsgWindow::SetDomWindow(mozIDOMWindowProxy* aWindow) {
281   NS_ENSURE_ARG_POINTER(aWindow);
282   mDomWindow = do_GetWeakReference(aWindow);
283 
284   nsCOMPtr<nsPIDOMWindowOuter> win = nsPIDOMWindowOuter::From(aWindow);
285   nsIDocShell* docShell = nullptr;
286   if (win) docShell = win->GetDocShell();
287 
288   nsCOMPtr<nsIDocShellTreeItem> docShellAsItem(docShell);
289 
290   if (docShellAsItem) {
291     nsCOMPtr<nsIDocShellTreeItem> rootAsItem;
292     docShellAsItem->GetInProcessSameTypeRootTreeItem(
293         getter_AddRefs(rootAsItem));
294 
295     nsCOMPtr<nsIDocShell> rootAsShell(do_QueryInterface(rootAsItem));
296     SetRootDocShell(rootAsShell);
297 
298     // force ourselves to figure out the message pane
299     nsCOMPtr<nsIDocShell> messageWindowDocShell;
300     GetMessageWindowDocShell(getter_AddRefs(messageWindowDocShell));
301   }
302 
303   return NS_OK;
304 }
305 
SetNotificationCallbacks(nsIInterfaceRequestor * aNotificationCallbacks)306 NS_IMETHODIMP nsMsgWindow::SetNotificationCallbacks(
307     nsIInterfaceRequestor* aNotificationCallbacks) {
308   mNotificationCallbacks = aNotificationCallbacks;
309   return NS_OK;
310 }
311 
GetNotificationCallbacks(nsIInterfaceRequestor ** aNotificationCallbacks)312 NS_IMETHODIMP nsMsgWindow::GetNotificationCallbacks(
313     nsIInterfaceRequestor** aNotificationCallbacks) {
314   NS_ENSURE_ARG_POINTER(aNotificationCallbacks);
315   NS_IF_ADDREF(*aNotificationCallbacks = mNotificationCallbacks);
316   return NS_OK;
317 }
318 
StopUrls()319 NS_IMETHODIMP nsMsgWindow::StopUrls() {
320   m_stopped = true;
321   nsCOMPtr<nsIWebNavigation> webnav(do_QueryReferent(mRootDocShellWeak));
322   return webnav ? webnav->Stop(nsIWebNavigation::STOP_NETWORK)
323                 : NS_ERROR_FAILURE;
324 }
325 
326 // nsIURIContentListener support
327 
DoContent(const nsACString & aContentType,bool aIsContentPreferred,nsIRequest * request,nsIStreamListener ** aContentHandler,bool * aAbortProcess)328 NS_IMETHODIMP nsMsgWindow::DoContent(const nsACString& aContentType,
329                                      bool aIsContentPreferred,
330                                      nsIRequest* request,
331                                      nsIStreamListener** aContentHandler,
332                                      bool* aAbortProcess) {
333   if (!aContentType.IsEmpty()) {
334     // forward the DoContent call to our docshell
335     nsCOMPtr<nsIDocShell> messageWindowDocShell;
336     GetMessageWindowDocShell(getter_AddRefs(messageWindowDocShell));
337     nsCOMPtr<nsIURIContentListener> ctnListener =
338         do_QueryInterface(messageWindowDocShell);
339     if (ctnListener) {
340       nsCOMPtr<nsIChannel> aChannel = do_QueryInterface(request);
341       if (!aChannel) return NS_ERROR_FAILURE;
342 
343       // get the url for the channel...let's hope it is a mailnews url so we can
344       // set our msg hdr sink on it.. right now, this is the only way I can
345       // think of to force the msg hdr sink into the mime converter so it can
346       // get too it later...
347       nsCOMPtr<nsIURI> uri;
348       aChannel->GetURI(getter_AddRefs(uri));
349       if (uri) {
350         nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl(do_QueryInterface(uri));
351         if (mailnewsUrl) mailnewsUrl->SetMsgWindow(this);
352       }
353       return ctnListener->DoContent(aContentType, aIsContentPreferred, request,
354                                     aContentHandler, aAbortProcess);
355     }
356   }
357   return NS_OK;
358 }
359 
360 NS_IMETHODIMP
IsPreferred(const char * aContentType,char ** aDesiredContentType,bool * aCanHandleContent)361 nsMsgWindow::IsPreferred(const char* aContentType, char** aDesiredContentType,
362                          bool* aCanHandleContent) {
363   // We don't want to handle opening any attachments inside the
364   // message pane, but want to let nsIExternalHelperAppService take care.
365   *aCanHandleContent = false;
366   return NS_OK;
367 }
368 
CanHandleContent(const char * aContentType,bool aIsContentPreferred,char ** aDesiredContentType,bool * aCanHandleContent)369 NS_IMETHODIMP nsMsgWindow::CanHandleContent(const char* aContentType,
370                                             bool aIsContentPreferred,
371                                             char** aDesiredContentType,
372                                             bool* aCanHandleContent)
373 
374 {
375   // the mail window knows nothing about the default content types
376   // its docshell can handle...ask the content area if it can handle
377   // the content type...
378 
379   nsCOMPtr<nsIDocShell> messageWindowDocShell;
380   GetMessageWindowDocShell(getter_AddRefs(messageWindowDocShell));
381   nsCOMPtr<nsIURIContentListener> ctnListener(
382       do_GetInterface(messageWindowDocShell));
383   if (ctnListener)
384     return ctnListener->CanHandleContent(aContentType, aIsContentPreferred,
385                                          aDesiredContentType,
386                                          aCanHandleContent);
387   else
388     *aCanHandleContent = false;
389   return NS_OK;
390 }
391 
GetParentContentListener(nsIURIContentListener ** aParent)392 NS_IMETHODIMP nsMsgWindow::GetParentContentListener(
393     nsIURIContentListener** aParent) {
394   NS_ENSURE_ARG_POINTER(aParent);
395   *aParent = nullptr;
396   return NS_OK;
397 }
398 
SetParentContentListener(nsIURIContentListener * aParent)399 NS_IMETHODIMP nsMsgWindow::SetParentContentListener(
400     nsIURIContentListener* aParent) {
401   return NS_OK;
402 }
403 
GetLoadCookie(nsISupports ** aLoadCookie)404 NS_IMETHODIMP nsMsgWindow::GetLoadCookie(nsISupports** aLoadCookie) {
405   NS_ENSURE_ARG_POINTER(aLoadCookie);
406   *aLoadCookie = nullptr;
407   return NS_OK;
408 }
409 
SetLoadCookie(nsISupports * aLoadCookie)410 NS_IMETHODIMP nsMsgWindow::SetLoadCookie(nsISupports* aLoadCookie) {
411   return NS_OK;
412 }
413 
GetPromptDialog(nsIPrompt ** aPrompt)414 NS_IMETHODIMP nsMsgWindow::GetPromptDialog(nsIPrompt** aPrompt) {
415   NS_ENSURE_ARG_POINTER(aPrompt);
416 
417   // testing only
418   if (mPromptDialog) {
419     NS_ADDREF(*aPrompt = mPromptDialog);
420     return NS_OK;
421   }
422 
423   nsresult rv;
424   nsCOMPtr<nsIDocShell> rootShell(do_QueryReferent(mRootDocShellWeak, &rv));
425   if (rootShell) {
426     nsCOMPtr<nsIPrompt> dialog;
427     dialog = do_GetInterface(rootShell, &rv);
428     dialog.forget(aPrompt);
429   }
430   return rv;
431 }
432 
SetPromptDialog(nsIPrompt * aPromptDialog)433 NS_IMETHODIMP nsMsgWindow::SetPromptDialog(nsIPrompt* aPromptDialog) {
434   mPromptDialog = aPromptDialog;
435   return NS_OK;
436 }
437 
438 NS_IMETHODIMP
DisplayURIInMessagePane(const nsAString & uri,bool clearMsgHdr,nsIPrincipal * principal)439 nsMsgWindow::DisplayURIInMessagePane(const nsAString& uri, bool clearMsgHdr,
440                                      nsIPrincipal* principal) {
441   if (clearMsgHdr && mMsgWindowCommands) mMsgWindowCommands->ClearMsgPane();
442 
443   nsCOMPtr<nsIDocShell> docShell;
444   GetMessageWindowDocShell(getter_AddRefs(docShell));
445   NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
446 
447   nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(docShell));
448   NS_ENSURE_TRUE(webNav, NS_ERROR_FAILURE);
449 
450   mozilla::dom::LoadURIOptions loadURIOptions;
451   loadURIOptions.mTriggeringPrincipal = principal;
452   return webNav->LoadURI(uri, loadURIOptions);
453 }
454 
455 NS_IMETHODIMP
DisplayHTMLInMessagePane(const nsAString & title,const nsAString & body,bool clearMsgHdr)456 nsMsgWindow::DisplayHTMLInMessagePane(const nsAString& title,
457                                       const nsAString& body, bool clearMsgHdr) {
458   nsString htmlStr;
459   htmlStr.AppendLiteral(
460       u"<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; "
461       u"charset=UTF-8\"></head><body>");
462   htmlStr.Append(body);
463   htmlStr.AppendLiteral(u"</body></html>");
464 
465   char* encodedHtml =
466       PL_Base64Encode(NS_ConvertUTF16toUTF8(htmlStr).get(), 0, nullptr);
467   if (!encodedHtml) return NS_ERROR_OUT_OF_MEMORY;
468 
469   nsCString dataSpec;
470   dataSpec = "data:text/html;base64,";
471   dataSpec += encodedHtml;
472 
473   PR_FREEIF(encodedHtml);
474 
475   return DisplayURIInMessagePane(NS_ConvertASCIItoUTF16(dataSpec), clearMsgHdr,
476                                  nsContentUtils::GetSystemPrincipal());
477 }
478 
479 NS_IMPL_GETSET(nsMsgWindow, Stopped, bool, m_stopped)
480