1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/renderer/content_settings_observer.h"
7 #include "chrome/common/render_messages.h"
8 #include "chrome/common/url_constants.h"
9 #include "content/public/renderer/document_state.h"
10 #include "content/public/renderer/navigation_state.h"
11 #include "content/public/renderer/render_view.h"
12 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDataSource.h"
13 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
14 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
15 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrameClient.h"
16 #include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityOrigin.h"
17 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebURL.h"
18 #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
20 using WebKit::WebDataSource
;
21 using WebKit::WebFrame
;
22 using WebKit::WebFrameClient
;
23 using WebKit::WebSecurityOrigin
;
24 using WebKit::WebString
;
26 using WebKit::WebView
;
27 using content::DocumentState
;
28 using content::NavigationState
;
32 GURL
GetOriginOrURL(const WebFrame
* frame
) {
33 WebString top_origin
= frame
->top()->document().securityOrigin().toString();
34 // The the |top_origin| is unique ("null") e.g., for file:// URLs. Use the
35 // document URL as the primary URL in those cases.
36 if (top_origin
== "null")
37 return frame
->top()->document().url();
38 return GURL(top_origin
);
41 ContentSetting
GetContentSettingFromRules(
42 const ContentSettingsForOneType
& rules
,
43 const WebFrame
* frame
,
44 const GURL
& secondary_url
) {
45 ContentSettingsForOneType::const_iterator it
;
46 // If there is only one rule, it's the default rule and we don't need to match
48 if (rules
.size() == 1) {
49 DCHECK(rules
[0].primary_pattern
== ContentSettingsPattern::Wildcard());
50 DCHECK(rules
[0].secondary_pattern
== ContentSettingsPattern::Wildcard());
51 return rules
[0].setting
;
53 const GURL
& primary_url
= GetOriginOrURL(frame
);
54 for (it
= rules
.begin(); it
!= rules
.end(); ++it
) {
55 if (it
->primary_pattern
.Matches(primary_url
) &&
56 it
->secondary_pattern
.Matches(secondary_url
)) {
61 return CONTENT_SETTING_DEFAULT
;
66 ContentSettingsObserver::ContentSettingsObserver(
67 content::RenderView
* render_view
)
68 : content::RenderViewObserver(render_view
),
69 content::RenderViewObserverTracker
<ContentSettingsObserver
>(render_view
),
70 content_setting_rules_(NULL
),
71 plugins_temporarily_allowed_(false),
72 is_interstitial_page_(false) {
73 ClearBlockedContentSettings();
76 ContentSettingsObserver::~ContentSettingsObserver() {
79 void ContentSettingsObserver::SetContentSettingRules(
80 const RendererContentSettingRules
* content_setting_rules
) {
81 content_setting_rules_
= content_setting_rules
;
84 void ContentSettingsObserver::DidBlockContentType(
85 ContentSettingsType settings_type
,
86 const std::string
& resource_identifier
) {
87 // Always send a message when |resource_identifier| is not empty, to tell the
88 // browser which resource was blocked (otherwise the browser will only show
89 // the first resource to be blocked, and none that are blocked at a later
91 if (!content_blocked_
[settings_type
] || !resource_identifier
.empty()) {
92 content_blocked_
[settings_type
] = true;
93 Send(new ChromeViewHostMsg_ContentBlocked(routing_id(), settings_type
,
94 resource_identifier
));
98 bool ContentSettingsObserver::OnMessageReceived(const IPC::Message
& message
) {
100 IPC_BEGIN_MESSAGE_MAP(ContentSettingsObserver
, message
)
101 // Don't swallow LoadBlockedPlugins messages, as they're sent to every
103 IPC_MESSAGE_HANDLER_GENERIC(ChromeViewMsg_LoadBlockedPlugins
,
104 OnLoadBlockedPlugins(); handled
= false)
105 IPC_MESSAGE_UNHANDLED(handled
= false)
106 IPC_END_MESSAGE_MAP()
110 void ContentSettingsObserver::DidCommitProvisionalLoad(
111 WebFrame
* frame
, bool is_new_navigation
) {
113 return; // Not a top-level navigation.
115 DocumentState
* document_state
= DocumentState::FromDataSource(
116 frame
->dataSource());
117 NavigationState
* navigation_state
= document_state
->navigation_state();
118 if (!navigation_state
->was_within_same_page()) {
119 // Clear "block" flags for the new page. This needs to happen before any of
120 // |AllowScript()|, |AllowScriptFromSource()|, |AllowImage()|, or
121 // |AllowPlugins()| is called for the new page so that these functions can
122 // correctly detect that a piece of content flipped from "not blocked" to
124 ClearBlockedContentSettings();
125 plugins_temporarily_allowed_
= false;
128 GURL url
= frame
->document().url();
129 // If we start failing this DCHECK, please makes sure we don't regress
130 // this bug: http://code.google.com/p/chromium/issues/detail?id=79304
131 DCHECK(frame
->document().securityOrigin().toString() == "null" ||
132 !url
.SchemeIs(chrome::kDataScheme
));
135 bool ContentSettingsObserver::AllowDatabase(WebFrame
* frame
,
136 const WebString
& name
,
137 const WebString
& display_name
,
138 unsigned long estimated_size
) {
139 if (frame
->document().securityOrigin().isUnique() ||
140 frame
->top()->document().securityOrigin().isUnique())
144 Send(new ChromeViewHostMsg_AllowDatabase(
145 routing_id(), GURL(frame
->document().securityOrigin().toString()),
146 GURL(frame
->top()->document().securityOrigin().toString()),
147 name
, display_name
, &result
));
151 bool ContentSettingsObserver::AllowFileSystem(WebFrame
* frame
) {
152 if (frame
->document().securityOrigin().isUnique() ||
153 frame
->top()->document().securityOrigin().isUnique())
157 Send(new ChromeViewHostMsg_AllowFileSystem(
158 routing_id(), GURL(frame
->document().securityOrigin().toString()),
159 GURL(frame
->top()->document().securityOrigin().toString()), &result
));
163 bool ContentSettingsObserver::AllowImage(WebFrame
* frame
,
164 bool enabled_per_settings
,
165 const WebURL
& image_url
) {
166 if (is_interstitial_page_
)
168 if (IsWhitelistedForContentSettings(frame
))
171 bool allow
= enabled_per_settings
;
172 if (content_setting_rules_
&& enabled_per_settings
) {
173 GURL
secondary_url(image_url
);
174 allow
= GetContentSettingFromRules(
175 content_setting_rules_
->image_rules
,
176 frame
, secondary_url
) != CONTENT_SETTING_BLOCK
;
180 DidBlockContentType(CONTENT_SETTINGS_TYPE_IMAGES
, std::string());
184 bool ContentSettingsObserver::AllowIndexedDB(WebFrame
* frame
,
185 const WebString
& name
,
186 const WebSecurityOrigin
& origin
) {
187 if (frame
->document().securityOrigin().isUnique() ||
188 frame
->top()->document().securityOrigin().isUnique())
192 Send(new ChromeViewHostMsg_AllowIndexedDB(
193 routing_id(), GURL(frame
->document().securityOrigin().toString()),
194 GURL(frame
->top()->document().securityOrigin().toString()),
199 bool ContentSettingsObserver::AllowPlugins(WebFrame
* frame
,
200 bool enabled_per_settings
) {
201 return enabled_per_settings
;
204 bool ContentSettingsObserver::AllowScript(WebFrame
* frame
,
205 bool enabled_per_settings
) {
206 if (is_interstitial_page_
)
208 if (!enabled_per_settings
)
211 std::map
<WebFrame
*, bool>::const_iterator it
=
212 cached_script_permissions_
.find(frame
);
213 if (it
!= cached_script_permissions_
.end())
216 // Evaluate the content setting rules before
217 // |IsWhitelistedForContentSettings|; if there is only the default rule
218 // allowing all scripts, it's quicker this way.
220 if (content_setting_rules_
) {
221 ContentSetting setting
= GetContentSettingFromRules(
222 content_setting_rules_
->script_rules
,
224 GURL(frame
->document().securityOrigin().toString()));
225 allow
= setting
!= CONTENT_SETTING_BLOCK
;
227 allow
= allow
|| IsWhitelistedForContentSettings(frame
);
229 cached_script_permissions_
[frame
] = allow
;
233 bool ContentSettingsObserver::AllowScriptFromSource(
235 bool enabled_per_settings
,
236 const WebKit::WebURL
& script_url
) {
237 if (is_interstitial_page_
)
239 if (!enabled_per_settings
)
243 if (content_setting_rules_
) {
244 ContentSetting setting
= GetContentSettingFromRules(
245 content_setting_rules_
->script_rules
,
248 allow
= setting
!= CONTENT_SETTING_BLOCK
;
250 return allow
|| IsWhitelistedForContentSettings(frame
);
253 bool ContentSettingsObserver::AllowStorage(WebFrame
* frame
, bool local
) {
254 if (frame
->document().securityOrigin().isUnique() ||
255 frame
->top()->document().securityOrigin().isUnique())
259 StoragePermissionsKey
key(
260 GURL(frame
->document().securityOrigin().toString()), local
);
261 std::map
<StoragePermissionsKey
, bool>::const_iterator permissions
=
262 cached_storage_permissions_
.find(key
);
263 if (permissions
!= cached_storage_permissions_
.end())
264 return permissions
->second
;
266 Send(new ChromeViewHostMsg_AllowDOMStorage(
267 routing_id(), GURL(frame
->document().securityOrigin().toString()),
268 GURL(frame
->top()->document().securityOrigin().toString()),
270 cached_storage_permissions_
[key
] = result
;
274 void ContentSettingsObserver::DidNotAllowPlugins(WebFrame
* frame
) {
275 DidBlockContentType(CONTENT_SETTINGS_TYPE_PLUGINS
, std::string());
278 void ContentSettingsObserver::DidNotAllowScript(WebFrame
* frame
) {
279 DidBlockContentType(CONTENT_SETTINGS_TYPE_JAVASCRIPT
, std::string());
282 void ContentSettingsObserver::SetAsInterstitial() {
283 is_interstitial_page_
= true;
286 void ContentSettingsObserver::OnLoadBlockedPlugins() {
287 plugins_temporarily_allowed_
= true;
290 void ContentSettingsObserver::ClearBlockedContentSettings() {
291 for (size_t i
= 0; i
< arraysize(content_blocked_
); ++i
)
292 content_blocked_
[i
] = false;
293 cached_storage_permissions_
.clear();
294 cached_script_permissions_
.clear();
297 bool ContentSettingsObserver::IsWhitelistedForContentSettings(WebFrame
* frame
) {
298 return IsWhitelistedForContentSettings(frame
->document().securityOrigin(),
299 frame
->document().url());
302 bool ContentSettingsObserver::IsWhitelistedForContentSettings(
303 const WebSecurityOrigin
& origin
,
304 const GURL
& document_url
) {
305 if (origin
.isUnique())
306 return false; // Uninitialized document?
308 if (EqualsASCII(origin
.protocol(), chrome::kChromeUIScheme
))
309 return true; // Browser UI elements should still work.
311 if (EqualsASCII(origin
.protocol(), chrome::kChromeDevToolsScheme
))
312 return true; // DevTools UI elements should still work.
314 if (EqualsASCII(origin
.protocol(), chrome::kExtensionScheme
))
317 if (EqualsASCII(origin
.protocol(), chrome::kChromeInternalScheme
))
320 // If the scheme is ftp: or file:, an empty file name indicates a directory
321 // listing, which requires JavaScript to function properly.
322 const char* kDirProtocols
[] = { chrome::kFtpScheme
, chrome::kFileScheme
};
323 for (size_t i
= 0; i
< arraysize(kDirProtocols
); ++i
) {
324 if (EqualsASCII(origin
.protocol(), kDirProtocols
[i
])) {
325 return document_url
.SchemeIs(kDirProtocols
[i
]) &&
326 document_url
.ExtractFileName().empty();