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