1049 lines
34 KiB
C++
1049 lines
34 KiB
C++
// 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 "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_ = NULL;
|
|
|
|
// 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());
|
|
}
|
|
}
|
|
}
|
|
|
|
// CefRequestHandler 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_ = NULL;
|
|
extension_browser_ = NULL;
|
|
|
|
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_; }
|
|
|
|
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.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) {}
|
|
|
|
// CefRequestHandler 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) {}
|
|
|
|
// CefRequestHandler 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);
|