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