Bug 1839316: part 5) Guard the "fetchpriority" attribute behind a pref. r=kershaw...
[gecko.git] / docshell / base / nsDocShellTreeOwner.cpp
blobfd0ef1828fad93e6fe70026fd76c20e2ea067e10
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/. */
7 // Local Includes
8 #include "nsDocShellTreeOwner.h"
9 #include "nsWebBrowser.h"
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"
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 "nsXULTooltipListener.h"
48 #include "nsIConstraintValidation.h"
49 #include "mozilla/Attributes.h"
50 #include "mozilla/EventListenerManager.h"
51 #include "mozilla/dom/DragEvent.h"
52 #include "mozilla/dom/Event.h" // for Event
53 #include "mozilla/dom/File.h" // for input type=file
54 #include "mozilla/dom/FileList.h" // for input type=file
55 #include "mozilla/dom/LoadURIOptionsBinding.h"
56 #include "mozilla/PresShell.h"
57 #include "mozilla/TextEvents.h"
59 using namespace mozilla;
60 using namespace mozilla::dom;
62 // A helper routine that navigates the tricky path from a |nsWebBrowser| to
63 // a |EventTarget| via the window root and chrome event handler.
64 static nsresult GetDOMEventTarget(nsWebBrowser* aInBrowser,
65 EventTarget** aTarget) {
66 if (!aInBrowser) {
67 return NS_ERROR_INVALID_POINTER;
70 nsCOMPtr<mozIDOMWindowProxy> domWindow;
71 aInBrowser->GetContentDOMWindow(getter_AddRefs(domWindow));
72 if (!domWindow) {
73 return NS_ERROR_FAILURE;
76 auto* outerWindow = nsPIDOMWindowOuter::From(domWindow);
77 nsPIDOMWindowOuter* rootWindow = outerWindow->GetPrivateRoot();
78 NS_ENSURE_TRUE(rootWindow, NS_ERROR_FAILURE);
79 nsCOMPtr<EventTarget> target = rootWindow->GetChromeEventHandler();
80 NS_ENSURE_TRUE(target, NS_ERROR_FAILURE);
81 target.forget(aTarget);
83 return NS_OK;
86 nsDocShellTreeOwner::nsDocShellTreeOwner()
87 : mWebBrowser(nullptr),
88 mTreeOwner(nullptr),
89 mPrimaryContentShell(nullptr),
90 mWebBrowserChrome(nullptr),
91 mOwnerWin(nullptr),
92 mOwnerRequestor(nullptr) {}
94 nsDocShellTreeOwner::~nsDocShellTreeOwner() { RemoveChromeListeners(); }
96 NS_IMPL_ADDREF(nsDocShellTreeOwner)
97 NS_IMPL_RELEASE(nsDocShellTreeOwner)
99 NS_INTERFACE_MAP_BEGIN(nsDocShellTreeOwner)
100 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDocShellTreeOwner)
101 NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeOwner)
102 NS_INTERFACE_MAP_ENTRY(nsIBaseWindow)
103 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
104 NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
105 NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
106 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
107 NS_INTERFACE_MAP_END
109 // The class that listens to the chrome events and tells the embedding chrome to
110 // show tooltips, as appropriate. Handles registering itself with the DOM with
111 // AddChromeListeners() and removing itself with RemoveChromeListeners().
112 class ChromeTooltipListener final : public nsIDOMEventListener {
113 protected:
114 virtual ~ChromeTooltipListener();
116 public:
117 NS_DECL_ISUPPORTS
119 ChromeTooltipListener(nsWebBrowser* aInBrowser,
120 nsIWebBrowserChrome* aInChrome);
122 NS_DECL_NSIDOMEVENTLISTENER
123 NS_IMETHOD MouseMove(mozilla::dom::Event* aMouseEvent);
125 // Add/remove the relevant listeners, based on what interfaces the embedding
126 // chrome implements.
127 NS_IMETHOD AddChromeListeners();
128 NS_IMETHOD RemoveChromeListeners();
130 NS_IMETHOD HideTooltip();
132 bool WebProgressShowedTooltip(nsIWebProgress* aWebProgress);
134 private:
135 // pixel tolerance for mousemove event
136 static constexpr CSSIntCoord kTooltipMouseMoveTolerance = 7;
138 NS_IMETHOD AddTooltipListener();
139 NS_IMETHOD RemoveTooltipListener();
141 NS_IMETHOD ShowTooltip(int32_t aInXCoords, int32_t aInYCoords,
142 const nsAString& aInTipText,
143 const nsAString& aDirText);
144 nsITooltipTextProvider* GetTooltipTextProvider();
146 nsWebBrowser* mWebBrowser;
147 nsCOMPtr<mozilla::dom::EventTarget> mEventTarget;
148 nsCOMPtr<nsITooltipTextProvider> mTooltipTextProvider;
150 // This must be a strong ref in order to make sure we can hide the tooltip if
151 // the window goes away while we're displaying one. If we don't hold a strong
152 // ref, the chrome might have been disposed of before we get a chance to tell
153 // it, and no one would ever tell us of that fact.
154 nsCOMPtr<nsIWebBrowserChrome> mWebBrowserChrome;
156 bool mTooltipListenerInstalled;
158 nsCOMPtr<nsITimer> mTooltipTimer;
159 static void sTooltipCallback(nsITimer* aTimer, void* aListener);
161 // Mouse coordinates for last mousemove event we saw
162 CSSIntPoint mMouseClientPoint;
164 // Mouse coordinates for tooltip event
165 LayoutDeviceIntPoint mMouseScreenPoint;
167 bool mShowingTooltip;
169 bool mTooltipShownOnce;
171 // The string of text that we last displayed.
172 nsString mLastShownTooltipText;
174 nsWeakPtr mLastDocshell;
176 // The node hovered over that fired the timer. This may turn into the node
177 // that triggered the tooltip, but only if the timer ever gets around to
178 // firing. This is a strong reference, because the tooltip content can be
179 // destroyed while we're waiting for the tooltip to pop up, and we need to
180 // detect that. It's set only when the tooltip timer is created and launched.
181 // The timer must either fire or be cancelled (or possibly released?), and we
182 // release this reference in each of those cases. So we don't leak.
183 nsCOMPtr<nsINode> mPossibleTooltipNode;
186 //*****************************************************************************
187 // nsDocShellTreeOwner::nsIInterfaceRequestor
188 //*****************************************************************************
190 NS_IMETHODIMP
191 nsDocShellTreeOwner::GetInterface(const nsIID& aIID, void** aSink) {
192 NS_ENSURE_ARG_POINTER(aSink);
194 if (NS_SUCCEEDED(QueryInterface(aIID, aSink))) {
195 return NS_OK;
198 if (aIID.Equals(NS_GET_IID(nsIWebBrowserChromeFocus))) {
199 if (mWebBrowserChromeWeak != nullptr) {
200 return mWebBrowserChromeWeak->QueryReferent(aIID, aSink);
202 return mOwnerWin->QueryInterface(aIID, aSink);
205 if (aIID.Equals(NS_GET_IID(nsIPrompt))) {
206 nsCOMPtr<nsIPrompt> prompt;
207 EnsurePrompter();
208 prompt = mPrompter;
209 if (prompt) {
210 prompt.forget(aSink);
211 return NS_OK;
213 return NS_NOINTERFACE;
216 if (aIID.Equals(NS_GET_IID(nsIAuthPrompt))) {
217 nsCOMPtr<nsIAuthPrompt> prompt;
218 EnsureAuthPrompter();
219 prompt = mAuthPrompter;
220 if (prompt) {
221 prompt.forget(aSink);
222 return NS_OK;
224 return NS_NOINTERFACE;
227 nsCOMPtr<nsIInterfaceRequestor> req = GetOwnerRequestor();
228 if (req) {
229 return req->GetInterface(aIID, aSink);
232 return NS_NOINTERFACE;
235 //*****************************************************************************
236 // nsDocShellTreeOwner::nsIDocShellTreeOwner
237 //*****************************************************************************
239 void nsDocShellTreeOwner::EnsurePrompter() {
240 if (mPrompter) {
241 return;
244 nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
245 if (wwatch && mWebBrowser) {
246 nsCOMPtr<mozIDOMWindowProxy> domWindow;
247 mWebBrowser->GetContentDOMWindow(getter_AddRefs(domWindow));
248 if (domWindow) {
249 wwatch->GetNewPrompter(domWindow, getter_AddRefs(mPrompter));
254 void nsDocShellTreeOwner::EnsureAuthPrompter() {
255 if (mAuthPrompter) {
256 return;
259 nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
260 if (wwatch && mWebBrowser) {
261 nsCOMPtr<mozIDOMWindowProxy> domWindow;
262 mWebBrowser->GetContentDOMWindow(getter_AddRefs(domWindow));
263 if (domWindow) {
264 wwatch->GetNewAuthPrompter(domWindow, getter_AddRefs(mAuthPrompter));
269 void nsDocShellTreeOwner::AddToWatcher() {
270 if (mWebBrowser) {
271 nsCOMPtr<mozIDOMWindowProxy> domWindow;
272 mWebBrowser->GetContentDOMWindow(getter_AddRefs(domWindow));
273 if (domWindow) {
274 nsCOMPtr<nsPIWindowWatcher> wwatch(
275 do_GetService(NS_WINDOWWATCHER_CONTRACTID));
276 if (wwatch) {
277 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome();
278 if (webBrowserChrome) {
279 wwatch->AddWindow(domWindow, webBrowserChrome);
286 void nsDocShellTreeOwner::RemoveFromWatcher() {
287 if (mWebBrowser) {
288 nsCOMPtr<mozIDOMWindowProxy> domWindow;
289 mWebBrowser->GetContentDOMWindow(getter_AddRefs(domWindow));
290 if (domWindow) {
291 nsCOMPtr<nsPIWindowWatcher> wwatch(
292 do_GetService(NS_WINDOWWATCHER_CONTRACTID));
293 if (wwatch) {
294 wwatch->RemoveWindow(domWindow);
300 void nsDocShellTreeOwner::EnsureContentTreeOwner() {
301 if (mContentTreeOwner) {
302 return;
305 mContentTreeOwner = new nsDocShellTreeOwner();
306 nsCOMPtr<nsIWebBrowserChrome> browserChrome = GetWebBrowserChrome();
307 if (browserChrome) {
308 mContentTreeOwner->SetWebBrowserChrome(browserChrome);
311 if (mWebBrowser) {
312 mContentTreeOwner->WebBrowser(mWebBrowser);
316 NS_IMETHODIMP
317 nsDocShellTreeOwner::ContentShellAdded(nsIDocShellTreeItem* aContentShell,
318 bool aPrimary) {
319 if (mTreeOwner) return mTreeOwner->ContentShellAdded(aContentShell, aPrimary);
321 EnsureContentTreeOwner();
322 aContentShell->SetTreeOwner(mContentTreeOwner);
324 if (aPrimary) {
325 mPrimaryContentShell = aContentShell;
326 mPrimaryRemoteTab = nullptr;
328 return NS_OK;
331 NS_IMETHODIMP
332 nsDocShellTreeOwner::ContentShellRemoved(nsIDocShellTreeItem* aContentShell) {
333 if (mTreeOwner) {
334 return mTreeOwner->ContentShellRemoved(aContentShell);
337 if (mPrimaryContentShell == aContentShell) {
338 mPrimaryContentShell = nullptr;
341 return NS_OK;
344 NS_IMETHODIMP
345 nsDocShellTreeOwner::GetPrimaryContentShell(nsIDocShellTreeItem** aShell) {
346 NS_ENSURE_ARG_POINTER(aShell);
348 if (mTreeOwner) {
349 return mTreeOwner->GetPrimaryContentShell(aShell);
352 nsCOMPtr<nsIDocShellTreeItem> shell;
353 if (!mPrimaryRemoteTab) {
354 shell =
355 mPrimaryContentShell ? mPrimaryContentShell : mWebBrowser->mDocShell;
357 shell.forget(aShell);
359 return NS_OK;
362 NS_IMETHODIMP
363 nsDocShellTreeOwner::RemoteTabAdded(nsIRemoteTab* aTab, bool aPrimary) {
364 if (mTreeOwner) {
365 return mTreeOwner->RemoteTabAdded(aTab, aPrimary);
368 if (aPrimary) {
369 mPrimaryRemoteTab = aTab;
370 mPrimaryContentShell = nullptr;
371 } else if (mPrimaryRemoteTab == aTab) {
372 mPrimaryRemoteTab = nullptr;
375 return NS_OK;
378 NS_IMETHODIMP
379 nsDocShellTreeOwner::RemoteTabRemoved(nsIRemoteTab* aTab) {
380 if (mTreeOwner) {
381 return mTreeOwner->RemoteTabRemoved(aTab);
384 if (aTab == mPrimaryRemoteTab) {
385 mPrimaryRemoteTab = nullptr;
388 return NS_OK;
391 NS_IMETHODIMP
392 nsDocShellTreeOwner::GetPrimaryRemoteTab(nsIRemoteTab** aTab) {
393 if (mTreeOwner) {
394 return mTreeOwner->GetPrimaryRemoteTab(aTab);
397 nsCOMPtr<nsIRemoteTab> tab = mPrimaryRemoteTab;
398 tab.forget(aTab);
399 return NS_OK;
402 NS_IMETHODIMP
403 nsDocShellTreeOwner::GetPrimaryContentBrowsingContext(
404 mozilla::dom::BrowsingContext** aBc) {
405 if (mTreeOwner) {
406 return mTreeOwner->GetPrimaryContentBrowsingContext(aBc);
408 if (mPrimaryRemoteTab) {
409 return mPrimaryRemoteTab->GetBrowsingContext(aBc);
411 if (mPrimaryContentShell) {
412 return mPrimaryContentShell->GetBrowsingContextXPCOM(aBc);
414 if (mWebBrowser->mDocShell) {
415 return mWebBrowser->mDocShell->GetBrowsingContextXPCOM(aBc);
417 *aBc = nullptr;
418 return NS_OK;
421 NS_IMETHODIMP
422 nsDocShellTreeOwner::GetPrimaryContentSize(int32_t* aWidth, int32_t* aHeight) {
423 return NS_ERROR_NOT_IMPLEMENTED;
426 NS_IMETHODIMP
427 nsDocShellTreeOwner::SetPrimaryContentSize(int32_t aWidth, int32_t aHeight) {
428 return NS_ERROR_NOT_IMPLEMENTED;
431 NS_IMETHODIMP
432 nsDocShellTreeOwner::GetRootShellSize(int32_t* aWidth, int32_t* aHeight) {
433 return NS_ERROR_NOT_IMPLEMENTED;
436 NS_IMETHODIMP
437 nsDocShellTreeOwner::SetRootShellSize(int32_t aWidth, int32_t aHeight) {
438 return NS_ERROR_NOT_IMPLEMENTED;
441 NS_IMETHODIMP
442 nsDocShellTreeOwner::SizeShellTo(nsIDocShellTreeItem* aShellItem, int32_t aCX,
443 int32_t aCY) {
444 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome();
446 NS_ENSURE_STATE(mTreeOwner || webBrowserChrome);
448 if (nsCOMPtr<nsIDocShellTreeOwner> treeOwner = mTreeOwner) {
449 return treeOwner->SizeShellTo(aShellItem, aCX, aCY);
452 if (aShellItem == mWebBrowser->mDocShell) {
453 nsCOMPtr<nsIBrowserChild> browserChild =
454 do_QueryInterface(webBrowserChrome);
455 if (browserChild) {
456 // The XUL window to resize is in the parent process, but there we
457 // won't be able to get the size of aShellItem. We can ask the parent
458 // process to change our size instead.
459 nsCOMPtr<nsIBaseWindow> shellAsWin(do_QueryInterface(aShellItem));
460 NS_ENSURE_TRUE(shellAsWin, NS_ERROR_FAILURE);
462 LayoutDeviceIntSize shellSize;
463 shellAsWin->GetSize(&shellSize.width, &shellSize.height);
464 LayoutDeviceIntSize deltaSize = LayoutDeviceIntSize(aCX, aCY) - shellSize;
466 LayoutDeviceIntSize currentSize;
467 GetSize(&currentSize.width, &currentSize.height);
469 LayoutDeviceIntSize newSize = currentSize + deltaSize;
470 return SetSize(newSize.width, newSize.height, true);
472 // XXX: this is weird, but we used to call a method here
473 // (webBrowserChrome->SizeBrowserTo()) whose implementations all failed
474 // like this, so...
475 return NS_ERROR_NOT_IMPLEMENTED;
478 MOZ_ASSERT_UNREACHABLE("This is unimplemented, API should be cleaned up");
479 return NS_ERROR_NOT_IMPLEMENTED;
482 NS_IMETHODIMP
483 nsDocShellTreeOwner::SetPersistence(bool aPersistPosition, bool aPersistSize,
484 bool aPersistSizeMode) {
485 return NS_ERROR_NOT_IMPLEMENTED;
488 NS_IMETHODIMP
489 nsDocShellTreeOwner::GetPersistence(bool* aPersistPosition, bool* aPersistSize,
490 bool* aPersistSizeMode) {
491 return NS_ERROR_NOT_IMPLEMENTED;
494 NS_IMETHODIMP
495 nsDocShellTreeOwner::GetTabCount(uint32_t* aResult) {
496 if (mTreeOwner) {
497 return mTreeOwner->GetTabCount(aResult);
500 *aResult = 0;
501 return NS_OK;
504 NS_IMETHODIMP
505 nsDocShellTreeOwner::GetHasPrimaryContent(bool* aResult) {
506 *aResult = mPrimaryRemoteTab || mPrimaryContentShell;
507 return NS_OK;
510 //*****************************************************************************
511 // nsDocShellTreeOwner::nsIBaseWindow
512 //*****************************************************************************
514 NS_IMETHODIMP
515 nsDocShellTreeOwner::InitWindow(nativeWindow aParentNativeWindow,
516 nsIWidget* aParentWidget, int32_t aX,
517 int32_t aY, int32_t aCX, int32_t aCY) {
518 return NS_ERROR_NULL_POINTER;
521 NS_IMETHODIMP
522 nsDocShellTreeOwner::Destroy() {
523 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome();
524 if (webBrowserChrome) {
525 // XXX: this is weird, but we used to call a method here
526 // (webBrowserChrome->DestroyBrowserWindow()) whose implementations all
527 // failed like this, so...
528 return NS_ERROR_NOT_IMPLEMENTED;
531 return NS_ERROR_NULL_POINTER;
534 double nsDocShellTreeOwner::GetWidgetCSSToDeviceScale() {
535 return mWebBrowser ? mWebBrowser->GetWidgetCSSToDeviceScale() : 1.0;
538 NS_IMETHODIMP
539 nsDocShellTreeOwner::GetDevicePixelsPerDesktopPixel(double* aScale) {
540 if (mWebBrowser) {
541 return mWebBrowser->GetDevicePixelsPerDesktopPixel(aScale);
544 *aScale = 1.0;
545 return NS_OK;
548 NS_IMETHODIMP
549 nsDocShellTreeOwner::SetPositionDesktopPix(int32_t aX, int32_t aY) {
550 if (mWebBrowser) {
551 nsresult rv = mWebBrowser->SetPositionDesktopPix(aX, aY);
552 NS_ENSURE_SUCCESS(rv, rv);
555 double scale = 1.0;
556 GetDevicePixelsPerDesktopPixel(&scale);
557 return SetPosition(NSToIntRound(aX * scale), NSToIntRound(aY * scale));
560 NS_IMETHODIMP
561 nsDocShellTreeOwner::SetPosition(int32_t aX, int32_t aY) {
562 return SetDimensions(
563 {DimensionKind::Outer, Some(aX), Some(aY), Nothing(), Nothing()});
566 NS_IMETHODIMP
567 nsDocShellTreeOwner::GetPosition(int32_t* aX, int32_t* aY) {
568 return GetDimensions(DimensionKind::Outer, aX, aY, nullptr, nullptr);
571 NS_IMETHODIMP
572 nsDocShellTreeOwner::SetSize(int32_t aCX, int32_t aCY, bool aRepaint) {
573 return SetDimensions(
574 {DimensionKind::Outer, Nothing(), Nothing(), Some(aCX), Some(aCY)});
577 NS_IMETHODIMP
578 nsDocShellTreeOwner::GetSize(int32_t* aCX, int32_t* aCY) {
579 return GetDimensions(DimensionKind::Outer, nullptr, nullptr, aCX, aCY);
582 NS_IMETHODIMP
583 nsDocShellTreeOwner::SetPositionAndSize(int32_t aX, int32_t aY, int32_t aCX,
584 int32_t aCY, uint32_t aFlags) {
585 return SetDimensions(
586 {DimensionKind::Outer, Some(aX), Some(aY), Some(aCX), Some(aCY)});
589 NS_IMETHODIMP
590 nsDocShellTreeOwner::GetPositionAndSize(int32_t* aX, int32_t* aY, int32_t* aCX,
591 int32_t* aCY) {
592 return GetDimensions(DimensionKind::Outer, aX, aY, aCX, aCY);
595 NS_IMETHODIMP
596 nsDocShellTreeOwner::SetDimensions(DimensionRequest&& aRequest) {
597 nsCOMPtr<nsIBaseWindow> ownerWin = GetOwnerWin();
598 if (ownerWin) {
599 return ownerWin->SetDimensions(std::move(aRequest));
602 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome();
603 NS_ENSURE_STATE(webBrowserChrome);
604 return webBrowserChrome->SetDimensions(std::move(aRequest));
607 NS_IMETHODIMP
608 nsDocShellTreeOwner::GetDimensions(DimensionKind aDimensionKind, int32_t* aX,
609 int32_t* aY, int32_t* aCX, int32_t* aCY) {
610 nsCOMPtr<nsIBaseWindow> ownerWin = GetOwnerWin();
611 if (ownerWin) {
612 return ownerWin->GetDimensions(aDimensionKind, aX, aY, aCX, aCY);
615 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome();
616 NS_ENSURE_STATE(webBrowserChrome);
617 return webBrowserChrome->GetDimensions(aDimensionKind, aX, aY, aCX, aCY);
620 NS_IMETHODIMP
621 nsDocShellTreeOwner::Repaint(bool aForce) { return NS_ERROR_NULL_POINTER; }
623 NS_IMETHODIMP
624 nsDocShellTreeOwner::GetParentWidget(nsIWidget** aParentWidget) {
625 return NS_ERROR_NULL_POINTER;
628 NS_IMETHODIMP
629 nsDocShellTreeOwner::SetParentWidget(nsIWidget* aParentWidget) {
630 return NS_ERROR_NULL_POINTER;
633 NS_IMETHODIMP
634 nsDocShellTreeOwner::GetParentNativeWindow(nativeWindow* aParentNativeWindow) {
635 nsCOMPtr<nsIBaseWindow> ownerWin = GetOwnerWin();
636 if (ownerWin) {
637 return ownerWin->GetParentNativeWindow(aParentNativeWindow);
639 return NS_ERROR_NULL_POINTER;
642 NS_IMETHODIMP
643 nsDocShellTreeOwner::SetParentNativeWindow(nativeWindow aParentNativeWindow) {
644 return NS_ERROR_NULL_POINTER;
647 NS_IMETHODIMP
648 nsDocShellTreeOwner::GetNativeHandle(nsAString& aNativeHandle) {
649 // the nativeHandle should be accessed from nsIAppWindow
650 return NS_ERROR_NOT_IMPLEMENTED;
653 NS_IMETHODIMP
654 nsDocShellTreeOwner::GetVisibility(bool* aVisibility) {
655 nsCOMPtr<nsIBaseWindow> ownerWin = GetOwnerWin();
656 if (ownerWin) {
657 return ownerWin->GetVisibility(aVisibility);
660 return NS_ERROR_NOT_IMPLEMENTED;
663 NS_IMETHODIMP
664 nsDocShellTreeOwner::SetVisibility(bool aVisibility) {
665 nsCOMPtr<nsIBaseWindow> ownerWin = GetOwnerWin();
666 if (ownerWin) {
667 return ownerWin->SetVisibility(aVisibility);
669 return NS_ERROR_NULL_POINTER;
672 NS_IMETHODIMP
673 nsDocShellTreeOwner::GetEnabled(bool* aEnabled) {
674 NS_ENSURE_ARG_POINTER(aEnabled);
675 *aEnabled = true;
676 return NS_ERROR_NOT_IMPLEMENTED;
679 NS_IMETHODIMP
680 nsDocShellTreeOwner::SetEnabled(bool aEnabled) {
681 return NS_ERROR_NOT_IMPLEMENTED;
684 NS_IMETHODIMP
685 nsDocShellTreeOwner::GetMainWidget(nsIWidget** aMainWidget) {
686 return NS_ERROR_NULL_POINTER;
689 NS_IMETHODIMP
690 nsDocShellTreeOwner::GetTitle(nsAString& aTitle) {
691 nsCOMPtr<nsIBaseWindow> ownerWin = GetOwnerWin();
692 if (ownerWin) {
693 return ownerWin->GetTitle(aTitle);
695 return NS_ERROR_NULL_POINTER;
698 NS_IMETHODIMP
699 nsDocShellTreeOwner::SetTitle(const nsAString& aTitle) {
700 nsCOMPtr<nsIBaseWindow> ownerWin = GetOwnerWin();
701 if (ownerWin) {
702 return ownerWin->SetTitle(aTitle);
704 return NS_ERROR_NULL_POINTER;
707 //*****************************************************************************
708 // nsDocShellTreeOwner::nsIWebProgressListener
709 //*****************************************************************************
711 NS_IMETHODIMP
712 nsDocShellTreeOwner::OnProgressChange(nsIWebProgress* aProgress,
713 nsIRequest* aRequest,
714 int32_t aCurSelfProgress,
715 int32_t aMaxSelfProgress,
716 int32_t aCurTotalProgress,
717 int32_t aMaxTotalProgress) {
718 // In the absence of DOM document creation event, this method is the
719 // most convenient place to install the mouse listener on the
720 // DOM document.
721 return AddChromeListeners();
724 NS_IMETHODIMP
725 nsDocShellTreeOwner::OnStateChange(nsIWebProgress* aProgress,
726 nsIRequest* aRequest,
727 uint32_t aProgressStateFlags,
728 nsresult aStatus) {
729 return NS_OK;
732 NS_IMETHODIMP
733 nsDocShellTreeOwner::OnLocationChange(nsIWebProgress* aWebProgress,
734 nsIRequest* aRequest, nsIURI* aURI,
735 uint32_t aFlags) {
736 if (mChromeTooltipListener && aWebProgress &&
737 !(aFlags & nsIWebProgressListener::LOCATION_CHANGE_SAME_DOCUMENT) &&
738 mChromeTooltipListener->WebProgressShowedTooltip(aWebProgress)) {
739 mChromeTooltipListener->HideTooltip();
741 return NS_OK;
744 NS_IMETHODIMP
745 nsDocShellTreeOwner::OnStatusChange(nsIWebProgress* aWebProgress,
746 nsIRequest* aRequest, nsresult aStatus,
747 const char16_t* aMessage) {
748 return NS_OK;
751 NS_IMETHODIMP
752 nsDocShellTreeOwner::OnSecurityChange(nsIWebProgress* aWebProgress,
753 nsIRequest* aRequest, uint32_t aState) {
754 return NS_OK;
757 NS_IMETHODIMP
758 nsDocShellTreeOwner::OnContentBlockingEvent(nsIWebProgress* aWebProgress,
759 nsIRequest* aRequest,
760 uint32_t aEvent) {
761 return NS_OK;
764 //*****************************************************************************
765 // nsDocShellTreeOwner: Accessors
766 //*****************************************************************************
768 void nsDocShellTreeOwner::WebBrowser(nsWebBrowser* aWebBrowser) {
769 if (!aWebBrowser) {
770 RemoveChromeListeners();
772 if (aWebBrowser != mWebBrowser) {
773 mPrompter = nullptr;
774 mAuthPrompter = nullptr;
777 mWebBrowser = aWebBrowser;
779 if (mContentTreeOwner) {
780 mContentTreeOwner->WebBrowser(aWebBrowser);
781 if (!aWebBrowser) {
782 mContentTreeOwner = nullptr;
787 nsWebBrowser* nsDocShellTreeOwner::WebBrowser() { return mWebBrowser; }
789 NS_IMETHODIMP
790 nsDocShellTreeOwner::SetTreeOwner(nsIDocShellTreeOwner* aTreeOwner) {
791 if (aTreeOwner) {
792 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome(do_GetInterface(aTreeOwner));
793 NS_ENSURE_TRUE(webBrowserChrome, NS_ERROR_INVALID_ARG);
794 NS_ENSURE_SUCCESS(SetWebBrowserChrome(webBrowserChrome),
795 NS_ERROR_INVALID_ARG);
796 mTreeOwner = aTreeOwner;
797 } else {
798 mTreeOwner = nullptr;
799 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome();
800 if (!webBrowserChrome) {
801 NS_ENSURE_SUCCESS(SetWebBrowserChrome(nullptr), NS_ERROR_FAILURE);
805 return NS_OK;
808 NS_IMETHODIMP
809 nsDocShellTreeOwner::SetWebBrowserChrome(
810 nsIWebBrowserChrome* aWebBrowserChrome) {
811 if (!aWebBrowserChrome) {
812 mWebBrowserChrome = nullptr;
813 mOwnerWin = nullptr;
814 mOwnerRequestor = nullptr;
815 mWebBrowserChromeWeak = nullptr;
816 } else {
817 nsCOMPtr<nsISupportsWeakReference> supportsweak =
818 do_QueryInterface(aWebBrowserChrome);
819 if (supportsweak) {
820 supportsweak->GetWeakReference(getter_AddRefs(mWebBrowserChromeWeak));
821 } else {
822 nsCOMPtr<nsIBaseWindow> ownerWin(do_QueryInterface(aWebBrowserChrome));
823 nsCOMPtr<nsIInterfaceRequestor> requestor(
824 do_QueryInterface(aWebBrowserChrome));
826 // it's ok for ownerWin or requestor to be null.
827 mWebBrowserChrome = aWebBrowserChrome;
828 mOwnerWin = ownerWin;
829 mOwnerRequestor = requestor;
833 if (mContentTreeOwner) {
834 mContentTreeOwner->SetWebBrowserChrome(aWebBrowserChrome);
837 return NS_OK;
840 // Hook up things to the chrome like context menus and tooltips, if the chrome
841 // has implemented the right interfaces.
842 NS_IMETHODIMP
843 nsDocShellTreeOwner::AddChromeListeners() {
844 nsresult rv = NS_OK;
846 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome();
847 if (!webBrowserChrome) {
848 return NS_ERROR_FAILURE;
851 // install tooltips
852 if (!mChromeTooltipListener) {
853 nsCOMPtr<nsITooltipListener> tooltipListener(
854 do_QueryInterface(webBrowserChrome));
855 if (tooltipListener) {
856 mChromeTooltipListener =
857 new ChromeTooltipListener(mWebBrowser, webBrowserChrome);
858 rv = mChromeTooltipListener->AddChromeListeners();
862 nsCOMPtr<EventTarget> target;
863 GetDOMEventTarget(mWebBrowser, getter_AddRefs(target));
865 // register dragover and drop event listeners with the listener manager
866 MOZ_ASSERT(target, "how does this happen? (see bug 1659758)");
867 if (target) {
868 if (EventListenerManager* elmP = target->GetOrCreateListenerManager()) {
869 elmP->AddEventListenerByType(this, u"dragover"_ns,
870 TrustedEventsAtSystemGroupBubble());
871 elmP->AddEventListenerByType(this, u"drop"_ns,
872 TrustedEventsAtSystemGroupBubble());
876 return rv;
879 NS_IMETHODIMP
880 nsDocShellTreeOwner::RemoveChromeListeners() {
881 if (mChromeTooltipListener) {
882 mChromeTooltipListener->RemoveChromeListeners();
883 mChromeTooltipListener = nullptr;
886 nsCOMPtr<EventTarget> piTarget;
887 GetDOMEventTarget(mWebBrowser, getter_AddRefs(piTarget));
888 if (!piTarget) {
889 return NS_OK;
892 EventListenerManager* elmP = piTarget->GetOrCreateListenerManager();
893 if (elmP) {
894 elmP->RemoveEventListenerByType(this, u"dragover"_ns,
895 TrustedEventsAtSystemGroupBubble());
896 elmP->RemoveEventListenerByType(this, u"drop"_ns,
897 TrustedEventsAtSystemGroupBubble());
900 return NS_OK;
903 NS_IMETHODIMP
904 nsDocShellTreeOwner::HandleEvent(Event* aEvent) {
905 DragEvent* dragEvent = aEvent ? aEvent->AsDragEvent() : nullptr;
906 if (NS_WARN_IF(!dragEvent)) {
907 return NS_ERROR_INVALID_ARG;
910 if (dragEvent->DefaultPrevented()) {
911 return NS_OK;
914 nsCOMPtr<nsIDroppedLinkHandler> handler =
915 do_GetService("@mozilla.org/content/dropped-link-handler;1");
916 if (!handler) {
917 return NS_OK;
920 nsAutoString eventType;
921 aEvent->GetType(eventType);
922 if (eventType.EqualsLiteral("dragover")) {
923 bool canDropLink = false;
924 handler->CanDropLink(dragEvent, false, &canDropLink);
925 if (canDropLink) {
926 aEvent->PreventDefault();
927 WidgetDragEvent* asWidgetDropEvent =
928 dragEvent->WidgetEventPtr()->AsDragEvent();
929 asWidgetDropEvent->UpdateDefaultPreventedOnContent(
930 asWidgetDropEvent->mCurrentTarget);
932 } else if (eventType.EqualsLiteral("drop")) {
933 nsCOMPtr<nsIWebNavigation> webnav =
934 static_cast<nsIWebNavigation*>(mWebBrowser);
936 // The page might have cancelled the dragover event itself, so check to
937 // make sure that the link can be dropped first.
938 bool canDropLink = false;
939 handler->CanDropLink(dragEvent, false, &canDropLink);
940 if (!canDropLink) {
941 return NS_OK;
944 nsTArray<RefPtr<nsIDroppedLinkItem>> links;
945 if (webnav && NS_SUCCEEDED(handler->DropLinks(dragEvent, true, links))) {
946 if (links.Length() >= 1) {
947 nsCOMPtr<nsIPrincipal> triggeringPrincipal;
948 handler->GetTriggeringPrincipal(dragEvent,
949 getter_AddRefs(triggeringPrincipal));
950 if (triggeringPrincipal) {
951 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome =
952 GetWebBrowserChrome();
953 if (webBrowserChrome) {
954 nsCOMPtr<nsIBrowserChild> browserChild =
955 do_QueryInterface(webBrowserChrome);
956 if (browserChild) {
957 nsresult rv = browserChild->RemoteDropLinks(links);
958 return rv;
961 nsAutoString url;
962 if (NS_SUCCEEDED(links[0]->GetUrl(url))) {
963 if (!url.IsEmpty()) {
964 #ifndef ANDROID
965 MOZ_ASSERT(triggeringPrincipal,
966 "nsDocShellTreeOwner::HandleEvent: Need a valid "
967 "triggeringPrincipal");
968 #endif
969 LoadURIOptions loadURIOptions;
970 loadURIOptions.mTriggeringPrincipal = triggeringPrincipal;
971 nsCOMPtr<nsIContentSecurityPolicy> csp;
972 handler->GetCsp(dragEvent, getter_AddRefs(csp));
973 loadURIOptions.mCsp = csp;
974 webnav->FixupAndLoadURIString(url, loadURIOptions);
979 } else {
980 aEvent->StopPropagation();
981 aEvent->PreventDefault();
982 WidgetDragEvent* asWidgetDropEvent =
983 dragEvent->WidgetEventPtr()->AsDragEvent();
984 asWidgetDropEvent->UpdateDefaultPreventedOnContent(
985 asWidgetDropEvent->mCurrentTarget);
989 return NS_OK;
992 already_AddRefed<nsIWebBrowserChrome>
993 nsDocShellTreeOwner::GetWebBrowserChrome() {
994 nsCOMPtr<nsIWebBrowserChrome> chrome;
995 if (mWebBrowserChromeWeak) {
996 chrome = do_QueryReferent(mWebBrowserChromeWeak);
997 } else if (mWebBrowserChrome) {
998 chrome = mWebBrowserChrome;
1000 return chrome.forget();
1003 already_AddRefed<nsIBaseWindow> nsDocShellTreeOwner::GetOwnerWin() {
1004 nsCOMPtr<nsIBaseWindow> win;
1005 if (mWebBrowserChromeWeak) {
1006 win = do_QueryReferent(mWebBrowserChromeWeak);
1007 } else if (mOwnerWin) {
1008 win = mOwnerWin;
1010 return win.forget();
1013 already_AddRefed<nsIInterfaceRequestor>
1014 nsDocShellTreeOwner::GetOwnerRequestor() {
1015 nsCOMPtr<nsIInterfaceRequestor> req;
1016 if (mWebBrowserChromeWeak) {
1017 req = do_QueryReferent(mWebBrowserChromeWeak);
1018 } else if (mOwnerRequestor) {
1019 req = mOwnerRequestor;
1021 return req.forget();
1024 NS_IMPL_ISUPPORTS(ChromeTooltipListener, nsIDOMEventListener)
1026 ChromeTooltipListener::ChromeTooltipListener(nsWebBrowser* aInBrowser,
1027 nsIWebBrowserChrome* aInChrome)
1028 : mWebBrowser(aInBrowser),
1029 mWebBrowserChrome(aInChrome),
1030 mTooltipListenerInstalled(false),
1031 mShowingTooltip(false),
1032 mTooltipShownOnce(false) {}
1034 ChromeTooltipListener::~ChromeTooltipListener() {}
1036 nsITooltipTextProvider* ChromeTooltipListener::GetTooltipTextProvider() {
1037 if (!mTooltipTextProvider) {
1038 mTooltipTextProvider = do_GetService(NS_TOOLTIPTEXTPROVIDER_CONTRACTID);
1041 if (!mTooltipTextProvider) {
1042 mTooltipTextProvider =
1043 do_GetService(NS_DEFAULTTOOLTIPTEXTPROVIDER_CONTRACTID);
1046 return mTooltipTextProvider;
1049 // Hook up things to the chrome like context menus and tooltips, if the chrome
1050 // has implemented the right interfaces.
1051 NS_IMETHODIMP
1052 ChromeTooltipListener::AddChromeListeners() {
1053 if (!mEventTarget) {
1054 GetDOMEventTarget(mWebBrowser, getter_AddRefs(mEventTarget));
1057 // Register the appropriate events for tooltips, but only if
1058 // the embedding chrome cares.
1059 nsresult rv = NS_OK;
1060 nsCOMPtr<nsITooltipListener> tooltipListener(
1061 do_QueryInterface(mWebBrowserChrome));
1062 if (tooltipListener && !mTooltipListenerInstalled) {
1063 rv = AddTooltipListener();
1064 if (NS_FAILED(rv)) {
1065 return rv;
1069 return rv;
1072 // Subscribe to the events that will allow us to track tooltips. We need "mouse"
1073 // for mouseExit, "mouse motion" for mouseMove, and "key" for keyDown. As we
1074 // add the listeners, keep track of how many succeed so we can clean up
1075 // correctly in Release().
1076 NS_IMETHODIMP
1077 ChromeTooltipListener::AddTooltipListener() {
1078 if (mEventTarget) {
1079 MOZ_TRY(mEventTarget->AddSystemEventListener(u"keydown"_ns, this, false,
1080 false));
1081 MOZ_TRY(mEventTarget->AddSystemEventListener(u"mousedown"_ns, this, false,
1082 false));
1083 MOZ_TRY(mEventTarget->AddSystemEventListener(u"mouseout"_ns, this, false,
1084 false));
1085 MOZ_TRY(mEventTarget->AddSystemEventListener(u"mousemove"_ns, this, false,
1086 false));
1088 mTooltipListenerInstalled = true;
1091 return NS_OK;
1094 // Unsubscribe from the various things we've hooked up to the window root.
1095 NS_IMETHODIMP
1096 ChromeTooltipListener::RemoveChromeListeners() {
1097 HideTooltip();
1099 if (mTooltipListenerInstalled) {
1100 RemoveTooltipListener();
1103 mEventTarget = nullptr;
1105 // it really doesn't matter if these fail...
1106 return NS_OK;
1109 // Unsubscribe from all the various tooltip events that we were listening to.
1110 NS_IMETHODIMP
1111 ChromeTooltipListener::RemoveTooltipListener() {
1112 if (mEventTarget) {
1113 mEventTarget->RemoveSystemEventListener(u"keydown"_ns, this, false);
1114 mEventTarget->RemoveSystemEventListener(u"mousedown"_ns, this, false);
1115 mEventTarget->RemoveSystemEventListener(u"mouseout"_ns, this, false);
1116 mEventTarget->RemoveSystemEventListener(u"mousemove"_ns, this, false);
1117 mTooltipListenerInstalled = false;
1120 return NS_OK;
1123 NS_IMETHODIMP
1124 ChromeTooltipListener::HandleEvent(Event* aEvent) {
1125 nsAutoString eventType;
1126 aEvent->GetType(eventType);
1128 if (eventType.EqualsLiteral("mousedown")) {
1129 return HideTooltip();
1130 } else if (eventType.EqualsLiteral("keydown")) {
1131 WidgetKeyboardEvent* keyEvent = aEvent->WidgetEventPtr()->AsKeyboardEvent();
1132 if (nsXULTooltipListener::KeyEventHidesTooltip(*keyEvent)) {
1133 return HideTooltip();
1135 return NS_OK;
1136 } else if (eventType.EqualsLiteral("mouseout")) {
1137 // Reset flag so that tooltip will display on the next MouseMove
1138 mTooltipShownOnce = false;
1139 return HideTooltip();
1140 } else if (eventType.EqualsLiteral("mousemove")) {
1141 return MouseMove(aEvent);
1144 NS_ERROR("Unexpected event type");
1145 return NS_OK;
1148 // If we're a tooltip, fire off a timer to see if a tooltip should be shown. If
1149 // the timer fires, we cache the node in |mPossibleTooltipNode|.
1150 nsresult ChromeTooltipListener::MouseMove(Event* aMouseEvent) {
1151 if (!nsXULTooltipListener::ShowTooltips()) {
1152 return NS_OK;
1155 MouseEvent* mouseEvent = aMouseEvent->AsMouseEvent();
1156 if (!mouseEvent) {
1157 return NS_OK;
1160 // stash the coordinates of the event so that we can still get back to it from
1161 // within the timer callback. On win32, we'll get a MouseMove event even when
1162 // a popup goes away -- even when the mouse doesn't change position! To get
1163 // around this, we make sure the mouse has really moved before proceeding.
1164 CSSIntPoint newMouseClientPoint = mouseEvent->ClientPoint();
1165 if (mMouseClientPoint == newMouseClientPoint) {
1166 return NS_OK;
1169 // Filter out minor mouse movements.
1170 if (mShowingTooltip &&
1171 (abs(mMouseClientPoint.x - newMouseClientPoint.x) <=
1172 kTooltipMouseMoveTolerance) &&
1173 (abs(mMouseClientPoint.y - newMouseClientPoint.y) <=
1174 kTooltipMouseMoveTolerance)) {
1175 return NS_OK;
1178 mMouseClientPoint = newMouseClientPoint;
1179 mMouseScreenPoint = mouseEvent->ScreenPointLayoutDevicePix();
1181 if (mTooltipTimer) {
1182 mTooltipTimer->Cancel();
1183 mTooltipTimer = nullptr;
1186 if (!mShowingTooltip) {
1187 nsIEventTarget* target = nullptr;
1188 if (nsCOMPtr<EventTarget> eventTarget = aMouseEvent->GetComposedTarget()) {
1189 mPossibleTooltipNode = nsINode::FromEventTarget(eventTarget);
1190 nsCOMPtr<nsIGlobalObject> global(eventTarget->GetOwnerGlobal());
1191 if (global) {
1192 target = global->EventTargetFor(TaskCategory::UI);
1196 if (mPossibleTooltipNode) {
1197 nsresult rv = NS_NewTimerWithFuncCallback(
1198 getter_AddRefs(mTooltipTimer), sTooltipCallback, this,
1199 LookAndFeel::GetInt(LookAndFeel::IntID::TooltipDelay, 500),
1200 nsITimer::TYPE_ONE_SHOT, "ChromeTooltipListener::MouseMove", target);
1201 if (NS_FAILED(rv)) {
1202 mPossibleTooltipNode = nullptr;
1203 NS_WARNING("Could not create a timer for tooltip tracking");
1206 } else {
1207 mTooltipShownOnce = true;
1208 return HideTooltip();
1211 return NS_OK;
1214 // Tell the registered chrome that they should show the tooltip.
1215 NS_IMETHODIMP
1216 ChromeTooltipListener::ShowTooltip(int32_t aInXCoords, int32_t aInYCoords,
1217 const nsAString& aInTipText,
1218 const nsAString& aTipDir) {
1219 nsresult rv = NS_OK;
1221 // do the work to call the client
1222 nsCOMPtr<nsITooltipListener> tooltipListener(
1223 do_QueryInterface(mWebBrowserChrome));
1224 if (tooltipListener) {
1225 rv = tooltipListener->OnShowTooltip(aInXCoords, aInYCoords, aInTipText,
1226 aTipDir);
1227 if (NS_SUCCEEDED(rv)) {
1228 mShowingTooltip = true;
1232 return rv;
1235 // Tell the registered chrome that they should rollup the tooltip
1236 // NOTE: This routine is safe to call even if the popup is already closed.
1237 NS_IMETHODIMP
1238 ChromeTooltipListener::HideTooltip() {
1239 nsresult rv = NS_OK;
1241 // shut down the relevant timers
1242 if (mTooltipTimer) {
1243 mTooltipTimer->Cancel();
1244 mTooltipTimer = nullptr;
1245 // release tooltip target
1246 mPossibleTooltipNode = nullptr;
1247 mLastDocshell = nullptr;
1250 // if we're showing the tip, tell the chrome to hide it
1251 if (mShowingTooltip) {
1252 nsCOMPtr<nsITooltipListener> tooltipListener(
1253 do_QueryInterface(mWebBrowserChrome));
1254 if (tooltipListener) {
1255 rv = tooltipListener->OnHideTooltip();
1256 if (NS_SUCCEEDED(rv)) {
1257 mShowingTooltip = false;
1262 return rv;
1265 bool ChromeTooltipListener::WebProgressShowedTooltip(
1266 nsIWebProgress* aWebProgress) {
1267 nsCOMPtr<nsIDocShell> docshell = do_QueryInterface(aWebProgress);
1268 nsCOMPtr<nsIDocShell> lastUsed = do_QueryReferent(mLastDocshell);
1269 while (lastUsed) {
1270 if (lastUsed == docshell) {
1271 return true;
1273 // We can't use the docshell hierarchy here, because when the parent
1274 // docshell is navigated, the child docshell is disconnected (ie its
1275 // references to the parent are nulled out) despite it still being
1276 // alive here. So we use the document hierarchy instead:
1277 Document* document = lastUsed->GetDocument();
1278 if (document) {
1279 document = document->GetInProcessParentDocument();
1281 if (!document) {
1282 break;
1284 lastUsed = document->GetDocShell();
1286 return false;
1289 // A timer callback, fired when the mouse has hovered inside of a frame for the
1290 // appropriate amount of time. Getting to this point means that we should show
1291 // the tooltip, but only after we determine there is an appropriate TITLE
1292 // element.
1294 // This relies on certain things being cached into the |aChromeTooltipListener|
1295 // object passed to us by the timer:
1296 // -- the x/y coordinates of the mouse (mMouseClientY, mMouseClientX)
1297 // -- the dom node the user hovered over (mPossibleTooltipNode)
1298 void ChromeTooltipListener::sTooltipCallback(nsITimer* aTimer,
1299 void* aChromeTooltipListener) {
1300 auto* self = static_cast<ChromeTooltipListener*>(aChromeTooltipListener);
1301 if (!self || !self->mPossibleTooltipNode) {
1302 return;
1304 // release tooltip target once done, no matter what we do here.
1305 auto cleanup = MakeScopeExit([&] { self->mPossibleTooltipNode = nullptr; });
1306 if (!self->mPossibleTooltipNode->IsInComposedDoc()) {
1307 return;
1309 // Check that the document or its ancestors haven't been replaced.
1311 Document* doc = self->mPossibleTooltipNode->OwnerDoc();
1312 while (doc) {
1313 if (!doc->IsCurrentActiveDocument()) {
1314 return;
1316 doc = doc->GetInProcessParentDocument();
1320 nsCOMPtr<nsIDocShell> docShell =
1321 do_GetInterface(static_cast<nsIWebBrowser*>(self->mWebBrowser));
1322 if (!docShell || !docShell->GetBrowsingContext()->IsActive()) {
1323 return;
1326 // if there is text associated with the node, show the tip and fire
1327 // off a timer to auto-hide it.
1328 nsITooltipTextProvider* tooltipProvider = self->GetTooltipTextProvider();
1329 if (!tooltipProvider) {
1330 return;
1332 nsString tooltipText;
1333 nsString directionText;
1334 bool textFound = false;
1335 tooltipProvider->GetNodeText(self->mPossibleTooltipNode,
1336 getter_Copies(tooltipText),
1337 getter_Copies(directionText), &textFound);
1339 if (textFound && (!self->mTooltipShownOnce ||
1340 tooltipText != self->mLastShownTooltipText)) {
1341 // ShowTooltip expects screen-relative position.
1342 self->ShowTooltip(self->mMouseScreenPoint.x, self->mMouseScreenPoint.y,
1343 tooltipText, directionText);
1344 self->mLastShownTooltipText = std::move(tooltipText);
1345 self->mLastDocshell = do_GetWeakReference(
1346 self->mPossibleTooltipNode->OwnerDoc()->GetDocShell());