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 // Local Includes
8 #include "nsDocShellTreeOwner.h"
9 #include "nsWebBrowser.h"
10 
11 // Helper Classes
12 #include "nsContentUtils.h"
13 #include "nsSize.h"
14 #include "mozilla/ReflowInput.h"
15 #include "mozilla/ScopeExit.h"
16 #include "nsComponentManagerUtils.h"
17 #include "nsString.h"
18 #include "nsAtom.h"
19 #include "nsReadableUtils.h"
20 #include "nsUnicharUtils.h"
21 #include "mozilla/LookAndFeel.h"
22 
23 // Interfaces needed to be included
24 #include "nsPresContext.h"
25 #include "nsITooltipListener.h"
26 #include "nsINode.h"
27 #include "Link.h"
28 #include "mozilla/dom/Document.h"
29 #include "mozilla/dom/Element.h"
30 #include "mozilla/dom/MouseEvent.h"
31 #include "mozilla/dom/SVGTitleElement.h"
32 #include "nsIFormControl.h"
33 #include "nsIWebNavigation.h"
34 #include "nsPIDOMWindow.h"
35 #include "nsPIWindowRoot.h"
36 #include "nsIWindowWatcher.h"
37 #include "nsPIWindowWatcher.h"
38 #include "nsIPrompt.h"
39 #include "nsIRemoteTab.h"
40 #include "nsIBrowserChild.h"
41 #include "nsRect.h"
42 #include "nsIWebBrowserChromeFocus.h"
43 #include "nsIContent.h"
44 #include "nsServiceManagerUtils.h"
45 #include "nsViewManager.h"
46 #include "nsView.h"
47 #include "nsIConstraintValidation.h"
48 #include "mozilla/Attributes.h"
49 #include "mozilla/EventListenerManager.h"
50 #include "mozilla/dom/DragEvent.h"
51 #include "mozilla/dom/Event.h"     // for Event
52 #include "mozilla/dom/File.h"      // for input type=file
53 #include "mozilla/dom/FileList.h"  // for input type=file
54 #include "mozilla/dom/LoadURIOptionsBinding.h"
55 #include "mozilla/PresShell.h"
56 #include "mozilla/TextEvents.h"
57 
58 using namespace mozilla;
59 using namespace mozilla::dom;
60 
61 // A helper routine that navigates the tricky path from a |nsWebBrowser| to
62 // a |EventTarget| via the window root and chrome event handler.
GetDOMEventTarget(nsWebBrowser * aInBrowser,EventTarget ** aTarget)63 static nsresult GetDOMEventTarget(nsWebBrowser* aInBrowser,
64                                   EventTarget** aTarget) {
65   if (!aInBrowser) {
66     return NS_ERROR_INVALID_POINTER;
67   }
68 
69   nsCOMPtr<mozIDOMWindowProxy> domWindow;
70   aInBrowser->GetContentDOMWindow(getter_AddRefs(domWindow));
71   if (!domWindow) {
72     return NS_ERROR_FAILURE;
73   }
74 
75   auto* outerWindow = nsPIDOMWindowOuter::From(domWindow);
76   nsPIDOMWindowOuter* rootWindow = outerWindow->GetPrivateRoot();
77   NS_ENSURE_TRUE(rootWindow, NS_ERROR_FAILURE);
78   nsCOMPtr<EventTarget> target = rootWindow->GetChromeEventHandler();
79   NS_ENSURE_TRUE(target, NS_ERROR_FAILURE);
80   target.forget(aTarget);
81 
82   return NS_OK;
83 }
84 
nsDocShellTreeOwner()85 nsDocShellTreeOwner::nsDocShellTreeOwner()
86     : mWebBrowser(nullptr),
87       mTreeOwner(nullptr),
88       mPrimaryContentShell(nullptr),
89       mWebBrowserChrome(nullptr),
90       mOwnerWin(nullptr),
91       mOwnerRequestor(nullptr) {}
92 
~nsDocShellTreeOwner()93 nsDocShellTreeOwner::~nsDocShellTreeOwner() { RemoveChromeListeners(); }
94 
95 NS_IMPL_ADDREF(nsDocShellTreeOwner)
NS_IMPL_RELEASE(nsDocShellTreeOwner)96 NS_IMPL_RELEASE(nsDocShellTreeOwner)
97 
98 NS_INTERFACE_MAP_BEGIN(nsDocShellTreeOwner)
99   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDocShellTreeOwner)
100   NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeOwner)
101   NS_INTERFACE_MAP_ENTRY(nsIBaseWindow)
102   NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
103   NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
104   NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
105   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
106 NS_INTERFACE_MAP_END
107 
108 //*****************************************************************************
109 // nsDocShellTreeOwner::nsIInterfaceRequestor
110 //*****************************************************************************
111 
112 NS_IMETHODIMP
113 nsDocShellTreeOwner::GetInterface(const nsIID& aIID, void** aSink) {
114   NS_ENSURE_ARG_POINTER(aSink);
115 
116   if (NS_SUCCEEDED(QueryInterface(aIID, aSink))) {
117     return NS_OK;
118   }
119 
120   if (aIID.Equals(NS_GET_IID(nsIWebBrowserChromeFocus))) {
121     if (mWebBrowserChromeWeak != nullptr) {
122       return mWebBrowserChromeWeak->QueryReferent(aIID, aSink);
123     }
124     return mOwnerWin->QueryInterface(aIID, aSink);
125   }
126 
127   if (aIID.Equals(NS_GET_IID(nsIPrompt))) {
128     nsCOMPtr<nsIPrompt> prompt;
129     EnsurePrompter();
130     prompt = mPrompter;
131     if (prompt) {
132       prompt.forget(aSink);
133       return NS_OK;
134     }
135     return NS_NOINTERFACE;
136   }
137 
138   if (aIID.Equals(NS_GET_IID(nsIAuthPrompt))) {
139     nsCOMPtr<nsIAuthPrompt> prompt;
140     EnsureAuthPrompter();
141     prompt = mAuthPrompter;
142     if (prompt) {
143       prompt.forget(aSink);
144       return NS_OK;
145     }
146     return NS_NOINTERFACE;
147   }
148 
149   nsCOMPtr<nsIInterfaceRequestor> req = GetOwnerRequestor();
150   if (req) {
151     return req->GetInterface(aIID, aSink);
152   }
153 
154   return NS_NOINTERFACE;
155 }
156 
157 //*****************************************************************************
158 // nsDocShellTreeOwner::nsIDocShellTreeOwner
159 //*****************************************************************************
160 
EnsurePrompter()161 void nsDocShellTreeOwner::EnsurePrompter() {
162   if (mPrompter) {
163     return;
164   }
165 
166   nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
167   if (wwatch && mWebBrowser) {
168     nsCOMPtr<mozIDOMWindowProxy> domWindow;
169     mWebBrowser->GetContentDOMWindow(getter_AddRefs(domWindow));
170     if (domWindow) {
171       wwatch->GetNewPrompter(domWindow, getter_AddRefs(mPrompter));
172     }
173   }
174 }
175 
EnsureAuthPrompter()176 void nsDocShellTreeOwner::EnsureAuthPrompter() {
177   if (mAuthPrompter) {
178     return;
179   }
180 
181   nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
182   if (wwatch && mWebBrowser) {
183     nsCOMPtr<mozIDOMWindowProxy> domWindow;
184     mWebBrowser->GetContentDOMWindow(getter_AddRefs(domWindow));
185     if (domWindow) {
186       wwatch->GetNewAuthPrompter(domWindow, getter_AddRefs(mAuthPrompter));
187     }
188   }
189 }
190 
AddToWatcher()191 void nsDocShellTreeOwner::AddToWatcher() {
192   if (mWebBrowser) {
193     nsCOMPtr<mozIDOMWindowProxy> domWindow;
194     mWebBrowser->GetContentDOMWindow(getter_AddRefs(domWindow));
195     if (domWindow) {
196       nsCOMPtr<nsPIWindowWatcher> wwatch(
197           do_GetService(NS_WINDOWWATCHER_CONTRACTID));
198       if (wwatch) {
199         nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome();
200         if (webBrowserChrome) {
201           wwatch->AddWindow(domWindow, webBrowserChrome);
202         }
203       }
204     }
205   }
206 }
207 
RemoveFromWatcher()208 void nsDocShellTreeOwner::RemoveFromWatcher() {
209   if (mWebBrowser) {
210     nsCOMPtr<mozIDOMWindowProxy> domWindow;
211     mWebBrowser->GetContentDOMWindow(getter_AddRefs(domWindow));
212     if (domWindow) {
213       nsCOMPtr<nsPIWindowWatcher> wwatch(
214           do_GetService(NS_WINDOWWATCHER_CONTRACTID));
215       if (wwatch) {
216         wwatch->RemoveWindow(domWindow);
217       }
218     }
219   }
220 }
221 
EnsureContentTreeOwner()222 void nsDocShellTreeOwner::EnsureContentTreeOwner() {
223   if (mContentTreeOwner) {
224     return;
225   }
226 
227   mContentTreeOwner = new nsDocShellTreeOwner();
228   nsCOMPtr<nsIWebBrowserChrome> browserChrome = GetWebBrowserChrome();
229   if (browserChrome) {
230     mContentTreeOwner->SetWebBrowserChrome(browserChrome);
231   }
232 
233   if (mWebBrowser) {
234     mContentTreeOwner->WebBrowser(mWebBrowser);
235   }
236 }
237 
238 NS_IMETHODIMP
ContentShellAdded(nsIDocShellTreeItem * aContentShell,bool aPrimary)239 nsDocShellTreeOwner::ContentShellAdded(nsIDocShellTreeItem* aContentShell,
240                                        bool aPrimary) {
241   if (mTreeOwner) return mTreeOwner->ContentShellAdded(aContentShell, aPrimary);
242 
243   EnsureContentTreeOwner();
244   aContentShell->SetTreeOwner(mContentTreeOwner);
245 
246   if (aPrimary) {
247     mPrimaryContentShell = aContentShell;
248     mPrimaryRemoteTab = nullptr;
249   }
250   return NS_OK;
251 }
252 
253 NS_IMETHODIMP
ContentShellRemoved(nsIDocShellTreeItem * aContentShell)254 nsDocShellTreeOwner::ContentShellRemoved(nsIDocShellTreeItem* aContentShell) {
255   if (mTreeOwner) {
256     return mTreeOwner->ContentShellRemoved(aContentShell);
257   }
258 
259   if (mPrimaryContentShell == aContentShell) {
260     mPrimaryContentShell = nullptr;
261   }
262 
263   return NS_OK;
264 }
265 
266 NS_IMETHODIMP
GetPrimaryContentShell(nsIDocShellTreeItem ** aShell)267 nsDocShellTreeOwner::GetPrimaryContentShell(nsIDocShellTreeItem** aShell) {
268   NS_ENSURE_ARG_POINTER(aShell);
269 
270   if (mTreeOwner) {
271     return mTreeOwner->GetPrimaryContentShell(aShell);
272   }
273 
274   nsCOMPtr<nsIDocShellTreeItem> shell;
275   if (!mPrimaryRemoteTab) {
276     shell =
277         mPrimaryContentShell ? mPrimaryContentShell : mWebBrowser->mDocShell;
278   }
279   shell.forget(aShell);
280 
281   return NS_OK;
282 }
283 
284 NS_IMETHODIMP
RemoteTabAdded(nsIRemoteTab * aTab,bool aPrimary)285 nsDocShellTreeOwner::RemoteTabAdded(nsIRemoteTab* aTab, bool aPrimary) {
286   if (mTreeOwner) {
287     return mTreeOwner->RemoteTabAdded(aTab, aPrimary);
288   }
289 
290   if (aPrimary) {
291     mPrimaryRemoteTab = aTab;
292     mPrimaryContentShell = nullptr;
293   } else if (mPrimaryRemoteTab == aTab) {
294     mPrimaryRemoteTab = nullptr;
295   }
296 
297   return NS_OK;
298 }
299 
300 NS_IMETHODIMP
RemoteTabRemoved(nsIRemoteTab * aTab)301 nsDocShellTreeOwner::RemoteTabRemoved(nsIRemoteTab* aTab) {
302   if (mTreeOwner) {
303     return mTreeOwner->RemoteTabRemoved(aTab);
304   }
305 
306   if (aTab == mPrimaryRemoteTab) {
307     mPrimaryRemoteTab = nullptr;
308   }
309 
310   return NS_OK;
311 }
312 
313 NS_IMETHODIMP
GetPrimaryRemoteTab(nsIRemoteTab ** aTab)314 nsDocShellTreeOwner::GetPrimaryRemoteTab(nsIRemoteTab** aTab) {
315   if (mTreeOwner) {
316     return mTreeOwner->GetPrimaryRemoteTab(aTab);
317   }
318 
319   nsCOMPtr<nsIRemoteTab> tab = mPrimaryRemoteTab;
320   tab.forget(aTab);
321   return NS_OK;
322 }
323 
324 NS_IMETHODIMP
GetPrimaryContentSize(int32_t * aWidth,int32_t * aHeight)325 nsDocShellTreeOwner::GetPrimaryContentSize(int32_t* aWidth, int32_t* aHeight) {
326   return NS_ERROR_NOT_IMPLEMENTED;
327 }
328 
329 NS_IMETHODIMP
SetPrimaryContentSize(int32_t aWidth,int32_t aHeight)330 nsDocShellTreeOwner::SetPrimaryContentSize(int32_t aWidth, int32_t aHeight) {
331   return NS_ERROR_NOT_IMPLEMENTED;
332 }
333 
334 NS_IMETHODIMP
GetRootShellSize(int32_t * aWidth,int32_t * aHeight)335 nsDocShellTreeOwner::GetRootShellSize(int32_t* aWidth, int32_t* aHeight) {
336   return NS_ERROR_NOT_IMPLEMENTED;
337 }
338 
339 NS_IMETHODIMP
SetRootShellSize(int32_t aWidth,int32_t aHeight)340 nsDocShellTreeOwner::SetRootShellSize(int32_t aWidth, int32_t aHeight) {
341   return NS_ERROR_NOT_IMPLEMENTED;
342 }
343 
344 NS_IMETHODIMP
SizeShellTo(nsIDocShellTreeItem * aShellItem,int32_t aCX,int32_t aCY)345 nsDocShellTreeOwner::SizeShellTo(nsIDocShellTreeItem* aShellItem, int32_t aCX,
346                                  int32_t aCY) {
347   nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome();
348 
349   NS_ENSURE_STATE(mTreeOwner || webBrowserChrome);
350 
351   if (nsCOMPtr<nsIDocShellTreeOwner> treeOwner = mTreeOwner) {
352     return treeOwner->SizeShellTo(aShellItem, aCX, aCY);
353   }
354 
355   if (aShellItem == mWebBrowser->mDocShell) {
356     nsCOMPtr<nsIBrowserChild> browserChild =
357         do_QueryInterface(webBrowserChrome);
358     if (browserChild) {
359       // The XUL window to resize is in the parent process, but there we
360       // won't be able to get aShellItem to do the hack in
361       // AppWindow::SizeShellTo, so let's send the width and height of
362       // aShellItem too.
363       nsCOMPtr<nsIBaseWindow> shellAsWin(do_QueryInterface(aShellItem));
364       NS_ENSURE_TRUE(shellAsWin, NS_ERROR_FAILURE);
365 
366       int32_t width = 0;
367       int32_t height = 0;
368       shellAsWin->GetSize(&width, &height);
369       return browserChild->RemoteSizeShellTo(aCX, aCY, width, height);
370     }
371     // XXX: this is weird, but we used to call a method here
372     // (webBrowserChrome->SizeBrowserTo()) whose implementations all failed
373     // like this, so...
374     return NS_ERROR_NOT_IMPLEMENTED;
375   }
376 
377   NS_ENSURE_TRUE(aShellItem, NS_ERROR_FAILURE);
378 
379   RefPtr<Document> document = aShellItem->GetDocument();
380   NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
381 
382   NS_ENSURE_TRUE(document->GetDocumentElement(), NS_ERROR_FAILURE);
383 
384   // Set the preferred Size
385   // XXX
386   NS_ERROR("Implement this");
387   /*
388   Set the preferred size on the aShellItem.
389   */
390 
391   RefPtr<nsPresContext> presContext = mWebBrowser->mDocShell->GetPresContext();
392   NS_ENSURE_TRUE(presContext, NS_ERROR_FAILURE);
393 
394   RefPtr<PresShell> presShell = presContext->GetPresShell();
395   NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
396 
397   NS_ENSURE_SUCCESS(
398       presShell->ResizeReflow(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE),
399       NS_ERROR_FAILURE);
400 
401   // XXX: this is weird, but we used to call a method here
402   // (webBrowserChrome->SizeBrowserTo()) whose implementations all failed like
403   // this, so...
404   return NS_ERROR_NOT_IMPLEMENTED;
405 }
406 
407 NS_IMETHODIMP
SetPersistence(bool aPersistPosition,bool aPersistSize,bool aPersistSizeMode)408 nsDocShellTreeOwner::SetPersistence(bool aPersistPosition, bool aPersistSize,
409                                     bool aPersistSizeMode) {
410   return NS_ERROR_NOT_IMPLEMENTED;
411 }
412 
413 NS_IMETHODIMP
GetPersistence(bool * aPersistPosition,bool * aPersistSize,bool * aPersistSizeMode)414 nsDocShellTreeOwner::GetPersistence(bool* aPersistPosition, bool* aPersistSize,
415                                     bool* aPersistSizeMode) {
416   return NS_ERROR_NOT_IMPLEMENTED;
417 }
418 
419 NS_IMETHODIMP
GetTabCount(uint32_t * aResult)420 nsDocShellTreeOwner::GetTabCount(uint32_t* aResult) {
421   if (mTreeOwner) {
422     return mTreeOwner->GetTabCount(aResult);
423   }
424 
425   *aResult = 0;
426   return NS_OK;
427 }
428 
429 NS_IMETHODIMP
GetHasPrimaryContent(bool * aResult)430 nsDocShellTreeOwner::GetHasPrimaryContent(bool* aResult) {
431   *aResult = mPrimaryRemoteTab || mPrimaryContentShell;
432   return NS_OK;
433 }
434 
435 //*****************************************************************************
436 // nsDocShellTreeOwner::nsIBaseWindow
437 //*****************************************************************************
438 
439 NS_IMETHODIMP
InitWindow(nativeWindow aParentNativeWindow,nsIWidget * aParentWidget,int32_t aX,int32_t aY,int32_t aCX,int32_t aCY)440 nsDocShellTreeOwner::InitWindow(nativeWindow aParentNativeWindow,
441                                 nsIWidget* aParentWidget, int32_t aX,
442                                 int32_t aY, int32_t aCX, int32_t aCY) {
443   return NS_ERROR_NULL_POINTER;
444 }
445 
446 NS_IMETHODIMP
Destroy()447 nsDocShellTreeOwner::Destroy() {
448   nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome();
449   if (webBrowserChrome) {
450     // XXX: this is weird, but we used to call a method here
451     // (webBrowserChrome->DestroyBrowserWindow()) whose implementations all
452     // failed like this, so...
453     return NS_ERROR_NOT_IMPLEMENTED;
454   }
455 
456   return NS_ERROR_NULL_POINTER;
457 }
458 
459 NS_IMETHODIMP
GetUnscaledDevicePixelsPerCSSPixel(double * aScale)460 nsDocShellTreeOwner::GetUnscaledDevicePixelsPerCSSPixel(double* aScale) {
461   if (mWebBrowser) {
462     return mWebBrowser->GetUnscaledDevicePixelsPerCSSPixel(aScale);
463   }
464 
465   *aScale = 1.0;
466   return NS_OK;
467 }
468 
469 NS_IMETHODIMP
GetDevicePixelsPerDesktopPixel(double * aScale)470 nsDocShellTreeOwner::GetDevicePixelsPerDesktopPixel(double* aScale) {
471   if (mWebBrowser) {
472     return mWebBrowser->GetDevicePixelsPerDesktopPixel(aScale);
473   }
474 
475   *aScale = 1.0;
476   return NS_OK;
477 }
478 
479 NS_IMETHODIMP
SetPositionDesktopPix(int32_t aX,int32_t aY)480 nsDocShellTreeOwner::SetPositionDesktopPix(int32_t aX, int32_t aY) {
481   if (mWebBrowser) {
482     nsresult rv = mWebBrowser->SetPositionDesktopPix(aX, aY);
483     NS_ENSURE_SUCCESS(rv, rv);
484   }
485 
486   double scale = 1.0;
487   GetDevicePixelsPerDesktopPixel(&scale);
488   return SetPosition(NSToIntRound(aX * scale), NSToIntRound(aY * scale));
489 }
490 
491 NS_IMETHODIMP
SetPosition(int32_t aX,int32_t aY)492 nsDocShellTreeOwner::SetPosition(int32_t aX, int32_t aY) {
493   nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin();
494   if (ownerWin) {
495     return ownerWin->SetDimensions(nsIEmbeddingSiteWindow::DIM_FLAGS_POSITION,
496                                    aX, aY, 0, 0);
497   }
498   return NS_ERROR_NULL_POINTER;
499 }
500 
501 NS_IMETHODIMP
GetPosition(int32_t * aX,int32_t * aY)502 nsDocShellTreeOwner::GetPosition(int32_t* aX, int32_t* aY) {
503   nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin();
504   if (ownerWin) {
505     return ownerWin->GetDimensions(nsIEmbeddingSiteWindow::DIM_FLAGS_POSITION,
506                                    aX, aY, nullptr, nullptr);
507   }
508   return NS_ERROR_NULL_POINTER;
509 }
510 
511 NS_IMETHODIMP
SetSize(int32_t aCX,int32_t aCY,bool aRepaint)512 nsDocShellTreeOwner::SetSize(int32_t aCX, int32_t aCY, bool aRepaint) {
513   nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin();
514   if (ownerWin) {
515     return ownerWin->SetDimensions(nsIEmbeddingSiteWindow::DIM_FLAGS_SIZE_OUTER,
516                                    0, 0, aCX, aCY);
517   }
518   return NS_ERROR_NULL_POINTER;
519 }
520 
521 NS_IMETHODIMP
GetSize(int32_t * aCX,int32_t * aCY)522 nsDocShellTreeOwner::GetSize(int32_t* aCX, int32_t* aCY) {
523   nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin();
524   if (ownerWin) {
525     return ownerWin->GetDimensions(nsIEmbeddingSiteWindow::DIM_FLAGS_SIZE_OUTER,
526                                    nullptr, nullptr, aCX, aCY);
527   }
528   return NS_ERROR_NULL_POINTER;
529 }
530 
531 NS_IMETHODIMP
SetPositionAndSize(int32_t aX,int32_t aY,int32_t aCX,int32_t aCY,uint32_t aFlags)532 nsDocShellTreeOwner::SetPositionAndSize(int32_t aX, int32_t aY, int32_t aCX,
533                                         int32_t aCY, uint32_t aFlags) {
534   nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin();
535   if (ownerWin) {
536     return ownerWin->SetDimensions(
537         nsIEmbeddingSiteWindow::DIM_FLAGS_SIZE_OUTER |
538             nsIEmbeddingSiteWindow::DIM_FLAGS_POSITION,
539         aX, aY, aCX, aCY);
540   }
541   return NS_ERROR_NULL_POINTER;
542 }
543 
544 NS_IMETHODIMP
GetPositionAndSize(int32_t * aX,int32_t * aY,int32_t * aCX,int32_t * aCY)545 nsDocShellTreeOwner::GetPositionAndSize(int32_t* aX, int32_t* aY, int32_t* aCX,
546                                         int32_t* aCY) {
547   nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin();
548   if (ownerWin) {
549     return ownerWin->GetDimensions(
550         nsIEmbeddingSiteWindow::DIM_FLAGS_SIZE_OUTER |
551             nsIEmbeddingSiteWindow::DIM_FLAGS_POSITION,
552         aX, aY, aCX, aCY);
553   }
554   return NS_ERROR_NULL_POINTER;
555 }
556 
557 NS_IMETHODIMP
Repaint(bool aForce)558 nsDocShellTreeOwner::Repaint(bool aForce) { return NS_ERROR_NULL_POINTER; }
559 
560 NS_IMETHODIMP
GetParentWidget(nsIWidget ** aParentWidget)561 nsDocShellTreeOwner::GetParentWidget(nsIWidget** aParentWidget) {
562   return NS_ERROR_NULL_POINTER;
563 }
564 
565 NS_IMETHODIMP
SetParentWidget(nsIWidget * aParentWidget)566 nsDocShellTreeOwner::SetParentWidget(nsIWidget* aParentWidget) {
567   return NS_ERROR_NULL_POINTER;
568 }
569 
570 NS_IMETHODIMP
GetParentNativeWindow(nativeWindow * aParentNativeWindow)571 nsDocShellTreeOwner::GetParentNativeWindow(nativeWindow* aParentNativeWindow) {
572   nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin();
573   if (ownerWin) {
574     return ownerWin->GetSiteWindow(aParentNativeWindow);
575   }
576   return NS_ERROR_NULL_POINTER;
577 }
578 
579 NS_IMETHODIMP
SetParentNativeWindow(nativeWindow aParentNativeWindow)580 nsDocShellTreeOwner::SetParentNativeWindow(nativeWindow aParentNativeWindow) {
581   return NS_ERROR_NULL_POINTER;
582 }
583 
584 NS_IMETHODIMP
GetNativeHandle(nsAString & aNativeHandle)585 nsDocShellTreeOwner::GetNativeHandle(nsAString& aNativeHandle) {
586   // the nativeHandle should be accessed from nsIAppWindow
587   return NS_ERROR_NOT_IMPLEMENTED;
588 }
589 
590 NS_IMETHODIMP
GetVisibility(bool * aVisibility)591 nsDocShellTreeOwner::GetVisibility(bool* aVisibility) {
592   nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin();
593   if (ownerWin) {
594     return ownerWin->GetVisibility(aVisibility);
595   }
596   return NS_ERROR_NULL_POINTER;
597 }
598 
599 NS_IMETHODIMP
SetVisibility(bool aVisibility)600 nsDocShellTreeOwner::SetVisibility(bool aVisibility) {
601   nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin();
602   if (ownerWin) {
603     return ownerWin->SetVisibility(aVisibility);
604   }
605   return NS_ERROR_NULL_POINTER;
606 }
607 
608 NS_IMETHODIMP
GetEnabled(bool * aEnabled)609 nsDocShellTreeOwner::GetEnabled(bool* aEnabled) {
610   NS_ENSURE_ARG_POINTER(aEnabled);
611   *aEnabled = true;
612   return NS_ERROR_NOT_IMPLEMENTED;
613 }
614 
615 NS_IMETHODIMP
SetEnabled(bool aEnabled)616 nsDocShellTreeOwner::SetEnabled(bool aEnabled) {
617   return NS_ERROR_NOT_IMPLEMENTED;
618 }
619 
620 NS_IMETHODIMP
GetMainWidget(nsIWidget ** aMainWidget)621 nsDocShellTreeOwner::GetMainWidget(nsIWidget** aMainWidget) {
622   return NS_ERROR_NULL_POINTER;
623 }
624 
625 NS_IMETHODIMP
GetTitle(nsAString & aTitle)626 nsDocShellTreeOwner::GetTitle(nsAString& aTitle) {
627   nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin();
628   if (ownerWin) {
629     return ownerWin->GetTitle(aTitle);
630   }
631   return NS_ERROR_NULL_POINTER;
632 }
633 
634 NS_IMETHODIMP
SetTitle(const nsAString & aTitle)635 nsDocShellTreeOwner::SetTitle(const nsAString& aTitle) {
636   nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin();
637   if (ownerWin) {
638     return ownerWin->SetTitle(aTitle);
639   }
640   return NS_ERROR_NULL_POINTER;
641 }
642 
643 //*****************************************************************************
644 // nsDocShellTreeOwner::nsIWebProgressListener
645 //*****************************************************************************
646 
647 NS_IMETHODIMP
OnProgressChange(nsIWebProgress * aProgress,nsIRequest * aRequest,int32_t aCurSelfProgress,int32_t aMaxSelfProgress,int32_t aCurTotalProgress,int32_t aMaxTotalProgress)648 nsDocShellTreeOwner::OnProgressChange(nsIWebProgress* aProgress,
649                                       nsIRequest* aRequest,
650                                       int32_t aCurSelfProgress,
651                                       int32_t aMaxSelfProgress,
652                                       int32_t aCurTotalProgress,
653                                       int32_t aMaxTotalProgress) {
654   // In the absence of DOM document creation event, this method is the
655   // most convenient place to install the mouse listener on the
656   // DOM document.
657   return AddChromeListeners();
658 }
659 
660 NS_IMETHODIMP
OnStateChange(nsIWebProgress * aProgress,nsIRequest * aRequest,uint32_t aProgressStateFlags,nsresult aStatus)661 nsDocShellTreeOwner::OnStateChange(nsIWebProgress* aProgress,
662                                    nsIRequest* aRequest,
663                                    uint32_t aProgressStateFlags,
664                                    nsresult aStatus) {
665   return NS_OK;
666 }
667 
668 NS_IMETHODIMP
OnLocationChange(nsIWebProgress * aWebProgress,nsIRequest * aRequest,nsIURI * aURI,uint32_t aFlags)669 nsDocShellTreeOwner::OnLocationChange(nsIWebProgress* aWebProgress,
670                                       nsIRequest* aRequest, nsIURI* aURI,
671                                       uint32_t aFlags) {
672   if (mChromeTooltipListener && aWebProgress &&
673       !(aFlags & nsIWebProgressListener::LOCATION_CHANGE_SAME_DOCUMENT) &&
674       mChromeTooltipListener->WebProgressShowedTooltip(aWebProgress)) {
675     mChromeTooltipListener->HideTooltip();
676   }
677   return NS_OK;
678 }
679 
680 NS_IMETHODIMP
OnStatusChange(nsIWebProgress * aWebProgress,nsIRequest * aRequest,nsresult aStatus,const char16_t * aMessage)681 nsDocShellTreeOwner::OnStatusChange(nsIWebProgress* aWebProgress,
682                                     nsIRequest* aRequest, nsresult aStatus,
683                                     const char16_t* aMessage) {
684   return NS_OK;
685 }
686 
687 NS_IMETHODIMP
OnSecurityChange(nsIWebProgress * aWebProgress,nsIRequest * aRequest,uint32_t aState)688 nsDocShellTreeOwner::OnSecurityChange(nsIWebProgress* aWebProgress,
689                                       nsIRequest* aRequest, uint32_t aState) {
690   return NS_OK;
691 }
692 
693 NS_IMETHODIMP
OnContentBlockingEvent(nsIWebProgress * aWebProgress,nsIRequest * aRequest,uint32_t aEvent)694 nsDocShellTreeOwner::OnContentBlockingEvent(nsIWebProgress* aWebProgress,
695                                             nsIRequest* aRequest,
696                                             uint32_t aEvent) {
697   return NS_OK;
698 }
699 
700 //*****************************************************************************
701 // nsDocShellTreeOwner: Accessors
702 //*****************************************************************************
703 
WebBrowser(nsWebBrowser * aWebBrowser)704 void nsDocShellTreeOwner::WebBrowser(nsWebBrowser* aWebBrowser) {
705   if (!aWebBrowser) {
706     RemoveChromeListeners();
707   }
708   if (aWebBrowser != mWebBrowser) {
709     mPrompter = nullptr;
710     mAuthPrompter = nullptr;
711   }
712 
713   mWebBrowser = aWebBrowser;
714 
715   if (mContentTreeOwner) {
716     mContentTreeOwner->WebBrowser(aWebBrowser);
717     if (!aWebBrowser) {
718       mContentTreeOwner = nullptr;
719     }
720   }
721 }
722 
WebBrowser()723 nsWebBrowser* nsDocShellTreeOwner::WebBrowser() { return mWebBrowser; }
724 
725 NS_IMETHODIMP
SetTreeOwner(nsIDocShellTreeOwner * aTreeOwner)726 nsDocShellTreeOwner::SetTreeOwner(nsIDocShellTreeOwner* aTreeOwner) {
727   if (aTreeOwner) {
728     nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome(do_GetInterface(aTreeOwner));
729     NS_ENSURE_TRUE(webBrowserChrome, NS_ERROR_INVALID_ARG);
730     NS_ENSURE_SUCCESS(SetWebBrowserChrome(webBrowserChrome),
731                       NS_ERROR_INVALID_ARG);
732     mTreeOwner = aTreeOwner;
733   } else {
734     mTreeOwner = nullptr;
735     nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome();
736     if (!webBrowserChrome) {
737       NS_ENSURE_SUCCESS(SetWebBrowserChrome(nullptr), NS_ERROR_FAILURE);
738     }
739   }
740 
741   return NS_OK;
742 }
743 
744 NS_IMETHODIMP
SetWebBrowserChrome(nsIWebBrowserChrome * aWebBrowserChrome)745 nsDocShellTreeOwner::SetWebBrowserChrome(
746     nsIWebBrowserChrome* aWebBrowserChrome) {
747   if (!aWebBrowserChrome) {
748     mWebBrowserChrome = nullptr;
749     mOwnerWin = nullptr;
750     mOwnerRequestor = nullptr;
751     mWebBrowserChromeWeak = nullptr;
752   } else {
753     nsCOMPtr<nsISupportsWeakReference> supportsweak =
754         do_QueryInterface(aWebBrowserChrome);
755     if (supportsweak) {
756       supportsweak->GetWeakReference(getter_AddRefs(mWebBrowserChromeWeak));
757     } else {
758       nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin(
759           do_QueryInterface(aWebBrowserChrome));
760       nsCOMPtr<nsIInterfaceRequestor> requestor(
761           do_QueryInterface(aWebBrowserChrome));
762 
763       // it's ok for ownerWin or requestor to be null.
764       mWebBrowserChrome = aWebBrowserChrome;
765       mOwnerWin = ownerWin;
766       mOwnerRequestor = requestor;
767     }
768   }
769 
770   if (mContentTreeOwner) {
771     mContentTreeOwner->SetWebBrowserChrome(aWebBrowserChrome);
772   }
773 
774   return NS_OK;
775 }
776 
777 // Hook up things to the chrome like context menus and tooltips, if the chrome
778 // has implemented the right interfaces.
779 NS_IMETHODIMP
AddChromeListeners()780 nsDocShellTreeOwner::AddChromeListeners() {
781   nsresult rv = NS_OK;
782 
783   nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome();
784   if (!webBrowserChrome) {
785     return NS_ERROR_FAILURE;
786   }
787 
788   // install tooltips
789   if (!mChromeTooltipListener) {
790     nsCOMPtr<nsITooltipListener> tooltipListener(
791         do_QueryInterface(webBrowserChrome));
792     if (tooltipListener) {
793       mChromeTooltipListener =
794           new ChromeTooltipListener(mWebBrowser, webBrowserChrome);
795       rv = mChromeTooltipListener->AddChromeListeners();
796     }
797   }
798 
799   nsCOMPtr<EventTarget> target;
800   GetDOMEventTarget(mWebBrowser, getter_AddRefs(target));
801 
802   // register dragover and drop event listeners with the listener manager
803   MOZ_ASSERT(target, "how does this happen? (see bug 1659758)");
804   if (target) {
805     if (EventListenerManager* elmP = target->GetOrCreateListenerManager()) {
806       elmP->AddEventListenerByType(this, u"dragover"_ns,
807                                    TrustedEventsAtSystemGroupBubble());
808       elmP->AddEventListenerByType(this, u"drop"_ns,
809                                    TrustedEventsAtSystemGroupBubble());
810     }
811   }
812 
813   return rv;
814 }
815 
816 NS_IMETHODIMP
RemoveChromeListeners()817 nsDocShellTreeOwner::RemoveChromeListeners() {
818   if (mChromeTooltipListener) {
819     mChromeTooltipListener->RemoveChromeListeners();
820     mChromeTooltipListener = nullptr;
821   }
822 
823   nsCOMPtr<EventTarget> piTarget;
824   GetDOMEventTarget(mWebBrowser, getter_AddRefs(piTarget));
825   if (!piTarget) {
826     return NS_OK;
827   }
828 
829   EventListenerManager* elmP = piTarget->GetOrCreateListenerManager();
830   if (elmP) {
831     elmP->RemoveEventListenerByType(this, u"dragover"_ns,
832                                     TrustedEventsAtSystemGroupBubble());
833     elmP->RemoveEventListenerByType(this, u"drop"_ns,
834                                     TrustedEventsAtSystemGroupBubble());
835   }
836 
837   return NS_OK;
838 }
839 
840 NS_IMETHODIMP
HandleEvent(Event * aEvent)841 nsDocShellTreeOwner::HandleEvent(Event* aEvent) {
842   DragEvent* dragEvent = aEvent ? aEvent->AsDragEvent() : nullptr;
843   if (NS_WARN_IF(!dragEvent)) {
844     return NS_ERROR_INVALID_ARG;
845   }
846 
847   if (dragEvent->DefaultPrevented()) {
848     return NS_OK;
849   }
850 
851   nsCOMPtr<nsIDroppedLinkHandler> handler =
852       do_GetService("@mozilla.org/content/dropped-link-handler;1");
853   if (!handler) {
854     return NS_OK;
855   }
856 
857   nsAutoString eventType;
858   aEvent->GetType(eventType);
859   if (eventType.EqualsLiteral("dragover")) {
860     bool canDropLink = false;
861     handler->CanDropLink(dragEvent, false, &canDropLink);
862     if (canDropLink) {
863       aEvent->PreventDefault();
864     }
865   } else if (eventType.EqualsLiteral("drop")) {
866     nsIWebNavigation* webnav = static_cast<nsIWebNavigation*>(mWebBrowser);
867 
868     nsTArray<RefPtr<nsIDroppedLinkItem>> links;
869     if (webnav && NS_SUCCEEDED(handler->DropLinks(dragEvent, true, links))) {
870       if (links.Length() >= 1) {
871         nsCOMPtr<nsIPrincipal> triggeringPrincipal;
872         handler->GetTriggeringPrincipal(dragEvent,
873                                         getter_AddRefs(triggeringPrincipal));
874         if (triggeringPrincipal) {
875           nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome =
876               GetWebBrowserChrome();
877           if (webBrowserChrome) {
878             nsCOMPtr<nsIBrowserChild> browserChild =
879                 do_QueryInterface(webBrowserChrome);
880             if (browserChild) {
881               nsresult rv = browserChild->RemoteDropLinks(links);
882               return rv;
883             }
884           }
885           nsAutoString url;
886           if (NS_SUCCEEDED(links[0]->GetUrl(url))) {
887             if (!url.IsEmpty()) {
888 #ifndef ANDROID
889               MOZ_ASSERT(triggeringPrincipal,
890                          "nsDocShellTreeOwner::HandleEvent: Need a valid "
891                          "triggeringPrincipal");
892 #endif
893               LoadURIOptions loadURIOptions;
894               loadURIOptions.mTriggeringPrincipal = triggeringPrincipal;
895               nsCOMPtr<nsIContentSecurityPolicy> csp;
896               handler->GetCSP(dragEvent, getter_AddRefs(csp));
897               loadURIOptions.mCsp = csp;
898               webnav->LoadURI(url, loadURIOptions);
899             }
900           }
901         }
902       }
903     } else {
904       aEvent->StopPropagation();
905       aEvent->PreventDefault();
906     }
907   }
908 
909   return NS_OK;
910 }
911 
912 already_AddRefed<nsIWebBrowserChrome>
GetWebBrowserChrome()913 nsDocShellTreeOwner::GetWebBrowserChrome() {
914   nsCOMPtr<nsIWebBrowserChrome> chrome;
915   if (mWebBrowserChromeWeak) {
916     chrome = do_QueryReferent(mWebBrowserChromeWeak);
917   } else if (mWebBrowserChrome) {
918     chrome = mWebBrowserChrome;
919   }
920   return chrome.forget();
921 }
922 
GetOwnerWin()923 already_AddRefed<nsIEmbeddingSiteWindow> nsDocShellTreeOwner::GetOwnerWin() {
924   nsCOMPtr<nsIEmbeddingSiteWindow> win;
925   if (mWebBrowserChromeWeak) {
926     win = do_QueryReferent(mWebBrowserChromeWeak);
927   } else if (mOwnerWin) {
928     win = mOwnerWin;
929   }
930   return win.forget();
931 }
932 
933 already_AddRefed<nsIInterfaceRequestor>
GetOwnerRequestor()934 nsDocShellTreeOwner::GetOwnerRequestor() {
935   nsCOMPtr<nsIInterfaceRequestor> req;
936   if (mWebBrowserChromeWeak) {
937     req = do_QueryReferent(mWebBrowserChromeWeak);
938   } else if (mOwnerRequestor) {
939     req = mOwnerRequestor;
940   }
941   return req.forget();
942 }
943 
NS_IMPL_ISUPPORTS(ChromeTooltipListener,nsIDOMEventListener)944 NS_IMPL_ISUPPORTS(ChromeTooltipListener, nsIDOMEventListener)
945 
946 ChromeTooltipListener::ChromeTooltipListener(nsWebBrowser* aInBrowser,
947                                              nsIWebBrowserChrome* aInChrome)
948     : mWebBrowser(aInBrowser),
949       mWebBrowserChrome(aInChrome),
950       mTooltipListenerInstalled(false),
951       mMouseClientX(0),
952       mMouseClientY(0),
953       mMouseScreenX(0),
954       mMouseScreenY(0),
955       mShowingTooltip(false),
956       mTooltipShownOnce(false) {}
957 
~ChromeTooltipListener()958 ChromeTooltipListener::~ChromeTooltipListener() {}
959 
GetTooltipTextProvider()960 nsITooltipTextProvider* ChromeTooltipListener::GetTooltipTextProvider() {
961   if (!mTooltipTextProvider) {
962     mTooltipTextProvider = do_GetService(NS_TOOLTIPTEXTPROVIDER_CONTRACTID);
963   }
964 
965   if (!mTooltipTextProvider) {
966     mTooltipTextProvider =
967         do_GetService(NS_DEFAULTTOOLTIPTEXTPROVIDER_CONTRACTID);
968   }
969 
970   return mTooltipTextProvider;
971 }
972 
973 // Hook up things to the chrome like context menus and tooltips, if the chrome
974 // has implemented the right interfaces.
975 NS_IMETHODIMP
AddChromeListeners()976 ChromeTooltipListener::AddChromeListeners() {
977   if (!mEventTarget) {
978     GetDOMEventTarget(mWebBrowser, getter_AddRefs(mEventTarget));
979   }
980 
981   // Register the appropriate events for tooltips, but only if
982   // the embedding chrome cares.
983   nsresult rv = NS_OK;
984   nsCOMPtr<nsITooltipListener> tooltipListener(
985       do_QueryInterface(mWebBrowserChrome));
986   if (tooltipListener && !mTooltipListenerInstalled) {
987     rv = AddTooltipListener();
988     if (NS_FAILED(rv)) {
989       return rv;
990     }
991   }
992 
993   return rv;
994 }
995 
996 // Subscribe to the events that will allow us to track tooltips. We need "mouse"
997 // for mouseExit, "mouse motion" for mouseMove, and "key" for keyDown. As we
998 // add the listeners, keep track of how many succeed so we can clean up
999 // correctly in Release().
1000 NS_IMETHODIMP
AddTooltipListener()1001 ChromeTooltipListener::AddTooltipListener() {
1002   if (mEventTarget) {
1003     nsresult rv = NS_OK;
1004 #ifndef XP_WIN
1005     rv =
1006         mEventTarget->AddSystemEventListener(u"keydown"_ns, this, false, false);
1007     NS_ENSURE_SUCCESS(rv, rv);
1008 #endif
1009     rv = mEventTarget->AddSystemEventListener(u"mousedown"_ns, this, false,
1010                                               false);
1011     NS_ENSURE_SUCCESS(rv, rv);
1012     rv = mEventTarget->AddSystemEventListener(u"mouseout"_ns, this, false,
1013                                               false);
1014     NS_ENSURE_SUCCESS(rv, rv);
1015     rv = mEventTarget->AddSystemEventListener(u"mousemove"_ns, this, false,
1016                                               false);
1017     NS_ENSURE_SUCCESS(rv, rv);
1018 
1019     mTooltipListenerInstalled = true;
1020   }
1021 
1022   return NS_OK;
1023 }
1024 
1025 // Unsubscribe from the various things we've hooked up to the window root.
1026 NS_IMETHODIMP
RemoveChromeListeners()1027 ChromeTooltipListener::RemoveChromeListeners() {
1028   HideTooltip();
1029 
1030   if (mTooltipListenerInstalled) {
1031     RemoveTooltipListener();
1032   }
1033 
1034   mEventTarget = nullptr;
1035 
1036   // it really doesn't matter if these fail...
1037   return NS_OK;
1038 }
1039 
1040 // Unsubscribe from all the various tooltip events that we were listening to.
1041 NS_IMETHODIMP
RemoveTooltipListener()1042 ChromeTooltipListener::RemoveTooltipListener() {
1043   if (mEventTarget) {
1044 #ifndef XP_WIN
1045     mEventTarget->RemoveSystemEventListener(u"keydown"_ns, this, false);
1046 #endif
1047     mEventTarget->RemoveSystemEventListener(u"mousedown"_ns, this, false);
1048     mEventTarget->RemoveSystemEventListener(u"mouseout"_ns, this, false);
1049     mEventTarget->RemoveSystemEventListener(u"mousemove"_ns, this, false);
1050     mTooltipListenerInstalled = false;
1051   }
1052 
1053   return NS_OK;
1054 }
1055 
1056 NS_IMETHODIMP
HandleEvent(Event * aEvent)1057 ChromeTooltipListener::HandleEvent(Event* aEvent) {
1058   nsAutoString eventType;
1059   aEvent->GetType(eventType);
1060 
1061   if (eventType.EqualsLiteral("mousedown")) {
1062     return HideTooltip();
1063   } else if (eventType.EqualsLiteral("keydown")) {
1064     WidgetKeyboardEvent* keyEvent = aEvent->WidgetEventPtr()->AsKeyboardEvent();
1065     if (!keyEvent->IsModifierKeyEvent()) {
1066       return HideTooltip();
1067     }
1068 
1069     return NS_OK;
1070   } else if (eventType.EqualsLiteral("mouseout")) {
1071     // Reset flag so that tooltip will display on the next MouseMove
1072     mTooltipShownOnce = false;
1073     return HideTooltip();
1074   } else if (eventType.EqualsLiteral("mousemove")) {
1075     return MouseMove(aEvent);
1076   }
1077 
1078   NS_ERROR("Unexpected event type");
1079   return NS_OK;
1080 }
1081 
1082 // If we're a tooltip, fire off a timer to see if a tooltip should be shown. If
1083 // the timer fires, we cache the node in |mPossibleTooltipNode|.
MouseMove(Event * aMouseEvent)1084 nsresult ChromeTooltipListener::MouseMove(Event* aMouseEvent) {
1085   MouseEvent* mouseEvent = aMouseEvent->AsMouseEvent();
1086   if (!mouseEvent) {
1087     return NS_OK;
1088   }
1089 
1090   // stash the coordinates of the event so that we can still get back to it from
1091   // within the timer callback. On win32, we'll get a MouseMove event even when
1092   // a popup goes away -- even when the mouse doesn't change position! To get
1093   // around this, we make sure the mouse has really moved before proceeding.
1094   int32_t newMouseX = mouseEvent->ClientX();
1095   int32_t newMouseY = mouseEvent->ClientY();
1096   if (mMouseClientX == newMouseX && mMouseClientY == newMouseY) {
1097     return NS_OK;
1098   }
1099 
1100   // Filter out minor mouse movements.
1101   if (mShowingTooltip &&
1102       (abs(mMouseClientX - newMouseX) <= kTooltipMouseMoveTolerance) &&
1103       (abs(mMouseClientY - newMouseY) <= kTooltipMouseMoveTolerance)) {
1104     return NS_OK;
1105   }
1106 
1107   mMouseClientX = newMouseX;
1108   mMouseClientY = newMouseY;
1109   mMouseScreenX = mouseEvent->ScreenX(CallerType::System);
1110   mMouseScreenY = mouseEvent->ScreenY(CallerType::System);
1111 
1112   if (mTooltipTimer) {
1113     mTooltipTimer->Cancel();
1114     mTooltipTimer = nullptr;
1115   }
1116 
1117   if (!mShowingTooltip) {
1118     nsIEventTarget* target = nullptr;
1119     if (nsCOMPtr<EventTarget> eventTarget = aMouseEvent->GetComposedTarget()) {
1120       mPossibleTooltipNode = do_QueryInterface(eventTarget);
1121       nsCOMPtr<nsIGlobalObject> global(eventTarget->GetOwnerGlobal());
1122       if (global) {
1123         target = global->EventTargetFor(TaskCategory::UI);
1124       }
1125     }
1126 
1127     if (mPossibleTooltipNode) {
1128       nsresult rv = NS_NewTimerWithFuncCallback(
1129           getter_AddRefs(mTooltipTimer), sTooltipCallback, this,
1130           LookAndFeel::GetInt(LookAndFeel::IntID::TooltipDelay, 500),
1131           nsITimer::TYPE_ONE_SHOT, "ChromeTooltipListener::MouseMove", target);
1132       if (NS_FAILED(rv)) {
1133         mPossibleTooltipNode = nullptr;
1134         NS_WARNING("Could not create a timer for tooltip tracking");
1135       }
1136     }
1137   } else {
1138     mTooltipShownOnce = true;
1139     return HideTooltip();
1140   }
1141 
1142   return NS_OK;
1143 }
1144 
1145 // Tell the registered chrome that they should show the tooltip.
1146 NS_IMETHODIMP
ShowTooltip(int32_t aInXCoords,int32_t aInYCoords,const nsAString & aInTipText,const nsAString & aTipDir)1147 ChromeTooltipListener::ShowTooltip(int32_t aInXCoords, int32_t aInYCoords,
1148                                    const nsAString& aInTipText,
1149                                    const nsAString& aTipDir) {
1150   nsresult rv = NS_OK;
1151 
1152   // do the work to call the client
1153   nsCOMPtr<nsITooltipListener> tooltipListener(
1154       do_QueryInterface(mWebBrowserChrome));
1155   if (tooltipListener) {
1156     rv = tooltipListener->OnShowTooltip(aInXCoords, aInYCoords, aInTipText,
1157                                         aTipDir);
1158     if (NS_SUCCEEDED(rv)) {
1159       mShowingTooltip = true;
1160     }
1161   }
1162 
1163   return rv;
1164 }
1165 
1166 // Tell the registered chrome that they should rollup the tooltip
1167 // NOTE: This routine is safe to call even if the popup is already closed.
1168 NS_IMETHODIMP
HideTooltip()1169 ChromeTooltipListener::HideTooltip() {
1170   nsresult rv = NS_OK;
1171 
1172   // shut down the relevant timers
1173   if (mTooltipTimer) {
1174     mTooltipTimer->Cancel();
1175     mTooltipTimer = nullptr;
1176     // release tooltip target
1177     mPossibleTooltipNode = nullptr;
1178     mLastDocshell = nullptr;
1179   }
1180 
1181   // if we're showing the tip, tell the chrome to hide it
1182   if (mShowingTooltip) {
1183     nsCOMPtr<nsITooltipListener> tooltipListener(
1184         do_QueryInterface(mWebBrowserChrome));
1185     if (tooltipListener) {
1186       rv = tooltipListener->OnHideTooltip();
1187       if (NS_SUCCEEDED(rv)) {
1188         mShowingTooltip = false;
1189       }
1190     }
1191   }
1192 
1193   return rv;
1194 }
1195 
WebProgressShowedTooltip(nsIWebProgress * aWebProgress)1196 bool ChromeTooltipListener::WebProgressShowedTooltip(
1197     nsIWebProgress* aWebProgress) {
1198   nsCOMPtr<nsIDocShell> docshell = do_QueryInterface(aWebProgress);
1199   nsCOMPtr<nsIDocShell> lastUsed = do_QueryReferent(mLastDocshell);
1200   while (lastUsed) {
1201     if (lastUsed == docshell) {
1202       return true;
1203     }
1204     // We can't use the docshell hierarchy here, because when the parent
1205     // docshell is navigated, the child docshell is disconnected (ie its
1206     // references to the parent are nulled out) despite it still being
1207     // alive here. So we use the document hierarchy instead:
1208     Document* document = lastUsed->GetDocument();
1209     if (document) {
1210       document = document->GetInProcessParentDocument();
1211     }
1212     if (!document) {
1213       break;
1214     }
1215     lastUsed = document->GetDocShell();
1216   }
1217   return false;
1218 }
1219 
1220 // A timer callback, fired when the mouse has hovered inside of a frame for the
1221 // appropriate amount of time. Getting to this point means that we should show
1222 // the tooltip, but only after we determine there is an appropriate TITLE
1223 // element.
1224 //
1225 // This relies on certain things being cached into the |aChromeTooltipListener|
1226 // object passed to us by the timer:
1227 //   -- the x/y coordinates of the mouse      (mMouseClientY, mMouseClientX)
1228 //   -- the dom node the user hovered over    (mPossibleTooltipNode)
sTooltipCallback(nsITimer * aTimer,void * aChromeTooltipListener)1229 void ChromeTooltipListener::sTooltipCallback(nsITimer* aTimer,
1230                                              void* aChromeTooltipListener) {
1231   auto self = static_cast<ChromeTooltipListener*>(aChromeTooltipListener);
1232   if (self && self->mPossibleTooltipNode) {
1233     // release tooltip target once done, no matter what we do here.
1234     auto cleanup = MakeScopeExit([&] { self->mPossibleTooltipNode = nullptr; });
1235     if (!self->mPossibleTooltipNode->IsInComposedDoc()) {
1236       return;
1237     }
1238     // Check that the document or its ancestors haven't been replaced.
1239     Document* doc = self->mPossibleTooltipNode->OwnerDoc();
1240     while (doc) {
1241       if (!doc->IsCurrentActiveDocument()) {
1242         return;
1243       }
1244       doc = doc->GetInProcessParentDocument();
1245     }
1246 
1247     // The actual coordinates we want to put the tooltip at are relative to the
1248     // toplevel docshell of our mWebBrowser.  We know what the screen
1249     // coordinates of the mouse event were, which means we just need the screen
1250     // coordinates of the docshell.  Unfortunately, there is no good way to
1251     // find those short of groveling for the presentation in that docshell and
1252     // finding the screen coords of its toplevel widget...
1253     nsCOMPtr<nsIDocShell> docShell =
1254         do_GetInterface(static_cast<nsIWebBrowser*>(self->mWebBrowser));
1255     RefPtr<PresShell> presShell = docShell ? docShell->GetPresShell() : nullptr;
1256 
1257     nsIWidget* widget = nullptr;
1258     if (presShell) {
1259       nsViewManager* vm = presShell->GetViewManager();
1260       if (vm) {
1261         nsView* view = vm->GetRootView();
1262         if (view) {
1263           nsPoint offset;
1264           widget = view->GetNearestWidget(&offset);
1265         }
1266       }
1267     }
1268 
1269     if (!widget || !docShell || !docShell->GetBrowsingContext()->IsActive()) {
1270       return;
1271     }
1272 
1273     // if there is text associated with the node, show the tip and fire
1274     // off a timer to auto-hide it.
1275     nsITooltipTextProvider* tooltipProvider = self->GetTooltipTextProvider();
1276     if (tooltipProvider) {
1277       nsString tooltipText;
1278       nsString directionText;
1279       bool textFound = false;
1280       tooltipProvider->GetNodeText(self->mPossibleTooltipNode,
1281                                    getter_Copies(tooltipText),
1282                                    getter_Copies(directionText), &textFound);
1283 
1284       if (textFound && (!self->mTooltipShownOnce ||
1285                         tooltipText != self->mLastShownTooltipText)) {
1286         // ShowTooltip expects screen-relative position.
1287         self->ShowTooltip(self->mMouseScreenX, self->mMouseScreenY, tooltipText,
1288                           directionText);
1289         self->mLastShownTooltipText = std::move(tooltipText);
1290         self->mLastDocshell = do_GetWeakReference(
1291             self->mPossibleTooltipNode->OwnerDoc()->GetDocShell());
1292       }
1293     }
1294   }
1295 }
1296