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