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