1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sts=2 sw=2 et cin: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "ToastNotificationHandler.h"
8 
9 #include "WidgetUtils.h"
10 #include "WinTaskbar.h"
11 #include "WinUtils.h"
12 #include "imgIContainer.h"
13 #include "imgIRequest.h"
14 #include "mozilla/WindowsVersion.h"
15 #include "mozilla/gfx/2D.h"
16 #include "nsDirectoryServiceDefs.h"
17 #include "nsIStringBundle.h"
18 #include "nsIURI.h"
19 #include "nsIUUIDGenerator.h"
20 #include "nsIWidget.h"
21 #include "nsIWindowMediator.h"
22 #include "nsNetUtil.h"
23 #include "nsPIDOMWindow.h"
24 #include "nsProxyRelease.h"
25 
26 #include "ToastNotification.h"
27 
28 namespace mozilla {
29 namespace widget {
30 
31 typedef ABI::Windows::Foundation::ITypedEventHandler<
32     ABI::Windows::UI::Notifications::ToastNotification*, IInspectable*>
33     ToastActivationHandler;
34 typedef ABI::Windows::Foundation::ITypedEventHandler<
35     ABI::Windows::UI::Notifications::ToastNotification*,
36     ABI::Windows::UI::Notifications::ToastDismissedEventArgs*>
37     ToastDismissedHandler;
38 typedef ABI::Windows::Foundation::ITypedEventHandler<
39     ABI::Windows::UI::Notifications::ToastNotification*,
40     ABI::Windows::UI::Notifications::ToastFailedEventArgs*>
41     ToastFailedHandler;
42 
43 using namespace ABI::Windows::Data::Xml::Dom;
44 using namespace ABI::Windows::Foundation;
45 using namespace ABI::Windows::UI::Notifications;
46 using namespace Microsoft::WRL;
47 using namespace Microsoft::WRL::Wrappers;
48 using namespace mozilla;
49 
NS_IMPL_ISUPPORTS(ToastNotificationHandler,nsIAlertNotificationImageListener)50 NS_IMPL_ISUPPORTS(ToastNotificationHandler, nsIAlertNotificationImageListener)
51 
52 static bool SetNodeValueString(const nsString& aString, IXmlNode* node,
53                                IXmlDocument* xml) {
54   ComPtr<IXmlText> inputText;
55   if (NS_WARN_IF(FAILED(xml->CreateTextNode(
56           HStringReference(static_cast<const wchar_t*>(aString.get())).Get(),
57           &inputText)))) {
58     return false;
59   }
60   ComPtr<IXmlNode> inputTextNode;
61   if (NS_WARN_IF(FAILED(inputText.As(&inputTextNode)))) {
62     return false;
63   }
64   ComPtr<IXmlNode> appendedChild;
65   if (NS_WARN_IF(
66           FAILED(node->AppendChild(inputTextNode.Get(), &appendedChild)))) {
67     return false;
68   }
69   return true;
70 }
71 
SetAttribute(IXmlElement * element,const HSTRING name,const nsAString & value)72 static bool SetAttribute(IXmlElement* element, const HSTRING name,
73                          const nsAString& value) {
74   HSTRING valueStr = HStringReference(static_cast<const wchar_t*>(
75                                           PromiseFlatString(value).get()))
76                          .Get();
77   if (NS_WARN_IF(FAILED(element->SetAttribute(name, valueStr)))) {
78     return false;
79   }
80   return true;
81 }
82 
AddActionNode(IXmlDocument * toastXml,IXmlNode * actionsNode,const nsAString & actionTitle,const nsAString & actionArgs)83 static bool AddActionNode(IXmlDocument* toastXml, IXmlNode* actionsNode,
84                           const nsAString& actionTitle,
85                           const nsAString& actionArgs) {
86   ComPtr<IXmlElement> action;
87   HRESULT hr =
88       toastXml->CreateElement(HStringReference(L"action").Get(), &action);
89   if (NS_WARN_IF(FAILED(hr))) {
90     return false;
91   }
92 
93   if (NS_WARN_IF(!SetAttribute(action.Get(), HStringReference(L"content").Get(),
94                                actionTitle))) {
95     return false;
96   }
97 
98   if (NS_WARN_IF(!SetAttribute(
99           action.Get(), HStringReference(L"arguments").Get(), actionArgs))) {
100     return false;
101   }
102   if (NS_WARN_IF(!SetAttribute(action.Get(),
103                                HStringReference(L"placement").Get(),
104                                u"contextmenu"_ns))) {
105     return false;
106   }
107 
108   // Add <action> to <actions>
109   ComPtr<IXmlNode> actionNode;
110   hr = action.As(&actionNode);
111   if (NS_WARN_IF(FAILED(hr))) {
112     return false;
113   }
114 
115   ComPtr<IXmlNode> appendedChild;
116   hr = actionsNode->AppendChild(actionNode.Get(), &appendedChild);
117   if (NS_WARN_IF(FAILED(hr))) {
118     return false;
119   }
120 
121   return true;
122 }
123 
124 static ComPtr<IToastNotificationManagerStatics>
GetToastNotificationManagerStatics()125 GetToastNotificationManagerStatics() {
126   ComPtr<IToastNotificationManagerStatics> toastNotificationManagerStatics;
127   if (NS_WARN_IF(FAILED(GetActivationFactory(
128           HStringReference(
129               RuntimeClass_Windows_UI_Notifications_ToastNotificationManager)
130               .Get(),
131           &toastNotificationManagerStatics)))) {
132     return nullptr;
133   }
134 
135   return toastNotificationManagerStatics;
136 }
137 
~ToastNotificationHandler()138 ToastNotificationHandler::~ToastNotificationHandler() {
139   if (mImageRequest) {
140     mImageRequest->Cancel(NS_BINDING_ABORTED);
141     mImageRequest = nullptr;
142   }
143 
144   if (mHasImage) {
145     DebugOnly<nsresult> rv = mImageFile->Remove(false);
146     NS_ASSERTION(NS_SUCCEEDED(rv), "Cannot remove temporary image file");
147   }
148 
149   UnregisterHandler();
150 }
151 
UnregisterHandler()152 void ToastNotificationHandler::UnregisterHandler() {
153   if (mNotification && mNotifier) {
154     mNotification->remove_Dismissed(mDismissedToken);
155     mNotification->remove_Activated(mActivatedToken);
156     mNotification->remove_Failed(mFailedToken);
157     mNotifier->Hide(mNotification.Get());
158   }
159 
160   mNotification = nullptr;
161   mNotifier = nullptr;
162 
163   SendFinished();
164 }
165 
InitializeXmlForTemplate(ToastTemplateType templateType)166 ComPtr<IXmlDocument> ToastNotificationHandler::InitializeXmlForTemplate(
167     ToastTemplateType templateType) {
168   ComPtr<IToastNotificationManagerStatics> toastNotificationManagerStatics =
169       GetToastNotificationManagerStatics();
170 
171   ComPtr<IXmlDocument> toastXml;
172   toastNotificationManagerStatics->GetTemplateContent(templateType, &toastXml);
173 
174   return toastXml;
175 }
176 
InitAlertAsync(nsIAlertNotification * aAlert)177 nsresult ToastNotificationHandler::InitAlertAsync(
178     nsIAlertNotification* aAlert) {
179   return aAlert->LoadImage(/* aTimeout = */ 0, this, /* aUserData = */ nullptr,
180                            getter_AddRefs(mImageRequest));
181 }
182 
ShowAlert()183 bool ToastNotificationHandler::ShowAlert() {
184   if (!mBackend->IsActiveHandler(mName, this)) {
185     return true;
186   }
187 
188   ToastTemplateType toastTemplate;
189   if (mHostPort.IsEmpty()) {
190     toastTemplate =
191         mHasImage ? ToastTemplateType::ToastTemplateType_ToastImageAndText03
192                   : ToastTemplateType::ToastTemplateType_ToastText03;
193   } else {
194     toastTemplate =
195         mHasImage ? ToastTemplateType::ToastTemplateType_ToastImageAndText04
196                   : ToastTemplateType::ToastTemplateType_ToastText04;
197   }
198 
199   ComPtr<IXmlDocument> toastXml = InitializeXmlForTemplate(toastTemplate);
200   if (!toastXml) {
201     return false;
202   }
203 
204   HRESULT hr;
205 
206   if (mHasImage) {
207     ComPtr<IXmlNodeList> toastImageElements;
208     hr = toastXml->GetElementsByTagName(HStringReference(L"image").Get(),
209                                         &toastImageElements);
210     if (NS_WARN_IF(FAILED(hr))) {
211       return false;
212     }
213     ComPtr<IXmlNode> imageNode;
214     hr = toastImageElements->Item(0, &imageNode);
215     if (NS_WARN_IF(FAILED(hr))) {
216       return false;
217     }
218     ComPtr<IXmlElement> image;
219     hr = imageNode.As(&image);
220     if (NS_WARN_IF(FAILED(hr))) {
221       return false;
222     }
223     if (NS_WARN_IF(!SetAttribute(image.Get(), HStringReference(L"src").Get(),
224                                  mImageUri))) {
225       return false;
226     }
227   }
228 
229   ComPtr<IXmlNodeList> toastTextElements;
230   hr = toastXml->GetElementsByTagName(HStringReference(L"text").Get(),
231                                       &toastTextElements);
232   if (NS_WARN_IF(FAILED(hr))) {
233     return false;
234   }
235 
236   ComPtr<IXmlNode> titleTextNodeRoot;
237   hr = toastTextElements->Item(0, &titleTextNodeRoot);
238   if (NS_WARN_IF(FAILED(hr))) {
239     return false;
240   }
241   ComPtr<IXmlNode> msgTextNodeRoot;
242   hr = toastTextElements->Item(1, &msgTextNodeRoot);
243   if (NS_WARN_IF(FAILED(hr))) {
244     return false;
245   }
246 
247   if (NS_WARN_IF(!SetNodeValueString(mTitle, titleTextNodeRoot.Get(),
248                                      toastXml.Get()))) {
249     return false;
250   }
251   if (NS_WARN_IF(
252           !SetNodeValueString(mMsg, msgTextNodeRoot.Get(), toastXml.Get()))) {
253     return false;
254   }
255 
256   ComPtr<IXmlNodeList> toastElements;
257   hr = toastXml->GetElementsByTagName(HStringReference(L"toast").Get(),
258                                       &toastElements);
259   if (NS_WARN_IF(FAILED(hr))) {
260     return false;
261   }
262 
263   ComPtr<IXmlNode> toastNodeRoot;
264   hr = toastElements->Item(0, &toastNodeRoot);
265   if (NS_WARN_IF(FAILED(hr))) {
266     return false;
267   }
268 
269   ComPtr<IXmlElement> actions;
270   hr = toastXml->CreateElement(HStringReference(L"actions").Get(), &actions);
271   if (NS_WARN_IF(FAILED(hr))) {
272     return false;
273   }
274 
275   ComPtr<IXmlNode> actionsNode;
276   hr = actions.As(&actionsNode);
277   if (NS_WARN_IF(FAILED(hr))) {
278     return false;
279   }
280 
281   nsCOMPtr<nsIStringBundleService> sbs =
282       do_GetService(NS_STRINGBUNDLE_CONTRACTID);
283   if (NS_WARN_IF(!sbs)) {
284     return false;
285   }
286 
287   nsCOMPtr<nsIStringBundle> bundle;
288   sbs->CreateBundle("chrome://alerts/locale/alert.properties",
289                     getter_AddRefs(bundle));
290   if (NS_WARN_IF(!bundle)) {
291     return false;
292   }
293 
294   if (!mHostPort.IsEmpty()) {
295     AutoTArray<nsString, 1> formatStrings = {mHostPort};
296 
297     ComPtr<IXmlNode> urlTextNodeRoot;
298     hr = toastTextElements->Item(2, &urlTextNodeRoot);
299     if (NS_WARN_IF(FAILED(hr))) {
300       return false;
301     }
302 
303     nsAutoString urlReference;
304     bundle->FormatStringFromName("source.label", formatStrings, urlReference);
305 
306     if (NS_WARN_IF(!SetNodeValueString(urlReference, urlTextNodeRoot.Get(),
307                                        toastXml.Get()))) {
308       return false;
309     }
310 
311     if (IsWin10AnniversaryUpdateOrLater()) {
312       ComPtr<IXmlElement> placementText;
313       hr = urlTextNodeRoot.As(&placementText);
314       if (SUCCEEDED(hr)) {
315         // placement is supported on Windows 10 Anniversary Update or later
316         SetAttribute(placementText.Get(), HStringReference(L"placement").Get(),
317                      u"attribution"_ns);
318       }
319     }
320 
321     nsAutoString disableButtonTitle;
322     bundle->FormatStringFromName("webActions.disableForOrigin.label",
323                                  formatStrings, disableButtonTitle);
324 
325     AddActionNode(toastXml.Get(), actionsNode.Get(), disableButtonTitle,
326                   u"snooze"_ns);
327   }
328 
329   nsAutoString settingsButtonTitle;
330   bundle->GetStringFromName("webActions.settings.label", settingsButtonTitle);
331   AddActionNode(toastXml.Get(), actionsNode.Get(), settingsButtonTitle,
332                 u"settings"_ns);
333 
334   ComPtr<IXmlNode> appendedChild;
335   hr = toastNodeRoot->AppendChild(actionsNode.Get(), &appendedChild);
336   if (NS_WARN_IF(FAILED(hr))) {
337     return false;
338   }
339 
340   return CreateWindowsNotificationFromXml(toastXml.Get());
341 }
342 
CreateWindowsNotificationFromXml(IXmlDocument * aXml)343 bool ToastNotificationHandler::CreateWindowsNotificationFromXml(
344     IXmlDocument* aXml) {
345   ComPtr<IToastNotificationFactory> factory;
346   HRESULT hr = GetActivationFactory(
347       HStringReference(RuntimeClass_Windows_UI_Notifications_ToastNotification)
348           .Get(),
349       &factory);
350   if (NS_WARN_IF(FAILED(hr))) {
351     return false;
352   }
353 
354   hr = factory->CreateToastNotification(aXml, &mNotification);
355   if (NS_WARN_IF(FAILED(hr))) {
356     return false;
357   }
358 
359   RefPtr<ToastNotificationHandler> self = this;
360 
361   hr = mNotification->add_Activated(
362       Callback<ToastActivationHandler>([self](IToastNotification* aNotification,
363                                               IInspectable* aInspectable) {
364         return self->OnActivate(aNotification, aInspectable);
365       }).Get(),
366       &mActivatedToken);
367   if (NS_WARN_IF(FAILED(hr))) {
368     return false;
369   }
370 
371   hr = mNotification->add_Dismissed(
372       Callback<ToastDismissedHandler>([self](IToastNotification* aNotification,
373                                              IToastDismissedEventArgs* aArgs) {
374         return self->OnDismiss(aNotification, aArgs);
375       }).Get(),
376       &mDismissedToken);
377   if (NS_WARN_IF(FAILED(hr))) {
378     return false;
379   }
380 
381   hr = mNotification->add_Failed(
382       Callback<ToastFailedHandler>([self](IToastNotification* aNotification,
383                                           IToastFailedEventArgs* aArgs) {
384         return self->OnFail(aNotification, aArgs);
385       }).Get(),
386       &mFailedToken);
387   if (NS_WARN_IF(FAILED(hr))) {
388     return false;
389   }
390 
391   ComPtr<IToastNotificationManagerStatics> toastNotificationManagerStatics =
392       GetToastNotificationManagerStatics();
393   if (NS_WARN_IF(!toastNotificationManagerStatics)) {
394     return false;
395   }
396 
397   nsAutoString uid;
398   if (NS_WARN_IF(!WinTaskbar::GetAppUserModelID(uid))) {
399     return false;
400   }
401 
402   HSTRING uidStr =
403       HStringReference(static_cast<const wchar_t*>(uid.get())).Get();
404   hr = toastNotificationManagerStatics->CreateToastNotifierWithId(uidStr,
405                                                                   &mNotifier);
406   if (NS_WARN_IF(FAILED(hr))) {
407     return false;
408   }
409 
410   hr = mNotifier->Show(mNotification.Get());
411   if (NS_WARN_IF(FAILED(hr))) {
412     return false;
413   }
414 
415   if (mAlertListener) {
416     mAlertListener->Observe(nullptr, "alertshow", mCookie.get());
417   }
418 
419   return true;
420 }
421 
SendFinished()422 void ToastNotificationHandler::SendFinished() {
423   if (!mSentFinished && mAlertListener) {
424     mAlertListener->Observe(nullptr, "alertfinished", mCookie.get());
425   }
426 
427   mSentFinished = true;
428 }
429 
430 HRESULT
OnActivate(IToastNotification * notification,IInspectable * inspectable)431 ToastNotificationHandler::OnActivate(IToastNotification* notification,
432                                      IInspectable* inspectable) {
433   if (mAlertListener) {
434     nsAutoString argString;
435     if (inspectable) {
436       ComPtr<IToastActivatedEventArgs> eventArgs;
437       HRESULT hr = inspectable->QueryInterface(
438           __uuidof(IToastActivatedEventArgs), (void**)&eventArgs);
439       if (SUCCEEDED(hr)) {
440         HSTRING arguments;
441         hr = eventArgs->get_Arguments(&arguments);
442         if (SUCCEEDED(hr)) {
443           uint32_t len = 0;
444           const wchar_t* buffer = WindowsGetStringRawBuffer(arguments, &len);
445           if (buffer) {
446             argString.Assign(buffer, len);
447           }
448         }
449       }
450     }
451 
452     if (argString.EqualsLiteral("settings")) {
453       mAlertListener->Observe(nullptr, "alertsettingscallback", mCookie.get());
454     } else if (argString.EqualsLiteral("snooze")) {
455       mAlertListener->Observe(nullptr, "alertdisablecallback", mCookie.get());
456     } else if (mClickable) {
457       // When clicking toast, focus moves to another process, but we want to set
458       // focus on Firefox process.
459       nsCOMPtr<nsIWindowMediator> winMediator(
460           do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
461       if (winMediator) {
462         nsCOMPtr<mozIDOMWindowProxy> navWin;
463         winMediator->GetMostRecentWindow(u"navigator:browser",
464                                          getter_AddRefs(navWin));
465         if (navWin) {
466           nsCOMPtr<nsIWidget> widget =
467               WidgetUtils::DOMWindowToWidget(nsPIDOMWindowOuter::From(navWin));
468           if (widget) {
469             SetForegroundWindow(
470                 static_cast<HWND>(widget->GetNativeData(NS_NATIVE_WINDOW)));
471           }
472         }
473       }
474       mAlertListener->Observe(nullptr, "alertclickcallback", mCookie.get());
475     }
476   }
477   mBackend->RemoveHandler(mName, this);
478   return S_OK;
479 }
480 
481 HRESULT
OnDismiss(IToastNotification * notification,IToastDismissedEventArgs * aArgs)482 ToastNotificationHandler::OnDismiss(IToastNotification* notification,
483                                     IToastDismissedEventArgs* aArgs) {
484   SendFinished();
485   mBackend->RemoveHandler(mName, this);
486   return S_OK;
487 }
488 
489 HRESULT
OnFail(IToastNotification * notification,IToastFailedEventArgs * aArgs)490 ToastNotificationHandler::OnFail(IToastNotification* notification,
491                                  IToastFailedEventArgs* aArgs) {
492   SendFinished();
493   mBackend->RemoveHandler(mName, this);
494   return S_OK;
495 }
496 
TryShowAlert()497 nsresult ToastNotificationHandler::TryShowAlert() {
498   if (NS_WARN_IF(!ShowAlert())) {
499     mBackend->RemoveHandler(mName, this);
500     return NS_ERROR_FAILURE;
501   }
502   return NS_OK;
503 }
504 
505 NS_IMETHODIMP
OnImageMissing(nsISupports *)506 ToastNotificationHandler::OnImageMissing(nsISupports*) {
507   return TryShowAlert();
508 }
509 
510 NS_IMETHODIMP
OnImageReady(nsISupports *,imgIRequest * aRequest)511 ToastNotificationHandler::OnImageReady(nsISupports*, imgIRequest* aRequest) {
512   nsresult rv = AsyncSaveImage(aRequest);
513   if (NS_FAILED(rv)) {
514     return TryShowAlert();
515   }
516   return rv;
517 }
518 
AsyncSaveImage(imgIRequest * aRequest)519 nsresult ToastNotificationHandler::AsyncSaveImage(imgIRequest* aRequest) {
520   nsresult rv =
521       NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(mImageFile));
522   if (NS_WARN_IF(NS_FAILED(rv))) {
523     return rv;
524   }
525 
526   rv = mImageFile->Append(u"notificationimages"_ns);
527   if (NS_WARN_IF(NS_FAILED(rv))) {
528     return rv;
529   }
530 
531   rv = mImageFile->Create(nsIFile::DIRECTORY_TYPE, 0500);
532   if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS) {
533     return rv;
534   }
535 
536   nsCOMPtr<nsIUUIDGenerator> idGen =
537       do_GetService("@mozilla.org/uuid-generator;1", &rv);
538   if (NS_WARN_IF(NS_FAILED(rv))) {
539     return rv;
540   }
541 
542   nsID uuid;
543   rv = idGen->GenerateUUIDInPlace(&uuid);
544   if (NS_WARN_IF(NS_FAILED(rv))) {
545     return rv;
546   }
547 
548   char uuidChars[NSID_LENGTH];
549   uuid.ToProvidedString(uuidChars);
550   // Remove the brackets at the beginning and ending of the generated UUID.
551   nsAutoCString uuidStr(Substring(uuidChars + 1, uuidChars + NSID_LENGTH - 2));
552   uuidStr.AppendLiteral(".bmp");
553   mImageFile->AppendNative(uuidStr);
554 
555   nsCOMPtr<imgIContainer> imgContainer;
556   rv = aRequest->GetImage(getter_AddRefs(imgContainer));
557   if (NS_WARN_IF(NS_FAILED(rv))) {
558     return rv;
559   }
560 
561   nsMainThreadPtrHandle<ToastNotificationHandler> self(
562       new nsMainThreadPtrHolder<ToastNotificationHandler>(
563           "ToastNotificationHandler", this));
564 
565   nsCOMPtr<nsIFile> imageFile(mImageFile);
566   RefPtr<mozilla::gfx::SourceSurface> surface = imgContainer->GetFrame(
567       imgIContainer::FRAME_FIRST,
568       imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY);
569   nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
570       "ToastNotificationHandler::AsyncWriteBitmap",
571       [self, imageFile, surface]() -> void {
572         nsresult rv;
573         if (!surface) {
574           rv = NS_ERROR_FAILURE;
575         } else {
576           rv = WinUtils::WriteBitmap(imageFile, surface);
577         }
578 
579         nsCOMPtr<nsIRunnable> cbRunnable = NS_NewRunnableFunction(
580             "ToastNotificationHandler::AsyncWriteBitmapCb",
581             [self, rv]() -> void {
582               auto handler = const_cast<ToastNotificationHandler*>(self.get());
583               handler->OnWriteBitmapFinished(rv);
584             });
585 
586         NS_DispatchToMainThread(cbRunnable);
587       });
588 
589   return mBackend->BackgroundDispatch(r);
590 }
591 
OnWriteBitmapFinished(nsresult rv)592 void ToastNotificationHandler::OnWriteBitmapFinished(nsresult rv) {
593   if (NS_SUCCEEDED(rv)) {
594     OnWriteBitmapSuccess();
595   }
596   TryShowAlert();
597 }
598 
OnWriteBitmapSuccess()599 nsresult ToastNotificationHandler::OnWriteBitmapSuccess() {
600   nsresult rv;
601 
602   nsCOMPtr<nsIURI> fileURI;
603   rv = NS_NewFileURI(getter_AddRefs(fileURI), mImageFile);
604   if (NS_WARN_IF(NS_FAILED(rv))) {
605     return rv;
606   }
607 
608   nsAutoCString uriStr;
609   rv = fileURI->GetSpec(uriStr);
610   if (NS_WARN_IF(NS_FAILED(rv))) {
611     return rv;
612   }
613 
614   AppendUTF8toUTF16(uriStr, mImageUri);
615 
616   mHasImage = true;
617 
618   return NS_OK;
619 }
620 
621 }  // namespace widget
622 }  // namespace mozilla
623