Add support for Chrome Alarms Extension API (issue #1947)
This commit is contained in:
parent
64e6971099
commit
dddfce456c
|
@ -430,6 +430,7 @@
|
|||
'tests/ceftests/download_unittest.cc',
|
||||
'tests/ceftests/draggable_regions_unittest.cc',
|
||||
'tests/ceftests/extensions/background_unittest.cc',
|
||||
'tests/ceftests/extensions/chrome_alarms_unittest.cc',
|
||||
'tests/ceftests/extensions/chrome_tabs_unittest.cc',
|
||||
'tests/ceftests/extensions/extension_test_handler.cc',
|
||||
'tests/ceftests/extensions/extension_test_handler.h',
|
||||
|
|
|
@ -7,12 +7,14 @@
|
|||
#include "chrome/browser/content_settings/cookie_settings_factory.h"
|
||||
#include "chrome/browser/extensions/api/streams_private/streams_private_api.h"
|
||||
#include "chrome/browser/ui/prefs/prefs_tab_helper.h"
|
||||
#include "extensions/browser/api/alarms/alarm_manager.h"
|
||||
#include "extensions/browser/renderer_startup_helper.h"
|
||||
|
||||
namespace extensions {
|
||||
namespace cef {
|
||||
|
||||
void EnsureBrowserContextKeyedServiceFactoriesBuilt() {
|
||||
AlarmManager::GetFactoryInstance();
|
||||
CookieSettingsFactory::GetInstance();
|
||||
PrefsTabHelper::GetServiceInstance();
|
||||
RendererStartupHelperFactory::GetInstance();
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include "chrome/browser/extensions/api/resources_private/resources_private_api.h"
|
||||
#include "chrome/browser/extensions/api/streams_private/streams_private_api.h"
|
||||
#include "extensions/browser/api/alarms/alarms_api.h"
|
||||
#include "extensions/browser/extension_function_registry.h"
|
||||
|
||||
namespace extensions {
|
||||
|
@ -29,6 +30,12 @@ const char* const kSupportedAPIs[] = {
|
|||
EXTENSION_FUNCTION_NAME(ResourcesPrivateGetStringsFunction),
|
||||
"streamsPrivate",
|
||||
EXTENSION_FUNCTION_NAME(StreamsPrivateAbortFunction),
|
||||
"alarms",
|
||||
EXTENSION_FUNCTION_NAME(AlarmsCreateFunction),
|
||||
EXTENSION_FUNCTION_NAME(AlarmsGetFunction),
|
||||
EXTENSION_FUNCTION_NAME(AlarmsGetAllFunction),
|
||||
EXTENSION_FUNCTION_NAME(AlarmsClearFunction),
|
||||
EXTENSION_FUNCTION_NAME(AlarmsClearAllFunction),
|
||||
"tabs",
|
||||
EXTENSION_FUNCTION_NAME(cefimpl::TabsGetFunction),
|
||||
EXTENSION_FUNCTION_NAME(cefimpl::TabsExecuteScriptFunction),
|
||||
|
@ -55,6 +62,11 @@ bool ChromeFunctionRegistry::IsSupported(const std::string& name) {
|
|||
void ChromeFunctionRegistry::RegisterAll(ExtensionFunctionRegistry* registry) {
|
||||
registry->RegisterFunction<ResourcesPrivateGetStringsFunction>();
|
||||
registry->RegisterFunction<StreamsPrivateAbortFunction>();
|
||||
registry->RegisterFunction<AlarmsCreateFunction>();
|
||||
registry->RegisterFunction<AlarmsGetFunction>();
|
||||
registry->RegisterFunction<AlarmsGetAllFunction>();
|
||||
registry->RegisterFunction<AlarmsClearFunction>();
|
||||
registry->RegisterFunction<AlarmsClearAllFunction>();
|
||||
registry->RegisterFunction<cefimpl::TabsExecuteScriptFunction>();
|
||||
registry->RegisterFunction<cefimpl::TabsInsertCSSFunction>();
|
||||
registry->RegisterFunction<cefimpl::TabsGetFunction>();
|
||||
|
|
|
@ -480,6 +480,9 @@ scoped_refptr<const Extension> CefExtensionSystem::CreateExtension(
|
|||
}
|
||||
return Extension::Create(
|
||||
info.root_directory,
|
||||
// Tests should continue to use the Manifest::COMMAND_LINE value here
|
||||
// Some Chrome APIs will cause undesired effects if this is incorrect
|
||||
// e.g.: alarms API has 1 minute minimum applied to Packed Extensions
|
||||
info.internal ? Manifest::COMPONENT : Manifest::COMMAND_LINE,
|
||||
*info.manifest, flags, utf8_error);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,340 @@
|
|||
// Copyright (c) 2017 The Chromium Embedded Framework 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 "tests/ceftests/extensions/extension_test_handler.h"
|
||||
#include "tests/ceftests/test_util.h"
|
||||
#include "tests/shared/browser/extension_util.h"
|
||||
|
||||
#define ALARMS_TEST_GROUP_ALL(name, test_class) \
|
||||
EXTENSION_TEST_GROUP_ALL(ChromeAlarms##name, test_class)
|
||||
#define ALARMS_TEST_GROUP_MINIMAL(name, test_class) \
|
||||
EXTENSION_TEST_GROUP_MINIMAL(ChromeAlarms##name, test_class)
|
||||
|
||||
namespace {
|
||||
|
||||
const char kExtensionPath[] = "alarms-extension";
|
||||
const char kSuccessMessage[] = "success";
|
||||
|
||||
// Base class for testing chrome.alarms methods.
|
||||
// See https://developer.chrome.com/extensions/alarms
|
||||
class AlarmsTestHandler : public ExtensionTestHandler {
|
||||
public:
|
||||
explicit AlarmsTestHandler(RequestContextType request_context_type)
|
||||
: ExtensionTestHandler(request_context_type) {
|
||||
// Only creating the extension browser.
|
||||
set_create_main_browser(false);
|
||||
}
|
||||
|
||||
// CefExtensionHandler methods:
|
||||
void OnExtensionLoaded(CefRefPtr<CefExtension> extension) override {
|
||||
EXPECT_TRUE(CefCurrentlyOn(TID_UI));
|
||||
EXPECT_FALSE(got_loaded_);
|
||||
got_loaded_.yes();
|
||||
|
||||
// Verify |extension| contents.
|
||||
EXPECT_FALSE(extension->GetIdentifier().empty());
|
||||
EXPECT_STREQ(("extensions/" + std::string(kExtensionPath)).c_str(),
|
||||
client::extension_util::GetInternalExtensionResourcePath(
|
||||
extension->GetPath())
|
||||
.c_str());
|
||||
TestDictionaryEqual(CreateManifest(), extension->GetManifest());
|
||||
|
||||
EXPECT_FALSE(extension_);
|
||||
extension_ = extension;
|
||||
|
||||
CreateBrowserForExtension();
|
||||
}
|
||||
|
||||
void OnExtensionUnloaded(CefRefPtr<CefExtension> extension) override {
|
||||
EXPECT_TRUE(CefCurrentlyOn(TID_UI));
|
||||
EXPECT_TRUE(extension_);
|
||||
EXPECT_TRUE(extension_->IsSame(extension));
|
||||
EXPECT_FALSE(got_unloaded_);
|
||||
got_unloaded_.yes();
|
||||
extension_ = NULL;
|
||||
|
||||
// Execute asynchronously so call stacks have a chance to unwind.
|
||||
// Will close the browser windows.
|
||||
CefPostTask(TID_UI, base::Bind(&AlarmsTestHandler::DestroyTest, this));
|
||||
}
|
||||
|
||||
// CefLoadHandler methods:
|
||||
void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
|
||||
bool isLoading,
|
||||
bool canGoBack,
|
||||
bool canGoForward) override {
|
||||
CefRefPtr<CefExtension> extension = browser->GetHost()->GetExtension();
|
||||
EXPECT_TRUE(extension);
|
||||
EXPECT_TRUE(extension_->IsSame(extension));
|
||||
|
||||
if (isLoading) {
|
||||
EXPECT_FALSE(extension_browser_);
|
||||
extension_browser_ = browser;
|
||||
} else {
|
||||
EXPECT_TRUE(browser->IsSame(extension_browser_));
|
||||
|
||||
const std::string& url = browser->GetMainFrame()->GetURL();
|
||||
EXPECT_STREQ(extension_url_.c_str(), url.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// CefRequestHandler methods:
|
||||
CefRefPtr<CefResourceHandler> GetResourceHandler(
|
||||
CefRefPtr<CefBrowser> browser,
|
||||
CefRefPtr<CefFrame> frame,
|
||||
CefRefPtr<CefRequest> request) override {
|
||||
EXPECT_TRUE(browser->IsSame(extension_browser_));
|
||||
|
||||
CefRefPtr<CefExtension> extension = browser->GetHost()->GetExtension();
|
||||
EXPECT_TRUE(extension);
|
||||
EXPECT_TRUE(extension_->IsSame(extension));
|
||||
|
||||
const std::string& url = request->GetURL();
|
||||
EXPECT_STREQ(extension_url_.c_str(), url.c_str());
|
||||
|
||||
EXPECT_FALSE(got_url_request_);
|
||||
got_url_request_.yes();
|
||||
|
||||
// Handle the resource request.
|
||||
return RoutingTestHandler::GetResourceHandler(browser, frame, request);
|
||||
}
|
||||
|
||||
protected:
|
||||
void OnLoadExtensions() override {
|
||||
LoadExtension(kExtensionPath, CreateManifest());
|
||||
}
|
||||
|
||||
bool OnMessage(CefRefPtr<CefBrowser> browser,
|
||||
const std::string& message) override {
|
||||
if (message == "extension_onload") {
|
||||
// From body onLoad in the extension browser.
|
||||
EXPECT_TRUE(browser->IsSame(extension_browser_));
|
||||
EXPECT_FALSE(got_body_onload_);
|
||||
got_body_onload_.yes();
|
||||
TriggerAlarmsApiJSFunction();
|
||||
return true;
|
||||
}
|
||||
EXPECT_TRUE(browser->IsSame(extension_browser_));
|
||||
EXPECT_FALSE(got_success_message_);
|
||||
got_success_message_.yes();
|
||||
EXPECT_STREQ(kSuccessMessage, message.c_str());
|
||||
TriggerDestroyTest();
|
||||
return true;
|
||||
}
|
||||
|
||||
void OnDestroyTest() override {
|
||||
extension_browser_ = NULL;
|
||||
|
||||
EXPECT_TRUE(got_loaded_);
|
||||
EXPECT_TRUE(got_url_request_);
|
||||
EXPECT_TRUE(got_body_onload_);
|
||||
EXPECT_TRUE(got_trigger_api_function_);
|
||||
EXPECT_TRUE(got_success_message_);
|
||||
EXPECT_TRUE(got_unloaded_);
|
||||
}
|
||||
|
||||
// Create a manifest that grants access to the alarms API.
|
||||
virtual CefRefPtr<CefDictionaryValue> CreateManifest() const {
|
||||
ApiPermissionsList api_permissions;
|
||||
api_permissions.push_back("alarms");
|
||||
return CreateDefaultManifest(api_permissions);
|
||||
}
|
||||
|
||||
// Add resources in the extension browser.
|
||||
virtual void OnAddExtensionResources(const std::string& origin) {
|
||||
extension_url_ = origin + "extension.html";
|
||||
AddResource(extension_url_, GetExtensionHTML(), "text/html");
|
||||
}
|
||||
|
||||
// Returns the chrome.alarms.* JS that is executed in the extension browser
|
||||
// when the triggerAlarmsApi() JS function is called.
|
||||
virtual std::string GetAlarmsApiJS() const = 0;
|
||||
|
||||
// Returns the JS that will be loaded in the extension browser. This
|
||||
// implements the triggerAlarmsApi() JS function called from
|
||||
// TriggerAlarmsApiJSFunction().
|
||||
virtual std::string GetExtensionJS() const {
|
||||
return "function triggerAlarmsApi() {" + GetAlarmsApiJS() + "}";
|
||||
}
|
||||
|
||||
// Returns the HTML that will be loaded in the extension browser.
|
||||
virtual std::string GetExtensionHTML() const {
|
||||
return "<html><head><script>" + GetExtensionJS() +
|
||||
"</script></head><body onLoad=" + GetMessageJS("extension_onload") +
|
||||
">Extension</body></html>";
|
||||
}
|
||||
|
||||
virtual void TriggerDestroyTest() {
|
||||
// Execute asynchronously so call stacks have a chance to unwind.
|
||||
CefPostTask(TID_UI, base::Bind(&AlarmsTestHandler::UnloadExtension, this,
|
||||
extension_));
|
||||
}
|
||||
|
||||
CefRefPtr<CefExtension> extension() const { return extension_; }
|
||||
std::string extension_url() const { return extension_url_; }
|
||||
CefRefPtr<CefBrowser> extension_browser() const { return extension_browser_; }
|
||||
|
||||
bool got_success_message() const { return got_success_message_; }
|
||||
|
||||
private:
|
||||
void CreateBrowserForExtension() {
|
||||
const std::string& identifier = extension_->GetIdentifier();
|
||||
EXPECT_FALSE(identifier.empty());
|
||||
const std::string& origin =
|
||||
client::extension_util::GetExtensionOrigin(identifier);
|
||||
EXPECT_FALSE(origin.empty());
|
||||
|
||||
// Add extension resources.
|
||||
OnAddExtensionResources(origin);
|
||||
|
||||
// Create a browser to host the extension.
|
||||
CreateBrowser(extension_url_, request_context());
|
||||
}
|
||||
|
||||
void TriggerAlarmsApiJSFunction() {
|
||||
EXPECT_FALSE(got_trigger_api_function_);
|
||||
got_trigger_api_function_.yes();
|
||||
|
||||
extension_browser_->GetMainFrame()->ExecuteJavaScript("triggerAlarmsApi();",
|
||||
extension_url_, 0);
|
||||
}
|
||||
|
||||
CefRefPtr<CefExtension> extension_;
|
||||
std::string extension_url_;
|
||||
CefRefPtr<CefBrowser> extension_browser_;
|
||||
|
||||
TrackCallback got_loaded_;
|
||||
TrackCallback got_url_request_;
|
||||
TrackCallback got_body_onload_;
|
||||
TrackCallback got_trigger_api_function_;
|
||||
TrackCallback got_success_message_;
|
||||
TrackCallback got_unloaded_;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
|
||||
// Test for chrome.alarms.create(string name, object alarmInfo)
|
||||
// and chrome.alarms.onAlarm.addListener(function callback)
|
||||
class CreateAlarmTestHandler : public AlarmsTestHandler {
|
||||
public:
|
||||
explicit CreateAlarmTestHandler(RequestContextType request_context_type)
|
||||
: AlarmsTestHandler(request_context_type) {}
|
||||
|
||||
protected:
|
||||
std::string GetAlarmsApiJS() const override {
|
||||
return "chrome.alarms.onAlarm.addListener(function (alarm) {" +
|
||||
GetMessageJS(kSuccessMessage) +
|
||||
"});"
|
||||
"chrome.alarms.create(\"test\", {delayInMinutes:0.01})";
|
||||
}
|
||||
|
||||
private:
|
||||
IMPLEMENT_REFCOUNTING(CreateAlarmTestHandler);
|
||||
DISALLOW_COPY_AND_ASSIGN(CreateAlarmTestHandler);
|
||||
};
|
||||
} // namespace
|
||||
|
||||
ALARMS_TEST_GROUP_ALL(CreateAlarm, CreateAlarmTestHandler);
|
||||
|
||||
namespace {
|
||||
|
||||
// Test for chrome.alarms.get(string name, function callback)
|
||||
class GetAlarmTestHandler : public AlarmsTestHandler {
|
||||
public:
|
||||
explicit GetAlarmTestHandler(RequestContextType request_context_type)
|
||||
: AlarmsTestHandler(request_context_type) {}
|
||||
|
||||
protected:
|
||||
std::string GetAlarmsApiJS() const override {
|
||||
return "chrome.alarms.create(\"test\", {delayInMinutes:1});"
|
||||
"setTimeout(function() {"
|
||||
"chrome.alarms.get(\"test\", function (alarm) {" +
|
||||
GetMessageJS(kSuccessMessage) + "})}, 100)";
|
||||
}
|
||||
|
||||
private:
|
||||
IMPLEMENT_REFCOUNTING(GetAlarmTestHandler);
|
||||
DISALLOW_COPY_AND_ASSIGN(GetAlarmTestHandler);
|
||||
};
|
||||
} // namespace
|
||||
|
||||
ALARMS_TEST_GROUP_MINIMAL(GetAlarm, GetAlarmTestHandler);
|
||||
|
||||
namespace {
|
||||
|
||||
// Test for chrome.alarms.getAll(function callback)
|
||||
class GetAllAlarmsTestHandler : public AlarmsTestHandler {
|
||||
public:
|
||||
explicit GetAllAlarmsTestHandler(RequestContextType request_context_type)
|
||||
: AlarmsTestHandler(request_context_type) {}
|
||||
|
||||
protected:
|
||||
std::string GetAlarmsApiJS() const override {
|
||||
return "chrome.alarms.create(\"alarm1\", {delayInMinutes:1});"
|
||||
"chrome.alarms.create(\"alarm2\", {delayInMinutes:1});"
|
||||
"setTimeout(function() {"
|
||||
"chrome.alarms.getAll(function (alarms) {"
|
||||
"if (alarms.length == 2) {" +
|
||||
GetMessageJS(kSuccessMessage) + "}})}, 100)";
|
||||
}
|
||||
|
||||
private:
|
||||
IMPLEMENT_REFCOUNTING(GetAllAlarmsTestHandler);
|
||||
DISALLOW_COPY_AND_ASSIGN(GetAllAlarmsTestHandler);
|
||||
};
|
||||
} // namespace
|
||||
|
||||
ALARMS_TEST_GROUP_MINIMAL(GetAllAlarms, GetAllAlarmsTestHandler);
|
||||
|
||||
namespace {
|
||||
|
||||
// Test for chrome.alarms.clear(string name, function callback)
|
||||
class ClearAlarmTestHandler : public AlarmsTestHandler {
|
||||
public:
|
||||
explicit ClearAlarmTestHandler(RequestContextType request_context_type)
|
||||
: AlarmsTestHandler(request_context_type) {}
|
||||
|
||||
protected:
|
||||
std::string GetAlarmsApiJS() const override {
|
||||
return "chrome.alarms.create(\"test\", {delayInMinutes:1});"
|
||||
"setTimeout(function() {"
|
||||
"chrome.alarms.clear(\"test\", function (wasCleared) {"
|
||||
"if (wasCleared) {" +
|
||||
GetMessageJS(kSuccessMessage) + "}})}, 100)";
|
||||
}
|
||||
|
||||
private:
|
||||
IMPLEMENT_REFCOUNTING(ClearAlarmTestHandler);
|
||||
DISALLOW_COPY_AND_ASSIGN(ClearAlarmTestHandler);
|
||||
};
|
||||
} // namespace
|
||||
|
||||
ALARMS_TEST_GROUP_MINIMAL(ClearAlarm, ClearAlarmTestHandler);
|
||||
|
||||
namespace {
|
||||
|
||||
// Test for chrome.alarms.clearAll(function callback)
|
||||
class ClearAllAlarmsTestHandler : public AlarmsTestHandler {
|
||||
public:
|
||||
explicit ClearAllAlarmsTestHandler(RequestContextType request_context_type)
|
||||
: AlarmsTestHandler(request_context_type) {}
|
||||
|
||||
protected:
|
||||
std::string GetAlarmsApiJS() const override {
|
||||
return "chrome.alarms.create(\"alarm1\", {delayInMinutes:1});"
|
||||
"chrome.alarms.create(\"alarm2\", {delayInMinutes:1});"
|
||||
"setTimeout(function() {"
|
||||
"chrome.alarms.clearAll(function (wasCleared) {"
|
||||
"if (wasCleared) {" +
|
||||
GetMessageJS(kSuccessMessage) + "}})}, 100)";
|
||||
}
|
||||
|
||||
private:
|
||||
IMPLEMENT_REFCOUNTING(ClearAllAlarmsTestHandler);
|
||||
DISALLOW_COPY_AND_ASSIGN(ClearAllAlarmsTestHandler);
|
||||
};
|
||||
} // namespace
|
||||
|
||||
ALARMS_TEST_GROUP_MINIMAL(ClearAllAlarms, ClearAllAlarmsTestHandler);
|
Loading…
Reference in New Issue