dom/src/notification/DesktopNotification.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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 #include "mozilla/dom/DesktopNotification.h"
michael@0 5 #include "mozilla/dom/DesktopNotificationBinding.h"
michael@0 6 #include "mozilla/dom/AppNotificationServiceOptionsBinding.h"
michael@0 7 #include "nsContentPermissionHelper.h"
michael@0 8 #include "nsXULAppAPI.h"
michael@0 9 #include "mozilla/dom/PBrowserChild.h"
michael@0 10 #include "nsIDOMDesktopNotification.h"
michael@0 11 #include "TabChild.h"
michael@0 12 #include "mozilla/Preferences.h"
michael@0 13 #include "nsGlobalWindow.h"
michael@0 14 #include "nsIAppsService.h"
michael@0 15 #include "PCOMContentPermissionRequestChild.h"
michael@0 16 #include "nsIScriptSecurityManager.h"
michael@0 17 #include "nsServiceManagerUtils.h"
michael@0 18 #include "PermissionMessageUtils.h"
michael@0 19
michael@0 20 namespace mozilla {
michael@0 21 namespace dom {
michael@0 22
michael@0 23 /*
michael@0 24 * Simple Request
michael@0 25 */
michael@0 26 class DesktopNotificationRequest : public nsIContentPermissionRequest,
michael@0 27 public nsRunnable,
michael@0 28 public PCOMContentPermissionRequestChild
michael@0 29
michael@0 30 {
michael@0 31 public:
michael@0 32 NS_DECL_ISUPPORTS
michael@0 33 NS_DECL_NSICONTENTPERMISSIONREQUEST
michael@0 34
michael@0 35 DesktopNotificationRequest(DesktopNotification* notification)
michael@0 36 : mDesktopNotification(notification) {}
michael@0 37
michael@0 38 NS_IMETHOD Run() MOZ_OVERRIDE
michael@0 39 {
michael@0 40 nsCOMPtr<nsIContentPermissionPrompt> prompt =
michael@0 41 do_CreateInstance(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
michael@0 42 if (prompt) {
michael@0 43 prompt->Prompt(this);
michael@0 44 }
michael@0 45 return NS_OK;
michael@0 46 }
michael@0 47
michael@0 48 ~DesktopNotificationRequest()
michael@0 49 {
michael@0 50 }
michael@0 51
michael@0 52 virtual bool Recv__delete__(const bool& aAllow,
michael@0 53 const InfallibleTArray<PermissionChoice>& choices) MOZ_OVERRIDE
michael@0 54 {
michael@0 55 MOZ_ASSERT(choices.IsEmpty(), "DesktopNotification doesn't support permission choice");
michael@0 56 if (aAllow) {
michael@0 57 (void) Allow(JS::UndefinedHandleValue);
michael@0 58 } else {
michael@0 59 (void) Cancel();
michael@0 60 }
michael@0 61 return true;
michael@0 62 }
michael@0 63 virtual void IPDLRelease() MOZ_OVERRIDE { Release(); }
michael@0 64
michael@0 65 nsRefPtr<DesktopNotification> mDesktopNotification;
michael@0 66 };
michael@0 67
michael@0 68 /* ------------------------------------------------------------------------ */
michael@0 69 /* AlertServiceObserver */
michael@0 70 /* ------------------------------------------------------------------------ */
michael@0 71
michael@0 72 NS_IMPL_ISUPPORTS(AlertServiceObserver, nsIObserver)
michael@0 73
michael@0 74 /* ------------------------------------------------------------------------ */
michael@0 75 /* DesktopNotification */
michael@0 76 /* ------------------------------------------------------------------------ */
michael@0 77
michael@0 78 uint32_t DesktopNotification::sCount = 0;
michael@0 79
michael@0 80 nsresult
michael@0 81 DesktopNotification::PostDesktopNotification()
michael@0 82 {
michael@0 83 if (!mObserver) {
michael@0 84 mObserver = new AlertServiceObserver(this);
michael@0 85 }
michael@0 86
michael@0 87 #ifdef MOZ_B2G
michael@0 88 nsCOMPtr<nsIAppNotificationService> appNotifier =
michael@0 89 do_GetService("@mozilla.org/system-alerts-service;1");
michael@0 90 if (appNotifier) {
michael@0 91 nsCOMPtr<nsPIDOMWindow> window = GetOwner();
michael@0 92 uint32_t appId = (window.get())->GetDoc()->NodePrincipal()->GetAppId();
michael@0 93
michael@0 94 if (appId != nsIScriptSecurityManager::UNKNOWN_APP_ID) {
michael@0 95 nsCOMPtr<nsIAppsService> appsService = do_GetService("@mozilla.org/AppsService;1");
michael@0 96 nsString manifestUrl = EmptyString();
michael@0 97 appsService->GetManifestURLByLocalId(appId, manifestUrl);
michael@0 98 mozilla::AutoSafeJSContext cx;
michael@0 99 JS::Rooted<JS::Value> val(cx);
michael@0 100 AppNotificationServiceOptions ops;
michael@0 101 ops.mTextClickable = true;
michael@0 102 ops.mManifestURL = manifestUrl;
michael@0 103
michael@0 104 if (!ops.ToObject(cx, &val)) {
michael@0 105 return NS_ERROR_FAILURE;
michael@0 106 }
michael@0 107
michael@0 108 return appNotifier->ShowAppNotification(mIconURL, mTitle, mDescription,
michael@0 109 mObserver, val);
michael@0 110 }
michael@0 111 }
michael@0 112 #endif
michael@0 113
michael@0 114 nsCOMPtr<nsIAlertsService> alerts = do_GetService("@mozilla.org/alerts-service;1");
michael@0 115 if (!alerts) {
michael@0 116 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 117 }
michael@0 118
michael@0 119 // Generate a unique name (which will also be used as a cookie) because
michael@0 120 // the nsIAlertsService will coalesce notifications with the same name.
michael@0 121 // In the case of IPC, the parent process will use the cookie to map
michael@0 122 // to nsIObservers, thus cookies must be unique to differentiate observers.
michael@0 123 nsString uniqueName = NS_LITERAL_STRING("desktop-notification:");
michael@0 124 uniqueName.AppendInt(sCount++);
michael@0 125 nsIPrincipal* principal = GetOwner()->GetDoc()->NodePrincipal();
michael@0 126 return alerts->ShowAlertNotification(mIconURL, mTitle, mDescription,
michael@0 127 true,
michael@0 128 uniqueName,
michael@0 129 mObserver,
michael@0 130 uniqueName,
michael@0 131 NS_LITERAL_STRING("auto"),
michael@0 132 EmptyString(),
michael@0 133 principal);
michael@0 134 }
michael@0 135
michael@0 136 DesktopNotification::DesktopNotification(const nsAString & title,
michael@0 137 const nsAString & description,
michael@0 138 const nsAString & iconURL,
michael@0 139 nsPIDOMWindow *aWindow,
michael@0 140 nsIPrincipal* principal)
michael@0 141 : DOMEventTargetHelper(aWindow)
michael@0 142 , mTitle(title)
michael@0 143 , mDescription(description)
michael@0 144 , mIconURL(iconURL)
michael@0 145 , mPrincipal(principal)
michael@0 146 , mAllow(false)
michael@0 147 , mShowHasBeenCalled(false)
michael@0 148 {
michael@0 149 if (Preferences::GetBool("notification.disabled", false)) {
michael@0 150 return;
michael@0 151 }
michael@0 152
michael@0 153 // If we are in testing mode (running mochitests, for example)
michael@0 154 // and we are suppose to allow requests, then just post an allow event.
michael@0 155 if (Preferences::GetBool("notification.prompt.testing", false) &&
michael@0 156 Preferences::GetBool("notification.prompt.testing.allow", true)) {
michael@0 157 mAllow = true;
michael@0 158 }
michael@0 159 }
michael@0 160
michael@0 161 void
michael@0 162 DesktopNotification::Init()
michael@0 163 {
michael@0 164 nsRefPtr<DesktopNotificationRequest> request = new DesktopNotificationRequest(this);
michael@0 165
michael@0 166 // if we are in the content process, then remote it to the parent.
michael@0 167 if (XRE_GetProcessType() == GeckoProcessType_Content) {
michael@0 168
michael@0 169 // if for some reason mOwner is null, just silently
michael@0 170 // bail. The user will not see a notification, and that
michael@0 171 // is fine.
michael@0 172 if (!GetOwner())
michael@0 173 return;
michael@0 174
michael@0 175 // because owner implements nsITabChild, we can assume that it is
michael@0 176 // the one and only TabChild for this docshell.
michael@0 177 TabChild* child = TabChild::GetFrom(GetOwner()->GetDocShell());
michael@0 178
michael@0 179 // Retain a reference so the object isn't deleted without IPDL's knowledge.
michael@0 180 // Corresponding release occurs in DeallocPContentPermissionRequest.
michael@0 181 nsRefPtr<DesktopNotificationRequest> copy = request;
michael@0 182
michael@0 183 nsTArray<PermissionRequest> permArray;
michael@0 184 nsTArray<nsString> emptyOptions;
michael@0 185 permArray.AppendElement(PermissionRequest(
michael@0 186 NS_LITERAL_CSTRING("desktop-notification"),
michael@0 187 NS_LITERAL_CSTRING("unused"),
michael@0 188 emptyOptions));
michael@0 189 child->SendPContentPermissionRequestConstructor(copy.forget().take(),
michael@0 190 permArray,
michael@0 191 IPC::Principal(mPrincipal));
michael@0 192
michael@0 193 request->Sendprompt();
michael@0 194 return;
michael@0 195 }
michael@0 196
michael@0 197 // otherwise, dispatch it
michael@0 198 NS_DispatchToMainThread(request);
michael@0 199 }
michael@0 200
michael@0 201 DesktopNotification::~DesktopNotification()
michael@0 202 {
michael@0 203 if (mObserver) {
michael@0 204 mObserver->Disconnect();
michael@0 205 }
michael@0 206 }
michael@0 207
michael@0 208 void
michael@0 209 DesktopNotification::DispatchNotificationEvent(const nsString& aName)
michael@0 210 {
michael@0 211 if (NS_FAILED(CheckInnerWindowCorrectness())) {
michael@0 212 return;
michael@0 213 }
michael@0 214
michael@0 215 nsCOMPtr<nsIDOMEvent> event;
michael@0 216 nsresult rv = NS_NewDOMEvent(getter_AddRefs(event), this, nullptr, nullptr);
michael@0 217 if (NS_SUCCEEDED(rv)) {
michael@0 218 // it doesn't bubble, and it isn't cancelable
michael@0 219 rv = event->InitEvent(aName, false, false);
michael@0 220 if (NS_SUCCEEDED(rv)) {
michael@0 221 event->SetTrusted(true);
michael@0 222 DispatchDOMEvent(nullptr, event, nullptr, nullptr);
michael@0 223 }
michael@0 224 }
michael@0 225 }
michael@0 226
michael@0 227 nsresult
michael@0 228 DesktopNotification::SetAllow(bool aAllow)
michael@0 229 {
michael@0 230 mAllow = aAllow;
michael@0 231
michael@0 232 // if we have called Show() already, lets go ahead and post a notification
michael@0 233 if (mShowHasBeenCalled && aAllow) {
michael@0 234 return PostDesktopNotification();
michael@0 235 }
michael@0 236
michael@0 237 return NS_OK;
michael@0 238 }
michael@0 239
michael@0 240 void
michael@0 241 DesktopNotification::HandleAlertServiceNotification(const char *aTopic)
michael@0 242 {
michael@0 243 if (NS_FAILED(CheckInnerWindowCorrectness())) {
michael@0 244 return;
michael@0 245 }
michael@0 246
michael@0 247 if (!strcmp("alertclickcallback", aTopic)) {
michael@0 248 DispatchNotificationEvent(NS_LITERAL_STRING("click"));
michael@0 249 } else if (!strcmp("alertfinished", aTopic)) {
michael@0 250 DispatchNotificationEvent(NS_LITERAL_STRING("close"));
michael@0 251 }
michael@0 252 }
michael@0 253
michael@0 254 void
michael@0 255 DesktopNotification::Show(ErrorResult& aRv)
michael@0 256 {
michael@0 257 mShowHasBeenCalled = true;
michael@0 258
michael@0 259 if (!mAllow) {
michael@0 260 return;
michael@0 261 }
michael@0 262
michael@0 263 aRv = PostDesktopNotification();
michael@0 264 }
michael@0 265
michael@0 266 JSObject*
michael@0 267 DesktopNotification::WrapObject(JSContext* aCx)
michael@0 268 {
michael@0 269 return DesktopNotificationBinding::Wrap(aCx, this);
michael@0 270 }
michael@0 271
michael@0 272 /* ------------------------------------------------------------------------ */
michael@0 273 /* DesktopNotificationCenter */
michael@0 274 /* ------------------------------------------------------------------------ */
michael@0 275
michael@0 276 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(DesktopNotificationCenter)
michael@0 277 NS_IMPL_CYCLE_COLLECTING_ADDREF(DesktopNotificationCenter)
michael@0 278 NS_IMPL_CYCLE_COLLECTING_RELEASE(DesktopNotificationCenter)
michael@0 279 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DesktopNotificationCenter)
michael@0 280 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
michael@0 281 NS_INTERFACE_MAP_ENTRY(nsISupports)
michael@0 282 NS_INTERFACE_MAP_END
michael@0 283
michael@0 284 already_AddRefed<DesktopNotification>
michael@0 285 DesktopNotificationCenter::CreateNotification(const nsAString& aTitle,
michael@0 286 const nsAString& aDescription,
michael@0 287 const nsAString& aIconURL)
michael@0 288 {
michael@0 289 MOZ_ASSERT(mOwner);
michael@0 290
michael@0 291 nsRefPtr<DesktopNotification> notification =
michael@0 292 new DesktopNotification(aTitle,
michael@0 293 aDescription,
michael@0 294 aIconURL,
michael@0 295 mOwner,
michael@0 296 mPrincipal);
michael@0 297 notification->Init();
michael@0 298 return notification.forget();
michael@0 299 }
michael@0 300
michael@0 301 JSObject*
michael@0 302 DesktopNotificationCenter::WrapObject(JSContext* aCx)
michael@0 303 {
michael@0 304 return DesktopNotificationCenterBinding::Wrap(aCx, this);
michael@0 305 }
michael@0 306
michael@0 307 /* ------------------------------------------------------------------------ */
michael@0 308 /* DesktopNotificationRequest */
michael@0 309 /* ------------------------------------------------------------------------ */
michael@0 310
michael@0 311 NS_IMPL_ISUPPORTS(DesktopNotificationRequest,
michael@0 312 nsIContentPermissionRequest,
michael@0 313 nsIRunnable)
michael@0 314
michael@0 315 NS_IMETHODIMP
michael@0 316 DesktopNotificationRequest::GetPrincipal(nsIPrincipal * *aRequestingPrincipal)
michael@0 317 {
michael@0 318 if (!mDesktopNotification) {
michael@0 319 return NS_ERROR_NOT_INITIALIZED;
michael@0 320 }
michael@0 321
michael@0 322 NS_IF_ADDREF(*aRequestingPrincipal = mDesktopNotification->mPrincipal);
michael@0 323 return NS_OK;
michael@0 324 }
michael@0 325
michael@0 326 NS_IMETHODIMP
michael@0 327 DesktopNotificationRequest::GetWindow(nsIDOMWindow * *aRequestingWindow)
michael@0 328 {
michael@0 329 if (!mDesktopNotification) {
michael@0 330 return NS_ERROR_NOT_INITIALIZED;
michael@0 331 }
michael@0 332
michael@0 333 NS_IF_ADDREF(*aRequestingWindow = mDesktopNotification->GetOwner());
michael@0 334 return NS_OK;
michael@0 335 }
michael@0 336
michael@0 337 NS_IMETHODIMP
michael@0 338 DesktopNotificationRequest::GetElement(nsIDOMElement * *aElement)
michael@0 339 {
michael@0 340 NS_ENSURE_ARG_POINTER(aElement);
michael@0 341 *aElement = nullptr;
michael@0 342 return NS_OK;
michael@0 343 }
michael@0 344
michael@0 345 NS_IMETHODIMP
michael@0 346 DesktopNotificationRequest::Cancel()
michael@0 347 {
michael@0 348 nsresult rv = mDesktopNotification->SetAllow(false);
michael@0 349 mDesktopNotification = nullptr;
michael@0 350 return rv;
michael@0 351 }
michael@0 352
michael@0 353 NS_IMETHODIMP
michael@0 354 DesktopNotificationRequest::Allow(JS::HandleValue aChoices)
michael@0 355 {
michael@0 356 MOZ_ASSERT(aChoices.isUndefined());
michael@0 357 nsresult rv = mDesktopNotification->SetAllow(true);
michael@0 358 mDesktopNotification = nullptr;
michael@0 359 return rv;
michael@0 360 }
michael@0 361
michael@0 362 NS_IMETHODIMP
michael@0 363 DesktopNotificationRequest::GetTypes(nsIArray** aTypes)
michael@0 364 {
michael@0 365 nsTArray<nsString> emptyOptions;
michael@0 366 return CreatePermissionArray(NS_LITERAL_CSTRING("desktop-notification"),
michael@0 367 NS_LITERAL_CSTRING("unused"),
michael@0 368 emptyOptions,
michael@0 369 aTypes);
michael@0 370 }
michael@0 371
michael@0 372 } // namespace dom
michael@0 373 } // namespace mozilla

mercurial