feat: enable pdf viewer (#21794)

This commit is contained in:
Jeremy Apthorp 2020-02-12 16:39:12 -08:00 committed by GitHub
parent 85f9c779a8
commit d590f2efe3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
51 changed files with 1579 additions and 210 deletions

View file

@ -0,0 +1,35 @@
# 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.
import("//extensions/buildflags/buildflags.gni")
import("//tools/json_schema_compiler/json_schema_api.gni")
assert(enable_extensions,
"Cannot depend on extensions because enable_extensions=false.")
function_registration("api_registration") {
sources = [
"//electron/shell/common/extensions/api/extension.json",
"//electron/shell/common/extensions/api/resources_private.idl",
"//electron/shell/common/extensions/api/tabs.json",
]
impl_dir = "//electron/shell/browser/extensions/api"
configs = [ "//build/config:precompiled_headers" ]
bundle_name = "Electron"
root_namespace = "extensions::api::%(namespace)s"
schema_include_rules = "extensions/common/api:extensions::api::%(namespace)s"
deps = [
# Different APIs include headers from these targets.
"//components/zoom",
"//content/public/browser",
"//extensions/browser",
# Different APIs include some headers from chrome/common that in turn
# include generated headers from these targets.
# TODO(brettw) this should be made unnecessary if possible.
"//electron/shell/common/extensions/api",
]
deps += [ "//extensions/common/api" ]
}

View file

@ -0,0 +1,113 @@
// Copyright 2015 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 "shell/browser/extensions/api/resources_private/resources_private_api.h"
#include <memory>
#include <string>
#include <utility>
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/common/extensions/api/resources_private.h"
#include "chrome/grit/generated_resources.h"
#include "components/strings/grit/components_strings.h"
#include "components/zoom/page_zoom_constants.h"
#include "pdf/buildflags.h"
#include "printing/buildflags/buildflags.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/webui/web_ui_util.h"
#if BUILDFLAG(ENABLE_PDF)
#include "pdf/pdf_features.h"
#endif // BUILDFLAG(ENABLE_PDF)
// To add a new component to this API, simply:
// 1. Add your component to the Component enum in
// chrome/common/extensions/api/resources_private.idl
// 2. Create an AddStringsForMyComponent(base::DictionaryValue * dict) method.
// 3. Tie in that method to the switch statement in Run()
namespace extensions {
namespace {
void AddStringsForPdf(base::DictionaryValue* dict) {
#if BUILDFLAG(ENABLE_PDF)
static constexpr webui::LocalizedString kPdfResources[] = {
{"passwordDialogTitle", IDS_PDF_PASSWORD_DIALOG_TITLE},
{"passwordPrompt", IDS_PDF_NEED_PASSWORD},
{"passwordSubmit", IDS_PDF_PASSWORD_SUBMIT},
{"passwordInvalid", IDS_PDF_PASSWORD_INVALID},
{"pageLoading", IDS_PDF_PAGE_LOADING},
{"pageLoadFailed", IDS_PDF_PAGE_LOAD_FAILED},
{"errorDialogTitle", IDS_PDF_ERROR_DIALOG_TITLE},
{"pageReload", IDS_PDF_PAGE_RELOAD_BUTTON},
{"bookmarks", IDS_PDF_BOOKMARKS},
{"labelPageNumber", IDS_PDF_LABEL_PAGE_NUMBER},
{"tooltipRotateCW", IDS_PDF_TOOLTIP_ROTATE_CW},
{"tooltipDownload", IDS_PDF_TOOLTIP_DOWNLOAD},
{"tooltipPrint", IDS_PDF_TOOLTIP_PRINT},
{"tooltipFitToPage", IDS_PDF_TOOLTIP_FIT_PAGE},
{"tooltipFitToWidth", IDS_PDF_TOOLTIP_FIT_WIDTH},
{"tooltipZoomIn", IDS_PDF_TOOLTIP_ZOOM_IN},
{"tooltipZoomOut", IDS_PDF_TOOLTIP_ZOOM_OUT},
};
for (const auto& resource : kPdfResources)
dict->SetString(resource.name, l10n_util::GetStringUTF16(resource.id));
dict->SetString("presetZoomFactors", zoom::GetPresetZoomFactorsAsJSON());
#endif // BUILDFLAG(ENABLE_PDF)
}
void AddAdditionalDataForPdf(base::DictionaryValue* dict) {
#if BUILDFLAG(ENABLE_PDF)
dict->SetKey("pdfFormSaveEnabled",
base::Value(base::FeatureList::IsEnabled(
chrome_pdf::features::kSaveEditedPDFForm)));
dict->SetKey("pdfAnnotationsEnabled",
base::Value(base::FeatureList::IsEnabled(
chrome_pdf::features::kPDFAnnotations)));
// TODO(nornagon): enable printing once it works.
bool enable_printing = false;
dict->SetKey("printingEnabled", base::Value(enable_printing));
#endif // BUILDFLAG(ENABLE_PDF)
}
} // namespace
namespace get_strings = api::resources_private::GetStrings;
ResourcesPrivateGetStringsFunction::ResourcesPrivateGetStringsFunction() {}
ResourcesPrivateGetStringsFunction::~ResourcesPrivateGetStringsFunction() {}
ExtensionFunction::ResponseAction ResourcesPrivateGetStringsFunction::Run() {
std::unique_ptr<get_strings::Params> params(
get_strings::Params::Create(*args_));
auto dict = std::make_unique<base::DictionaryValue>();
api::resources_private::Component component = params->component;
switch (component) {
case api::resources_private::COMPONENT_PDF:
AddStringsForPdf(dict.get());
AddAdditionalDataForPdf(dict.get());
break;
case api::resources_private::COMPONENT_IDENTITY:
NOTREACHED();
break;
case api::resources_private::COMPONENT_NONE:
NOTREACHED();
break;
}
const std::string& app_locale = g_browser_process->GetApplicationLocale();
webui::SetLoadTimeDataDefaults(app_locale, dict.get());
return RespondNow(OneArgument(std::move(dict)));
}
} // namespace extensions

View file

@ -0,0 +1,31 @@
// Copyright 2015 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 SHELL_BROWSER_EXTENSIONS_API_RESOURCES_PRIVATE_RESOURCES_PRIVATE_API_H_
#define SHELL_BROWSER_EXTENSIONS_API_RESOURCES_PRIVATE_RESOURCES_PRIVATE_API_H_
#include "base/macros.h"
#include "extensions/browser/extension_function.h"
namespace extensions {
class ResourcesPrivateGetStringsFunction : public ExtensionFunction {
public:
DECLARE_EXTENSION_FUNCTION("resourcesPrivate.getStrings",
RESOURCESPRIVATE_GETSTRINGS)
ResourcesPrivateGetStringsFunction();
protected:
~ResourcesPrivateGetStringsFunction() override;
// Override from ExtensionFunction:
ExtensionFunction::ResponseAction Run() override;
private:
DISALLOW_COPY_AND_ASSIGN(ResourcesPrivateGetStringsFunction);
};
} // namespace extensions
#endif // SHELL_BROWSER_EXTENSIONS_API_RESOURCES_PRIVATE_RESOURCES_PRIVATE_API_H_

View file

@ -0,0 +1,74 @@
// 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 "electron/shell/browser/extensions/api/streams_private/streams_private_api.h"
#include <utility>
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "extensions/browser/extension_registry.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_guest.h"
#include "extensions/common/manifest_handlers/mime_types_handler.h"
#include "shell/browser/api/electron_api_web_contents.h"
namespace extensions {
void StreamsPrivateAPI::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();
const extensions::Extension* extension =
extensions::ExtensionRegistry::Get(browser_context)
->enabled_extensions()
.GetByID(extension_id);
if (!extension)
return;
MimeTypesHandler* handler = MimeTypesHandler::GetHandler(extension);
if (!handler->HasPlugin())
return;
// If the mime handler uses MimeHandlerViewGuest, the MimeHandlerViewGuest
// will take ownership of the stream.
GURL handler_url(
extensions::Extension::GetBaseURLFromExtensionId(extension_id).spec() +
handler->handler_url());
int tab_id = -1;
auto* api_contents = electron::api::WebContents::FromWrappedClass(
v8::Isolate::GetCurrent(), web_contents);
if (api_contents)
tab_id = api_contents->ID();
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);
}
} // namespace extensions

