michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsString.h" michael@0: michael@0: #include "nsIController.h" michael@0: #include "nsIControllers.h" michael@0: #include "nsIObserver.h" michael@0: michael@0: #include "nsIComponentManager.h" michael@0: michael@0: #include "nsServiceManagerUtils.h" michael@0: #include "nsIScriptSecurityManager.h" michael@0: michael@0: #include "nsIDOMWindow.h" michael@0: #include "nsPIDOMWindow.h" michael@0: #include "nsPIWindowRoot.h" michael@0: #include "nsIFocusManager.h" michael@0: michael@0: #include "nsCOMArray.h" michael@0: michael@0: #include "nsCommandManager.h" michael@0: michael@0: michael@0: nsCommandManager::nsCommandManager() michael@0: : mWindow(nullptr) michael@0: { michael@0: /* member initializers and constructor code */ michael@0: } michael@0: michael@0: nsCommandManager::~nsCommandManager() michael@0: { michael@0: /* destructor code */ michael@0: } michael@0: michael@0: michael@0: static PLDHashOperator michael@0: TraverseCommandObservers(const char* aKey, michael@0: nsCommandManager::ObserverList* aObservers, michael@0: void* aClosure) michael@0: { michael@0: nsCycleCollectionTraversalCallback *cb = michael@0: static_cast(aClosure); michael@0: michael@0: int32_t i, numItems = aObservers->Length(); michael@0: for (i = 0; i < numItems; ++i) { michael@0: cb->NoteXPCOMChild(aObservers->ElementAt(i)); michael@0: } michael@0: michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CLASS(nsCommandManager) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsCommandManager) michael@0: tmp->mObserversTable.Clear(); michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_END michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsCommandManager) michael@0: tmp->mObserversTable.EnumerateRead(TraverseCommandObservers, &cb); michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTING_ADDREF(nsCommandManager) michael@0: NS_IMPL_CYCLE_COLLECTING_RELEASE(nsCommandManager) michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsCommandManager) michael@0: NS_INTERFACE_MAP_ENTRY(nsICommandManager) michael@0: NS_INTERFACE_MAP_ENTRY(nsPICommandUpdater) michael@0: NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) michael@0: NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsICommandManager) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: #if 0 michael@0: #pragma mark - michael@0: #endif michael@0: michael@0: /* void init (in nsIDOMWindow aWindow); */ michael@0: NS_IMETHODIMP michael@0: nsCommandManager::Init(nsIDOMWindow *aWindow) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aWindow); michael@0: michael@0: NS_ASSERTION(aWindow, "Need non-null window here"); michael@0: mWindow = aWindow; // weak ptr michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* void commandStatusChanged (in DOMString aCommandName, in long aChangeFlags); */ michael@0: NS_IMETHODIMP michael@0: nsCommandManager::CommandStatusChanged(const char * aCommandName) michael@0: { michael@0: ObserverList* commandObservers; michael@0: mObserversTable.Get(aCommandName, &commandObservers); michael@0: michael@0: if (commandObservers) michael@0: { michael@0: // XXX Should we worry about observers removing themselves from Observe()? michael@0: int32_t i, numItems = commandObservers->Length(); michael@0: for (i = 0; i < numItems; ++i) michael@0: { michael@0: nsCOMPtr observer = commandObservers->ElementAt(i); michael@0: // should we get the command state to pass here? This might be expensive. michael@0: observer->Observe(NS_ISUPPORTS_CAST(nsICommandManager*, this), michael@0: aCommandName, michael@0: MOZ_UTF16("command_status_changed")); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: #if 0 michael@0: #pragma mark - michael@0: #endif michael@0: michael@0: /* void addCommandObserver (in nsIObserver aCommandObserver, in wstring aCommandToObserve); */ michael@0: NS_IMETHODIMP michael@0: nsCommandManager::AddCommandObserver(nsIObserver *aCommandObserver, const char *aCommandToObserve) michael@0: { michael@0: NS_ENSURE_ARG(aCommandObserver); michael@0: michael@0: // XXX todo: handle special cases of aCommandToObserve being null, or empty michael@0: michael@0: // for each command in the table, we make a list of observers for that command michael@0: ObserverList* commandObservers; michael@0: if (!mObserversTable.Get(aCommandToObserve, &commandObservers)) michael@0: { michael@0: commandObservers = new ObserverList; michael@0: mObserversTable.Put(aCommandToObserve, commandObservers); michael@0: } michael@0: michael@0: // need to check that this command observer hasn't already been registered michael@0: int32_t existingIndex = commandObservers->IndexOf(aCommandObserver); michael@0: if (existingIndex == -1) michael@0: commandObservers->AppendElement(aCommandObserver); michael@0: else michael@0: NS_WARNING("Registering command observer twice on the same command"); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* void removeCommandObserver (in nsIObserver aCommandObserver, in wstring aCommandObserved); */ michael@0: NS_IMETHODIMP michael@0: nsCommandManager::RemoveCommandObserver(nsIObserver *aCommandObserver, const char *aCommandObserved) michael@0: { michael@0: NS_ENSURE_ARG(aCommandObserver); michael@0: michael@0: // XXX todo: handle special cases of aCommandToObserve being null, or empty michael@0: michael@0: ObserverList* commandObservers; michael@0: if (!mObserversTable.Get(aCommandObserved, &commandObservers)) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: commandObservers->RemoveElement(aCommandObserver); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* boolean isCommandSupported(in string aCommandName, michael@0: in nsIDOMWindow aTargetWindow); */ michael@0: NS_IMETHODIMP michael@0: nsCommandManager::IsCommandSupported(const char *aCommandName, michael@0: nsIDOMWindow *aTargetWindow, michael@0: bool *outCommandSupported) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(outCommandSupported); michael@0: michael@0: nsCOMPtr controller; michael@0: GetControllerForCommand(aCommandName, aTargetWindow, getter_AddRefs(controller)); michael@0: *outCommandSupported = (controller.get() != nullptr); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* boolean isCommandEnabled(in string aCommandName, michael@0: in nsIDOMWindow aTargetWindow); */ michael@0: NS_IMETHODIMP michael@0: nsCommandManager::IsCommandEnabled(const char *aCommandName, michael@0: nsIDOMWindow *aTargetWindow, michael@0: bool *outCommandEnabled) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(outCommandEnabled); michael@0: michael@0: bool commandEnabled = false; michael@0: michael@0: nsCOMPtr controller; michael@0: GetControllerForCommand(aCommandName, aTargetWindow, getter_AddRefs(controller)); michael@0: if (controller) michael@0: { michael@0: controller->IsCommandEnabled(aCommandName, &commandEnabled); michael@0: } michael@0: *outCommandEnabled = commandEnabled; michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* void getCommandState (in DOMString aCommandName, michael@0: in nsIDOMWindow aTargetWindow, michael@0: inout nsICommandParams aCommandParams); */ michael@0: NS_IMETHODIMP michael@0: nsCommandManager::GetCommandState(const char *aCommandName, michael@0: nsIDOMWindow *aTargetWindow, michael@0: nsICommandParams *aCommandParams) michael@0: { michael@0: nsCOMPtr controller; michael@0: nsAutoString tValue; michael@0: nsresult rv = GetControllerForCommand(aCommandName, aTargetWindow, getter_AddRefs(controller)); michael@0: if (!controller) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsCOMPtr commandController = do_QueryInterface(controller); michael@0: if (commandController) michael@0: rv = commandController->GetCommandStateWithParams(aCommandName, aCommandParams); michael@0: else michael@0: rv = NS_ERROR_NOT_IMPLEMENTED; michael@0: return rv; michael@0: } michael@0: michael@0: /* void doCommand(in string aCommandName, michael@0: in nsICommandParams aCommandParams, michael@0: in nsIDOMWindow aTargetWindow); */ michael@0: NS_IMETHODIMP michael@0: nsCommandManager::DoCommand(const char *aCommandName, michael@0: nsICommandParams *aCommandParams, michael@0: nsIDOMWindow *aTargetWindow) michael@0: { michael@0: nsCOMPtr controller; michael@0: nsresult rv = GetControllerForCommand(aCommandName, aTargetWindow, getter_AddRefs(controller)); michael@0: if (!controller) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsCOMPtr commandController = do_QueryInterface(controller); michael@0: if (commandController && aCommandParams) michael@0: rv = commandController->DoCommandWithParams(aCommandName, aCommandParams); michael@0: else michael@0: rv = controller->DoCommand(aCommandName); michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsCommandManager::IsCallerChrome(bool *is_caller_chrome) michael@0: { michael@0: *is_caller_chrome = false; michael@0: nsresult rv = NS_OK; michael@0: nsCOMPtr secMan = michael@0: do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: if (!secMan) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: rv = secMan->SubjectPrincipalIsSystem(is_caller_chrome); michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsCommandManager::GetControllerForCommand(const char *aCommand, michael@0: nsIDOMWindow *aTargetWindow, michael@0: nsIController** outController) michael@0: { michael@0: nsresult rv = NS_ERROR_FAILURE; michael@0: *outController = nullptr; michael@0: michael@0: // check if we're in content or chrome michael@0: // if we're not chrome we must have a target window or we bail michael@0: bool isChrome = false; michael@0: rv = IsCallerChrome(&isChrome); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: if (!isChrome) { michael@0: if (!aTargetWindow) michael@0: return rv; michael@0: michael@0: // if a target window is specified, it must be the window we expect michael@0: if (aTargetWindow != mWindow) michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: if (aTargetWindow) { michael@0: // get the controller for this particular window michael@0: nsCOMPtr controllers; michael@0: rv = aTargetWindow->GetControllers(getter_AddRefs(controllers)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: if (!controllers) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: // dispatch the command michael@0: return controllers->GetControllerForCommand(aCommand, outController); michael@0: } michael@0: michael@0: nsCOMPtr window(do_QueryInterface(mWindow)); michael@0: NS_ENSURE_TRUE(window, NS_ERROR_FAILURE); michael@0: nsCOMPtr root = window->GetTopWindowRoot(); michael@0: NS_ENSURE_TRUE(root, NS_ERROR_FAILURE); michael@0: michael@0: // no target window; send command to focus controller michael@0: return root->GetControllerForCommand(aCommand, outController); michael@0: } michael@0: