diff --git a/shell/browser/extensions/api/management/electron_management_api_delegate.cc b/shell/browser/extensions/api/management/electron_management_api_delegate.cc index 341ea9ee3fcc..f0d49e0f6ae6 100644 --- a/shell/browser/extensions/api/management/electron_management_api_delegate.cc +++ b/shell/browser/extensions/api/management/electron_management_api_delegate.cc @@ -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); diff --git a/shell/browser/extensions/api/management/electron_management_api_delegate.h b/shell/browser/extensions/api/management/electron_management_api_delegate.h index 19402d392d7c..fce21d57b19c 100644 --- a/shell/browser/extensions/api/management/electron_management_api_delegate.h +++ b/shell/browser/extensions/api/management/electron_management_api_delegate.h @@ -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, diff --git a/shell/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_api.cc b/shell/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_api.cc index e93bd81b710d..ec83bcd76809 100644 --- a/shell/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_api.cc +++ b/shell/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_api.cc @@ -6,10 +6,16 @@ #include +#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 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 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 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 params = + SetPdfPluginAttributes::Params::Create(args()); + EXTENSION_FUNCTION_VALIDATE(params); + + base::WeakPtr 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( + 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 diff --git a/shell/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_api.h b/shell/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_api.h index f2e1401e7601..468b3de3af36 100644 --- a/shell/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_api.h +++ b/shell/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_api.h @@ -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_ diff --git a/shell/browser/extensions/api/streams_private/streams_private_api.cc b/shell/browser/extensions/api/streams_private/streams_private_api.cc index 5960fd2e8257..9d015c151bfd 100644 --- a/shell/browser/extensions/api/streams_private/streams_private_api.cc +++ b/shell/browser/extensions/api/streams_private/streams_private_api.cc @@ -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( 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); } diff --git a/shell/common/extensions/api/pdf_viewer_private.idl b/shell/common/extensions/api/pdf_viewer_private.idl index 67eaab116f44..677a600c3df8 100644 --- a/shell/common/extensions/api/pdf_viewer_private.idl +++ b/shell/common/extensions/api/pdf_viewer_private.idl @@ -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); }; };