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