Home | History | Annotate | Download | only in renderer
      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 
    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 
    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 
    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 
    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 
    183 ContentSettingsObserver::~ContentSettingsObserver() {
    184 }
    185 
    186 void ContentSettingsObserver::SetContentSettingRules(
    187     const RendererContentSettingRules* content_setting_rules) {
    188   content_setting_rules_ = content_setting_rules;
    189 }
    190 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    338 bool ContentSettingsObserver::allowPlugins(bool enabled_per_settings) {
    339   return enabled_per_settings;
    340 }
    341 
    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 
    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 
    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 
    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 
    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 
    451 bool ContentSettingsObserver::allowMutationEvents(bool default_value) {
    452   return IsPlatformApp() ? false : default_value;
    453 }
    454 
    455 bool ContentSettingsObserver::allowPushState() {
    456   return !IsPlatformApp();
    457 }
    458 
    459 static void SendInsecureContentSignal(int signal) {
    460   UMA_HISTOGRAM_ENUMERATION("SSL.InsecureContent", signal,
    461                             INSECURE_CONTENT_NUM_EVENTS);
    462 }
    463 
    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 
    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 
    586 void ContentSettingsObserver::didNotAllowPlugins() {
    587   DidBlockContentType(CONTENT_SETTINGS_TYPE_PLUGINS);
    588 }
    589 
    590 void ContentSettingsObserver::didNotAllowScript() {
    591   DidBlockContentType(CONTENT_SETTINGS_TYPE_JAVASCRIPT);
    592 }
    593 
    594 bool ContentSettingsObserver::AreNPAPIPluginsBlocked() const {
    595   return npapi_plugins_blocked_;
    596 }
    597 
    598 void ContentSettingsObserver::OnLoadBlockedPlugins(
    599     const std::string& identifier) {
    600   temporarily_allowed_plugins_.insert(identifier);
    601 }
    602 
    603 void ContentSettingsObserver::OnSetAsInterstitial() {
    604   is_interstitial_page_ = true;
    605 }
    606 
    607 void ContentSettingsObserver::OnNPAPINotSupported() {
    608   npapi_plugins_blocked_ = true;
    609 }
    610 
    611 void ContentSettingsObserver::OnSetAllowDisplayingInsecureContent(bool allow) {
    612   allow_displaying_insecure_content_ = allow;
    613 }
    614 
    615 void ContentSettingsObserver::OnSetAllowRunningInsecureContent(bool allow) {
    616   allow_running_insecure_content_ = allow;
    617   OnSetAllowDisplayingInsecureContent(allow);
    618 }
    619 
    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 
    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 
    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 
    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)
    662 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 
    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 
    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