Commit eb322fc9 authored by peter's avatar peter Committed by Commit bot

Support persistent notifications in the PlatformNotificationServiceImpl.

This patch makes it possible to create and use persistent notifications based
on delegates. This works reliable on desktop, but does not yet implement the
after-browser-restart call for Android.

BUG=432527

Review URL: https://codereview.chromium.org/784383002

Cr-Commit-Position: refs/heads/master@{#308353}
parent cf2d9283
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/notifications/persistent_notification_delegate.h"
#include "base/bind.h"
#include "base/guid.h"
#include "chrome/browser/notifications/platform_notification_service_impl.h"
#include "content/public/common/persistent_notification_status.h"
namespace {
// Persistent notifications fired through the delegate do not care about the
// lifetime of the Service Worker responsible for executing the event.
void OnEventDispatchComplete(content::PersistentNotificationStatus status) {}
} // namespace
PersistentNotificationDelegate::PersistentNotificationDelegate(
content::BrowserContext* browser_context,
int64 service_worker_registration_id,
const GURL& origin,
const content::PlatformNotificationData& notification_data)
: browser_context_(browser_context),
service_worker_registration_id_(service_worker_registration_id),
origin_(origin),
notification_data_(notification_data),
id_(base::GenerateGUID()) {}
PersistentNotificationDelegate::~PersistentNotificationDelegate() {}
void PersistentNotificationDelegate::Display() {}
void PersistentNotificationDelegate::Close(bool by_user) {}
void PersistentNotificationDelegate::Click() {
PlatformNotificationServiceImpl::GetInstance()->OnPersistentNotificationClick(
browser_context_,
service_worker_registration_id_,
id_,
origin_,
notification_data_,
base::Bind(&OnEventDispatchComplete));
}
std::string PersistentNotificationDelegate::id() const {
return id_;
}
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_NOTIFICATIONS_PERSISTENT_NOTIFICATION_DELEGATE_H_
#define CHROME_BROWSER_NOTIFICATIONS_PERSISTENT_NOTIFICATION_DELEGATE_H_
#include <string>
#include "chrome/browser/notifications/notification_delegate.h"
#include "content/public/common/platform_notification_data.h"
#include "url/gurl.h"
namespace content {
class BrowserContext;
}
// Delegate responsible for listening to the click event on persistent
// notifications, to forward them to the PlatformNotificationService so that
// JavaScript events can be fired on the associated Service Worker.
class PersistentNotificationDelegate : public NotificationDelegate {
public:
PersistentNotificationDelegate(
content::BrowserContext* browser_context,
int64 service_worker_registration_id,
const GURL& origin,
const content::PlatformNotificationData& notification_data);
// NotificationDelegate implementation.
void Display() override;
void Close(bool by_user) override;
void Click() override;
std::string id() const override;
protected:
~PersistentNotificationDelegate() override;
private:
content::BrowserContext* browser_context_;
int64 service_worker_registration_id_;
GURL origin_;
content::PlatformNotificationData notification_data_;
std::string id_;
};
#endif // CHROME_BROWSER_NOTIFICATIONS_PERSISTENT_NOTIFICATION_DELEGATE_H_
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <string>
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/memory/scoped_ptr.h"
#include "chrome/browser/infobars/infobar_service.h"
#include "chrome/browser/notifications/notification_test_util.h"
#include "chrome/browser/notifications/platform_notification_service_impl.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/infobars/core/confirm_infobar_delegate.h"
#include "components/infobars/core/infobar.h"
#include "components/infobars/core/infobar_manager.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test_utils.h"
#include "net/test/spawned_test_server/spawned_test_server.h"
// -----------------------------------------------------------------------------
// Accept or rejects the first shown confirm infobar. The infobar will be
// responsed to asynchronously, to imitate the behavior of a user.
// TODO(peter): Generalize this class, as it's commonly useful.
class InfoBarResponder : public infobars::InfoBarManager::Observer {
public:
InfoBarResponder(Browser* browser, bool accept);
~InfoBarResponder() override;
// infobars::InfoBarManager::Observer overrides.
void OnInfoBarAdded(infobars::InfoBar* infobar) override;
private:
void Respond(ConfirmInfoBarDelegate* delegate);
InfoBarService* infobar_service_;
bool accept_;
bool has_observed_;
};
InfoBarResponder::InfoBarResponder(Browser* browser, bool accept)
: infobar_service_(InfoBarService::FromWebContents(
browser->tab_strip_model()->GetActiveWebContents())),
accept_(accept),
has_observed_(false) {
infobar_service_->AddObserver(this);
}
InfoBarResponder::~InfoBarResponder() {
infobar_service_->RemoveObserver(this);
}
void InfoBarResponder::OnInfoBarAdded(infobars::InfoBar* infobar) {
if (has_observed_)
return;
has_observed_ = true;
ConfirmInfoBarDelegate* delegate =
infobar->delegate()->AsConfirmInfoBarDelegate();
DCHECK(delegate);
// Respond to the infobar asynchronously, like a person.
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(
&InfoBarResponder::Respond, base::Unretained(this), delegate));
}
void InfoBarResponder::Respond(ConfirmInfoBarDelegate* delegate) {
if (accept_)
delegate->Accept();
else
delegate->Cancel();
}
// -----------------------------------------------------------------------------
class PlatformNotificationServiceBrowserTest : public InProcessBrowserTest {
public:
~PlatformNotificationServiceBrowserTest() override {}
// InProcessBrowserTest overrides.
void SetUpCommandLine(base::CommandLine* command_line) override;
void SetUp() override;
void SetUpOnMainThread() override;
void TearDown() override;
protected:
// Returns the Platform Notification Service these unit tests are for.
PlatformNotificationServiceImpl* service() const {
return PlatformNotificationServiceImpl::GetInstance();
}
// Returns the UI Manager on which notifications will be displayed.
StubNotificationUIManager* ui_manager() const { return ui_manager_.get(); }
// Navigates the browser to the test page indicated by |path|.
void NavigateToTestPage(const std::string& path) const;
// Executes |script| and stores the result as a string in |result|. A boolean
// will be returned, indicating whether the script was executed successfully.
bool RunScript(const std::string& script, std::string* result) const;
private:
scoped_ptr<StubNotificationUIManager> ui_manager_;
scoped_ptr<net::SpawnedTestServer> https_server_;
};
// -----------------------------------------------------------------------------
namespace {
const char kTestPageUrl[] =
"files/notifications/platform_notification_service.html";
} // namespace
void PlatformNotificationServiceBrowserTest::SetUpCommandLine(
base::CommandLine* command_line) {
command_line->AppendSwitch(
switches::kEnableExperimentalWebPlatformFeatures);
InProcessBrowserTest::SetUpCommandLine(command_line);
}
void PlatformNotificationServiceBrowserTest::SetUp() {
ui_manager_.reset(new StubNotificationUIManager);
https_server_.reset(new net::SpawnedTestServer(
net::SpawnedTestServer::TYPE_HTTPS,
net::BaseTestServer::SSLOptions(
net::BaseTestServer::SSLOptions::CERT_OK),
base::FilePath(FILE_PATH_LITERAL("chrome/test/data/"))));
ASSERT_TRUE(https_server_->Start());
service()->SetNotificationUIManagerForTesting(ui_manager_.get());
InProcessBrowserTest::SetUp();
}
void PlatformNotificationServiceBrowserTest::SetUpOnMainThread() {
NavigateToTestPage(kTestPageUrl);
InProcessBrowserTest::SetUpOnMainThread();
}
void PlatformNotificationServiceBrowserTest::TearDown() {
service()->SetNotificationUIManagerForTesting(nullptr);
}
void PlatformNotificationServiceBrowserTest::NavigateToTestPage(
const std::string& path) const {
ui_test_utils::NavigateToURL(browser(), https_server_->GetURL(path));
}
bool PlatformNotificationServiceBrowserTest::RunScript(
const std::string& script, std::string* result) const {
return content::ExecuteScriptAndExtractString(
browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame(),
script,
result);
}
// -----------------------------------------------------------------------------
// TODO(peter): Move PlatformNotificationService-related tests over from
// notification_browsertest.cc to this file.
IN_PROC_BROWSER_TEST_F(PlatformNotificationServiceBrowserTest,
DisplayPersistentNotificationWithoutPermission) {
std::string script_result;
InfoBarResponder accepting_responder(browser(), false);
ASSERT_TRUE(RunScript("RequestPermission()", &script_result));
EXPECT_EQ("denied", script_result);
ASSERT_TRUE(RunScript("DisplayPersistentNotification()", &script_result));
EXPECT_EQ(
"TypeError: No notification permission has been granted for this origin.",
script_result);
ASSERT_EQ(0u, ui_manager()->GetNotificationCount());
}
IN_PROC_BROWSER_TEST_F(PlatformNotificationServiceBrowserTest,
DisplayPersistentNotificationWithPermission) {
std::string script_result;
InfoBarResponder accepting_responder(browser(), true);
ASSERT_TRUE(RunScript("RequestPermission()", &script_result));
EXPECT_EQ("granted", script_result);
ASSERT_TRUE(RunScript("DisplayPersistentNotification('action_none')",
&script_result));
EXPECT_EQ("ok", script_result);
ASSERT_EQ(1u, ui_manager()->GetNotificationCount());
const Notification& notification = ui_manager()->GetNotificationAt(0);
notification.delegate()->Click();
ASSERT_TRUE(RunScript("GetMessageFromWorker()", &script_result));
EXPECT_EQ("action_none", script_result);
ASSERT_EQ(1u, ui_manager()->GetNotificationCount());
}
IN_PROC_BROWSER_TEST_F(PlatformNotificationServiceBrowserTest,
CloseDisplayedPersistentNotification) {
std::string script_result;
InfoBarResponder accepting_responder(browser(), true);
ASSERT_TRUE(RunScript("RequestPermission()", &script_result));
EXPECT_EQ("granted", script_result);
ASSERT_TRUE(RunScript("DisplayPersistentNotification('action_close')",
&script_result));
EXPECT_EQ("ok", script_result);
ASSERT_EQ(1u, ui_manager()->GetNotificationCount());
const Notification& notification = ui_manager()->GetNotificationAt(0);
notification.delegate()->Click();
ASSERT_TRUE(RunScript("GetMessageFromWorker()", &script_result));
EXPECT_EQ("action_close", script_result);
ASSERT_EQ(0u, ui_manager()->GetNotificationCount());
}
......@@ -6,15 +6,16 @@
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/notifications/notification.h"
#include "chrome/browser/notifications/notification_object_proxy.h"
#include "chrome/browser/notifications/notification_ui_manager.h"
#include "chrome/browser/notifications/persistent_notification_delegate.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_io_data.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/content_settings.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/desktop_notification_delegate.h"
#include "content/public/browser/notification_event_dispatcher.h"
#include "content/public/common/platform_notification_data.h"
#include "ui/message_center/notifier_settings.h"
......@@ -51,12 +52,31 @@ PlatformNotificationServiceImpl::PlatformNotificationServiceImpl()
PlatformNotificationServiceImpl::~PlatformNotificationServiceImpl() {}
void PlatformNotificationServiceImpl::OnPersistentNotificationClick(
content::BrowserContext* browser_context,
int64 service_worker_registration_id,
const std::string& notification_id,
const GURL& origin,
const content::PlatformNotificationData& notification_data,
const base::Callback<void(content::PersistentNotificationStatus)>&
callback) const {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
content::NotificationEventDispatcher::GetInstance()
->DispatchNotificationClickEvent(
browser_context,
origin,
service_worker_registration_id,
notification_id,
notification_data,
callback);
}
blink::WebNotificationPermission
PlatformNotificationServiceImpl::CheckPermission(
content::ResourceContext* resource_context,
const GURL& origin,
int render_process_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK_CURRENTLY_ON(BrowserThread::IO);
ProfileIOData* io_data = ProfileIOData::FromResourceContext(resource_context);
#if defined(ENABLE_EXTENSIONS)
......@@ -102,31 +122,20 @@ void PlatformNotificationServiceImpl::DisplayNotification(
scoped_ptr<content::DesktopNotificationDelegate> delegate,
int render_process_id,
base::Closure* cancel_callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK_CURRENTLY_ON(BrowserThread::UI);
Profile* profile = Profile::FromBrowserContext(browser_context);
DCHECK(profile);
NotificationObjectProxy* proxy = new NotificationObjectProxy(delegate.Pass());
base::string16 display_source = DisplayNameForOriginInProcessId(
profile, origin, render_process_id);
// TODO(peter): Icons for Web Notifications are currently always requested for
// 1x scale, whereas the displays on which they can be displayed can have a
// different pixel density. Be smarter about this when the API gets updated
// with a way for developers to specify images of different resolutions.
Notification notification(origin, notification_data.title,
notification_data.body, gfx::Image::CreateFrom1xBitmap(icon),
display_source, notification_data.tag, proxy);
// Web Notifications do not timeout.
notification.set_never_timeout(true);
Notification notification = CreateNotificationFromData(
profile, origin, icon, notification_data, proxy, render_process_id);
GetNotificationUIManager()->Add(notification, profile);
if (cancel_callback)
*cancel_callback =
base::Bind(&CancelNotification,
proxy->id(),
notification.delegate_id(),
NotificationUIManager::GetProfileID(profile));
profile->GetHostContentSettingsMap()->UpdateLastUsage(
......@@ -140,13 +149,60 @@ void PlatformNotificationServiceImpl::DisplayPersistentNotification(
const SkBitmap& icon,
const content::PlatformNotificationData& notification_data,
int render_process_id) {
NOTIMPLEMENTED();
DCHECK_CURRENTLY_ON(BrowserThread::UI);
Profile* profile = Profile::FromBrowserContext(browser_context);
DCHECK(profile);
PersistentNotificationDelegate* delegate = new PersistentNotificationDelegate(
browser_context,
service_worker_registration_id,
origin,
notification_data);
Notification notification = CreateNotificationFromData(
profile, origin, icon, notification_data, delegate, render_process_id);
GetNotificationUIManager()->Add(notification, profile);
profile->GetHostContentSettingsMap()->UpdateLastUsage(
origin, origin, CONTENT_SETTINGS_TYPE_NOTIFICATIONS);
}
void PlatformNotificationServiceImpl::ClosePersistentNotification(
content::BrowserContext* browser_context,
const std::string& persistent_notification_id) {
NOTIMPLEMENTED();
DCHECK_CURRENTLY_ON(BrowserThread::UI);
Profile* profile = Profile::FromBrowserContext(browser_context);
DCHECK(profile);
GetNotificationUIManager()->CancelById(
persistent_notification_id, NotificationUIManager::GetProfileID(profile));
}
Notification PlatformNotificationServiceImpl::CreateNotificationFromData(
Profile* profile,
const GURL& origin,
const SkBitmap& icon,
const content::PlatformNotificationData& notification_data,
NotificationDelegate* delegate,
int render_process_id) const {
base::string16 display_source = DisplayNameForOriginInProcessId(
profile, origin, render_process_id);
// TODO(peter): Icons for Web Notifications are currently always requested for
// 1x scale, whereas the displays on which they can be displayed can have a
// different pixel density. Be smarter about this when the API gets updated
// with a way for developers to specify images of different resolutions.
Notification notification(origin, notification_data.title,
notification_data.body, gfx::Image::CreateFrom1xBitmap(icon),
display_source, notification_data.tag, delegate);
// Web Notifications do not timeout.
notification.set_never_timeout(true);
return notification;
}
NotificationUIManager*
......@@ -163,7 +219,7 @@ void PlatformNotificationServiceImpl::SetNotificationUIManagerForTesting(
}
base::string16 PlatformNotificationServiceImpl::DisplayNameForOriginInProcessId(
Profile* profile, const GURL& origin, int process_id) {
Profile* profile, const GURL& origin, int process_id) const {
#if defined(ENABLE_EXTENSIONS)
// If the source is an extension, lookup the display name.
if (origin.SchemeIs(extensions::kExtensionScheme)) {
......
......@@ -8,8 +8,11 @@
#include "base/gtest_prod_util.h"
#include "base/memory/singleton.h"
#include "base/strings/string16.h"
#include "chrome/browser/notifications/notification.h"
#include "content/public/browser/platform_notification_service.h"
#include "content/public/common/persistent_notification_status.h"
class NotificationDelegate;
class NotificationUIManager;
class Profile;
......@@ -22,6 +25,18 @@ class PlatformNotificationServiceImpl
// be called from any thread.
static PlatformNotificationServiceImpl* GetInstance();
// To be called when a persistent notification has been clicked on. The
// Service Worker associated with the registration will be started if
// needed, on which the event will be fired. Must be called on the UI thread.
void OnPersistentNotificationClick(
content::BrowserContext* browser_context,
int64 service_worker_registration_id,
const std::string& notification_id,
const GURL& origin,
const content::PlatformNotificationData& notification_data,
const base::Callback<void(content::PersistentNotificationStatus)>&
callback) const;
// Returns the Notification UI Manager through which notifications can be
// displayed to the user. Can be overridden for testing.
NotificationUIManager* GetNotificationUIManager() const;
......@@ -52,6 +67,7 @@ class PlatformNotificationServiceImpl
private:
friend struct DefaultSingletonTraits<PlatformNotificationServiceImpl>;
friend class PlatformNotificationServiceBrowserTest;
friend class PlatformNotificationServiceTest;
FRIEND_TEST_ALL_PREFIXES(
PlatformNotificationServiceTest, DisplayNameForOrigin);
......@@ -59,6 +75,17 @@ class PlatformNotificationServiceImpl
PlatformNotificationServiceImpl();
~PlatformNotificationServiceImpl() override;
// Creates a new Web Notification-based Notification object.
// TODO(peter): |delegate| can be a scoped_refptr, but properly passing this
// through requires changing a whole lot of Notification constructor calls.
Notification CreateNotificationFromData(
Profile* profile,
const GURL& origin,
const SkBitmap& icon,
const content::PlatformNotificationData& notification_data,
NotificationDelegate* delegate,
int render_process_id) const;
// Overrides the Notification UI Manager to use to |manager|. Only to be
// used by tests. Tests are responsible for cleaning up after themselves.
void SetNotificationUIManagerForTesting(NotificationUIManager* manager);
......@@ -68,7 +95,7 @@ class PlatformNotificationServiceImpl
// from the origin itself when dealing with extensions.
base::string16 DisplayNameForOriginInProcessId(Profile* profile,
const GURL& origin,
int process_id);
int process_id) const;
// Weak reference. Ownership maintains with the test.
NotificationUIManager* notification_ui_manager_for_tests_;
......
......@@ -3,9 +3,11 @@
// found in the LICENSE file.
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "chrome/browser/notifications/notification_test_util.h"
#include "chrome/browser/notifications/platform_notification_service_impl.h"
#include "chrome/test/base/testing_profile.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "content/public/browser/desktop_notification_delegate.h"
#include "content/public/common/platform_notification_data.h"
#include "content/public/test/test_browser_thread_bundle.h"
......@@ -74,7 +76,7 @@ class PlatformNotificationServiceTest : public testing::Test {
new MockDesktopNotificationDelegate();
service()->DisplayNotification(profile(),
GURL("https://example.com/"),
GURL("https://chrome.com/"),
SkBitmap(),
notification_data,
make_scoped_ptr(delegate),
......@@ -124,6 +126,32 @@ TEST_F(PlatformNotificationServiceTest, DisplayPageCloseClosure) {
// delegate given that it'd result in a use-after-free.
}
TEST_F(PlatformNotificationServiceTest, PersistentNotificationDisplay) {
content::PlatformNotificationData notification_data;
notification_data.title = base::ASCIIToUTF16("My notification's title");
notification_data.body = base::ASCIIToUTF16("Hello, world!");
service()->DisplayPersistentNotification(profile(),
42 /* sw_registration_id */,
GURL("https://chrome.com/"),
SkBitmap(),
notification_data,
0 /* render_process_id */);
ASSERT_EQ(1u, ui_manager()->GetNotificationCount());
const Notification& notification = ui_manager()->GetNotificationAt(0);
EXPECT_EQ("https://chrome.com/", notification.origin_url().spec());
EXPECT_EQ("My notification's title",
base::UTF16ToUTF8(notification.title()));
EXPECT_EQ("Hello, world!",
base::UTF16ToUTF8(notification.message()));
service()->ClosePersistentNotification(profile(),
notification.delegate_id());
EXPECT_EQ(0u, ui_manager()->GetNotificationCount());
}
TEST_F(PlatformNotificationServiceTest, DisplayPageNotificationMatches) {
content::PlatformNotificationData notification_data;
notification_data.title = base::ASCIIToUTF16("My notification's title");
......@@ -160,3 +188,29 @@ TEST_F(PlatformNotificationServiceTest, DisplayNameForOrigin) {
// TODO(peter): Include unit tests for the extension-name translation
// functionality of DisplayNameForOriginInProcessId.
}
TEST_F(PlatformNotificationServiceTest, NotificationPermissionLastUsage) {
// Both page and persistent notifications should update the last usage
// time of the notification permission for the origin.
GURL origin("https://chrome.com/");
base::Time begin_time;
CreateSimplePageNotification();
base::Time after_page_notification =
profile()->GetHostContentSettingsMap()->GetLastUsage(
origin, origin, CONTENT_SETTINGS_TYPE_NOTIFICATIONS);
EXPECT_GT(after_page_notification, begin_time);
service()->DisplayPersistentNotification(profile(),
42 /* sw_registration_id */,
origin,
SkBitmap(),
content::PlatformNotificationData(),
0 /* render_process_id */);
base::Time after_persistent_notification =
profile()->GetHostContentSettingsMap()->GetLastUsage(
origin, origin, CONTENT_SETTINGS_TYPE_NOTIFICATIONS);
EXPECT_GT(after_persistent_notification, after_page_notification);
}
......@@ -1827,6 +1827,8 @@
'browser/notifications/notification_ui_manager.h',
'browser/notifications/notification_ui_manager_android.cc',
'browser/notifications/notification_ui_manager_android.h',
'browser/notifications/persistent_notification_delegate.cc',
'browser/notifications/persistent_notification_delegate.h',
'browser/notifications/platform_notification_service_impl.cc',
'browser/notifications/platform_notification_service_impl.h',
'browser/notifications/profile_notification.cc',
......
......@@ -1479,6 +1479,7 @@
# cross-platform panels).
'sources': [
'browser/notifications/notification_browsertest.cc',
'browser/notifications/platform_notification_service_browsertest.cc',
],
}],
],
......@@ -1490,6 +1491,7 @@
# TODO(peter): Enable the Notification browser tests.
'browser/notifications/notification_browsertest.cc',
'browser/notifications/platform_notification_service_browsertest.cc',
]
}],
['toolkit_views==1', {
......
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Platform Notification Service BrowserTest service page</title>
</head>
<body>
<!-- This page is intended to be used by the
PlatformNotificationServiceBrowserTest. -->
<script>
var messagePort = null,
messageStack = [],
expectingMessage = false;
// Requests permission to display Web Notifications. Will return the
// permission level to the DOM Automation Controller.
function RequestPermission() {
Notification.requestPermission(function (level) {
domAutomationController.send(level);
});
}
// Returns a promise that will be resolved with an activated Service
// Worker, or rejects when the Service Worker could not be started. There
// will be a message port to and from the worker in |messagePort|.
// TODO(peter): Generalize this in some sort of Service Worker utility
// JavaScript file so that other tests can re-use the same logic.
function GetActivatedServiceWorker(script, scope) {
return navigator.serviceWorker.getRegistration(scope)
.then(function (registration) {
// Unregister any existing Service Worker.
if (registration)
return registration.unregister();
}).then(function () {
// Register the Service Worker again.
return navigator.serviceWorker.register(script, { scope: scope });
}).then(function (registration) {
if (registration.active) {
return registration;
} else if (registration.waiting || registration.installing) {
var worker = registration.waiting || registration.installing;
return new Promise(function (resolve) {
worker.addEventListener('statechange', function () {
if (worker.state === 'activated')
resolve(registration);
});
});
} else {
return Promise.reject('Service Worker in invalid state.');
}
}).then(function (registration) {
return new Promise(function (resolve) {
var channel = new MessageChannel();
channel.port1.addEventListener('message', function (event) {
if (event.data == 'ready')
resolve(registration);
});
registration.active.postMessage(channel.port2,
[ channel.port2 ]);
messagePort = channel.port1;
messagePort.start();
});
});
}
// Renews the registered Service Worker registration for this page, then
// displays a notification on the activated ServiceWorkerRegistration.
function DisplayPersistentNotification(title) {
GetActivatedServiceWorker('platform_notification_service.js',
location.pathname)
.then(function (registration) {
return registration.showNotification(title, {
body: 'Hello, world!',
icon: 'icon.png'
});
}).then(function () {
messagePort.addEventListener('message', function (event) {
if (expectingMessage)
domAutomationController.send(event.data);
else
messageStack.push(event.data);
});
domAutomationController.send('ok');
}).catch(function (error) {
domAutomationController.send('' + error);
});
}
// Returns the latest received message from the worker. If no message has
// been received, nothing will be done. For successfully registered
// Service Workers this is OK, however, since the "message" event handler
// in DisplayPersistentNotification will take care of notifying the DOM
// Automation Controller instead.
function GetMessageFromWorker() {
if (!messageStack.length) {
expectingMessage = true;
return;
}
domAutomationController.send('' + messageStack.pop());
}
</script>
</body>
</html>
\ No newline at end of file
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Service Worker to be used with the platform_notification_service.html page.
var messagePort = null;
addEventListener('message', function (event) {
messagePort = event.data;
messagePort.postMessage('ready');
});
// The notificationclick event will be invoked when a persistent notification
// has been clicked on. When this happens, the title determines whether this
// Service Worker has to act upon this.
addEventListener('notificationclick', function (event) {
if (event.notification.title == 'action_close')
event.notification.close();
messagePort.postMessage(event.notification.title);
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment