Skip to content

Commit

Permalink
Move the content settings code out of RenderView, since it belongs in…
Browse files Browse the repository at this point in the history
… the Chrome layer.

BUG=76793
Review URL: http://codereview.chromium.org/6873040

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@81955 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
jam@chromium.org committed Apr 18, 2011
1 parent f3f4d7f commit 5e56df8
Show file tree
Hide file tree
Showing 11 changed files with 399 additions and 195 deletions.
2 changes: 2 additions & 0 deletions chrome/chrome_renderer.gypi
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@
'renderer/chrome_render_process_observer.h',
'renderer/chrome_render_view_observer.cc',
'renderer/chrome_render_view_observer.h',
'renderer/content_settings_observer.cc',
'renderer/content_settings_observer.h',
'renderer/devtools_agent.cc',
'renderer/devtools_agent.h',
'renderer/devtools_agent_filter.cc',
Expand Down
9 changes: 6 additions & 3 deletions chrome/renderer/chrome_content_renderer_client.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "chrome/renderer/blocked_plugin.h"
#include "chrome/renderer/chrome_render_process_observer.h"
#include "chrome/renderer/chrome_render_view_observer.h"
#include "chrome/renderer/content_settings_observer.h"
#include "chrome/renderer/devtools_agent.h"
#include "chrome/renderer/devtools_agent_filter.h"
#include "chrome/renderer/extensions/bindings_utils.h"
Expand Down Expand Up @@ -281,6 +282,7 @@ void ChromeContentRendererClient::RenderViewCreated(RenderView* render_view) {
}
#endif

new ContentSettingsObserver(render_view);
new DevToolsAgent(render_view);
new ExtensionHelper(render_view, extension_dispatcher_.get());
new PageLoadHistograms(render_view, histogram_snapshots_.get());
Expand Down Expand Up @@ -381,8 +383,9 @@ WebPlugin* ChromeContentRendererClient::CreatePlugin(
}
}

ContentSetting host_setting = render_view->current_content_settings_.
settings[CONTENT_SETTINGS_TYPE_PLUGINS];
ContentSettingsObserver* observer = ContentSettingsObserver::Get(render_view);
ContentSetting host_setting =
observer->GetContentSetting(CONTENT_SETTINGS_TYPE_PLUGINS);

if (group->RequiresAuthorization() &&
!cmd->HasSwitch(switches::kAlwaysAuthorizePlugins) &&
Expand Down Expand Up @@ -425,7 +428,7 @@ WebPlugin* ChromeContentRendererClient::CreatePlugin(
std::string resource;
if (cmd->HasSwitch(switches::kEnableResourceContentSettings))
resource = group->identifier();
render_view->DidBlockContentType(CONTENT_SETTINGS_TYPE_PLUGINS, resource);
observer->DidBlockContentType(CONTENT_SETTINGS_TYPE_PLUGINS, resource);
if (plugin_setting == CONTENT_SETTING_ASK) {
return CreatePluginPlaceholder(
render_view, frame, params, *group, IDR_CLICK_TO_PLAY_PLUGIN_HTML,
Expand Down
7 changes: 5 additions & 2 deletions chrome/renderer/chrome_render_process_observer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/render_messages.h"
#include "chrome/renderer/content_settings_observer.h"
#include "content/common/view_messages.h"
#include "content/renderer/render_thread.h"
#include "content/renderer/render_view.h"
Expand Down Expand Up @@ -83,8 +84,10 @@ class RenderViewContentSettingsSetter : public RenderViewVisitor {
}

virtual bool Visit(RenderView* render_view) {
if (GURL(render_view->webview()->mainFrame()->url()) == url_)
render_view->SetContentSettings(content_settings_);
if (GURL(render_view->webview()->mainFrame()->url()) == url_) {
ContentSettingsObserver::Get(render_view)->SetContentSettings(
content_settings_);
}
return true;
}

Expand Down
184 changes: 184 additions & 0 deletions chrome/renderer/content_settings_observer.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
// Copyright (c) 2011 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 "chrome/renderer/content_settings_observer.h"

#include "chrome/common/render_messages.h"
#include "chrome/common/url_constants.h"
#include "content/renderer/render_view.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebDataSource.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrameClient.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityOrigin.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebURLRequest.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"

using WebKit::WebDataSource;
using WebKit::WebFrame;
using WebKit::WebFrameClient;
using WebKit::WebSecurityOrigin;
using WebKit::WebURLRequest;
using WebKit::WebView;

namespace {

// True if |frame| contains content that is white-listed for content settings.
static bool IsWhitelistedForContentSettings(WebFrame* frame) {
WebSecurityOrigin origin = frame->securityOrigin();
if (origin.isEmpty())
return false; // Uninitialized document?

if (EqualsASCII(origin.protocol(), chrome::kChromeUIScheme))
return true; // Browser UI elements should still work.

// If the scheme is ftp: or file:, an empty file name indicates a directory
// listing, which requires JavaScript to function properly.
GURL frame_url = frame->url();
const char* kDirProtocols[] = { "ftp", "file" };
for (size_t i = 0; i < arraysize(kDirProtocols); ++i) {
if (EqualsASCII(origin.protocol(), kDirProtocols[i])) {
return frame_url.SchemeIs(kDirProtocols[i]) &&
frame_url.ExtractFileName().empty();
}
}

return false;
}

} // namespace

ContentSettingsObserver::ContentSettingsObserver(RenderView* render_view)
: RenderViewObserver(render_view),
RenderViewObserverTracker<ContentSettingsObserver>(render_view) {
ClearBlockedContentSettings();
}

ContentSettingsObserver::~ContentSettingsObserver() {
}


void ContentSettingsObserver::SetContentSettings(
const ContentSettings& settings) {
current_content_settings_ = settings;
}

ContentSetting ContentSettingsObserver::GetContentSetting(
ContentSettingsType type) {
return current_content_settings_.settings[type];
}

void ContentSettingsObserver::DidBlockContentType(
ContentSettingsType settings_type,
const std::string& resource_identifier) {
if (!content_blocked_[settings_type]) {
content_blocked_[settings_type] = true;
Send(new ViewHostMsg_ContentBlocked(routing_id(), settings_type,
resource_identifier));
}
}

bool ContentSettingsObserver::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(ContentSettingsObserver, message)
IPC_MESSAGE_HANDLER(ViewMsg_SetContentSettingsForLoadingURL,
OnSetContentSettingsForLoadingURL)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}

void ContentSettingsObserver::DidCommitProvisionalLoad(
WebFrame* frame, bool is_new_navigation) {
if (frame->parent())
return; // Not a top-level navigation.

WebDataSource* ds = frame->dataSource();
const WebURLRequest& request = ds->request();

// Clear "block" flags for the new page. This needs to happen before any of
// allowScripts(), allowImages(), allowPlugins() is called for the new page
// so that these functions can correctly detect that a piece of content
// flipped from "not blocked" to "blocked".
ClearBlockedContentSettings();

// Set content settings. Default them from the parent window if one exists.
// This makes sure about:blank windows work as expected.
HostContentSettings::iterator host_content_settings =
host_content_settings_.find(GURL(request.url()));
if (host_content_settings != host_content_settings_.end()) {
SetContentSettings(host_content_settings->second);

// These content settings were merely recorded transiently for this load.
// We can erase them now. If at some point we reload this page, the
// browser will send us new, up-to-date content settings.
host_content_settings_.erase(host_content_settings);
} else if (frame->opener()) {
// The opener's view is not guaranteed to be non-null (it could be
// detached from its page but not yet destructed).
if (WebView* opener_view = frame->opener()->view()) {
RenderView* opener = RenderView::FromWebView(opener_view);
ContentSettingsObserver* observer = ContentSettingsObserver::Get(opener);
SetContentSettings(observer->current_content_settings_);
}
}
}

bool ContentSettingsObserver::AllowImages(WebFrame* frame,
bool enabled_per_settings) {
if (enabled_per_settings &&
AllowContentType(CONTENT_SETTINGS_TYPE_IMAGES)) {
return true;
}

if (IsWhitelistedForContentSettings(frame))
return true;

DidBlockContentType(CONTENT_SETTINGS_TYPE_IMAGES, std::string());
return false; // Other protocols fall through here.
}

bool ContentSettingsObserver::AllowPlugins(WebFrame* frame,
bool enabled_per_settings) {
return render_view()->WebFrameClient::allowPlugins(
frame, enabled_per_settings);
}

bool ContentSettingsObserver::AllowScript(WebFrame* frame,
bool enabled_per_settings) {
if (enabled_per_settings &&
AllowContentType(CONTENT_SETTINGS_TYPE_JAVASCRIPT)) {
return true;
}

if (IsWhitelistedForContentSettings(frame))
return true;

return false; // Other protocols fall through here.
}

void ContentSettingsObserver::DidNotAllowPlugins(WebFrame* frame) {
DidBlockContentType(CONTENT_SETTINGS_TYPE_PLUGINS, std::string());
}

void ContentSettingsObserver::DidNotAllowScript(WebFrame* frame) {
DidBlockContentType(CONTENT_SETTINGS_TYPE_JAVASCRIPT, std::string());
}

void ContentSettingsObserver::OnSetContentSettingsForLoadingURL(
const GURL& url,
const ContentSettings& content_settings) {
host_content_settings_[url] = content_settings;
}

bool ContentSettingsObserver::AllowContentType(
ContentSettingsType settings_type) {
// CONTENT_SETTING_ASK is only valid for cookies.
return current_content_settings_.settings[settings_type] !=
CONTENT_SETTING_BLOCK;
}

void ContentSettingsObserver::ClearBlockedContentSettings() {
for (size_t i = 0; i < arraysize(content_blocked_); ++i)
content_blocked_[i] = false;
}
74 changes: 74 additions & 0 deletions chrome/renderer/content_settings_observer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright (c) 2011 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.

#ifndef CHROME_RENDERER_CONTENT_SETTINGS_OBSERVER_H_
#define CHROME_RENDERER_CONTENT_SETTINGS_OBSERVER_H_
#pragma once

#include <map>

#include "chrome/common/content_settings.h"
#include "content/renderer/render_view_observer.h"
#include "content/renderer/render_view_observer_tracker.h"

class GURL;

// Handles blocking content per content settings for each RenderView.
class ContentSettingsObserver
: public RenderViewObserver,
public RenderViewObserverTracker<ContentSettingsObserver> {
public:
explicit ContentSettingsObserver(RenderView* render_view);
virtual ~ContentSettingsObserver();

// Sets the content settings that back allowScripts(), allowImages(), and
// allowPlugins().
void SetContentSettings(const ContentSettings& settings);

// Returns the setting for the given type.
ContentSetting GetContentSetting(ContentSettingsType type);

// Sends an IPC notification that the specified content type was blocked.
// If the content type requires it, |resource_identifier| names the specific
// resource that was blocked (the plugin path in the case of plugins),
// otherwise it's the empty string.
void DidBlockContentType(ContentSettingsType settings_type,
const std::string& resource_identifier);

private:
// RenderViewObserver implementation.
virtual bool OnMessageReceived(const IPC::Message& message);
virtual void DidCommitProvisionalLoad(WebKit::WebFrame* frame,
bool is_new_navigation);
virtual bool AllowImages(WebKit::WebFrame* frame, bool enabled_per_settings);
virtual bool AllowPlugins(WebKit::WebFrame* frame, bool enabled_per_settings);
virtual bool AllowScript(WebKit::WebFrame* frame, bool enabled_per_settings);
virtual void DidNotAllowPlugins(WebKit::WebFrame* frame);
virtual void DidNotAllowScript(WebKit::WebFrame* frame);

// Message handlers.
void OnSetContentSettingsForLoadingURL(
const GURL& url,
const ContentSettings& content_settings);

// Helper method that returns if the user wants to block content of type
// |content_type|.
bool AllowContentType(ContentSettingsType settings_type);

// Resets the |content_blocked_| array.
void ClearBlockedContentSettings();

typedef std::map<GURL, ContentSettings> HostContentSettings;
HostContentSettings host_content_settings_;

// Stores if loading of images, scripts, and plugins is allowed.
ContentSettings current_content_settings_;

// Stores if images, scripts, and plugins have actually been blocked.
bool content_blocked_[CONTENT_SETTINGS_NUM_TYPES];

DISALLOW_COPY_AND_ASSIGN(ContentSettingsObserver);
};

#endif // CHROME_RENDERER_CONTENT_SETTINGS_OBSERVER_H_
61 changes: 61 additions & 0 deletions chrome/renderer/content_settings_observer_browsertest.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright (c) 2011 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 "chrome/common/content_settings.h"
#include "chrome/common/render_messages.h"
#include "chrome/renderer/content_settings_observer.h"
#include "chrome/test/render_view_test.h"
#include "content/common/view_messages.h"
#include "testing/gtest/include/gtest/gtest.h"

// Regression test for http://crbug.com/35011
TEST_F(RenderViewTest, JSBlockSentAfterPageLoad) {
// 1. Load page with JS.
std::string html = "<html>"
"<head>"
"<script>document.createElement('div');</script>"
"</head>"
"<body>"
"</body>"
"</html>";
render_thread_.sink().ClearMessages();
LoadHTML(html.c_str());

// 2. Block JavaScript.
ContentSettings settings;
for (int i = 0; i < CONTENT_SETTINGS_NUM_TYPES; ++i)
settings.settings[i] = CONTENT_SETTING_ALLOW;
settings.settings[CONTENT_SETTINGS_TYPE_JAVASCRIPT] = CONTENT_SETTING_BLOCK;
ContentSettingsObserver* observer = ContentSettingsObserver::Get(view_);
observer->SetContentSettings(settings);

// Make sure no pending messages are in the queue.
ProcessPendingMessages();
render_thread_.sink().ClearMessages();

// 3. Reload page.
ViewMsg_Navigate_Params params;
std::string url_str = "data:text/html;charset=utf-8,";
url_str.append(html);
GURL url(url_str);
params.url = url;
params.navigation_type = ViewMsg_Navigate_Type::RELOAD;
view_->OnNavigate(params);
ProcessPendingMessages();

// 4. Verify that the notification that javascript was blocked is sent after
// the navigation notifiction is sent.
int navigation_index = -1;
int block_index = -1;
for (size_t i = 0; i < render_thread_.sink().message_count(); ++i) {
const IPC::Message* msg = render_thread_.sink().GetMessageAt(i);
if (msg->type() == ViewHostMsg_FrameNavigate::ID)
navigation_index = i;
if (msg->type() == ViewHostMsg_ContentBlocked::ID)
block_index = i;
}
EXPECT_NE(-1, navigation_index);
EXPECT_NE(-1, block_index);
EXPECT_LT(navigation_index, block_index);
}
Loading

0 comments on commit 5e56df8

Please sign in to comment.