Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "PCOMContentPermissionRequestChild.h"
6 #include "mozilla/dom/Notification.h"
7 #include "mozilla/dom/AppNotificationServiceOptionsBinding.h"
8 #include "mozilla/dom/OwningNonNull.h"
9 #include "mozilla/dom/Promise.h"
10 #include "mozilla/Preferences.h"
11 #include "TabChild.h"
12 #include "nsContentUtils.h"
13 #include "nsIAlertsService.h"
14 #include "nsIAppsService.h"
15 #include "nsIContentPermissionPrompt.h"
16 #include "nsIDocument.h"
17 #include "nsINotificationStorage.h"
18 #include "nsIPermissionManager.h"
19 #include "nsIUUIDGenerator.h"
20 #include "nsServiceManagerUtils.h"
21 #include "nsToolkitCompsCID.h"
22 #include "nsGlobalWindow.h"
23 #include "nsDOMJSUtils.h"
24 #include "nsIScriptSecurityManager.h"
25 #include "mozilla/dom/PermissionMessageUtils.h"
26 #include "nsContentPermissionHelper.h"
27 #ifdef MOZ_B2G
28 #include "nsIDOMDesktopNotification.h"
29 #endif
31 namespace mozilla {
32 namespace dom {
34 class NotificationStorageCallback MOZ_FINAL : public nsINotificationStorageCallback
35 {
36 public:
37 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
38 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(NotificationStorageCallback)
40 NotificationStorageCallback(const GlobalObject& aGlobal, nsPIDOMWindow* aWindow, Promise* aPromise)
41 : mCount(0),
42 mGlobal(aGlobal.Get()),
43 mWindow(aWindow),
44 mPromise(aPromise)
45 {
46 MOZ_ASSERT(aWindow);
47 MOZ_ASSERT(aPromise);
48 JSContext* cx = aGlobal.GetContext();
49 JSAutoCompartment ac(cx, mGlobal);
50 mNotifications = JS_NewArrayObject(cx, 0);
51 HoldData();
52 }
54 NS_IMETHOD Handle(const nsAString& aID,
55 const nsAString& aTitle,
56 const nsAString& aDir,
57 const nsAString& aLang,
58 const nsAString& aBody,
59 const nsAString& aTag,
60 const nsAString& aIcon,
61 JSContext* aCx)
62 {
63 MOZ_ASSERT(!aID.IsEmpty());
65 NotificationOptions options;
66 options.mDir = Notification::StringToDirection(nsString(aDir));
67 options.mLang = aLang;
68 options.mBody = aBody;
69 options.mTag = aTag;
70 options.mIcon = aIcon;
71 nsRefPtr<Notification> notification = Notification::CreateInternal(mWindow,
72 aID,
73 aTitle,
74 options);
75 JSAutoCompartment ac(aCx, mGlobal);
76 JS::Rooted<JSObject*> element(aCx, notification->WrapObject(aCx));
77 NS_ENSURE_TRUE(element, NS_ERROR_FAILURE);
79 if (!JS_DefineElement(aCx, mNotifications, mCount++,
80 JS::ObjectValue(*element), nullptr, nullptr, 0)) {
81 return NS_ERROR_FAILURE;
82 }
83 return NS_OK;
84 }
86 NS_IMETHOD Done(JSContext* aCx)
87 {
88 JSAutoCompartment ac(aCx, mGlobal);
89 JS::Rooted<JS::Value> result(aCx, JS::ObjectValue(*mNotifications));
90 mPromise->MaybeResolve(aCx, result);
91 return NS_OK;
92 }
94 private:
95 ~NotificationStorageCallback()
96 {
97 DropData();
98 }
100 void HoldData()
101 {
102 mozilla::HoldJSObjects(this);
103 }
105 void DropData()
106 {
107 mGlobal = nullptr;
108 mNotifications = nullptr;
109 mozilla::DropJSObjects(this);
110 }
112 uint32_t mCount;
113 JS::Heap<JSObject *> mGlobal;
114 nsCOMPtr<nsPIDOMWindow> mWindow;
115 nsRefPtr<Promise> mPromise;
116 JS::Heap<JSObject *> mNotifications;
117 };
119 NS_IMPL_CYCLE_COLLECTING_ADDREF(NotificationStorageCallback)
120 NS_IMPL_CYCLE_COLLECTING_RELEASE(NotificationStorageCallback)
121 NS_IMPL_CYCLE_COLLECTION_CLASS(NotificationStorageCallback)
123 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(NotificationStorageCallback)
124 NS_INTERFACE_MAP_ENTRY(nsINotificationStorageCallback)
125 NS_INTERFACE_MAP_ENTRY(nsISupports)
126 NS_INTERFACE_MAP_END
128 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(NotificationStorageCallback)
129 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mGlobal)
130 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mNotifications)
131 NS_IMPL_CYCLE_COLLECTION_TRACE_END
133 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(NotificationStorageCallback)
134 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
135 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
136 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromise)
137 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
139 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(NotificationStorageCallback)
140 NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
141 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromise)
142 tmp->DropData();
143 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
145 class NotificationPermissionRequest : public nsIContentPermissionRequest,
146 public PCOMContentPermissionRequestChild,
147 public nsIRunnable
148 {
149 public:
150 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
151 NS_DECL_NSICONTENTPERMISSIONREQUEST
152 NS_DECL_NSIRUNNABLE
153 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(NotificationPermissionRequest,
154 nsIContentPermissionRequest)
156 NotificationPermissionRequest(nsIPrincipal* aPrincipal, nsPIDOMWindow* aWindow,
157 NotificationPermissionCallback* aCallback)
158 : mPrincipal(aPrincipal), mWindow(aWindow),
159 mPermission(NotificationPermission::Default),
160 mCallback(aCallback) {}
162 virtual ~NotificationPermissionRequest() {}
164 bool Recv__delete__(const bool& aAllow,
165 const InfallibleTArray<PermissionChoice>& choices);
166 void IPDLRelease() { Release(); }
168 protected:
169 nsresult CallCallback();
170 nsresult DispatchCallback();
171 nsCOMPtr<nsIPrincipal> mPrincipal;
172 nsCOMPtr<nsPIDOMWindow> mWindow;
173 NotificationPermission mPermission;
174 nsRefPtr<NotificationPermissionCallback> mCallback;
175 };
177 class NotificationObserver : public nsIObserver
178 {
179 public:
180 NS_DECL_ISUPPORTS
181 NS_DECL_NSIOBSERVER
183 NotificationObserver(Notification* aNotification)
184 : mNotification(aNotification) {}
186 virtual ~NotificationObserver() {}
188 protected:
189 nsRefPtr<Notification> mNotification;
190 };
192 class NotificationTask : public nsIRunnable
193 {
194 public:
195 NS_DECL_ISUPPORTS
196 NS_DECL_NSIRUNNABLE
198 enum NotificationAction {
199 eShow,
200 eClose
201 };
203 NotificationTask(Notification* aNotification, NotificationAction aAction)
204 : mNotification(aNotification), mAction(aAction) {}
206 virtual ~NotificationTask() {}
208 protected:
209 nsRefPtr<Notification> mNotification;
210 NotificationAction mAction;
211 };
213 uint32_t Notification::sCount = 0;
215 NS_IMPL_CYCLE_COLLECTION(NotificationPermissionRequest, mWindow)
217 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(NotificationPermissionRequest)
218 NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
219 NS_INTERFACE_MAP_ENTRY(nsIRunnable)
220 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest)
221 NS_INTERFACE_MAP_END
223 NS_IMPL_CYCLE_COLLECTING_ADDREF(NotificationPermissionRequest)
224 NS_IMPL_CYCLE_COLLECTING_RELEASE(NotificationPermissionRequest)
226 NS_IMETHODIMP
227 NotificationPermissionRequest::Run()
228 {
229 if (nsContentUtils::IsSystemPrincipal(mPrincipal)) {
230 mPermission = NotificationPermission::Granted;
231 } else {
232 // File are automatically granted permission.
233 nsCOMPtr<nsIURI> uri;
234 mPrincipal->GetURI(getter_AddRefs(uri));
236 if (uri) {
237 bool isFile;
238 uri->SchemeIs("file", &isFile);
239 if (isFile) {
240 mPermission = NotificationPermission::Granted;
241 }
242 }
243 }
245 // Grant permission if pref'ed on.
246 if (Preferences::GetBool("notification.prompt.testing", false)) {
247 if (Preferences::GetBool("notification.prompt.testing.allow", true)) {
248 mPermission = NotificationPermission::Granted;
249 } else {
250 mPermission = NotificationPermission::Denied;
251 }
252 }
254 if (mPermission != NotificationPermission::Default) {
255 return DispatchCallback();
256 }
258 if (XRE_GetProcessType() == GeckoProcessType_Content) {
259 // because owner implements nsITabChild, we can assume that it is
260 // the one and only TabChild.
261 TabChild* child = TabChild::GetFrom(mWindow->GetDocShell());
262 if (!child) {
263 return NS_ERROR_NOT_AVAILABLE;
264 }
266 // Retain a reference so the object isn't deleted without IPDL's knowledge.
267 // Corresponding release occurs in DeallocPContentPermissionRequest.
268 AddRef();
270 nsTArray<PermissionRequest> permArray;
271 nsTArray<nsString> emptyOptions;
272 permArray.AppendElement(PermissionRequest(
273 NS_LITERAL_CSTRING("desktop-notification"),
274 NS_LITERAL_CSTRING("unused"),
275 emptyOptions));
276 child->SendPContentPermissionRequestConstructor(this, permArray,
277 IPC::Principal(mPrincipal));
279 Sendprompt();
280 return NS_OK;
281 }
283 nsCOMPtr<nsIContentPermissionPrompt> prompt =
284 do_GetService(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
285 if (prompt) {
286 prompt->Prompt(this);
287 }
289 return NS_OK;
290 }
292 NS_IMETHODIMP
293 NotificationPermissionRequest::GetPrincipal(nsIPrincipal** aRequestingPrincipal)
294 {
295 NS_ADDREF(*aRequestingPrincipal = mPrincipal);
296 return NS_OK;
297 }
299 NS_IMETHODIMP
300 NotificationPermissionRequest::GetWindow(nsIDOMWindow** aRequestingWindow)
301 {
302 NS_ADDREF(*aRequestingWindow = mWindow);
303 return NS_OK;
304 }
306 NS_IMETHODIMP
307 NotificationPermissionRequest::GetElement(nsIDOMElement** aElement)
308 {
309 NS_ENSURE_ARG_POINTER(aElement);
310 *aElement = nullptr;
311 return NS_OK;
312 }
314 NS_IMETHODIMP
315 NotificationPermissionRequest::Cancel()
316 {
317 mPermission = NotificationPermission::Denied;
318 return DispatchCallback();
319 }
321 NS_IMETHODIMP
322 NotificationPermissionRequest::Allow(JS::HandleValue aChoices)
323 {
324 MOZ_ASSERT(aChoices.isUndefined());
326 mPermission = NotificationPermission::Granted;
327 return DispatchCallback();
328 }
330 inline nsresult
331 NotificationPermissionRequest::DispatchCallback()
332 {
333 if (!mCallback) {
334 return NS_OK;
335 }
337 nsCOMPtr<nsIRunnable> callbackRunnable = NS_NewRunnableMethod(this,
338 &NotificationPermissionRequest::CallCallback);
339 return NS_DispatchToMainThread(callbackRunnable);
340 }
342 nsresult
343 NotificationPermissionRequest::CallCallback()
344 {
345 ErrorResult rv;
346 mCallback->Call(mPermission, rv);
347 return rv.ErrorCode();
348 }
350 NS_IMETHODIMP
351 NotificationPermissionRequest::GetTypes(nsIArray** aTypes)
352 {
353 nsTArray<nsString> emptyOptions;
354 return CreatePermissionArray(NS_LITERAL_CSTRING("desktop-notification"),
355 NS_LITERAL_CSTRING("unused"),
356 emptyOptions,
357 aTypes);
358 }
360 bool
361 NotificationPermissionRequest::Recv__delete__(const bool& aAllow,
362 const InfallibleTArray<PermissionChoice>& choices)
363 {
364 MOZ_ASSERT(choices.IsEmpty(), "Notification doesn't support permission choice");
366 if (aAllow) {
367 (void) Allow(JS::UndefinedHandleValue);
368 } else {
369 (void) Cancel();
370 }
371 return true;
372 }
374 NS_IMPL_ISUPPORTS(NotificationTask, nsIRunnable)
376 NS_IMETHODIMP
377 NotificationTask::Run()
378 {
379 switch (mAction) {
380 case eShow:
381 mNotification->ShowInternal();
382 break;
383 case eClose:
384 mNotification->CloseInternal();
385 break;
386 default:
387 MOZ_CRASH("Unexpected action for NotificationTask.");
388 }
389 return NS_OK;
390 }
392 NS_IMPL_ISUPPORTS(NotificationObserver, nsIObserver)
394 NS_IMETHODIMP
395 NotificationObserver::Observe(nsISupports* aSubject, const char* aTopic,
396 const char16_t* aData)
397 {
398 if (!strcmp("alertclickcallback", aTopic)) {
399 nsCOMPtr<nsPIDOMWindow> window = mNotification->GetOwner();
400 nsIDocument* doc = window ? window->GetExtantDoc() : nullptr;
401 if (doc) {
402 nsContentUtils::DispatchChromeEvent(doc, window,
403 NS_LITERAL_STRING("DOMWebNotificationClicked"),
404 true, true);
405 }
406 mNotification->DispatchTrustedEvent(NS_LITERAL_STRING("click"));
407 } else if (!strcmp("alertfinished", aTopic)) {
408 mNotification->mIsClosed = true;
409 mNotification->DispatchTrustedEvent(NS_LITERAL_STRING("close"));
410 } else if (!strcmp("alertshow", aTopic)) {
411 mNotification->DispatchTrustedEvent(NS_LITERAL_STRING("show"));
412 }
414 return NS_OK;
415 }
417 Notification::Notification(const nsAString& aID, const nsAString& aTitle, const nsAString& aBody,
418 NotificationDirection aDir, const nsAString& aLang,
419 const nsAString& aTag, const nsAString& aIconUrl,
420 nsPIDOMWindow* aWindow)
421 : DOMEventTargetHelper(aWindow),
422 mID(aID), mTitle(aTitle), mBody(aBody), mDir(aDir), mLang(aLang),
423 mTag(aTag), mIconUrl(aIconUrl), mIsClosed(false)
424 {
425 }
427 // static
428 already_AddRefed<Notification>
429 Notification::Constructor(const GlobalObject& aGlobal,
430 const nsAString& aTitle,
431 const NotificationOptions& aOptions,
432 ErrorResult& aRv)
433 {
434 MOZ_ASSERT(NS_IsMainThread());
435 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
436 MOZ_ASSERT(window, "Window should not be null.");
437 nsRefPtr<Notification> notification = CreateInternal(window,
438 EmptyString(),
439 aTitle,
440 aOptions);
442 // Queue a task to show the notification.
443 nsCOMPtr<nsIRunnable> showNotificationTask =
444 new NotificationTask(notification, NotificationTask::eShow);
445 NS_DispatchToCurrentThread(showNotificationTask);
447 // Persist the notification.
448 nsresult rv;
449 nsCOMPtr<nsINotificationStorage> notificationStorage =
450 do_GetService(NS_NOTIFICATION_STORAGE_CONTRACTID, &rv);
451 if (NS_FAILED(rv)) {
452 aRv.Throw(rv);
453 return nullptr;
454 }
456 nsString origin;
457 aRv = GetOrigin(window, origin);
458 if (aRv.Failed()) {
459 return nullptr;
460 }
462 nsString id;
463 notification->GetID(id);
464 aRv = notificationStorage->Put(origin,
465 id,
466 aTitle,
467 DirectionToString(aOptions.mDir),
468 aOptions.mLang,
469 aOptions.mBody,
470 aOptions.mTag,
471 aOptions.mIcon);
472 if (aRv.Failed()) {
473 return nullptr;
474 }
476 return notification.forget();
477 }
479 already_AddRefed<Notification>
480 Notification::CreateInternal(nsPIDOMWindow* aWindow,
481 const nsAString& aID,
482 const nsAString& aTitle,
483 const NotificationOptions& aOptions)
484 {
485 nsString id;
486 if (!aID.IsEmpty()) {
487 id = aID;
488 } else {
489 nsCOMPtr<nsIUUIDGenerator> uuidgen =
490 do_GetService("@mozilla.org/uuid-generator;1");
491 NS_ENSURE_TRUE(uuidgen, nullptr);
492 nsID uuid;
493 nsresult rv = uuidgen->GenerateUUIDInPlace(&uuid);
494 NS_ENSURE_SUCCESS(rv, nullptr);
496 char buffer[NSID_LENGTH];
497 uuid.ToProvidedString(buffer);
498 NS_ConvertASCIItoUTF16 convertedID(buffer);
499 id = convertedID;
500 }
502 nsRefPtr<Notification> notification = new Notification(id,
503 aTitle,
504 aOptions.mBody,
505 aOptions.mDir,
506 aOptions.mLang,
507 aOptions.mTag,
508 aOptions.mIcon,
509 aWindow);
510 return notification.forget();
511 }
513 nsIPrincipal*
514 Notification::GetPrincipal()
515 {
516 nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(GetOwner());
517 NS_ENSURE_TRUE(sop, nullptr);
518 return sop->GetPrincipal();
519 }
521 void
522 Notification::ShowInternal()
523 {
524 nsCOMPtr<nsIAlertsService> alertService =
525 do_GetService(NS_ALERTSERVICE_CONTRACTID);
527 ErrorResult result;
528 if (GetPermissionInternal(GetOwner(), result) !=
529 NotificationPermission::Granted || !alertService) {
530 // We do not have permission to show a notification or alert service
531 // is not available.
532 DispatchTrustedEvent(NS_LITERAL_STRING("error"));
533 return;
534 }
536 nsresult rv;
537 nsAutoString absoluteUrl;
538 if (mIconUrl.Length() > 0) {
539 // Resolve image URL against document base URI.
540 nsIDocument* doc = GetOwner()->GetExtantDoc();
541 if (doc) {
542 nsCOMPtr<nsIURI> baseUri = doc->GetBaseURI();
543 if (baseUri) {
544 nsCOMPtr<nsIURI> srcUri;
545 rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(srcUri),
546 mIconUrl, doc, baseUri);
547 if (NS_SUCCEEDED(rv)) {
548 nsAutoCString src;
549 srcUri->GetSpec(src);
550 absoluteUrl = NS_ConvertUTF8toUTF16(src);
551 }
552 }
554 }
555 }
557 nsCOMPtr<nsIObserver> observer = new NotificationObserver(this);
559 nsString alertName;
560 rv = GetAlertName(alertName);
561 NS_ENSURE_SUCCESS_VOID(rv);
563 #ifdef MOZ_B2G
564 nsCOMPtr<nsIAppNotificationService> appNotifier =
565 do_GetService("@mozilla.org/system-alerts-service;1");
566 if (appNotifier) {
567 nsCOMPtr<nsPIDOMWindow> window = GetOwner();
568 uint32_t appId = (window.get())->GetDoc()->NodePrincipal()->GetAppId();
570 if (appId != nsIScriptSecurityManager::UNKNOWN_APP_ID) {
571 nsCOMPtr<nsIAppsService> appsService = do_GetService("@mozilla.org/AppsService;1");
572 nsString manifestUrl = EmptyString();
573 rv = appsService->GetManifestURLByLocalId(appId, manifestUrl);
574 if (NS_SUCCEEDED(rv)) {
575 mozilla::AutoSafeJSContext cx;
576 JS::Rooted<JS::Value> val(cx);
577 AppNotificationServiceOptions ops;
578 ops.mTextClickable = true;
579 ops.mManifestURL = manifestUrl;
580 ops.mId = alertName;
581 ops.mDbId = mID;
582 ops.mDir = DirectionToString(mDir);
583 ops.mLang = mLang;
584 ops.mTag = mTag;
586 if (!ops.ToObject(cx, &val)) {
587 NS_WARNING("Converting dict to object failed!");
588 return;
589 }
591 appNotifier->ShowAppNotification(mIconUrl, mTitle, mBody,
592 observer, val);
593 return;
594 }
595 }
596 }
597 #endif
599 // In the case of IPC, the parent process uses the cookie to map to
600 // nsIObserver. Thus the cookie must be unique to differentiate observers.
601 nsString uniqueCookie = NS_LITERAL_STRING("notification:");
602 uniqueCookie.AppendInt(sCount++);
603 alertService->ShowAlertNotification(absoluteUrl, mTitle, mBody, true,
604 uniqueCookie, observer, alertName,
605 DirectionToString(mDir), mLang,
606 GetPrincipal());
607 }
609 void
610 Notification::RequestPermission(const GlobalObject& aGlobal,
611 const Optional<OwningNonNull<NotificationPermissionCallback> >& aCallback,
612 ErrorResult& aRv)
613 {
614 // Get principal from global to make permission request for notifications.
615 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
616 nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aGlobal.GetAsSupports());
617 if (!sop) {
618 aRv.Throw(NS_ERROR_UNEXPECTED);
619 return;
620 }
621 nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
623 NotificationPermissionCallback* permissionCallback = nullptr;
624 if (aCallback.WasPassed()) {
625 permissionCallback = &aCallback.Value();
626 }
627 nsCOMPtr<nsIRunnable> request =
628 new NotificationPermissionRequest(principal, window, permissionCallback);
630 NS_DispatchToMainThread(request);
631 }
633 NotificationPermission
634 Notification::GetPermission(const GlobalObject& aGlobal, ErrorResult& aRv)
635 {
636 return GetPermissionInternal(aGlobal.GetAsSupports(), aRv);
637 }
639 NotificationPermission
640 Notification::GetPermissionInternal(nsISupports* aGlobal, ErrorResult& aRv)
641 {
642 // Get principal from global to check permission for notifications.
643 nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aGlobal);
644 if (!sop) {
645 aRv.Throw(NS_ERROR_UNEXPECTED);
646 return NotificationPermission::Denied;
647 }
649 nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
650 if (nsContentUtils::IsSystemPrincipal(principal)) {
651 return NotificationPermission::Granted;
652 } else {
653 // Allow files to show notifications by default.
654 nsCOMPtr<nsIURI> uri;
655 principal->GetURI(getter_AddRefs(uri));
656 if (uri) {
657 bool isFile;
658 uri->SchemeIs("file", &isFile);
659 if (isFile) {
660 return NotificationPermission::Granted;
661 }
662 }
663 }
665 // We also allow notifications is they are pref'ed on.
666 if (Preferences::GetBool("notification.prompt.testing", false)) {
667 if (Preferences::GetBool("notification.prompt.testing.allow", true)) {
668 return NotificationPermission::Granted;
669 } else {
670 return NotificationPermission::Denied;
671 }
672 }
674 uint32_t permission = nsIPermissionManager::UNKNOWN_ACTION;
676 nsCOMPtr<nsIPermissionManager> permissionManager =
677 do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
679 permissionManager->TestPermissionFromPrincipal(principal,
680 "desktop-notification",
681 &permission);
683 // Convert the result to one of the enum types.
684 switch (permission) {
685 case nsIPermissionManager::ALLOW_ACTION:
686 return NotificationPermission::Granted;
687 case nsIPermissionManager::DENY_ACTION:
688 return NotificationPermission::Denied;
689 default:
690 return NotificationPermission::Default;
691 }
692 }
694 already_AddRefed<Promise>
695 Notification::Get(const GlobalObject& aGlobal,
696 const GetNotificationOptions& aFilter,
697 ErrorResult& aRv)
698 {
699 nsCOMPtr<nsIGlobalObject> global =
700 do_QueryInterface(aGlobal.GetAsSupports());
701 MOZ_ASSERT(global);
702 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(global);
703 MOZ_ASSERT(window);
704 nsIDocument* doc = window->GetExtantDoc();
705 if (!doc) {
706 aRv.Throw(NS_ERROR_UNEXPECTED);
707 return nullptr;
708 }
710 nsString origin;
711 aRv = GetOrigin(window, origin);
712 if (aRv.Failed()) {
713 return nullptr;
714 }
716 nsresult rv;
717 nsCOMPtr<nsINotificationStorage> notificationStorage =
718 do_GetService(NS_NOTIFICATION_STORAGE_CONTRACTID, &rv);
719 if (NS_FAILED(rv)) {
720 aRv.Throw(rv);
721 return nullptr;
722 }
724 nsRefPtr<Promise> promise = new Promise(global);
725 nsCOMPtr<nsINotificationStorageCallback> callback =
726 new NotificationStorageCallback(aGlobal, window, promise);
727 nsString tag = aFilter.mTag.WasPassed() ?
728 aFilter.mTag.Value() :
729 EmptyString();
730 aRv = notificationStorage->Get(origin, tag, callback);
731 if (aRv.Failed()) {
732 return nullptr;
733 }
735 return promise.forget();
736 }
738 JSObject*
739 Notification::WrapObject(JSContext* aCx)
740 {
741 return mozilla::dom::NotificationBinding::Wrap(aCx, this);
742 }
744 void
745 Notification::Close()
746 {
747 // Queue a task to close the notification.
748 nsCOMPtr<nsIRunnable> closeNotificationTask =
749 new NotificationTask(this, NotificationTask::eClose);
750 NS_DispatchToMainThread(closeNotificationTask);
751 }
753 void
754 Notification::CloseInternal()
755 {
756 if (!mIsClosed) {
757 nsresult rv;
758 // Don't bail out if notification storage fails, since we still
759 // want to send the close event through the alert service.
760 nsCOMPtr<nsINotificationStorage> notificationStorage =
761 do_GetService(NS_NOTIFICATION_STORAGE_CONTRACTID);
762 if (notificationStorage) {
763 nsString origin;
764 rv = GetOrigin(GetOwner(), origin);
765 if (NS_SUCCEEDED(rv)) {
766 notificationStorage->Delete(origin, mID);
767 }
768 }
770 nsCOMPtr<nsIAlertsService> alertService =
771 do_GetService(NS_ALERTSERVICE_CONTRACTID);
772 if (alertService) {
773 nsString alertName;
774 rv = GetAlertName(alertName);
775 if (NS_SUCCEEDED(rv)) {
776 alertService->CloseAlert(alertName, GetPrincipal());
777 }
778 }
779 }
780 }
782 nsresult
783 Notification::GetOrigin(nsPIDOMWindow* aWindow, nsString& aOrigin)
784 {
785 if (!aWindow) {
786 return NS_ERROR_FAILURE;
787 }
788 nsresult rv;
789 nsIDocument* doc = aWindow->GetExtantDoc();
790 NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED);
791 nsIPrincipal* principal = doc->NodePrincipal();
792 NS_ENSURE_TRUE(principal, NS_ERROR_UNEXPECTED);
794 uint16_t appStatus = principal->GetAppStatus();
795 uint32_t appId = principal->GetAppId();
797 if (appStatus == nsIPrincipal::APP_STATUS_NOT_INSTALLED ||
798 appId == nsIScriptSecurityManager::NO_APP_ID ||
799 appId == nsIScriptSecurityManager::UNKNOWN_APP_ID) {
800 rv = nsContentUtils::GetUTFOrigin(principal, aOrigin);
801 NS_ENSURE_SUCCESS(rv, rv);
802 } else {
803 // If we are in "app code", use manifest URL as unique origin since
804 // multiple apps can share the same origin but not same notifications.
805 nsCOMPtr<nsIAppsService> appsService =
806 do_GetService("@mozilla.org/AppsService;1", &rv);
807 NS_ENSURE_SUCCESS(rv, rv);
808 appsService->GetManifestURLByLocalId(appId, aOrigin);
809 }
811 return NS_OK;
812 }
814 nsresult
815 Notification::GetAlertName(nsString& aAlertName)
816 {
817 // Get the notification name that is unique per origin + tag/ID.
818 // The name of the alert is of the form origin#tag/ID.
819 nsresult rv = GetOrigin(GetOwner(), aAlertName);
820 NS_ENSURE_SUCCESS(rv, rv);
821 aAlertName.AppendLiteral("#");
822 if (!mTag.IsEmpty()) {
823 aAlertName.Append(NS_LITERAL_STRING("tag:"));
824 aAlertName.Append(mTag);
825 } else {
826 aAlertName.Append(NS_LITERAL_STRING("notag:"));
827 aAlertName.Append(mID);
828 }
829 return NS_OK;
830 }
832 } // namespace dom
833 } // namespace mozilla