1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/src/notification/Notification.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,834 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#include "PCOMContentPermissionRequestChild.h" 1.9 +#include "mozilla/dom/Notification.h" 1.10 +#include "mozilla/dom/AppNotificationServiceOptionsBinding.h" 1.11 +#include "mozilla/dom/OwningNonNull.h" 1.12 +#include "mozilla/dom/Promise.h" 1.13 +#include "mozilla/Preferences.h" 1.14 +#include "TabChild.h" 1.15 +#include "nsContentUtils.h" 1.16 +#include "nsIAlertsService.h" 1.17 +#include "nsIAppsService.h" 1.18 +#include "nsIContentPermissionPrompt.h" 1.19 +#include "nsIDocument.h" 1.20 +#include "nsINotificationStorage.h" 1.21 +#include "nsIPermissionManager.h" 1.22 +#include "nsIUUIDGenerator.h" 1.23 +#include "nsServiceManagerUtils.h" 1.24 +#include "nsToolkitCompsCID.h" 1.25 +#include "nsGlobalWindow.h" 1.26 +#include "nsDOMJSUtils.h" 1.27 +#include "nsIScriptSecurityManager.h" 1.28 +#include "mozilla/dom/PermissionMessageUtils.h" 1.29 +#include "nsContentPermissionHelper.h" 1.30 +#ifdef MOZ_B2G 1.31 +#include "nsIDOMDesktopNotification.h" 1.32 +#endif 1.33 + 1.34 +namespace mozilla { 1.35 +namespace dom { 1.36 + 1.37 +class NotificationStorageCallback MOZ_FINAL : public nsINotificationStorageCallback 1.38 +{ 1.39 +public: 1.40 + NS_DECL_CYCLE_COLLECTING_ISUPPORTS 1.41 + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(NotificationStorageCallback) 1.42 + 1.43 + NotificationStorageCallback(const GlobalObject& aGlobal, nsPIDOMWindow* aWindow, Promise* aPromise) 1.44 + : mCount(0), 1.45 + mGlobal(aGlobal.Get()), 1.46 + mWindow(aWindow), 1.47 + mPromise(aPromise) 1.48 + { 1.49 + MOZ_ASSERT(aWindow); 1.50 + MOZ_ASSERT(aPromise); 1.51 + JSContext* cx = aGlobal.GetContext(); 1.52 + JSAutoCompartment ac(cx, mGlobal); 1.53 + mNotifications = JS_NewArrayObject(cx, 0); 1.54 + HoldData(); 1.55 + } 1.56 + 1.57 + NS_IMETHOD Handle(const nsAString& aID, 1.58 + const nsAString& aTitle, 1.59 + const nsAString& aDir, 1.60 + const nsAString& aLang, 1.61 + const nsAString& aBody, 1.62 + const nsAString& aTag, 1.63 + const nsAString& aIcon, 1.64 + JSContext* aCx) 1.65 + { 1.66 + MOZ_ASSERT(!aID.IsEmpty()); 1.67 + 1.68 + NotificationOptions options; 1.69 + options.mDir = Notification::StringToDirection(nsString(aDir)); 1.70 + options.mLang = aLang; 1.71 + options.mBody = aBody; 1.72 + options.mTag = aTag; 1.73 + options.mIcon = aIcon; 1.74 + nsRefPtr<Notification> notification = Notification::CreateInternal(mWindow, 1.75 + aID, 1.76 + aTitle, 1.77 + options); 1.78 + JSAutoCompartment ac(aCx, mGlobal); 1.79 + JS::Rooted<JSObject*> element(aCx, notification->WrapObject(aCx)); 1.80 + NS_ENSURE_TRUE(element, NS_ERROR_FAILURE); 1.81 + 1.82 + if (!JS_DefineElement(aCx, mNotifications, mCount++, 1.83 + JS::ObjectValue(*element), nullptr, nullptr, 0)) { 1.84 + return NS_ERROR_FAILURE; 1.85 + } 1.86 + return NS_OK; 1.87 + } 1.88 + 1.89 + NS_IMETHOD Done(JSContext* aCx) 1.90 + { 1.91 + JSAutoCompartment ac(aCx, mGlobal); 1.92 + JS::Rooted<JS::Value> result(aCx, JS::ObjectValue(*mNotifications)); 1.93 + mPromise->MaybeResolve(aCx, result); 1.94 + return NS_OK; 1.95 + } 1.96 + 1.97 +private: 1.98 + ~NotificationStorageCallback() 1.99 + { 1.100 + DropData(); 1.101 + } 1.102 + 1.103 + void HoldData() 1.104 + { 1.105 + mozilla::HoldJSObjects(this); 1.106 + } 1.107 + 1.108 + void DropData() 1.109 + { 1.110 + mGlobal = nullptr; 1.111 + mNotifications = nullptr; 1.112 + mozilla::DropJSObjects(this); 1.113 + } 1.114 + 1.115 + uint32_t mCount; 1.116 + JS::Heap<JSObject *> mGlobal; 1.117 + nsCOMPtr<nsPIDOMWindow> mWindow; 1.118 + nsRefPtr<Promise> mPromise; 1.119 + JS::Heap<JSObject *> mNotifications; 1.120 +}; 1.121 + 1.122 +NS_IMPL_CYCLE_COLLECTING_ADDREF(NotificationStorageCallback) 1.123 +NS_IMPL_CYCLE_COLLECTING_RELEASE(NotificationStorageCallback) 1.124 +NS_IMPL_CYCLE_COLLECTION_CLASS(NotificationStorageCallback) 1.125 + 1.126 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(NotificationStorageCallback) 1.127 + NS_INTERFACE_MAP_ENTRY(nsINotificationStorageCallback) 1.128 + NS_INTERFACE_MAP_ENTRY(nsISupports) 1.129 +NS_INTERFACE_MAP_END 1.130 + 1.131 +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(NotificationStorageCallback) 1.132 + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mGlobal) 1.133 + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mNotifications) 1.134 +NS_IMPL_CYCLE_COLLECTION_TRACE_END 1.135 + 1.136 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(NotificationStorageCallback) 1.137 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS 1.138 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow) 1.139 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromise) 1.140 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 1.141 + 1.142 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(NotificationStorageCallback) 1.143 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow) 1.144 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromise) 1.145 + tmp->DropData(); 1.146 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END 1.147 + 1.148 +class NotificationPermissionRequest : public nsIContentPermissionRequest, 1.149 + public PCOMContentPermissionRequestChild, 1.150 + public nsIRunnable 1.151 +{ 1.152 +public: 1.153 + NS_DECL_CYCLE_COLLECTING_ISUPPORTS 1.154 + NS_DECL_NSICONTENTPERMISSIONREQUEST 1.155 + NS_DECL_NSIRUNNABLE 1.156 + NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(NotificationPermissionRequest, 1.157 + nsIContentPermissionRequest) 1.158 + 1.159 + NotificationPermissionRequest(nsIPrincipal* aPrincipal, nsPIDOMWindow* aWindow, 1.160 + NotificationPermissionCallback* aCallback) 1.161 + : mPrincipal(aPrincipal), mWindow(aWindow), 1.162 + mPermission(NotificationPermission::Default), 1.163 + mCallback(aCallback) {} 1.164 + 1.165 + virtual ~NotificationPermissionRequest() {} 1.166 + 1.167 + bool Recv__delete__(const bool& aAllow, 1.168 + const InfallibleTArray<PermissionChoice>& choices); 1.169 + void IPDLRelease() { Release(); } 1.170 + 1.171 +protected: 1.172 + nsresult CallCallback(); 1.173 + nsresult DispatchCallback(); 1.174 + nsCOMPtr<nsIPrincipal> mPrincipal; 1.175 + nsCOMPtr<nsPIDOMWindow> mWindow; 1.176 + NotificationPermission mPermission; 1.177 + nsRefPtr<NotificationPermissionCallback> mCallback; 1.178 +}; 1.179 + 1.180 +class NotificationObserver : public nsIObserver 1.181 +{ 1.182 +public: 1.183 + NS_DECL_ISUPPORTS 1.184 + NS_DECL_NSIOBSERVER 1.185 + 1.186 + NotificationObserver(Notification* aNotification) 1.187 + : mNotification(aNotification) {} 1.188 + 1.189 + virtual ~NotificationObserver() {} 1.190 + 1.191 +protected: 1.192 + nsRefPtr<Notification> mNotification; 1.193 +}; 1.194 + 1.195 +class NotificationTask : public nsIRunnable 1.196 +{ 1.197 +public: 1.198 + NS_DECL_ISUPPORTS 1.199 + NS_DECL_NSIRUNNABLE 1.200 + 1.201 + enum NotificationAction { 1.202 + eShow, 1.203 + eClose 1.204 + }; 1.205 + 1.206 + NotificationTask(Notification* aNotification, NotificationAction aAction) 1.207 + : mNotification(aNotification), mAction(aAction) {} 1.208 + 1.209 + virtual ~NotificationTask() {} 1.210 + 1.211 +protected: 1.212 + nsRefPtr<Notification> mNotification; 1.213 + NotificationAction mAction; 1.214 +}; 1.215 + 1.216 +uint32_t Notification::sCount = 0; 1.217 + 1.218 +NS_IMPL_CYCLE_COLLECTION(NotificationPermissionRequest, mWindow) 1.219 + 1.220 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(NotificationPermissionRequest) 1.221 + NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest) 1.222 + NS_INTERFACE_MAP_ENTRY(nsIRunnable) 1.223 + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest) 1.224 +NS_INTERFACE_MAP_END 1.225 + 1.226 +NS_IMPL_CYCLE_COLLECTING_ADDREF(NotificationPermissionRequest) 1.227 +NS_IMPL_CYCLE_COLLECTING_RELEASE(NotificationPermissionRequest) 1.228 + 1.229 +NS_IMETHODIMP 1.230 +NotificationPermissionRequest::Run() 1.231 +{ 1.232 + if (nsContentUtils::IsSystemPrincipal(mPrincipal)) { 1.233 + mPermission = NotificationPermission::Granted; 1.234 + } else { 1.235 + // File are automatically granted permission. 1.236 + nsCOMPtr<nsIURI> uri; 1.237 + mPrincipal->GetURI(getter_AddRefs(uri)); 1.238 + 1.239 + if (uri) { 1.240 + bool isFile; 1.241 + uri->SchemeIs("file", &isFile); 1.242 + if (isFile) { 1.243 + mPermission = NotificationPermission::Granted; 1.244 + } 1.245 + } 1.246 + } 1.247 + 1.248 + // Grant permission if pref'ed on. 1.249 + if (Preferences::GetBool("notification.prompt.testing", false)) { 1.250 + if (Preferences::GetBool("notification.prompt.testing.allow", true)) { 1.251 + mPermission = NotificationPermission::Granted; 1.252 + } else { 1.253 + mPermission = NotificationPermission::Denied; 1.254 + } 1.255 + } 1.256 + 1.257 + if (mPermission != NotificationPermission::Default) { 1.258 + return DispatchCallback(); 1.259 + } 1.260 + 1.261 + if (XRE_GetProcessType() == GeckoProcessType_Content) { 1.262 + // because owner implements nsITabChild, we can assume that it is 1.263 + // the one and only TabChild. 1.264 + TabChild* child = TabChild::GetFrom(mWindow->GetDocShell()); 1.265 + if (!child) { 1.266 + return NS_ERROR_NOT_AVAILABLE; 1.267 + } 1.268 + 1.269 + // Retain a reference so the object isn't deleted without IPDL's knowledge. 1.270 + // Corresponding release occurs in DeallocPContentPermissionRequest. 1.271 + AddRef(); 1.272 + 1.273 + nsTArray<PermissionRequest> permArray; 1.274 + nsTArray<nsString> emptyOptions; 1.275 + permArray.AppendElement(PermissionRequest( 1.276 + NS_LITERAL_CSTRING("desktop-notification"), 1.277 + NS_LITERAL_CSTRING("unused"), 1.278 + emptyOptions)); 1.279 + child->SendPContentPermissionRequestConstructor(this, permArray, 1.280 + IPC::Principal(mPrincipal)); 1.281 + 1.282 + Sendprompt(); 1.283 + return NS_OK; 1.284 + } 1.285 + 1.286 + nsCOMPtr<nsIContentPermissionPrompt> prompt = 1.287 + do_GetService(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID); 1.288 + if (prompt) { 1.289 + prompt->Prompt(this); 1.290 + } 1.291 + 1.292 + return NS_OK; 1.293 +} 1.294 + 1.295 +NS_IMETHODIMP 1.296 +NotificationPermissionRequest::GetPrincipal(nsIPrincipal** aRequestingPrincipal) 1.297 +{ 1.298 + NS_ADDREF(*aRequestingPrincipal = mPrincipal); 1.299 + return NS_OK; 1.300 +} 1.301 + 1.302 +NS_IMETHODIMP 1.303 +NotificationPermissionRequest::GetWindow(nsIDOMWindow** aRequestingWindow) 1.304 +{ 1.305 + NS_ADDREF(*aRequestingWindow = mWindow); 1.306 + return NS_OK; 1.307 +} 1.308 + 1.309 +NS_IMETHODIMP 1.310 +NotificationPermissionRequest::GetElement(nsIDOMElement** aElement) 1.311 +{ 1.312 + NS_ENSURE_ARG_POINTER(aElement); 1.313 + *aElement = nullptr; 1.314 + return NS_OK; 1.315 +} 1.316 + 1.317 +NS_IMETHODIMP 1.318 +NotificationPermissionRequest::Cancel() 1.319 +{ 1.320 + mPermission = NotificationPermission::Denied; 1.321 + return DispatchCallback(); 1.322 +} 1.323 + 1.324 +NS_IMETHODIMP 1.325 +NotificationPermissionRequest::Allow(JS::HandleValue aChoices) 1.326 +{ 1.327 + MOZ_ASSERT(aChoices.isUndefined()); 1.328 + 1.329 + mPermission = NotificationPermission::Granted; 1.330 + return DispatchCallback(); 1.331 +} 1.332 + 1.333 +inline nsresult 1.334 +NotificationPermissionRequest::DispatchCallback() 1.335 +{ 1.336 + if (!mCallback) { 1.337 + return NS_OK; 1.338 + } 1.339 + 1.340 + nsCOMPtr<nsIRunnable> callbackRunnable = NS_NewRunnableMethod(this, 1.341 + &NotificationPermissionRequest::CallCallback); 1.342 + return NS_DispatchToMainThread(callbackRunnable); 1.343 +} 1.344 + 1.345 +nsresult 1.346 +NotificationPermissionRequest::CallCallback() 1.347 +{ 1.348 + ErrorResult rv; 1.349 + mCallback->Call(mPermission, rv); 1.350 + return rv.ErrorCode(); 1.351 +} 1.352 + 1.353 +NS_IMETHODIMP 1.354 +NotificationPermissionRequest::GetTypes(nsIArray** aTypes) 1.355 +{ 1.356 + nsTArray<nsString> emptyOptions; 1.357 + return CreatePermissionArray(NS_LITERAL_CSTRING("desktop-notification"), 1.358 + NS_LITERAL_CSTRING("unused"), 1.359 + emptyOptions, 1.360 + aTypes); 1.361 +} 1.362 + 1.363 +bool 1.364 +NotificationPermissionRequest::Recv__delete__(const bool& aAllow, 1.365 + const InfallibleTArray<PermissionChoice>& choices) 1.366 +{ 1.367 + MOZ_ASSERT(choices.IsEmpty(), "Notification doesn't support permission choice"); 1.368 + 1.369 + if (aAllow) { 1.370 + (void) Allow(JS::UndefinedHandleValue); 1.371 + } else { 1.372 + (void) Cancel(); 1.373 + } 1.374 + return true; 1.375 +} 1.376 + 1.377 +NS_IMPL_ISUPPORTS(NotificationTask, nsIRunnable) 1.378 + 1.379 +NS_IMETHODIMP 1.380 +NotificationTask::Run() 1.381 +{ 1.382 + switch (mAction) { 1.383 + case eShow: 1.384 + mNotification->ShowInternal(); 1.385 + break; 1.386 + case eClose: 1.387 + mNotification->CloseInternal(); 1.388 + break; 1.389 + default: 1.390 + MOZ_CRASH("Unexpected action for NotificationTask."); 1.391 + } 1.392 + return NS_OK; 1.393 +} 1.394 + 1.395 +NS_IMPL_ISUPPORTS(NotificationObserver, nsIObserver) 1.396 + 1.397 +NS_IMETHODIMP 1.398 +NotificationObserver::Observe(nsISupports* aSubject, const char* aTopic, 1.399 + const char16_t* aData) 1.400 +{ 1.401 + if (!strcmp("alertclickcallback", aTopic)) { 1.402 + nsCOMPtr<nsPIDOMWindow> window = mNotification->GetOwner(); 1.403 + nsIDocument* doc = window ? window->GetExtantDoc() : nullptr; 1.404 + if (doc) { 1.405 + nsContentUtils::DispatchChromeEvent(doc, window, 1.406 + NS_LITERAL_STRING("DOMWebNotificationClicked"), 1.407 + true, true); 1.408 + } 1.409 + mNotification->DispatchTrustedEvent(NS_LITERAL_STRING("click")); 1.410 + } else if (!strcmp("alertfinished", aTopic)) { 1.411 + mNotification->mIsClosed = true; 1.412 + mNotification->DispatchTrustedEvent(NS_LITERAL_STRING("close")); 1.413 + } else if (!strcmp("alertshow", aTopic)) { 1.414 + mNotification->DispatchTrustedEvent(NS_LITERAL_STRING("show")); 1.415 + } 1.416 + 1.417 + return NS_OK; 1.418 +} 1.419 + 1.420 +Notification::Notification(const nsAString& aID, const nsAString& aTitle, const nsAString& aBody, 1.421 + NotificationDirection aDir, const nsAString& aLang, 1.422 + const nsAString& aTag, const nsAString& aIconUrl, 1.423 + nsPIDOMWindow* aWindow) 1.424 + : DOMEventTargetHelper(aWindow), 1.425 + mID(aID), mTitle(aTitle), mBody(aBody), mDir(aDir), mLang(aLang), 1.426 + mTag(aTag), mIconUrl(aIconUrl), mIsClosed(false) 1.427 +{ 1.428 +} 1.429 + 1.430 +// static 1.431 +already_AddRefed<Notification> 1.432 +Notification::Constructor(const GlobalObject& aGlobal, 1.433 + const nsAString& aTitle, 1.434 + const NotificationOptions& aOptions, 1.435 + ErrorResult& aRv) 1.436 +{ 1.437 + MOZ_ASSERT(NS_IsMainThread()); 1.438 + nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports()); 1.439 + MOZ_ASSERT(window, "Window should not be null."); 1.440 + nsRefPtr<Notification> notification = CreateInternal(window, 1.441 + EmptyString(), 1.442 + aTitle, 1.443 + aOptions); 1.444 + 1.445 + // Queue a task to show the notification. 1.446 + nsCOMPtr<nsIRunnable> showNotificationTask = 1.447 + new NotificationTask(notification, NotificationTask::eShow); 1.448 + NS_DispatchToCurrentThread(showNotificationTask); 1.449 + 1.450 + // Persist the notification. 1.451 + nsresult rv; 1.452 + nsCOMPtr<nsINotificationStorage> notificationStorage = 1.453 + do_GetService(NS_NOTIFICATION_STORAGE_CONTRACTID, &rv); 1.454 + if (NS_FAILED(rv)) { 1.455 + aRv.Throw(rv); 1.456 + return nullptr; 1.457 + } 1.458 + 1.459 + nsString origin; 1.460 + aRv = GetOrigin(window, origin); 1.461 + if (aRv.Failed()) { 1.462 + return nullptr; 1.463 + } 1.464 + 1.465 + nsString id; 1.466 + notification->GetID(id); 1.467 + aRv = notificationStorage->Put(origin, 1.468 + id, 1.469 + aTitle, 1.470 + DirectionToString(aOptions.mDir), 1.471 + aOptions.mLang, 1.472 + aOptions.mBody, 1.473 + aOptions.mTag, 1.474 + aOptions.mIcon); 1.475 + if (aRv.Failed()) { 1.476 + return nullptr; 1.477 + } 1.478 + 1.479 + return notification.forget(); 1.480 +} 1.481 + 1.482 +already_AddRefed<Notification> 1.483 +Notification::CreateInternal(nsPIDOMWindow* aWindow, 1.484 + const nsAString& aID, 1.485 + const nsAString& aTitle, 1.486 + const NotificationOptions& aOptions) 1.487 +{ 1.488 + nsString id; 1.489 + if (!aID.IsEmpty()) { 1.490 + id = aID; 1.491 + } else { 1.492 + nsCOMPtr<nsIUUIDGenerator> uuidgen = 1.493 + do_GetService("@mozilla.org/uuid-generator;1"); 1.494 + NS_ENSURE_TRUE(uuidgen, nullptr); 1.495 + nsID uuid; 1.496 + nsresult rv = uuidgen->GenerateUUIDInPlace(&uuid); 1.497 + NS_ENSURE_SUCCESS(rv, nullptr); 1.498 + 1.499 + char buffer[NSID_LENGTH]; 1.500 + uuid.ToProvidedString(buffer); 1.501 + NS_ConvertASCIItoUTF16 convertedID(buffer); 1.502 + id = convertedID; 1.503 + } 1.504 + 1.505 + nsRefPtr<Notification> notification = new Notification(id, 1.506 + aTitle, 1.507 + aOptions.mBody, 1.508 + aOptions.mDir, 1.509 + aOptions.mLang, 1.510 + aOptions.mTag, 1.511 + aOptions.mIcon, 1.512 + aWindow); 1.513 + return notification.forget(); 1.514 +} 1.515 + 1.516 +nsIPrincipal* 1.517 +Notification::GetPrincipal() 1.518 +{ 1.519 + nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(GetOwner()); 1.520 + NS_ENSURE_TRUE(sop, nullptr); 1.521 + return sop->GetPrincipal(); 1.522 +} 1.523 + 1.524 +void 1.525 +Notification::ShowInternal() 1.526 +{ 1.527 + nsCOMPtr<nsIAlertsService> alertService = 1.528 + do_GetService(NS_ALERTSERVICE_CONTRACTID); 1.529 + 1.530 + ErrorResult result; 1.531 + if (GetPermissionInternal(GetOwner(), result) != 1.532 + NotificationPermission::Granted || !alertService) { 1.533 + // We do not have permission to show a notification or alert service 1.534 + // is not available. 1.535 + DispatchTrustedEvent(NS_LITERAL_STRING("error")); 1.536 + return; 1.537 + } 1.538 + 1.539 + nsresult rv; 1.540 + nsAutoString absoluteUrl; 1.541 + if (mIconUrl.Length() > 0) { 1.542 + // Resolve image URL against document base URI. 1.543 + nsIDocument* doc = GetOwner()->GetExtantDoc(); 1.544 + if (doc) { 1.545 + nsCOMPtr<nsIURI> baseUri = doc->GetBaseURI(); 1.546 + if (baseUri) { 1.547 + nsCOMPtr<nsIURI> srcUri; 1.548 + rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(srcUri), 1.549 + mIconUrl, doc, baseUri); 1.550 + if (NS_SUCCEEDED(rv)) { 1.551 + nsAutoCString src; 1.552 + srcUri->GetSpec(src); 1.553 + absoluteUrl = NS_ConvertUTF8toUTF16(src); 1.554 + } 1.555 + } 1.556 + 1.557 + } 1.558 + } 1.559 + 1.560 + nsCOMPtr<nsIObserver> observer = new NotificationObserver(this); 1.561 + 1.562 + nsString alertName; 1.563 + rv = GetAlertName(alertName); 1.564 + NS_ENSURE_SUCCESS_VOID(rv); 1.565 + 1.566 +#ifdef MOZ_B2G 1.567 + nsCOMPtr<nsIAppNotificationService> appNotifier = 1.568 + do_GetService("@mozilla.org/system-alerts-service;1"); 1.569 + if (appNotifier) { 1.570 + nsCOMPtr<nsPIDOMWindow> window = GetOwner(); 1.571 + uint32_t appId = (window.get())->GetDoc()->NodePrincipal()->GetAppId(); 1.572 + 1.573 + if (appId != nsIScriptSecurityManager::UNKNOWN_APP_ID) { 1.574 + nsCOMPtr<nsIAppsService> appsService = do_GetService("@mozilla.org/AppsService;1"); 1.575 + nsString manifestUrl = EmptyString(); 1.576 + rv = appsService->GetManifestURLByLocalId(appId, manifestUrl); 1.577 + if (NS_SUCCEEDED(rv)) { 1.578 + mozilla::AutoSafeJSContext cx; 1.579 + JS::Rooted<JS::Value> val(cx); 1.580 + AppNotificationServiceOptions ops; 1.581 + ops.mTextClickable = true; 1.582 + ops.mManifestURL = manifestUrl; 1.583 + ops.mId = alertName; 1.584 + ops.mDbId = mID; 1.585 + ops.mDir = DirectionToString(mDir); 1.586 + ops.mLang = mLang; 1.587 + ops.mTag = mTag; 1.588 + 1.589 + if (!ops.ToObject(cx, &val)) { 1.590 + NS_WARNING("Converting dict to object failed!"); 1.591 + return; 1.592 + } 1.593 + 1.594 + appNotifier->ShowAppNotification(mIconUrl, mTitle, mBody, 1.595 + observer, val); 1.596 + return; 1.597 + } 1.598 + } 1.599 + } 1.600 +#endif 1.601 + 1.602 + // In the case of IPC, the parent process uses the cookie to map to 1.603 + // nsIObserver. Thus the cookie must be unique to differentiate observers. 1.604 + nsString uniqueCookie = NS_LITERAL_STRING("notification:"); 1.605 + uniqueCookie.AppendInt(sCount++); 1.606 + alertService->ShowAlertNotification(absoluteUrl, mTitle, mBody, true, 1.607 + uniqueCookie, observer, alertName, 1.608 + DirectionToString(mDir), mLang, 1.609 + GetPrincipal()); 1.610 +} 1.611 + 1.612 +void 1.613 +Notification::RequestPermission(const GlobalObject& aGlobal, 1.614 + const Optional<OwningNonNull<NotificationPermissionCallback> >& aCallback, 1.615 + ErrorResult& aRv) 1.616 +{ 1.617 + // Get principal from global to make permission request for notifications. 1.618 + nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports()); 1.619 + nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aGlobal.GetAsSupports()); 1.620 + if (!sop) { 1.621 + aRv.Throw(NS_ERROR_UNEXPECTED); 1.622 + return; 1.623 + } 1.624 + nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal(); 1.625 + 1.626 + NotificationPermissionCallback* permissionCallback = nullptr; 1.627 + if (aCallback.WasPassed()) { 1.628 + permissionCallback = &aCallback.Value(); 1.629 + } 1.630 + nsCOMPtr<nsIRunnable> request = 1.631 + new NotificationPermissionRequest(principal, window, permissionCallback); 1.632 + 1.633 + NS_DispatchToMainThread(request); 1.634 +} 1.635 + 1.636 +NotificationPermission 1.637 +Notification::GetPermission(const GlobalObject& aGlobal, ErrorResult& aRv) 1.638 +{ 1.639 + return GetPermissionInternal(aGlobal.GetAsSupports(), aRv); 1.640 +} 1.641 + 1.642 +NotificationPermission 1.643 +Notification::GetPermissionInternal(nsISupports* aGlobal, ErrorResult& aRv) 1.644 +{ 1.645 + // Get principal from global to check permission for notifications. 1.646 + nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aGlobal); 1.647 + if (!sop) { 1.648 + aRv.Throw(NS_ERROR_UNEXPECTED); 1.649 + return NotificationPermission::Denied; 1.650 + } 1.651 + 1.652 + nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal(); 1.653 + if (nsContentUtils::IsSystemPrincipal(principal)) { 1.654 + return NotificationPermission::Granted; 1.655 + } else { 1.656 + // Allow files to show notifications by default. 1.657 + nsCOMPtr<nsIURI> uri; 1.658 + principal->GetURI(getter_AddRefs(uri)); 1.659 + if (uri) { 1.660 + bool isFile; 1.661 + uri->SchemeIs("file", &isFile); 1.662 + if (isFile) { 1.663 + return NotificationPermission::Granted; 1.664 + } 1.665 + } 1.666 + } 1.667 + 1.668 + // We also allow notifications is they are pref'ed on. 1.669 + if (Preferences::GetBool("notification.prompt.testing", false)) { 1.670 + if (Preferences::GetBool("notification.prompt.testing.allow", true)) { 1.671 + return NotificationPermission::Granted; 1.672 + } else { 1.673 + return NotificationPermission::Denied; 1.674 + } 1.675 + } 1.676 + 1.677 + uint32_t permission = nsIPermissionManager::UNKNOWN_ACTION; 1.678 + 1.679 + nsCOMPtr<nsIPermissionManager> permissionManager = 1.680 + do_GetService(NS_PERMISSIONMANAGER_CONTRACTID); 1.681 + 1.682 + permissionManager->TestPermissionFromPrincipal(principal, 1.683 + "desktop-notification", 1.684 + &permission); 1.685 + 1.686 + // Convert the result to one of the enum types. 1.687 + switch (permission) { 1.688 + case nsIPermissionManager::ALLOW_ACTION: 1.689 + return NotificationPermission::Granted; 1.690 + case nsIPermissionManager::DENY_ACTION: 1.691 + return NotificationPermission::Denied; 1.692 + default: 1.693 + return NotificationPermission::Default; 1.694 + } 1.695 +} 1.696 + 1.697 +already_AddRefed<Promise> 1.698 +Notification::Get(const GlobalObject& aGlobal, 1.699 + const GetNotificationOptions& aFilter, 1.700 + ErrorResult& aRv) 1.701 +{ 1.702 + nsCOMPtr<nsIGlobalObject> global = 1.703 + do_QueryInterface(aGlobal.GetAsSupports()); 1.704 + MOZ_ASSERT(global); 1.705 + nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(global); 1.706 + MOZ_ASSERT(window); 1.707 + nsIDocument* doc = window->GetExtantDoc(); 1.708 + if (!doc) { 1.709 + aRv.Throw(NS_ERROR_UNEXPECTED); 1.710 + return nullptr; 1.711 + } 1.712 + 1.713 + nsString origin; 1.714 + aRv = GetOrigin(window, origin); 1.715 + if (aRv.Failed()) { 1.716 + return nullptr; 1.717 + } 1.718 + 1.719 + nsresult rv; 1.720 + nsCOMPtr<nsINotificationStorage> notificationStorage = 1.721 + do_GetService(NS_NOTIFICATION_STORAGE_CONTRACTID, &rv); 1.722 + if (NS_FAILED(rv)) { 1.723 + aRv.Throw(rv); 1.724 + return nullptr; 1.725 + } 1.726 + 1.727 + nsRefPtr<Promise> promise = new Promise(global); 1.728 + nsCOMPtr<nsINotificationStorageCallback> callback = 1.729 + new NotificationStorageCallback(aGlobal, window, promise); 1.730 + nsString tag = aFilter.mTag.WasPassed() ? 1.731 + aFilter.mTag.Value() : 1.732 + EmptyString(); 1.733 + aRv = notificationStorage->Get(origin, tag, callback); 1.734 + if (aRv.Failed()) { 1.735 + return nullptr; 1.736 + } 1.737 + 1.738 + return promise.forget(); 1.739 +} 1.740 + 1.741 +JSObject* 1.742 +Notification::WrapObject(JSContext* aCx) 1.743 +{ 1.744 + return mozilla::dom::NotificationBinding::Wrap(aCx, this); 1.745 +} 1.746 + 1.747 +void 1.748 +Notification::Close() 1.749 +{ 1.750 + // Queue a task to close the notification. 1.751 + nsCOMPtr<nsIRunnable> closeNotificationTask = 1.752 + new NotificationTask(this, NotificationTask::eClose); 1.753 + NS_DispatchToMainThread(closeNotificationTask); 1.754 +} 1.755 + 1.756 +void 1.757 +Notification::CloseInternal() 1.758 +{ 1.759 + if (!mIsClosed) { 1.760 + nsresult rv; 1.761 + // Don't bail out if notification storage fails, since we still 1.762 + // want to send the close event through the alert service. 1.763 + nsCOMPtr<nsINotificationStorage> notificationStorage = 1.764 + do_GetService(NS_NOTIFICATION_STORAGE_CONTRACTID); 1.765 + if (notificationStorage) { 1.766 + nsString origin; 1.767 + rv = GetOrigin(GetOwner(), origin); 1.768 + if (NS_SUCCEEDED(rv)) { 1.769 + notificationStorage->Delete(origin, mID); 1.770 + } 1.771 + } 1.772 + 1.773 + nsCOMPtr<nsIAlertsService> alertService = 1.774 + do_GetService(NS_ALERTSERVICE_CONTRACTID); 1.775 + if (alertService) { 1.776 + nsString alertName; 1.777 + rv = GetAlertName(alertName); 1.778 + if (NS_SUCCEEDED(rv)) { 1.779 + alertService->CloseAlert(alertName, GetPrincipal()); 1.780 + } 1.781 + } 1.782 + } 1.783 +} 1.784 + 1.785 +nsresult 1.786 +Notification::GetOrigin(nsPIDOMWindow* aWindow, nsString& aOrigin) 1.787 +{ 1.788 + if (!aWindow) { 1.789 + return NS_ERROR_FAILURE; 1.790 + } 1.791 + nsresult rv; 1.792 + nsIDocument* doc = aWindow->GetExtantDoc(); 1.793 + NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED); 1.794 + nsIPrincipal* principal = doc->NodePrincipal(); 1.795 + NS_ENSURE_TRUE(principal, NS_ERROR_UNEXPECTED); 1.796 + 1.797 + uint16_t appStatus = principal->GetAppStatus(); 1.798 + uint32_t appId = principal->GetAppId(); 1.799 + 1.800 + if (appStatus == nsIPrincipal::APP_STATUS_NOT_INSTALLED || 1.801 + appId == nsIScriptSecurityManager::NO_APP_ID || 1.802 + appId == nsIScriptSecurityManager::UNKNOWN_APP_ID) { 1.803 + rv = nsContentUtils::GetUTFOrigin(principal, aOrigin); 1.804 + NS_ENSURE_SUCCESS(rv, rv); 1.805 + } else { 1.806 + // If we are in "app code", use manifest URL as unique origin since 1.807 + // multiple apps can share the same origin but not same notifications. 1.808 + nsCOMPtr<nsIAppsService> appsService = 1.809 + do_GetService("@mozilla.org/AppsService;1", &rv); 1.810 + NS_ENSURE_SUCCESS(rv, rv); 1.811 + appsService->GetManifestURLByLocalId(appId, aOrigin); 1.812 + } 1.813 + 1.814 + return NS_OK; 1.815 +} 1.816 + 1.817 +nsresult 1.818 +Notification::GetAlertName(nsString& aAlertName) 1.819 +{ 1.820 + // Get the notification name that is unique per origin + tag/ID. 1.821 + // The name of the alert is of the form origin#tag/ID. 1.822 + nsresult rv = GetOrigin(GetOwner(), aAlertName); 1.823 + NS_ENSURE_SUCCESS(rv, rv); 1.824 + aAlertName.AppendLiteral("#"); 1.825 + if (!mTag.IsEmpty()) { 1.826 + aAlertName.Append(NS_LITERAL_STRING("tag:")); 1.827 + aAlertName.Append(mTag); 1.828 + } else { 1.829 + aAlertName.Append(NS_LITERAL_STRING("notag:")); 1.830 + aAlertName.Append(mID); 1.831 + } 1.832 + return NS_OK; 1.833 +} 1.834 + 1.835 +} // namespace dom 1.836 +} // namespace mozilla 1.837 +