Browse Source

chore: implement no-op `chrome.action` extension APIs (#40222)

chore: implement no-op chrome.action extension APIs
Shelley Vohr 1 year ago
parent
commit
5b105f911f

+ 2 - 0
filenames.gni

@@ -709,6 +709,8 @@ filenames = {
   ]
 
   lib_sources_extensions = [
+    "shell/browser/extensions/api/extension_action/extension_action_api.cc",
+    "shell/browser/extensions/api/extension_action/extension_action_api.h",
     "shell/browser/extensions/api/management/electron_management_api_delegate.cc",
     "shell/browser/extensions/api/management/electron_management_api_delegate.h",
     "shell/browser/extensions/api/resources_private/resources_private_api.cc",

+ 197 - 0
shell/browser/extensions/api/extension_action/extension_action_api.cc

@@ -0,0 +1,197 @@
+// Copyright (c) 2023 Microsoft, GmbH
+// Use of this source code is governed by the MIT license that can be
+// found in the LICENSE file.
+
+#include "shell/browser/extensions/api/extension_action/extension_action_api.h"
+
+#include <stddef.h>
+
+#include <memory>
+#include <utility>
+
+#include "base/functional/bind.h"
+#include "base/lazy_instance.h"
+#include "base/observer_list.h"
+#include "extensions/browser/event_router.h"
+#include "extensions/browser/extension_prefs.h"
+#include "extensions/browser/extension_util.h"
+#include "extensions/common/mojom/view_type.mojom.h"
+
+using content::WebContents;
+
+namespace extensions {
+
+//
+// ExtensionActionAPI::Observer
+//
+
+void ExtensionActionAPI::Observer::OnExtensionActionUpdated(
+    ExtensionAction* extension_action,
+    content::WebContents* web_contents,
+    content::BrowserContext* browser_context) {}
+
+void ExtensionActionAPI::Observer::OnExtensionActionAPIShuttingDown() {}
+
+ExtensionActionAPI::Observer::~Observer() {}
+
+//
+// ExtensionActionAPI
+//
+
+static base::LazyInstance<BrowserContextKeyedAPIFactory<ExtensionActionAPI>>::
+    DestructorAtExit g_extension_action_api_factory = LAZY_INSTANCE_INITIALIZER;
+
+ExtensionActionAPI::ExtensionActionAPI(content::BrowserContext* context)
+    : browser_context_(context), extension_prefs_(nullptr) {}
+
+ExtensionActionAPI::~ExtensionActionAPI() {}
+
+// static
+BrowserContextKeyedAPIFactory<ExtensionActionAPI>*
+ExtensionActionAPI::GetFactoryInstance() {
+  return g_extension_action_api_factory.Pointer();
+}
+
+// static
+ExtensionActionAPI* ExtensionActionAPI::Get(content::BrowserContext* context) {
+  return BrowserContextKeyedAPIFactory<ExtensionActionAPI>::Get(context);
+}
+
+ExtensionPrefs* ExtensionActionAPI::GetExtensionPrefs() {
+  return nullptr;
+}
+
+void ExtensionActionAPI::Shutdown() {}
+
+//
+// ExtensionActionFunction
+//
+
+ExtensionActionFunction::ExtensionActionFunction() {}
+
+ExtensionActionFunction::~ExtensionActionFunction() {}
+
+ExtensionFunction::ResponseAction ExtensionActionFunction::Run() {
+  return RunExtensionAction();
+}
+
+ExtensionFunction::ResponseAction
+ExtensionActionShowFunction::RunExtensionAction() {
+  LOG(INFO) << "chrome.action.show is not supported in Electron";
+
+  return RespondNow(NoArguments());
+}
+
+ExtensionFunction::ResponseAction
+ExtensionActionHideFunction::RunExtensionAction() {
+  LOG(INFO) << "chrome.action.hide is not supported in Electron";
+
+  return RespondNow(NoArguments());
+}
+
+ExtensionFunction::ResponseAction
+ActionIsEnabledFunction::RunExtensionAction() {
+  LOG(INFO) << "chrome.action.isEnabled is not supported in Electron";
+
+  return RespondNow(WithArguments(false));
+}
+
+ExtensionFunction::ResponseAction
+ExtensionActionSetIconFunction::RunExtensionAction() {
+  LOG(INFO) << "chrome.action.setIcon is not supported in Electron";
+
+  return RespondNow(NoArguments());
+}
+
+ExtensionFunction::ResponseAction
+ExtensionActionSetTitleFunction::RunExtensionAction() {
+  LOG(INFO) << "chrome.action.setTitle is not supported in Electron";
+
+  return RespondNow(NoArguments());
+}
+
+ExtensionFunction::ResponseAction
+ExtensionActionSetPopupFunction::RunExtensionAction() {
+  LOG(INFO) << "chrome.action.setPopup is not supported in Electron";
+
+  return RespondNow(NoArguments());
+}
+
+ExtensionFunction::ResponseAction
+ExtensionActionSetBadgeTextFunction::RunExtensionAction() {
+  LOG(INFO) << "chrome.action.setBadgeText is not supported in Electron";
+
+  return RespondNow(NoArguments());
+}
+
+ExtensionFunction::ResponseAction
+ExtensionActionSetBadgeBackgroundColorFunction::RunExtensionAction() {
+  LOG(INFO)
+      << "chrome.action.setBadgeBackgroundColor is not supported in Electron";
+
+  return RespondNow(NoArguments());
+}
+
+ExtensionFunction::ResponseAction
+ActionSetBadgeTextColorFunction::RunExtensionAction() {
+  LOG(INFO) << "chrome.action.setBadgeTextColor is not supported in Electron";
+
+  return RespondNow(NoArguments());
+}
+
+ExtensionFunction::ResponseAction
+ExtensionActionGetTitleFunction::RunExtensionAction() {
+  LOG(INFO) << "chrome.action.getTitle is not supported in Electron";
+
+  return RespondNow(WithArguments(""));
+}
+
+ExtensionFunction::ResponseAction
+ExtensionActionGetPopupFunction::RunExtensionAction() {
+  LOG(INFO) << "chrome.action.getPopup is not supported in Electron";
+
+  return RespondNow(WithArguments(""));
+}
+
+ExtensionFunction::ResponseAction
+ExtensionActionGetBadgeTextFunction::RunExtensionAction() {
+  LOG(INFO) << "chrome.action.getBadgeText is not supported in Electron";
+
+  return RespondNow(WithArguments(""));
+}
+
+ExtensionFunction::ResponseAction
+ExtensionActionGetBadgeBackgroundColorFunction::RunExtensionAction() {
+  LOG(INFO)
+      << "chrome.action.getBadgeBackgroundColor is not supported in Electron";
+
+  base::Value::List list;
+  return RespondNow(WithArguments(std::move(list)));
+}
+
+ExtensionFunction::ResponseAction
+ActionGetBadgeTextColorFunction::RunExtensionAction() {
+  LOG(INFO) << "chrome.action.getBadgeTextColor is not supported in Electron";
+
+  base::Value::List list;
+  return RespondNow(WithArguments(std::move(list)));
+}
+
+ActionGetUserSettingsFunction::ActionGetUserSettingsFunction() = default;
+ActionGetUserSettingsFunction::~ActionGetUserSettingsFunction() = default;
+
+ExtensionFunction::ResponseAction ActionGetUserSettingsFunction::Run() {
+  LOG(INFO) << "chrome.action.getUserSettings is not supported in Electron";
+
+  base::Value::Dict ui_settings;
+  return RespondNow(WithArguments(std::move(ui_settings)));
+}
+
+ExtensionFunction::ResponseAction
+ActionOpenPopupFunction::RunExtensionAction() {
+  LOG(INFO) << "chrome.action.openPopup is not supported in Electron";
+
+  return RespondNow(NoArguments());
+}
+
+}  // namespace extensions

+ 520 - 0
shell/browser/extensions/api/extension_action/extension_action_api.h

@@ -0,0 +1,520 @@
+// Copyright (c) 2023 Microsoft, GmbH
+// Use of this source code is governed by the MIT license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_BROWSER_EXTENSIONS_API_EXTENSION_ACTION_EXTENSION_ACTION_API_H_
+#define SHELL_BROWSER_EXTENSIONS_API_EXTENSION_ACTION_EXTENSION_ACTION_API_H_
+
+#include <string>
+
+#include "base/memory/raw_ptr.h"
+#include "base/observer_list.h"
+#include "base/values.h"
+#include "extensions/browser/browser_context_keyed_api_factory.h"
+#include "extensions/browser/extension_action.h"
+#include "extensions/browser/extension_function.h"
+#include "extensions/browser/extension_host_registry.h"
+
+namespace content {
+class BrowserContext;
+class WebContents;
+}  // namespace content
+
+namespace extensions {
+
+class ExtensionHost;
+class ExtensionPrefs;
+
+class ExtensionActionAPI : public BrowserContextKeyedAPI {
+ public:
+  class Observer {
+   public:
+    virtual void OnExtensionActionUpdated(
+        ExtensionAction* extension_action,
+        content::WebContents* web_contents,
+        content::BrowserContext* browser_context);
+
+    virtual void OnExtensionActionAPIShuttingDown();
+
+   protected:
+    virtual ~Observer();
+  };
+
+  explicit ExtensionActionAPI(content::BrowserContext* context);
+
+  ExtensionActionAPI(const ExtensionActionAPI&) = delete;
+  ExtensionActionAPI& operator=(const ExtensionActionAPI&) = delete;
+
+  ~ExtensionActionAPI() override;
+
+  // Convenience method to get the instance for a profile.
+  static ExtensionActionAPI* Get(content::BrowserContext* context);
+
+  static BrowserContextKeyedAPIFactory<ExtensionActionAPI>*
+  GetFactoryInstance();
+
+  // Add or remove observers.
+  void AddObserver(Observer* observer) {}
+  void RemoveObserver(Observer* observer) {}
+
+  // Notifies that there has been a change in the given |extension_action|.
+  void NotifyChange(ExtensionAction* extension_action,
+                    content::WebContents* web_contents,
+                    content::BrowserContext* browser_context) {}
+
+  // Dispatches the onClicked event for extension that owns the given action.
+  void DispatchExtensionActionClicked(const ExtensionAction& extension_action,
+                                      content::WebContents* web_contents,
+                                      const Extension* extension) {}
+
+  // Clears the values for all ExtensionActions for the tab associated with the
+  // given |web_contents| (and signals that page actions changed).
+  void ClearAllValuesForTab(content::WebContents* web_contents) {}
+
+ private:
+  friend class BrowserContextKeyedAPIFactory<ExtensionActionAPI>;
+
+  ExtensionPrefs* GetExtensionPrefs();
+
+  // BrowserContextKeyedAPI implementation.
+  void Shutdown() override;
+  static const char* service_name() { return "ExtensionActionAPI"; }
+  static const bool kServiceRedirectedInIncognito = true;
+
+  raw_ptr<content::BrowserContext> browser_context_;
+
+  raw_ptr<ExtensionPrefs> extension_prefs_;
+};
+
+// Implementation of the browserAction and pageAction APIs.
+class ExtensionActionFunction : public ExtensionFunction {
+ protected:
+  ExtensionActionFunction();
+  ~ExtensionActionFunction() override;
+  ResponseAction Run() override;
+
+  virtual ResponseAction RunExtensionAction() = 0;
+};
+
+//
+// Implementations of each extension action API.
+//
+// pageAction and browserAction bindings are created for these by extending them
+// then declaring an EXTENSION_FUNCTION_NAME.
+//
+
+// show
+class ExtensionActionShowFunction : public ExtensionActionFunction {
+ protected:
+  ~ExtensionActionShowFunction() override {}
+  ResponseAction RunExtensionAction() override;
+};
+
+// hide
+class ExtensionActionHideFunction : public ExtensionActionFunction {
+ protected:
+  ~ExtensionActionHideFunction() override {}
+  ResponseAction RunExtensionAction() override;
+};
+
+// setIcon
+class ExtensionActionSetIconFunction : public ExtensionActionFunction {
+ public:
+  static void SetReportErrorForInvisibleIconForTesting(bool value);
+
+ protected:
+  ~ExtensionActionSetIconFunction() override {}
+  ResponseAction RunExtensionAction() override;
+};
+
+// setTitle
+class ExtensionActionSetTitleFunction : public ExtensionActionFunction {
+ protected:
+  ~ExtensionActionSetTitleFunction() override {}
+  ResponseAction RunExtensionAction() override;
+};
+
+// setPopup
+class ExtensionActionSetPopupFunction : public ExtensionActionFunction {
+ protected:
+  ~ExtensionActionSetPopupFunction() override {}
+  ResponseAction RunExtensionAction() override;
+};
+
+// setBadgeText
+class ExtensionActionSetBadgeTextFunction : public ExtensionActionFunction {
+ protected:
+  ~ExtensionActionSetBadgeTextFunction() override {}
+  ResponseAction RunExtensionAction() override;
+};
+
+// setBadgeBackgroundColor
+class ExtensionActionSetBadgeBackgroundColorFunction
+    : public ExtensionActionFunction {
+ protected:
+  ~ExtensionActionSetBadgeBackgroundColorFunction() override {}
+  ResponseAction RunExtensionAction() override;
+};
+
+// getTitle
+class ExtensionActionGetTitleFunction : public ExtensionActionFunction {
+ protected:
+  ~ExtensionActionGetTitleFunction() override {}
+  ResponseAction RunExtensionAction() override;
+};
+
+// getPopup
+class ExtensionActionGetPopupFunction : public ExtensionActionFunction {
+ protected:
+  ~ExtensionActionGetPopupFunction() override {}
+  ResponseAction RunExtensionAction() override;
+};
+
+// openPopup
+class ExtensionActionOpenPopupFunction : public ExtensionActionFunction {
+ protected:
+  ~ExtensionActionOpenPopupFunction() override {}
+  ResponseAction RunExtensionAction() override;
+};
+
+// getBadgeText
+class ExtensionActionGetBadgeTextFunction : public ExtensionActionFunction {
+ protected:
+  ~ExtensionActionGetBadgeTextFunction() override {}
+  ResponseAction RunExtensionAction() override;
+};
+
+// getBadgeBackgroundColor
+class ExtensionActionGetBadgeBackgroundColorFunction
+    : public ExtensionActionFunction {
+ protected:
+  ~ExtensionActionGetBadgeBackgroundColorFunction() override {}
+  ResponseAction RunExtensionAction() override;
+};
+
+//
+// action.* aliases for supported action APIs.
+//
+
+class ActionSetIconFunction : public ExtensionActionSetIconFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("action.setIcon", ACTION_SETICON)
+
+ protected:
+  ~ActionSetIconFunction() override {}
+};
+
+class ActionGetPopupFunction : public ExtensionActionGetPopupFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("action.getPopup", ACTION_GETPOPUP)
+
+ protected:
+  ~ActionGetPopupFunction() override {}
+};
+
+class ActionSetPopupFunction : public ExtensionActionSetPopupFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("action.setPopup", ACTION_SETPOPUP)
+
+ protected:
+  ~ActionSetPopupFunction() override {}
+};
+
+class ActionGetTitleFunction : public ExtensionActionGetTitleFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("action.getTitle", ACTION_GETTITLE)
+
+ protected:
+  ~ActionGetTitleFunction() override {}
+};
+
+class ActionSetTitleFunction : public ExtensionActionSetTitleFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("action.setTitle", ACTION_SETTITLE)
+
+ protected:
+  ~ActionSetTitleFunction() override {}
+};
+
+class ActionGetBadgeTextFunction : public ExtensionActionGetBadgeTextFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("action.getBadgeText", ACTION_GETBADGETEXT)
+
+ protected:
+  ~ActionGetBadgeTextFunction() override {}
+};
+
+class ActionSetBadgeTextFunction : public ExtensionActionSetBadgeTextFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("action.setBadgeText", ACTION_SETBADGETEXT)
+
+ protected:
+  ~ActionSetBadgeTextFunction() override {}
+};
+
+class ActionGetBadgeBackgroundColorFunction
+    : public ExtensionActionGetBadgeBackgroundColorFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("action.getBadgeBackgroundColor",
+                             ACTION_GETBADGEBACKGROUNDCOLOR)
+
+ protected:
+  ~ActionGetBadgeBackgroundColorFunction() override {}
+};
+
+class ActionSetBadgeBackgroundColorFunction
+    : public ExtensionActionSetBadgeBackgroundColorFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("action.setBadgeBackgroundColor",
+                             ACTION_SETBADGEBACKGROUNDCOLOR)
+
+ protected:
+  ~ActionSetBadgeBackgroundColorFunction() override {}
+};
+
+class ActionGetBadgeTextColorFunction : public ExtensionActionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("action.getBadgeTextColor",
+                             ACTION_GETBADGETEXTCOLOR)
+
+ protected:
+  ~ActionGetBadgeTextColorFunction() override = default;
+  ResponseAction RunExtensionAction() override;
+};
+
+class ActionSetBadgeTextColorFunction : public ExtensionActionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("action.setBadgeTextColor",
+                             ACTION_SETBADGETEXTCOLOR)
+
+ protected:
+  ~ActionSetBadgeTextColorFunction() override = default;
+  ResponseAction RunExtensionAction() override;
+};
+
+class ActionEnableFunction : public ExtensionActionShowFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("action.enable", ACTION_ENABLE)
+
+ protected:
+  ~ActionEnableFunction() override {}
+};
+
+class ActionDisableFunction : public ExtensionActionHideFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("action.disable", ACTION_DISABLE)
+
+ protected:
+  ~ActionDisableFunction() override {}
+};
+
+class ActionIsEnabledFunction : public ExtensionActionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("action.isEnabled", ACTION_ISENABLED)
+
+ protected:
+  ~ActionIsEnabledFunction() override = default;
+  ResponseAction RunExtensionAction() override;
+};
+
+class ActionGetUserSettingsFunction : public ExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("action.getUserSettings", ACTION_GETUSERSETTINGS)
+
+  ActionGetUserSettingsFunction();
+  ActionGetUserSettingsFunction(const ActionGetUserSettingsFunction&) = delete;
+  ActionGetUserSettingsFunction& operator=(
+      const ActionGetUserSettingsFunction&) = delete;
+
+  ResponseAction Run() override;
+
+ protected:
+  ~ActionGetUserSettingsFunction() override;
+};
+
+class ActionOpenPopupFunction : public ExtensionActionOpenPopupFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("action.openPopup", ACTION_OPENPOPUP)
+
+ protected:
+  ~ActionOpenPopupFunction() override = default;
+  ResponseAction RunExtensionAction() override;
+};
+
+//
+// browserAction.* aliases for supported browserAction APIs.
+//
+
+class BrowserActionSetIconFunction : public ExtensionActionSetIconFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("browserAction.setIcon", BROWSERACTION_SETICON)
+
+ protected:
+  ~BrowserActionSetIconFunction() override {}
+};
+
+class BrowserActionSetTitleFunction : public ExtensionActionSetTitleFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("browserAction.setTitle", BROWSERACTION_SETTITLE)
+
+ protected:
+  ~BrowserActionSetTitleFunction() override {}
+};
+
+class BrowserActionSetPopupFunction : public ExtensionActionSetPopupFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("browserAction.setPopup", BROWSERACTION_SETPOPUP)
+
+ protected:
+  ~BrowserActionSetPopupFunction() override {}
+};
+
+class BrowserActionGetTitleFunction : public ExtensionActionGetTitleFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("browserAction.getTitle", BROWSERACTION_GETTITLE)
+
+ protected:
+  ~BrowserActionGetTitleFunction() override {}
+};
+
+class BrowserActionGetPopupFunction : public ExtensionActionGetPopupFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("browserAction.getPopup", BROWSERACTION_GETPOPUP)
+
+ protected:
+  ~BrowserActionGetPopupFunction() override {}
+};
+
+class BrowserActionSetBadgeTextFunction
+    : public ExtensionActionSetBadgeTextFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("browserAction.setBadgeText",
+                             BROWSERACTION_SETBADGETEXT)
+
+ protected:
+  ~BrowserActionSetBadgeTextFunction() override {}
+};
+
+class BrowserActionSetBadgeBackgroundColorFunction
+    : public ExtensionActionSetBadgeBackgroundColorFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("browserAction.setBadgeBackgroundColor",
+                             BROWSERACTION_SETBADGEBACKGROUNDCOLOR)
+
+ protected:
+  ~BrowserActionSetBadgeBackgroundColorFunction() override {}
+};
+
+class BrowserActionGetBadgeTextFunction
+    : public ExtensionActionGetBadgeTextFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("browserAction.getBadgeText",
+                             BROWSERACTION_GETBADGETEXT)
+
+ protected:
+  ~BrowserActionGetBadgeTextFunction() override {}
+};
+
+class BrowserActionGetBadgeBackgroundColorFunction
+    : public ExtensionActionGetBadgeBackgroundColorFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("browserAction.getBadgeBackgroundColor",
+                             BROWSERACTION_GETBADGEBACKGROUNDCOLOR)
+
+ protected:
+  ~BrowserActionGetBadgeBackgroundColorFunction() override {}
+};
+
+class BrowserActionEnableFunction : public ExtensionActionShowFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("browserAction.enable", BROWSERACTION_ENABLE)
+
+ protected:
+  ~BrowserActionEnableFunction() override {}
+};
+
+class BrowserActionDisableFunction : public ExtensionActionHideFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("browserAction.disable", BROWSERACTION_DISABLE)
+
+ protected:
+  ~BrowserActionDisableFunction() override {}
+};
+
+class BrowserActionOpenPopupFunction : public ExtensionActionOpenPopupFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("browserAction.openPopup",
+                             BROWSERACTION_OPEN_POPUP)
+
+ protected:
+  ~BrowserActionOpenPopupFunction() override {}
+};
+
+}  // namespace extensions
+
+//
+// pageAction.* aliases for supported pageAction APIs.
+//
+
+class PageActionShowFunction : public extensions::ExtensionActionShowFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("pageAction.show", PAGEACTION_SHOW)
+
+ protected:
+  ~PageActionShowFunction() override {}
+};
+
+class PageActionHideFunction : public extensions::ExtensionActionHideFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("pageAction.hide", PAGEACTION_HIDE)
+
+ protected:
+  ~PageActionHideFunction() override {}
+};
+
+class PageActionSetIconFunction
+    : public extensions::ExtensionActionSetIconFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("pageAction.setIcon", PAGEACTION_SETICON)
+
+ protected:
+  ~PageActionSetIconFunction() override {}
+};
+
+class PageActionSetTitleFunction
+    : public extensions::ExtensionActionSetTitleFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("pageAction.setTitle", PAGEACTION_SETTITLE)
+
+ protected:
+  ~PageActionSetTitleFunction() override {}
+};
+
+class PageActionSetPopupFunction
+    : public extensions::ExtensionActionSetPopupFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("pageAction.setPopup", PAGEACTION_SETPOPUP)
+
+ protected:
+  ~PageActionSetPopupFunction() override {}
+};
+
+class PageActionGetTitleFunction
+    : public extensions::ExtensionActionGetTitleFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("pageAction.getTitle", PAGEACTION_GETTITLE)
+
+ protected:
+  ~PageActionGetTitleFunction() override {}
+};
+
+class PageActionGetPopupFunction
+    : public extensions::ExtensionActionGetPopupFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("pageAction.getPopup", PAGEACTION_GETPOPUP)
+
+ protected:
+  ~PageActionGetPopupFunction() override {}
+};
+
+#endif  // SHELL_BROWSER_EXTENSIONS_API_EXTENSION_ACTION_EXTENSION_ACTION_API_H_

