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