// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/renderer/content_settings_agent_impl.h"

#include <utility>
#include <vector>

#include "base/bind.h"
#include "base/feature_list.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_number_conversions.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/client_hints/client_hints.h"
#include "chrome/common/render_messages.h"
#include "chrome/common/ssl_insecure_content.h"
#include "components/content_settings/core/common/content_settings.h"
#include "components/content_settings/core/common/content_settings.mojom.h"
#include "components/content_settings/core/common/content_settings_pattern.h"
#include "components/content_settings/core/common/content_settings_utils.h"
#include "content/public/child/child_thread.h"
#include "content/public/common/client_hints.mojom.h"
#include "content/public/common/origin_util.h"
#include "content/public/common/previews_state.h"
#include "content/public/common/url_constants.h"
#include "content/public/renderer/document_state.h"
#include "content/public/renderer/render_frame.h"
#include "content/public/renderer/render_view.h"
#include "extensions/buildflags/buildflags.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
#include "third_party/blink/public/platform/url_conversion.h"
#include "third_party/blink/public/platform/web_client_hints_type.h"
#include "third_party/blink/public/platform/web_security_origin.h"
#include "third_party/blink/public/platform/web_url.h"
#include "third_party/blink/public/web/web_document.h"
#include "third_party/blink/public/web/web_local_frame.h"
#include "third_party/blink/public/web/web_local_frame_client.h"
#include "third_party/blink/public/web/web_view.h"
#include "url/gurl.h"
#include "url/origin.h"
#include "url/url_constants.h"

#if BUILDFLAG(ENABLE_EXTENSIONS)
#include "extensions/common/constants.h"
#include "extensions/common/extension.h"
#include "extensions/common/permissions/api_permission.h"
#include "extensions/common/permissions/permissions_data.h"
#include "extensions/renderer/dispatcher.h"
#include "extensions/renderer/renderer_extension_registry.h"
#endif

using blink::WebDocument;
using blink::WebFrame;
using blink::WebLocalFrame;
using blink::WebSecurityOrigin;
using blink::WebString;
using blink::WebURL;
using blink::WebView;
using content::DocumentState;

namespace {

GURL GetOriginOrURL(const WebFrame* frame) {
  url::Origin top_origin = url::Origin(frame->Top()->GetSecurityOrigin());
  // The |top_origin| is unique ("null") e.g., for file:// URLs. Use the
  // document URL as the primary URL in those cases.
  // TODO(alexmos): This is broken for --site-per-process, since top() can be a
  // WebRemoteFrame which does not have a document(), and the WebRemoteFrame's
  // URL is not replicated.  See https://crbug.com/628759.
  if (top_origin.opaque() && frame->Top()->IsWebLocalFrame())
    return frame->Top()->ToWebLocalFrame()->GetDocument().Url();
  return top_origin.GetURL();
}

// Allow passing both WebURL and GURL here, so that we can early return without
// allocating a new backing string if only the default rule matches.
template <typename URL>
ContentSetting GetContentSettingFromRules(
    const ContentSettingsForOneType& rules,
    const WebFrame* frame,
    const URL& secondary_url) {
  // If there is only one rule, it's the default rule and we don't need to match
  // the patterns.
  if (rules.size() == 1) {
    DCHECK(rules[0].primary_pattern == ContentSettingsPattern::Wildcard());
    DCHECK(rules[0].secondary_pattern == ContentSettingsPattern::Wildcard());
    return rules[0].GetContentSetting();
  }
  const GURL& primary_url = GetOriginOrURL(frame);
  const GURL& secondary_gurl = secondary_url;
  for (const auto& rule : rules) {
    if (rule.primary_pattern.Matches(primary_url) &&
        rule.secondary_pattern.Matches(secondary_gurl)) {
      return rule.GetContentSetting();
    }
  }
  NOTREACHED();
  return CONTENT_SETTING_DEFAULT;
}

bool IsScriptDisabledForPreview(content::RenderFrame* render_frame) {
  return render_frame->GetPreviewsState() & content::NOSCRIPT_ON;
}

bool IsFrameWithOpaqueOrigin(WebFrame* frame) {
  // Storage access is keyed off the top origin and the frame's origin.
  // It will be denied any opaque origins so have this method to return early
  // instead of making a Sync IPC call.
  return frame->GetSecurityOrigin().IsOpaque() ||
         frame->Top()->GetSecurityOrigin().IsOpaque();
}

}  // namespace

