Bug 1114554 - Patch 3 - Fire notificationclick event on ServiceWorkerGlobalScope. r=wchen,baku

Bug 1114554 - Patch 3.1 - ServiceWorker principal fixes. r=baku

Bug 1162088 introduced origin attributes that ServiceWorkerManager callers have to use. This patch updates notificationclick events to work.

Folded:
Hide NotificationEvent behind pref
This commit is contained in:
Nikhil Marathe 2015-06-25 18:50:25 -07:00
parent d7c89d1814
commit 40af10ec0b
19 changed files with 675 additions and 25 deletions

View File

@ -33,7 +33,7 @@ interface nsIServiceWorkerInfo : nsISupports
readonly attribute DOMString waitingCacheName;
};
[scriptable, builtinclass, uuid(e9abb123-0099-4d9e-85db-c8cd0aff19e6)]
[scriptable, builtinclass, uuid(ed1cbbf2-0400-4caa-8eb2-b09d21a94e20)]
interface nsIServiceWorkerManager : nsISupports
{
/**
@ -126,6 +126,17 @@ interface nsIServiceWorkerManager : nsISupports
in nsIServiceWorkerUnregisterCallback aCallback,
in DOMString aScope);
void sendNotificationClickEvent(in ACString aOriginSuffix,
in ACString scope,
in AString aID,
in AString aTitle,
in AString aDir,
in AString aLang,
in AString aBody,
in AString aTag,
in AString aIcon,
in AString aData,
in AString aBehavior);
void sendPushEvent(in ACString aOriginAttributes,
in ACString aScope,
in DOMString aData);

View File

@ -41,7 +41,7 @@ interface nsINotificationStorageCallback : nsISupports
/**
* Interface for notification persistence layer.
*/
[scriptable, uuid(cac01fb0-c2eb-4252-b2f4-5b1fac933bd4)]
[scriptable, uuid(2f8f84b7-70b5-4673-98d8-fd3f9f8e0e5c)]
interface nsINotificationStorage : nsISupports
{
@ -86,6 +86,20 @@ interface nsINotificationStorage : nsISupports
in DOMString tag,
in nsINotificationStorageCallback aCallback);
/**
* Retrieve a notification by ID.
*
* @param origin: the origin/app for which to fetch notifications.
* @param id: the id of the notification.
* @param callback: nsINotificationStorageCallback whose Handle method will
* be called *at most once* if the notification with that ID is found. Not
* called if that ID is not found. Done() will be called right after
* Handle().
*/
void getByID(in DOMString origin,
in DOMString id,
in nsINotificationStorageCallback aCallback);
/**
* Remove a notification from storage.
*

View File

@ -19,6 +19,7 @@
#include "nsIDocument.h"
#include "nsINotificationStorage.h"
#include "nsIPermissionManager.h"
#include "nsIServiceWorkerManager.h"
#include "nsIUUIDGenerator.h"
#include "nsServiceManagerUtils.h"
#include "nsStructuredCloneContainer.h"
@ -29,8 +30,9 @@
#include "nsNetUtil.h"
#include "nsIScriptSecurityManager.h"
#include "nsIXPConnect.h"
#include "mozilla/dom/PermissionMessageUtils.h"
#include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h"
#include "mozilla/dom/NotificationEvent.h"
#include "mozilla/dom/PermissionMessageUtils.h"
#include "mozilla/Services.h"
#include "nsContentPermissionHelper.h"
#include "nsILoadContext.h"
@ -41,6 +43,7 @@
#include "ServiceWorkerManager.h"
#include "WorkerPrivate.h"
#include "WorkerRunnable.h"
#include "WorkerScope.h"
namespace mozilla {
namespace dom {
@ -735,6 +738,48 @@ Notification::Constructor(const GlobalObject& aGlobal,
return notification.forget();
}
// static
already_AddRefed<Notification>
Notification::ConstructFromFields(
nsIGlobalObject* aGlobal,
const nsAString& aID,
const nsAString& aTitle,
const nsAString& aDir,
const nsAString& aLang,
const nsAString& aBody,
const nsAString& aTag,
const nsAString& aIcon,
const nsAString& aData,
ErrorResult& aRv)
{
MOZ_ASSERT(aGlobal);
AutoJSAPI jsapi;
DebugOnly<bool> ok = jsapi.Init(aGlobal);
MOZ_ASSERT(ok);
RootedDictionary<NotificationOptions> options(jsapi.cx());
options.mDir = Notification::StringToDirection(nsString(aDir));
options.mLang = aLang;
options.mBody = aBody;
options.mTag = aTag;
options.mIcon = aIcon;
nsRefPtr<Notification> notification;
notification = Notification::CreateInternal(aID,
aTitle,
options);
if (NS_IsMainThread()) {
notification->BindToOwner(aGlobal);
}
notification->InitFromBase64(jsapi.cx(), aData, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
return notification.forget();
}
nsresult
Notification::PersistNotification()
@ -946,6 +991,63 @@ protected:
NS_IMPL_ISUPPORTS_INHERITED0(WorkerNotificationObserver, NotificationObserver)
class ServiceWorkerNotificationObserver final : public nsIObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
ServiceWorkerNotificationObserver(const nsAString& aScope,
nsIPrincipal* aPrincipal,
const nsAString& aID)
: mScope(aScope), mID(aID), mPrincipal(aPrincipal)
{
AssertIsOnMainThread();
MOZ_ASSERT(aPrincipal);
}
private:
~ServiceWorkerNotificationObserver()
{}
const nsString mScope;
const nsString mID;
nsCOMPtr<nsIPrincipal> mPrincipal;
};
NS_IMPL_ISUPPORTS(ServiceWorkerNotificationObserver, nsIObserver)
// For ServiceWorkers.
bool
Notification::DispatchNotificationClickEvent()
{
MOZ_ASSERT(mWorkerPrivate);
MOZ_ASSERT(mWorkerPrivate->IsServiceWorker());
mWorkerPrivate->AssertIsOnWorkerThread();
NotificationEventInit options;
options.mNotification = this;
ErrorResult result;
nsRefPtr<EventTarget> target = mWorkerPrivate->GlobalScope();
nsRefPtr<NotificationEvent> event =
NotificationEvent::Constructor(target,
NS_LITERAL_STRING("notificationclick"),
options,
result);
if (NS_WARN_IF(result.Failed())) {
return false;
}
event->SetTrusted(true);
WantsPopupControlCheck popupControlCheck(event);
target->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
// We always return false since in case of dispatching on the serviceworker,
// there is no well defined window to focus. The script may use the
// Client.focus() API if it wishes.
return false;
}
bool
Notification::DispatchClickEvent()
{
@ -975,12 +1077,15 @@ public:
: NotificationWorkerRunnable(aNotification->mWorkerPrivate)
, mNotification(aNotification)
, mWindow(aWindow)
{}
{
MOZ_ASSERT_IF(mWorkerPrivate->IsServiceWorker(), !mWindow);
}
void
WorkerRunInternal() override
{
bool doDefaultAction = mNotification->DispatchClickEvent();
MOZ_ASSERT_IF(mWorkerPrivate->IsServiceWorker(), !doDefaultAction);
if (doDefaultAction) {
nsRefPtr<FocusWindowRunnable> r = new FocusWindowRunnable(mWindow);
NS_DispatchToMainThread(r);
@ -1042,15 +1147,18 @@ WorkerNotificationObserver::Observe(nsISupports* aSubject, const char* aTopic,
nsRefPtr<WorkerRunnable> r;
if (!strcmp("alertclickcallback", aTopic)) {
WorkerPrivate* top = notification->mWorkerPrivate;
while (top->GetParent()) {
top = top->GetParent();
}
nsPIDOMWindow* window = nullptr;
if (!notification->mWorkerPrivate->IsServiceWorker()) {
WorkerPrivate* top = notification->mWorkerPrivate;
while (top->GetParent()) {
top = top->GetParent();
}
nsPIDOMWindow* window = top->GetWindow();
if (NS_WARN_IF(!window || !window->IsCurrentInnerWindow())) {
// Window has been closed, this observer is not valid anymore
return NS_ERROR_FAILURE;
window = top->GetWindow();
if (NS_WARN_IF(!window || !window->IsCurrentInnerWindow())) {
// Window has been closed, this observer is not valid anymore
return NS_ERROR_FAILURE;
}
}
// Instead of bothering with adding features and other worker lifecycle
@ -1077,6 +1185,107 @@ WorkerNotificationObserver::Observe(nsISupports* aSubject, const char* aTopic,
return NS_OK;
}
class NotificationClickEventCallback final : public nsINotificationStorageCallback
{
public:
NS_DECL_ISUPPORTS
NotificationClickEventCallback(nsIPrincipal* aPrincipal,
const nsAString& aScope)
: mPrincipal(aPrincipal), mScope(aScope)
{
MOZ_ASSERT(aPrincipal);
}
NS_IMETHOD Handle(const nsAString& aID,
const nsAString& aTitle,
const nsAString& aDir,
const nsAString& aLang,
const nsAString& aBody,
const nsAString& aTag,
const nsAString& aIcon,
const nsAString& aData,
const nsAString& aBehavior,
JSContext* aCx) override
{
MOZ_ASSERT(!aID.IsEmpty());
AssertIsOnMainThread();
nsAutoCString originSuffix;
nsresult rv = mPrincipal->GetOriginSuffix(originSuffix);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsCOMPtr<nsIServiceWorkerManager> swm =
mozilla::services::GetServiceWorkerManager();
if (swm) {
swm->SendNotificationClickEvent(originSuffix,
NS_ConvertUTF16toUTF8(mScope),
aID,
aTitle,
aDir,
aLang,
aBody,
aTag,
aIcon,
aData,
aBehavior);
}
return NS_OK;
}
NS_IMETHOD Done(JSContext* aCx) override
{
return NS_OK;
}
private:
~NotificationClickEventCallback()
{
}
nsCOMPtr<nsIPrincipal> mPrincipal;
nsString mScope;
};
NS_IMPL_ISUPPORTS(NotificationClickEventCallback, nsINotificationStorageCallback)
NS_IMETHODIMP
ServiceWorkerNotificationObserver::Observe(nsISupports* aSubject,
const char* aTopic,
const char16_t* aData)
{
AssertIsOnMainThread();
// Persistent notifications only care about the click event.
if (!strcmp("alertclickcallback", aTopic)) {
nsresult rv;
nsCOMPtr<nsINotificationStorage> notificationStorage =
do_GetService(NS_NOTIFICATION_STORAGE_CONTRACTID, &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsCOMPtr<nsINotificationStorageCallback> callback =
new NotificationClickEventCallback(mPrincipal, mScope);
nsAutoString origin;
rv = Notification::GetOrigin(mPrincipal, origin);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = notificationStorage->GetByID(origin, mID, callback);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
return NS_OK;
}
void
Notification::ShowInternal()
{
@ -1126,16 +1335,27 @@ Notification::ShowInternal()
nsAutoString soundUrl;
ResolveIconAndSoundURL(iconUrl, soundUrl);
// Ownership passed to observer.
nsCOMPtr<nsIObserver> observer;
if (mWorkerPrivate) {
// Keep a pointer so that the feature can tell the observer not to release
// the notification.
mObserver = new WorkerNotificationObserver(Move(ownership));
observer = mObserver;
if (mScope.IsEmpty()) {
// Ownership passed to observer.
if (mWorkerPrivate) {
// Scope better be set on ServiceWorker initiated requests.
MOZ_ASSERT(!mWorkerPrivate->IsServiceWorker());
// Keep a pointer so that the feature can tell the observer not to release
// the notification.
mObserver = new WorkerNotificationObserver(Move(ownership));
observer = mObserver;
} else {
observer = new NotificationObserver(Move(ownership));
}
} else {
observer = new NotificationObserver(Move(ownership));
// This observer does not care about the Notification. It will be released
// at the end of this function.
//
// The observer is wholly owned by the alerts service.
observer = new ServiceWorkerNotificationObserver(mScope, GetPrincipal(), mID);
}
MOZ_ASSERT(observer);
// mDataObjectContainer might be uninitialized here because the notification
// was constructed with an undefined data property.
@ -1805,6 +2025,8 @@ Notification::ShowPersistentNotification(nsIGlobalObject *aGlobal,
return nullptr;
}
notification->SetScope(aScope);
return p.forget();
}

View File

@ -115,6 +115,7 @@ class Notification : public DOMEventTargetHelper
friend class NotificationPermissionRequest;
friend class NotificationObserver;
friend class NotificationStorageCallback;
friend class ServiceWorkerNotificationObserver;
friend class WorkerNotificationObserver;
public:
@ -134,6 +135,30 @@ public:
const nsAString& aTitle,
const NotificationOptions& aOption,
ErrorResult& aRv);
/**
* Used when dispatching the ServiceWorkerEvent.
*
* Does not initialize the Notification's behavior.
* This is because:
* 1) The Notification is not shown to the user and so the behavior
* parameters don't matter.
* 2) The default binding requires main thread for parsing the JSON from the
* string behavior.
*/
static already_AddRefed<Notification>
ConstructFromFields(
nsIGlobalObject* aGlobal,
const nsAString& aID,
const nsAString& aTitle,
const nsAString& aDir,
const nsAString& aLang,
const nsAString& aBody,
const nsAString& aTag,
const nsAString& aIcon,
const nsAString& aData,
ErrorResult& aRv);
void GetID(nsAString& aRetval) {
aRetval = mID;
}
@ -249,6 +274,7 @@ public:
ErrorResult& rv);
bool DispatchClickEvent();
bool DispatchNotificationClickEvent();
protected:
// Callers MUST bind the Notification to the correct DOMEventTargetHelper parent using
// BindToOwner().
@ -301,6 +327,18 @@ protected:
aRetval = mAlertName;
}
void GetScope(nsAString& aScope)
{
aScope = mScope;
}
void
SetScope(const nsAString& aScope)
{
MOZ_ASSERT(mScope.IsEmpty());
mScope = aScope;
}
const nsString mID;
const nsString mTitle;
const nsString mBody;
@ -315,6 +353,7 @@ protected:
nsCOMPtr<nsIVariant> mData;
nsString mAlertName;
nsString mScope;
// Main thread only.
bool mIsClosed;

