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
34using blink::WebDataSource;
35using blink::WebDocument;
36using blink::WebFrame;
37using blink::WebPermissionCallbacks;
38using blink::WebSecurityOrigin;
39using blink::WebString;
40using blink::WebURL;
41using blink::WebView;
42using content::DocumentState;
43using content::NavigationState;
44
45namespace {
46
47enum {
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.
88static const char kWWWDotGoogleDotCom[] = "www.google.com";
89static const char kMailDotGoogleDotCom[] = "mail.google.com";
90static const char kPlusDotGoogleDotCom[] = "plus.google.com";
91static const char kDocsDotGoogleDotCom[] = "docs.google.com";
92static const char kSitesDotGoogleDotCom[] = "sites.google.com";
93static const char kPicasawebDotGoogleDotCom[] = "picasaweb.google.com";
94static const char kCodeDotGoogleDotCom[] = "code.google.com";
95static const char kGroupsDotGoogleDotCom[] = "groups.google.com";
96static const char kMapsDotGoogleDotCom[] = "maps.google.com";
97static const char kWWWDotYoutubeDotCom[] = "www.youtube.com";
98static const char kDotGoogleUserContentDotCom[] = ".googleusercontent.com";
99static const char kGoogleReaderPathPrefix[] = "/reader/";
100static const char kGoogleSupportPathPrefix[] = "/support/";
101static const char kGoogleIntlPathPrefix[] = "/intl/";
102static const char kDotJS[] = ".js";
103static const char kDotCSS[] = ".css";
104static const char kDotSWF[] = ".swf";
105static const char kDotHTML[] = ".html";
106
107// Constants for mixed-content blocking.
108static const char kGoogleDotCom[] = "google.com";
109
110static 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
117GURL 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
126ContentSetting 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
151ContentSettingsObserver::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
183ContentSettingsObserver::~ContentSettingsObserver() {
184}
185
186void ContentSettingsObserver::SetContentSettingRules(
187    const RendererContentSettingRules* content_setting_rules) {
188  content_setting_rules_ = content_setting_rules;
189}
190
191bool 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
201void 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
209bool 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
235void 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
260bool 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
276void 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
300bool 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
323bool 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
338bool ContentSettingsObserver::allowPlugins(bool enabled_per_settings) {
339  return enabled_per_settings;
340}
341
342bool 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
371bool 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
390bool 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
412bool 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
428bool 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
451bool ContentSettingsObserver::allowMutationEvents(bool default_value) {
452  return IsPlatformApp() ? false : default_value;
453}
454
455bool ContentSettingsObserver::allowPushState() {
456  return !IsPlatformApp();
457}
458
459static void SendInsecureContentSignal(int signal) {
460  UMA_HISTOGRAM_ENUMERATION("SSL.InsecureContent", signal,
461                            INSECURE_CONTENT_NUM_EVENTS);
462}
463
464bool 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
520bool 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
586void ContentSettingsObserver::didNotAllowPlugins() {
587  DidBlockContentType(CONTENT_SETTINGS_TYPE_PLUGINS);
588}
589
590void ContentSettingsObserver::didNotAllowScript() {
591  DidBlockContentType(CONTENT_SETTINGS_TYPE_JAVASCRIPT);
592}
593
594bool ContentSettingsObserver::AreNPAPIPluginsBlocked() const {
595  return npapi_plugins_blocked_;
596}
597
598void ContentSettingsObserver::OnLoadBlockedPlugins(
599    const std::string& identifier) {
600  temporarily_allowed_plugins_.insert(identifier);
601}
602
603void ContentSettingsObserver::OnSetAsInterstitial() {
604  is_interstitial_page_ = true;
605}
606
607void ContentSettingsObserver::OnNPAPINotSupported() {
608  npapi_plugins_blocked_ = true;
609}
610
611void ContentSettingsObserver::OnSetAllowDisplayingInsecureContent(bool allow) {
612  allow_displaying_insecure_content_ = allow;
613}
614
615void ContentSettingsObserver::OnSetAllowRunningInsecureContent(bool allow) {
616  allow_running_insecure_content_ = allow;
617  OnSetAllowDisplayingInsecureContent(allow);
618}
619
620void ContentSettingsObserver::OnReloadFrame() {
621  DCHECK(!render_frame()->GetWebFrame()->parent()) <<
622      "Should only be called on the main frame";
623  render_frame()->GetWebFrame()->reload();
624}
625
626void 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
643void 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
650bool 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)
662const 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
675bool 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
691bool 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