ContentSettingsAgentImpl::ContentSettingsAgentImpl(
    content::RenderFrame* render_frame,
    bool should_whitelist,
    service_manager::BinderRegistry* registry)
    : content::RenderFrameObserver(render_frame),
      content::RenderFrameObserverTracker<ContentSettingsAgentImpl>(
          render_frame),
      should_whitelist_(should_whitelist) {
  ClearBlockedContentSettings();
  render_frame->GetWebFrame()->SetContentSettingsClient(this);

  render_frame->GetAssociatedInterfaceRegistry()->AddInterface(
      base::Bind(&ContentSettingsAgentImpl::OnContentSettingsAgentRequest,
                 base::Unretained(this)));

  content::RenderFrame* main_frame =
      render_frame->GetRenderView()->GetMainRenderFrame();
  // TODO(nasko): The main frame is not guaranteed to be in the same process
  // with this frame with --site-per-process. This code needs to be updated
  // to handle this case. See https://crbug.com/496670.
  if (main_frame && main_frame != render_frame) {
    // Copy all the settings from the main render frame to avoid race conditions
    // when initializing this data. See https://crbug.com/333308.
    ContentSettingsAgentImpl* parent =
        ContentSettingsAgentImpl::Get(main_frame);
    allow_running_insecure_content_ = parent->allow_running_insecure_content_;
    temporarily_allowed_plugins_ = parent->temporarily_allowed_plugins_;
    is_interstitial_page_ = parent->is_interstitial_page_;
  }
}

ContentSettingsAgentImpl::~ContentSettingsAgentImpl() {}

chrome::mojom::ContentSettingsManager&
ContentSettingsAgentImpl::GetContentSettingsManager() {
  if (!content_settings_manager_)
    BindContentSettingsManager(&content_settings_manager_);
  return *content_settings_manager_;
}

#if BUILDFLAG(ENABLE_EXTENSIONS)
void ContentSettingsAgentImpl::SetExtensionDispatcher(
    extensions::Dispatcher* extension_dispatcher) {
  DCHECK(!extension_dispatcher_)
      << "SetExtensionDispatcher() should only be called once.";
  extension_dispatcher_ = extension_dispatcher;
}
#endif

void ContentSettingsAgentImpl::SetContentSettingRules(
    const RendererContentSettingRules* content_setting_rules) {
  content_setting_rules_ = content_setting_rules;
  UMA_HISTOGRAM_COUNTS_1M("ClientHints.CountRulesReceived",
                          content_setting_rules_->client_hints_rules.size());
}

const RendererContentSettingRules*
ContentSettingsAgentImpl::GetContentSettingRules() {
  return content_setting_rules_;
}

bool ContentSettingsAgentImpl::IsPluginTemporarilyAllowed(
    const std::string& identifier) {
  // If the empty string is in here, it means all plugins are allowed.
  // TODO(bauerb): Remove this once we only pass in explicit identifiers.
  return base::Contains(temporarily_allowed_plugins_, identifier) ||
         base::Contains(temporarily_allowed_plugins_, std::string());
}

void ContentSettingsAgentImpl::DidBlockContentType(
    ContentSettingsType settings_type) {
  bool newly_blocked = content_blocked_.insert(settings_type).second;
  if (newly_blocked)
    GetContentSettingsManager().OnContentBlocked(routing_id(), settings_type);
}

void ContentSettingsAgentImpl::BindContentSettingsManager(
    mojo::Remote<chrome::mojom::ContentSettingsManager>* manager) {
  DCHECK(!*manager);
  content::ChildThread::Get()->BindHostReceiver(
      manager->BindNewPipeAndPassReceiver());
}

bool ContentSettingsAgentImpl::OnMessageReceived(const IPC::Message& message) {
  // Don't swallow LoadBlockedPlugins messages, as they're sent to every
  // blocked plugin.
  IPC_BEGIN_MESSAGE_MAP(ContentSettingsAgentImpl, message)
    IPC_MESSAGE_HANDLER(ChromeViewMsg_LoadBlockedPlugins, OnLoadBlockedPlugins)
  IPC_END_MESSAGE_MAP()
  return false;
}

