Browse Source

refactor: move PDF viewer to OOPIF (#41728)

https://issues.chromium.org/issues/40268279
Shelley Vohr 1 year ago
parent
commit
38ef9a7690

+ 4 - 4
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);

+ 7 - 5
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,

+ 118 - 0
shell/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_api.cc

@@ -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

+ 55 - 0
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_

+ 22 - 0
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<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);
 }

+ 54 - 3
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);
   };
 };