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.
4
5 #include "chrome/renderer/content_settings_observer.h"
6
7 #include "base/command_line.h"
8 #include "base/metrics/histogram.h"
9 #include "chrome/common/chrome_switches.h"
10 #include "chrome/common/render_messages.h"
11 #include "chrome/common/url_constants.h"
12 #include "content/public/renderer/document_state.h"
13 #include "content/public/renderer/navigation_state.h"
14 #include "content/public/renderer/render_frame.h"
15 #include "content/public/renderer/render_view.h"
16 #include "extensions/common/constants.h"
17 #include "extensions/common/extension.h"
18 #include "extensions/common/permissions/permissions_data.h"
19 #include "extensions/renderer/dispatcher.h"
20 #include "third_party/WebKit/public/platform/WebPermissionCallbacks.h"
21 #include "third_party/WebKit/public/platform/WebURL.h"
22 #include "third_party/WebKit/public/web/WebDataSource.h"
23 #include "third_party/WebKit/public/web/WebDocument.h"
24 #include "third_party/WebKit/public/web/WebFrame.h"
25 #include "third_party/WebKit/public/web/WebFrameClient.h"
26 #include "third_party/WebKit/public/web/WebSecurityOrigin.h"
27 #include "third_party/WebKit/public/web/WebView.h"
28 #include "webkit/child/weburlresponse_extradata_impl.h"
29
30 using blink::WebDataSource;
31 using blink::WebDocument;
32 using blink::WebFrame;
33 using blink::WebPermissionCallbacks;
34 using blink::WebSecurityOrigin;
35 using blink::WebString;
36 using blink::WebURL;
37 using blink::WebView;
38 using content::DocumentState;
39 using content::NavigationState;
40 using extensions::APIPermission;
41
42 namespace {
43
44 enum {
45 INSECURE_CONTENT_DISPLAY = 0,
46 INSECURE_CONTENT_DISPLAY_HOST_GOOGLE,
47 INSECURE_CONTENT_DISPLAY_HOST_WWW_GOOGLE,
48 INSECURE_CONTENT_DISPLAY_HTML,
49 INSECURE_CONTENT_RUN,
50 INSECURE_CONTENT_RUN_HOST_GOOGLE,
51 INSECURE_CONTENT_RUN_HOST_WWW_GOOGLE,
52 INSECURE_CONTENT_RUN_TARGET_YOUTUBE,
53 INSECURE_CONTENT_RUN_JS,
54 INSECURE_CONTENT_RUN_CSS,
55 INSECURE_CONTENT_RUN_SWF,
56 INSECURE_CONTENT_DISPLAY_HOST_YOUTUBE,
57 INSECURE_CONTENT_RUN_HOST_YOUTUBE,
58 INSECURE_CONTENT_RUN_HOST_GOOGLEUSERCONTENT,
59 INSECURE_CONTENT_DISPLAY_HOST_MAIL_GOOGLE,
60 INSECURE_CONTENT_RUN_HOST_MAIL_GOOGLE,
61 INSECURE_CONTENT_DISPLAY_HOST_PLUS_GOOGLE,
62 INSECURE_CONTENT_RUN_HOST_PLUS_GOOGLE,
63 INSECURE_CONTENT_DISPLAY_HOST_DOCS_GOOGLE,
64 INSECURE_CONTENT_RUN_HOST_DOCS_GOOGLE,
65 INSECURE_CONTENT_DISPLAY_HOST_SITES_GOOGLE,
66 INSECURE_CONTENT_RUN_HOST_SITES_GOOGLE,
67 INSECURE_CONTENT_DISPLAY_HOST_PICASAWEB_GOOGLE,
68 INSECURE_CONTENT_RUN_HOST_PICASAWEB_GOOGLE,
69 INSECURE_CONTENT_DISPLAY_HOST_GOOGLE_READER,
70 INSECURE_CONTENT_RUN_HOST_GOOGLE_READER,
71 INSECURE_CONTENT_DISPLAY_HOST_CODE_GOOGLE,
72 INSECURE_CONTENT_RUN_HOST_CODE_GOOGLE,
73 INSECURE_CONTENT_DISPLAY_HOST_GROUPS_GOOGLE,
74 INSECURE_CONTENT_RUN_HOST_GROUPS_GOOGLE,
75 INSECURE_CONTENT_DISPLAY_HOST_MAPS_GOOGLE,
76 INSECURE_CONTENT_RUN_HOST_MAPS_GOOGLE,
77 INSECURE_CONTENT_DISPLAY_HOST_GOOGLE_SUPPORT,
78 INSECURE_CONTENT_RUN_HOST_GOOGLE_SUPPORT,
79 INSECURE_CONTENT_DISPLAY_HOST_GOOGLE_INTL,
80 INSECURE_CONTENT_RUN_HOST_GOOGLE_INTL,
81 INSECURE_CONTENT_NUM_EVENTS
82 };
83
84 // Constants for UMA statistic collection.
85 static const char kWWWDotGoogleDotCom[] = "www.google.com";
86 static const char kMailDotGoogleDotCom[] = "mail.google.com";
87 static const char kPlusDotGoogleDotCom[] = "plus.google.com";
88 static const char kDocsDotGoogleDotCom[] = "docs.google.com";
89 static const char kSitesDotGoogleDotCom[] = "sites.google.com";
90 static const char kPicasawebDotGoogleDotCom[] = "picasaweb.google.com";
91 static const char kCodeDotGoogleDotCom[] = "code.google.com";
92 static const char kGroupsDotGoogleDotCom[] = "groups.google.com";
93 static const char kMapsDotGoogleDotCom[] = "maps.google.com";
94 static const char kWWWDotYoutubeDotCom[] = "www.youtube.com";
95 static const char kDotGoogleUserContentDotCom[] = ".googleusercontent.com";
96 static const char kGoogleReaderPathPrefix[] = "/reader/";
97 static const char kGoogleSupportPathPrefix[] = "/support/";
98 static const char kGoogleIntlPathPrefix[] = "/intl/";
99 static const char kDotJS[] = ".js";
100 static const char kDotCSS[] = ".css";
101 static const char kDotSWF[] = ".swf";
102 static const char kDotHTML[] = ".html";
103
104 // Constants for mixed-content blocking.
105 static const char kGoogleDotCom[] = "google.com";
106
IsHostInDomain(const std::string & host,const std::string & domain)107 static bool IsHostInDomain(const std::string& host, const std::string& domain) {
108 return (EndsWith(host, domain, false) &&
109 (host.length() == domain.length() ||
110 (host.length() > domain.length() &&
111 host[host.length() - domain.length() - 1] == '.')));
112 }
113
GetOriginOrURL(const WebFrame * frame)114 GURL GetOriginOrURL(const WebFrame* frame) {
115 WebString top_origin = frame->top()->document().securityOrigin().toString();
116 // The the |top_origin| is unique ("null") e.g., for file:// URLs. Use the
117 // document URL as the primary URL in those cases.
118 if (top_origin == "null")
119 return frame->top()->document().url();
120 return GURL(top_origin);
121 }
122
GetContentSettingFromRules(const ContentSettingsForOneType & rules,const WebFrame * frame,const GURL & secondary_url)123 ContentSetting GetContentSettingFromRules(
124 const ContentSettingsForOneType& rules,
125 const WebFrame* frame,
126 const GURL& secondary_url) {
127 ContentSettingsForOneType::const_iterator it;
128 // If there is only one rule, it's the default rule and we don't need to match
129 // the patterns.
130 if (rules.size() == 1) {
131 DCHECK(rules[0].primary_pattern == ContentSettingsPattern::Wildcard());
132 DCHECK(rules[0].secondary_pattern == ContentSettingsPattern::Wildcard());
133 return rules[0].setting;
134 }
135 const GURL& primary_url = GetOriginOrURL(frame);
136 for (it = rules.begin(); it != rules.end(); ++it) {
137 if (it->primary_pattern.Matches(primary_url) &&
138 it->secondary_pattern.Matches(secondary_url)) {
139 return it->setting;
140 }
141 }
142 NOTREACHED();
143 return CONTENT_SETTING_DEFAULT;
144 }
145
146 } // namespace
147
ContentSettingsObserver(content::RenderFrame * render_frame,extensions::Dispatcher * extension_dispatcher)148 ContentSettingsObserver::ContentSettingsObserver(
149 content::RenderFrame* render_frame,
150 extensions::Dispatcher* extension_dispatcher)
151 : content::RenderFrameObserver(render_frame),
152 content::RenderFrameObserverTracker<ContentSettingsObserver>(
153 render_frame),
154 extension_dispatcher_(extension_dispatcher),
155 allow_displaying_insecure_content_(false),
156 allow_running_insecure_content_(false),
157 content_setting_rules_(NULL),
158 is_interstitial_page_(false),
159 npapi_plugins_blocked_(false),
160 current_request_id_(0) {
161 ClearBlockedContentSettings();
162 render_frame->GetWebFrame()->setPermissionClient(this);
163
164 if (render_frame->GetRenderView()->GetMainRenderFrame() != render_frame) {
165 // Copy all the settings from the main render frame to avoid race conditions
166 // when initializing this data. See http://crbug.com/333308.
167 ContentSettingsObserver* parent = ContentSettingsObserver::Get(
168 render_frame->GetRenderView()->GetMainRenderFrame());
169 allow_displaying_insecure_content_ =
170 parent->allow_displaying_insecure_content_;
171 allow_running_insecure_content_ = parent->allow_running_insecure_content_;
172 temporarily_allowed_plugins_ = parent->temporarily_allowed_plugins_;
173 is_interstitial_page_ = parent->is_interstitial_page_;
174 npapi_plugins_blocked_ = parent->npapi_plugins_blocked_;
175 }
176 }
177
~ContentSettingsObserver()178 ContentSettingsObserver::~ContentSettingsObserver() {
179 }
180
SetContentSettingRules(const RendererContentSettingRules * content_setting_rules)181 void ContentSettingsObserver::SetContentSettingRules(
182 const RendererContentSettingRules* content_setting_rules) {
183 content_setting_rules_ = content_setting_rules;
184 }
185
IsPluginTemporarilyAllowed(const std::string & identifier)186 bool ContentSettingsObserver::IsPluginTemporarilyAllowed(
187 const std::string& identifier) {
188 // If the empty string is in here, it means all plug-ins are allowed.
189 // TODO(bauerb): Remove this once we only pass in explicit identifiers.
190 return (temporarily_allowed_plugins_.find(identifier) !=
191 temporarily_allowed_plugins_.end()) ||
192 (temporarily_allowed_plugins_.find(std::string()) !=
193 temporarily_allowed_plugins_.end());
194 }
195
DidBlockContentType(ContentSettingsType settings_type)196 void ContentSettingsObserver::DidBlockContentType(
197 ContentSettingsType settings_type) {
198 if (!content_blocked_[settings_type]) {
199 content_blocked_[settings_type] = true;
200 Send(new ChromeViewHostMsg_ContentBlocked(routing_id(), settings_type));
201 }
202 }
203
OnMessageReceived(const IPC::Message & message)204 bool ContentSettingsObserver::OnMessageReceived(const IPC::Message& message) {
205 bool handled = true;
206 IPC_BEGIN_MESSAGE_MAP(ContentSettingsObserver, message)
207 IPC_MESSAGE_HANDLER(ChromeViewMsg_SetAsInterstitial, OnSetAsInterstitial)
208 IPC_MESSAGE_HANDLER(ChromeViewMsg_NPAPINotSupported, OnNPAPINotSupported)
209 IPC_MESSAGE_HANDLER(ChromeViewMsg_SetAllowDisplayingInsecureContent,
210 OnSetAllowDisplayingInsecureContent)
211 IPC_MESSAGE_HANDLER(ChromeViewMsg_SetAllowRunningInsecureContent,
212 OnSetAllowRunningInsecureContent)
213 IPC_MESSAGE_HANDLER(ChromeViewMsg_ReloadFrame, OnReloadFrame);
214 IPC_MESSAGE_HANDLER(ChromeViewMsg_RequestFileSystemAccessAsyncResponse,
215 OnRequestFileSystemAccessAsyncResponse)
216 IPC_MESSAGE_UNHANDLED(handled = false)
217 IPC_END_MESSAGE_MAP()
218 if (handled)
219 return true;
220
221 // Don't swallow LoadBlockedPlugins messages, as they're sent to every
222 // blocked plugin.
223 IPC_BEGIN_MESSAGE_MAP(ContentSettingsObserver, message)
224 IPC_MESSAGE_HANDLER(ChromeViewMsg_LoadBlockedPlugins, OnLoadBlockedPlugins)
225 IPC_END_MESSAGE_MAP()
226
227 return false;
228 }
229
DidCommitProvisionalLoad(bool is_new_navigation)230 void ContentSettingsObserver::DidCommitProvisionalLoad(bool is_new_navigation) {
231 WebFrame* frame = render_frame()->GetWebFrame();
232 if (frame->parent())
233 return; // Not a top-level navigation.
234
235 DocumentState* document_state = DocumentState::FromDataSource(
236 frame->dataSource());
237 NavigationState* navigation_state = document_state->navigation_state();
238 if (!navigation_state->was_within_same_page()) {
239 // Clear "block" flags for the new page. This needs to happen before any of
240 // |allowScript()|, |allowScriptFromSource()|, |allowImage()|, or
241 // |allowPlugins()| is called for the new page so that these functions can
242 // correctly detect that a piece of content flipped from "not blocked" to
243 // "blocked".
244 ClearBlockedContentSettings();
245 temporarily_allowed_plugins_.clear();
246 }
247
248 GURL url = frame->document().url();
249 // If we start failing this DCHECK, please makes sure we don't regress
250 // this bug: http://code.google.com/p/chromium/issues/detail?id=79304
251 DCHECK(frame->document().securityOrigin().toString() == "null" ||
252 !url.SchemeIs(url::kDataScheme));
253 }
254
allowDatabase(const WebString & name,const WebString & display_name,unsigned long estimated_size)255 bool ContentSettingsObserver::allowDatabase(const WebString& name,
256 const WebString& display_name,
257 unsigned long estimated_size) {
258 WebFrame* frame = render_frame()->GetWebFrame();
259 if (frame->document().securityOrigin().isUnique() ||
260 frame->top()->document().securityOrigin().isUnique())
261 return false;
262
263 bool result = false;
264 Send(new ChromeViewHostMsg_AllowDatabase(
265 routing_id(), GURL(frame->document().securityOrigin().toString()),
266 GURL(frame->top()->document().securityOrigin().toString()),
267 name, display_name, &result));
268 return result;
269 }
270
requestFileSystemAccessAsync(const WebPermissionCallbacks & callbacks)271 void ContentSettingsObserver::requestFileSystemAccessAsync(
272 const WebPermissionCallbacks& callbacks) {
273 WebFrame* frame = render_frame()->GetWebFrame();
274 if (frame->document().securityOrigin().isUnique() ||
275 frame->top()->document().securityOrigin().isUnique()) {
276 WebPermissionCallbacks permissionCallbacks(callbacks);
277 permissionCallbacks.doDeny();
278 return;
279 }
280 ++current_request_id_;
281 std::pair<PermissionRequestMap::iterator, bool> insert_result =
282 permission_requests_.insert(
283 std::make_pair(current_request_id_, callbacks));
284
285 // Verify there are no duplicate insertions.
286 DCHECK(insert_result.second);
287
288 Send(new ChromeViewHostMsg_RequestFileSystemAccessAsync(
289 routing_id(),
290 current_request_id_,
291 GURL(frame->document().securityOrigin().toString()),
292 GURL(frame->top()->document().securityOrigin().toString())));
293 }
294
allowImage(bool enabled_per_settings,const WebURL & image_url)295 bool ContentSettingsObserver::allowImage(bool enabled_per_settings,
296 const WebURL& image_url) {
297 bool allow = enabled_per_settings;
298 if (enabled_per_settings) {
299 if (is_interstitial_page_)
300 return true;
301
302 WebFrame* frame = render_frame()->GetWebFrame();
303 if (IsWhitelistedForContentSettings(frame))
304 return true;
305
306 if (content_setting_rules_) {
307 GURL secondary_url(image_url);
308 allow = GetContentSettingFromRules(
309 content_setting_rules_->image_rules,
310 frame, secondary_url) != CONTENT_SETTING_BLOCK;
311 }
312 }
313 if (!allow)
314 DidBlockContentType(CONTENT_SETTINGS_TYPE_IMAGES);
315 return allow;
316 }
317
allowIndexedDB(const WebString & name,const WebSecurityOrigin & origin)318 bool ContentSettingsObserver::allowIndexedDB(const WebString& name,
319 const WebSecurityOrigin& origin) {
320 WebFrame* frame = render_frame()->GetWebFrame();
321 if (frame->document().securityOrigin().isUnique() ||
322 frame->top()->document().securityOrigin().isUnique())
323 return false;
324
325 bool result = false;
326 Send(new ChromeViewHostMsg_AllowIndexedDB(
327 routing_id(), GURL(frame->document().securityOrigin().toString()),
328 GURL(frame->top()->document().securityOrigin().toString()),
329 name, &result));
330 return result;
331 }
332
allowPlugins(bool enabled_per_settings)333 bool ContentSettingsObserver::allowPlugins(bool enabled_per_settings) {
334 return enabled_per_settings;
335 }
336
allowScript(bool enabled_per_settings)337 bool ContentSettingsObserver::allowScript(bool enabled_per_settings) {
338 if (!enabled_per_settings)
339 return false;
340 if (is_interstitial_page_)
341 return true;
342
343 WebFrame* frame = render_frame()->GetWebFrame();
344 std::map<WebFrame*, bool>::const_iterator it =
345 cached_script_permissions_.find(frame);
346 if (it != cached_script_permissions_.end())
347 return it->second;
348
349 // Evaluate the content setting rules before
350 // |IsWhitelistedForContentSettings|; if there is only the default rule
351 // allowing all scripts, it's quicker this way.
352 bool allow = true;
353 if (content_setting_rules_) {
354 ContentSetting setting = GetContentSettingFromRules(
355 content_setting_rules_->script_rules,
356 frame,
357 GURL(frame->document().securityOrigin().toString()));
358 allow = setting != CONTENT_SETTING_BLOCK;
359 }
360 allow = allow || IsWhitelistedForContentSettings(frame);
361
362 cached_script_permissions_[frame] = allow;
363 return allow;
364 }
365
allowScriptFromSource(bool enabled_per_settings,const blink::WebURL & script_url)366 bool ContentSettingsObserver::allowScriptFromSource(
367 bool enabled_per_settings,
368 const blink::WebURL& script_url) {
369 if (!enabled_per_settings)
370 return false;
371 if (is_interstitial_page_)
372 return true;
373
374 bool allow = true;
375 WebFrame* frame = render_frame()->GetWebFrame();
376 if (content_setting_rules_) {
377 ContentSetting setting = GetContentSettingFromRules(
378 content_setting_rules_->script_rules,
379 frame,
380 GURL(script_url));
381 allow = setting != CONTENT_SETTING_BLOCK;
382 }
383 return allow || IsWhitelistedForContentSettings(frame);
384 }
385
allowStorage(bool local)386 bool ContentSettingsObserver::allowStorage(bool local) {
387 WebFrame* frame = render_frame()->GetWebFrame();
388 if (frame->document().securityOrigin().isUnique() ||
389 frame->top()->document().securityOrigin().isUnique())
390 return false;
391 bool result = false;
392
393 StoragePermissionsKey key(
394 GURL(frame->document().securityOrigin().toString()), local);
395 std::map<StoragePermissionsKey, bool>::const_iterator permissions =
396 cached_storage_permissions_.find(key);
397 if (permissions != cached_storage_permissions_.end())
398 return permissions->second;
399
400 Send(new ChromeViewHostMsg_AllowDOMStorage(
401 routing_id(), GURL(frame->document().securityOrigin().toString()),
402 GURL(frame->top()->document().securityOrigin().toString()),
403 local, &result));
404 cached_storage_permissions_[key] = result;
405 return result;
406 }
407
allowReadFromClipboard(bool default_value)408 bool ContentSettingsObserver::allowReadFromClipboard(bool default_value) {
409 bool allowed = false;
410 #if defined(ENABLE_EXTENSIONS)
411 WebFrame* frame = render_frame()->GetWebFrame();
412 // TODO(dcheng): Should we consider a toURL() method on WebSecurityOrigin?
413 Send(new ChromeViewHostMsg_CanTriggerClipboardRead(
414 GURL(frame->document().securityOrigin().toString()), &allowed));
415 #endif
416 return allowed;
417 }
418
allowWriteToClipboard(bool default_value)419 bool ContentSettingsObserver::allowWriteToClipboard(bool default_value) {
420 bool allowed = false;
421 #if defined(ENABLE_EXTENSIONS)
422 WebFrame* frame = render_frame()->GetWebFrame();
423 Send(new ChromeViewHostMsg_CanTriggerClipboardWrite(
424 GURL(frame->document().securityOrigin().toString()), &allowed));
425 #endif
426 return allowed;
427 }
428
allowWebComponents(bool default_value)429 bool ContentSettingsObserver::allowWebComponents(bool default_value) {
430 if (default_value)
431 return true;
432
433 WebFrame* frame = render_frame()->GetWebFrame();
434 WebSecurityOrigin origin = frame->document().securityOrigin();
435 if (EqualsASCII(origin.protocol(), content::kChromeUIScheme))
436 return true;
437
438 if (const extensions::Extension* extension = GetExtension(origin)) {
439 if (extension->permissions_data()->HasAPIPermission(
440 APIPermission::kExperimental))
441 return true;
442 }
443
444 return false;
445 }
446
allowMutationEvents(bool default_value)447 bool ContentSettingsObserver::allowMutationEvents(bool default_value) {
448 WebFrame* frame = render_frame()->GetWebFrame();
449 WebSecurityOrigin origin = frame->document().securityOrigin();
450 const extensions::Extension* extension = GetExtension(origin);
451 if (extension && extension->is_platform_app())
452 return false;
453 return default_value;
454 }
455
allowPushState()456 bool ContentSettingsObserver::allowPushState() {
457 WebFrame* frame = render_frame()->GetWebFrame();
458 WebSecurityOrigin origin = frame->document().securityOrigin();
459 const extensions::Extension* extension = GetExtension(origin);
460 return !extension || !extension->is_platform_app();
461 }
462
SendInsecureContentSignal(int signal)463 static void SendInsecureContentSignal(int signal) {
464 UMA_HISTOGRAM_ENUMERATION("SSL.InsecureContent", signal,
465 INSECURE_CONTENT_NUM_EVENTS);
466 }
467
allowDisplayingInsecureContent(bool allowed_per_settings,const blink::WebSecurityOrigin & origin,const blink::WebURL & resource_url)468 bool ContentSettingsObserver::allowDisplayingInsecureContent(
469 bool allowed_per_settings,
470 const blink::WebSecurityOrigin& origin,
471 const blink::WebURL& resource_url) {
472 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY);
473
474 std::string origin_host(origin.host().utf8());
475 WebFrame* frame = render_frame()->GetWebFrame();
476 GURL frame_gurl(frame->document().url());
477 if (IsHostInDomain(origin_host, kGoogleDotCom)) {
478 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_GOOGLE);
479 if (StartsWithASCII(frame_gurl.path(), kGoogleSupportPathPrefix, false)) {
480 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_GOOGLE_SUPPORT);
481 } else if (StartsWithASCII(frame_gurl.path(),
482 kGoogleIntlPathPrefix,
483 false)) {
484 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_GOOGLE_INTL);
485 }
486 }
487
488 if (origin_host == kWWWDotGoogleDotCom) {
489 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_WWW_GOOGLE);
490 if (StartsWithASCII(frame_gurl.path(), kGoogleReaderPathPrefix, false))
491 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_GOOGLE_READER);
492 } else if (origin_host == kMailDotGoogleDotCom) {
493 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_MAIL_GOOGLE);
494 } else if (origin_host == kPlusDotGoogleDotCom) {
495 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_PLUS_GOOGLE);
496 } else if (origin_host == kDocsDotGoogleDotCom) {
497 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_DOCS_GOOGLE);
498 } else if (origin_host == kSitesDotGoogleDotCom) {
499 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_SITES_GOOGLE);
500 } else if (origin_host == kPicasawebDotGoogleDotCom) {
501 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_PICASAWEB_GOOGLE);
502 } else if (origin_host == kCodeDotGoogleDotCom) {
503 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_CODE_GOOGLE);
504 } else if (origin_host == kGroupsDotGoogleDotCom) {
505 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_GROUPS_GOOGLE);
506 } else if (origin_host == kMapsDotGoogleDotCom) {
507 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_MAPS_GOOGLE);
508 } else if (origin_host == kWWWDotYoutubeDotCom) {
509 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_YOUTUBE);
510 }
511
512 GURL resource_gurl(resource_url);
513 if (EndsWith(resource_gurl.path(), kDotHTML, false))
514 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HTML);
515
516 if (allowed_per_settings || allow_displaying_insecure_content_)
517 return true;
518
519 Send(new ChromeViewHostMsg_DidBlockDisplayingInsecureContent(routing_id()));
520
521 return false;
522 }
523
allowRunningInsecureContent(bool allowed_per_settings,const blink::WebSecurityOrigin & origin,const blink::WebURL & resource_url)524 bool ContentSettingsObserver::allowRunningInsecureContent(
525 bool allowed_per_settings,
526 const blink::WebSecurityOrigin& origin,
527 const blink::WebURL& resource_url) {
528 std::string origin_host(origin.host().utf8());
529 WebFrame* frame = render_frame()->GetWebFrame();
530 GURL frame_gurl(frame->document().url());
531 DCHECK_EQ(frame_gurl.host(), origin_host);
532
533 bool is_google = IsHostInDomain(origin_host, kGoogleDotCom);
534 if (is_google) {
535 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_GOOGLE);
536 if (StartsWithASCII(frame_gurl.path(), kGoogleSupportPathPrefix, false)) {
537 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_GOOGLE_SUPPORT);
538 } else if (StartsWithASCII(frame_gurl.path(),
539 kGoogleIntlPathPrefix,
540 false)) {
541 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_GOOGLE_INTL);
542 }
543 }
544
545 if (origin_host == kWWWDotGoogleDotCom) {
546 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_WWW_GOOGLE);
547 if (StartsWithASCII(frame_gurl.path(), kGoogleReaderPathPrefix, false))
548 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_GOOGLE_READER);
549 } else if (origin_host == kMailDotGoogleDotCom) {
550 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_MAIL_GOOGLE);
551 } else if (origin_host == kPlusDotGoogleDotCom) {
552 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_PLUS_GOOGLE);
553 } else if (origin_host == kDocsDotGoogleDotCom) {
554 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_DOCS_GOOGLE);
555 } else if (origin_host == kSitesDotGoogleDotCom) {
556 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_SITES_GOOGLE);
557 } else if (origin_host == kPicasawebDotGoogleDotCom) {
558 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_PICASAWEB_GOOGLE);
559 } else if (origin_host == kCodeDotGoogleDotCom) {
560 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_CODE_GOOGLE);
561 } else if (origin_host == kGroupsDotGoogleDotCom) {
562 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_GROUPS_GOOGLE);
563 } else if (origin_host == kMapsDotGoogleDotCom) {
564 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_MAPS_GOOGLE);
565 } else if (origin_host == kWWWDotYoutubeDotCom) {
566 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_YOUTUBE);
567 } else if (EndsWith(origin_host, kDotGoogleUserContentDotCom, false)) {
568 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_GOOGLEUSERCONTENT);
569 }
570
571 GURL resource_gurl(resource_url);
572 if (resource_gurl.host() == kWWWDotYoutubeDotCom)
573 SendInsecureContentSignal(INSECURE_CONTENT_RUN_TARGET_YOUTUBE);
574
575 if (EndsWith(resource_gurl.path(), kDotJS, false))
576 SendInsecureContentSignal(INSECURE_CONTENT_RUN_JS);
577 else if (EndsWith(resource_gurl.path(), kDotCSS, false))
578 SendInsecureContentSignal(INSECURE_CONTENT_RUN_CSS);
579 else if (EndsWith(resource_gurl.path(), kDotSWF, false))
580 SendInsecureContentSignal(INSECURE_CONTENT_RUN_SWF);
581
582 if (!allow_running_insecure_content_ && !allowed_per_settings) {
583 DidBlockContentType(CONTENT_SETTINGS_TYPE_MIXEDSCRIPT);
584 return false;
585 }
586
587 return true;
588 }
589
didNotAllowPlugins()590 void ContentSettingsObserver::didNotAllowPlugins() {
591 DidBlockContentType(CONTENT_SETTINGS_TYPE_PLUGINS);
592 }
593
didNotAllowScript()594 void ContentSettingsObserver::didNotAllowScript() {
595 DidBlockContentType(CONTENT_SETTINGS_TYPE_JAVASCRIPT);
596 }
597
AreNPAPIPluginsBlocked() const598 bool ContentSettingsObserver::AreNPAPIPluginsBlocked() const {
599 return npapi_plugins_blocked_;
600 }
601
OnLoadBlockedPlugins(const std::string & identifier)602 void ContentSettingsObserver::OnLoadBlockedPlugins(
603 const std::string& identifier) {
604 temporarily_allowed_plugins_.insert(identifier);
605 }
606
OnSetAsInterstitial()607 void ContentSettingsObserver::OnSetAsInterstitial() {
608 is_interstitial_page_ = true;
609 }
610
OnNPAPINotSupported()611 void ContentSettingsObserver::OnNPAPINotSupported() {
612 npapi_plugins_blocked_ = true;
613 }
614
OnSetAllowDisplayingInsecureContent(bool allow)615 void ContentSettingsObserver::OnSetAllowDisplayingInsecureContent(bool allow) {
616 allow_displaying_insecure_content_ = allow;
617 }
618
OnSetAllowRunningInsecureContent(bool allow)619 void ContentSettingsObserver::OnSetAllowRunningInsecureContent(bool allow) {
620 allow_running_insecure_content_ = allow;
621 OnSetAllowDisplayingInsecureContent(allow);
622 }
623
OnReloadFrame()624 void ContentSettingsObserver::OnReloadFrame() {
625 DCHECK(!render_frame()->GetWebFrame()->parent()) <<
626 "Should only be called on the main frame";
627 render_frame()->GetWebFrame()->reload();
628 }
629
OnRequestFileSystemAccessAsyncResponse(int request_id,bool allowed)630 void ContentSettingsObserver::OnRequestFileSystemAccessAsyncResponse(
631 int request_id,
632 bool allowed) {
633 PermissionRequestMap::iterator it = permission_requests_.find(request_id);
634 if (it == permission_requests_.end())
635 return;
636
637 WebPermissionCallbacks callbacks = it->second;
638 permission_requests_.erase(it);
639
640 if (allowed) {
641 callbacks.doAllow();
642 return;
643 }
644 callbacks.doDeny();
645 }
646
ClearBlockedContentSettings()647 void ContentSettingsObserver::ClearBlockedContentSettings() {
648 for (size_t i = 0; i < arraysize(content_blocked_); ++i)
649 content_blocked_[i] = false;
650 cached_storage_permissions_.clear();
651 cached_script_permissions_.clear();
652 }
653
GetExtension(const WebSecurityOrigin & origin) const654 const extensions::Extension* ContentSettingsObserver::GetExtension(
655 const WebSecurityOrigin& origin) const {
656 if (!EqualsASCII(origin.protocol(), extensions::kExtensionScheme))
657 return NULL;
658
659 const std::string extension_id = origin.host().utf8().data();
660 if (!extension_dispatcher_->IsExtensionActive(extension_id))
661 return NULL;
662
663 return extension_dispatcher_->extensions()->GetByID(extension_id);
664 }
665
IsWhitelistedForContentSettings(WebFrame * frame)666 bool ContentSettingsObserver::IsWhitelistedForContentSettings(WebFrame* frame) {
667 // Whitelist Instant processes.
668 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kInstantProcess))
669 return true;
670
671 // Whitelist ftp directory listings, as they require JavaScript to function
672 // properly.
673 webkit_glue::WebURLResponseExtraDataImpl* extra_data =
674 static_cast<webkit_glue::WebURLResponseExtraDataImpl*>(
675 frame->dataSource()->response().extraData());
676 if (extra_data && extra_data->is_ftp_directory_listing())
677 return true;
678 return IsWhitelistedForContentSettings(frame->document().securityOrigin(),
679 frame->document().url());
680 }
681
IsWhitelistedForContentSettings(const WebSecurityOrigin & origin,const GURL & document_url)682 bool ContentSettingsObserver::IsWhitelistedForContentSettings(
683 const WebSecurityOrigin& origin,
684 const GURL& document_url) {
685 if (document_url == GURL(content::kUnreachableWebDataURL))
686 return true;
687
688 if (origin.isUnique())
689 return false; // Uninitialized document?
690
691 if (EqualsASCII(origin.protocol(), content::kChromeUIScheme))
692 return true; // Browser UI elements should still work.
693
694 if (EqualsASCII(origin.protocol(), content::kChromeDevToolsScheme))
695 return true; // DevTools UI elements should still work.
696
697 if (EqualsASCII(origin.protocol(), extensions::kExtensionScheme))
698 return true;
699
700 // TODO(creis, fsamuel): Remove this once the concept of swapped out
701 // RenderFrames goes away.
702 if (document_url == GURL(content::kSwappedOutURL))
703 return true;
704
705 // If the scheme is file:, an empty file name indicates a directory listing,
706 // which requires JavaScript to function properly.
707 if (EqualsASCII(origin.protocol(), url::kFileScheme)) {
708 return document_url.SchemeIs(url::kFileScheme) &&
709 document_url.ExtractFileName().empty();
710 }
711
712 return false;
713 }
714