dom/src/notification/Notification.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial