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"
13 #include "nsWindowRoot.h"
14 #include "nsPIDOMWindow.h"
15 #include "nsPresContext.h"
16 #include "nsLayoutCID.h"
17 #include "nsContentCID.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
) {
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
)
79 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsWindowRoot
)
80 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsWindowRoot
)
82 bool nsWindowRoot::DispatchEvent(Event
& aEvent
, CallerType aCallerType
,
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
);
94 bool nsWindowRoot::ComputeDefaultWantsUntrusted(ErrorResult
& aRv
) {
98 EventListenerManager
* nsWindowRoot::GetOrCreateListenerManager() {
99 if (!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
) {
123 nsPIDOMWindowOuter
* nsWindowRoot::GetOwnerGlobalForBindingsInternal() {
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...
134 nsPIDOMWindowOuter
* nsWindowRoot::GetWindow() { return mWindow
; }
136 nsresult
nsWindowRoot::GetControllers(bool aForVisibleWindow
,
137 nsIControllers
** aResult
) {
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
);
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
);
170 return focusedWindow
->GetControllers(aResult
);
176 nsresult
nsWindowRoot::GetControllerForCommand(const char* aCommand
,
177 bool aForVisibleWindow
,
178 nsIController
** _retval
) {
179 NS_ENSURE_ARG_POINTER(_retval
);
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();
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
);
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()
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());
220 controller
->SupportsCommand(aCommand
, &supported
);
222 controller
.forget(_retval
);
232 nsCOMPtr
<nsIControllers
> controllers
;
233 GetControllers(aForVisibleWindow
, getter_AddRefs(controllers
));
235 nsCOMPtr
<nsIController
> controller
;
236 controllers
->GetControllerForCommand(aCommand
,
237 getter_AddRefs(controller
));
239 controller
.forget(_retval
);
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
));
255 nsCOMPtr
<nsIController
> controller
;
256 controllers
->GetControllerForCommand(aCommand
,
257 getter_AddRefs(controller
));
259 controller
.forget(_retval
);
264 // XXXndeakin P3 is this casting safe?
265 nsGlobalWindowOuter
* win
= nsGlobalWindowOuter::Cast(focusedWindow
);
266 focusedWindow
= win
->GetPrivateParent();
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
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
294 if (aCommandsHandled
.EnsureInserted(commandStr
)) {
295 // We inserted a new entry into aCommandsHandled.
296 bool enabled
= false;
297 controller
->IsCommandEnabled(commandStr
.get(), &enabled
);
300 aEnabledCommands
.AppendElement(commandStr
);
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
));
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
));
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
));
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();