Compare commits

...

2 Commits

Author SHA1 Message Date
Kaitlin Newson
0a7df0ef3d docs: typo fix (#41787)
Typo fix
2024-04-10 10:25:13 +02:00
Shelley Vohr
38ef9a7690 refactor: move PDF viewer to OOPIF (#41728)
https://issues.chromium.org/issues/40268279
2024-04-09 19:59:48 -04:00
7 changed files with 261 additions and 13 deletions

View File

@@ -59,7 +59,7 @@ need to be called **before** the app's `ready` event is emitted.
With top-level `await` available in Node.js ESM, make sure to `await` every Promise that you need to
execute before the `ready` event. Otherwise, your app may be `ready` before your code executes.
This is particularly important to keep in mind for dynamic ESM import statmements (static imports are unaffected).
This is particularly important to keep in mind for dynamic ESM import statements (static imports are unaffected).
For example, if `index.mjs` calls `import('./set-up-paths.mjs')` at the top level, the app will
likely already be `ready` by the time that dynamic import resolves.

View File

@@ -154,7 +154,7 @@ void ElectronManagementAPIDelegate::InstallOrLaunchReplacementWebApp(
void ElectronManagementAPIDelegate::EnableExtension(
content::BrowserContext* context,
const std::string& extension_id) const {
const extensions::ExtensionId& extension_id) const {
// const extensions::Extension* extension =
// extensions::ExtensionRegistry::Get(context)->GetExtensionById(
// extension_id, extensions::ExtensionRegistry::EVERYTHING);
@@ -171,7 +171,7 @@ void ElectronManagementAPIDelegate::EnableExtension(
void ElectronManagementAPIDelegate::DisableExtension(
content::BrowserContext* context,
const extensions::Extension* source_extension,
const std::string& extension_id,
const extensions::ExtensionId& extension_id,
extensions::disable_reason::DisableReason disable_reason) const {
// TODO(sentialx): we don't have ExtensionService
// extensions::ExtensionSystem::Get(context)
@@ -182,7 +182,7 @@ void ElectronManagementAPIDelegate::DisableExtension(
bool ElectronManagementAPIDelegate::UninstallExtension(
content::BrowserContext* context,
const std::string& transient_extension_id,
const extensions::ExtensionId& transient_extension_id,
extensions::UninstallReason reason,
std::u16string* error) const {
// TODO(sentialx): we don't have ExtensionService
@@ -194,7 +194,7 @@ bool ElectronManagementAPIDelegate::UninstallExtension(
void ElectronManagementAPIDelegate::SetLaunchType(
content::BrowserContext* context,
const std::string& extension_id,
const extensions::ExtensionId& extension_id,
extensions::LaunchType launch_type) const {
// TODO(sentialx)
// extensions::SetLaunchType(context, extension_id, launch_type);

View File

@@ -10,6 +10,7 @@
#include "base/task/cancelable_task_tracker.h"
#include "extensions/browser/api/management/management_api_delegate.h"
#include "extensions/common/extension_id.h"
class ElectronManagementAPIDelegate : public extensions::ManagementAPIDelegate {
public:
@@ -51,19 +52,20 @@ class ElectronManagementAPIDelegate : public extensions::ManagementAPIDelegate {
const GURL& web_app_url,
ManagementAPIDelegate::InstallOrLaunchWebAppCallback callback)
const override;
void EnableExtension(content::BrowserContext* context,
const std::string& extension_id) const override;
void EnableExtension(
content::BrowserContext* context,
const extensions::ExtensionId& extension_id) const override;
void DisableExtension(
content::BrowserContext* context,
const extensions::Extension* source_extension,
const std::string& extension_id,
const extensions::ExtensionId& extension_id,
extensions::disable_reason::DisableReason disable_reason) const override;
bool UninstallExtension(content::BrowserContext* context,
const std::string& transient_extension_id,
const extensions::ExtensionId& transient_extension_id,
extensions::UninstallReason reason,
std::u16string* error) const override;
void SetLaunchType(content::BrowserContext* context,
const std::string& extension_id,
const extensions::ExtensionId& extension_id,
extensions::LaunchType launch_type) const override;
GURL GetIconURL(const extensions::Extension* extension,
int icon_size,

View File

@@ -6,10 +6,16 @@
#include <string>
#include "base/memory/weak_ptr.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/pdf/pdf_viewer_stream_manager.h"
#include "chrome/common/extensions/api/pdf_viewer_private.h"
#include "chrome/common/pref_names.h"
#include "components/pdf/common/constants.h"
#include "components/prefs/pref_service.h"
#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h"
#include "shell/browser/electron_browser_context.h"
#include "url/url_constants.h"
@@ -22,6 +28,11 @@ namespace IsAllowedLocalFileAccess =
namespace SetPdfOcrPref = api::pdf_viewer_private::SetPdfOcrPref;
namespace SetPdfPluginAttributes =
api::pdf_viewer_private::SetPdfPluginAttributes;
namespace SetPdfDocumentTitle = api::pdf_viewer_private::SetPdfDocumentTitle;
// Check if the current URL is allowed based on a list of allowlisted domains.
bool IsUrlAllowedToEmbedLocalFiles(
const GURL& current_url,
@@ -43,8 +54,46 @@ bool IsUrlAllowedToEmbedLocalFiles(
return false;
}
// Get the `StreamContainer` associated with the `extension_host`.
base::WeakPtr<StreamContainer> GetStreamContainer(
content::RenderFrameHost* extension_host) {
content::RenderFrameHost* embedder_host = extension_host->GetParent();
if (!embedder_host) {
return nullptr;
}
auto* pdf_viewer_stream_manager =
pdf::PdfViewerStreamManager::FromRenderFrameHost(embedder_host);
if (!pdf_viewer_stream_manager) {
return nullptr;
}
return pdf_viewer_stream_manager->GetStreamContainer(embedder_host);
}
} // namespace
PdfViewerPrivateGetStreamInfoFunction::PdfViewerPrivateGetStreamInfoFunction() =
default;
PdfViewerPrivateGetStreamInfoFunction::
~PdfViewerPrivateGetStreamInfoFunction() = default;
ExtensionFunction::ResponseAction PdfViewerPrivateGetStreamInfoFunction::Run() {
base::WeakPtr<StreamContainer> stream =
GetStreamContainer(render_frame_host());
if (!stream) {
return RespondNow(Error("Failed to get StreamContainer"));
}
api::pdf_viewer_private::StreamInfo stream_info;
stream_info.original_url = stream->original_url().spec();
stream_info.stream_url = stream->stream_url().spec();
stream_info.tab_id = stream->tab_id();
stream_info.embedded = stream->embedded();
return RespondNow(WithArguments(stream_info.ToValue()));
}
PdfViewerPrivateIsAllowedLocalFileAccessFunction::
PdfViewerPrivateIsAllowedLocalFileAccessFunction() = default;
@@ -61,6 +110,37 @@ PdfViewerPrivateIsAllowedLocalFileAccessFunction::Run() {
IsUrlAllowedToEmbedLocalFiles(GURL(params->url), base::Value::List())));
}
PdfViewerPrivateSetPdfDocumentTitleFunction::
PdfViewerPrivateSetPdfDocumentTitleFunction() = default;
PdfViewerPrivateSetPdfDocumentTitleFunction::
~PdfViewerPrivateSetPdfDocumentTitleFunction() = default;
// This function is only called for full-page PDFs.
ExtensionFunction::ResponseAction
PdfViewerPrivateSetPdfDocumentTitleFunction::Run() {
content::WebContents* web_contents = GetSenderWebContents();
if (!web_contents) {
return RespondNow(Error("Could not find a valid web contents."));
}
// Title should only be set for full-page PDFs.
// MIME type associated with sender `WebContents` must be `application/pdf`
// for a full-page PDF.
EXTENSION_FUNCTION_VALIDATE(web_contents->GetContentsMimeType() ==
pdf::kPDFMimeType);
std::optional<SetPdfDocumentTitle::Params> params =
SetPdfDocumentTitle::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
web_contents->UpdateTitleForEntry(
web_contents->GetController().GetLastCommittedEntry(),
base::UTF8ToUTF16(params->title));
return RespondNow(NoArguments());
}
PdfViewerPrivateIsPdfOcrAlwaysActiveFunction::
PdfViewerPrivateIsPdfOcrAlwaysActiveFunction() = default;
@@ -87,4 +167,42 @@ ExtensionFunction::ResponseAction PdfViewerPrivateSetPdfOcrPrefFunction::Run() {
return RespondNow(WithArguments(false));
}
PdfViewerPrivateSetPdfPluginAttributesFunction::
PdfViewerPrivateSetPdfPluginAttributesFunction() = default;
PdfViewerPrivateSetPdfPluginAttributesFunction::
~PdfViewerPrivateSetPdfPluginAttributesFunction() = default;
ExtensionFunction::ResponseAction
PdfViewerPrivateSetPdfPluginAttributesFunction::Run() {
std::optional<SetPdfPluginAttributes::Params> params =
SetPdfPluginAttributes::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
base::WeakPtr<StreamContainer> stream =
GetStreamContainer(render_frame_host());
if (!stream) {
return RespondNow(Error("Failed to get StreamContainer"));
}
const api::pdf_viewer_private::PdfPluginAttributes& attributes =
params->attributes;
// Check the `background_color` is an integer.
double whole = 0.0;
if (std::modf(attributes.background_color, &whole) != 0.0) {
return RespondNow(Error("Background color is not an integer"));
}
// Check the `background_color` is within the range of a uint32_t.
if (!base::IsValueInRangeForNumericType<uint32_t>(
attributes.background_color)) {
return RespondNow(Error("Background color out of bounds"));
}
stream->set_pdf_plugin_attributes(mime_handler::PdfPluginAttributes::New(
/*background_color=*/attributes.background_color,
/*allow_javascript=*/attributes.allow_javascript));
return RespondNow(NoArguments());
}
} // namespace extensions

View File

@@ -9,6 +9,24 @@
namespace extensions {
class PdfViewerPrivateGetStreamInfoFunction : public ExtensionFunction {
public:
DECLARE_EXTENSION_FUNCTION("pdfViewerPrivate.getStreamInfo",
PDFVIEWERPRIVATE_GETSTREAMINFO)
PdfViewerPrivateGetStreamInfoFunction();
PdfViewerPrivateGetStreamInfoFunction(
const PdfViewerPrivateGetStreamInfoFunction&) = delete;
PdfViewerPrivateGetStreamInfoFunction& operator=(
const PdfViewerPrivateGetStreamInfoFunction&) = delete;
protected:
~PdfViewerPrivateGetStreamInfoFunction() override;
// Override from ExtensionFunction:
ResponseAction Run() override;
};
class PdfViewerPrivateIsAllowedLocalFileAccessFunction
: public ExtensionFunction {
public:
@@ -28,6 +46,24 @@ class PdfViewerPrivateIsAllowedLocalFileAccessFunction
ResponseAction Run() override;
};
class PdfViewerPrivateSetPdfDocumentTitleFunction : public ExtensionFunction {
public:
DECLARE_EXTENSION_FUNCTION("pdfViewerPrivate.setPdfDocumentTitle",
PDFVIEWERPRIVATE_SETPDFDOCUMENTTITLE)
PdfViewerPrivateSetPdfDocumentTitleFunction();
PdfViewerPrivateSetPdfDocumentTitleFunction(
const PdfViewerPrivateSetPdfDocumentTitleFunction&) = delete;
PdfViewerPrivateSetPdfDocumentTitleFunction& operator=(
const PdfViewerPrivateSetPdfDocumentTitleFunction&) = delete;
protected:
~PdfViewerPrivateSetPdfDocumentTitleFunction() override;
// Override from ExtensionFunction:
ResponseAction Run() override;
};
class PdfViewerPrivateIsPdfOcrAlwaysActiveFunction : public ExtensionFunction {
public:
DECLARE_EXTENSION_FUNCTION("pdfViewerPrivate.isPdfOcrAlwaysActive",
@@ -64,6 +100,25 @@ class PdfViewerPrivateSetPdfOcrPrefFunction : public ExtensionFunction {
ResponseAction Run() override;
};
class PdfViewerPrivateSetPdfPluginAttributesFunction
: public ExtensionFunction {
public:
DECLARE_EXTENSION_FUNCTION("pdfViewerPrivate.setPdfPluginAttributes",
PDFVIEWERPRIVATE_SETPDFPLUGINATTRIBUTES)
PdfViewerPrivateSetPdfPluginAttributesFunction();
PdfViewerPrivateSetPdfPluginAttributesFunction(
const PdfViewerPrivateSetPdfPluginAttributesFunction&) = delete;
PdfViewerPrivateSetPdfPluginAttributesFunction& operator=(
const PdfViewerPrivateSetPdfPluginAttributesFunction&) = delete;
protected:
~PdfViewerPrivateSetPdfPluginAttributesFunction() override;
// Override from ExtensionFunction:
ResponseAction Run() override;
};
} // namespace extensions
#endif // ELECTRON_SHELL_BROWSER_EXTENSIONS_API_PDF_VIEWER_PRIVATE_PDF_VIEWER_PRIVATE_API_H_

View File

@@ -10,12 +10,20 @@
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "electron/buildflags/buildflags.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"
#if BUILDFLAG(ENABLE_PDF_VIEWER)
#include "base/feature_list.h"
#include "chrome/browser/pdf/pdf_viewer_stream_manager.h"
#include "extensions/common/constants.h"
#include "pdf/pdf_features.h"
#endif // BUILDFLAG(ENABLE_PDF_VIEWER)
namespace extensions {
void StreamsPrivateAPI::SendExecuteMimeTypeHandlerEvent(
@@ -51,13 +59,27 @@ void StreamsPrivateAPI::SendExecuteMimeTypeHandlerEvent(
GURL handler_url(
extensions::Extension::GetBaseURLFromExtensionId(extension_id).spec() +
handler->handler_url());
int tab_id = -1;
auto* api_contents = electron::api::WebContents::From(web_contents);
if (api_contents)
tab_id = api_contents->ID();
auto stream_container = std::make_unique<extensions::StreamContainer>(
tab_id, embedded, handler_url, extension_id,
std::move(transferrable_loader), original_url);
#if BUILDFLAG(ENABLE_PDF_VIEWER)
if (base::FeatureList::IsEnabled(chrome_pdf::features::kPdfOopif) &&
extension_id == extension_misc::kPdfExtensionId) {
pdf::PdfViewerStreamManager::Create(web_contents);
pdf::PdfViewerStreamManager::FromWebContents(web_contents)
->AddStreamContainer(frame_tree_node_id, internal_id,
std::move(stream_container));
return;
}
#endif // BUILDFLAG(ENABLE_PDF_VIEWER)
extensions::MimeHandlerStreamManager::Get(browser_context)
->AddStream(stream_id, std::move(stream_container), frame_tree_node_id);
}

View File

@@ -6,34 +6,85 @@
// functionality that the PDF Viewer needs from outside the PDF plugin. This API
// is exclusively for the PDF Viewer.
namespace pdfViewerPrivate {
// Nearly identical to mimeHandlerPrivate.StreamInfo, but without a mime type
// nor a response header field. Those fields are unused by the PDF viewer.
dictionary StreamInfo {
// The original URL that was intercepted.
DOMString originalUrl;
// The URL that the stream can be read from.
DOMString streamUrl;
// The ID of the tab that opened the stream. If the stream is not opened in
// a tab, it will be -1.
long tabId;
// Whether the stream is embedded within another document.
boolean embedded;
};
// Identical to mimeHandlerPrivate.StreamInfo.
dictionary PdfPluginAttributes {
// The background color in ARGB format for painting. Since the background
// color is an unsigned 32-bit integer which can be outside the range of
// "long" type, define it as a "double" type here.
double backgroundColor;
// Indicates whether the plugin allows to execute JavaScript and maybe XFA.
// Loading XFA for PDF forms will automatically be disabled if this flag is
// false.
boolean allowJavascript;
};
callback GetStreamInfoCallback = void(StreamInfo streamInfo);
callback IsAllowedLocalFileAccessCallback = void(boolean result);
callback IsPdfOcrAlwaysActiveCallback = void(boolean result);
callback OnPdfOcrPrefSetCallback = void(boolean result);
callback VoidCallback = void();
interface Functions {
// Returns the StreamInfo for the stream for this context if there is one.
static void getStreamInfo(
GetStreamInfoCallback callback);
// Determines if the given URL should be allowed to access local files from
// the PDF Viewer. |callback|: Called with true if URL should be allowed to
// access local files from the PDF Viewer, false otherwise.
[supportsPromises] static void isAllowedLocalFileAccess(
static void isAllowedLocalFileAccess(
DOMString url,
IsAllowedLocalFileAccessCallback callback);
// Determines if the preference for PDF OCR is set to run PDF OCR always.
// |callback|: Called with true if PDF OCR is set to be always active;
// false otherwise.
[supportsPromises] static void isPdfOcrAlwaysActive(
static void isPdfOcrAlwaysActive(
IsPdfOcrAlwaysActiveCallback callback);
// Sets the current tab title to `title` for a full-page PDF.
static void setPdfDocumentTitle(
DOMString title,
optional VoidCallback callback);
// Sets a pref value for PDF OCR.
// |value|: The new value of the pref.
// |callback|: The callback for whether the pref was set or not.
[supportsPromises] static void setPdfOcrPref(
static void setPdfOcrPref(
boolean value, OnPdfOcrPrefSetCallback callback);
// Sets PDF plugin attributes in the stream for this context if there is
// one.
static void setPdfPluginAttributes(
PdfPluginAttributes attributes,
optional VoidCallback callback);
};
interface Events {
// Fired when a pref value for PDF OCR has changed.
// |value| The pref value that changed.
static void onPdfOcrPrefChanged(boolean value);
// Fired when the browser wants the listener to perform a save.
// `streamUrl`: Unique ID for the instance that should perform the save.
static void onSave(DOMString streamUrl);
};
};