nsWindowRoot.cpp (13754B)
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 "nsWindowRoot.h" 8 9 #include "mozilla/BasicEvents.h" 10 #include "mozilla/EventDispatcher.h" 11 #include "mozilla/EventListenerManager.h" 12 #include "mozilla/StaticPrefs_browser.h" 13 #include "mozilla/dom/BrowserParent.h" 14 #include "mozilla/dom/CanonicalBrowsingContext.h" 15 #include "mozilla/dom/HTMLInputElement.h" 16 #include "mozilla/dom/HTMLTextAreaElement.h" 17 #include "mozilla/dom/JSActorService.h" 18 #include "mozilla/dom/WindowGlobalParent.h" 19 #include "mozilla/dom/WindowRootBinding.h" 20 #include "nsCOMPtr.h" 21 #include "nsCycleCollectionParticipant.h" 22 #include "nsFocusManager.h" 23 #include "nsFrameLoader.h" 24 #include "nsFrameLoaderOwner.h" 25 #include "nsGlobalWindowOuter.h" 26 #include "nsIContent.h" 27 #include "nsIController.h" 28 #include "nsIControllers.h" 29 #include "nsPIDOMWindow.h" 30 #include "nsPresContext.h" 31 #include "nsQueryActor.h" 32 #include "nsQueryObject.h" 33 #include "nsString.h" 34 #include "nsXULElement.h" 35 #include "xpcpublic.h" 36 37 using namespace mozilla; 38 using namespace mozilla::dom; 39 40 nsWindowRoot::nsWindowRoot(nsPIDOMWindowOuter* aWindow) { 41 SetIsOnMainThread(); 42 mWindow = aWindow; 43 } 44 45 nsWindowRoot::~nsWindowRoot() { 46 if (mListenerManager) { 47 mListenerManager->Disconnect(); 48 } 49 50 JSActorService::UnregisterChromeEventTarget(this); 51 } 52 53 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(nsWindowRoot) 54 55 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsWindowRoot) 56 JSActorService::UnregisterChromeEventTarget(tmp); 57 58 NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow) 59 NS_IMPL_CYCLE_COLLECTION_UNLINK(mListenerManager) 60 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent) 61 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER 62 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 63 64 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsWindowRoot) 65 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow) 66 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager) 67 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent) 68 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 69 70 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsWindowRoot) 71 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 72 NS_INTERFACE_MAP_ENTRY(nsISupports) 73 NS_INTERFACE_MAP_ENTRY(nsPIWindowRoot) 74 NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget) 75 NS_INTERFACE_MAP_END 76 77 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsWindowRoot) 78 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsWindowRoot) 79 80 bool nsWindowRoot::DispatchEvent(Event& aEvent, CallerType aCallerType, 81 ErrorResult& aRv) { 82 nsEventStatus status = nsEventStatus_eIgnore; 83 nsresult rv = EventDispatcher::DispatchDOMEvent( 84 static_cast<EventTarget*>(this), nullptr, &aEvent, nullptr, &status); 85 bool retval = !aEvent.DefaultPrevented(aCallerType); 86 if (NS_FAILED(rv)) { 87 aRv.Throw(rv); 88 } 89 return retval; 90 } 91 92 bool nsWindowRoot::ComputeDefaultWantsUntrusted(ErrorResult& aRv) { 93 return false; 94 } 95 96 EventListenerManager* nsWindowRoot::GetOrCreateListenerManager() { 97 if (!mListenerManager) { 98 mListenerManager = 99 new EventListenerManager(static_cast<EventTarget*>(this)); 100 } 101 102 return mListenerManager; 103 } 104 105 EventListenerManager* nsWindowRoot::GetExistingListenerManager() const { 106 return mListenerManager; 107 } 108 109 void nsWindowRoot::GetEventTargetParent(EventChainPreVisitor& aVisitor) { 110 aVisitor.mCanHandle = true; 111 aVisitor.mForceContentDispatch = true; // FIXME! Bug 329119 112 // To keep mWindow alive 113 aVisitor.mItemData = static_cast<nsISupports*>(mWindow); 114 aVisitor.SetParentTarget(mParent, false); 115 } 116 117 nsresult nsWindowRoot::PostHandleEvent(EventChainPostVisitor& aVisitor) { 118 return NS_OK; 119 } 120 121 nsPIDOMWindowOuter* nsWindowRoot::GetOwnerGlobalForBindingsInternal() { 122 return mWindow; 123 } 124 125 nsIGlobalObject* nsWindowRoot::GetOwnerGlobal() const { 126 nsCOMPtr<nsIGlobalObject> global = 127 do_QueryInterface(mWindow->GetCurrentInnerWindow()); 128 // We're still holding a ref to it, so returning the raw pointer is ok... 129 return global; 130 } 131 132 nsPIDOMWindowOuter* nsWindowRoot::GetWindow() { return mWindow; } 133 134 nsresult nsWindowRoot::GetControllers(bool aForVisibleWindow, 135 nsIControllers** aResult) { 136 *aResult = nullptr; 137 138 // XXX: we should fix this so there's a generic interface that 139 // describes controllers, so this code would have no special 140 // knowledge of what object might have controllers. 141 142 nsFocusManager::SearchRange searchRange = 143 aForVisibleWindow ? nsFocusManager::eIncludeVisibleDescendants 144 : nsFocusManager::eIncludeAllDescendants; 145 nsCOMPtr<nsPIDOMWindowOuter> focusedWindow; 146 nsIContent* focusedContent = nsFocusManager::GetFocusedDescendant( 147 mWindow, searchRange, getter_AddRefs(focusedWindow)); 148 if (focusedContent) { 149 if (auto* xulElement = nsXULElement::FromNode(focusedContent)) { 150 *aResult = xulElement->GetExtantControllers(); 151 NS_IF_ADDREF(*aResult); 152 return NS_OK; 153 } 154 auto* htmlTextArea = HTMLTextAreaElement::FromNode(focusedContent); 155 if (htmlTextArea) { 156 return htmlTextArea->GetControllers(aResult); 157 } 158 auto* htmlInputElement = HTMLInputElement::FromNode(focusedContent); 159 if (htmlInputElement) { 160 return htmlInputElement->GetControllers(aResult); 161 } 162 if (focusedContent->IsEditable() && focusedWindow) { 163 return focusedWindow->GetControllers(aResult); 164 } 165 } else { 166 return focusedWindow->GetControllers(aResult); 167 } 168 169 return NS_OK; 170 } 171 172 nsresult nsWindowRoot::GetControllerForCommand(const char* aCommand, 173 bool aForVisibleWindow, 174 nsIController** _retval) { 175 NS_ENSURE_ARG_POINTER(_retval); 176 *_retval = nullptr; 177 178 // If this is the parent process, check if a child browsing context from 179 // another process is focused, and ask if it has a controller actor that 180 // supports the command. 181 if (XRE_IsParentProcess()) { 182 nsFocusManager* fm = nsFocusManager::GetFocusManager(); 183 if (!fm) { 184 return NS_ERROR_FAILURE; 185 } 186 187 // Unfortunately, messages updating the active/focus state in the focus 188 // manager don't happen fast enough in the case when switching focus between 189 // processes when clicking on a chrome UI element while a child tab is 190 // focused, so we need to check whether the focus manager thinks a child 191 // frame is focused as well. 192 nsCOMPtr<nsPIDOMWindowOuter> focusedWindow; 193 nsIContent* focusedContent = nsFocusManager::GetFocusedDescendant( 194 mWindow, nsFocusManager::eIncludeAllDescendants, 195 getter_AddRefs(focusedWindow)); 196 RefPtr<nsFrameLoaderOwner> loaderOwner = do_QueryObject(focusedContent); 197 if (loaderOwner) { 198 // Only check browsing contexts if a remote frame is focused. If chrome is 199 // focused, just check the controllers directly below. 200 RefPtr<nsFrameLoader> frameLoader = loaderOwner->GetFrameLoader(); 201 if (frameLoader && frameLoader->IsRemoteFrame()) { 202 // GetActiveBrowsingContextInChrome actually returns the top-level 203 // browsing context if the focus is in a child process tab, or null if 204 // the focus is in chrome. 205 BrowsingContext* focusedBC = 206 fm->GetActiveBrowsingContextInChrome() 207 ? fm->GetFocusedBrowsingContextInChrome() 208 : nullptr; 209 if (focusedBC) { 210 // At this point, it is known that a child process is focused, so ask 211 // its Controllers actor if the command is supported. 212 nsCOMPtr<nsIController> controller = do_QueryActor( 213 "Controllers", focusedBC->Canonical()->GetCurrentWindowGlobal()); 214 if (controller) { 215 bool supported; 216 controller->SupportsCommand(aCommand, &supported); 217 if (supported) { 218 controller.forget(_retval); 219 return NS_OK; 220 } 221 } 222 } 223 } 224 } 225 } 226 227 { 228 nsCOMPtr<nsIControllers> controllers; 229 GetControllers(aForVisibleWindow, getter_AddRefs(controllers)); 230 if (controllers) { 231 nsCOMPtr<nsIController> controller; 232 controllers->GetControllerForCommand(aCommand, 233 getter_AddRefs(controller)); 234 if (controller) { 235 controller.forget(_retval); 236 return NS_OK; 237 } 238 } 239 } 240 241 nsFocusManager::SearchRange searchRange = 242 aForVisibleWindow ? nsFocusManager::eIncludeVisibleDescendants 243 : nsFocusManager::eIncludeAllDescendants; 244 nsCOMPtr<nsPIDOMWindowOuter> focusedWindow; 245 nsFocusManager::GetFocusedDescendant(mWindow, searchRange, 246 getter_AddRefs(focusedWindow)); 247 while (focusedWindow) { 248 nsCOMPtr<nsIControllers> controllers; 249 focusedWindow->GetControllers(getter_AddRefs(controllers)); 250 if (controllers) { 251 nsCOMPtr<nsIController> controller; 252 controllers->GetControllerForCommand(aCommand, 253 getter_AddRefs(controller)); 254 if (controller) { 255 controller.forget(_retval); 256 return NS_OK; 257 } 258 } 259 260 // XXXndeakin P3 is this casting safe? 261 nsGlobalWindowOuter* win = nsGlobalWindowOuter::Cast(focusedWindow); 262 focusedWindow = win->GetPrivateParent(); 263 } 264 265 return NS_OK; 266 } 267 268 void nsWindowRoot::GetEnabledDisabledCommandsForControllers( 269 nsIControllers* aControllers, nsTHashSet<nsCString>& aCommandsHandled, 270 nsTArray<nsCString>& aEnabledCommands, 271 nsTArray<nsCString>& aDisabledCommands) { 272 uint32_t controllerCount; 273 aControllers->GetControllerCount(&controllerCount); 274 for (uint32_t c = 0; c < controllerCount; c++) { 275 nsCOMPtr<nsIController> controller; 276 aControllers->GetControllerAt(c, getter_AddRefs(controller)); 277 278 nsCOMPtr<nsICommandController> commandController( 279 do_QueryInterface(controller)); 280 if (commandController) { 281 // All of our default command controllers have 20-60 commands. Let's just 282 // leave enough space here for all of them so we probably don't need to 283 // heap-allocate. 284 AutoTArray<nsCString, 64> commands; 285 if (NS_SUCCEEDED(commandController->GetSupportedCommands(commands))) { 286 for (auto& commandStr : commands) { 287 // Use a hash to determine which commands have already been handled by 288 // earlier controllers, as the earlier controller's result should get 289 // priority. 290 if (aCommandsHandled.EnsureInserted(commandStr)) { 291 // We inserted a new entry into aCommandsHandled. 292 bool enabled = false; 293 controller->IsCommandEnabled(commandStr.get(), &enabled); 294 295 if (enabled) { 296 aEnabledCommands.AppendElement(commandStr); 297 } else { 298 aDisabledCommands.AppendElement(commandStr); 299 } 300 } 301 } 302 } 303 } 304 } 305 } 306 307 void nsWindowRoot::GetEnabledDisabledCommands( 308 nsTArray<nsCString>& aEnabledCommands, 309 nsTArray<nsCString>& aDisabledCommands) { 310 nsTHashSet<nsCString> commandsHandled; 311 312 nsCOMPtr<nsIControllers> controllers; 313 GetControllers(false, getter_AddRefs(controllers)); 314 if (controllers) { 315 GetEnabledDisabledCommandsForControllers( 316 controllers, commandsHandled, aEnabledCommands, aDisabledCommands); 317 } 318 319 nsCOMPtr<nsPIDOMWindowOuter> focusedWindow; 320 nsFocusManager::GetFocusedDescendant(mWindow, 321 nsFocusManager::eIncludeAllDescendants, 322 getter_AddRefs(focusedWindow)); 323 while (focusedWindow) { 324 focusedWindow->GetControllers(getter_AddRefs(controllers)); 325 if (controllers) { 326 GetEnabledDisabledCommandsForControllers( 327 controllers, commandsHandled, aEnabledCommands, aDisabledCommands); 328 } 329 330 nsGlobalWindowOuter* win = nsGlobalWindowOuter::Cast(focusedWindow); 331 focusedWindow = win->GetPrivateParent(); 332 } 333 } 334 335 already_AddRefed<nsINode> nsWindowRoot::GetPopupNode() { 336 nsCOMPtr<nsINode> popupNode = do_QueryReferent(mPopupNode); 337 return popupNode.forget(); 338 } 339 340 void nsWindowRoot::SetPopupNode(nsINode* aNode) { 341 mPopupNode = do_GetWeakReference(aNode); 342 } 343 344 nsIGlobalObject* nsWindowRoot::GetParentObject() { 345 return xpc::NativeGlobal(xpc::PrivilegedJunkScope()); 346 } 347 348 JSObject* nsWindowRoot::WrapObject(JSContext* aCx, 349 JS::Handle<JSObject*> aGivenProto) { 350 return mozilla::dom::WindowRoot_Binding::Wrap(aCx, this, aGivenProto); 351 } 352 353 void nsWindowRoot::AddBrowser(nsIRemoteTab* aBrowser) { 354 nsWeakPtr weakBrowser = do_GetWeakReference(aBrowser); 355 mWeakBrowsers.Insert(weakBrowser); 356 } 357 358 void nsWindowRoot::RemoveBrowser(nsIRemoteTab* aBrowser) { 359 nsWeakPtr weakBrowser = do_GetWeakReference(aBrowser); 360 mWeakBrowsers.Remove(weakBrowser); 361 } 362 363 void nsWindowRoot::EnumerateBrowsers(BrowserEnumerator aEnumFunc, void* aArg) { 364 // Collect strong references to all browsers in a separate array in 365 // case aEnumFunc alters mWeakBrowsers. 366 nsTArray<nsCOMPtr<nsIRemoteTab>> remoteTabs; 367 for (const auto& key : mWeakBrowsers) { 368 nsCOMPtr<nsIRemoteTab> remoteTab(do_QueryReferent(key)); 369 if (remoteTab) { 370 remoteTabs.AppendElement(remoteTab); 371 } 372 } 373 374 for (uint32_t i = 0; i < remoteTabs.Length(); ++i) { 375 aEnumFunc(remoteTabs[i], aArg); 376 } 377 } 378 379 /////////////////////////////////////////////////////////////////////////////////// 380 381 already_AddRefed<EventTarget> NS_NewWindowRoot(nsPIDOMWindowOuter* aWindow) { 382 nsCOMPtr<EventTarget> result = new nsWindowRoot(aWindow); 383 384 RefPtr<JSActorService> wasvc = JSActorService::GetSingleton(); 385 wasvc->RegisterChromeEventTarget(result); 386 387 return result.forget(); 388 }