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