nsCommandManager.cpp (7862B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "nsCommandManager.h" 8 9 #include "nsCOMArray.h" 10 #include "nsContentUtils.h" 11 #include "nsIController.h" 12 #include "nsIControllers.h" 13 #include "nsIObserver.h" 14 #include "nsPIDOMWindow.h" 15 #include "nsPIWindowRoot.h" 16 #include "nsServiceManagerUtils.h" 17 #include "nsString.h" 18 19 nsCommandManager::nsCommandManager(mozIDOMWindowProxy* aWindow) 20 : mWindow(aWindow) { 21 MOZ_DIAGNOSTIC_ASSERT(mWindow); 22 } 23 24 nsCommandManager::~nsCommandManager() = default; 25 26 NS_IMPL_CYCLE_COLLECTION_CLASS(nsCommandManager) 27 28 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsCommandManager) 29 tmp->mObserversTable.Clear(); 30 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE 31 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 32 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsCommandManager) 33 for (const auto& entry : tmp->mObserversTable) { 34 nsCommandManager::ObserverList* observers = entry.GetWeak(); 35 int32_t numItems = observers->Length(); 36 for (int32_t i = 0; i < numItems; ++i) { 37 cb.NoteXPCOMChild(observers->ElementAt(i)); 38 } 39 } 40 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 41 42 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsCommandManager) 43 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsCommandManager) 44 45 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsCommandManager) 46 NS_INTERFACE_MAP_ENTRY(nsICommandManager) 47 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) 48 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsICommandManager) 49 NS_INTERFACE_MAP_END 50 51 void nsCommandManager::CommandStatusChanged(const char* aCommandName) { 52 ObserverList* commandObservers; 53 mObserversTable.Get(aCommandName, &commandObservers); 54 55 if (commandObservers) { 56 // XXX Should we worry about observers removing themselves from Observe()? 57 int32_t i, numItems = commandObservers->Length(); 58 for (i = 0; i < numItems; ++i) { 59 nsCOMPtr<nsIObserver> observer = commandObservers->ElementAt(i); 60 // should we get the command state to pass here? This might be expensive. 61 observer->Observe(NS_ISUPPORTS_CAST(nsICommandManager*, this), 62 aCommandName, u"command_status_changed"); 63 } 64 } 65 } 66 67 #if 0 68 # pragma mark - 69 #endif 70 71 NS_IMETHODIMP 72 nsCommandManager::AddCommandObserver(nsIObserver* aCommandObserver, 73 const char* aCommandToObserve) { 74 NS_ENSURE_ARG(aCommandObserver); 75 76 // XXX todo: handle special cases of aCommandToObserve being null, or empty 77 78 // for each command in the table, we make a list of observers for that command 79 auto* const commandObservers = 80 mObserversTable.GetOrInsertNew(aCommandToObserve); 81 82 // need to check that this command observer hasn't already been registered 83 int32_t existingIndex = commandObservers->IndexOf(aCommandObserver); 84 if (existingIndex == -1) { 85 commandObservers->AppendElement(aCommandObserver); 86 } else { 87 NS_WARNING("Registering command observer twice on the same command"); 88 } 89 90 return NS_OK; 91 } 92 93 NS_IMETHODIMP 94 nsCommandManager::RemoveCommandObserver(nsIObserver* aCommandObserver, 95 const char* aCommandObserved) { 96 NS_ENSURE_ARG(aCommandObserver); 97 98 // XXX todo: handle special cases of aCommandToObserve being null, or empty 99 100 ObserverList* commandObservers; 101 if (!mObserversTable.Get(aCommandObserved, &commandObservers)) { 102 return NS_ERROR_UNEXPECTED; 103 } 104 105 commandObservers->RemoveElement(aCommandObserver); 106 107 return NS_OK; 108 } 109 110 NS_IMETHODIMP 111 nsCommandManager::IsCommandSupported(const char* aCommandName, 112 mozIDOMWindowProxy* aTargetWindow, 113 bool* aResult) { 114 NS_ENSURE_ARG_POINTER(aResult); 115 116 nsCOMPtr<nsIController> controller; 117 GetControllerForCommand(aCommandName, aTargetWindow, 118 getter_AddRefs(controller)); 119 *aResult = (controller.get() != nullptr); 120 return NS_OK; 121 } 122 123 NS_IMETHODIMP 124 nsCommandManager::IsCommandEnabled(const char* aCommandName, 125 mozIDOMWindowProxy* aTargetWindow, 126 bool* aResult) { 127 NS_ENSURE_ARG_POINTER(aResult); 128 if (!aCommandName) { 129 *aResult = false; 130 return NS_OK; 131 } 132 *aResult = IsCommandEnabled(nsDependentCString(aCommandName), aTargetWindow); 133 return NS_OK; 134 } 135 136 bool nsCommandManager::IsCommandEnabled(const nsCString& aCommandName, 137 mozIDOMWindowProxy* aTargetWindow) { 138 nsCOMPtr<nsIController> controller; 139 GetControllerForCommand(aCommandName.get(), aTargetWindow, 140 getter_AddRefs(controller)); 141 if (!controller) { 142 return false; 143 } 144 145 bool enabled = false; 146 controller->IsCommandEnabled(aCommandName.get(), &enabled); 147 return enabled; 148 } 149 150 NS_IMETHODIMP 151 nsCommandManager::GetCommandState(const char* aCommandName, 152 mozIDOMWindowProxy* aTargetWindow, 153 nsICommandParams* aCommandParams) { 154 nsCOMPtr<nsIController> controller; 155 nsAutoString tValue; 156 nsresult rv = GetControllerForCommand(aCommandName, aTargetWindow, 157 getter_AddRefs(controller)); 158 if (!controller) { 159 return NS_ERROR_FAILURE; 160 } 161 162 nsCOMPtr<nsICommandController> commandController = 163 do_QueryInterface(controller); 164 if (commandController) { 165 rv = commandController->GetCommandStateWithParams(aCommandName, 166 aCommandParams); 167 } else { 168 rv = NS_ERROR_NOT_IMPLEMENTED; 169 } 170 return rv; 171 } 172 173 NS_IMETHODIMP 174 nsCommandManager::DoCommand(const char* aCommandName, 175 nsICommandParams* aCommandParams, 176 mozIDOMWindowProxy* aTargetWindow) { 177 nsCOMPtr<nsIController> controller; 178 nsresult rv = GetControllerForCommand(aCommandName, aTargetWindow, 179 getter_AddRefs(controller)); 180 if (!controller) { 181 return NS_ERROR_FAILURE; 182 } 183 184 nsCOMPtr<nsICommandController> commandController = 185 do_QueryInterface(controller); 186 if (commandController && aCommandParams) { 187 rv = commandController->DoCommandWithParams(aCommandName, aCommandParams); 188 } else { 189 rv = controller->DoCommand(aCommandName); 190 } 191 return rv; 192 } 193 194 nsresult nsCommandManager::GetControllerForCommand( 195 const char* aCommand, mozIDOMWindowProxy* aTargetWindow, 196 nsIController** aResult) { 197 nsresult rv = NS_ERROR_FAILURE; 198 *aResult = nullptr; 199 200 // check if we're in content or chrome 201 // if we're not chrome we must have a target window or we bail 202 if (!nsContentUtils::LegacyIsCallerChromeOrNativeCode()) { 203 if (!aTargetWindow) { 204 return rv; 205 } 206 207 // if a target window is specified, it must be the window we expect 208 if (aTargetWindow != mWindow) { 209 return NS_ERROR_FAILURE; 210 } 211 } 212 213 if (auto* targetWindow = nsPIDOMWindowOuter::From(aTargetWindow)) { 214 // get the controller for this particular window 215 nsCOMPtr<nsIControllers> controllers; 216 rv = targetWindow->GetControllers(getter_AddRefs(controllers)); 217 if (NS_FAILED(rv)) { 218 return rv; 219 } 220 if (!controllers) { 221 return NS_ERROR_FAILURE; 222 } 223 224 // dispatch the command 225 return controllers->GetControllerForCommand(aCommand, aResult); 226 } 227 228 auto* window = nsPIDOMWindowOuter::From(mWindow); 229 NS_ENSURE_TRUE(window, NS_ERROR_FAILURE); 230 nsCOMPtr<nsPIWindowRoot> root = window->GetTopWindowRoot(); 231 NS_ENSURE_TRUE(root, NS_ERROR_FAILURE); 232 233 // no target window; send command to focus controller 234 return root->GetControllerForCommand(aCommand, false /* for any window */, 235 aResult); 236 }