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