View File

@ -0,0 +1,26 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "NotificationEvent.h"
using namespace mozilla::dom;
BEGIN_WORKERS_NAMESPACE
NotificationEvent::NotificationEvent(EventTarget* aOwner)
: ExtendableEvent(aOwner)
{
}
NS_IMPL_ADDREF_INHERITED(NotificationEvent, ExtendableEvent)
NS_IMPL_RELEASE_INHERITED(NotificationEvent, ExtendableEvent)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(NotificationEvent)
NS_INTERFACE_MAP_END_INHERITING(ExtendableEvent)
NS_IMPL_CYCLE_COLLECTION_INHERITED(NotificationEvent, ExtendableEvent, mNotification)
END_WORKERS_NAMESPACE

View File

@ -0,0 +1,74 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_workers_notificationevent_h__
#define mozilla_dom_workers_notificationevent_h__
#include "mozilla/dom/Event.h"
#include "mozilla/dom/NotificationEventBinding.h"
#include "mozilla/dom/ServiceWorkerEvents.h"
#include "mozilla/dom/workers/Workers.h"
BEGIN_WORKERS_NAMESPACE
class ServiceWorker;
class ServiceWorkerClient;
class NotificationEvent final : public ExtendableEvent
{
protected:
explicit NotificationEvent(EventTarget* aOwner);
~NotificationEvent()
{}
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(NotificationEvent, ExtendableEvent)
NS_FORWARD_TO_EVENT
virtual JSObject* WrapObjectInternal(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override
{
return NotificationEventBinding::Wrap(aCx, this, aGivenProto);
}
static already_AddRefed<NotificationEvent>
Constructor(mozilla::dom::EventTarget* aOwner,
const nsAString& aType,
const NotificationEventInit& aOptions,
ErrorResult& aRv)
{
nsRefPtr<NotificationEvent> e = new NotificationEvent(aOwner);
bool trusted = e->Init(aOwner);
e->InitEvent(aType, aOptions.mBubbles, aOptions.mCancelable);
e->SetTrusted(trusted);
e->mNotification = aOptions.mNotification;
e->SetWantsPopupControlCheck(e->IsTrusted());
return e.forget();
}
static already_AddRefed<NotificationEvent>
Constructor(const GlobalObject& aGlobal,
const nsAString& aType,
const NotificationEventInit& aOptions,
ErrorResult& aRv)
{
nsCOMPtr<EventTarget> owner = do_QueryInterface(aGlobal.GetAsSupports());
return Constructor(owner, aType, aOptions, aRv);
}
already_AddRefed<Notification>
Notification_()
{
nsRefPtr<Notification> n = mNotification;
return n.forget();
}
private:
nsRefPtr<Notification> mNotification;
};
END_WORKERS_NAMESPACE
#endif /* mozilla_dom_workers_notificationevent_h__ */

View File

@ -4,7 +4,7 @@
"use strict";
const DEBUG = false;
const DEBUG = true;
function debug(s) { dump("-*- NotificationStorage.js: " + s + "\n"); }
const Cc = Components.classes;
@ -85,7 +85,7 @@ NotificationStorage.prototype = {
put: function(origin, id, title, dir, lang, body, tag, icon, alertName,
data, behavior) {
if (DEBUG) { debug("PUT: " + id + ": " + title); }
if (DEBUG) { debug("PUT: " + origin + " " + id + ": " + title); }
var notification = {
id: id,
title: title,
@ -134,6 +134,25 @@ NotificationStorage.prototype = {
}
},
getByID: function(origin, id, callback) {
if (DEBUG) { debug("GETBYID: " + origin + " " + id); }
var GetByIDProxyCallback = function(id, originalCallback) {
this.searchID = id;
this.originalCallback = originalCallback;
var self = this;
this.handle = function(id, title, dir, lang, body, tag, icon, data, behavior) {
if (id == this.searchID) {
self.originalCallback.handle(id, title, dir, lang, body, tag, icon, data, behavior);
}
};
this.done = function() {
self.originalCallback.done();
};
};
return this.get(origin, "", new GetByIDProxyCallback(id, callback));
},
delete: function(origin, id) {
if (DEBUG) { debug("DELETE: " + id); }
var notification = this._notifications[id];

View File

@ -18,11 +18,13 @@ EXTRA_JS_MODULES += [
EXPORTS.mozilla.dom += [
'DesktopNotification.h',
'Notification.h',
'NotificationEvent.h',
]
UNIFIED_SOURCES += [
'DesktopNotification.cpp',
'Notification.cpp',
'NotificationEvent.cpp',
]
FAIL_ON_WARNINGS = True

View File

@ -43,9 +43,10 @@ var MockServices = (function () {
setTimeout(function () {
listener.observe(null, "alertshow", cookie);
}, 100);
setTimeout(function () {
listener.observe(null, "alertclickcallback", cookie);
}, 100);
}
// ?? SpecialPowers.wrap(alertListener).observe(null, "alertclickcallback", cookie);
},
showAppNotification: function(aImageUrl, aTitle, aText, aAlertListener, aDetails) {

View File

@ -0,0 +1,26 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*
* The origin of this IDL file is
* http://notifications.spec.whatwg.org/
*
* Copyright:
* To the extent possible under law, the editors have waived all copyright and
* related or neighboring rights to this work.
*/
[Constructor(DOMString type, optional NotificationEventInit eventInitDict),
Exposed=ServiceWorker,Func="mozilla::dom::Notification::PrefEnabled"]
interface NotificationEvent : ExtendableEvent {
readonly attribute Notification notification;
};
dictionary NotificationEventInit : ExtendableEventInit {
required Notification notification;
};
partial interface ServiceWorkerGlobalScope {
attribute EventHandler onnotificationclick;
};

View File

@ -335,6 +335,7 @@ WEBIDL_FILES = [
'NodeIterator.webidl',
'NodeList.webidl',
'Notification.webidl',
'NotificationEvent.webidl',
'NotifyPaintEvent.webidl',
'OfflineAudioCompletionEvent.webidl',
'OfflineAudioContext.webidl',

View File

@ -39,6 +39,7 @@
#include "mozilla/dom/indexedDB/IDBFactory.h"
#include "mozilla/dom/InternalHeaders.h"
#include "mozilla/dom/Navigator.h"
#include "mozilla/dom/NotificationEvent.h"
#include "mozilla/dom/PromiseNativeHandler.h"
#include "mozilla/dom/Request.h"
#include "mozilla/dom/RootedDictionary.h"
@ -2282,6 +2283,117 @@ ServiceWorkerManager::SendPushSubscriptionChangeEvent(const nsACString& aOriginA
#endif
}
class SendNotificationClickEventRunnable final : public WorkerRunnable
{
nsMainThreadPtrHandle<ServiceWorker> mServiceWorker;
const nsString mID;
const nsString mTitle;
const nsString mDir;
const nsString mLang;
const nsString mBody;
const nsString mTag;
const nsString mIcon;
const nsString mData;
const nsString mBehavior;
public:
SendNotificationClickEventRunnable(
WorkerPrivate* aWorkerPrivate,
nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker,
const nsAString& aID,
const nsAString& aTitle,
const nsAString& aDir,
const nsAString& aLang,
const nsAString& aBody,
const nsAString& aTag,
const nsAString& aIcon,
const nsAString& aData,
const nsAString& aBehavior)
: WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
, mServiceWorker(aServiceWorker)
, mID(aID)
, mTitle(aTitle)
, mDir(aDir)
, mLang(aLang)
, mBody(aBody)
, mTag(aTag)
, mIcon(aIcon)
, mData(aData)
, mBehavior(aBehavior)
{
AssertIsOnMainThread();
MOZ_ASSERT(aWorkerPrivate);
MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
}
bool
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
{
MOZ_ASSERT(aWorkerPrivate);
nsRefPtr<EventTarget> target = do_QueryObject(aWorkerPrivate->GlobalScope());
ErrorResult result;
nsRefPtr<Notification> notification =
Notification::ConstructFromFields(aWorkerPrivate->GlobalScope(), mID, mTitle, mDir, mLang, mBody, mTag, mIcon, mData, result);
if (NS_WARN_IF(result.Failed())) {
return false;
}
NotificationEventInit nei;
nei.mNotification = notification;
nei.mBubbles = false;
nei.mCancelable = true;
nsRefPtr<NotificationEvent> event =
NotificationEvent::Constructor(target, NS_LITERAL_STRING("notificationclick"), nei, result);
if (NS_WARN_IF(result.Failed())) {
return false;
}
event->SetTrusted(true);
target->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
return true;
}
};
NS_IMETHODIMP
ServiceWorkerManager::SendNotificationClickEvent(const nsACString& aOriginSuffix,
const nsACString& aScope,
const nsAString& aID,
const nsAString& aTitle,
const nsAString& aDir,
const nsAString& aLang,
const nsAString& aBody,
const nsAString& aTag,
const nsAString& aIcon,
const nsAString& aData,
const nsAString& aBehavior)
{
OriginAttributes attrs;
if (!attrs.PopulateFromSuffix(aOriginSuffix)) {
return NS_ERROR_INVALID_ARG;
}
nsRefPtr<ServiceWorker> serviceWorker = CreateServiceWorkerForScope(attrs, aScope);
if (!serviceWorker) {
return NS_ERROR_FAILURE;
}
nsMainThreadPtrHandle<ServiceWorker> serviceWorkerHandle(
new nsMainThreadPtrHolder<ServiceWorker>(serviceWorker));
nsRefPtr<SendNotificationClickEventRunnable> r =
new SendNotificationClickEventRunnable(serviceWorker->GetWorkerPrivate(), serviceWorkerHandle, aID, aTitle, aDir, aLang, aBody, aTag, aIcon, aData, aBehavior);
AutoJSAPI jsapi;
jsapi.Init();
if (NS_WARN_IF(!r->Dispatch(jsapi.cx()))) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
ServiceWorkerManager::GetReadyPromise(nsIDOMWindow* aWindow,
nsISupports** aPromise)

View File

@ -93,6 +93,8 @@ support-files =
client_focus_worker.js
bug1151916_worker.js
bug1151916_driver.html
notificationclick.html
notificationclick.js
worker_updatefoundevent.js
worker_updatefoundevent2.js
updatefoundevent.html
@ -218,6 +220,7 @@ skip-if = !debug
[test_request_context_xslt.html]
[test_scopes.html]
[test_sandbox_intercept.html]
[test_notificationclick.html]
[test_notification_constructor_error.html]
[test_sanitize.html]
[test_sanitize_domain.html]

View File

@ -0,0 +1,27 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<head>
<title>Bug 1114554 - controlled page</title>
<script class="testbody" type="text/javascript">
var testWindow = parent;
if (opener) {
testWindow = opener;
}
navigator.serviceWorker.ready.then(function(swr) {
swr.showNotification("Hi there. The ServiceWorker should receive a click event for this.");
});
navigator.serviceWorker.onmessage = function(msg) {
testWindow.callback();
};
</script>
</head>
<body>
</body>
</html>

View File

@ -0,0 +1,15 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/publicdomain/zero/1.0/
//
onnotificationclick = function(e) {
self.clients.matchAll().then(function(clients) {
if (clients.length === 0) {
dump("********************* CLIENTS LIST EMPTY! Test will timeout! ***********************\n");
return;
}
clients.forEach(function(client) {
client.postMessage("done");
});
});
}

View File

@ -0,0 +1,56 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=916893
-->
<head>
<title>Bug 1114554 - Test ServiceWorkerGlobalScope.notificationclick event.</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/dom/tests/mochitest/notification/MockServices.js"></script>
<script type="text/javascript" src="/tests/dom/tests/mochitest/notification/NotificationTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1114554">Bug 1114554</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
<script type="text/javascript">
SimpleTest.requestFlakyTimeout("Mock alert service dispatches show and click events.");
function testFrame(src) {
var iframe = document.createElement("iframe");
iframe.src = src;
window.callback = function() {
window.callback = null;
document.body.removeChild(iframe);
iframe = null;
ok(true, "Got notificationclick event.");
MockServices.unregister();
SimpleTest.finish();
};
document.body.appendChild(iframe);
}
function runTest() {
MockServices.register();
testFrame('notificationclick.html');
navigator.serviceWorker.register("notificationclick.js", { scope: "notificationclick.html" }).then(function(reg) {
}, function(e) {
ok(false, "registration should have passed!");
});
};
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [
["dom.serviceWorkers.exemptFromPerDomainMax", true],
["dom.serviceWorkers.enabled", true],
["dom.serviceWorkers.testing.enabled", true],
["dom.webnotifications.workers.enabled", true],
["notification.prompt.testing", true],
]}, runTest);
</script>
</body>
</html>

View File

@ -158,6 +158,8 @@ var interfaceNamesInGlobalScope =
"MessagePort",
// IMPORTANT: Do not change this list without review from a DOM peer!
"Notification",
// IMPORTANT: Do not change this list without review from a DOM peer!
"NotificationEvent",
// IMPORTANT: Do not change this list without review from a DOM peer!
"Performance",
// IMPORTANT: Do not change this list without review from a DOM peer!

View File

@ -18,7 +18,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=916893
<pre id="test">
</pre>
<script type="text/javascript">
SimpleTest.requestFlakyTimeout("Mock alert service dispatches show event.");
SimpleTest.requestFlakyTimeout("Mock alert service dispatches show and click events.");
function runTest() {
MockServices.register();

View File

@ -1022,7 +1022,7 @@ pref("dom.allow_scripts_to_close_windows", false);
pref("dom.disable_open_during_load", false);
pref("dom.popup_maximum", 20);
pref("dom.popup_allowed_events", "change click dblclick mouseup reset submit touchend");
pref("dom.popup_allowed_events", "change click dblclick mouseup notificationclick reset submit touchend");
pref("dom.disable_open_click_delay", 1000);
pref("dom.storage.enabled", true);