void ContentSettingsAgentImpl::DidCommitProvisionalLoad(
    bool is_same_document_navigation,
    ui::PageTransition transition) {
  blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
  if (frame->Parent())
    return;  // Not a top-level navigation.

  if (!is_same_document_navigation) {
    // Clear "block" flags for the new page. This needs to happen before any of
    // |allowScript()|, |allowScriptFromSource()|, |allowImage()|, or
    // |allowPlugins()| is called for the new page so that these functions can
    // correctly detect that a piece of content flipped from "not blocked" to
    // "blocked".
    ClearBlockedContentSettings();
    temporarily_allowed_plugins_.clear();

    // The BrowserInterfaceBroker is reset on navigation, so we will need to
    // re-acquire the ContentSettingsManager.
    content_settings_manager_.reset();
  }

  GURL url = frame->GetDocument().Url();
  // If we start failing this DCHECK, please makes sure we don't regress
  // this bug: http://code.google.com/p/chromium/issues/detail?id=79304
  DCHECK(frame->GetDocument().GetSecurityOrigin().ToString() == "null" ||
         !url.SchemeIs(url::kDataScheme));
}

void ContentSettingsAgentImpl::OnDestruct() {
  delete this;
}

void ContentSettingsAgentImpl::SetAllowRunningInsecureContent() {
  allow_running_insecure_content_ = true;

  // Reload if we are the main frame.
  blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
  if (!frame->Parent())
    frame->StartReload(blink::WebFrameLoadType::kReload);
}

void ContentSettingsAgentImpl::SetAsInterstitial() {
  is_interstitial_page_ = true;
}

void ContentSettingsAgentImpl::SetDisabledMixedContentUpgrades() {
  mixed_content_autoupgrades_disabled_ = true;
}

void ContentSettingsAgentImpl::OnContentSettingsAgentRequest(
    mojo::PendingAssociatedReceiver<chrome::mojom::ContentSettingsAgent>
        receiver) {
  receivers_.Add(this, std::move(receiver));
}

bool ContentSettingsAgentImpl::AllowDatabase() {
  return AllowStorageAccess(
      chrome::mojom::ContentSettingsManager::StorageType::DATABASE);
}

void ContentSettingsAgentImpl::RequestFileSystemAccessAsync(
    base::OnceCallback<void(bool)> callback) {
  WebLocalFrame* frame = render_frame()->GetWebFrame();
  if (IsFrameWithOpaqueOrigin(frame)) {
    std::move(callback).Run(false);
    return;
  }

  GetContentSettingsManager().AllowStorageAccess(
      routing_id(),
      chrome::mojom::ContentSettingsManager::StorageType::FILE_SYSTEM,
      frame->GetSecurityOrigin(),
      frame->GetDocument().SiteForCookies().RepresentativeUrl(),
      frame->GetDocument().TopFrameOrigin(), std::move(callback));
}

bool ContentSettingsAgentImpl::AllowImage(bool enabled_per_settings,
                                          const WebURL& image_url) {
  bool allow = enabled_per_settings;
  if (enabled_per_settings) {
    if (is_interstitial_page_)
      return true;

    if (IsWhitelistedForContentSettings())
      return true;

    if (content_setting_rules_) {
      allow = GetContentSettingFromRules(content_setting_rules_->image_rules,
                                         render_frame()->GetWebFrame(),
                                         image_url) != CONTENT_SETTING_BLOCK;
    }
  }
  if (!allow)
    DidBlockContentType(ContentSettingsType::IMAGES);
  return allow;
}

bool ContentSettingsAgentImpl::AllowIndexedDB() {
  return AllowStorageAccess(
      chrome::mojom::ContentSettingsManager::StorageType::INDEXED_DB);
}

bool ContentSettingsAgentImpl::AllowCacheStorage() {
  return AllowStorageAccess(
      chrome::mojom::ContentSettingsManager::StorageType::CACHE);
}

bool ContentSettingsAgentImpl::AllowWebLocks() {
  return AllowStorageAccess(
      chrome::mojom::ContentSettingsManager::StorageType::WEB_LOCKS);
}

