blob: 56ec8a9ab5e0d28d4196d1afaa252ff6ff099c99 [file] [log] [blame]
// Copyright (c) 2012 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 <stddef.h>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/values.h"
#include "chrome/common/render_messages.h"
#include "chrome/renderer/content_settings_agent_impl.h"
#include "chrome/test/base/chrome_render_view_test.h"
#include "components/content_settings/core/common/content_settings.h"
#include "components/content_settings/core/common/content_settings_utils.h"
#include "content/public/renderer/render_frame.h"
#include "content/public/renderer/render_view.h"
#include "ipc/ipc_message_macros.h"
#include "ipc/ipc_test_sink.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
#include "third_party/blink/public/web/web_frame_content_dumper.h"
#include "third_party/blink/public/web/web_view.h"
namespace {
constexpr char kScriptHtml[] = R"HTML(
<html>
<head>
<script src='data:foo'></script>
</head>
<body></body>
</html>;
)HTML";
constexpr char kScriptWithSrcHtml[] = R"HTML(
<html>
<head>
<script src='http://www.example.com/script.js'></script>
</head>
<body></body>
</html>
)HTML";
class MockContentSettingsManagerImpl
: public chrome::mojom::ContentSettingsManager {
public:
struct Log {
int allow_storage_access_count = 0;
int on_content_blocked_count = 0;
ContentSettingsType on_content_blocked_type = ContentSettingsType::DEFAULT;
};
explicit MockContentSettingsManagerImpl(Log* log) : log_(log) {}
~MockContentSettingsManagerImpl() override = default;
// chrome::mojom::ContentSettingsManager methods:
void Clone(mojo::PendingReceiver<chrome::mojom::ContentSettingsManager>
receiver) override {
ADD_FAILURE() << "Not reached";
}
void AllowStorageAccess(int32_t render_frame_id,
StorageType storage_type,
const url::Origin& origin,
const GURL& site_for_cookies,
const url::Origin& top_frame_origin,
base::OnceCallback<void(bool)> callback) override {
++log_->allow_storage_access_count;
std::move(callback).Run(true);
}
void OnContentBlocked(int32_t render_frame_id,
ContentSettingsType type) override {
++log_->on_content_blocked_count;
log_->on_content_blocked_type = type;
}
private:
Log* log_;
};
class MockContentSettingsAgentImpl : public ContentSettingsAgentImpl {
public:
MockContentSettingsAgentImpl(content::RenderFrame* render_frame,
service_manager::BinderRegistry* registry);
~MockContentSettingsAgentImpl() override {}
const GURL& image_url() const { return image_url_; }
const std::string& image_origin() const { return image_origin_; }
// ContentSettingAgentImpl methods:
void BindContentSettingsManager(
mojo::Remote<chrome::mojom::ContentSettingsManager>* manager) override;
int allow_storage_access_count() const {
return log_.allow_storage_access_count;
}
int on_content_blocked_count() const { return log_.on_content_blocked_count; }
ContentSettingsType on_content_blocked_type() const {
return log_.on_content_blocked_type;
}
private:
MockContentSettingsManagerImpl::Log log_;
const GURL image_url_;
const std::string image_origin_;
DISALLOW_COPY_AND_ASSIGN(MockContentSettingsAgentImpl);
};
MockContentSettingsAgentImpl::MockContentSettingsAgentImpl(
content::RenderFrame* render_frame,
service_manager::BinderRegistry* registry)
: ContentSettingsAgentImpl(render_frame, false, registry),
image_url_("http://www.foo.com/image.jpg"),
image_origin_("http://www.foo.com") {}
void MockContentSettingsAgentImpl::BindContentSettingsManager(
mojo::Remote<chrome::mojom::ContentSettingsManager>* manager) {
mojo::MakeSelfOwnedReceiver(
std::make_unique<MockContentSettingsManagerImpl>(&log_),
manager->BindNewPipeAndPassReceiver());
}
// Evaluates a boolean |predicate| every time a provisional load is committed in
// the given |frame| while the instance of this class is in scope, and verifies
// that the result matches the |expectation|.
class CommitTimeConditionChecker : public content::RenderFrameObserver {
public:
using Predicate = base::RepeatingCallback<bool()>;
CommitTimeConditionChecker(content::RenderFrame* frame,
const Predicate& predicate,
bool expectation)
: content::RenderFrameObserver(frame),
predicate_(predicate),
expectation_(expectation) {}
protected:
// RenderFrameObserver:
void OnDestruct() override {}
void DidCommitProvisionalLoad(bool is_same_document_navigation,
ui::PageTransition transition) override {
EXPECT_EQ(expectation_, predicate_.Run());
}
private:
const Predicate predicate_;
const bool expectation_;
DISALLOW_COPY_AND_ASSIGN(CommitTimeConditionChecker);
};
} // namespace
class ContentSettingsAgentImplBrowserTest : public ChromeRenderViewTest {
void SetUp() override {
ChromeRenderViewTest::SetUp();
// Set up a fake url loader factory to ensure that script loader can create
// a WebURLLoader.
CreateFakeWebURLLoaderFactory();
// Unbind the ContentSettingsAgent interface that would be registered by
// the ContentSettingsAgentImpl created when the render frame is created.
view_->GetMainRenderFrame()
->GetAssociatedInterfaceRegistry()
->RemoveInterface(chrome::mojom::ContentSettingsAgent::Name_);
}
};
TEST_F(ContentSettingsAgentImplBrowserTest, DidBlockContentType) {
MockContentSettingsAgentImpl mock_agent(view_->GetMainRenderFrame(),
registry_.get());
mock_agent.DidBlockContentType(ContentSettingsType::COOKIES);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, mock_agent.on_content_blocked_count());
EXPECT_EQ(ContentSettingsType::COOKIES, mock_agent.on_content_blocked_type());
// Blocking the same content type a second time shouldn't send a notification.
mock_agent.DidBlockContentType(ContentSettingsType::COOKIES);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, mock_agent.on_content_blocked_count());
}
// Tests that multiple invokations of AllowDOMStorage result in a single IPC.
TEST_F(ContentSettingsAgentImplBrowserTest, AllowDOMStorage) {
// Load some HTML, so we have a valid security origin.
LoadHTMLWithUrlOverride("<html></html>", "https://example.com/");
MockContentSettingsAgentImpl mock_agent(view_->GetMainRenderFrame(),
registry_.get());
mock_agent.AllowStorage(true);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, mock_agent.allow_storage_access_count());
// Accessing localStorage from the same origin again shouldn't result in a
// new IPC.
mock_agent.AllowStorage(true);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, mock_agent.allow_storage_access_count());
}
// Regression test for http://crbug.com/35011
TEST_F(ContentSettingsAgentImplBrowserTest, JSBlockSentAfterPageLoad) {
MockContentSettingsAgentImpl mock_agent(view_->GetMainRenderFrame(),
registry_.get());
// 1. Load page with JS.
const char kHtml[] =
"<html>"
"<head>"
"<script>document.createElement('div');</script>"
"</head>"
"<body>"
"</body>"
"</html>";
render_thread_->sink().ClearMessages();
LoadHTML(kHtml);
// 2. Block JavaScript.
RendererContentSettingRules content_setting_rules;
ContentSettingsForOneType& script_setting_rules =
content_setting_rules.script_rules;
script_setting_rules.push_back(ContentSettingPatternSource(
ContentSettingsPattern::Wildcard(), ContentSettingsPattern::Wildcard(),
base::Value::FromUniquePtrValue(
content_settings::ContentSettingToValue(CONTENT_SETTING_BLOCK)),
std::string(), false));
ContentSettingsAgentImpl* agent =
ContentSettingsAgentImpl::Get(view_->GetMainRenderFrame());
agent->SetContentSettingRules(&content_setting_rules);
// Make sure no pending messages are in the queue.
base::RunLoop().RunUntilIdle();
render_thread_->sink().ClearMessages();
const auto HasSentOnContentBlocked =
[](MockContentSettingsAgentImpl* mock_agent) {
return mock_agent->on_content_blocked_count() > 0;
};
// 3. Reload page. Verify that the notification that javascript was blocked
// has not yet been sent at the time when the navigation commits.
CommitTimeConditionChecker checker(
view_->GetMainRenderFrame(),
base::Bind(HasSentOnContentBlocked, base::Unretained(&mock_agent)),
false);
std::string url_str = "data:text/html;charset=utf-8,";
url_str.append(kHtml);
GURL url(url_str);
Reload(url);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(HasSentOnContentBlocked(&mock_agent));
}
TEST_F(ContentSettingsAgentImplBrowserTest, PluginsTemporarilyAllowed) {
// Load some HTML.
LoadHTML("<html>Foo</html>");
std::string foo_plugin = "foo";
std::string bar_plugin = "bar";
ContentSettingsAgentImpl* agent =
ContentSettingsAgentImpl::Get(view_->GetMainRenderFrame());
EXPECT_FALSE(agent->IsPluginTemporarilyAllowed(foo_plugin));
// Temporarily allow the "foo" plugin.
agent->OnLoadBlockedPlugins(foo_plugin);
EXPECT_TRUE(agent->IsPluginTemporarilyAllowed(foo_plugin));
EXPECT_FALSE(agent->IsPluginTemporarilyAllowed(bar_plugin));
// Simulate same document navigation.
OnSameDocumentNavigation(GetMainFrame(), true);
EXPECT_TRUE(agent->IsPluginTemporarilyAllowed(foo_plugin));
EXPECT_FALSE(agent->IsPluginTemporarilyAllowed(bar_plugin));
// Navigate to a different page.
LoadHTML("<html>Bar</html>");
EXPECT_FALSE(agent->IsPluginTemporarilyAllowed(foo_plugin));
EXPECT_FALSE(agent->IsPluginTemporarilyAllowed(bar_plugin));
// Temporarily allow all plugins.
agent->OnLoadBlockedPlugins(std::string());
EXPECT_TRUE(agent->IsPluginTemporarilyAllowed(foo_plugin));
EXPECT_TRUE(agent->IsPluginTemporarilyAllowed(bar_plugin));
}
TEST_F(ContentSettingsAgentImplBrowserTest, ImagesBlockedByDefault) {
MockContentSettingsAgentImpl mock_agent(view_->GetMainRenderFrame(),
registry_.get());
// Load some HTML.
LoadHTML("<html>Foo</html>");
// Set the default image blocking setting.
RendererContentSettingRules content_setting_rules;
ContentSettingsForOneType& image_setting_rules =
content_setting_rules.image_rules;
image_setting_rules.push_back(ContentSettingPatternSource(
ContentSettingsPattern::Wildcard(), ContentSettingsPattern::Wildcard(),
base::Value::FromUniquePtrValue(
content_settings::ContentSettingToValue(CONTENT_SETTING_BLOCK)),
std::string(), false));
ContentSettingsAgentImpl* agent =
ContentSettingsAgentImpl::Get(view_->GetMainRenderFrame());
agent->SetContentSettingRules(&content_setting_rules);
EXPECT_FALSE(agent->AllowImage(true, mock_agent.image_url()));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, mock_agent.on_content_blocked_count());
EXPECT_EQ(ContentSettingsType::IMAGES, mock_agent.on_content_blocked_type());
// Create an exception which allows the image.
image_setting_rules.insert(
image_setting_rules.begin(),
ContentSettingPatternSource(
ContentSettingsPattern::Wildcard(),
ContentSettingsPattern::FromString(mock_agent.image_origin()),
base::Value::FromUniquePtrValue(
content_settings::ContentSettingToValue(CONTENT_SETTING_ALLOW)),
std::string(), false));
EXPECT_TRUE(agent->AllowImage(true, mock_agent.image_url()));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, mock_agent.on_content_blocked_count());
}
TEST_F(ContentSettingsAgentImplBrowserTest, ImagesAllowedByDefault) {
MockContentSettingsAgentImpl mock_agent(view_->GetMainRenderFrame(),
registry_.get());
// Load some HTML.
LoadHTML("<html>Foo</html>");
// Set the default image blocking setting.
RendererContentSettingRules content_setting_rules;
ContentSettingsForOneType& image_setting_rules =
content_setting_rules.image_rules;
image_setting_rules.push_back(ContentSettingPatternSource(
ContentSettingsPattern::Wildcard(), ContentSettingsPattern::Wildcard(),
base::Value::FromUniquePtrValue(
content_settings::ContentSettingToValue(CONTENT_SETTING_ALLOW)),
std::string(), false));
ContentSettingsAgentImpl* agent =
ContentSettingsAgentImpl::Get(view_->GetMainRenderFrame());
agent->SetContentSettingRules(&content_setting_rules);
EXPECT_TRUE(agent->AllowImage(true, mock_agent.image_url()));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(0, mock_agent.on_content_blocked_count());
// Create an exception which blocks the image.
image_setting_rules.insert(
image_setting_rules.begin(),
ContentSettingPatternSource(
ContentSettingsPattern::Wildcard(),
ContentSettingsPattern::FromString(mock_agent.image_origin()),
base::Value::FromUniquePtrValue(
content_settings::ContentSettingToValue(CONTENT_SETTING_BLOCK)),
std::string(), false));
EXPECT_FALSE(agent->AllowImage(true, mock_agent.image_url()));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, mock_agent.on_content_blocked_count());
EXPECT_EQ(ContentSettingsType::IMAGES, mock_agent.on_content_blocked_type());
}
TEST_F(ContentSettingsAgentImplBrowserTest, ContentSettingsBlockScripts) {
MockContentSettingsAgentImpl mock_agent(view_->GetMainRenderFrame(),
registry_.get());
// Set the content settings for scripts.
RendererContentSettingRules content_setting_rules;
ContentSettingsForOneType& script_setting_rules =
content_setting_rules.script_rules;
script_setting_rules.push_back(ContentSettingPatternSource(
ContentSettingsPattern::Wildcard(), ContentSettingsPattern::Wildcard(),
base::Value::FromUniquePtrValue(
content_settings::ContentSettingToValue(CONTENT_SETTING_BLOCK)),
std::string(), false));
ContentSettingsAgentImpl* agent =
ContentSettingsAgentImpl::Get(view_->GetMainRenderFrame());
agent->SetContentSettingRules(&content_setting_rules);
// Load a page which contains a script.
LoadHTML(kScriptHtml);
// Verify that the script was blocked.
EXPECT_EQ(1, mock_agent.on_content_blocked_count());
}
TEST_F(ContentSettingsAgentImplBrowserTest, ContentSettingsAllowScripts) {
MockContentSettingsAgentImpl mock_agent(view_->GetMainRenderFrame(),
registry_.get());
// Set the content settings for scripts.
RendererContentSettingRules content_setting_rules;
ContentSettingsForOneType& script_setting_rules =
content_setting_rules.script_rules;
script_setting_rules.push_back(ContentSettingPatternSource(
ContentSettingsPattern::Wildcard(), ContentSettingsPattern::Wildcard(),
base::Value::FromUniquePtrValue(
content_settings::ContentSettingToValue(CONTENT_SETTING_ALLOW)),
std::string(), false));
ContentSettingsAgentImpl* agent =
ContentSettingsAgentImpl::Get(view_->GetMainRenderFrame());
agent->SetContentSettingRules(&content_setting_rules);
// Load a page which contains a script.
LoadHTML(kScriptHtml);
// Verify that the script was not blocked.
EXPECT_EQ(0, mock_agent.on_content_blocked_count());
}
TEST_F(ContentSettingsAgentImplBrowserTest,
ContentSettingsAllowScriptsWithSrc) {
MockContentSettingsAgentImpl mock_agent(view_->GetMainRenderFrame(),
registry_.get());
// Set the content settings for scripts.
RendererContentSettingRules content_setting_rules;
ContentSettingsForOneType& script_setting_rules =
content_setting_rules.script_rules;
script_setting_rules.push_back(ContentSettingPatternSource(
ContentSettingsPattern::Wildcard(), ContentSettingsPattern::Wildcard(),
base::Value::FromUniquePtrValue(
content_settings::ContentSettingToValue(CONTENT_SETTING_ALLOW)),
std::string(), false));
ContentSettingsAgentImpl* agent =
ContentSettingsAgentImpl::Get(view_->GetMainRenderFrame());
agent->SetContentSettingRules(&content_setting_rules);
// Load a page which contains a script.
LoadHTML(kScriptWithSrcHtml);
// Verify that the script was not blocked.
EXPECT_EQ(0, mock_agent.on_content_blocked_count());
}
// Regression test for crbug.com/232410: Load a page with JS blocked. Then,
// allow JS and reload the page. In each case, only one of noscript or script
// tags should be enabled, but never both.
TEST_F(ContentSettingsAgentImplBrowserTest, ContentSettingsNoscriptTag) {
MockContentSettingsAgentImpl mock_agent(view_->GetMainRenderFrame(),
registry_.get());
// 1. Block JavaScript.
RendererContentSettingRules content_setting_rules;
ContentSettingsForOneType& script_setting_rules =
content_setting_rules.script_rules;
script_setting_rules.push_back(ContentSettingPatternSource(
ContentSettingsPattern::Wildcard(), ContentSettingsPattern::Wildcard(),
base::Value::FromUniquePtrValue(
content_settings::ContentSettingToValue(CONTENT_SETTING_BLOCK)),
std::string(), false));
ContentSettingsAgentImpl* agent =
ContentSettingsAgentImpl::Get(view_->GetMainRenderFrame());
agent->SetContentSettingRules(&content_setting_rules);
// 2. Load a page which contains a noscript tag and a script tag. Note that
// the page doesn't have a body tag.
const char kHtml[] =
"<html>"
"<noscript>JS_DISABLED</noscript>"
"<script>document.write('JS_ENABLED');</script>"
"</html>";
LoadHTML(kHtml);
EXPECT_NE(
std::string::npos,
blink::WebFrameContentDumper::DumpLayoutTreeAsText(
GetMainFrame(), blink::WebFrameContentDumper::kLayoutAsTextNormal)
.Utf8()
.find("JS_DISABLED"));
EXPECT_EQ(
std::string::npos,
blink::WebFrameContentDumper::DumpLayoutTreeAsText(
GetMainFrame(), blink::WebFrameContentDumper::kLayoutAsTextNormal)
.Utf8()
.find("JS_ENABLED"));
// 3. Allow JavaScript.
script_setting_rules.clear();
script_setting_rules.push_back(ContentSettingPatternSource(
ContentSettingsPattern::Wildcard(), ContentSettingsPattern::Wildcard(),
base::Value::FromUniquePtrValue(
content_settings::ContentSettingToValue(CONTENT_SETTING_ALLOW)),
std::string(), false));
agent->SetContentSettingRules(&content_setting_rules);
// 4. Reload the page.
std::string url_str = "data:text/html;charset=utf-8,";
url_str.append(kHtml);
GURL url(url_str);
Reload(url);
EXPECT_NE(
std::string::npos,
blink::WebFrameContentDumper::DumpLayoutTreeAsText(
GetMainFrame(), blink::WebFrameContentDumper::kLayoutAsTextNormal)
.Utf8()
.find("JS_ENABLED"));
EXPECT_EQ(
std::string::npos,
blink::WebFrameContentDumper::DumpLayoutTreeAsText(
GetMainFrame(), blink::WebFrameContentDumper::kLayoutAsTextNormal)
.Utf8()
.find("JS_DISABLED"));
}
// Checks that same document navigations don't update content settings for the
// page.
TEST_F(ContentSettingsAgentImplBrowserTest,
ContentSettingsSameDocumentNavigation) {
MockContentSettingsAgentImpl mock_agent(view_->GetMainRenderFrame(),
registry_.get());
// Load a page which contains a script.
LoadHTML(kScriptHtml);
// Verify that the script was not blocked.
EXPECT_EQ(0, mock_agent.on_content_blocked_count());
// Block JavaScript.
RendererContentSettingRules content_setting_rules;
ContentSettingsForOneType& script_setting_rules =
content_setting_rules.script_rules;
script_setting_rules.push_back(ContentSettingPatternSource(
ContentSettingsPattern::Wildcard(), ContentSettingsPattern::Wildcard(),
base::Value::FromUniquePtrValue(
content_settings::ContentSettingToValue(CONTENT_SETTING_BLOCK)),
std::string(), false));
ContentSettingsAgentImpl* agent =
ContentSettingsAgentImpl::Get(view_->GetMainRenderFrame());
agent->SetContentSettingRules(&content_setting_rules);
// The page shouldn't see the change to script blocking setting after a
// same document navigation.
OnSameDocumentNavigation(GetMainFrame(), true);
EXPECT_TRUE(agent->AllowScript(true));
}
TEST_F(ContentSettingsAgentImplBrowserTest, ContentSettingsInterstitialPages) {
MockContentSettingsAgentImpl mock_agent(view_->GetMainRenderFrame(),
registry_.get());
// Block scripts.
RendererContentSettingRules content_setting_rules;
ContentSettingsForOneType& script_setting_rules =
content_setting_rules.script_rules;
script_setting_rules.push_back(ContentSettingPatternSource(
ContentSettingsPattern::Wildcard(), ContentSettingsPattern::Wildcard(),
base::Value::FromUniquePtrValue(
content_settings::ContentSettingToValue(CONTENT_SETTING_BLOCK)),
std::string(), false));
// Block images.
ContentSettingsForOneType& image_setting_rules =
content_setting_rules.image_rules;
image_setting_rules.push_back(ContentSettingPatternSource(
ContentSettingsPattern::Wildcard(), ContentSettingsPattern::Wildcard(),
base::Value::FromUniquePtrValue(
content_settings::ContentSettingToValue(CONTENT_SETTING_BLOCK)),
std::string(), false));
ContentSettingsAgentImpl* agent =
ContentSettingsAgentImpl::Get(view_->GetMainRenderFrame());
agent->SetContentSettingRules(&content_setting_rules);
agent->SetAsInterstitial();
// Load a page which contains a script.
LoadHTML(kScriptHtml);
// Verify that the script was allowed.
EXPECT_EQ(0, mock_agent.on_content_blocked_count());
// Verify that images are allowed.
EXPECT_TRUE(agent->AllowImage(true, mock_agent.image_url()));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(0, mock_agent.on_content_blocked_count());
}
TEST_F(ContentSettingsAgentImplBrowserTest, MixedAutoupgradesDisabledByRules) {
MockContentSettingsAgentImpl mock_agent(view_->GetMainRenderFrame(),
registry_.get());
LoadHTMLWithUrlOverride("<html></html>", "https://example.com/");
// Set the default mixed content blocking setting.
RendererContentSettingRules content_setting_rules;
ContentSettingsForOneType& mixed_content_setting_rules =
content_setting_rules.mixed_content_rules;
mixed_content_setting_rules.push_back(ContentSettingPatternSource(
ContentSettingsPattern::Wildcard(), ContentSettingsPattern::Wildcard(),
base::Value::FromUniquePtrValue(
content_settings::ContentSettingToValue(CONTENT_SETTING_BLOCK)),
std::string(), false));
ContentSettingsAgentImpl* agent =
ContentSettingsAgentImpl::Get(view_->GetMainRenderFrame());
agent->SetContentSettingRules(&content_setting_rules);
EXPECT_TRUE(agent->ShouldAutoupgradeMixedContent());
// Create an exception which allows mixed content.
mixed_content_setting_rules.insert(
mixed_content_setting_rules.begin(),
ContentSettingPatternSource(
ContentSettingsPattern::FromString("https://example.com/"),
ContentSettingsPattern::Wildcard(),
base::Value::FromUniquePtrValue(
content_settings::ContentSettingToValue(CONTENT_SETTING_ALLOW)),
std::string(), false));
EXPECT_FALSE(agent->ShouldAutoupgradeMixedContent());
}