• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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