View file

@ -0,0 +1,44 @@
// 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.
#ifndef SHELL_BROWSER_EXTENSIONS_API_STREAMS_PRIVATE_STREAMS_PRIVATE_API_H_
#define SHELL_BROWSER_EXTENSIONS_API_STREAMS_PRIVATE_STREAMS_PRIVATE_API_H_
#include <memory>
#include <string>
#include "base/macros.h"
#include "content/public/common/transferrable_url_loader.mojom.h"
namespace extensions {
// TODO(devlin): This is now only used for the MimeTypesHandler API. We should
// rename and move it to make that clear. https://crbug.com/890401.
class StreamsPrivateAPI {
public:
// 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 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);
};
} // namespace extensions
#endif // SHELL_BROWSER_EXTENSIONS_API_STREAMS_PRIVATE_STREAMS_PRIVATE_API_H_

View file

@ -12,13 +12,48 @@
#include "extensions/common/manifest_constants.h"
#include "extensions/common/permissions/permissions_data.h"
#include "shell/browser/api/electron_api_web_contents.h"
#include "shell/browser/web_contents_zoom_controller.h"
#include "shell/common/extensions/api/tabs.h"
#include "third_party/blink/public/common/page/page_zoom.h"
using electron::WebContentsZoomController;
namespace extensions {
namespace tabs = api::tabs;
const char kFrameNotFoundError[] = "No frame with id * in tab *.";
const char kPerOriginOnlyInAutomaticError[] =
"Can only set scope to "
"\"per-origin\" in \"automatic\" mode.";
using api::extension_types::InjectDetails;
namespace {
void ZoomModeToZoomSettings(WebContentsZoomController::ZoomMode zoom_mode,
api::tabs::ZoomSettings* zoom_settings) {
DCHECK(zoom_settings);
switch (zoom_mode) {
case WebContentsZoomController::ZoomMode::DEFAULT:
zoom_settings->mode = api::tabs::ZOOM_SETTINGS_MODE_AUTOMATIC;
zoom_settings->scope = api::tabs::ZOOM_SETTINGS_SCOPE_PER_ORIGIN;
break;
case WebContentsZoomController::ZoomMode::ISOLATED:
zoom_settings->mode = api::tabs::ZOOM_SETTINGS_MODE_AUTOMATIC;
zoom_settings->scope = api::tabs::ZOOM_SETTINGS_SCOPE_PER_TAB;
break;
case WebContentsZoomController::ZoomMode::MANUAL:
zoom_settings->mode = api::tabs::ZOOM_SETTINGS_MODE_MANUAL;
zoom_settings->scope = api::tabs::ZOOM_SETTINGS_SCOPE_PER_TAB;
break;
case WebContentsZoomController::ZoomMode::DISABLED:
zoom_settings->mode = api::tabs::ZOOM_SETTINGS_MODE_DISABLED;
zoom_settings->scope = api::tabs::ZOOM_SETTINGS_SCOPE_PER_TAB;
break;
}
}
} // namespace
ExecuteCodeInTabFunction::ExecuteCodeInTabFunction() : execute_tab_id_(-1) {}
ExecuteCodeInTabFunction::~ExecuteCodeInTabFunction() {}
@ -130,4 +165,149 @@ bool TabsExecuteScriptFunction::ShouldInsertCSS() const {
return false;
}
ExtensionFunction::ResponseAction TabsGetFunction::Run() {
std::unique_ptr<tabs::Get::Params> params(tabs::Get::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params.get());
int tab_id = params->tab_id;
auto* contents = electron::api::WebContents::FromWeakMapID(
v8::Isolate::GetCurrent(), tab_id);
if (!contents)
return RespondNow(Error("No such tab"));
tabs::Tab tab;
tab.id.reset(new int(tab_id));
// TODO(nornagon): in Chrome, the tab URL is only available to extensions
// that have the "tabs" (or "activeTab") permission. We should do the same
// permission check here.
tab.url = std::make_unique<std::string>(
contents->web_contents()->GetLastCommittedURL().spec());
return RespondNow(ArgumentList(tabs::Get::Results::Create(std::move(tab))));
}
ExtensionFunction::ResponseAction TabsSetZoomFunction::Run() {
std::unique_ptr<tabs::SetZoom::Params> params(
tabs::SetZoom::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params);
int tab_id = params->tab_id ? *params->tab_id : -1;
auto* contents = electron::api::WebContents::FromWeakMapID(
v8::Isolate::GetCurrent(), tab_id);
if (!contents)
return RespondNow(Error("No such tab"));
auto* web_contents = contents->web_contents();
GURL url(web_contents->GetVisibleURL());
std::string error;
if (extension()->permissions_data()->IsRestrictedUrl(url, &error))
return RespondNow(Error(error));
auto* zoom_controller = contents->GetZoomController();
double zoom_level =
params->zoom_factor > 0
? blink::PageZoomFactorToZoomLevel(params->zoom_factor)
: blink::PageZoomFactorToZoomLevel(
zoom_controller->GetDefaultZoomFactor());
zoom_controller->SetZoomLevel(zoom_level);
return RespondNow(NoArguments());
}
ExtensionFunction::ResponseAction TabsGetZoomFunction::Run() {
std::unique_ptr<tabs::GetZoom::Params> params(
tabs::GetZoom::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params);
int tab_id = params->tab_id ? *params->tab_id : -1;
auto* contents = electron::api::WebContents::FromWeakMapID(
v8::Isolate::GetCurrent(), tab_id);
if (!contents)
return RespondNow(Error("No such tab"));
double zoom_level = contents->GetZoomController()->GetZoomLevel();
double zoom_factor = blink::PageZoomLevelToZoomFactor(zoom_level);
return RespondNow(ArgumentList(tabs::GetZoom::Results::Create(zoom_factor)));
}
ExtensionFunction::ResponseAction TabsGetZoomSettingsFunction::Run() {
std::unique_ptr<tabs::GetZoomSettings::Params> params(
tabs::GetZoomSettings::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params);
int tab_id = params->tab_id ? *params->tab_id : -1;
auto* contents = electron::api::WebContents::FromWeakMapID(
v8::Isolate::GetCurrent(), tab_id);
if (!contents)
return RespondNow(Error("No such tab"));
auto* zoom_controller = contents->GetZoomController();
WebContentsZoomController::ZoomMode zoom_mode =
contents->GetZoomController()->zoom_mode();
api::tabs::ZoomSettings zoom_settings;
ZoomModeToZoomSettings(zoom_mode, &zoom_settings);
zoom_settings.default_zoom_factor.reset(
new double(blink::PageZoomLevelToZoomFactor(
zoom_controller->GetDefaultZoomLevel())));
return RespondNow(
ArgumentList(api::tabs::GetZoomSettings::Results::Create(zoom_settings)));
}
ExtensionFunction::ResponseAction TabsSetZoomSettingsFunction::Run() {
using api::tabs::ZoomSettings;
std::unique_ptr<tabs::SetZoomSettings::Params> params(
tabs::SetZoomSettings::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params);
int tab_id = params->tab_id ? *params->tab_id : -1;
auto* contents = electron::api::WebContents::FromWeakMapID(
v8::Isolate::GetCurrent(), tab_id);
if (!contents)
return RespondNow(Error("No such tab"));
std::string error;
GURL url(contents->web_contents()->GetVisibleURL());
if (extension()->permissions_data()->IsRestrictedUrl(url, &error))
return RespondNow(Error(error));
// "per-origin" scope is only available in "automatic" mode.
if (params->zoom_settings.scope == tabs::ZOOM_SETTINGS_SCOPE_PER_ORIGIN &&
params->zoom_settings.mode != tabs::ZOOM_SETTINGS_MODE_AUTOMATIC &&
params->zoom_settings.mode != tabs::ZOOM_SETTINGS_MODE_NONE) {
return RespondNow(Error(kPerOriginOnlyInAutomaticError));
}
// Determine the correct internal zoom mode to set |web_contents| to from the
// user-specified |zoom_settings|.
WebContentsZoomController::ZoomMode zoom_mode =
WebContentsZoomController::ZoomMode::DEFAULT;
switch (params->zoom_settings.mode) {
case tabs::ZOOM_SETTINGS_MODE_NONE:
case tabs::ZOOM_SETTINGS_MODE_AUTOMATIC:
switch (params->zoom_settings.scope) {
case tabs::ZOOM_SETTINGS_SCOPE_NONE:
case tabs::ZOOM_SETTINGS_SCOPE_PER_ORIGIN:
zoom_mode = WebContentsZoomController::ZoomMode::DEFAULT;
break;
case tabs::ZOOM_SETTINGS_SCOPE_PER_TAB:
zoom_mode = WebContentsZoomController::ZoomMode::ISOLATED;
}
break;
case tabs::ZOOM_SETTINGS_MODE_MANUAL:
zoom_mode = WebContentsZoomController::ZoomMode::MANUAL;
break;
case tabs::ZOOM_SETTINGS_MODE_DISABLED:
zoom_mode = WebContentsZoomController::ZoomMode::DISABLED;
}
contents->GetZoomController()->SetZoomMode(zoom_mode);
return RespondNow(NoArguments());
}
} // namespace extensions

