| // 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 <sstream> |
| |
| #include "include/cef_parser.h" |
| |
| #include "tests/ceftests/test_util.h" |
| #include "tests/shared/browser/extension_util.h" |
| |
| #define TABS_TEST_GROUP_ALL(name, test_class) \ |
| EXTENSION_TEST_GROUP_ALL(ChromeTabs##name, test_class) |
| #define TABS_TEST_GROUP_MINIMAL(name, test_class) \ |
| EXTENSION_TEST_GROUP_MINIMAL(ChromeTabs##name, test_class) |
| |
| namespace { |
| |
| const char kMainBrowserURL[] = "https://test-extensions.com/chrome-tabs"; |
| const char kExtensionPath[] = "tabs-extension"; |
| const char kSuccessMessage[] = "success"; |
| |
| // Base class for testing chrome.tabs methods. |
| // See https://developer.chrome.com/extensions/tabs |
| class TabsTestHandler : public ExtensionTestHandler { |
| public: |
| explicit TabsTestHandler(RequestContextType request_context_type) |
| : ExtensionTestHandler(request_context_type), |
| create_main_browser_first_(false), |
| expect_get_active_browser_(true), |
| expect_success_in_main_browser_(true), |
| expected_api_call_count_(1), |
| got_get_active_browser_count_(0), |
| got_can_access_browser_count_(0) {} |
| |
| // CefExtensionHandler methods: |
| void OnExtensionLoaded(CefRefPtr<CefExtension> extension) override { |
| EXPECT_TRUE(CefCurrentlyOn(TID_UI)); |
| EXPECT_FALSE(got_extension_loaded_); |
| got_extension_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; |
| |
| if (create_main_browser_first_) |
| CreateBrowserForExtensionIfReady(); |
| else |
| CreateBrowserForExtension(); |
| } |
| |
| void OnExtensionUnloaded(CefRefPtr<CefExtension> extension) override { |
| EXPECT_TRUE(CefCurrentlyOn(TID_UI)); |
| EXPECT_TRUE(extension_); |
| EXPECT_TRUE(extension_->IsSame(extension)); |
| EXPECT_FALSE(got_extension_unloaded_); |
| got_extension_unloaded_.yes(); |
| extension_ = nullptr; |
| |
| // Execute asynchronously so call stacks have a chance to unwind. |
| // Will close the browser windows. |
| CefPostTask(TID_UI, base::Bind(&TabsTestHandler::DestroyTest, this)); |
| } |
| |
| CefRefPtr<CefBrowser> GetActiveBrowser(CefRefPtr<CefExtension> extension, |
| CefRefPtr<CefBrowser> browser, |
| bool include_incognito) override { |
| EXPECT_TRUE(CefCurrentlyOn(TID_UI)); |
| EXPECT_TRUE(extension_); |
| EXPECT_TRUE(extension_->IsSame(extension)); |
| EXPECT_TRUE(main_browser_); |
| |
| EXPECT_LE(got_get_active_browser_count_, expected_api_call_count_); |
| got_get_active_browser_count_++; |
| |
| // Tabs APIs will operate on the main browser. |
| return main_browser_; |
| } |
| |
| bool CanAccessBrowser(CefRefPtr<CefExtension> extension, |
| CefRefPtr<CefBrowser> browser, |
| bool include_incognito, |
| CefRefPtr<CefBrowser> target_browser) override { |
| EXPECT_TRUE(CefCurrentlyOn(TID_UI)); |
| EXPECT_TRUE(extension_); |
| EXPECT_TRUE(extension_->IsSame(extension)); |
| EXPECT_TRUE(main_browser_); |
| EXPECT_TRUE(main_browser_->IsSame(target_browser)); |
| |
| EXPECT_LE(got_can_access_browser_count_, expected_api_call_count_); |
| got_can_access_browser_count_++; |
| |
| return true; |
| } |
| |
| // CefLoadHandler methods: |
| void OnLoadingStateChange(CefRefPtr<CefBrowser> browser, |
| bool isLoading, |
| bool canGoBack, |
| bool canGoForward) override { |
| if (isLoading) { |
| // Keep a reference to both browsers. |
| if (browser->GetHost()->GetExtension()) { |
| EXPECT_FALSE(extension_browser_); |
| extension_browser_ = browser; |
| } else { |
| EXPECT_FALSE(main_browser_); |
| main_browser_ = browser; |
| } |
| } else { |
| const std::string& url = browser->GetMainFrame()->GetURL(); |
| if (browser->GetHost()->GetExtension()) { |
| EXPECT_TRUE(browser->IsSame(extension_browser_)); |
| EXPECT_STREQ(extension_url_.c_str(), url.c_str()); |
| } else { |
| EXPECT_TRUE(browser->IsSame(main_browser_)); |
| EXPECT_STREQ(kMainBrowserURL, url.c_str()); |
| } |
| } |
| } |
| |
| // CefResourceRequestHandler methods: |
| CefRefPtr<CefResourceHandler> GetResourceHandler( |
| CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| CefRefPtr<CefRequest> request) override { |
| const std::string& url = request->GetURL(); |
| if (url == kMainBrowserURL) { |
| EXPECT_TRUE(browser->IsSame(main_browser_)); |
| EXPECT_FALSE(got_main_url_request_); |
| got_main_url_request_.yes(); |
| } else if (url == extension_url_) { |
| EXPECT_TRUE(browser->IsSame(extension_browser_)); |
| EXPECT_FALSE(got_extension_url_request_); |
| got_extension_url_request_.yes(); |
| } |
| |
| // Handle the resource request. |
| return RoutingTestHandler::GetResourceHandler(browser, frame, request); |
| } |
| |
| protected: |
| void OnAddMainBrowserResources() override { |
| AddResource(kMainBrowserURL, GetMainBrowserHTML(), "text/html"); |
| } |
| |
| void OnCreateMainBrowser() override { |
| CreateBrowser(kMainBrowserURL, request_context()); |
| } |
| |
| void OnLoadExtensions() override { |
| LoadExtension(kExtensionPath, CreateManifest()); |
| } |
| |
| bool OnMessage(CefRefPtr<CefBrowser> browser, |
| const std::string& message) override { |
| if (message == "main_onload") { |
| // From body onLoad in the main browser. |
| EXPECT_TRUE(browser->IsSame(main_browser_)); |
| EXPECT_FALSE(got_main_body_onload_); |
| got_main_body_onload_.yes(); |
| if (create_main_browser_first_) |
| CreateBrowserForExtensionIfReady(); |
| TriggerTabsApiJSFunctionIfReady(); |
| return true; |
| } |
| if (message == "extension_onload") { |
| // From body onLoad in the extension browser. |
| EXPECT_TRUE(browser->IsSame(extension_browser_)); |
| EXPECT_FALSE(got_extension_body_onload_); |
| got_extension_body_onload_.yes(); |
| TriggerTabsApiJSFunctionIfReady(); |
| return true; |
| } |
| |
| // The success message usually orginates from the logic in |
| // GetMainBrowserSuccessHEAD/BODY(). It may occasionally originate from the |
| // extension browser if we don't know how to detect success in the main |
| // browser. |
| if (expect_success_in_main_browser_) { |
| EXPECT_TRUE(browser->IsSame(main_browser_)); |
| } else { |
| 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 { |
| main_browser_ = nullptr; |
| extension_browser_ = nullptr; |
| |
| EXPECT_TRUE(got_extension_loaded_); |
| EXPECT_TRUE(got_main_url_request_); |
| EXPECT_TRUE(got_extension_url_request_); |
| EXPECT_TRUE(got_main_body_onload_); |
| EXPECT_TRUE(got_extension_body_onload_); |
| EXPECT_TRUE(got_trigger_api_function_); |
| EXPECT_TRUE(got_success_message_); |
| EXPECT_TRUE(got_extension_unloaded_); |
| |
| if (expect_get_active_browser_) { |
| EXPECT_EQ(expected_api_call_count_, got_get_active_browser_count_); |
| EXPECT_EQ(0, got_can_access_browser_count_); |
| } else { |
| EXPECT_EQ(0, got_get_active_browser_count_); |
| EXPECT_EQ(expected_api_call_count_, got_can_access_browser_count_); |
| } |
| } |
| |
| // Create a manifest that grants access to the tabs API. |
| virtual CefRefPtr<CefDictionaryValue> CreateManifest() const { |
| ApiPermissionsList api_permissions; |
| api_permissions.push_back("tabs"); |
| 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 target tabId (null, or value >= 0). |
| virtual std::string GetTargetTabId() const { return "null"; } |
| |
| // Returns the logic in the main browser that triggers on success. It should |
| // execute GetMessageJS(kSuccessMessage). |
| virtual std::string GetMainBrowserSuccessHEAD() const { |
| return std::string(); |
| } |
| virtual std::string GetMainBrowserSuccessBODY() const { |
| return std::string(); |
| } |
| |
| // Returns the HTML that will be loaded in the main browser. |
| virtual std::string GetMainBrowserHTML() const { |
| return "<html><head>" + GetMainBrowserSuccessHEAD() + |
| "</head><body onLoad=" + GetMessageJS("main_onload") + ">Main" + |
| GetMainBrowserSuccessBODY() + "</body></html>"; |
| } |
| |
| // Returns the chrome.tabs.* JS that is executed in the extension browser |
| // when the triggerTabsApi() JS function is called. |
| virtual std::string GetTabsApiJS() const = 0; |
| |
| // Returns the JS that will be loaded in the extension browser. This |
| // implements the triggerTabsApi() JS function called from |
| // TriggerTabsApiJSFunction(). |
| virtual std::string GetExtensionJS() const { |
| return "function triggerTabsApi() {" + GetTabsApiJS() + "}"; |
| } |
| |
| // 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(&TabsTestHandler::UnloadExtension, this, |
| extension_)); |
| } |
| |
| CefRefPtr<CefExtension> extension() const { return extension_; } |
| std::string extension_url() const { return extension_url_; } |
| CefRefPtr<CefBrowser> main_browser() const { return main_browser_; } |
| CefRefPtr<CefBrowser> extension_browser() const { return extension_browser_; } |
| |
| void set_create_main_browser_first(bool val) { |
| create_main_browser_first_ = val; |
| } |
| void set_expect_get_active_browser(bool val) { |
| expect_get_active_browser_ = val; |
| } |
| void set_expect_success_in_main_browser(bool val) { |
| expect_success_in_main_browser_ = val; |
| } |
| void set_expected_api_call_count(int val) { expected_api_call_count_ = val; } |
| |
| bool got_success_message() const { return got_success_message_; } |
| void set_got_success_message() { got_success_message_.yes(); } |
| |
| private: |
| void CreateBrowserForExtensionIfReady() { |
| DCHECK(create_main_browser_first_); |
| if (extension_ && main_browser_) |
| CreateBrowserForExtension(); |
| } |
| |
| 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 TriggerTabsApiJSFunctionIfReady() { |
| if (got_main_body_onload_ && got_extension_body_onload_) |
| TriggerTabsApiJSFunction(); |
| } |
| |
| void TriggerTabsApiJSFunction() { |
| EXPECT_FALSE(got_trigger_api_function_); |
| got_trigger_api_function_.yes(); |
| |
| extension_browser_->GetMainFrame()->ExecuteJavaScript("triggerTabsApi();", |
| extension_url_, 0); |
| } |
| |
| // If true the main browser will be created before the extension browser. |
| // Otherwise the creation order is undefined. |
| bool create_main_browser_first_; |
| |
| // If true we expect GetActiveBrowser() but not CanAccessBrowser() to be |
| // called. Else visa-versa. |
| bool expect_get_active_browser_; |
| |
| // If true we expect the success message to be delivered in the main browser. |
| // Else expect it in the extension browser. |
| bool expect_success_in_main_browser_; |
| |
| // Number of expected calls to GetActiveBrowser or CanAccessBrowser. This |
| // should match the number of calls to chrome.tabs.* API functions in the |
| // test. |
| int expected_api_call_count_; |
| |
| CefRefPtr<CefExtension> extension_; |
| std::string extension_url_; |
| CefRefPtr<CefBrowser> main_browser_; |
| CefRefPtr<CefBrowser> extension_browser_; |
| |
| TrackCallback got_extension_loaded_; |
| TrackCallback got_main_url_request_; |
| TrackCallback got_extension_url_request_; |
| TrackCallback got_main_body_onload_; |
| TrackCallback got_extension_body_onload_; |
| TrackCallback got_trigger_api_function_; |
| TrackCallback got_success_message_; |
| TrackCallback got_extension_unloaded_; |
| |
| int got_get_active_browser_count_; |
| int got_can_access_browser_count_; |
| }; |
| |
| // |
| // chrome.tabs.create tests. |
| // |
| |
| const char kCreateBrowserURL[] = |
| "https://test-extensions.com/chrome-tabs-create"; |
| const char kTabCallbackMessage[] = "tab-callback"; |
| const int kCreateTabIndex = 2; |
| |
| // Class for chrome.tabs.create tests. |
| class CreateTestHandler : public TabsTestHandler { |
| public: |
| explicit CreateTestHandler(RequestContextType request_context_type) |
| : TabsTestHandler(request_context_type) {} |
| |
| bool OnBeforeBrowser(CefRefPtr<CefExtension> extension, |
| CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefBrowser> active_browser, |
| int index, |
| const CefString& url, |
| bool foreground, |
| CefWindowInfo& windowInfo, |
| CefRefPtr<CefClient>& client, |
| CefBrowserSettings& settings) override { |
| EXPECT_TRUE(extension->IsSame(this->extension())); |
| EXPECT_TRUE(browser->IsSame(extension_browser())); |
| EXPECT_TRUE(active_browser->IsSame(main_browser())); |
| EXPECT_EQ(kCreateTabIndex, index); |
| EXPECT_STREQ(kCreateBrowserURL, url.ToString().c_str()); |
| EXPECT_TRUE(foreground); |
| EXPECT_TRUE(client); |
| |
| EXPECT_FALSE(got_on_before_browser_); |
| got_on_before_browser_.yes(); |
| |
| return false; |
| } |
| |
| void OnAddMainBrowserResources() override { |
| AddResource(kCreateBrowserURL, GetCreatedBrowserHTML(), "text/html"); |
| |
| TabsTestHandler::OnAddMainBrowserResources(); |
| } |
| |
| void OnLoadingStateChange(CefRefPtr<CefBrowser> browser, |
| bool isLoading, |
| bool canGoBack, |
| bool canGoForward) override { |
| if (extension_browser() && main_browser()) { |
| if (isLoading) { |
| // Keep a reference to the newly created browser. |
| EXPECT_FALSE(created_browser_); |
| created_browser_ = browser; |
| return; |
| } else { |
| const std::string& url = browser->GetMainFrame()->GetURL(); |
| if (url == kCreateBrowserURL) { |
| EXPECT_TRUE(browser->IsSame(created_browser_)); |
| return; |
| } |
| } |
| } |
| |
| TabsTestHandler::OnLoadingStateChange(browser, isLoading, canGoBack, |
| canGoForward); |
| } |
| |
| CefRefPtr<CefResourceHandler> GetResourceHandler( |
| CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| CefRefPtr<CefRequest> request) override { |
| const std::string& url = request->GetURL(); |
| if (url == kCreateBrowserURL) { |
| EXPECT_TRUE(browser->IsSame(created_browser_)); |
| EXPECT_FALSE(got_create_browser_url_request_); |
| got_create_browser_url_request_.yes(); |
| } |
| |
| return TabsTestHandler::GetResourceHandler(browser, frame, request); |
| } |
| |
| protected: |
| std::string GetTabsApiJS() const override { |
| std::stringstream ss; |
| ss << kCreateTabIndex; |
| |
| return "chrome.tabs.create({url: \"" + std::string(kCreateBrowserURL) + |
| "\", index: " + ss.str() + |
| "}, function(tab) { window.testQuery({request:'" + |
| kTabCallbackMessage + ":' + JSON.stringify(tab)}); });"; |
| } |
| |
| bool OnMessage(CefRefPtr<CefBrowser> browser, |
| const std::string& message) override { |
| if (message.find(kTabCallbackMessage) != std::string::npos) { |
| EXPECT_TRUE(browser->IsSame(extension_browser())); |
| EXPECT_FALSE(got_tab_callback_message_); |
| got_tab_callback_message_.yes(); |
| |
| // Verify the contents of the Tab object. |
| const std::string& json_str = |
| message.substr(strlen(kTabCallbackMessage) + 1); |
| CefRefPtr<CefValue> obj = CefParseJSON(json_str, JSON_PARSER_RFC); |
| EXPECT_TRUE(obj); |
| EXPECT_EQ(VTYPE_DICTIONARY, obj->GetType()); |
| CefRefPtr<CefDictionaryValue> dict = obj->GetDictionary(); |
| |
| int index = dict->GetInt("index"); |
| EXPECT_EQ(kCreateTabIndex, index); |
| |
| int id = dict->GetInt("id"); |
| int windowId = dict->GetInt("windowId"); |
| EXPECT_EQ(created_browser_->GetIdentifier(), id); |
| EXPECT_EQ(created_browser_->GetIdentifier(), windowId); |
| |
| const std::string& url = dict->GetString("url"); |
| EXPECT_STREQ(kCreateBrowserURL, url.c_str()); |
| |
| TriggerDestroyTestIfReady(); |
| return true; |
| } else if (message == kSuccessMessage) { |
| // Overriding default kSuccessMessage handling. |
| EXPECT_TRUE(browser->IsSame(created_browser_)); |
| EXPECT_FALSE(got_success_message()); |
| set_got_success_message(); |
| TriggerDestroyTestIfReady(); |
| return true; |
| } |
| |
| return TabsTestHandler::OnMessage(browser, message); |
| } |
| |
| void OnDestroyTest() override { |
| created_browser_ = nullptr; |
| |
| EXPECT_TRUE(got_on_before_browser_); |
| EXPECT_TRUE(got_create_browser_url_request_); |
| EXPECT_TRUE(got_tab_callback_message_); |
| |
| TabsTestHandler::OnDestroyTest(); |
| } |
| |
| private: |
| std::string GetCreatedBrowserHTML() { |
| return "<html><body onLoad=" + GetMessageJS(kSuccessMessage) + |
| ">Created</body></html>"; |
| } |
| |
| void TriggerDestroyTestIfReady() { |
| if (got_tab_callback_message_ && got_success_message()) |
| TriggerDestroyTest(); |
| } |
| |
| CefRefPtr<CefBrowser> created_browser_; |
| |
| TrackCallback got_on_before_browser_; |
| TrackCallback got_create_browser_url_request_; |
| TrackCallback got_tab_callback_message_; |
| |
| IMPLEMENT_REFCOUNTING(CreateTestHandler); |
| DISALLOW_COPY_AND_ASSIGN(CreateTestHandler); |
| }; |
| |
| } // namespace |
| |
| TABS_TEST_GROUP_ALL(Create, CreateTestHandler) |
| |
| namespace { |
| |
| // |
| // chrome.tabs.executeScript tests. |
| // |
| |
| // Base class for chrome.tabs.executeScript tests. |
| class ExecuteScriptTestHandler : public TabsTestHandler { |
| public: |
| explicit ExecuteScriptTestHandler(RequestContextType request_context_type) |
| : TabsTestHandler(request_context_type) {} |
| |
| protected: |
| std::string GetMainBrowserSuccessHEAD() const override { |
| return "<script>function onTrigger() {" + GetMessageJS(kSuccessMessage) + |
| "}</script>"; |
| } |
| |
| // Returns the code that will be injected as a content script. |
| virtual std::string GetContentScriptJS() const { |
| // Execute the onTrigger() JS function. |
| return "var s = document.createElement('script');" |
| "s.textContent = 'onTrigger();';" |
| "document.head.appendChild(s);"; |
| } |
| |
| std::string GetTabsApiJS() const override { |
| return "chrome.tabs.executeScript(" + GetTargetTabId() + ", {code:\"" + |
| GetContentScriptJS() + "\"});"; |
| } |
| }; |
| |
| // Test for chrome.tabs.executeScript with a null tabId value. |
| class ExecuteScriptNullTabTestHandler : public ExecuteScriptTestHandler { |
| public: |
| explicit ExecuteScriptNullTabTestHandler( |
| RequestContextType request_context_type) |
| : ExecuteScriptTestHandler(request_context_type) {} |
| |
| private: |
| IMPLEMENT_REFCOUNTING(ExecuteScriptNullTabTestHandler); |
| DISALLOW_COPY_AND_ASSIGN(ExecuteScriptNullTabTestHandler); |
| }; |
| |
| } // namespace |
| |
| TABS_TEST_GROUP_ALL(ExecuteScriptNullTab, ExecuteScriptNullTabTestHandler) |
| |
| namespace { |
| |
| // Test for chrome.tabs.executeScript with an explicit tabId value. |
| class ExecuteScriptExplicitTabTestHandler : public ExecuteScriptTestHandler { |
| public: |
| explicit ExecuteScriptExplicitTabTestHandler( |
| RequestContextType request_context_type) |
| : ExecuteScriptTestHandler(request_context_type) { |
| // Create the main browser first so we can retrieve the id. |
| set_create_main_browser_first(true); |
| // When a tabId is specified we should get a call to CanAccessBrowser |
| // instead of GetActiveBrowser. |
| set_expect_get_active_browser(false); |
| } |
| |
| protected: |
| std::string GetTargetTabId() const override { |
| DCHECK(main_browser()); |
| std::stringstream ss; |
| ss << main_browser()->GetIdentifier(); |
| return ss.str(); |
| } |
| |
| private: |
| IMPLEMENT_REFCOUNTING(ExecuteScriptExplicitTabTestHandler); |
| DISALLOW_COPY_AND_ASSIGN(ExecuteScriptExplicitTabTestHandler); |
| }; |
| |
| } // namespace |
| |
| TABS_TEST_GROUP_ALL(ExecuteScriptExplicitTab, |
| ExecuteScriptExplicitTabTestHandler) |
| |
| namespace { |
| |
| // Test for chrome.tabs.executeScript with a file argument loading a content |
| // script. |
| class ExecuteScriptFileTestHandler : public ExecuteScriptTestHandler { |
| public: |
| explicit ExecuteScriptFileTestHandler(RequestContextType request_context_type) |
| : ExecuteScriptTestHandler(request_context_type) {} |
| |
| // CefExtensionHandler methods: |
| bool GetExtensionResource( |
| CefRefPtr<CefExtension> extension, |
| CefRefPtr<CefBrowser> browser, |
| const CefString& file, |
| CefRefPtr<CefGetExtensionResourceCallback> callback) override { |
| EXPECT_TRUE(CefCurrentlyOn(TID_UI)); |
| EXPECT_TRUE(this->extension()); |
| EXPECT_TRUE(this->extension()->IsSame(extension)); |
| |
| if (file == "script.js") { |
| EXPECT_FALSE(got_get_extension_resource_); |
| got_get_extension_resource_.yes(); |
| |
| const std::string& content = GetContentScriptJS(); |
| CefRefPtr<CefStreamReader> stream = CefStreamReader::CreateForData( |
| const_cast<char*>(content.data()), content.size()); |
| callback->Continue(stream); |
| return true; |
| } |
| |
| EXPECT_FALSE(true); // Not reached. |
| return false; |
| } |
| |
| protected: |
| std::string GetTabsApiJS() const override { |
| return "chrome.tabs.executeScript(" + GetTargetTabId() + |
| ", {file:\"script.js\"});"; |
| } |
| |
| void OnDestroyTest() override { |
| ExecuteScriptTestHandler::OnDestroyTest(); |
| EXPECT_TRUE(got_get_extension_resource_); |
| } |
| |
| private: |
| TrackCallback got_get_extension_resource_; |
| |
| IMPLEMENT_REFCOUNTING(ExecuteScriptFileTestHandler); |
| DISALLOW_COPY_AND_ASSIGN(ExecuteScriptFileTestHandler); |
| }; |
| |
| } // namespace |
| |
| TABS_TEST_GROUP_ALL(ExecuteScriptFile, ExecuteScriptFileTestHandler) |
| |
| namespace { |
| |
| // Test for chrome.tabs.executeScript with a callback argument. |
| class ExecuteScriptCallbackTestHandler : public ExecuteScriptTestHandler { |
| public: |
| explicit ExecuteScriptCallbackTestHandler( |
| RequestContextType request_context_type) |
| : ExecuteScriptTestHandler(request_context_type) {} |
| |
| protected: |
| bool OnMessage(CefRefPtr<CefBrowser> browser, |
| const std::string& message) override { |
| if (message == "callback") { |
| EXPECT_FALSE(got_callback_message_); |
| got_callback_message_.yes(); |
| EXPECT_TRUE(browser->IsSame(extension_browser())); |
| TriggerDestroyTest(); |
| return true; |
| } |
| return ExecuteScriptTestHandler::OnMessage(browser, message); |
| } |
| |
| std::string GetTabsApiJS() const override { |
| return "chrome.tabs.executeScript(" + GetTargetTabId() + ", {code:\"" + |
| GetContentScriptJS() + "\"}, function(results) {" + |
| GetMessageJS("callback") + "});"; |
| } |
| |
| void TriggerDestroyTest() override { |
| // Only destroy the test if we got both callbacks. |
| if (got_callback_message_ && got_success_message()) { |
| ExecuteScriptTestHandler::TriggerDestroyTest(); |
| } |
| } |
| |
| void OnDestroyTest() override { |
| ExecuteScriptTestHandler::OnDestroyTest(); |
| EXPECT_TRUE(got_callback_message_); |
| } |
| |
| private: |
| TrackCallback got_callback_message_; |
| |
| IMPLEMENT_REFCOUNTING(ExecuteScriptCallbackTestHandler); |
| DISALLOW_COPY_AND_ASSIGN(ExecuteScriptCallbackTestHandler); |
| }; |
| |
| } // namespace |
| |
| TABS_TEST_GROUP_MINIMAL(ExecuteScriptCallback, ExecuteScriptCallbackTestHandler) |
| |
| namespace { |
| |
| // Test for chrome.tabs.executeScript with execution occuring from a separate |
| // resource script. |
| class ExecuteScriptResourceTabTestHandler : public ExecuteScriptTestHandler { |
| public: |
| explicit ExecuteScriptResourceTabTestHandler( |
| RequestContextType request_context_type) |
| : ExecuteScriptTestHandler(request_context_type) {} |
| |
| // CefResourceRequestHandler methods: |
| CefRefPtr<CefResourceHandler> GetResourceHandler( |
| CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| CefRefPtr<CefRequest> request) override { |
| const std::string& url = request->GetURL(); |
| if (url == resource_url_) { |
| EXPECT_TRUE(browser->IsSame(extension_browser())); |
| EXPECT_FALSE(got_resource_url_request_); |
| got_resource_url_request_.yes(); |
| } |
| |
| return ExecuteScriptTestHandler::GetResourceHandler(browser, frame, |
| request); |
| } |
| |
| protected: |
| void OnAddExtensionResources(const std::string& origin) override { |
| ExecuteScriptTestHandler::OnAddExtensionResources(origin); |
| resource_url_ = origin + "resource.js"; |
| AddResource(resource_url_, GetExtensionJS(), "text/javascript"); |
| } |
| |
| std::string GetExtensionHTML() const override { |
| return "<html><head><script src=\"resource.js\"></script></head><body " |
| "onLoad=" + |
| GetMessageJS("extension_onload") + ">Extension</body></html>"; |
| } |
| |
| void OnDestroyTest() override { |
| ExecuteScriptTestHandler::OnDestroyTest(); |
| EXPECT_TRUE(got_resource_url_request_); |
| } |
| |
| private: |
| std::string resource_url_; |
| TrackCallback got_resource_url_request_; |
| |
| IMPLEMENT_REFCOUNTING(ExecuteScriptResourceTabTestHandler); |
| DISALLOW_COPY_AND_ASSIGN(ExecuteScriptResourceTabTestHandler); |
| }; |
| |
| } // namespace |
| |
| TABS_TEST_GROUP_MINIMAL(ExecuteScriptResource, |
| ExecuteScriptResourceTabTestHandler) |
| |
| // |
| // chrome.tabs.insertCSS tests. |
| // |
| |
| namespace { |
| |
| // Base class for chrome.tabs.insertCSS tests. |
| class InsertCSSTestHandler : public TabsTestHandler { |
| public: |
| explicit InsertCSSTestHandler(RequestContextType request_context_type) |
| : TabsTestHandler(request_context_type) {} |
| |
| protected: |
| std::string GetMainBrowserSuccessBODY() const override { |
| // We can't use a MutationObserver here because insertCSS does not modify |
| // the style attribute. We could detect the change by tracking modifications |
| // to document.styleSheets but that's complicated. Use a simple timer loop |
| // implementation calling getComputedStyle instead. |
| return "<script>var interval = setInterval(function() {" |
| "if (window.getComputedStyle(document.body,null)." |
| "getPropertyValue('background-color') == 'rgb(255, 0, 0)') {" + |
| GetMessageJS(kSuccessMessage) + |
| "clearInterval(interval);}}, 100);</script>"; |
| } |
| |
| // Returns the code that will be injected as a content script. |
| virtual std::string GetContentScriptCSS() const { |
| return "body{background-color:red}"; |
| } |
| |
| std::string GetTabsApiJS() const override { |
| return "chrome.tabs.insertCSS(" + GetTargetTabId() + ", {code:\"" + |
| GetContentScriptCSS() + "\"});"; |
| } |
| }; |
| |
| // Test for chrome.tabs.insertCSS with a null tabId value. |
| class InsertCSSNullTabTestHandler : public InsertCSSTestHandler { |
| public: |
| explicit InsertCSSNullTabTestHandler(RequestContextType request_context_type) |
| : InsertCSSTestHandler(request_context_type) {} |
| |
| private: |
| IMPLEMENT_REFCOUNTING(InsertCSSNullTabTestHandler); |
| DISALLOW_COPY_AND_ASSIGN(InsertCSSNullTabTestHandler); |
| }; |
| |
| } // namespace |
| |
| TABS_TEST_GROUP_ALL(InsertCSSNullTab, InsertCSSNullTabTestHandler) |
| |
| namespace { |
| |
| // Test for chrome.tabs.insertCSS with an explicit tabId value. |
| class InsertCSSExplicitTabTestHandler : public InsertCSSTestHandler { |
| public: |
| explicit InsertCSSExplicitTabTestHandler( |
| RequestContextType request_context_type) |
| : InsertCSSTestHandler(request_context_type) { |
| // Create the main browser first so we can retrieve the id. |
| set_create_main_browser_first(true); |
| // When a tabId is specified we should get a call to CanAccessBrowser |
| // instead of GetActiveBrowser. |
| set_expect_get_active_browser(false); |
| } |
| |
| protected: |
| std::string GetTargetTabId() const override { |
| DCHECK(main_browser()); |
| std::stringstream ss; |
| ss << main_browser()->GetIdentifier(); |
| return ss.str(); |
| } |
| |
| private: |
| IMPLEMENT_REFCOUNTING(InsertCSSExplicitTabTestHandler); |
| DISALLOW_COPY_AND_ASSIGN(InsertCSSExplicitTabTestHandler); |
| }; |
| |
| } // namespace |
| |
| TABS_TEST_GROUP_ALL(InsertCSSExplicitTab, InsertCSSExplicitTabTestHandler) |
| |
| namespace { |
| |
| // Test for chrome.tabs.insertCSS with a file argument loading a content |
| // script. |
| class InsertCSSFileTestHandler : public InsertCSSTestHandler { |
| public: |
| explicit InsertCSSFileTestHandler(RequestContextType request_context_type) |
| : InsertCSSTestHandler(request_context_type) {} |
| |
| // CefExtensionHandler methods: |
| bool GetExtensionResource( |
| CefRefPtr<CefExtension> extension, |
| CefRefPtr<CefBrowser> browser, |
| const CefString& file, |
| CefRefPtr<CefGetExtensionResourceCallback> callback) override { |
| EXPECT_TRUE(CefCurrentlyOn(TID_UI)); |
| EXPECT_TRUE(this->extension()); |
| EXPECT_TRUE(this->extension()->IsSame(extension)); |
| |
| if (file == "script.css") { |
| EXPECT_FALSE(got_get_extension_resource_); |
| got_get_extension_resource_.yes(); |
| |
| const std::string& content = GetContentScriptCSS(); |
| CefRefPtr<CefStreamReader> stream = CefStreamReader::CreateForData( |
| const_cast<char*>(content.data()), content.size()); |
| callback->Continue(stream); |
| return true; |
| } |
| |
| EXPECT_FALSE(true); // Not reached. |
| return false; |
| } |
| |
| protected: |
| std::string GetTabsApiJS() const override { |
| return "chrome.tabs.insertCSS(" + GetTargetTabId() + |
| ", {file:\"script.css\"});"; |
| } |
| |
| void OnDestroyTest() override { |
| InsertCSSTestHandler::OnDestroyTest(); |
| EXPECT_TRUE(got_get_extension_resource_); |
| } |
| |
| private: |
| TrackCallback got_get_extension_resource_; |
| |
| IMPLEMENT_REFCOUNTING(InsertCSSFileTestHandler); |
| DISALLOW_COPY_AND_ASSIGN(InsertCSSFileTestHandler); |
| }; |
| |
| } // namespace |
| |
| TABS_TEST_GROUP_ALL(InsertCSSFile, InsertCSSFileTestHandler) |
| |
| namespace { |
| |
| // Test for chrome.tabs.insertCSS with a callback argument. |
| class InsertCSSCallbackTestHandler : public InsertCSSTestHandler { |
| public: |
| explicit InsertCSSCallbackTestHandler(RequestContextType request_context_type) |
| : InsertCSSTestHandler(request_context_type) {} |
| |
| protected: |
| bool OnMessage(CefRefPtr<CefBrowser> browser, |
| const std::string& message) override { |
| if (message == "callback") { |
| EXPECT_FALSE(got_callback_message_); |
| got_callback_message_.yes(); |
| EXPECT_TRUE(browser->IsSame(extension_browser())); |
| TriggerDestroyTest(); |
| return true; |
| } |
| return InsertCSSTestHandler::OnMessage(browser, message); |
| } |
| |
| std::string GetTabsApiJS() const override { |
| return "chrome.tabs.insertCSS(" + GetTargetTabId() + ", {code:\"" + |
| GetContentScriptCSS() + "\"}, function(results) {" + |
| GetMessageJS("callback") + "});"; |
| } |
| |
| void TriggerDestroyTest() override { |
| // Only destroy the test if we got both callbacks. |
| if (got_callback_message_ && got_success_message()) { |
| InsertCSSTestHandler::TriggerDestroyTest(); |
| } |
| } |
| |
| void OnDestroyTest() override { |
| InsertCSSTestHandler::OnDestroyTest(); |
| EXPECT_TRUE(got_callback_message_); |
| } |
| |
| private: |
| TrackCallback got_callback_message_; |
| |
| IMPLEMENT_REFCOUNTING(InsertCSSCallbackTestHandler); |
| DISALLOW_COPY_AND_ASSIGN(InsertCSSCallbackTestHandler); |
| }; |
| |
| } // namespace |
| |
| TABS_TEST_GROUP_MINIMAL(InsertCSSCallback, InsertCSSCallbackTestHandler) |
| |
| namespace { |
| |
| // Test for chrome.tabs.insertCSS with execution occuring from a separate |
| // resource script. |
| class InsertCSSResourceTabTestHandler : public InsertCSSTestHandler { |
| public: |
| explicit InsertCSSResourceTabTestHandler( |
| RequestContextType request_context_type) |
| : InsertCSSTestHandler(request_context_type) {} |
| |
| // CefResourceRequestHandler methods: |
| CefRefPtr<CefResourceHandler> GetResourceHandler( |
| CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| CefRefPtr<CefRequest> request) override { |
| const std::string& url = request->GetURL(); |
| if (url == resource_url_) { |
| EXPECT_TRUE(browser->IsSame(extension_browser())); |
| EXPECT_FALSE(got_resource_url_request_); |
| got_resource_url_request_.yes(); |
| } |
| |
| return InsertCSSTestHandler::GetResourceHandler(browser, frame, request); |
| } |
| |
| protected: |
| void OnAddExtensionResources(const std::string& origin) override { |
| InsertCSSTestHandler::OnAddExtensionResources(origin); |
| resource_url_ = origin + "resource.js"; |
| AddResource(resource_url_, GetExtensionJS(), "text/javascript"); |
| } |
| |
| std::string GetExtensionHTML() const override { |
| return "<html><head><script src=\"resource.js\"></script></head><body " |
| "onLoad=" + |
| GetMessageJS("extension_onload") + ">Extension</body></html>"; |
| } |
| |
| void OnDestroyTest() override { |
| InsertCSSTestHandler::OnDestroyTest(); |
| EXPECT_TRUE(got_resource_url_request_); |
| } |
| |
| private: |
| std::string resource_url_; |
| TrackCallback got_resource_url_request_; |
| |
| IMPLEMENT_REFCOUNTING(InsertCSSResourceTabTestHandler); |
| DISALLOW_COPY_AND_ASSIGN(InsertCSSResourceTabTestHandler); |
| }; |
| |
| } // namespace |
| |
| TABS_TEST_GROUP_MINIMAL(InsertCSSResource, InsertCSSResourceTabTestHandler) |
| |
| // |
| // chrome.tabs.setZoom/getZoom tests. |
| // |
| |
| namespace { |
| |
| // Base class for chrome.tabs.setZoom/getZoom tests. |
| class ZoomTestHandler : public TabsTestHandler { |
| public: |
| explicit ZoomTestHandler(RequestContextType request_context_type) |
| : TabsTestHandler(request_context_type) { |
| // We call API functions three times in this handler. |
| set_expected_api_call_count(3); |
| } |
| |
| protected: |
| std::string GetMainBrowserSuccessHEAD() const override { |
| return "<script>var orig_width = window.innerWidth;</script>"; |
| } |
| |
| std::string GetMainBrowserSuccessBODY() const override { |
| // We can't directly detect zoom changes, so instead we look for changes |
| // in window.innerWidth. |
| return "<script>var interval = setInterval(function() {" |
| "if (window.innerWidth != orig_width) {" + |
| GetMessageJS(kSuccessMessage) + |
| "clearInterval(interval);}}, 100);</script>"; |
| } |
| |
| std::string GetTabsApiJS() const override { |
| // Results in a change to window.innerWidth. |
| return "chrome.tabs.setZoom(" + GetTargetTabId() + ", 2.0);"; |
| } |
| |
| bool OnMessage(CefRefPtr<CefBrowser> browser, |
| const std::string& message) override { |
| if (message == "restored") { |
| EXPECT_TRUE(browser->IsSame(extension_browser())); |
| EXPECT_FALSE(got_restored_message_); |
| got_restored_message_.yes(); |
| // Destroy the test for real. |
| TabsTestHandler::TriggerDestroyTest(); |
| return true; |
| } |
| return TabsTestHandler::OnMessage(browser, message); |
| } |
| |
| void TriggerDestroyTest() override { |
| // Before destroying the test we need to restore the zoom factor so that |
| // it doesn't persist in the RequestContext. This also tests the callback |
| // argument and the getZoom function so there's no need to do that |
| // separately. |
| extension_browser()->GetMainFrame()->ExecuteJavaScript( |
| "chrome.tabs.setZoom(" + GetTargetTabId() + ", 1.0, function() {" + |
| "chrome.tabs.getZoom(" + GetTargetTabId() + |
| ", function(zoomFactor) { if (zoomFactor == 1.0) {" + |
| GetMessageJS("restored") + "}})});", |
| extension_url(), 0); |
| } |
| |
| void OnDestroyTest() override { |
| TabsTestHandler::OnDestroyTest(); |
| EXPECT_TRUE(got_restored_message_); |
| } |
| |
| private: |
| TrackCallback got_restored_message_; |
| }; |
| |
| // Test for chrome.tabs.setZoom/getZoom with a null tabId value. |
| class ZoomNullTabTestHandler : public ZoomTestHandler { |
| public: |
| explicit ZoomNullTabTestHandler(RequestContextType request_context_type) |
| : ZoomTestHandler(request_context_type) {} |
| |
| private: |
| IMPLEMENT_REFCOUNTING(ZoomNullTabTestHandler); |
| DISALLOW_COPY_AND_ASSIGN(ZoomNullTabTestHandler); |
| }; |
| |
| } // namespace |
| |
| TABS_TEST_GROUP_ALL(ZoomNullTab, ZoomNullTabTestHandler) |
| |
| namespace { |
| |
| // Test for chrome.tabs.setZoom/getZoom with an explicit tabId value. |
| class ZoomExplicitTabTestHandler : public ZoomTestHandler { |
| public: |
| explicit ZoomExplicitTabTestHandler(RequestContextType request_context_type) |
| : ZoomTestHandler(request_context_type) { |
| // Create the main browser first so we can retrieve the id. |
| set_create_main_browser_first(true); |
| // When a tabId is specified we should get a call to CanAccessBrowser |
| // instead of GetActiveBrowser. |
| set_expect_get_active_browser(false); |
| } |
| |
| protected: |
| std::string GetTargetTabId() const override { |
| DCHECK(main_browser()); |
| std::stringstream ss; |
| ss << main_browser()->GetIdentifier(); |
| return ss.str(); |
| } |
| |
| private: |
| IMPLEMENT_REFCOUNTING(ZoomExplicitTabTestHandler); |
| DISALLOW_COPY_AND_ASSIGN(ZoomExplicitTabTestHandler); |
| }; |
| |
| } // namespace |
| |
| TABS_TEST_GROUP_ALL(ZoomExplicitTab, ZoomExplicitTabTestHandler) |
| |
| // |
| // chrome.tabs.setZoomSettings/getZoomSettings tests. |
| // |
| |
| namespace { |
| |
| // Base class for chrome.tabs.setZoomSettings/getZoomSettings tests. |
| class ZoomSettingsTestHandler : public TabsTestHandler { |
| public: |
| explicit ZoomSettingsTestHandler(RequestContextType request_context_type) |
| : TabsTestHandler(request_context_type) { |
| // We call API functions two times in this handler. |
| set_expected_api_call_count(2); |
| // Success message will be delivered in the extension browser because we |
| // don't know how to detect zoom settings changes in the main browser. |
| set_expect_success_in_main_browser(false); |
| } |
| |
| protected: |
| std::string GetTabsApiJS() const override { |
| // Set and restore the zoom settings. This also tests the callback argument |
| // and the getZoomSettings function so there's no need to do that |
| // separately. This is safe because zoom settings are not persisted in the |
| // RequestContext across navigations. |
| return "chrome.tabs.setZoomSettings(" + GetTargetTabId() + |
| ", {mode: 'manual', scope: 'per-tab'}, function() {" + |
| "chrome.tabs.getZoomSettings(" + GetTargetTabId() + |
| ", function(zoomSettings) { if (zoomSettings.mode == 'manual' " |
| "&& zoomSettings.scope == 'per-tab') {" + |
| GetMessageJS(kSuccessMessage) + "}})});"; |
| } |
| }; |
| |
| // Test for chrome.tabs.setZoomSettings/getZoomSettings with a null tabId value. |
| class ZoomSettingsNullTabTestHandler : public ZoomSettingsTestHandler { |
| public: |
| explicit ZoomSettingsNullTabTestHandler( |
| RequestContextType request_context_type) |
| : ZoomSettingsTestHandler(request_context_type) {} |
| |
| private: |
| IMPLEMENT_REFCOUNTING(ZoomSettingsNullTabTestHandler); |
| DISALLOW_COPY_AND_ASSIGN(ZoomSettingsNullTabTestHandler); |
| }; |
| |
| } // namespace |
| |
| TABS_TEST_GROUP_ALL(ZoomSettingsNullTab, ZoomSettingsNullTabTestHandler) |
| |
| namespace { |
| |
| // Test for chrome.tabs.setZoomSettings/getZoomSettings with an explicit tabId |
| // value. |
| class ZoomSettingsExplicitTabTestHandler : public ZoomSettingsTestHandler { |
| public: |
| explicit ZoomSettingsExplicitTabTestHandler( |
| RequestContextType request_context_type) |
| : ZoomSettingsTestHandler(request_context_type) { |
| // Create the main browser first so we can retrieve the id. |
| set_create_main_browser_first(true); |
| // When a tabId is specified we should get a call to CanAccessBrowser |
| // instead of GetActiveBrowser. |
| set_expect_get_active_browser(false); |
| } |
| |
| protected: |
| std::string GetTargetTabId() const override { |
| DCHECK(main_browser()); |
| std::stringstream ss; |
| ss << main_browser()->GetIdentifier(); |
| return ss.str(); |
| } |
| |
| private: |
| IMPLEMENT_REFCOUNTING(ZoomSettingsExplicitTabTestHandler); |
| DISALLOW_COPY_AND_ASSIGN(ZoomSettingsExplicitTabTestHandler); |
| }; |
| |
| } // namespace |
| |
| TABS_TEST_GROUP_ALL(ZoomSettingsExplicitTab, ZoomSettingsExplicitTabTestHandler) |