blob: a107aaf22b846bfef2c1c3651b10304d97bc3547 [file] [log] [blame]
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "extensions/browser/extension_frame_host.h"
#include <string>
#include "base/memory/ref_counted.h"
#include "content/public/browser/web_contents_user_data.h"
#include "extensions/browser/browsertest_util.h"
#include "extensions/browser/extension_host.h"
#include "extensions/browser/process_manager.h"
#include "extensions/common/constants.h"
#include "extensions/common/extension.h"
#include "extensions/shell/browser/shell_extension_host_delegate.h"
#include "extensions/shell/browser/shell_extension_web_contents_observer.h"
#include "extensions/shell/browser/shell_extensions_browser_client.h"
#include "extensions/shell/test/shell_apitest.h"
#include "extensions/test/result_catcher.h"
#include "testing/gtest/include/gtest/gtest-spi.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace extensions {
namespace {
class TestExtensionFrameHost : public ExtensionFrameHost {
public:
explicit TestExtensionFrameHost(content::WebContents* web_contents)
: ExtensionFrameHost(web_contents) {}
TestExtensionFrameHost(const TestExtensionFrameHost&) = delete;
TestExtensionFrameHost& operator=(const TestExtensionFrameHost&) = delete;
~TestExtensionFrameHost() override = default;
void SetInvalidRequest(const std::string& name) { invalid_request_ = name; }
private:
// mojom::LocalFrameHost:
void Request(mojom::RequestParamsPtr params,
RequestCallback callback) override {
// If the name of |params| is set to an invalid request, it sets it to
// an empty string so that the request causes an error.
if (invalid_request_ == params->name)
params->name = std::string();
ExtensionFrameHost::Request(std::move(params), std::move(callback));
}
std::string invalid_request_;
};
class TestShellExtensionWebContentsObserver
: public ExtensionWebContentsObserver,
public content::WebContentsUserData<
TestShellExtensionWebContentsObserver> {
public:
TestShellExtensionWebContentsObserver(
const TestShellExtensionWebContentsObserver&) = delete;
TestShellExtensionWebContentsObserver& operator=(
const TestShellExtensionWebContentsObserver&) = delete;
~TestShellExtensionWebContentsObserver() override = default;
// Creates and initializes an instance of this class for the given
// |web_contents|, if it doesn't already exist.
static void CreateForWebContents(content::WebContents* web_contents) {
content::WebContentsUserData<TestShellExtensionWebContentsObserver>::
CreateForWebContents(web_contents);
// Initialize this instance if necessary.
FromWebContents(web_contents)->Initialize();
}
// Overrides to create TestExtensionFrameHost.
std::unique_ptr<ExtensionFrameHost> CreateExtensionFrameHost(
content::WebContents* web_contents) override {
return std::make_unique<TestExtensionFrameHost>(web_contents);
}
private:
friend class content::WebContentsUserData<
TestShellExtensionWebContentsObserver>;
explicit TestShellExtensionWebContentsObserver(
content::WebContents* web_contents)
: ExtensionWebContentsObserver(web_contents) {}
WEB_CONTENTS_USER_DATA_KEY_DECL();
};
WEB_CONTENTS_USER_DATA_KEY_IMPL(TestShellExtensionWebContentsObserver)
class TestShellExtensionHostDelegate : public ShellExtensionHostDelegate {
public:
TestShellExtensionHostDelegate() = default;
TestShellExtensionHostDelegate(const TestShellExtensionHostDelegate&) =
delete;
TestShellExtensionHostDelegate& operator=(
const TestShellExtensionHostDelegate&) = delete;
~TestShellExtensionHostDelegate() override = default;
// Overrides to create TestShellExtensionWebContentsObserver.
void OnExtensionHostCreated(content::WebContents* web_contents) override {
TestShellExtensionWebContentsObserver::CreateForWebContents(web_contents);
}
};
class ExtensionFrameHostTestExtensionsBrowserClient
: public ShellExtensionsBrowserClient {
public:
ExtensionFrameHostTestExtensionsBrowserClient() = default;
ExtensionFrameHostTestExtensionsBrowserClient(
const ExtensionFrameHostTestExtensionsBrowserClient&) = delete;
ExtensionFrameHostTestExtensionsBrowserClient& operator=(
const ExtensionFrameHostTestExtensionsBrowserClient&) = delete;
~ExtensionFrameHostTestExtensionsBrowserClient() override = default;
// Overrides to create TestShellExtensionHostDelegate.
std::unique_ptr<ExtensionHostDelegate> CreateExtensionHostDelegate()
override {
return std::make_unique<TestShellExtensionHostDelegate>();
}
// Overrides to return TestShellExtensionWebContentsObserver.
ExtensionWebContentsObserver* GetExtensionWebContentsObserver(
content::WebContents* web_contents) override {
return TestShellExtensionWebContentsObserver::FromWebContents(web_contents);
}
};
} // namespace
class ExtensionFrameHostBrowserTest : public ShellApiTest {
public:
ExtensionFrameHostBrowserTest() = default;
ExtensionFrameHostBrowserTest(const ExtensionFrameHostBrowserTest&) = delete;
ExtensionFrameHostBrowserTest& operator=(
const ExtensionFrameHostBrowserTest&) = delete;
~ExtensionFrameHostBrowserTest() override = default;
void SetUpOnMainThread() override {
ShellApiTest::SetUpOnMainThread();
extensions_browser_client_ =
std::make_unique<ExtensionFrameHostTestExtensionsBrowserClient>();
ExtensionsBrowserClient::Set(extensions_browser_client_.get());
extension_ = LoadExtension("extension");
ASSERT_TRUE(extension_.get());
ResultCatcher catcher;
ASSERT_TRUE(catcher.GetNextResult());
}
protected:
const Extension* extension() const { return extension_.get(); }
void SetInvalidNameOnRequest(const std::string& method_name) {
ExtensionHost* host =
ProcessManager::Get(browser_context())
->GetBackgroundHostForExtension(extension()->id());
ASSERT_TRUE(host);
ASSERT_TRUE(host->host_contents());
ExtensionWebContentsObserver* observer =
extensions_browser_client_->GetExtensionWebContentsObserver(
host->host_contents());
static_cast<TestExtensionFrameHost*>(observer->extension_frame_host_.get())
->SetInvalidRequest(method_name);
}
private:
scoped_refptr<const Extension> extension_;
std::unique_ptr<ExtensionFrameHostTestExtensionsBrowserClient>
extensions_browser_client_;
};
// Test that when ExtensionFrameHost dispatches an invalid request it gets
// an error associated with it. This is a regression test for
// https://crbug.com/1196377.
IN_PROC_BROWSER_TEST_F(ExtensionFrameHostBrowserTest, InValidNameRequest) {
// Set 'test.getConfig' is invalid request.
SetInvalidNameOnRequest("test.getConfig");
// Run a script asynchronously that passes the test.
ResultCatcher catcher;
ASSERT_TRUE(browsertest_util::ExecuteScriptInBackgroundPageNoWait(
browser_context(), extension()->id(), R"(
chrome.test.getConfig(() => {
const expectedError = 'Access to extension API denied.';
if (chrome.runtime.lastError &&
expectedError == chrome.runtime.lastError.message) {
chrome.test.notifyPass();
} else {
chrome.test.notifyFail('TestFailed');
}
});)"));
ASSERT_TRUE(catcher.GetNextResult());
}
} // namespace extensions