blob: bc4971bc0f13e3691fa4ff06a25758a44318ffbf [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/extensions/extension_tab_util.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "components/version_info/version_info.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/prerender_test_util.h"
#include "extensions/browser/api/declarative/rules_registry.h"
#include "extensions/browser/api/declarative/rules_registry_service.h"
#include "extensions/browser/extension_action_manager.h"
#include "extensions/common/features/feature_channel.h"
#include "extensions/test/extension_test_message_listener.h"
#include "extensions/test/test_extension_dir.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "ui/gfx/image/image.h"
namespace extensions {
namespace {
const char kDeclarativeContentManifest[] = R"({
"name": "Declarative Content apitest",
"version": "0.1",
"manifest_version": 2,
"description": "end-to-end browser test for the declarative Content API",
"background": {"scripts": ["background.js"]},
"page_action": {},
"permissions": ["declarativeContent"]
})";
constexpr char kOneByOneImageData[] =
"GAAAAAAAAAAQAAAAAAAAADAAAAAAAAAAKAAAAAAAAAACAAAAAQAAAAEAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAwAAAAEAAAAAAAAAAAAAAA=";
class SetIconAPITest : public ExtensionApiTest {
public:
SetIconAPITest()
// Set the channel to "trunk" since declarativeContent is restricted
// to trunk.
: current_channel_(version_info::Channel::UNKNOWN) {}
~SetIconAPITest() override {}
protected:
const Extension* LoadTestExtension() {
ext_dir_.WriteManifest(kDeclarativeContentManifest);
ext_dir_.WriteFile(FILE_PATH_LITERAL("background.js"), R"(
var declarative = chrome.declarative;
var PageStateMatcher = chrome.declarativeContent.PageStateMatcher;
var SetIcon = chrome.declarativeContent.SetIcon;
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
var imageData = ctx.createImageData(1, 1);
var rule0 = {
conditions: [new PageStateMatcher({pageUrl: {queryContains: "show"}})],
actions: [new SetIcon({"imageData": imageData})]
};
var testEvent = chrome.declarativeContent.onPageChanged;
testEvent.removeRules(undefined, function() {
testEvent.addRules([rule0], function() {
chrome.test.sendMessage("ready", function(reply) {});
});
});
)");
ExtensionTestMessageListener ready("ready");
const Extension* extension = LoadExtension(ext_dir_.UnpackedPath());
if (!extension)
return nullptr;
// Wait for declarative rules to be set up.
profile()->GetDefaultStoragePartition()->FlushNetworkInterfaceForTesting();
EXPECT_TRUE(GetExtensionAction(*extension));
EXPECT_TRUE(ready.WaitUntilSatisfied());
return extension;
}
const ExtensionAction* GetExtensionAction(const Extension& extension) {
return ExtensionActionManager::Get(profile())->GetExtensionAction(
extension);
}
content::WebContents* GetActiveWebContents() {
return browser()->tab_strip_model()->GetWebContentsAt(0);
}
private:
extensions::ScopedCurrentChannel current_channel_;
TestExtensionDir ext_dir_;
};
IN_PROC_BROWSER_TEST_F(SetIconAPITest, Overview) {
const Extension* extension = LoadTestExtension();
ASSERT_TRUE(extension);
content::WebContents* const tab = GetActiveWebContents();
const int tab_id = ExtensionTabUtil::GetTabId(tab);
// There should be no declarative icon until we navigate to a matched page.
const ExtensionAction* action = GetExtensionAction(*extension);
ASSERT_TRUE(action);
EXPECT_TRUE(action->GetDeclarativeIcon(tab_id).IsEmpty());
EXPECT_FALSE(NavigateInRenderer(tab, GURL("http://example.com/?show")));
EXPECT_FALSE(action->GetDeclarativeIcon(tab_id).IsEmpty());
// Navigating to an unmatched page should reset the icon.
EXPECT_FALSE(NavigateInRenderer(tab, GURL("http://example.com/?hide")));
EXPECT_TRUE(action->GetDeclarativeIcon(tab_id).IsEmpty());
}
// Regression test for crbug.com/1231027.
IN_PROC_BROWSER_TEST_F(SetIconAPITest, Parameter) {
const Extension* extension = LoadTestExtension();
ASSERT_TRUE(extension);
scoped_refptr<RulesRegistry> rules_registry =
extensions::RulesRegistryService::Get(browser()->profile())
->GetRulesRegistry(RulesRegistryService::kDefaultRulesRegistryID,
"declarativeContent.onPageChanged");
ASSERT_TRUE(rules_registry);
std::vector<const api::events::Rule*> rules;
rules_registry->GetAllRules(extension->id(), &rules);
ASSERT_EQ(1u, rules.size());
ASSERT_EQ(rules[0]->actions.size(), 1u);
const base::Value::Dict& action_value = rules[0]->actions[0].GetDict();
const std::string* action_instance_type =
action_value.FindString("instanceType");
ASSERT_TRUE(action_instance_type);
EXPECT_EQ("declarativeContent.SetIcon", *action_instance_type);
const std::string* image_data_value =
action_value.FindStringByDottedPath("imageData.1");
ASSERT_TRUE(image_data_value);
EXPECT_EQ(kOneByOneImageData, *image_data_value);
}
class SetIconAPIPrerenderingTest : public SetIconAPITest {
public:
SetIconAPIPrerenderingTest()
: prerender_helper_(base::BindRepeating(
&SetIconAPIPrerenderingTest::GetActiveWebContents,
base::Unretained(this))) {}
~SetIconAPIPrerenderingTest() override = default;
protected:
int Prerender(const GURL& url) { return prerender_helper_.AddPrerender(url); }
void Activate(const GURL& url) { prerender_helper_.NavigatePrimaryPage(url); }
private:
void SetUp() override {
prerender_helper_.SetUp(embedded_test_server());
ExtensionApiTest::SetUp();
}
content::test::PrerenderTestHelper prerender_helper_;
};
IN_PROC_BROWSER_TEST_F(SetIconAPIPrerenderingTest, Overview) {
ASSERT_TRUE(embedded_test_server()->Start());
const Extension* extension = LoadTestExtension();
ASSERT_TRUE(extension);
content::WebContents* const tab = GetActiveWebContents();
const int tab_id = ExtensionTabUtil::GetTabId(tab);
// There should be no declarative icon until we navigate to a matched page.
const ExtensionAction* action = GetExtensionAction(*extension);
ASSERT_TRUE(action);
EXPECT_TRUE(action->GetDeclarativeIcon(tab_id).IsEmpty());
const GURL kInitialUrl = embedded_test_server()->GetURL("/empty.html?show");
EXPECT_TRUE(NavigateInRenderer(tab, kInitialUrl));
EXPECT_FALSE(action->GetDeclarativeIcon(tab_id).IsEmpty());
// Prerendering an unmatched page should not reset the icon.
const GURL kPrerenderingUrl =
embedded_test_server()->GetURL("/empty.html?hide");
int host_id = Prerender(kPrerenderingUrl);
ASSERT_NE(content::RenderFrameHost::kNoFrameTreeNodeId, host_id);
EXPECT_FALSE(action->GetDeclarativeIcon(tab_id).IsEmpty());
// Activating the unmatched page should reset the icon.
Activate(kPrerenderingUrl);
EXPECT_TRUE(action->GetDeclarativeIcon(tab_id).IsEmpty());
EXPECT_EQ(tab->GetLastCommittedURL(), kPrerenderingUrl);
}
} // namespace
} // namespace extensions