bool ContentSettingsAgentImpl::AllowScript(bool enabled_per_settings) {
  if (!enabled_per_settings)
    return false;
  if (IsScriptDisabledForPreview(render_frame()))
    return false;
  if (is_interstitial_page_)
    return true;

  blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
  const auto it = cached_script_permissions_.find(frame);
  if (it != cached_script_permissions_.end())
    return it->second;

  // Evaluate the content setting rules before
  // IsWhitelistedForContentSettings(); if there is only the default rule
  // allowing all scripts, it's quicker this way.
  bool allow = true;
  if (content_setting_rules_) {
    ContentSetting setting = GetContentSettingFromRules(
        content_setting_rules_->script_rules, frame,
        url::Origin(frame->GetDocument().GetSecurityOrigin()).GetURL());
    allow = setting != CONTENT_SETTING_BLOCK;
  }
  allow = allow || IsWhitelistedForContentSettings();

  cached_script_permissions_[frame] = allow;
  return allow;
}

bool ContentSettingsAgentImpl::AllowScriptFromSource(
    bool enabled_per_settings,
    const blink::WebURL& script_url) {
  if (!enabled_per_settings)
    return false;
  if (IsScriptDisabledForPreview(render_frame()))
    return false;
  if (is_interstitial_page_)
    return true;

  bool allow = true;
  if (content_setting_rules_) {
    ContentSetting setting =
        GetContentSettingFromRules(content_setting_rules_->script_rules,
                                   render_frame()->GetWebFrame(), script_url);
    allow = setting != CONTENT_SETTING_BLOCK;
  }
  return allow || IsWhitelistedForContentSettings();
}

bool ContentSettingsAgentImpl::AllowStorage(bool local) {
  WebLocalFrame* frame = render_frame()->GetWebFrame();
  if (IsFrameWithOpaqueOrigin(frame))
    return false;

  StoragePermissionsKey key(
      url::Origin(frame->GetDocument().GetSecurityOrigin()).GetURL(), local);
  const auto permissions = cached_storage_permissions_.find(key);
  if (permissions != cached_storage_permissions_.end())
    return permissions->second;

  bool result = false;
  GetContentSettingsManager().AllowStorageAccess(
      routing_id(),
      local
          ? chrome::mojom::ContentSettingsManager::StorageType::LOCAL_STORAGE
          : chrome::mojom::ContentSettingsManager::StorageType::SESSION_STORAGE,
      frame->GetSecurityOrigin(),
      frame->GetDocument().SiteForCookies().RepresentativeUrl(),
      frame->GetDocument().TopFrameOrigin(), &result);
  cached_storage_permissions_[key] = result;
  return result;
}

bool ContentSettingsAgentImpl::AllowReadFromClipboard(bool default_value) {
  bool allowed = default_value;
#if BUILDFLAG(ENABLE_EXTENSIONS)
  extensions::ScriptContext* current_context =
      extension_dispatcher_->script_context_set().GetCurrent();
  if (current_context) {
    allowed |= current_context->HasAPIPermission(
        extensions::APIPermission::kClipboardRead);
  }
#endif
  return allowed;
}

bool ContentSettingsAgentImpl::AllowWriteToClipboard(bool default_value) {
  bool allowed = default_value;
#if BUILDFLAG(ENABLE_EXTENSIONS)
  // All blessed extension pages could historically write to the clipboard, so
  // preserve that for compatibility.
  extensions::ScriptContext* current_context =
      extension_dispatcher_->script_context_set().GetCurrent();
  if (current_context) {
    if (current_context->effective_context_type() ==
            extensions::Feature::BLESSED_EXTENSION_CONTEXT &&
        !current_context->IsForServiceWorker()) {
      allowed = true;
    } else {
      allowed |= current_context->HasAPIPermission(
          extensions::APIPermission::kClipboardWrite);
    }
  }
#endif
  return allowed;
}

bool ContentSettingsAgentImpl::AllowMutationEvents(bool default_value) {
  return IsPlatformApp() ? false : default_value;
}

