Fixes option-right in textfields when VoiceOver is set to read to the right of the...
[chromium-blink-merge.git] / chrome / renderer / content_settings_observer.cc
blob7c540b9afa03d4899b6894cfb1223a5d16d52e85
1 // Copyright (c) 2012 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"
19 #include "webkit/glue/weburlresponse_extradata_impl.h"
21 using WebKit::WebDataSource;
22 using WebKit::WebFrame;
23 using WebKit::WebFrameClient;
24 using WebKit::WebSecurityOrigin;
25 using WebKit::WebString;
26 using WebKit::WebURL;
27 using WebKit::WebView;
28 using content::DocumentState;
29 using content::NavigationState;
31 namespace {
33 GURL GetOriginOrURL(const WebFrame* frame) {
34 WebString top_origin = frame->top()->document().securityOrigin().toString();
35 // The the |top_origin| is unique ("null") e.g., for file:// URLs. Use the
36 // document URL as the primary URL in those cases.
37 if (top_origin == "null")
38 return frame->top()->document().url();
39 return GURL(top_origin);
42 ContentSetting GetContentSettingFromRules(
43 const ContentSettingsForOneType& rules,
44 const WebFrame* frame,
45 const GURL& secondary_url) {
46 ContentSettingsForOneType::const_iterator it;
47 // If there is only one rule, it's the default rule and we don't need to match
48 // the patterns.
49 if (rules.size() == 1) {
50 DCHECK(rules[0].primary_pattern == ContentSettingsPattern::Wildcard());
51 DCHECK(rules[0].secondary_pattern == ContentSettingsPattern::Wildcard());
52 return rules[0].setting;
54 const GURL& primary_url = GetOriginOrURL(frame);
55 for (it = rules.begin(); it != rules.end(); ++it) {
56 if (it->primary_pattern.Matches(primary_url) &&
57 it->secondary_pattern.Matches(secondary_url)) {
58 return it->setting;
61 NOTREACHED();
62 return CONTENT_SETTING_DEFAULT;
65 } // namespace
67 ContentSettingsObserver::ContentSettingsObserver(
68 content::RenderView* render_view)
69 : content::RenderViewObserver(render_view),
70 content::RenderViewObserverTracker<ContentSettingsObserver>(render_view),
71 content_setting_rules_(NULL),
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 bool ContentSettingsObserver::IsPluginTemporarilyAllowed(
85 const std::string& identifier) {
86 // If the empty string is in here, it means all plug-ins are allowed.
87 // TODO(bauerb): Remove this once we only pass in explicit identifiers.
88 return (temporarily_allowed_plugins_.find(identifier) !=
89 temporarily_allowed_plugins_.end()) ||
90 (temporarily_allowed_plugins_.find(std::string()) !=
91 temporarily_allowed_plugins_.end());
94 void ContentSettingsObserver::DidBlockContentType(
95 ContentSettingsType settings_type,
96 const std::string& resource_identifier) {
97 // Always send a message when |resource_identifier| is not empty, to tell the
98 // browser which resource was blocked (otherwise the browser will only show
99 // the first resource to be blocked, and none that are blocked at a later
100 // time).
101 if (!content_blocked_[settings_type] || !resource_identifier.empty()) {
102 content_blocked_[settings_type] = true;
103 Send(new ChromeViewHostMsg_ContentBlocked(routing_id(), settings_type,
104 resource_identifier));
108 bool ContentSettingsObserver::OnMessageReceived(const IPC::Message& message) {
109 bool handled = true;
110 IPC_BEGIN_MESSAGE_MAP(ContentSettingsObserver, message)
111 IPC_MESSAGE_HANDLER(ChromeViewMsg_SetAsInterstitial, OnSetAsInterstitial)
112 IPC_MESSAGE_UNHANDLED(handled = false)
113 IPC_END_MESSAGE_MAP()
114 if (handled)
115 return true;
117 // Don't swallow LoadBlockedPlugins messages, as they're sent to every
118 // blocked plugin.
119 IPC_BEGIN_MESSAGE_MAP(ContentSettingsObserver, message)
120 IPC_MESSAGE_HANDLER(ChromeViewMsg_LoadBlockedPlugins, OnLoadBlockedPlugins)
121 IPC_END_MESSAGE_MAP()
123 return false;
126 void ContentSettingsObserver::DidCommitProvisionalLoad(
127 WebFrame* frame, bool is_new_navigation) {
128 if (frame->parent())
129 return; // Not a top-level navigation.
131 DocumentState* document_state = DocumentState::FromDataSource(
132 frame->dataSource());
133 NavigationState* navigation_state = document_state->navigation_state();
134 if (!navigation_state->was_within_same_page()) {
135 // Clear "block" flags for the new page. This needs to happen before any of
136 // |AllowScript()|, |AllowScriptFromSource()|, |AllowImage()|, or
137 // |AllowPlugins()| is called for the new page so that these functions can
138 // correctly detect that a piece of content flipped from "not blocked" to
139 // "blocked".
140 ClearBlockedContentSettings();
141 temporarily_allowed_plugins_.clear();
144 GURL url = frame->document().url();
145 // If we start failing this DCHECK, please makes sure we don't regress
146 // this bug: http://code.google.com/p/chromium/issues/detail?id=79304
147 DCHECK(frame->document().securityOrigin().toString() == "null" ||
148 !url.SchemeIs(chrome::kDataScheme));
151 bool ContentSettingsObserver::AllowDatabase(WebFrame* frame,
152 const WebString& name,
153 const WebString& display_name,
154 unsigned long estimated_size) {
155 if (frame->document().securityOrigin().isUnique() ||
156 frame->top()->document().securityOrigin().isUnique())
157 return false;
159 bool result = false;
160 Send(new ChromeViewHostMsg_AllowDatabase(
161 routing_id(), GURL(frame->document().securityOrigin().toString()),
162 GURL(frame->top()->document().securityOrigin().toString()),
163 name, display_name, &result));
164 return result;
167 bool ContentSettingsObserver::AllowFileSystem(WebFrame* frame) {
168 if (frame->document().securityOrigin().isUnique() ||
169 frame->top()->document().securityOrigin().isUnique())
170 return false;
172 bool result = false;
173 Send(new ChromeViewHostMsg_AllowFileSystem(
174 routing_id(), GURL(frame->document().securityOrigin().toString()),
175 GURL(frame->top()->document().securityOrigin().toString()), &result));
176 return result;
179 bool ContentSettingsObserver::AllowImage(WebFrame* frame,
180 bool enabled_per_settings,
181 const WebURL& image_url) {
182 bool allow = enabled_per_settings;
183 if (enabled_per_settings) {
184 if (is_interstitial_page_)
185 return true;
186 if (IsWhitelistedForContentSettings(frame))
187 return true;
189 if (content_setting_rules_) {
190 GURL secondary_url(image_url);
191 allow = GetContentSettingFromRules(
192 content_setting_rules_->image_rules,
193 frame, secondary_url) != CONTENT_SETTING_BLOCK;
196 if (!allow)
197 DidBlockContentType(CONTENT_SETTINGS_TYPE_IMAGES, std::string());
198 return allow;
201 bool ContentSettingsObserver::AllowIndexedDB(WebFrame* frame,
202 const WebString& name,
203 const WebSecurityOrigin& origin) {
204 if (frame->document().securityOrigin().isUnique() ||
205 frame->top()->document().securityOrigin().isUnique())
206 return false;
208 bool result = false;
209 Send(new ChromeViewHostMsg_AllowIndexedDB(
210 routing_id(), GURL(frame->document().securityOrigin().toString()),
211 GURL(frame->top()->document().securityOrigin().toString()),
212 name, &result));
213 return result;
216 bool ContentSettingsObserver::AllowPlugins(WebFrame* frame,
217 bool enabled_per_settings) {
218 return enabled_per_settings;
221 bool ContentSettingsObserver::AllowScript(WebFrame* frame,
222 bool enabled_per_settings) {
223 if (!enabled_per_settings)
224 return false;
225 if (is_interstitial_page_)
226 return true;
228 std::map<WebFrame*, bool>::const_iterator it =
229 cached_script_permissions_.find(frame);
230 if (it != cached_script_permissions_.end())
231 return it->second;
233 // Evaluate the content setting rules before
234 // |IsWhitelistedForContentSettings|; if there is only the default rule
235 // allowing all scripts, it's quicker this way.
236 bool allow = true;
237 if (content_setting_rules_) {
238 ContentSetting setting = GetContentSettingFromRules(
239 content_setting_rules_->script_rules,
240 frame,
241 GURL(frame->document().securityOrigin().toString()));
242 allow = setting != CONTENT_SETTING_BLOCK;
244 allow = allow || IsWhitelistedForContentSettings(frame);
246 cached_script_permissions_[frame] = allow;
247 return allow;
250 bool ContentSettingsObserver::AllowScriptFromSource(
251 WebFrame* frame,
252 bool enabled_per_settings,
253 const WebKit::WebURL& script_url) {
254 if (!enabled_per_settings)
255 return false;
256 if (is_interstitial_page_)
257 return true;
259 bool allow = true;
260 if (content_setting_rules_) {
261 ContentSetting setting = GetContentSettingFromRules(
262 content_setting_rules_->script_rules,
263 frame,
264 GURL(script_url));
265 allow = setting != CONTENT_SETTING_BLOCK;
267 return allow || IsWhitelistedForContentSettings(frame);
270 bool ContentSettingsObserver::AllowStorage(WebFrame* frame, bool local) {
271 if (frame->document().securityOrigin().isUnique() ||
272 frame->top()->document().securityOrigin().isUnique())
273 return false;
274 bool result = false;
276 StoragePermissionsKey key(
277 GURL(frame->document().securityOrigin().toString()), local);
278 std::map<StoragePermissionsKey, bool>::const_iterator permissions =
279 cached_storage_permissions_.find(key);
280 if (permissions != cached_storage_permissions_.end())
281 return permissions->second;
283 Send(new ChromeViewHostMsg_AllowDOMStorage(
284 routing_id(), GURL(frame->document().securityOrigin().toString()),
285 GURL(frame->top()->document().securityOrigin().toString()),
286 local, &result));
287 cached_storage_permissions_[key] = result;
288 return result;
291 void ContentSettingsObserver::DidNotAllowPlugins(WebFrame* frame) {
292 DidBlockContentType(CONTENT_SETTINGS_TYPE_PLUGINS, std::string());
295 void ContentSettingsObserver::DidNotAllowScript(WebFrame* frame) {
296 DidBlockContentType(CONTENT_SETTINGS_TYPE_JAVASCRIPT, std::string());
299 void ContentSettingsObserver::OnLoadBlockedPlugins(
300 const std::string& identifier) {
301 temporarily_allowed_plugins_.insert(identifier);
304 void ContentSettingsObserver::OnSetAsInterstitial() {
305 is_interstitial_page_ = true;
308 void ContentSettingsObserver::ClearBlockedContentSettings() {
309 for (size_t i = 0; i < arraysize(content_blocked_); ++i)
310 content_blocked_[i] = false;
311 cached_storage_permissions_.clear();
312 cached_script_permissions_.clear();
315 bool ContentSettingsObserver::IsWhitelistedForContentSettings(WebFrame* frame) {
316 // Whitelist ftp directory listings, as they require JavaScript to function
317 // properly.
318 webkit_glue::WebURLResponseExtraDataImpl* extra_data =
319 static_cast<webkit_glue::WebURLResponseExtraDataImpl*>(
320 frame->dataSource()->response().extraData());
321 if (extra_data && extra_data->is_ftp_directory_listing())
322 return true;
323 return IsWhitelistedForContentSettings(frame->document().securityOrigin(),
324 frame->document().url());
327 bool ContentSettingsObserver::IsWhitelistedForContentSettings(
328 const WebSecurityOrigin& origin,
329 const GURL& document_url) {
330 if (document_url == GURL(content::kUnreachableWebDataURL))
331 return true;
333 if (origin.isUnique())
334 return false; // Uninitialized document?
336 if (EqualsASCII(origin.protocol(), chrome::kChromeUIScheme))
337 return true; // Browser UI elements should still work.
339 if (EqualsASCII(origin.protocol(), chrome::kChromeDevToolsScheme))
340 return true; // DevTools UI elements should still work.
342 if (EqualsASCII(origin.protocol(), chrome::kExtensionScheme))
343 return true;
345 if (EqualsASCII(origin.protocol(), chrome::kChromeInternalScheme))
346 return true;
348 // If the scheme is file:, an empty file name indicates a directory listing,
349 // which requires JavaScript to function properly.
350 if (EqualsASCII(origin.protocol(), chrome::kFileScheme)) {
351 return document_url.SchemeIs(chrome::kFileScheme) &&
352 document_url.ExtractFileName().empty();
355 return false;