Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: reimplement PDF viewer #17163

Closed
wants to merge 13 commits into from
13 changes: 11 additions & 2 deletions BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -551,8 +551,17 @@ source_set("electron_lib") {
deps += [ "//third_party/crashpad/crashpad/client" ]
}

if (enable_pdf) {
deps += [ "//pdf" ]
if (enable_pdf_viewer) {
deps += [
"//chrome/browser/resources/pdf:closure_compile",
"//components/guest_view/common",
"//components/pdf/browser",
"//content/public/common:service_names",
"//extensions/browser",
"//extensions/common/api",
"//pdf",
"//services/service_manager/public/cpp",
]
}

if (enable_run_as_node) {
Expand Down
6 changes: 3 additions & 3 deletions buildflags/buildflags.gni
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ declare_args() {

enable_view_api = false

enable_pdf_viewer = false
enable_pdf_viewer = true

enable_tts = true

Expand All @@ -27,6 +27,6 @@ declare_args() {
# Enable flash plugin support.
enable_pepper_flash = true

# Enable Chrome extensions support.
enable_electron_extensions = false
# Enable Chrome extensions support
enable_electron_extensions = true
}
15 changes: 15 additions & 0 deletions chromium_src/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -182,4 +182,19 @@ static_library("chrome") {
]
}
}

if (enable_pdf_viewer) {
sources += [
"//chrome/browser/extensions/chrome_extension_browser_constants.h",
"//chrome/browser/ui/pdf/chrome_pdf_web_contents_helper_client.h",
"//chrome/browser/ui/webui/extensions/extensions_ui.h",
"//chrome/browser/ui/webui/localized_string.h",
"//chrome/browser/ui/webui/metrics_handler.h",
"//components/google/core/common/google_util.h",
"//content/public/browser/web_contents.h",
"//content/public/browser/web_ui.h",
"//extensions/shell/browser/shell_display_info_provider.cc",
"//extensions/shell/browser/shell_display_info_provider.h",
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
// 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 "chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.h"

#include "base/bind.h"
#include "base/feature_list.h"
#include "base/guid.h"
#include "base/strings/strcat.h"
#include "base/task/post_task.h"
#include "chrome/browser/extensions/api/streams_private/streams_private_api.h"
#include "chrome/browser/plugins/plugin_utils.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/download_utils.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/resource_type.h"
#include "content/public/common/transferrable_url_loader.mojom.h"
#include "extensions/browser/guest_view/mime_handler_view/mime_handler_stream_manager.h"
#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_attach_helper.h"
#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h"
#include "extensions/common/extension.h"
#include "mojo/public/cpp/system/data_pipe.h"
#include "shell/common/atom_constants.h"

PluginResponseInterceptorURLLoaderThrottle::
PluginResponseInterceptorURLLoaderThrottle(int resource_type,
int frame_tree_node_id)
: resource_type_(resource_type), frame_tree_node_id_(frame_tree_node_id) {}

PluginResponseInterceptorURLLoaderThrottle::
~PluginResponseInterceptorURLLoaderThrottle() = default;

void PluginResponseInterceptorURLLoaderThrottle::WillProcessResponse(
const GURL& response_url,
network::ResourceResponseHead* response_head,
bool* defer) {
if (content::download_utils::MustDownload(response_url,
response_head->headers.get(),
response_head->mime_type)) {
return;
}

std::string extension_id = electron::kPdfExtensionId;

if (response_head->mime_type != "application/pdf")
return;

std::string view_id = base::GenerateGUID();
// The string passed down to the original client with the response body.
std::string payload = view_id;

network::mojom::URLLoaderPtr dummy_new_loader;
mojo::MakeRequest(&dummy_new_loader);
network::mojom::URLLoaderClientPtr new_client;
network::mojom::URLLoaderClientRequest new_client_request =
mojo::MakeRequest(&new_client);

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(
frame_tree_node_id_, response_url, response_head->mime_type, view_id,
&payload, &data_pipe_size,
base::BindOnce(
&PluginResponseInterceptorURLLoaderThrottle::ResumeLoad,
weak_factory_.GetWeakPtr()));

mojo::DataPipe data_pipe(data_pipe_size);
uint32_t len = static_cast<uint32_t>(payload.size());
CHECK_EQ(MOJO_RESULT_OK,
data_pipe.producer_handle->WriteData(
payload.c_str(), &len, MOJO_WRITE_DATA_FLAG_ALL_OR_NONE));

new_client->OnStartLoadingResponseBody(std::move(data_pipe.consumer_handle));

network::URLLoaderCompletionStatus status(net::OK);
status.decoded_body_length = len;
new_client->OnComplete(status);

network::mojom::URLLoaderPtr original_loader;
network::mojom::URLLoaderClientRequest original_client;
delegate_->InterceptResponse(std::move(dummy_new_loader),
std::move(new_client_request), &original_loader,
&original_client);

// Make a deep copy of ResourceResponseHead before passing it cross-thread.
auto resource_response = base::MakeRefCounted<network::ResourceResponse>();
resource_response->head = *response_head;
auto deep_copied_response = resource_response->DeepCopy();

auto transferrable_loader = content::mojom::TransferrableURLLoader::New();
transferrable_loader->url = GURL(
extensions::Extension::GetBaseURLFromExtensionId(extension_id).spec() +
base::GenerateGUID());
transferrable_loader->url_loader = original_loader.PassInterface();
transferrable_loader->url_loader_client = std::move(original_client);
transferrable_loader->head = std::move(deep_copied_response->head);
transferrable_loader->head.intercepted_by_plugin = true;

bool embedded =
resource_type_ != static_cast<int>(content::ResourceType::kMainFrame);
base::PostTaskWithTraits(
FROM_HERE, {content::BrowserThread::UI},
base::BindOnce(&PluginResponseInterceptorURLLoaderThrottle::
SendExecuteMimeTypeHandlerEvent,
extension_id, view_id, embedded, frame_tree_node_id_,
-1 /* render_process_id */, -1 /* render_frame_id */,
std::move(transferrable_loader), response_url));
}

void PluginResponseInterceptorURLLoaderThrottle::ResumeLoad() {
delegate_->Resume();
}

void PluginResponseInterceptorURLLoaderThrottle::
SendExecuteMimeTypeHandlerEvent(
const std::string& extension_id,
const std::string& view_id,
bool embedded,
int frame_tree_node_id,
int render_process_id,
int render_frame_id,
content::mojom::TransferrableURLLoaderPtr transferrable_loader,
const GURL& original_url) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);

content::WebContents* web_contents = nullptr;
if (frame_tree_node_id != -1) {
web_contents =
content::WebContents::FromFrameTreeNodeId(frame_tree_node_id);
} else {
web_contents = content::WebContents::FromRenderFrameHost(
content::RenderFrameHost::FromID(render_process_id, render_frame_id));
}
if (!web_contents)
return;

auto* browser_context = web_contents->GetBrowserContext();

GURL handler_url(
GURL(base::StrCat({"chrome-extension://", extension_id})).spec() +
"index.html");
int tab_id = -1;
std::unique_ptr<extensions::StreamContainer> stream_container(
new extensions::StreamContainer(
tab_id, embedded, handler_url, extension_id,
std::move(transferrable_loader), original_url));
extensions::MimeHandlerStreamManager::Get(browser_context)
->AddStream(view_id, std::move(stream_container), frame_tree_node_id,
render_process_id, render_frame_id);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// 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.

#ifndef CHROME_BROWSER_PLUGINS_PLUGIN_RESPONSE_INTERCEPTOR_URL_LOADER_THROTTLE_H_
#define CHROME_BROWSER_PLUGINS_PLUGIN_RESPONSE_INTERCEPTOR_URL_LOADER_THROTTLE_H_

#include <string>

#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "content/public/common/transferrable_url_loader.mojom.h"
#include "content/public/common/url_loader_throttle.h"

namespace content {
class BrowserContext;
class ResourceContext;
} // namespace content

// Used to watch navigation responses to look for mime types that are handled by
// extensions. When it finds such a response, it will intercept it by extracting
// the URLLoader interface pointer. It will create a random string and send that
// to the extension which handles the mime type. It will also write that string
// into the object tag for the plugin, which will cause the pepper plugin to
// make a request for that URL. The renderer would have gotten a
// TransferrableURLLoader that allows it to map from that URL to the original
// URLLoader interface pointer.
class PluginResponseInterceptorURLLoaderThrottle
: public content::URLLoaderThrottle {
public:
PluginResponseInterceptorURLLoaderThrottle(int resource_type,
int frame_tree_node_id);
~PluginResponseInterceptorURLLoaderThrottle() override;

// Send the onExecuteMimeTypeHandler event to |extension_id|. If the viewer is
// being opened in a BrowserPlugin, specify a non-empty |view_id| of the
// plugin. |embedded| should be set to whether the document is embedded
// within another document. The |frame_tree_node_id| parameter is used for
// PlzNavigate for the top level plugins case. (PDF, etc). If this parameter
// has a valid value then it overrides the |render_process_id| and
// |render_frame_id| parameters. The |render_process_id| is the id of the
// renderer process. The |render_frame_id| is the routing id of the
// RenderFrameHost.
//
// If the network service is not enabled, |stream| is used; otherwise,
// |transferrable_loader| and |original_url| are used instead.
static void SendExecuteMimeTypeHandlerEvent(
const std::string& extension_id,
const std::string& view_id,
bool embedded,
int frame_tree_node_id,
int render_process_id,
int render_frame_id,
content::mojom::TransferrableURLLoaderPtr transferrable_loader,
const GURL& original_url);

private:
// content::URLLoaderThrottle overrides;
void WillProcessResponse(const GURL& response_url,
network::ResourceResponseHead* response_head,
bool* defer) override;
// Resumes loading for an intercepted response. This would give the extension
// layer chance to initialize its browser side state.
void ResumeLoad();

const int resource_type_;
const int frame_tree_node_id_;

base::WeakPtrFactory<PluginResponseInterceptorURLLoaderThrottle>
weak_factory_{this};

DISALLOW_COPY_AND_ASSIGN(PluginResponseInterceptorURLLoaderThrottle);
};

#endif // CHROME_BROWSER_PLUGINS_PLUGIN_RESPONSE_INTERCEPTOR_URL_LOADER_THROTTLE_H_
1 change: 1 addition & 0 deletions electron_resources.grd
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
</messages>
<includes>
<include name="IDR_CONTENT_SHELL_DEVTOOLS_DISCOVERY_PAGE" file="${target_gen_dir}\shell_devtools_discovery_page.html" use_base_dir="false" type="BINDATA" />
<include name="IDR_PDF_MANIFEST" file="..\chrome\browser\resources\pdf\manifest.json" type="BINDATA" />
</includes>
</release>
</grit>
2 changes: 2 additions & 0 deletions filenames.gni
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,8 @@ filenames = {
"chromium_src/chrome/browser/process_singleton_posix.cc",
"chromium_src/chrome/browser/process_singleton_win.cc",
"chromium_src/chrome/browser/process_singleton.h",
"chromium_src/chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.cc",
"chromium_src/chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.h",
"chromium_src/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.cc",
"chromium_src/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.h",
]
Expand Down
15 changes: 14 additions & 1 deletion shell/app/atom_content_client.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@
#endif // defined(WIDEVINE_CDM_AVAILABLE)

#if BUILDFLAG(ENABLE_PDF_VIEWER)
#include "content/public/browser/plugin_service.h"
#include "pdf/pdf.h"
#include "pdf/pdf_ppapi.h"
#include "shell/common/atom_constants.h"
#endif // BUILDFLAG(ENABLE_PDF_VIEWER)

Expand Down Expand Up @@ -145,7 +147,7 @@ void ComputeBuiltInPlugins(std::vector<content::PepperPluginInfo>* plugins) {
content::PepperPluginInfo pdf_info;
pdf_info.is_internal = true;
pdf_info.is_out_of_process = true;
pdf_info.name = "Chromium PDF Viewer";
pdf_info.name = kPDFExtensionPluginName;
pdf_info.description = "Portable Document Format";
pdf_info.path = base::FilePath::FromUTF8Unsafe(kPdfPluginPath);
content::WebPluginMimeType pdf_mime_type(kPdfPluginMimeType, "pdf",
Expand All @@ -158,6 +160,17 @@ void ComputeBuiltInPlugins(std::vector<content::PepperPluginInfo>* plugins) {
chrome_pdf::PPP_ShutdownModule;
pdf_info.permissions = ppapi::PERMISSION_PRIVATE | ppapi::PERMISSION_DEV;
plugins->push_back(pdf_info);

content::WebPluginInfo info;
info.type = content::WebPluginInfo::PLUGIN_TYPE_BROWSER_PLUGIN;
info.name = base::UTF8ToUTF16(kPDFExtensionPluginName);
info.path = base::FilePath::FromUTF8Unsafe(kPdfPluginPath);
info.background_color = content::WebPluginInfo::kDefaultBackgroundColor;
content::WebPluginMimeType mime_type_info("application/pdf", "pdf",
"Portable Document Format");
info.mime_types.push_back(mime_type_info);
content::PluginService::GetInstance()->RefreshPlugins();
content::PluginService::GetInstance()->RegisterInternalPlugin(info, true);
#endif // BUILDFLAG(ENABLE_PDF_VIEWER)
}

Expand Down
9 changes: 0 additions & 9 deletions shell/app/atom_main_delegate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -115,15 +115,6 @@ void LoadResourceBundle(const std::string& locale) {
bundle.ReloadLocaleResources(locale);
bundle.AddDataPackFromPath(pak_dir.Append(FILE_PATH_LITERAL("resources.pak")),
ui::SCALE_FACTOR_NONE);
#if BUILDFLAG(ENABLE_PDF_VIEWER)
NOTIMPLEMENTED()
<< "Hi, whoever's fixing PDF support! Thanks! The pdf "
"viewer resources haven't been ported over to the GN build yet, so "
"you'll probably need to change this bit of code.";
bundle.AddDataPackFromPath(
pak_dir.Append(FILE_PATH_LITERAL("pdf_viewer_resources.pak")),
ui::GetSupportedScaleFactors()[0]);
#endif // BUILDFLAG(ENABLE_PDF_VIEWER)
}

AtomMainDelegate::AtomMainDelegate() {}
Expand Down
Loading