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 "nsGlobalWindow.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"
38 # include "nsXULElement.h"
41 using namespace mozilla
;
42 using namespace mozilla::dom
;
44 nsWindowRoot::nsWindowRoot(nsPIDOMWindowOuter
* aWindow
) {
46 mShowFocusRings
= StaticPrefs::browser_display_show_focus_rings();
49 nsWindowRoot::~nsWindowRoot() {
50 if (mListenerManager
) {
51 mListenerManager
->Disconnect();
54 if (XRE_IsContentProcess()) {
55 JSActorService::UnregisterChromeEventTarget(this);
59 NS_IMPL_CYCLE_COLLECTION_CLASS(nsWindowRoot
)
61 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsWindowRoot
)
62 if (XRE_IsContentProcess()) {
63 JSActorService::UnregisterChromeEventTarget(tmp
);
66 NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow
)
67 NS_IMPL_CYCLE_COLLECTION_UNLINK(mListenerManager
)
68 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent
)
69 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
70 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
72 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsWindowRoot
)
73 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow
)
74 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager
)
75 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent
)
76 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
78 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(nsWindowRoot
)
80 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsWindowRoot
)
81 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
82 NS_INTERFACE_MAP_ENTRY(nsISupports
)
83 NS_INTERFACE_MAP_ENTRY(nsPIWindowRoot
)
84 NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget
)
87 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsWindowRoot
)
88 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsWindowRoot
)
90 bool nsWindowRoot::DispatchEvent(Event
& aEvent
, CallerType aCallerType
,
92 nsEventStatus status
= nsEventStatus_eIgnore
;
93 nsresult rv
= EventDispatcher::DispatchDOMEvent(
94 static_cast<EventTarget
*>(this), nullptr, &aEvent
, nullptr, &status
);
95 bool retval
= !aEvent
.DefaultPrevented(aCallerType
);
102 bool nsWindowRoot::ComputeDefaultWantsUntrusted(ErrorResult
& aRv
) {
106 EventListenerManager
* nsWindowRoot::GetOrCreateListenerManager() {
107 if (!mListenerManager
) {
109 new EventListenerManager(static_cast<EventTarget
*>(this));
112 return mListenerManager
;
115 EventListenerManager
* nsWindowRoot::GetExistingListenerManager() const {
116 return mListenerManager
;
119 void nsWindowRoot::GetEventTargetParent(EventChainPreVisitor
& aVisitor
) {
120 aVisitor
.mCanHandle
= true;
121 aVisitor
.mForceContentDispatch
= true; // FIXME! Bug 329119
122 // To keep mWindow alive
123 aVisitor
.mItemData
= static_cast<nsISupports
*>(mWindow
);
124 aVisitor
.SetParentTarget(mParent
, false);
127 nsresult
nsWindowRoot::PostHandleEvent(EventChainPostVisitor
& aVisitor
) {
131 nsPIDOMWindowOuter
* nsWindowRoot::GetOwnerGlobalForBindingsInternal() {
135 nsIGlobalObject
* nsWindowRoot::GetOwnerGlobal() const {
136 nsCOMPtr
<nsIGlobalObject
> global
=
137 do_QueryInterface(mWindow
->GetCurrentInnerWindow());
138 // We're still holding a ref to it, so returning the raw pointer is ok...
142 nsPIDOMWindowOuter
* nsWindowRoot::GetWindow() { return mWindow
; }
144 nsresult
nsWindowRoot::GetControllers(bool aForVisibleWindow
,
145 nsIControllers
** aResult
) {
148 // XXX: we should fix this so there's a generic interface that
149 // describes controllers, so this code would have no special
150 // knowledge of what object might have controllers.
152 nsFocusManager::SearchRange searchRange
=
153 aForVisibleWindow
? nsFocusManager::eIncludeVisibleDescendants
154 : nsFocusManager::eIncludeAllDescendants
;
155 nsCOMPtr
<nsPIDOMWindowOuter
> focusedWindow
;
156 nsIContent
* focusedContent
= nsFocusManager::GetFocusedDescendant(
157 mWindow
, searchRange
, getter_AddRefs(focusedWindow
));
158 if (focusedContent
) {
160 RefPtr
<nsXULElement
> xulElement
= nsXULElement::FromNode(focusedContent
);
163 *aResult
= xulElement
->GetControllers(rv
);
164 NS_IF_ADDREF(*aResult
);
165 return rv
.StealNSResult();
169 HTMLTextAreaElement
* htmlTextArea
=
170 HTMLTextAreaElement::FromNode(focusedContent
);
171 if (htmlTextArea
) return htmlTextArea
->GetControllers(aResult
);
173 HTMLInputElement
* htmlInputElement
=
174 HTMLInputElement::FromNode(focusedContent
);
175 if (htmlInputElement
) return htmlInputElement
->GetControllers(aResult
);
177 if (focusedContent
->IsEditable() && focusedWindow
)
178 return focusedWindow
->GetControllers(aResult
);
180 return focusedWindow
->GetControllers(aResult
);
186 nsresult
nsWindowRoot::GetControllerForCommand(const char* aCommand
,
187 bool aForVisibleWindow
,
188 nsIController
** _retval
) {
189 NS_ENSURE_ARG_POINTER(_retval
);
192 // If this is the parent process, check if a child browsing context from
193 // another process is focused, and ask if it has a controller actor that
194 // supports the command.
195 if (XRE_IsParentProcess()) {
196 nsFocusManager
* fm
= nsFocusManager::GetFocusManager();
198 return NS_ERROR_FAILURE
;
201 // Unfortunately, messages updating the active/focus state in the focus
202 // manager don't happen fast enough in the case when switching focus between
203 // processes when clicking on a chrome UI element while a child tab is
204 // focused, so we need to check whether the focus manager thinks a child
205 // frame is focused as well.
206 nsCOMPtr
<nsPIDOMWindowOuter
> focusedWindow
;
207 nsIContent
* focusedContent
= nsFocusManager::GetFocusedDescendant(
208 mWindow
, nsFocusManager::eIncludeAllDescendants
,
209 getter_AddRefs(focusedWindow
));
210 RefPtr
<nsFrameLoaderOwner
> loaderOwner
= do_QueryObject(focusedContent
);
212 // Only check browsing contexts if a remote frame is focused. If chrome is
213 // focused, just check the controllers directly below.
214 RefPtr
<nsFrameLoader
> frameLoader
= loaderOwner
->GetFrameLoader();
215 if (frameLoader
&& frameLoader
->IsRemoteFrame()) {
216 // GetActiveBrowsingContextInChrome actually returns the top-level
217 // browsing context if the focus is in a child process tab, or null if
218 // the focus is in chrome.
219 BrowsingContext
* focusedBC
=
220 fm
->GetActiveBrowsingContextInChrome()
221 ? fm
->GetFocusedBrowsingContextInChrome()
224 // At this point, it is known that a child process is focused, so ask
225 // its Controllers actor if the command is supported.
226 nsCOMPtr
<nsIController
> controller
= do_QueryActor(
227 "Controllers", focusedBC
->Canonical()->GetCurrentWindowGlobal());
230 controller
->SupportsCommand(aCommand
, &supported
);
232 controller
.forget(_retval
);
242 nsCOMPtr
<nsIControllers
> controllers
;
243 GetControllers(aForVisibleWindow
, getter_AddRefs(controllers
));
245 nsCOMPtr
<nsIController
> controller
;
246 controllers
->GetControllerForCommand(aCommand
,
247 getter_AddRefs(controller
));
249 controller
.forget(_retval
);
255 nsFocusManager::SearchRange searchRange
=
256 aForVisibleWindow
? nsFocusManager::eIncludeVisibleDescendants
257 : nsFocusManager::eIncludeAllDescendants
;
258 nsCOMPtr
<nsPIDOMWindowOuter
> focusedWindow
;
259 nsFocusManager::GetFocusedDescendant(mWindow
, searchRange
,
260 getter_AddRefs(focusedWindow
));
261 while (focusedWindow
) {
262 nsCOMPtr
<nsIControllers
> controllers
;
263 focusedWindow
->GetControllers(getter_AddRefs(controllers
));
265 nsCOMPtr
<nsIController
> controller
;
266 controllers
->GetControllerForCommand(aCommand
,
267 getter_AddRefs(controller
));
269 controller
.forget(_retval
);
274 // XXXndeakin P3 is this casting safe?
275 nsGlobalWindowOuter
* win
= nsGlobalWindowOuter::Cast(focusedWindow
);
276 focusedWindow
= win
->GetPrivateParent();
282 void nsWindowRoot::GetEnabledDisabledCommandsForControllers(
283 nsIControllers
* aControllers
, nsTHashSet
<nsCString
>& aCommandsHandled
,
284 nsTArray
<nsCString
>& aEnabledCommands
,
285 nsTArray
<nsCString
>& aDisabledCommands
) {
286 uint32_t controllerCount
;
287 aControllers
->GetControllerCount(&controllerCount
);
288 for (uint32_t c
= 0; c
< controllerCount
; c
++) {
289 nsCOMPtr
<nsIController
> controller
;
290 aControllers
->GetControllerAt(c
, getter_AddRefs(controller
));
292 nsCOMPtr
<nsICommandController
> commandController(
293 do_QueryInterface(controller
));
294 if (commandController
) {
295 // All of our default command controllers have 20-60 commands. Let's just
296 // leave enough space here for all of them so we probably don't need to
298 AutoTArray
<nsCString
, 64> commands
;
299 if (NS_SUCCEEDED(commandController
->GetSupportedCommands(commands
))) {
300 for (auto& commandStr
: commands
) {
301 // Use a hash to determine which commands have already been handled by
302 // earlier controllers, as the earlier controller's result should get
304 if (aCommandsHandled
.EnsureInserted(commandStr
)) {
305 // We inserted a new entry into aCommandsHandled.
306 bool enabled
= false;
307 controller
->IsCommandEnabled(commandStr
.get(), &enabled
);
310 aEnabledCommands
.AppendElement(commandStr
);
312 aDisabledCommands
.AppendElement(commandStr
);
321 void nsWindowRoot::GetEnabledDisabledCommands(
322 nsTArray
<nsCString
>& aEnabledCommands
,
323 nsTArray
<nsCString
>& aDisabledCommands
) {
324 nsTHashSet
<nsCString
> commandsHandled
;
326 nsCOMPtr
<nsIControllers
> controllers
;
327 GetControllers(false, getter_AddRefs(controllers
));
329 GetEnabledDisabledCommandsForControllers(
330 controllers
, commandsHandled
, aEnabledCommands
, aDisabledCommands
);
333 nsCOMPtr
<nsPIDOMWindowOuter
> focusedWindow
;
334 nsFocusManager::GetFocusedDescendant(mWindow
,
335 nsFocusManager::eIncludeAllDescendants
,
336 getter_AddRefs(focusedWindow
));
337 while (focusedWindow
) {
338 focusedWindow
->GetControllers(getter_AddRefs(controllers
));
340 GetEnabledDisabledCommandsForControllers(
341 controllers
, commandsHandled
, aEnabledCommands
, aDisabledCommands
);
344 nsGlobalWindowOuter
* win
= nsGlobalWindowOuter::Cast(focusedWindow
);
345 focusedWindow
= win
->GetPrivateParent();
349 already_AddRefed
<nsINode
> nsWindowRoot::GetPopupNode() {
350 nsCOMPtr
<nsINode
> popupNode
= do_QueryReferent(mPopupNode
);
351 return popupNode
.forget();
354 void nsWindowRoot::SetPopupNode(nsINode
* aNode
) {
355 mPopupNode
= do_GetWeakReference(aNode
);
358 nsIGlobalObject
* nsWindowRoot::GetParentObject() {
359 return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
362 JSObject
* nsWindowRoot::WrapObject(JSContext
* aCx
,
363 JS::Handle
<JSObject
*> aGivenProto
) {
364 return mozilla::dom::WindowRoot_Binding::Wrap(aCx
, this, aGivenProto
);
367 void nsWindowRoot::AddBrowser(nsIRemoteTab
* aBrowser
) {
368 nsWeakPtr weakBrowser
= do_GetWeakReference(aBrowser
);
369 mWeakBrowsers
.Insert(weakBrowser
);
372 void nsWindowRoot::RemoveBrowser(nsIRemoteTab
* aBrowser
) {
373 nsWeakPtr weakBrowser
= do_GetWeakReference(aBrowser
);
374 mWeakBrowsers
.Remove(weakBrowser
);
377 void nsWindowRoot::EnumerateBrowsers(BrowserEnumerator aEnumFunc
, void* aArg
) {
378 // Collect strong references to all browsers in a separate array in
379 // case aEnumFunc alters mWeakBrowsers.
380 nsTArray
<nsCOMPtr
<nsIRemoteTab
>> remoteTabs
;
381 for (const auto& key
: mWeakBrowsers
) {
382 nsCOMPtr
<nsIRemoteTab
> remoteTab(do_QueryReferent(key
));
384 remoteTabs
.AppendElement(remoteTab
);
388 for (uint32_t i
= 0; i
< remoteTabs
.Length(); ++i
) {
389 aEnumFunc(remoteTabs
[i
], aArg
);
393 ///////////////////////////////////////////////////////////////////////////////////
395 already_AddRefed
<EventTarget
> NS_NewWindowRoot(nsPIDOMWindowOuter
* aWindow
) {
396 nsCOMPtr
<EventTarget
> result
= new nsWindowRoot(aWindow
);
398 if (XRE_IsContentProcess()) {
399 RefPtr
<JSActorService
> wasvc
= JSActorService::GetSingleton();
400 wasvc
->RegisterChromeEventTarget(result
);
403 return result
.forget();