summaryrefslogtreecommitdiffstats
path: root/src/core/net/plugin_response_interceptor_url_loader_throttle.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/net/plugin_response_interceptor_url_loader_throttle.cpp')
-rw-r--r--src/core/net/plugin_response_interceptor_url_loader_throttle.cpp201
1 files changed, 201 insertions, 0 deletions
diff --git a/src/core/net/plugin_response_interceptor_url_loader_throttle.cpp b/src/core/net/plugin_response_interceptor_url_loader_throttle.cpp
new file mode 100644
index 000000000..159fa28ca
--- /dev/null
+++ b/src/core/net/plugin_response_interceptor_url_loader_throttle.cpp
@@ -0,0 +1,201 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+// based on chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.cc
+// Copyright 2018 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 "plugin_response_interceptor_url_loader_throttle.h"
+
+#include "base/functional/bind.h"
+#include "base/uuid.h"
+#include "chrome/browser/extensions/api/streams_private/streams_private_api.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/download_manager.h"
+#include "content/public/browser/download_request_utils.h"
+#include "content/public/browser/download_utils.h"
+#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_attach_helper.h"
+#include "extensions/common/constants.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/manifest_handlers/mime_types_handler.h"
+#include "third_party/blink/public/mojom/loader/transferrable_url_loader.mojom.h"
+
+#include "extensions/extension_system_qt.h"
+#include "web_contents_delegate_qt.h"
+#include "web_engine_settings.h"
+
+#include <string>
+#include <tuple>
+
+namespace {
+void ClearAllButFrameAncestors(network::mojom::URLResponseHead *response_head)
+{
+ response_head->headers->RemoveHeader("Content-Security-Policy");
+ response_head->headers->RemoveHeader("Content-Security-Policy-Report-Only");
+
+ if (!response_head->parsed_headers)
+ return;
+
+ std::vector<network::mojom::ContentSecurityPolicyPtr> &csp =
+ response_head->parsed_headers->content_security_policy;
+ std::vector<network::mojom::ContentSecurityPolicyPtr> cleared;
+
+ for (auto &policy : csp) {
+ auto frame_ancestors = policy->directives.find(network::mojom::CSPDirectiveName::FrameAncestors);
+ if (frame_ancestors == policy->directives.end())
+ continue;
+
+ auto cleared_policy = network::mojom::ContentSecurityPolicy::New();
+ cleared_policy->self_origin = std::move(policy->self_origin);
+ cleared_policy->header = std::move(policy->header);
+ cleared_policy->header->header_value = "";
+ cleared_policy->directives[network::mojom::CSPDirectiveName::FrameAncestors] = std::move(frame_ancestors->second);
+
+ auto raw_frame_ancestors = policy->raw_directives.find(network::mojom::CSPDirectiveName::FrameAncestors);
+ DCHECK(raw_frame_ancestors != policy->raw_directives.end());
+
+ cleared_policy->header->header_value = "frame-ancestors " + raw_frame_ancestors->second;
+ response_head->headers->AddHeader(
+ cleared_policy->header->type == network::mojom::ContentSecurityPolicyType::kEnforce
+ ? "Content-Security-Policy"
+ : "Content-Security-Policy-Report-Only",
+ cleared_policy->header->header_value);
+ cleared_policy->raw_directives[network::mojom::CSPDirectiveName::FrameAncestors] =
+ std::move(raw_frame_ancestors->second);
+
+ cleared.push_back(std::move(cleared_policy));
+ }
+
+ csp.swap(cleared);
+}
+} // namespace
+
+
+namespace QtWebEngineCore {
+
+PluginResponseInterceptorURLLoaderThrottle::PluginResponseInterceptorURLLoaderThrottle(
+ network::mojom::RequestDestination request_destination,
+ int frame_tree_node_id)
+ : m_request_destination(request_destination), m_frame_tree_node_id(frame_tree_node_id)
+{}
+
+void PluginResponseInterceptorURLLoaderThrottle::WillProcessResponse(const GURL &response_url,
+ network::mojom::URLResponseHead *response_head,
+ bool *defer)
+{
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ content::WebContents *web_contents = content::WebContents::FromFrameTreeNodeId(m_frame_tree_node_id);
+ if (!web_contents)
+ return;
+
+ if (content::download_utils::MustDownload(
+ web_contents->GetBrowserContext(),
+ response_url, response_head->headers.get(), response_head->mime_type))
+ return;
+
+ std::string extension_id;
+ if (response_head->mime_type == "application/pdf")
+ extension_id = extension_misc::kPdfExtensionId;
+ if (extension_id.empty())
+ return;
+
+ WebContentsDelegateQt *contentsDelegate = static_cast<WebContentsDelegateQt *>(web_contents->GetDelegate());
+ if (!contentsDelegate)
+ return;
+
+ WebEngineSettings *settings = contentsDelegate->webEngineSettings();
+ if (!settings->testAttribute(QWebEngineSettings::PdfViewerEnabled)
+ || !settings->testAttribute(QWebEngineSettings::PluginsEnabled)) {
+ // PluginServiceFilterQt will inform the URLLoader about the disabled state of plugins
+ // and we can expect the download to be triggered automatically. It's unnecessary to
+ // go further and start the guest view embedding process.
+ return;
+ }
+
+ // Chrome's PDF Extension does not work properly in the face of a restrictive
+ // Content-Security-Policy, and does not currently respect the policy anyway.
+ // Ignore CSP served on a PDF response. https://crbug.com/271452
+ if (extension_id == extension_misc::kPdfExtensionId && response_head->headers)
+ ClearAllButFrameAncestors(response_head);
+
+ MimeTypesHandler::ReportUsedHandler(extension_id);
+
+ std::string view_id = base::Uuid::GenerateRandomV4().AsLowercaseString();
+ // The string passed down to the original client with the response body.
+ std::string payload = view_id;
+
+ mojo::PendingRemote<network::mojom::URLLoader> dummy_new_loader;
+ std::ignore = dummy_new_loader.InitWithNewPipeAndPassReceiver();
+ mojo::Remote<network::mojom::URLLoaderClient> new_client;
+ mojo::PendingReceiver<network::mojom::URLLoaderClient> new_client_receiver =
+ new_client.BindNewPipeAndPassReceiver();
+
+
+ uint32_t data_pipe_size = 64U;
+ // Provide the MimeHandlerView code a chance to override the payload. This is
+ // the case where the resource is handled by frame-based MimeHandlerView.
+ *defer = extensions::MimeHandlerViewAttachHelper::OverrideBodyForInterceptedResponse(
+ m_frame_tree_node_id, response_url, response_head->mime_type, view_id,
+ &payload, &data_pipe_size,
+ base::BindOnce(
+ &PluginResponseInterceptorURLLoaderThrottle::ResumeLoad,
+ weak_factory_.GetWeakPtr()));
+
+ mojo::ScopedDataPipeProducerHandle producer_handle;
+ mojo::ScopedDataPipeConsumerHandle consumer_handle;
+ CHECK_EQ(MOJO_RESULT_OK, mojo::CreateDataPipe(data_pipe_size, producer_handle, consumer_handle));
+
+ uint32_t len = static_cast<uint32_t>(payload.size());
+ CHECK_EQ(MOJO_RESULT_OK,
+ producer_handle->WriteData(
+ payload.c_str(), &len, MOJO_WRITE_DATA_FLAG_ALL_OR_NONE));
+
+ network::URLLoaderCompletionStatus status(net::OK);
+ status.decoded_body_length = len;
+ new_client->OnComplete(status);
+
+ mojo::PendingRemote<network::mojom::URLLoader> original_loader;
+ mojo::PendingReceiver<network::mojom::URLLoaderClient> original_client;
+ mojo::ScopedDataPipeConsumerHandle body = std::move(consumer_handle);
+ delegate_->InterceptResponse(std::move(dummy_new_loader),
+ std::move(new_client_receiver),
+ &original_loader, &original_client,
+ &body);
+
+ // Make a deep copy of URLResponseHead before passing it cross-thread.
+ auto deep_copied_response = response_head->Clone();
+ if (response_head->headers) {
+ deep_copied_response->headers =
+ base::MakeRefCounted<net::HttpResponseHeaders>(
+ response_head->headers->raw_headers());
+ }
+
+ auto transferrable_loader = blink::mojom::TransferrableURLLoader::New();
+ transferrable_loader->url = GURL(
+ extensions::Extension::GetBaseURLFromExtensionId(extension_id).spec() +
+ base::Uuid::GenerateRandomV4().AsLowercaseString());
+ transferrable_loader->url_loader = std::move(original_loader);
+ transferrable_loader->url_loader_client = std::move(original_client);
+ transferrable_loader->head = std::move(deep_copied_response);
+ transferrable_loader->head->intercepted_by_plugin = true;
+ transferrable_loader->body = std::move(body);
+
+ bool embedded = m_request_destination !=
+ network::mojom::RequestDestination::kDocument;
+ content::GetUIThreadTaskRunner({})->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ &extensions::StreamsPrivateAPI::SendExecuteMimeTypeHandlerEvent,
+ extension_id, view_id, embedded, m_frame_tree_node_id,
+ std::move(transferrable_loader), response_url));
+}
+
+void PluginResponseInterceptorURLLoaderThrottle::ResumeLoad()
+{
+ delegate_->Resume();
+}
+
+} // namespace QtWebEngineCore