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