// 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 STORAGE_TEST_GROUP_ALL(name, test_class) \ EXTENSION_TEST_GROUP_ALL(ChromeStorage##name, test_class) #define STORAGE_TEST_GROUP_MINIMAL(name, test_class) \ EXTENSION_TEST_GROUP_MINIMAL(ChromeStorage##name, test_class) namespace { const char kExtensionPath[] = "storage-extension"; const char kSuccessMessage[] = "success"; // Base class for testing chrome.storage methods. // See https://developer.chrome.com/extensions/storage class StorageTestHandler : public ExtensionTestHandler { public: explicit StorageTestHandler(RequestContextType request_context_type) : ExtensionTestHandler(request_context_type) { // Only creating the extension browser. set_create_main_browser(false); } // CefExtensionHandler methods: void OnExtensionLoaded(CefRefPtr 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 extension) override { EXPECT_TRUE(CefCurrentlyOn(TID_UI)); EXPECT_TRUE(extension_); EXPECT_TRUE(extension_->IsSame(extension)); EXPECT_FALSE(got_unloaded_); got_unloaded_.yes(); extension_ = nullptr; // Execute asynchronously so call stacks have a chance to unwind. // Will close the browser windows. CefPostTask(TID_UI, base::BindOnce(&StorageTestHandler::DestroyTest, this)); } // CefLoadHandler methods: void OnLoadingStateChange(CefRefPtr browser, bool isLoading, bool canGoBack, bool canGoForward) override { CefRefPtr 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()); } } // CefResourceRequestHandler methods: CefRefPtr GetResourceHandler( CefRefPtr browser, CefRefPtr frame, CefRefPtr request) override { EXPECT_TRUE(browser->IsSame(extension_browser_)); CefRefPtr 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 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(); TriggerStorageApiJSFunction(); 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_ = nullptr; 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 storage API. virtual CefRefPtr CreateManifest() const { ApiPermissionsList api_permissions; api_permissions.push_back("storage"); 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.storage.* JS that is executed in the extension browser // when the triggerStorageApi() JS function is called. virtual std::string GetStorageApiJS() const = 0; // Returns the JS that will be loaded in the extension browser. This // implements the triggerStorageApi() JS function called from // TriggerStorageApiJSFunction(). virtual std::string GetExtensionJS() const { return "function triggerStorageApi() {" + GetStorageApiJS() + "}"; } // Returns the HTML that will be loaded in the extension browser. virtual std::string GetExtensionHTML() const { return "Extension"; } virtual void TriggerDestroyTest() { // Execute asynchronously so call stacks have a chance to unwind. CefPostTask(TID_UI, base::BindOnce(&StorageTestHandler::UnloadExtension, this, extension_)); } CefRefPtr extension() const { return extension_; } std::string extension_url() const { return extension_url_; } CefRefPtr 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 TriggerStorageApiJSFunction() { EXPECT_FALSE(got_trigger_api_function_); got_trigger_api_function_.yes(); extension_browser_->GetMainFrame()->ExecuteJavaScript( "triggerStorageApi();", extension_url_, 0); } CefRefPtr extension_; std::string extension_url_; CefRefPtr 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.storage.local.set(object items, function callback) // and for chrome.storage.local.get(string or array of string or object keys, // function callback) class LocalStorageTestHandler : public StorageTestHandler { public: explicit LocalStorageTestHandler(RequestContextType request_context_type) : StorageTestHandler(request_context_type) {} protected: std::string GetStorageApiJS() const override { return "chrome.storage.local.set({\"local_key_1\": \"local_value_1\"}, " "function() {" "chrome.storage.local.get(\"local_key_1\", function (items) {" "if(items[\"local_key_1\"] == \"local_value_1\") {" + GetMessageJS(kSuccessMessage) + "}});" "});"; } private: IMPLEMENT_REFCOUNTING(LocalStorageTestHandler); DISALLOW_COPY_AND_ASSIGN(LocalStorageTestHandler); }; } // namespace STORAGE_TEST_GROUP_ALL(LocalStorage, LocalStorageTestHandler) namespace { // Test for chrome.storage.local.getBytesInUse(string or array of string keys, // function callback) class LocalStorageGetBytesInUseTestHandler : public StorageTestHandler { public: explicit LocalStorageGetBytesInUseTestHandler( RequestContextType request_context_type) : StorageTestHandler(request_context_type) {} protected: std::string GetStorageApiJS() const override { return "chrome.storage.local.set({\"local_key_2\": \"local_value_2\"}, " "function() {" "chrome.storage.local.getBytesInUse(\"local_key_2\", function " "(bytesInUse) {" "if (bytesInUse == 26) {" + GetMessageJS(kSuccessMessage) + "}});" "});"; } private: IMPLEMENT_REFCOUNTING(LocalStorageGetBytesInUseTestHandler); DISALLOW_COPY_AND_ASSIGN(LocalStorageGetBytesInUseTestHandler); }; } // namespace STORAGE_TEST_GROUP_MINIMAL(LocalStorageGetBytesInUse, LocalStorageGetBytesInUseTestHandler) namespace { // Test for chrome.storage.local.remove(string or array of string keys, function // callback) class LocalStorageRemoveTestHandler : public StorageTestHandler { public: explicit LocalStorageRemoveTestHandler( RequestContextType request_context_type) : StorageTestHandler(request_context_type) {} protected: std::string GetStorageApiJS() const override { return "chrome.storage.local.set({\"local_key_3\": \"local_value_3\"}, " "function() {" "chrome.storage.local.remove(\"local_key_3\", function () {" "chrome.storage.local.get(\"local_key_3\", function(items) {" "if (items[\"local_key_3\"] == undefined) {" + GetMessageJS(kSuccessMessage) + "}})})" "});"; } private: IMPLEMENT_REFCOUNTING(LocalStorageRemoveTestHandler); DISALLOW_COPY_AND_ASSIGN(LocalStorageRemoveTestHandler); }; } // namespace STORAGE_TEST_GROUP_MINIMAL(LocalStorageRemove, LocalStorageRemoveTestHandler) namespace { // Test for chrome.storage.local.clear(function callback) class LocalStorageClearTestHandler : public StorageTestHandler { public: explicit LocalStorageClearTestHandler(RequestContextType request_context_type) : StorageTestHandler(request_context_type) {} protected: std::string GetStorageApiJS() const override { return "var value1Cleared = false;" "var value2Cleared = false;" "function checkCleared() {" "if (value1Cleared && value2Cleared) {" + GetMessageJS(kSuccessMessage) + "}}" "chrome.storage.local.set({\"local_key_4\": \"local_value_4\"," "\"local_key_5\": \"local_value_5\"}, function() {" "chrome.storage.local.clear(function () {" "chrome.storage.local.get(\"local_key_4\", function(items) {" "if (items[\"local_key_4\"] == undefined) {" "value1Cleared = true;" "checkCleared();" "}});" "chrome.storage.local.get(\"local_key_5\", function(items) {" "if (items[\"local_key_5\"] == undefined) {" "value2Cleared = true;" "checkCleared();" "}});" "})});"; } private: IMPLEMENT_REFCOUNTING(LocalStorageClearTestHandler); DISALLOW_COPY_AND_ASSIGN(LocalStorageClearTestHandler); }; } // namespace STORAGE_TEST_GROUP_MINIMAL(LocalStorageClear, LocalStorageClearTestHandler) namespace { // Test for chrome.storage.sync.set(object items, function callback) // and for chrome.storage.sync.get(string or array of string or object keys, // function callback) class SyncStorageTestHandler : public StorageTestHandler { public: explicit SyncStorageTestHandler(RequestContextType request_context_type) : StorageTestHandler(request_context_type) {} protected: std::string GetStorageApiJS() const override { return "chrome.storage.sync.set({\"sync_key_1\": \"sync_value_1\"}, " "function() {" "chrome.storage.sync.get(\"sync_key_1\", function (items) {" "if (items[\"sync_key_1\"] == \"sync_value_1\") {" + GetMessageJS(kSuccessMessage) + "}});" "});"; } private: IMPLEMENT_REFCOUNTING(SyncStorageTestHandler); DISALLOW_COPY_AND_ASSIGN(SyncStorageTestHandler); }; } // namespace STORAGE_TEST_GROUP_ALL(SyncStorage, SyncStorageTestHandler) namespace { // Test for chrome.storage.sync.getBytesInUse(string or array of string keys, // function callback) class SyncStorageGetBytesInUseTestHandler : public StorageTestHandler { public: explicit SyncStorageGetBytesInUseTestHandler( RequestContextType request_context_type) : StorageTestHandler(request_context_type) {} protected: std::string GetStorageApiJS() const override { return "chrome.storage.sync.set({\"sync_key_2\": \"sync_value_2\"}, " "function() {" "chrome.storage.sync.getBytesInUse(\"sync_key_2\", function " "(bytesInUse) {" "if (bytesInUse == 24) {" + GetMessageJS(kSuccessMessage) + "}});" "});"; } private: IMPLEMENT_REFCOUNTING(SyncStorageGetBytesInUseTestHandler); DISALLOW_COPY_AND_ASSIGN(SyncStorageGetBytesInUseTestHandler); }; } // namespace STORAGE_TEST_GROUP_MINIMAL(SyncStorageGetBytesInUse, SyncStorageGetBytesInUseTestHandler) namespace { // Test for chrome.storage.sync.remove(string or array of string keys, function // callback) class SyncStorageRemoveTestHandler : public StorageTestHandler { public: explicit SyncStorageRemoveTestHandler(RequestContextType request_context_type) : StorageTestHandler(request_context_type) {} protected: std::string GetStorageApiJS() const override { return "chrome.storage.sync.set({\"sync_key_3\": \"sync_value_3\"}, " "function() {" "chrome.storage.sync.remove(\"sync_key_3\", function () {" "chrome.storage.sync.get(\"sync_key_3\", function(items) {" "if (items[\"sync_key_3\"] == undefined) {" + GetMessageJS(kSuccessMessage) + "}})})" "});"; } private: IMPLEMENT_REFCOUNTING(SyncStorageRemoveTestHandler); DISALLOW_COPY_AND_ASSIGN(SyncStorageRemoveTestHandler); }; } // namespace STORAGE_TEST_GROUP_MINIMAL(SyncStorageRemove, SyncStorageRemoveTestHandler) namespace { // Test for chrome.storage.sync.clear(function callback) class SyncStorageClearTestHandler : public StorageTestHandler { public: explicit SyncStorageClearTestHandler(RequestContextType request_context_type) : StorageTestHandler(request_context_type) {} protected: std::string GetStorageApiJS() const override { return "var value1Cleared = false;" "var value2Cleared = false;" "function checkCleared() {" "if (value1Cleared && value2Cleared) {" + GetMessageJS(kSuccessMessage) + "}}" "chrome.storage.sync.set({\"sync_key_4\": \"sync_value_4\"," "\"sync_key_5\": \"sync_value_5\"}, function() {" "chrome.storage.sync.clear(function () {" "chrome.storage.sync.get(\"sync_key_4\", function(items) {" "if (items[\"sync_key_4\"] == undefined) {" "value1Cleared = true;" "checkCleared();" "}});" "chrome.storage.sync.get(\"sync_key_5\", function(items) {" "if (items[\"sync_key_5\"] == undefined) {" "value2Cleared = true;" "checkCleared();" "}});" "})});"; } private: IMPLEMENT_REFCOUNTING(SyncStorageClearTestHandler); DISALLOW_COPY_AND_ASSIGN(SyncStorageClearTestHandler); }; } // namespace STORAGE_TEST_GROUP_MINIMAL(SyncStorageClear, SyncStorageClearTestHandler)