blob: 18b0caf2499300904b66156471f369435559c706 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/renderer/chrome_content_settings_agent_delegate.h"
#include "build/chromeos_buildflags.h"
#include "pdf/buildflags.h"
// TODO(b/197163596): Remove File Manager constants
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "ash/webui/file_manager/url_constants.h"
#endif
#include "base/containers/contains.h"
#include "content/public/common/url_constants.h"
#include "content/public/renderer/render_frame.h"
#include "third_party/blink/public/platform/web_security_origin.h"
#include "third_party/blink/public/web/web_local_frame.h"
#if BUILDFLAG(ENABLE_EXTENSIONS)
#include "extensions/common/constants.h"
#include "extensions/common/extension.h"
#include "extensions/common/mojom/context_type.mojom.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
#if BUILDFLAG(ENABLE_PDF)
#include "components/pdf/common/pdf_util.h"
#include "third_party/blink/public/web/web_frame.h"
#include "url/origin.h"
#endif
ChromeContentSettingsAgentDelegate::ChromeContentSettingsAgentDelegate(
content::RenderFrame* render_frame)
: RenderFrameObserver(render_frame),
RenderFrameObserverTracker<ChromeContentSettingsAgentDelegate>(
render_frame),
render_frame_(render_frame) {
content::RenderFrame* main_frame = render_frame->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) {
auto* parent = ChromeContentSettingsAgentDelegate::Get(main_frame);
temporarily_allowed_plugins_ = parent->temporarily_allowed_plugins_;
}
}
ChromeContentSettingsAgentDelegate::~ChromeContentSettingsAgentDelegate() =
default;
#if BUILDFLAG(ENABLE_EXTENSIONS)
void ChromeContentSettingsAgentDelegate::SetExtensionDispatcher(
extensions::Dispatcher* extension_dispatcher) {
DCHECK(!extension_dispatcher_)
<< "SetExtensionDispatcher() should only be called once.";
extension_dispatcher_ = extension_dispatcher;
}
#endif
bool ChromeContentSettingsAgentDelegate::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 ChromeContentSettingsAgentDelegate::AllowPluginTemporarily(
const std::string& identifier) {
temporarily_allowed_plugins_.insert(identifier);
}
bool ChromeContentSettingsAgentDelegate::IsFrameAllowlistedForStorageAccess(
blink::WebFrame* frame) const {
#if BUILDFLAG(ENABLE_PDF)
// Allow the Chrome PDF Viewer's extension frame to access storage. This is
// needed when a data: URL navigates to or embeds a PDF. Normally, data: URLs
// are opaque and shouldn't be able to access storage. However, the Chrome PDF
// viewer is an internal use case and does not need to adhere to the web spec.
// The origin should match the PDF extension's origin. A PDF extension frame
// should always have a parent (the PDF embedder frame).
if (IsPdfExtensionOrigin(url::Origin(frame->GetSecurityOrigin())) &&
frame->Parent()) {
return true;
}
#endif
return false;
}
bool ChromeContentSettingsAgentDelegate::IsSchemeAllowlisted(
const std::string& scheme) {
#if BUILDFLAG(ENABLE_EXTENSIONS)
return scheme == extensions::kExtensionScheme;
#else
return false;
#endif
}
bool ChromeContentSettingsAgentDelegate::AllowReadFromClipboard() {
#if BUILDFLAG(ENABLE_EXTENSIONS)
extensions::ScriptContext* current_context =
extension_dispatcher_->script_context_set().GetCurrent();
if (current_context &&
current_context->HasAPIPermission(
extensions::mojom::APIPermissionID::kClipboardRead)) {
return true;
}
if (IsAllowListedSystemWebApp()) {
return true;
}
#endif
return false;
}
bool ChromeContentSettingsAgentDelegate::AllowWriteToClipboard() {
#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::mojom::ContextType::kPrivilegedExtension &&
!current_context->IsForServiceWorker()) {
return true;
}
if (current_context->HasAPIPermission(
extensions::mojom::APIPermissionID::kClipboardWrite)) {
return true;
}
}
#endif
return false;
}
std::optional<bool> ChromeContentSettingsAgentDelegate::AllowMutationEvents() {
if (IsPlatformApp())
return false;
return std::nullopt;
}
void ChromeContentSettingsAgentDelegate::DidCommitProvisionalLoad(
ui::PageTransition transition) {
if (render_frame()->GetWebFrame()->Parent())
return;
temporarily_allowed_plugins_.clear();
}
void ChromeContentSettingsAgentDelegate::OnDestruct() {}
bool ChromeContentSettingsAgentDelegate::IsPlatformApp() {
#if BUILDFLAG(ENABLE_EXTENSIONS)
blink::WebLocalFrame* frame = render_frame_->GetWebFrame();
blink::WebSecurityOrigin origin = frame->GetDocument().GetSecurityOrigin();
const extensions::Extension* extension = GetExtension(origin);
return extension && extension->is_platform_app();
#else
return false;
#endif
}
bool ChromeContentSettingsAgentDelegate::IsAllowListedSystemWebApp() {
#if BUILDFLAG(IS_CHROMEOS_ASH)
blink::WebLocalFrame* frame = render_frame_->GetWebFrame();
blink::WebSecurityOrigin origin = frame->GetDocument().GetSecurityOrigin();
// TODO(crbug.com/1233395): Migrate Files SWA to Clipboard API and remove this
// allow-list.
if (origin.Protocol().Ascii() == ::content::kChromeUIScheme &&
origin.Host().Utf8() == ::ash::file_manager::kChromeUIFileManagerHost) {
return true;
}
#endif
return false;
}
#if BUILDFLAG(ENABLE_EXTENSIONS)
const extensions::Extension* ChromeContentSettingsAgentDelegate::GetExtension(
const blink::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