bool ContentSettingsAgentImpl::AllowRunningInsecureContent(
    bool allowed_per_settings,
    const blink::WebURL& resource_url) {
  bool allow = allowed_per_settings;

  if (base::FeatureList::IsEnabled(features::kMixedContentSiteSetting)) {
    if (content_setting_rules_) {
      auto setting = GetContentSettingFromRules(
          content_setting_rules_->mixed_content_rules,
          render_frame()->GetWebFrame(), GURL());
      allow |= (setting == CONTENT_SETTING_ALLOW);
    }
  } else {
    allow |= allow_running_insecure_content_;
    if (!allow) {
      DidBlockContentType(ContentSettingsType::MIXEDSCRIPT);
    }
  }

  // Note: this implementation is a mirror of
  // Browser::ShouldAllowRunningInsecureContent.
  FilteredReportInsecureContentRan(GURL(resource_url));

  return allow;
}

bool ContentSettingsAgentImpl::AllowPopupsAndRedirects(bool default_value) {
  if (!content_setting_rules_)
    return default_value;
  blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
  return GetContentSettingFromRules(
             content_setting_rules_->popup_redirect_rules, frame,
             url::Origin(frame->GetDocument().GetSecurityOrigin()).GetURL()) ==
         CONTENT_SETTING_ALLOW;
}

void ContentSettingsAgentImpl::PassiveInsecureContentFound(
    const blink::WebURL& resource_url) {
  // Note: this implementation is a mirror of
  // Browser::PassiveInsecureContentFound.
  ReportInsecureContent(SslInsecureContentType::DISPLAY);
  FilteredReportInsecureContentDisplayed(GURL(resource_url));
}

void ContentSettingsAgentImpl::PersistClientHints(
    const blink::WebEnabledClientHints& enabled_client_hints,
    base::TimeDelta duration,
    const blink::WebURL& url) {
  if (duration <= base::TimeDelta())
    return;

  const GURL primary_url(url);
  const url::Origin primary_origin = url::Origin::Create(primary_url);
  if (!content::IsOriginSecure(primary_url))
    return;

  // TODO(tbansal): crbug.com/735518. Determine if the value should be
  // merged or overridden. Also, determine if the merger should happen on the
  // browser side or the renderer. If the value needs to be overridden,
  // this method should not return early if |update_count| is 0.
  std::vector<::blink::mojom::WebClientHintsType> client_hints;
  static constexpr size_t kWebClientHintsCount =
      static_cast<size_t>(blink::mojom::WebClientHintsType::kMaxValue) + 1;
  client_hints.reserve(kWebClientHintsCount);

  for (size_t i = 0; i < kWebClientHintsCount; ++i) {
    if (enabled_client_hints.IsEnabled(
            static_cast<blink::mojom::WebClientHintsType>(i))) {
      client_hints.push_back(static_cast<blink::mojom::WebClientHintsType>(i));
    }
  }
  size_t update_count = client_hints.size();
  if (update_count == 0)
    return;

  UMA_HISTOGRAM_CUSTOM_TIMES(
      "ClientHints.PersistDuration", duration, base::TimeDelta::FromSeconds(1),
      // TODO(crbug.com/949034): Rename and fix this histogram to have some
      // intended max value. We throw away the 32 most-significant bits of the
      // 64-bit time delta in milliseconds. Before it happened silently in
      // histogram.cc, now it is explicit here. The previous value of 365 days
      // effectively turns into roughly 17 days when getting cast to int.
      base::TimeDelta::FromMilliseconds(
          static_cast<int>(base::TimeDelta::FromDays(365).InMilliseconds())),
      100);

  UMA_HISTOGRAM_COUNTS_100("ClientHints.UpdateSize", update_count);

  // Notify the embedder.
  mojo::AssociatedRemote<client_hints::mojom::ClientHints> host_observer;
  render_frame()->GetRemoteAssociatedInterfaces()->GetInterface(&host_observer);
  host_observer->PersistClientHints(primary_origin, std::move(client_hints),
                                    duration);
}

void ContentSettingsAgentImpl::GetAllowedClientHintsFromSource(
    const blink::WebURL& url,
    blink::WebEnabledClientHints* client_hints) const {
  if (!content_setting_rules_)
    return;

  if (content_setting_rules_->client_hints_rules.empty())
    return;

  client_hints::GetAllowedClientHintsFromSource(
      url, content_setting_rules_->client_hints_rules, client_hints);
}

