зеркало из https://github.com/mozilla/gecko-dev.git
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 --HG-- extra : rebase_source : b536cc6a6e0ba212f225a3638cf4140635628047
This commit is contained in:
Родитель
7df5ce7b58
Коммит
1abb9e59d8
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
|
@ -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__ */
|
||||
|
|
@ -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];
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
};
|
|
@ -330,6 +330,7 @@ WEBIDL_FILES = [
|
|||
'NodeIterator.webidl',
|
||||
'NodeList.webidl',
|
||||
'Notification.webidl',
|
||||
'NotificationEvent.webidl',
|
||||
'NotifyPaintEvent.webidl',
|
||||
'OfflineAudioCompletionEvent.webidl',
|
||||
'OfflineAudioContext.webidl',
|
||||
|
|
|
@ -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"
|
||||
|
@ -2269,6 +2270,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)
|
||||
|
|
|
@ -89,6 +89,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
|
||||
|
@ -186,6 +188,7 @@ support-files =
|
|||
skip-if = toolkit == 'android' # Bug 1163410
|
||||
[test_scopes.html]
|
||||
[test_sandbox_intercept.html]
|
||||
[test_notificationclick.html]
|
||||
[test_notification_constructor_error.html]
|
||||
[test_sanitize.html]
|
||||
[test_sanitize_domain.html]
|
||||
|
|
|
@ -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>
|
|
@ -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");
|
||||
});
|
||||
});
|
||||
}
|
|
@ -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>
|
|
@ -152,6 +152,8 @@ var interfaceNamesInGlobalScope =
|
|||
"MessageEvent",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"MessagePort",
|
||||
// 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!
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -1007,7 +1007,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);
|
||||
|
|
Загрузка…
Ссылка в новой задаче