+ 1 - 0
shell/browser/extensions/electron_extensions_browser_api_provider.cc

@@ -6,6 +6,7 @@
 
 #include "extensions/browser/api/i18n/i18n_api.h"
 #include "extensions/browser/extension_function_registry.h"
+#include "shell/browser/extensions/api/extension_action/extension_action_api.h"
 #include "shell/browser/extensions/api/generated_api_registration.h"
 #include "shell/browser/extensions/api/scripting/scripting_api.h"
 #include "shell/browser/extensions/api/tabs/tabs_api.h"

+ 1 - 0
shell/common/extensions/api/BUILD.gn

@@ -37,6 +37,7 @@ group("extensions_features") {
 
 generated_json_strings("generated_api_json_strings") {
   sources = [
+    "action.json",
     "extension.json",
     "resources_private.idl",
     "scripting.idl",

+ 483 - 0
shell/common/extensions/api/action.json

@@ -0,0 +1,483 @@
+// Copyright (c) 2023 Microsoft, GmbH
+// Use of this source code is governed by the MIT license that can be
+// found in the LICENSE file.
+[
+  {
+    "namespace": "action",
+    "description": "Use the <code>chrome.action</code> API to control the extension's icon in the Google Chrome toolbar.",
+    "compiler_options": {
+      "implemented_in": "shell/browser/extensions/api/extension_action/extension_action_api.h"
+    },
+    "types": [
+      {
+        "id": "TabDetails",
+        "type": "object",
+        "properties": {
+          "tabId": {
+            "type": "integer",
+            "optional": true,
+            "minimum": 0,
+            "description": "The ID of the tab to query state for. If no tab is specified, the non-tab-specific state is returned."
+          }
+        }
+      },
+      {
+        "id": "UserSettings",
+        "type": "object",
+        "properties": {
+          "isOnToolbar": {
+            "type": "boolean",
+            "description": "Whether the extension's action icon is visible on browser windows' top-level toolbar (i.e., whether the extension has been 'pinned' by the user)."
+          }
+        },
+        "description": "The collection of user-specified settings relating to an extension's action."
+      },
+      {
+        "id": "OpenPopupOptions",
+        "type": "object",
+        "properties": {
+          "windowId": {
+            "type": "integer",
+            "description": "The id of the window to open the action popup in. Defaults to the currently-active window if unspecified.",
+            "optional": true
+          }
+        }
+      }
+    ],
+    "functions": [
+      {
+        "name": "setTitle",
+        "deprecated": "chrome.action.setTitle is not supported in Electron",
+        "type": "function",
+        "description": "Sets the title of the action. This shows up in the tooltip.",
+        "parameters": [
+          {
+            "name": "details",
+            "type": "object",
+            "properties": {
+              "title": {
+                "type": "string",
+                "description": "The string the action should display when moused over."
+              },
+              "tabId": {
+                "type": "integer",
+                "optional": true,
+                "minimum": 0,
+                "description": "Limits the change to when a particular tab is selected. Automatically resets when the tab is closed."
+              }
+            }
+          }
+        ],
+        "returns_async": {
+          "name": "callback",
+          "parameters": [],
+          "optional": true
+        }
+      },
+      {
+        "name": "getTitle",
+        "deprecated": "chrome.action.getTitle is not supported in Electron",
+        "type": "function",
+        "description": "Gets the title of the action.",
+        "parameters": [
+          {
+            "name": "details",
+            "$ref": "TabDetails"
+          }
+        ],
+        "returns_async": {
+          "name": "callback",
+          "parameters": [
+            {
+              "name": "result",
+              "type": "string"
+            }
+          ]
+        }
+      },
+      {
+        "name": "setIcon",
+        "deprecated": "chrome.action.setIcon is not supported in Electron",
+        "type": "function",
+        "description": "Sets the icon for the action. The icon can be specified either as the path to an image file or as the pixel data from a canvas element, or as dictionary of either one of those. Either the <b>path</b> or the <b>imageData</b> property must be specified.",
+        "parameters": [
+          {
+            "name": "details",
+            "type": "object",
+            "properties": {
+              "imageData": {
+                "choices": [
+                  {
+                    "$ref": "browserAction.ImageDataType"
+                  },
+                  {
+                    "type": "object",
+                    "additionalProperties": {
+                      "type": "any"
+                    }
+                  }
+                ],
+                "optional": true,
+                "description": "Either an ImageData object or a dictionary {size -> ImageData} representing icon to be set. If the icon is specified as a dictionary, the actual image to be used is chosen depending on screen's pixel density. If the number of image pixels that fit into one screen space unit equals <code>scale</code>, then image with size <code>scale</code> * n will be selected, where n is the size of the icon in the UI. At least one image must be specified. Note that 'details.imageData = foo' is equivalent to 'details.imageData = {'16': foo}'"
+              },
+              "path": {
+                "choices": [
+                  {
+                    "type": "string"
+                  },
+                  {
+                    "type": "object",
+                    "additionalProperties": {
+                      "type": "any"
+                    }
+                  }
+                ],
+                "optional": true,
+                "description": "Either a relative image path or a dictionary {size -> relative image path} pointing to icon to be set. If the icon is specified as a dictionary, the actual image to be used is chosen depending on screen's pixel density. If the number of image pixels that fit into one screen space unit equals <code>scale</code>, then image with size <code>scale</code> * n will be selected, where n is the size of the icon in the UI. At least one image must be specified. Note that 'details.path = foo' is equivalent to 'details.path = {'16': foo}'"
+              },
+              "tabId": {
+                "type": "integer",
+                "optional": true,
+                "minimum": 0,
+                "description": "Limits the change to when a particular tab is selected. Automatically resets when the tab is closed."
+              }
+            }
+          }
+        ],
+        "returns_async": {
+          "name": "callback",
+          "optional": true,
+          "parameters": []
+        }
+      },
+      {
+        "name": "setPopup",
+        "deprecated": "chrome.action.setPopup is not supported in Electron",
+        "type": "function",
+        "description": "Sets the HTML document to be opened as a popup when the user clicks on the action's icon.",
+        "parameters": [
+          {
+            "name": "details",
+            "type": "object",
+            "properties": {
+              "tabId": {
+                "type": "integer",
+                "optional": true,
+                "minimum": 0,
+                "description": "Limits the change to when a particular tab is selected. Automatically resets when the tab is closed."
+              },
+              "popup": {
+                "type": "string",
+                "description": "The relative path to the HTML file to show in a popup. If set to the empty string (<code>''</code>), no popup is shown."
+              }
+            }
+          }
+        ],
+        "returns_async": {
+          "name": "callback",
+          "parameters": [],
+          "optional": true
+        }
+      },
+      {
+        "name": "getPopup",
+        "deprecated": "chrome.action.getPopup is not supported in Electron",
+        "type": "function",
+        "description": "Gets the html document set as the popup for this action.",
+        "parameters": [
+          {
+            "name": "details",
+            "$ref": "TabDetails"
+          }
+        ],
+        "returns_async": {
+          "name": "callback",
+          "parameters": [
+            {
+              "name": "result",
+              "type": "string"
+            }
+          ]
+        }
+      },
+      {
+        "name": "setBadgeText",
+        "deprecated": "chrome.action.setBadgeText is not supported in Electron",
+        "type": "function",
+        "description": "Sets the badge text for the action. The badge is displayed on top of the icon.",
+        "parameters": [
+          {
+            "name": "details",
+            "type": "object",
+            "properties": {
+              "text": {
+                "type": "string",
+                "optional": true,
+                "description": "Any number of characters can be passed, but only about four can fit in the space. If an empty string (<code>''</code>) is passed, the badge text is cleared.  If <code>tabId</code> is specified and <code>text</code> is null, the text for the specified tab is cleared and defaults to the global badge text."
+              },
+              "tabId": {
+                "type": "integer",
+                "optional": true,
+                "minimum": 0,
+                "description": "Limits the change to when a particular tab is selected. Automatically resets when the tab is closed."
+              }
+            }
+          }
+        ],
+        "returns_async": {
+          "name": "callback",
+          "parameters": [],
+          "optional": true
+        }
+      },
+      {
+        "name": "getBadgeText",
+        "deprecated": "chrome.action.getBadgeText is not supported in Electron",
+        "type": "function",
+        "description": "Gets the badge text of the action. If no tab is specified, the non-tab-specific badge text is returned. If <a href='declarativeNetRequest#setExtensionActionOptions'>displayActionCountAsBadgeText</a> is enabled, a placeholder text will be returned unless the <a href='declare_permissions#declarativeNetRequestFeedback'>declarativeNetRequestFeedback</a> permission is present or tab-specific badge text was provided.",
+        "parameters": [
+          {
+            "name": "details",
+            "$ref": "TabDetails"
+          }
+        ],
+        "returns_async": {
+          "name": "callback",
+          "parameters": [
+            {
+              "name": "result",
+              "type": "string"
+            }
+          ]
+        }
+      },
+      {
+        "name": "setBadgeBackgroundColor",
+        "deprecated": "chrome.action.setBadgeBackgroundColor is not supported in Electron",
+        "type": "function",
+        "description": "Sets the background color for the badge.",
+        "parameters": [
+          {
+            "name": "details",
+            "type": "object",
+            "properties": {
+              "color": {
+                "description": "An array of four integers in the range [0,255] that make up the RGBA color of the badge. For example, opaque red is <code>[255, 0, 0, 255]</code>. Can also be a string with a CSS value, with opaque red being <code>#FF0000</code> or <code>#F00</code>.",
+                "choices": [
+                  {
+                    "type": "string"
+                  },
+                  {
+                    "$ref": "browserAction.ColorArray"
+                  }
+                ]
+              },
+              "tabId": {
+                "type": "integer",
+                "optional": true,
+                "minimum": 0,
+                "description": "Limits the change to when a particular tab is selected. Automatically resets when the tab is closed."
+              }
+            }
+          }
+        ],
+        "returns_async": {
+          "name": "callback",
+          "parameters": [],
+          "optional": true
+        }
+      },
+      {
+        "name": "getBadgeBackgroundColor",
+        "deprecated": "chrome.action.getBadgeBackgroundColor is not supported in Electron",
+        "type": "function",
+        "description": "Gets the background color of the action.",
+        "parameters": [
+          {
+            "name": "details",
+            "$ref": "TabDetails"
+          }
+        ],
+        "returns_async": {
+          "name": "callback",
+          "parameters": [
+            {
+              "name": "result",
+              "$ref": "browserAction.ColorArray"
+            }
+          ]
+        }
+      },
+      {
+        "name": "setBadgeTextColor",
+        "deprecated": "chrome.action.setBadgeTextColor is not supported in Electron",
+        "type": "function",
+        "description": "Sets the text color for the badge.",
+        "parameters": [
+          {
+            "name": "details",
+            "type": "object",
+            "properties": {
+              "color": {
+                "description": "An array of four integers in the range [0,255] that make up the RGBA color of the badge. For example, opaque red is <code>[255, 0, 0, 255]</code>. Can also be a string with a CSS value, with opaque red being <code>#FF0000</code> or <code>#F00</code>. Not setting this value will cause a color to be automatically chosen that will contrast with the badge's background color so the text will be visible. Colors with alpha values equivalent to 0 will not be set and will return an error.",
+                "choices": [
+                  {
+                    "type": "string"
+                  },
+                  {
+                    "$ref": "browserAction.ColorArray"
+                  }
+                ]
+              },
+              "tabId": {
+                "type": "integer",
+                "optional": true,
+                "minimum": 0,
+                "description": "Limits the change to when a particular tab is selected. Automatically resets when the tab is closed."
+              }
+            }
+          }
+        ],
+        "returns_async": {
+          "name": "callback",
+          "parameters": [],
+          "optional": true
+        }
+      },
+      {
+        "name": "getBadgeTextColor",
+        "deprecated": "chrome.action.getBadgeTextColor is not supported in Electron",
+        "type": "function",
+        "description": "Gets the text color of the action.",
+        "parameters": [
+          {
+            "name": "details",
+            "$ref": "TabDetails"
+          }
+        ],
+        "returns_async": {
+          "name": "callback",
+          "parameters": [
+            {
+              "name": "result",
+              "$ref": "browserAction.ColorArray"
+            }
+          ]
+        }
+      },
+      {
+        "name": "enable",
+        "deprecated": "chrome.action.enable is not supported in Electron",
+        "type": "function",
+        "description": "Enables the action for a tab. By default, actions are enabled.",
+        "parameters": [
+          {
+            "type": "integer",
+            "optional": true,
+            "name": "tabId",
+            "minimum": 0,
+            "description": "The id of the tab for which you want to modify the action."
+          }
+        ],
+        "returns_async": {
+          "name": "callback",
+          "parameters": [],
+          "optional": true
+        }
+      },
+      {
+        "name": "disable",
+        "deprecated": "chrome.action.disable is not supported in Electron",
+        "type": "function",
+        "description": "Disables the action for a tab.",
+        "parameters": [
+          {
+            "type": "integer",
+            "optional": true,
+            "name": "tabId",
+            "minimum": 0,
+            "description": "The id of the tab for which you want to modify the action."
+          }
+        ],
+        "returns_async": {
+          "name": "callback",
+          "parameters": [],
+          "optional": true
+        }
+      },
+      {
+        "name": "isEnabled",
+        "deprecated": "chrome.action.isEnabled is not supported in Electron",
+        "type": "function",
+        "description": "Indicates whether the extension action is enabled for a tab (or globally if no <code>tabId</code> is provided). Actions enabled using only $(ref:declarativeContent) always return false.",
+        "parameters": [
+          {
+            "type": "integer",
+            "optional": true,
+            "name": "tabId",
+            "minimum": 0,
+            "description": "The id of the tab for which you want check enabled status."
+          }
+        ],
+        "returns_async": {
+          "name": "callback",
+          "parameters": [
+            {
+              "name": "isEnabled",
+              "type": "boolean",
+              "description": "True if the extension action is enabled."
+            }
+          ]
+        }
+      },
+      {
+        "name": "getUserSettings",
+        "deprecated": "chrome.action.getUserSettings is not supported in Electron",
+        "type": "function",
+        "description": "Returns the user-specified settings relating to an extension's action.",
+        "parameters": [],
+        "returns_async": {
+          "name": "callback",
+          "parameters": [
+            {
+              "name": "userSettings",
+              "$ref": "UserSettings"
+            }
+          ]
+        }
+      },
+      {
+        "name": "openPopup",
+        "deprecated": "chrome.action.openPopup is not supported in Electron",
+        "type": "function",
+        "description": "Opens the extension's popup.",
+        "parameters": [
+          {
+            "$ref": "OpenPopupOptions",
+            "name": "options",
+            "optional": true,
+            "description": "Specifies options for opening the popup."
+          }
+        ],
+        "returns_async": {
+          "name": "callback",
+          "parameters": []
+        }
+      }
+    ],
+    "events": [
+      {
+        "name": "onClicked",
+        "deprecated": "chrome.action.onClicked is not supported in Electron",
+        "type": "function",
+        "description": "Fired when an action icon is clicked.  This event will not fire if the action has a popup.",
+        "parameters": [
+          {
+            "name": "tab",
+            "$ref": "tabs.Tab"
+          }
+        ]
+      }
+    ]
+  }
+]