bool ContentSettingsAgentImpl::ShouldAutoupgradeMixedContent() {
  if (mixed_content_autoupgrades_disabled_)
    return false;

  if (content_setting_rules_) {
    auto setting =
        GetContentSettingFromRules(content_setting_rules_->mixed_content_rules,
                                   render_frame()->GetWebFrame(), GURL());
    return setting != CONTENT_SETTING_ALLOW;
  }
  return false;
}

void ContentSettingsAgentImpl::DidNotAllowPlugins() {
  DidBlockContentType(ContentSettingsType::PLUGINS);
}

void ContentSettingsAgentImpl::DidNotAllowScript() {
  DidBlockContentType(ContentSettingsType::JAVASCRIPT);
}

void ContentSettingsAgentImpl::OnLoadBlockedPlugins(
    const std::string& identifier) {
  temporarily_allowed_plugins_.insert(identifier);
}

void ContentSettingsAgentImpl::ClearBlockedContentSettings() {
  content_blocked_.clear();
  cached_storage_permissions_.clear();
  cached_script_permissions_.clear();
}

bool ContentSettingsAgentImpl::IsPlatformApp() {
#if BUILDFLAG(ENABLE_EXTENSIONS)
  blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
  WebSecurityOrigin origin = frame->GetDocument().GetSecurityOrigin();
  const extensions::Extension* extension = GetExtension(origin);
  return extension && extension->is_platform_app();
#else
  return false;
#endif
}

#if BUILDFLAG(ENABLE_EXTENSIONS)
const extensions::Extension* ContentSettingsAgentImpl::GetExtension(
    const WebSecurityOrigin& origin) const {
  if (origin.Protocol().Ascii() != extensions::kExtensionScheme)
    return nullptr;

  const std::string extension_id = origin.Host().Utf8().data();
  if (!extension_dispatcher_->IsExtensionActive(extension_id))
    return nullptr;

  return extensions::RendererExtensionRegistry::Get()->GetByID(extension_id);
}
#endif

// static
bool ContentSettingsAgentImpl::IsWhitelistedForContentSettings() const {
  if (should_whitelist_)
    return true;

  // Whitelist ftp directory listings, as they require JavaScript to function
  // properly.
  if (render_frame()->IsFTPDirectoryListing())
    return true;

  const WebDocument& document = render_frame()->GetWebFrame()->GetDocument();
  return IsWhitelistedForContentSettings(document.GetSecurityOrigin(),
                                         document.Url());
}

bool ContentSettingsAgentImpl::IsWhitelistedForContentSettings(
    const WebSecurityOrigin& origin,
    const WebURL& document_url) {
  if (document_url.GetString() == content::kUnreachableWebDataURL)
    return true;

  if (origin.IsOpaque())
    return false;  // Uninitialized document?

  blink::WebString protocol = origin.Protocol();

  if (protocol == content::kChromeUIScheme)
    return true;  // Browser UI elements should still work.

  if (protocol == content::kChromeDevToolsScheme)
    return true;  // DevTools UI elements should still work.

#if BUILDFLAG(ENABLE_EXTENSIONS)
  if (protocol == extensions::kExtensionScheme)
    return true;
#endif

  // If the scheme is file:, an empty file name indicates a directory listing,
  // which requires JavaScript to function properly.
  if (protocol == url::kFileScheme &&
      document_url.ProtocolIs(url::kFileScheme)) {
    return GURL(document_url).ExtractFileName().empty();
  }
  return false;
}

bool ContentSettingsAgentImpl::AllowStorageAccess(
    chrome::mojom::ContentSettingsManager::StorageType storage_type) {
  WebLocalFrame* frame = render_frame()->GetWebFrame();
  if (IsFrameWithOpaqueOrigin(frame))
    return false;

  bool result = false;
  GetContentSettingsManager().AllowStorageAccess(
      routing_id(), storage_type, frame->GetSecurityOrigin(),
      frame->GetDocument().SiteForCookies().RepresentativeUrl(),
      frame->GetDocument().TopFrameOrigin(), &result);
  return result;
}