View file

@ -44,6 +44,48 @@ class TabsExecuteScriptFunction : public ExecuteCodeInTabFunction {
DECLARE_EXTENSION_FUNCTION("tabs.executeScript", TABS_EXECUTESCRIPT)
};
class TabsGetFunction : public ExtensionFunction {
~TabsGetFunction() override {}
ResponseAction Run() override;
DECLARE_EXTENSION_FUNCTION("tabs.get", TABS_GET)
};
class TabsSetZoomFunction : public ExtensionFunction {
private:
~TabsSetZoomFunction() override {}
ResponseAction Run() override;
DECLARE_EXTENSION_FUNCTION("tabs.setZoom", TABS_SETZOOM)
};
class TabsGetZoomFunction : public ExtensionFunction {
private:
~TabsGetZoomFunction() override {}
ResponseAction Run() override;
DECLARE_EXTENSION_FUNCTION("tabs.getZoom", TABS_GETZOOM)
};
class TabsSetZoomSettingsFunction : public ExtensionFunction {
private:
~TabsSetZoomSettingsFunction() override {}
ResponseAction Run() override;
DECLARE_EXTENSION_FUNCTION("tabs.setZoomSettings", TABS_SETZOOMSETTINGS)
};
class TabsGetZoomSettingsFunction : public ExtensionFunction {
private:
~TabsGetZoomSettingsFunction() override {}
ResponseAction Run() override;
DECLARE_EXTENSION_FUNCTION("tabs.getZoomSettings", TABS_GETZOOMSETTINGS)
};
} // namespace extensions
#endif // SHELL_BROWSER_EXTENSIONS_API_TABS_TABS_API_H_