summaryrefslogtreecommitdiffstats
path: root/dom/commandhandler
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /dom/commandhandler
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/commandhandler')
-rw-r--r--dom/commandhandler/moz.build38
-rw-r--r--dom/commandhandler/nsBaseCommandController.cpp203
-rw-r--r--dom/commandhandler/nsBaseCommandController.h56
-rw-r--r--dom/commandhandler/nsCommandManager.cpp240
-rw-r--r--dom/commandhandler/nsCommandManager.h69
-rw-r--r--dom/commandhandler/nsCommandParams.cpp303
-rw-r--r--dom/commandhandler/nsCommandParams.h166
-rw-r--r--dom/commandhandler/nsControllerCommandTable.cpp228
-rw-r--r--dom/commandhandler/nsControllerCommandTable.h52
-rw-r--r--dom/commandhandler/nsICommandManager.idl119
-rw-r--r--dom/commandhandler/nsICommandParams.idl92
-rw-r--r--dom/commandhandler/nsIControllerCommand.idl52
-rw-r--r--dom/commandhandler/nsIControllerCommandTable.idl102
-rw-r--r--dom/commandhandler/nsIControllerContext.idl22
14 files changed, 1742 insertions, 0 deletions
diff --git a/dom/commandhandler/moz.build b/dom/commandhandler/moz.build
new file mode 100644
index 0000000000..1aaa78e8af
--- /dev/null
+++ b/dom/commandhandler/moz.build
@@ -0,0 +1,38 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+with Files("**"):
+ BUG_COMPONENT = ("Core", "DOM: UI Events & Focus Handling")
+
+EXPORTS += [
+ "nsBaseCommandController.h",
+ "nsCommandManager.h",
+ "nsCommandParams.h",
+ "nsControllerCommandTable.h",
+]
+
+XPIDL_SOURCES += [
+ "nsICommandManager.idl",
+ "nsICommandParams.idl",
+ "nsIControllerCommand.idl",
+ "nsIControllerCommandTable.idl",
+ "nsIControllerContext.idl",
+]
+
+XPIDL_MODULE = "commandhandler"
+
+UNIFIED_SOURCES += [
+ "nsBaseCommandController.cpp",
+ "nsCommandManager.cpp",
+ "nsCommandParams.cpp",
+ "nsControllerCommandTable.cpp",
+]
+
+LOCAL_INCLUDES += [
+ "/dom/base",
+]
+
+FINAL_LIBRARY = "xul"
diff --git a/dom/commandhandler/nsBaseCommandController.cpp b/dom/commandhandler/nsBaseCommandController.cpp
new file mode 100644
index 0000000000..2d8eadb13f
--- /dev/null
+++ b/dom/commandhandler/nsBaseCommandController.cpp
@@ -0,0 +1,203 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsString.h"
+#include "nsIWeakReferenceUtils.h"
+#include "nsBaseCommandController.h"
+#include "nsControllerCommandTable.h"
+
+NS_IMPL_ADDREF(nsBaseCommandController)
+NS_IMPL_RELEASE(nsBaseCommandController)
+
+NS_INTERFACE_MAP_BEGIN(nsBaseCommandController)
+ NS_INTERFACE_MAP_ENTRY(nsIController)
+ NS_INTERFACE_MAP_ENTRY(nsICommandController)
+ NS_INTERFACE_MAP_ENTRY(nsIControllerContext)
+ NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIControllerContext)
+NS_INTERFACE_MAP_END
+
+nsBaseCommandController::nsBaseCommandController(
+ nsControllerCommandTable* aControllerCommandTable)
+ : mCommandContextRawPtr(nullptr), mCommandTable(aControllerCommandTable) {}
+
+nsBaseCommandController::~nsBaseCommandController() = default;
+
+NS_IMETHODIMP
+nsBaseCommandController::SetCommandContext(nsISupports* aCommandContext) {
+ mCommandContextWeakPtr = nullptr;
+ mCommandContextRawPtr = nullptr;
+
+ if (aCommandContext) {
+ nsCOMPtr<nsISupportsWeakReference> weak =
+ do_QueryInterface(aCommandContext);
+ if (weak) {
+ nsresult rv =
+ weak->GetWeakReference(getter_AddRefs(mCommandContextWeakPtr));
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else {
+ mCommandContextRawPtr = aCommandContext;
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBaseCommandController::GetInterface(const nsIID& aIID, void** aResult) {
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ if (NS_SUCCEEDED(QueryInterface(aIID, aResult))) {
+ return NS_OK;
+ }
+
+ if (aIID.Equals(NS_GET_IID(nsIControllerCommandTable))) {
+ if (mCommandTable) {
+ *aResult =
+ do_AddRef(static_cast<nsIControllerCommandTable*>(mCommandTable))
+ .take();
+ return NS_OK;
+ }
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ return NS_NOINTERFACE;
+}
+
+/* =======================================================================
+ * nsIController
+ * ======================================================================= */
+
+NS_IMETHODIMP
+nsBaseCommandController::IsCommandEnabled(const char* aCommand, bool* aResult) {
+ NS_ENSURE_ARG_POINTER(aCommand);
+ NS_ENSURE_ARG_POINTER(aResult);
+ NS_ENSURE_STATE(mCommandTable);
+
+ nsISupports* context = mCommandContextRawPtr;
+ nsCOMPtr<nsISupports> weak;
+ if (!context) {
+ weak = do_QueryReferent(mCommandContextWeakPtr);
+ context = weak;
+ }
+ return mCommandTable->IsCommandEnabled(aCommand, context, aResult);
+}
+
+NS_IMETHODIMP
+nsBaseCommandController::SupportsCommand(const char* aCommand, bool* aResult) {
+ NS_ENSURE_ARG_POINTER(aCommand);
+ NS_ENSURE_ARG_POINTER(aResult);
+ NS_ENSURE_STATE(mCommandTable);
+
+ nsISupports* context = mCommandContextRawPtr;
+ nsCOMPtr<nsISupports> weak;
+ if (!context) {
+ weak = do_QueryReferent(mCommandContextWeakPtr);
+ context = weak;
+ }
+ return mCommandTable->SupportsCommand(aCommand, context, aResult);
+}
+
+NS_IMETHODIMP
+nsBaseCommandController::DoCommand(const char* aCommand) {
+ NS_ENSURE_ARG_POINTER(aCommand);
+ NS_ENSURE_STATE(mCommandTable);
+
+ nsCOMPtr<nsISupports> context = mCommandContextRawPtr;
+ if (!context) {
+ context = do_QueryReferent(mCommandContextWeakPtr);
+ }
+ RefPtr<nsControllerCommandTable> commandTable(mCommandTable);
+ return commandTable->DoCommand(aCommand, context);
+}
+
+NS_IMETHODIMP
+nsBaseCommandController::DoCommandWithParams(const char* aCommand,
+ nsICommandParams* aParams) {
+ NS_ENSURE_ARG_POINTER(aCommand);
+ NS_ENSURE_STATE(mCommandTable);
+
+ nsCOMPtr<nsISupports> context = mCommandContextRawPtr;
+ if (!context) {
+ context = do_QueryReferent(mCommandContextWeakPtr);
+ }
+ RefPtr<nsControllerCommandTable> commandTable(mCommandTable);
+ return commandTable->DoCommandParams(aCommand, aParams, context);
+}
+
+NS_IMETHODIMP
+nsBaseCommandController::GetCommandStateWithParams(const char* aCommand,
+ nsICommandParams* aParams) {
+ NS_ENSURE_ARG_POINTER(aCommand);
+ NS_ENSURE_STATE(mCommandTable);
+
+ nsISupports* context = mCommandContextRawPtr;
+ nsCOMPtr<nsISupports> weak;
+ if (!context) {
+ weak = do_QueryReferent(mCommandContextWeakPtr);
+ context = weak;
+ }
+ return mCommandTable->GetCommandState(aCommand, aParams, context);
+}
+
+NS_IMETHODIMP
+nsBaseCommandController::OnEvent(const char* aEventName) {
+ NS_ENSURE_ARG_POINTER(aEventName);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBaseCommandController::GetSupportedCommands(nsTArray<nsCString>& aCommands) {
+ NS_ENSURE_STATE(mCommandTable);
+ return mCommandTable->GetSupportedCommands(aCommands);
+}
+
+using CommandTableCreatorFn = already_AddRefed<nsControllerCommandTable> (*)();
+
+static already_AddRefed<nsBaseCommandController>
+CreateControllerWithSingletonCommandTable(CommandTableCreatorFn aCreatorFn) {
+ RefPtr<nsControllerCommandTable> commandTable = aCreatorFn();
+ if (!commandTable) {
+ return nullptr;
+ }
+
+ // this is a singleton; make it immutable
+ commandTable->MakeImmutable();
+
+ RefPtr<nsBaseCommandController> commandController =
+ new nsBaseCommandController(commandTable);
+ return commandController.forget();
+}
+
+already_AddRefed<nsBaseCommandController>
+nsBaseCommandController::CreateWindowController() {
+ return CreateControllerWithSingletonCommandTable(
+ nsControllerCommandTable::CreateWindowCommandTable);
+}
+
+already_AddRefed<nsBaseCommandController>
+nsBaseCommandController::CreateEditorController() {
+ return CreateControllerWithSingletonCommandTable(
+ nsControllerCommandTable::CreateEditorCommandTable);
+}
+
+already_AddRefed<nsBaseCommandController>
+nsBaseCommandController::CreateEditingController() {
+ return CreateControllerWithSingletonCommandTable(
+ nsControllerCommandTable::CreateEditingCommandTable);
+}
+
+already_AddRefed<nsBaseCommandController>
+nsBaseCommandController::CreateHTMLEditorController() {
+ return CreateControllerWithSingletonCommandTable(
+ nsControllerCommandTable::CreateHTMLEditorCommandTable);
+}
+
+already_AddRefed<nsBaseCommandController>
+nsBaseCommandController::CreateHTMLEditorDocStateController() {
+ return CreateControllerWithSingletonCommandTable(
+ nsControllerCommandTable::CreateHTMLEditorDocStateCommandTable);
+}
diff --git a/dom/commandhandler/nsBaseCommandController.h b/dom/commandhandler/nsBaseCommandController.h
new file mode 100644
index 0000000000..1a3601be1d
--- /dev/null
+++ b/dom/commandhandler/nsBaseCommandController.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsBaseCommandController_h__
+#define nsBaseCommandController_h__
+
+#include "nsIController.h"
+#include "nsIControllerContext.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIWeakReferenceUtils.h"
+#include "nsControllerCommandTable.h"
+
+// The base editor controller is used for both text widgets, and all other text
+// and html editing
+class nsBaseCommandController final : public nsIController,
+ public nsIControllerContext,
+ public nsIInterfaceRequestor,
+ public nsICommandController {
+ public:
+ /**
+ * The default constructor initializes the instance with new
+ * nsControllerCommandTable. The other constructor does it with
+ * the given aControllerCommandTable.
+ */
+ explicit nsBaseCommandController(
+ nsControllerCommandTable* aControllerCommandTable =
+ new nsControllerCommandTable());
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSICONTROLLER
+ NS_DECL_NSICOMMANDCONTROLLER
+ NS_DECL_NSICONTROLLERCONTEXT
+ NS_DECL_NSIINTERFACEREQUESTOR
+
+ static already_AddRefed<nsBaseCommandController> CreateWindowController();
+ static already_AddRefed<nsBaseCommandController> CreateEditorController();
+ static already_AddRefed<nsBaseCommandController> CreateEditingController();
+ static already_AddRefed<nsBaseCommandController> CreateHTMLEditorController();
+ static already_AddRefed<nsBaseCommandController>
+ CreateHTMLEditorDocStateController();
+
+ protected:
+ virtual ~nsBaseCommandController();
+
+ private:
+ nsWeakPtr mCommandContextWeakPtr;
+ nsISupports* mCommandContextRawPtr;
+
+ // Our reference to the command manager
+ RefPtr<nsControllerCommandTable> mCommandTable;
+};
+
+#endif /* nsBaseCommandController_h_ */
diff --git a/dom/commandhandler/nsCommandManager.cpp b/dom/commandhandler/nsCommandManager.cpp
new file mode 100644
index 0000000000..1c8d7d79d2
--- /dev/null
+++ b/dom/commandhandler/nsCommandManager.cpp
@@ -0,0 +1,240 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsString.h"
+
+#include "nsIController.h"
+#include "nsIControllers.h"
+#include "nsIObserver.h"
+
+#include "nsServiceManagerUtils.h"
+
+#include "nsContentUtils.h"
+#include "nsPIDOMWindow.h"
+#include "nsPIWindowRoot.h"
+
+#include "nsCOMArray.h"
+
+#include "nsCommandManager.h"
+
+nsCommandManager::nsCommandManager(mozIDOMWindowProxy* aWindow)
+ : mWindow(aWindow) {
+ MOZ_DIAGNOSTIC_ASSERT(mWindow);
+}
+
+nsCommandManager::~nsCommandManager() = default;
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(nsCommandManager)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsCommandManager)
+ tmp->mObserversTable.Clear();
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsCommandManager)
+ for (const auto& entry : tmp->mObserversTable) {
+ nsCommandManager::ObserverList* observers = entry.GetWeak();
+ int32_t numItems = observers->Length();
+ for (int32_t i = 0; i < numItems; ++i) {
+ cb.NoteXPCOMChild(observers->ElementAt(i));
+ }
+ }
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(nsCommandManager)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(nsCommandManager)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsCommandManager)
+ NS_INTERFACE_MAP_ENTRY(nsICommandManager)
+ NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsICommandManager)
+NS_INTERFACE_MAP_END
+
+void nsCommandManager::CommandStatusChanged(const char* aCommandName) {
+ ObserverList* commandObservers;
+ mObserversTable.Get(aCommandName, &commandObservers);
+
+ if (commandObservers) {
+ // XXX Should we worry about observers removing themselves from Observe()?
+ int32_t i, numItems = commandObservers->Length();
+ for (i = 0; i < numItems; ++i) {
+ nsCOMPtr<nsIObserver> observer = commandObservers->ElementAt(i);
+ // should we get the command state to pass here? This might be expensive.
+ observer->Observe(NS_ISUPPORTS_CAST(nsICommandManager*, this),
+ aCommandName, u"command_status_changed");
+ }
+ }
+}
+
+#if 0
+# pragma mark -
+#endif
+
+NS_IMETHODIMP
+nsCommandManager::AddCommandObserver(nsIObserver* aCommandObserver,
+ const char* aCommandToObserve) {
+ NS_ENSURE_ARG(aCommandObserver);
+
+ // XXX todo: handle special cases of aCommandToObserve being null, or empty
+
+ // for each command in the table, we make a list of observers for that command
+ auto* const commandObservers =
+ mObserversTable.GetOrInsertNew(aCommandToObserve);
+
+ // need to check that this command observer hasn't already been registered
+ int32_t existingIndex = commandObservers->IndexOf(aCommandObserver);
+ if (existingIndex == -1) {
+ commandObservers->AppendElement(aCommandObserver);
+ } else {
+ NS_WARNING("Registering command observer twice on the same command");
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCommandManager::RemoveCommandObserver(nsIObserver* aCommandObserver,
+ const char* aCommandObserved) {
+ NS_ENSURE_ARG(aCommandObserver);
+
+ // XXX todo: handle special cases of aCommandToObserve being null, or empty
+
+ ObserverList* commandObservers;
+ if (!mObserversTable.Get(aCommandObserved, &commandObservers)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ commandObservers->RemoveElement(aCommandObserver);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCommandManager::IsCommandSupported(const char* aCommandName,
+ mozIDOMWindowProxy* aTargetWindow,
+ bool* aResult) {
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ nsCOMPtr<nsIController> controller;
+ GetControllerForCommand(aCommandName, aTargetWindow,
+ getter_AddRefs(controller));
+ *aResult = (controller.get() != nullptr);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCommandManager::IsCommandEnabled(const char* aCommandName,
+ mozIDOMWindowProxy* aTargetWindow,
+ bool* aResult) {
+ NS_ENSURE_ARG_POINTER(aResult);
+ if (!aCommandName) {
+ *aResult = false;
+ return NS_OK;
+ }
+ *aResult = IsCommandEnabled(nsDependentCString(aCommandName), aTargetWindow);
+ return NS_OK;
+}
+
+bool nsCommandManager::IsCommandEnabled(const nsCString& aCommandName,
+ mozIDOMWindowProxy* aTargetWindow) {
+ nsCOMPtr<nsIController> controller;
+ GetControllerForCommand(aCommandName.get(), aTargetWindow,
+ getter_AddRefs(controller));
+ if (!controller) {
+ return false;
+ }
+
+ bool enabled = false;
+ controller->IsCommandEnabled(aCommandName.get(), &enabled);
+ return enabled;
+}
+
+NS_IMETHODIMP
+nsCommandManager::GetCommandState(const char* aCommandName,
+ mozIDOMWindowProxy* aTargetWindow,
+ nsICommandParams* aCommandParams) {
+ nsCOMPtr<nsIController> controller;
+ nsAutoString tValue;
+ nsresult rv = GetControllerForCommand(aCommandName, aTargetWindow,
+ getter_AddRefs(controller));
+ if (!controller) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsICommandController> commandController =
+ do_QueryInterface(controller);
+ if (commandController) {
+ rv = commandController->GetCommandStateWithParams(aCommandName,
+ aCommandParams);
+ } else {
+ rv = NS_ERROR_NOT_IMPLEMENTED;
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsCommandManager::DoCommand(const char* aCommandName,
+ nsICommandParams* aCommandParams,
+ mozIDOMWindowProxy* aTargetWindow) {
+ nsCOMPtr<nsIController> controller;
+ nsresult rv = GetControllerForCommand(aCommandName, aTargetWindow,
+ getter_AddRefs(controller));
+ if (!controller) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsICommandController> commandController =
+ do_QueryInterface(controller);
+ if (commandController && aCommandParams) {
+ rv = commandController->DoCommandWithParams(aCommandName, aCommandParams);
+ } else {
+ rv = controller->DoCommand(aCommandName);
+ }
+ return rv;
+}
+
+nsresult nsCommandManager::GetControllerForCommand(
+ const char* aCommand, mozIDOMWindowProxy* aTargetWindow,
+ nsIController** aResult) {
+ nsresult rv = NS_ERROR_FAILURE;
+ *aResult = nullptr;
+
+ // check if we're in content or chrome
+ // if we're not chrome we must have a target window or we bail
+ if (!nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
+ if (!aTargetWindow) {
+ return rv;
+ }
+
+ // if a target window is specified, it must be the window we expect
+ if (aTargetWindow != mWindow) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ if (auto* targetWindow = nsPIDOMWindowOuter::From(aTargetWindow)) {
+ // get the controller for this particular window
+ nsCOMPtr<nsIControllers> controllers;
+ rv = targetWindow->GetControllers(getter_AddRefs(controllers));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (!controllers) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // dispatch the command
+ return controllers->GetControllerForCommand(aCommand, aResult);
+ }
+
+ auto* window = nsPIDOMWindowOuter::From(mWindow);
+ NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
+ nsCOMPtr<nsPIWindowRoot> root = window->GetTopWindowRoot();
+ NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
+
+ // no target window; send command to focus controller
+ return root->GetControllerForCommand(aCommand, false /* for any window */,
+ aResult);
+}
diff --git a/dom/commandhandler/nsCommandManager.h b/dom/commandhandler/nsCommandManager.h
new file mode 100644
index 0000000000..77a10e5f35
--- /dev/null
+++ b/dom/commandhandler/nsCommandManager.h
@@ -0,0 +1,69 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsCommandManager_h__
+#define nsCommandManager_h__
+
+#include "nsString.h"
+#include "nsClassHashtable.h"
+#include "nsWeakReference.h"
+
+#include "nsICommandManager.h"
+#include "nsCycleCollectionParticipant.h"
+
+class nsIController;
+template <class E>
+class nsCOMArray;
+
+class nsCommandManager final : public nsICommandManager,
+ public nsSupportsWeakReference {
+ public:
+ using ObserverList = nsTArray<nsCOMPtr<nsIObserver>>;
+
+ nsCommandManager() = delete;
+
+ /**
+ * @param aWindow An window which is what this command manager lives on.
+ */
+ explicit nsCommandManager(mozIDOMWindowProxy* aWindow);
+
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsCommandManager, nsICommandManager)
+
+ NS_DECL_NSICOMMANDMANAGER
+
+ /**
+ * Notify the command manager that the status of a command changed. It may
+ * have changed from enabled to disabled, or vice versa, or become toggled
+ * etc.
+ */
+ void CommandStatusChanged(const char* aCommandName);
+
+ bool IsCommandEnabled(const nsCString& aCommandName,
+ mozIDOMWindowProxy* aTargetWindow);
+
+ protected:
+ virtual ~nsCommandManager();
+
+ nsresult GetControllerForCommand(const char* aCommand,
+ mozIDOMWindowProxy* aDirectedToThisWindow,
+ nsIController** aResult);
+
+ protected:
+ nsClassHashtable<nsCharPtrHashKey, ObserverList> mObserversTable;
+
+ mozIDOMWindowProxy* mWindow; // weak ptr. The window should always outlive us
+};
+
+nsCommandManager* nsICommandManager::AsCommandManager() {
+ return static_cast<nsCommandManager*>(this);
+}
+
+const nsCommandManager* nsICommandManager::AsCommandManager() const {
+ return static_cast<const nsCommandManager*>(this);
+}
+
+#endif // nsCommandManager_h__
diff --git a/dom/commandhandler/nsCommandParams.cpp b/dom/commandhandler/nsCommandParams.cpp
new file mode 100644
index 0000000000..5ac76abb43
--- /dev/null
+++ b/dom/commandhandler/nsCommandParams.cpp
@@ -0,0 +1,303 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "xpcom-config.h"
+#include <new>
+#include "nscore.h"
+#include "nsCRT.h"
+
+#include "nsCommandParams.h"
+#include "mozilla/HashFunctions.h"
+
+using namespace mozilla;
+
+const PLDHashTableOps nsCommandParams::sHashOps = {
+ HashKey, HashMatchEntry, HashMoveEntry, HashClearEntry};
+
+NS_IMPL_ISUPPORTS(nsCommandParams, nsICommandParams)
+
+nsCommandParams::nsCommandParams()
+ : mValuesHash(&sHashOps, sizeof(HashEntry), 2) {}
+
+nsCommandParams::~nsCommandParams() = default;
+
+NS_IMETHODIMP
+nsCommandParams::GetValueType(const char* aName, int16_t* aRetVal) {
+ NS_ENSURE_ARG_POINTER(aRetVal);
+
+ HashEntry* foundEntry = GetNamedEntry(aName);
+ if (foundEntry) {
+ *aRetVal = foundEntry->mEntryType;
+ return NS_OK;
+ }
+ *aRetVal = eNoType;
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsCommandParams::GetBooleanValue(const char* aName, bool* aRetVal) {
+ NS_ENSURE_ARG_POINTER(aRetVal);
+
+ ErrorResult error;
+ *aRetVal = GetBool(aName, error);
+ return error.StealNSResult();
+}
+
+bool nsCommandParams::GetBool(const char* aName, ErrorResult& aRv) const {
+ MOZ_ASSERT(!aRv.Failed());
+
+ HashEntry* foundEntry = GetNamedEntry(aName);
+ if (foundEntry && foundEntry->mEntryType == eBooleanType) {
+ return foundEntry->mData.mBoolean;
+ }
+ aRv.Throw(NS_ERROR_FAILURE);
+ return false;
+}
+
+NS_IMETHODIMP
+nsCommandParams::GetLongValue(const char* aName, int32_t* aRetVal) {
+ NS_ENSURE_ARG_POINTER(aRetVal);
+
+ ErrorResult error;
+ *aRetVal = GetInt(aName, error);
+ return error.StealNSResult();
+}
+
+int32_t nsCommandParams::GetInt(const char* aName, ErrorResult& aRv) const {
+ MOZ_ASSERT(!aRv.Failed());
+
+ HashEntry* foundEntry = GetNamedEntry(aName);
+ if (foundEntry && foundEntry->mEntryType == eLongType) {
+ return foundEntry->mData.mLong;
+ }
+ aRv.Throw(NS_ERROR_FAILURE);
+ return 0;
+}
+
+NS_IMETHODIMP
+nsCommandParams::GetDoubleValue(const char* aName, double* aRetVal) {
+ NS_ENSURE_ARG_POINTER(aRetVal);
+
+ ErrorResult error;
+ *aRetVal = GetDouble(aName, error);
+ return error.StealNSResult();
+}
+
+double nsCommandParams::GetDouble(const char* aName, ErrorResult& aRv) const {
+ MOZ_ASSERT(!aRv.Failed());
+
+ HashEntry* foundEntry = GetNamedEntry(aName);
+ if (foundEntry && foundEntry->mEntryType == eDoubleType) {
+ return foundEntry->mData.mDouble;
+ }
+ aRv.Throw(NS_ERROR_FAILURE);
+ return 0.0;
+}
+
+NS_IMETHODIMP
+nsCommandParams::GetStringValue(const char* aName, nsAString& aRetVal) {
+ return GetString(aName, aRetVal);
+}
+
+nsresult nsCommandParams::GetString(const char* aName,
+ nsAString& aRetVal) const {
+ HashEntry* foundEntry = GetNamedEntry(aName);
+ if (foundEntry && foundEntry->mEntryType == eWStringType) {
+ NS_ASSERTION(foundEntry->mData.mString, "Null string");
+ aRetVal.Assign(*foundEntry->mData.mString);
+ return NS_OK;
+ }
+ aRetVal.Truncate();
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsCommandParams::GetCStringValue(const char* aName, nsACString& aRetVal) {
+ return GetCString(aName, aRetVal);
+}
+
+nsresult nsCommandParams::GetCString(const char* aName,
+ nsACString& aRetVal) const {
+ HashEntry* foundEntry = GetNamedEntry(aName);
+ if (foundEntry && foundEntry->mEntryType == eStringType) {
+ NS_ASSERTION(foundEntry->mData.mCString, "Null string");
+ aRetVal.Assign(*foundEntry->mData.mCString);
+ return NS_OK;
+ }
+ aRetVal.Truncate();
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsCommandParams::GetISupportsValue(const char* aName, nsISupports** aRetVal) {
+ NS_ENSURE_ARG_POINTER(aRetVal);
+
+ ErrorResult error;
+ nsCOMPtr<nsISupports> result = GetISupports(aName, error);
+ if (result) {
+ result.forget(aRetVal);
+ } else {
+ *aRetVal = nullptr;
+ }
+ return error.StealNSResult();
+}
+
+already_AddRefed<nsISupports> nsCommandParams::GetISupports(
+ const char* aName, ErrorResult& aRv) const {
+ MOZ_ASSERT(!aRv.Failed());
+
+ HashEntry* foundEntry = GetNamedEntry(aName);
+ if (foundEntry && foundEntry->mEntryType == eISupportsType) {
+ nsCOMPtr<nsISupports> result = foundEntry->mISupports;
+ return result.forget();
+ }
+ aRv.Throw(NS_ERROR_FAILURE);
+ return nullptr;
+}
+
+NS_IMETHODIMP
+nsCommandParams::SetBooleanValue(const char* aName, bool aValue) {
+ return SetBool(aName, aValue);
+}
+
+nsresult nsCommandParams::SetBool(const char* aName, bool aValue) {
+ HashEntry* foundEntry = GetOrMakeEntry(aName, eBooleanType);
+ if (!foundEntry) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ foundEntry->mData.mBoolean = aValue;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCommandParams::SetLongValue(const char* aName, int32_t aValue) {
+ return SetInt(aName, aValue);
+}
+
+nsresult nsCommandParams::SetInt(const char* aName, int32_t aValue) {
+ HashEntry* foundEntry = GetOrMakeEntry(aName, eLongType);
+ if (!foundEntry) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ foundEntry->mData.mLong = aValue;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCommandParams::SetDoubleValue(const char* aName, double aValue) {
+ return SetDouble(aName, aValue);
+}
+
+nsresult nsCommandParams::SetDouble(const char* aName, double aValue) {
+ HashEntry* foundEntry = GetOrMakeEntry(aName, eDoubleType);
+ if (!foundEntry) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ foundEntry->mData.mDouble = aValue;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCommandParams::SetStringValue(const char* aName, const nsAString& aValue) {
+ return SetString(aName, aValue);
+}
+
+nsresult nsCommandParams::SetString(const char* aName,
+ const nsAString& aValue) {
+ HashEntry* foundEntry = GetOrMakeEntry(aName, eWStringType);
+ if (!foundEntry) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ foundEntry->mData.mString = new nsString(aValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCommandParams::SetCStringValue(const char* aName, const nsACString& aValue) {
+ return SetCString(aName, aValue);
+}
+
+nsresult nsCommandParams::SetCString(const char* aName,
+ const nsACString& aValue) {
+ HashEntry* foundEntry = GetOrMakeEntry(aName, eStringType);
+ if (!foundEntry) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ foundEntry->mData.mCString = new nsCString(aValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCommandParams::SetISupportsValue(const char* aName, nsISupports* aValue) {
+ return SetISupports(aName, aValue);
+}
+
+nsresult nsCommandParams::SetISupports(const char* aName, nsISupports* aValue) {
+ HashEntry* foundEntry = GetOrMakeEntry(aName, eISupportsType);
+ if (!foundEntry) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ foundEntry->mISupports = aValue; // addrefs
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCommandParams::RemoveValue(const char* aName) {
+ mValuesHash.Remove((void*)aName);
+ return NS_OK;
+}
+
+nsCommandParams::HashEntry* nsCommandParams::GetNamedEntry(
+ const char* aName) const {
+ return static_cast<HashEntry*>(mValuesHash.Search((void*)aName));
+}
+
+nsCommandParams::HashEntry* nsCommandParams::GetOrMakeEntry(
+ const char* aName, uint8_t aEntryType) {
+ auto foundEntry = static_cast<HashEntry*>(mValuesHash.Search((void*)aName));
+ if (foundEntry) { // reuse existing entry
+ foundEntry->Reset(aEntryType);
+ return foundEntry;
+ }
+
+ foundEntry = static_cast<HashEntry*>(mValuesHash.Add((void*)aName, fallible));
+ if (!foundEntry) {
+ return nullptr;
+ }
+
+ // Use placement new. Our ctor does not clobber keyHash, which is important.
+ new (foundEntry) HashEntry(aEntryType, aName);
+ return foundEntry;
+}
+
+PLDHashNumber nsCommandParams::HashKey(const void* aKey) {
+ return HashString((const char*)aKey);
+}
+
+bool nsCommandParams::HashMatchEntry(const PLDHashEntryHdr* aEntry,
+ const void* aKey) {
+ const char* keyString = (const char*)aKey;
+ const HashEntry* thisEntry = static_cast<const HashEntry*>(aEntry);
+ return thisEntry->mEntryName.Equals(keyString);
+}
+
+void nsCommandParams::HashMoveEntry(PLDHashTable* aTable,
+ const PLDHashEntryHdr* aFrom,
+ PLDHashEntryHdr* aTo) {
+ auto* fromEntry =
+ const_cast<HashEntry*>(static_cast<const HashEntry*>(aFrom));
+ HashEntry* toEntry = static_cast<HashEntry*>(aTo);
+
+ new (KnownNotNull, toEntry) HashEntry(std::move(*fromEntry));
+
+ fromEntry->~HashEntry();
+}
+
+void nsCommandParams::HashClearEntry(PLDHashTable* aTable,
+ PLDHashEntryHdr* aEntry) {
+ HashEntry* thisEntry = static_cast<HashEntry*>(aEntry);
+ thisEntry->~HashEntry();
+}
diff --git a/dom/commandhandler/nsCommandParams.h b/dom/commandhandler/nsCommandParams.h
new file mode 100644
index 0000000000..cad7f25e04
--- /dev/null
+++ b/dom/commandhandler/nsCommandParams.h
@@ -0,0 +1,166 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsCommandParams_h
+#define nsCommandParams_h
+
+#include "mozilla/ErrorResult.h"
+#include "nsString.h"
+#include "nsICommandParams.h"
+#include "nsCOMPtr.h"
+#include "PLDHashTable.h"
+
+class nsCommandParams : public nsICommandParams {
+ using ErrorResult = mozilla::ErrorResult;
+ using IgnoredErrorResult = mozilla::IgnoredErrorResult;
+
+ public:
+ nsCommandParams();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSICOMMANDPARAMS
+
+ bool GetBool(const char* aName, ErrorResult& aRv) const;
+ inline bool GetBool(const char* aName) const {
+ IgnoredErrorResult error;
+ return GetBool(aName, error);
+ }
+ int32_t GetInt(const char* aName, ErrorResult& aRv) const;
+ inline int32_t GetInt(const char* aName) const {
+ IgnoredErrorResult error;
+ return GetInt(aName, error);
+ }
+ double GetDouble(const char* aName, ErrorResult& aRv) const;
+ inline double GetDouble(const char* aName) const {
+ IgnoredErrorResult error;
+ return GetDouble(aName, error);
+ }
+ nsresult GetString(const char* aName, nsAString& aValue) const;
+ nsresult GetCString(const char* aName, nsACString& aValue) const;
+ already_AddRefed<nsISupports> GetISupports(const char* aName,
+ ErrorResult& aRv) const;
+ inline already_AddRefed<nsISupports> GetISupports(const char* aName) const {
+ IgnoredErrorResult error;
+ return GetISupports(aName, error);
+ }
+
+ nsresult SetBool(const char* aName, bool aValue);
+ nsresult SetInt(const char* aName, int32_t aValue);
+ nsresult SetDouble(const char* aName, double aValue);
+ nsresult SetString(const char* aName, const nsAString& aValue);
+ nsresult SetCString(const char* aName, const nsACString& aValue);
+ nsresult SetISupports(const char* aName, nsISupports* aValue);
+
+ protected:
+ virtual ~nsCommandParams();
+
+ struct HashEntry : public PLDHashEntryHdr {
+ nsCString mEntryName;
+
+ uint8_t mEntryType;
+ union {
+ bool mBoolean;
+ int32_t mLong;
+ double mDouble;
+ nsString* mString;
+ nsCString* mCString;
+ } mData;
+
+ nsCOMPtr<nsISupports> mISupports;
+
+ HashEntry(uint8_t aType, const char* aEntryName)
+ : mEntryName(aEntryName), mEntryType(aType), mData() {
+ Reset(mEntryType);
+ }
+
+ explicit HashEntry(const HashEntry& aRHS) : mEntryType(aRHS.mEntryType) {
+ Reset(mEntryType);
+ switch (mEntryType) {
+ case eBooleanType:
+ mData.mBoolean = aRHS.mData.mBoolean;
+ break;
+ case eLongType:
+ mData.mLong = aRHS.mData.mLong;
+ break;
+ case eDoubleType:
+ mData.mDouble = aRHS.mData.mDouble;
+ break;
+ case eWStringType:
+ NS_ASSERTION(aRHS.mData.mString, "Source entry has no string");
+ mData.mString = new nsString(*aRHS.mData.mString);
+ break;
+ case eStringType:
+ NS_ASSERTION(aRHS.mData.mCString, "Source entry has no string");
+ mData.mCString = new nsCString(*aRHS.mData.mCString);
+ break;
+ case eISupportsType:
+ mISupports = aRHS.mISupports.get();
+ break;
+ default:
+ NS_ERROR("Unknown type");
+ }
+ }
+
+ ~HashEntry() { Reset(eNoType); }
+
+ void Reset(uint8_t aNewType) {
+ switch (mEntryType) {
+ case eNoType:
+ break;
+ case eBooleanType:
+ mData.mBoolean = false;
+ break;
+ case eLongType:
+ mData.mLong = 0;
+ break;
+ case eDoubleType:
+ mData.mDouble = 0.0;
+ break;
+ case eWStringType:
+ delete mData.mString;
+ mData.mString = nullptr;
+ break;
+ case eISupportsType:
+ mISupports = nullptr;
+ break;
+ case eStringType:
+ delete mData.mCString;
+ mData.mCString = nullptr;
+ break;
+ default:
+ NS_ERROR("Unknown type");
+ }
+ mEntryType = aNewType;
+ }
+ };
+
+ HashEntry* GetNamedEntry(const char* aName) const;
+ HashEntry* GetOrMakeEntry(const char* aName, uint8_t aEntryType);
+
+ protected:
+ static PLDHashNumber HashKey(const void* aKey);
+
+ static bool HashMatchEntry(const PLDHashEntryHdr* aEntry, const void* aKey);
+
+ static void HashMoveEntry(PLDHashTable* aTable, const PLDHashEntryHdr* aFrom,
+ PLDHashEntryHdr* aTo);
+
+ static void HashClearEntry(PLDHashTable* aTable, PLDHashEntryHdr* aEntry);
+
+ PLDHashTable mValuesHash;
+
+ static const PLDHashTableOps sHashOps;
+};
+
+nsCommandParams* nsICommandParams::AsCommandParams() {
+ return static_cast<nsCommandParams*>(this);
+}
+
+const nsCommandParams* nsICommandParams::AsCommandParams() const {
+ return static_cast<const nsCommandParams*>(this);
+}
+
+#endif // nsCommandParams_h
diff --git a/dom/commandhandler/nsControllerCommandTable.cpp b/dom/commandhandler/nsControllerCommandTable.cpp
new file mode 100644
index 0000000000..25238338fc
--- /dev/null
+++ b/dom/commandhandler/nsControllerCommandTable.cpp
@@ -0,0 +1,228 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsString.h"
+#include "nsIControllerCommand.h"
+#include "nsControllerCommandTable.h"
+#include "nsGlobalWindowCommands.h"
+#include "mozilla/EditorController.h"
+#include "mozilla/HTMLEditorController.h"
+
+// this value is used to size the hash table. Just a sensible upper bound
+#define NUM_COMMANDS_LENGTH 32
+
+nsControllerCommandTable::nsControllerCommandTable()
+ : mCommandsTable(NUM_COMMANDS_LENGTH), mMutable(true) {}
+
+nsControllerCommandTable::~nsControllerCommandTable() = default;
+
+NS_IMPL_ISUPPORTS(nsControllerCommandTable, nsIControllerCommandTable,
+ nsISupportsWeakReference)
+
+NS_IMETHODIMP
+nsControllerCommandTable::MakeImmutable(void) {
+ mMutable = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsControllerCommandTable::RegisterCommand(const char* aCommandName,
+ nsIControllerCommand* aCommand) {
+ NS_ENSURE_TRUE(mMutable, NS_ERROR_FAILURE);
+
+ mCommandsTable.InsertOrUpdate(nsDependentCString(aCommandName), aCommand);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsControllerCommandTable::UnregisterCommand(const char* aCommandName,
+ nsIControllerCommand* aCommand) {
+ NS_ENSURE_TRUE(mMutable, NS_ERROR_FAILURE);
+
+ nsDependentCString commandKey(aCommandName);
+ if (!mCommandsTable.Get(commandKey, nullptr)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mCommandsTable.Remove(commandKey);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsControllerCommandTable::FindCommandHandler(const char* aCommandName,
+ nsIControllerCommand** aResult) {
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ *aResult = nullptr;
+
+ nsCOMPtr<nsIControllerCommand> foundCommand;
+ mCommandsTable.Get(nsDependentCString(aCommandName),
+ getter_AddRefs(foundCommand));
+ if (!foundCommand) {
+ return NS_ERROR_FAILURE;
+ }
+
+ foundCommand.forget(aResult);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsControllerCommandTable::IsCommandEnabled(const char* aCommandName,
+ nsISupports* aCommandRefCon,
+ bool* aResult) {
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ *aResult = false;
+
+ nsCOMPtr<nsIControllerCommand> commandHandler;
+ FindCommandHandler(aCommandName, getter_AddRefs(commandHandler));
+ if (!commandHandler) {
+ NS_WARNING(
+ "Controller command table asked about a command that it does "
+ "not handle");
+ return NS_OK;
+ }
+
+ return commandHandler->IsCommandEnabled(aCommandName, aCommandRefCon,
+ aResult);
+}
+
+NS_IMETHODIMP
+nsControllerCommandTable::UpdateCommandState(const char* aCommandName,
+ nsISupports* aCommandRefCon) {
+ nsCOMPtr<nsIControllerCommand> commandHandler;
+ FindCommandHandler(aCommandName, getter_AddRefs(commandHandler));
+ if (!commandHandler) {
+ NS_WARNING(
+ "Controller command table asked to update the state of a "
+ "command that it does not handle");
+ return NS_OK;
+ }
+
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsControllerCommandTable::SupportsCommand(const char* aCommandName,
+ nsISupports* aCommandRefCon,
+ bool* aResult) {
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ // XXX: need to check the readonly and disabled states
+
+ *aResult = false;
+
+ nsCOMPtr<nsIControllerCommand> commandHandler;
+ FindCommandHandler(aCommandName, getter_AddRefs(commandHandler));
+
+ *aResult = (commandHandler.get() != nullptr);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsControllerCommandTable::DoCommand(const char* aCommandName,
+ nsISupports* aCommandRefCon) {
+ nsCOMPtr<nsIControllerCommand> commandHandler;
+ FindCommandHandler(aCommandName, getter_AddRefs(commandHandler));
+ if (!commandHandler) {
+ NS_WARNING(
+ "Controller command table asked to do a command that it does "
+ "not handle");
+ return NS_OK;
+ }
+
+ return commandHandler->DoCommand(aCommandName, aCommandRefCon);
+}
+
+NS_IMETHODIMP
+nsControllerCommandTable::DoCommandParams(const char* aCommandName,
+ nsICommandParams* aParams,
+ nsISupports* aCommandRefCon) {
+ nsCOMPtr<nsIControllerCommand> commandHandler;
+ FindCommandHandler(aCommandName, getter_AddRefs(commandHandler));
+ if (!commandHandler) {
+ NS_WARNING(
+ "Controller command table asked to do a command that it does "
+ "not handle");
+ return NS_OK;
+ }
+ return commandHandler->DoCommandParams(aCommandName, aParams, aCommandRefCon);
+}
+
+NS_IMETHODIMP
+nsControllerCommandTable::GetCommandState(const char* aCommandName,
+ nsICommandParams* aParams,
+ nsISupports* aCommandRefCon) {
+ nsCOMPtr<nsIControllerCommand> commandHandler;
+ FindCommandHandler(aCommandName, getter_AddRefs(commandHandler));
+ if (!commandHandler) {
+ NS_WARNING(
+ "Controller command table asked to do a command that it does "
+ "not handle");
+ return NS_OK;
+ }
+ return commandHandler->GetCommandStateParams(aCommandName, aParams,
+ aCommandRefCon);
+}
+
+NS_IMETHODIMP
+nsControllerCommandTable::GetSupportedCommands(nsTArray<nsCString>& aCommands) {
+ mozilla::AppendToArray(aCommands, mCommandsTable.Keys());
+
+ return NS_OK;
+}
+
+using CommandTableRegistrar = nsresult (*)(nsControllerCommandTable*);
+
+static already_AddRefed<nsControllerCommandTable>
+CreateCommandTableWithCommands(CommandTableRegistrar aRegistrar) {
+ RefPtr<nsControllerCommandTable> commandTable =
+ new nsControllerCommandTable();
+
+ nsresult rv = aRegistrar(commandTable);
+ if (NS_FAILED(rv)) return nullptr;
+
+ // we don't know here whether we're being created as an instance,
+ // or a service, so we can't become immutable
+
+ return commandTable.forget();
+}
+
+// static
+already_AddRefed<nsControllerCommandTable>
+nsControllerCommandTable::CreateEditorCommandTable() {
+ return CreateCommandTableWithCommands(
+ mozilla::EditorController::RegisterEditorCommands);
+}
+
+// static
+already_AddRefed<nsControllerCommandTable>
+nsControllerCommandTable::CreateEditingCommandTable() {
+ return CreateCommandTableWithCommands(
+ mozilla::EditorController::RegisterEditingCommands);
+}
+
+// static
+already_AddRefed<nsControllerCommandTable>
+nsControllerCommandTable::CreateHTMLEditorCommandTable() {
+ return CreateCommandTableWithCommands(
+ mozilla::HTMLEditorController::RegisterHTMLEditorCommands);
+}
+
+// static
+already_AddRefed<nsControllerCommandTable>
+nsControllerCommandTable::CreateHTMLEditorDocStateCommandTable() {
+ return CreateCommandTableWithCommands(
+ mozilla::HTMLEditorController::RegisterEditorDocStateCommands);
+}
+
+// static
+already_AddRefed<nsControllerCommandTable>
+nsControllerCommandTable::CreateWindowCommandTable() {
+ return CreateCommandTableWithCommands(
+ nsWindowCommandRegistration::RegisterWindowCommands);
+}
diff --git a/dom/commandhandler/nsControllerCommandTable.h b/dom/commandhandler/nsControllerCommandTable.h
new file mode 100644
index 0000000000..dbaa4aa8f9
--- /dev/null
+++ b/dom/commandhandler/nsControllerCommandTable.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsControllerCommandTable_h_
+#define nsControllerCommandTable_h_
+
+#include "nsIControllerCommandTable.h"
+#include "nsWeakReference.h"
+#include "nsInterfaceHashtable.h"
+
+class nsIControllerCommand;
+
+class nsControllerCommandTable final : public nsIControllerCommandTable,
+ public nsSupportsWeakReference {
+ public:
+ nsControllerCommandTable();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSICONTROLLERCOMMANDTABLE
+
+ static already_AddRefed<nsControllerCommandTable> CreateEditorCommandTable();
+ static already_AddRefed<nsControllerCommandTable> CreateEditingCommandTable();
+ static already_AddRefed<nsControllerCommandTable>
+ CreateHTMLEditorCommandTable();
+ static already_AddRefed<nsControllerCommandTable>
+ CreateHTMLEditorDocStateCommandTable();
+ static already_AddRefed<nsControllerCommandTable> CreateWindowCommandTable();
+
+ protected:
+ virtual ~nsControllerCommandTable();
+
+ // Hash table of nsIControllerCommands, keyed by command name.
+ nsInterfaceHashtable<nsCStringHashKey, nsIControllerCommand> mCommandsTable;
+
+ // Are we mutable?
+ bool mMutable;
+};
+
+nsControllerCommandTable*
+nsIControllerCommandTable::AsControllerCommandTable() {
+ return static_cast<nsControllerCommandTable*>(this);
+}
+
+const nsControllerCommandTable*
+nsIControllerCommandTable::AsControllerCommandTable() const {
+ return static_cast<const nsControllerCommandTable*>(this);
+}
+
+#endif // nsControllerCommandTable_h_
diff --git a/dom/commandhandler/nsICommandManager.idl b/dom/commandhandler/nsICommandManager.idl
new file mode 100644
index 0000000000..384e14436c
--- /dev/null
+++ b/dom/commandhandler/nsICommandManager.idl
@@ -0,0 +1,119 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+#include "nsIObserver.idl"
+#include "nsICommandParams.idl"
+
+interface mozIDOMWindowProxy;
+
+%{C++
+class nsCommandManager;
+%}
+
+/*
+ * nsICommandManager is an interface used to executing user-level commands,
+ * and getting the state of available commands.
+ *
+ * Commands are identified by strings, which are documented elsewhere.
+ * In addition, the list of required and optional parameters for
+ * each command, that are passed in via the nsICommandParams, are
+ * also documented elsewhere. (Where? Need a good location for this).
+ */
+
+
+[scriptable, builtinclass, uuid(bb5a1730-d83b-4fa2-831b-35b9d5842e84)]
+interface nsICommandManager : nsISupports
+{
+ /*
+ * Register an observer on the specified command. The observer's Observe
+ * method will get called when the state (enabled/disabled, or toggled etc)
+ * of the command changes.
+ *
+ * You can register the same observer on multiple commmands by calling this
+ * multiple times.
+ */
+ void addCommandObserver(in nsIObserver aCommandObserver,
+ in string aCommandToObserve);
+
+ /*
+ * Stop an observer from observering the specified command. If the observer
+ * was also registered on ther commands, they will continue to be observed.
+ *
+ * Passing an empty string in 'aCommandObserved' will remove the observer
+ * from all commands.
+ */
+ void removeCommandObserver(in nsIObserver aCommandObserver,
+ in string aCommandObserved);
+
+ /*
+ * Ask the command manager if the specified command is supported.
+ * If aTargetWindow is null, the focused window is used.
+ *
+ */
+ boolean isCommandSupported(in string aCommandName,
+ in mozIDOMWindowProxy aTargetWindow);
+
+ /*
+ * Ask the command manager if the specified command is currently.
+ * enabled.
+ * If aTargetWindow is null, the focused window is used.
+ */
+ boolean isCommandEnabled(in string aCommandName,
+ in mozIDOMWindowProxy aTargetWindow);
+
+ /*
+ * Get the state of the specified commands.
+ *
+ * On input: aCommandParams filled in with values that the caller cares
+ * about, most of which are command-specific (see the command documentation
+ * for details). One boolean value, "enabled", applies to all commands,
+ * and, in return will be set to indicate whether the command is enabled
+ * (equivalent to calling isCommandEnabled).
+ *
+ * aCommandName is the name of the command that needs the state
+ * aTargetWindow is the source of command controller
+ * (null means use focus controller)
+ * On output: aCommandParams: values set by the caller filled in with
+ * state from the command.
+ */
+ void getCommandState(in string aCommandName,
+ in mozIDOMWindowProxy aTargetWindow,
+ /* inout */ in nsICommandParams aCommandParams);
+
+ /*
+ * Execute the specified command.
+ * The command will be executed in aTargetWindow if it is specified.
+ * If aTargetWindow is null, it will go to the focused window.
+ *
+ * param: aCommandParams, a list of name-value pairs of command parameters,
+ * may be null for parameter-less commands.
+ *
+ */
+ [can_run_script]
+ void doCommand(in string aCommandName,
+ in nsICommandParams aCommandParams,
+ in mozIDOMWindowProxy aTargetWindow);
+
+%{C++
+ /**
+ * In order to avoid circular dependency issues, these methods are defined
+ * in nsCommandManager.h. Consumers need to #include that header.
+ */
+ inline nsCommandManager* AsCommandManager();
+ inline const nsCommandManager* AsCommandManager() const;
+%}
+};
+
+
+/*
+
+Arguments to observers "Observe" method are as follows:
+
+ void Observe( in nsISupports aSubject, // The nsICommandManager calling this Observer
+ in string aTopic, // Name of the command
+ in wstring aDummy ); // unused
+
+*/
diff --git a/dom/commandhandler/nsICommandParams.idl b/dom/commandhandler/nsICommandParams.idl
new file mode 100644
index 0000000000..aa06951f94
--- /dev/null
+++ b/dom/commandhandler/nsICommandParams.idl
@@ -0,0 +1,92 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+/*
+ * nsICommandParams is used to pass parameters to commands executed
+ * via nsICommandManager, and to get command state.
+ *
+ */
+
+
+%{C++
+class nsCommandParams;
+%}
+
+[scriptable, builtinclass, uuid(b1fdf3c4-74e3-4f7d-a14d-2b76bcf53482)]
+interface nsICommandParams : nsISupports
+{
+ /*
+ * List of primitive types for parameter values.
+ */
+ const short eNoType = 0; /* Only used for sanity checking */
+ const short eBooleanType = 1;
+ const short eLongType = 2;
+ const short eDoubleType = 3;
+ const short eWStringType = 4;
+ const short eISupportsType = 5;
+ const short eStringType = 6;
+
+ /*
+ * getValueType
+ *
+ * Get the type of a specified parameter
+ */
+ short getValueType(in string name);
+
+ /*
+ * get_Value
+ *
+ * Get the value of a specified parameter. Will return
+ * an error if the parameter does not exist, or if the value
+ * is of the wrong type (no coercion is performed for you).
+ *
+ * nsISupports values can contain any XPCOM interface,
+ * as documented for the command. It is permissible
+ * for it to contain nsICommandParams, but not *this*
+ * one (i.e. self-containing is not allowed).
+ */
+ boolean getBooleanValue(in string name);
+ long getLongValue(in string name);
+ double getDoubleValue(in string name);
+ AString getStringValue(in string name);
+ ACString getCStringValue(in string name);
+ nsISupports getISupportsValue(in string name);
+
+ /*
+ * set_Value
+ *
+ * Set the value of a specified parameter (thus creating
+ * an entry for it).
+ *
+ * nsISupports values can contain any XPCOM interface,
+ * as documented for the command. It is permissible
+ * for it to contain nsICommandParams, but not *this*
+ * one (i.e. self-containing is not allowed).
+ */
+ void setBooleanValue(in string name, in boolean value);
+ void setLongValue(in string name, in long value);
+ void setDoubleValue(in string name, in double value);
+ void setStringValue(in string name, in AString value);
+ void setCStringValue(in string name, in ACString value);
+ void setISupportsValue(in string name, in nsISupports value);
+
+ /*
+ * removeValue
+ *
+ * Remove the specified parameter from the list.
+ */
+ void removeValue(in string name);
+
+%{C++
+ /**
+ * In order to avoid circular dependency issues, these methods are defined
+ * in nsCommandParams.h. Consumers need to #include that header.
+ */
+ inline nsCommandParams* AsCommandParams();
+ inline const nsCommandParams* AsCommandParams() const;
+%}
+};
diff --git a/dom/commandhandler/nsIControllerCommand.idl b/dom/commandhandler/nsIControllerCommand.idl
new file mode 100644
index 0000000000..7a627391b9
--- /dev/null
+++ b/dom/commandhandler/nsIControllerCommand.idl
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+#include "nsICommandParams.idl"
+
+/**
+ * nsIControllerCommand
+ *
+ * A generic command interface. You can register an nsIControllerCommand
+ * with the nsIControllerCommandTable.
+ */
+
+[scriptable, uuid(0eae9a46-1dd2-11b2-aca0-9176f05fe9db)]
+interface nsIControllerCommand : nsISupports
+{
+
+ /**
+ * Returns true if the command is currently enabled. An nsIControllerCommand
+ * can implement more than one commands; say, a group of related commands
+ * (e.g. delete left/delete right). Because of this, the command name is
+ * passed to each method.
+ *
+ * @param aCommandName the name of the command for which we want the enabled
+ * state.
+ * @param aCommandContext a cookie held by the nsIControllerCommandTable,
+ * allowing the command to get some context information.
+ * The contents of this cookie are implementation-defined.
+ */
+ boolean isCommandEnabled(in string aCommandName, in nsISupports aCommandContext);
+
+ void getCommandStateParams(in string aCommandName, in nsICommandParams aParams, in nsISupports aCommandContext);
+
+ /**
+ * Execute the name command.
+ *
+ * @param aCommandName the name of the command to execute.
+ *
+ * @param aCommandContext a cookie held by the nsIControllerCommandTable,
+ * allowing the command to get some context information.
+ * The contents of this cookie are implementation-defined.
+ */
+ [can_run_script]
+ void doCommand(in string aCommandName, in nsISupports aCommandContext);
+
+ [can_run_script]
+ void doCommandParams(in string aCommandName, in nsICommandParams aParams, in nsISupports aCommandContext);
+
+};
diff --git a/dom/commandhandler/nsIControllerCommandTable.idl b/dom/commandhandler/nsIControllerCommandTable.idl
new file mode 100644
index 0000000000..baec06fc12
--- /dev/null
+++ b/dom/commandhandler/nsIControllerCommandTable.idl
@@ -0,0 +1,102 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+#include "nsIControllerCommand.idl"
+#include "nsICommandParams.idl"
+
+%{C++
+class nsControllerCommandTable;
+%}
+
+/**
+ * nsIControllerCommandTable
+ *
+ * An interface via which a controller can maintain a series of commands,
+ * and efficiently dispatch commands to their respective handlers.
+ *
+ * Controllers that use an nsIControllerCommandTable should support
+ * nsIInterfaceRequestor, and be able to return an interface to their
+ * controller command table via getInterface().
+ *
+ */
+
+[scriptable, builtinclass, uuid(c847f90e-b8f3-49db-a4df-8867831f2800)]
+interface nsIControllerCommandTable : nsISupports
+{
+ /**
+ * Make this command table immutable, so that commands cannot
+ * be registered or unregistered. Some command tables are made
+ * mutable after command registration so that they can be
+ * used as singletons.
+ */
+ void makeImmutable();
+
+ /**
+ * Register and unregister commands with the command table.
+ *
+ * @param aCommandName the name of the command under which to register or
+ * unregister the given command handler.
+ *
+ * @param aCommand the handler for this command.
+ */
+ void registerCommand(in string aCommandName, in nsIControllerCommand aCommand);
+ void unregisterCommand(in string aCommandName, in nsIControllerCommand aCommand);
+
+ /**
+ * Find the command handler which has been registered to handle the named command.
+ *
+ * @param aCommandName the name of the command to find the handler for.
+ */
+ nsIControllerCommand findCommandHandler(in string aCommandName);
+
+ /**
+ * Get whether the named command is enabled.
+ *
+ * @param aCommandName the name of the command to test
+ * @param aCommandRefCon the command context data
+ */
+ boolean isCommandEnabled(in string aCommandName, in nsISupports aCommandRefCon);
+
+ /**
+ * Tell the command to update its state (if it is a state updating command)
+ *
+ * @param aCommandName the name of the command to update
+ * @param aCommandRefCon the command context data
+ */
+ void updateCommandState(in string aCommandName, in nsISupports aCommandRefCon);
+
+ /**
+ * Get whether the named command is supported.
+ *
+ * @param aCommandName the name of the command to test
+ * @param aCommandRefCon the command context data
+ */
+ boolean supportsCommand(in string aCommandName, in nsISupports aCommandRefCon);
+
+ /**
+ * Execute the named command.
+ *
+ * @param aCommandName the name of the command to execute
+ * @param aCommandRefCon the command context data
+ */
+ [can_run_script]
+ void doCommand(in string aCommandName, in nsISupports aCommandRefCon);
+
+ [can_run_script]
+ void doCommandParams(in string aCommandName, in nsICommandParams aParam, in nsISupports aCommandRefCon);
+
+ void getCommandState(in string aCommandName, in nsICommandParams aParam, in nsISupports aCommandRefCon);
+
+ Array<ACString> getSupportedCommands();
+
+%{C++
+ /**
+ * In order to avoid circular dependency issues, these methods are defined
+ * in nsControllerCommandTable.h. Consumers need to #include that header.
+ */
+ inline nsControllerCommandTable* AsControllerCommandTable();
+ inline const nsControllerCommandTable* AsControllerCommandTable() const;
+%}
+};
diff --git a/dom/commandhandler/nsIControllerContext.idl b/dom/commandhandler/nsIControllerContext.idl
new file mode 100644
index 0000000000..cd80512b94
--- /dev/null
+++ b/dom/commandhandler/nsIControllerContext.idl
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+#include "nsIControllerCommandTable.idl"
+
+[scriptable, builtinclass, uuid(47B82B60-A36F-4167-8072-6F421151ED50)]
+interface nsIControllerContext : nsISupports
+{
+ /**
+ * Set a context on this controller, which is passed
+ * to commands to give them some context when they execute.
+ *
+ * @param aCommandContext the context passed to commands.
+ * Note that this is *not* addreffed by the
+ * controller, and so needs to outlive it,
+ * or be nulled out.
+ */
+ void setCommandContext(in nsISupports aCommandContext);
+};