Bug 1920009 - Fix fragment titles being read by talkback after dismissal from menu...
[gecko.git] / docshell / base / nsDocShellTreeOwner.cpp
blobbd18a7dce0e6543a3e17775ccf617cd1809d2bff
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/StaticPrefs_ui.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 "nsIContent.h"
43 #include "nsServiceManagerUtils.h"
44 #include "nsViewManager.h"
45 #include "nsView.h"
46 #include "nsXULTooltipListener.h"
47 #include "nsIConstraintValidation.h"
48 #include "mozilla/Attributes.h"
49 #include "mozilla/EventListenerManager.h"
50 #include "mozilla/Try.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(nsIPrompt))) {
199 nsCOMPtr<nsIPrompt> prompt;
200 EnsurePrompter();
201 prompt = mPrompter;
202 if (prompt) {
203 prompt.forget(aSink);
204 return NS_OK;
206 return NS_NOINTERFACE;
209 if (aIID.Equals(NS_GET_IID(nsIAuthPrompt))) {
210 nsCOMPtr<nsIAuthPrompt> prompt;
211 EnsureAuthPrompter();
212 prompt = mAuthPrompter;
213 if (prompt) {
214 prompt.forget(aSink);
215 return NS_OK;
217 return NS_NOINTERFACE;
220 nsCOMPtr<nsIInterfaceRequestor> req = GetOwnerRequestor();
221 if (req) {
222 return req->GetInterface(aIID, aSink);
225 return NS_NOINTERFACE;
228 //*****************************************************************************
229 // nsDocShellTreeOwner::nsIDocShellTreeOwner
230 //*****************************************************************************
232 void nsDocShellTreeOwner::EnsurePrompter() {
233 if (mPrompter) {
234 return;
237 nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
238 if (wwatch && mWebBrowser) {
239 nsCOMPtr<mozIDOMWindowProxy> domWindow;
240 mWebBrowser->GetContentDOMWindow(getter_AddRefs(domWindow));
241 if (domWindow) {
242 wwatch->GetNewPrompter(domWindow, getter_AddRefs(mPrompter));
247 void nsDocShellTreeOwner::EnsureAuthPrompter() {
248 if (mAuthPrompter) {
249 return;
252 nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
253 if (wwatch && mWebBrowser) {
254 nsCOMPtr<mozIDOMWindowProxy> domWindow;
255 mWebBrowser->GetContentDOMWindow(getter_AddRefs(domWindow));
256 if (domWindow) {
257 wwatch->GetNewAuthPrompter(domWindow, getter_AddRefs(mAuthPrompter));
262 void nsDocShellTreeOwner::AddToWatcher() {
263 if (mWebBrowser) {
264 nsCOMPtr<mozIDOMWindowProxy> domWindow;
265 mWebBrowser->GetContentDOMWindow(getter_AddRefs(domWindow));
266 if (domWindow) {
267 nsCOMPtr<nsPIWindowWatcher> wwatch(
268 do_GetService(NS_WINDOWWATCHER_CONTRACTID));
269 if (wwatch) {
270 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome();
271 if (webBrowserChrome) {
272 wwatch->AddWindow(domWindow, webBrowserChrome);
279 void nsDocShellTreeOwner::RemoveFromWatcher() {
280 if (mWebBrowser) {
281 nsCOMPtr<mozIDOMWindowProxy> domWindow;
282 mWebBrowser->GetContentDOMWindow(getter_AddRefs(domWindow));
283 if (domWindow) {
284 nsCOMPtr<nsPIWindowWatcher> wwatch(
285 do_GetService(NS_WINDOWWATCHER_CONTRACTID));
286 if (wwatch) {
287 wwatch->RemoveWindow(domWindow);
293 void nsDocShellTreeOwner::EnsureContentTreeOwner() {
294 if (mContentTreeOwner) {
295 return;
298 mContentTreeOwner = new nsDocShellTreeOwner();
299 nsCOMPtr<nsIWebBrowserChrome> browserChrome = GetWebBrowserChrome();
300 if (browserChrome) {
301 mContentTreeOwner->SetWebBrowserChrome(browserChrome);
304 if (mWebBrowser) {
305 mContentTreeOwner->WebBrowser(mWebBrowser);
309 NS_IMETHODIMP
310 nsDocShellTreeOwner::ContentShellAdded(nsIDocShellTreeItem* aContentShell,
311 bool aPrimary) {
312 if (mTreeOwner) return mTreeOwner->ContentShellAdded(aContentShell, aPrimary);
314 EnsureContentTreeOwner();
315 aContentShell->SetTreeOwner(mContentTreeOwner);
317 if (aPrimary) {
318 mPrimaryContentShell = aContentShell;
319 mPrimaryRemoteTab = nullptr;
321 return NS_OK;
324 NS_IMETHODIMP
325 nsDocShellTreeOwner::ContentShellRemoved(nsIDocShellTreeItem* aContentShell) {
326 if (mTreeOwner) {
327 return mTreeOwner->ContentShellRemoved(aContentShell);
330 if (mPrimaryContentShell == aContentShell) {
331 mPrimaryContentShell = nullptr;
334 return NS_OK;
337 NS_IMETHODIMP
338 nsDocShellTreeOwner::GetPrimaryContentShell(nsIDocShellTreeItem** aShell) {
339 NS_ENSURE_ARG_POINTER(aShell);
341 if (mTreeOwner) {
342 return mTreeOwner->GetPrimaryContentShell(aShell);
345 nsCOMPtr<nsIDocShellTreeItem> shell;
346 if (!mPrimaryRemoteTab) {
347 shell =
348 mPrimaryContentShell ? mPrimaryContentShell : mWebBrowser->mDocShell;
350 shell.forget(aShell);
352 return NS_OK;
355 NS_IMETHODIMP
356 nsDocShellTreeOwner::RemoteTabAdded(nsIRemoteTab* aTab, bool aPrimary) {
357 if (mTreeOwner) {
358 return mTreeOwner->RemoteTabAdded(aTab, aPrimary);
361 if (aPrimary) {
362 mPrimaryRemoteTab = aTab;
363 mPrimaryContentShell = nullptr;
364 } else if (mPrimaryRemoteTab == aTab) {
365 mPrimaryRemoteTab = nullptr;
368 return NS_OK;
371 NS_IMETHODIMP
372 nsDocShellTreeOwner::RemoteTabRemoved(nsIRemoteTab* aTab) {
373 if (mTreeOwner) {
374 return mTreeOwner->RemoteTabRemoved(aTab);
377 if (aTab == mPrimaryRemoteTab) {
378 mPrimaryRemoteTab = nullptr;
381 return NS_OK;
384 NS_IMETHODIMP
385 nsDocShellTreeOwner::GetPrimaryRemoteTab(nsIRemoteTab** aTab) {
386 if (mTreeOwner) {
387 return mTreeOwner->GetPrimaryRemoteTab(aTab);
390 nsCOMPtr<nsIRemoteTab> tab = mPrimaryRemoteTab;
391 tab.forget(aTab);
392 return NS_OK;
395 NS_IMETHODIMP
396 nsDocShellTreeOwner::GetPrimaryContentBrowsingContext(
397 mozilla::dom::BrowsingContext** aBc) {
398 if (mTreeOwner) {
399 return mTreeOwner->GetPrimaryContentBrowsingContext(aBc);
401 if (mPrimaryRemoteTab) {
402 return mPrimaryRemoteTab->GetBrowsingContext(aBc);
404 if (mPrimaryContentShell) {
405 return mPrimaryContentShell->GetBrowsingContextXPCOM(aBc);
407 if (mWebBrowser->mDocShell) {
408 return mWebBrowser->mDocShell->GetBrowsingContextXPCOM(aBc);
410 *aBc = nullptr;
411 return NS_OK;
414 NS_IMETHODIMP
415 nsDocShellTreeOwner::GetPrimaryContentSize(int32_t* aWidth, int32_t* aHeight) {
416 return NS_ERROR_NOT_IMPLEMENTED;
419 NS_IMETHODIMP
420 nsDocShellTreeOwner::SetPrimaryContentSize(int32_t aWidth, int32_t aHeight) {
421 return NS_ERROR_NOT_IMPLEMENTED;
424 NS_IMETHODIMP
425 nsDocShellTreeOwner::GetRootShellSize(int32_t* aWidth, int32_t* aHeight) {
426 return NS_ERROR_NOT_IMPLEMENTED;
429 NS_IMETHODIMP
430 nsDocShellTreeOwner::SetRootShellSize(int32_t aWidth, int32_t aHeight) {
431 return NS_ERROR_NOT_IMPLEMENTED;
434 NS_IMETHODIMP
435 nsDocShellTreeOwner::SizeShellTo(nsIDocShellTreeItem* aShellItem, int32_t aCX,
436 int32_t aCY) {
437 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome();
439 NS_ENSURE_STATE(mTreeOwner || webBrowserChrome);
441 if (nsCOMPtr<nsIDocShellTreeOwner> treeOwner = mTreeOwner) {
442 return treeOwner->SizeShellTo(aShellItem, aCX, aCY);
445 if (aShellItem == mWebBrowser->mDocShell) {
446 nsCOMPtr<nsIBrowserChild> browserChild =
447 do_QueryInterface(webBrowserChrome);
448 if (browserChild) {
449 // The XUL window to resize is in the parent process, but there we
450 // won't be able to get the size of aShellItem. We can ask the parent
451 // process to change our size instead.
452 nsCOMPtr<nsIBaseWindow> shellAsWin(do_QueryInterface(aShellItem));
453 NS_ENSURE_TRUE(shellAsWin, NS_ERROR_FAILURE);
455 LayoutDeviceIntSize shellSize;
456 shellAsWin->GetSize(&shellSize.width, &shellSize.height);
457 LayoutDeviceIntSize deltaSize = LayoutDeviceIntSize(aCX, aCY) - shellSize;
459 LayoutDeviceIntSize currentSize;
460 GetSize(&currentSize.width, &currentSize.height);
462 LayoutDeviceIntSize newSize = currentSize + deltaSize;
463 return SetSize(newSize.width, newSize.height, true);
465 // XXX: this is weird, but we used to call a method here
466 // (webBrowserChrome->SizeBrowserTo()) whose implementations all failed
467 // like this, so...
468 return NS_ERROR_NOT_IMPLEMENTED;
471 MOZ_ASSERT_UNREACHABLE("This is unimplemented, API should be cleaned up");
472 return NS_ERROR_NOT_IMPLEMENTED;
475 NS_IMETHODIMP
476 nsDocShellTreeOwner::SetPersistence(bool aPersistPosition, bool aPersistSize,
477 bool aPersistSizeMode) {
478 return NS_ERROR_NOT_IMPLEMENTED;
481 NS_IMETHODIMP
482 nsDocShellTreeOwner::GetPersistence(bool* aPersistPosition, bool* aPersistSize,
483 bool* aPersistSizeMode) {
484 return NS_ERROR_NOT_IMPLEMENTED;
487 NS_IMETHODIMP
488 nsDocShellTreeOwner::GetTabCount(uint32_t* aResult) {
489 if (mTreeOwner) {
490 return mTreeOwner->GetTabCount(aResult);
493 *aResult = 0;
494 return NS_OK;
497 NS_IMETHODIMP
498 nsDocShellTreeOwner::GetHasPrimaryContent(bool* aResult) {
499 *aResult = mPrimaryRemoteTab || mPrimaryContentShell;
500 return NS_OK;
503 //*****************************************************************************
504 // nsDocShellTreeOwner::nsIBaseWindow
505 //*****************************************************************************
507 NS_IMETHODIMP
508 nsDocShellTreeOwner::InitWindow(nsIWidget* aParentWidget, int32_t aX,
509 int32_t aY, int32_t aCX, int32_t aCY) {
510 return NS_ERROR_NULL_POINTER;
513 NS_IMETHODIMP
514 nsDocShellTreeOwner::Destroy() {
515 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome();
516 if (webBrowserChrome) {
517 // XXX: this is weird, but we used to call a method here
518 // (webBrowserChrome->DestroyBrowserWindow()) whose implementations all
519 // failed like this, so...
520 return NS_ERROR_NOT_IMPLEMENTED;
523 return NS_ERROR_NULL_POINTER;
526 double nsDocShellTreeOwner::GetWidgetCSSToDeviceScale() {
527 return mWebBrowser ? mWebBrowser->GetWidgetCSSToDeviceScale() : 1.0;
530 NS_IMETHODIMP
531 nsDocShellTreeOwner::GetDevicePixelsPerDesktopPixel(double* aScale) {
532 if (mWebBrowser) {
533 return mWebBrowser->GetDevicePixelsPerDesktopPixel(aScale);
536 *aScale = 1.0;
537 return NS_OK;
540 NS_IMETHODIMP
541 nsDocShellTreeOwner::SetPositionDesktopPix(int32_t aX, int32_t aY) {
542 if (mWebBrowser) {
543 nsresult rv = mWebBrowser->SetPositionDesktopPix(aX, aY);
544 NS_ENSURE_SUCCESS(rv, rv);
547 double scale = 1.0;
548 GetDevicePixelsPerDesktopPixel(&scale);
549 return SetPosition(NSToIntRound(aX * scale), NSToIntRound(aY * scale));
552 NS_IMETHODIMP
553 nsDocShellTreeOwner::SetPosition(int32_t aX, int32_t aY) {
554 return SetDimensions(
555 {DimensionKind::Outer, Some(aX), Some(aY), Nothing(), Nothing()});
558 NS_IMETHODIMP
559 nsDocShellTreeOwner::GetPosition(int32_t* aX, int32_t* aY) {
560 return GetDimensions(DimensionKind::Outer, aX, aY, nullptr, nullptr);
563 NS_IMETHODIMP
564 nsDocShellTreeOwner::SetSize(int32_t aCX, int32_t aCY, bool aRepaint) {
565 return SetDimensions(
566 {DimensionKind::Outer, Nothing(), Nothing(), Some(aCX), Some(aCY)});
569 NS_IMETHODIMP
570 nsDocShellTreeOwner::GetSize(int32_t* aCX, int32_t* aCY) {
571 return GetDimensions(DimensionKind::Outer, nullptr, nullptr, aCX, aCY);
574 NS_IMETHODIMP
575 nsDocShellTreeOwner::SetPositionAndSize(int32_t aX, int32_t aY, int32_t aCX,
576 int32_t aCY, uint32_t aFlags) {
577 return SetDimensions(
578 {DimensionKind::Outer, Some(aX), Some(aY), Some(aCX), Some(aCY)});
581 NS_IMETHODIMP
582 nsDocShellTreeOwner::GetPositionAndSize(int32_t* aX, int32_t* aY, int32_t* aCX,
583 int32_t* aCY) {
584 return GetDimensions(DimensionKind::Outer, aX, aY, aCX, aCY);
587 NS_IMETHODIMP
588 nsDocShellTreeOwner::SetDimensions(DimensionRequest&& aRequest) {
589 nsCOMPtr<nsIBaseWindow> ownerWin = GetOwnerWin();
590 if (ownerWin) {
591 return ownerWin->SetDimensions(std::move(aRequest));
594 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome();
595 NS_ENSURE_STATE(webBrowserChrome);
596 return webBrowserChrome->SetDimensions(std::move(aRequest));
599 NS_IMETHODIMP
600 nsDocShellTreeOwner::GetDimensions(DimensionKind aDimensionKind, int32_t* aX,
601 int32_t* aY, int32_t* aCX, int32_t* aCY) {
602 nsCOMPtr<nsIBaseWindow> ownerWin = GetOwnerWin();
603 if (ownerWin) {
604 return ownerWin->GetDimensions(aDimensionKind, aX, aY, aCX, aCY);
607 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome();
608 NS_ENSURE_STATE(webBrowserChrome);
609 return webBrowserChrome->GetDimensions(aDimensionKind, aX, aY, aCX, aCY);
612 NS_IMETHODIMP
613 nsDocShellTreeOwner::Repaint(bool aForce) { return NS_ERROR_NULL_POINTER; }
615 NS_IMETHODIMP
616 nsDocShellTreeOwner::GetParentWidget(nsIWidget** aParentWidget) {
617 return NS_ERROR_NULL_POINTER;
620 NS_IMETHODIMP
621 nsDocShellTreeOwner::SetParentWidget(nsIWidget* aParentWidget) {
622 return NS_ERROR_NULL_POINTER;
625 NS_IMETHODIMP
626 nsDocShellTreeOwner::GetNativeHandle(nsAString& aNativeHandle) {
627 // the nativeHandle should be accessed from nsIAppWindow
628 return NS_ERROR_NOT_IMPLEMENTED;
631 NS_IMETHODIMP
632 nsDocShellTreeOwner::GetVisibility(bool* aVisibility) {
633 nsCOMPtr<nsIBaseWindow> ownerWin = GetOwnerWin();
634 if (ownerWin) {
635 return ownerWin->GetVisibility(aVisibility);
638 return NS_ERROR_NOT_IMPLEMENTED;
641 NS_IMETHODIMP
642 nsDocShellTreeOwner::SetVisibility(bool aVisibility) {
643 nsCOMPtr<nsIBaseWindow> ownerWin = GetOwnerWin();
644 if (ownerWin) {
645 return ownerWin->SetVisibility(aVisibility);
647 return NS_ERROR_NULL_POINTER;
650 NS_IMETHODIMP
651 nsDocShellTreeOwner::GetEnabled(bool* aEnabled) {
652 NS_ENSURE_ARG_POINTER(aEnabled);
653 *aEnabled = true;
654 return NS_ERROR_NOT_IMPLEMENTED;
657 NS_IMETHODIMP
658 nsDocShellTreeOwner::SetEnabled(bool aEnabled) {
659 return NS_ERROR_NOT_IMPLEMENTED;
662 NS_IMETHODIMP
663 nsDocShellTreeOwner::GetMainWidget(nsIWidget** aMainWidget) {
664 return NS_ERROR_NULL_POINTER;
667 NS_IMETHODIMP
668 nsDocShellTreeOwner::GetTitle(nsAString& aTitle) {
669 nsCOMPtr<nsIBaseWindow> ownerWin = GetOwnerWin();
670 if (ownerWin) {
671 return ownerWin->GetTitle(aTitle);
673 return NS_ERROR_NULL_POINTER;
676 NS_IMETHODIMP
677 nsDocShellTreeOwner::SetTitle(const nsAString& aTitle) {
678 nsCOMPtr<nsIBaseWindow> ownerWin = GetOwnerWin();
679 if (ownerWin) {
680 return ownerWin->SetTitle(aTitle);
682 return NS_ERROR_NULL_POINTER;
685 //*****************************************************************************
686 // nsDocShellTreeOwner::nsIWebProgressListener
687 //*****************************************************************************
689 NS_IMETHODIMP
690 nsDocShellTreeOwner::OnProgressChange(nsIWebProgress* aProgress,
691 nsIRequest* aRequest,
692 int32_t aCurSelfProgress,
693 int32_t aMaxSelfProgress,
694 int32_t aCurTotalProgress,
695 int32_t aMaxTotalProgress) {
696 // In the absence of DOM document creation event, this method is the
697 // most convenient place to install the mouse listener on the
698 // DOM document.
699 return AddChromeListeners();
702 NS_IMETHODIMP
703 nsDocShellTreeOwner::OnStateChange(nsIWebProgress* aProgress,
704 nsIRequest* aRequest,
705 uint32_t aProgressStateFlags,
706 nsresult aStatus) {
707 return NS_OK;
710 NS_IMETHODIMP
711 nsDocShellTreeOwner::OnLocationChange(nsIWebProgress* aWebProgress,
712 nsIRequest* aRequest, nsIURI* aURI,
713 uint32_t aFlags) {
714 if (mChromeTooltipListener && aWebProgress &&
715 !(aFlags & nsIWebProgressListener::LOCATION_CHANGE_SAME_DOCUMENT) &&
716 mChromeTooltipListener->WebProgressShowedTooltip(aWebProgress)) {
717 mChromeTooltipListener->HideTooltip();
719 return NS_OK;
722 NS_IMETHODIMP
723 nsDocShellTreeOwner::OnStatusChange(nsIWebProgress* aWebProgress,
724 nsIRequest* aRequest, nsresult aStatus,
725 const char16_t* aMessage) {
726 return NS_OK;
729 NS_IMETHODIMP
730 nsDocShellTreeOwner::OnSecurityChange(nsIWebProgress* aWebProgress,
731 nsIRequest* aRequest, uint32_t aState) {
732 return NS_OK;
735 NS_IMETHODIMP
736 nsDocShellTreeOwner::OnContentBlockingEvent(nsIWebProgress* aWebProgress,
737 nsIRequest* aRequest,
738 uint32_t aEvent) {
739 return NS_OK;
742 //*****************************************************************************
743 // nsDocShellTreeOwner: Accessors
744 //*****************************************************************************
746 void nsDocShellTreeOwner::WebBrowser(nsWebBrowser* aWebBrowser) {
747 if (!aWebBrowser) {
748 RemoveChromeListeners();
750 if (aWebBrowser != mWebBrowser) {
751 mPrompter = nullptr;
752 mAuthPrompter = nullptr;
755 mWebBrowser = aWebBrowser;
757 if (mContentTreeOwner) {
758 mContentTreeOwner->WebBrowser(aWebBrowser);
759 if (!aWebBrowser) {
760 mContentTreeOwner = nullptr;
765 nsWebBrowser* nsDocShellTreeOwner::WebBrowser() { return mWebBrowser; }
767 NS_IMETHODIMP
768 nsDocShellTreeOwner::SetTreeOwner(nsIDocShellTreeOwner* aTreeOwner) {
769 if (aTreeOwner) {
770 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome(do_GetInterface(aTreeOwner));
771 NS_ENSURE_TRUE(webBrowserChrome, NS_ERROR_INVALID_ARG);
772 NS_ENSURE_SUCCESS(SetWebBrowserChrome(webBrowserChrome),
773 NS_ERROR_INVALID_ARG);
774 mTreeOwner = aTreeOwner;
775 } else {
776 mTreeOwner = nullptr;
777 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome();
778 if (!webBrowserChrome) {
779 NS_ENSURE_SUCCESS(SetWebBrowserChrome(nullptr), NS_ERROR_FAILURE);
783 return NS_OK;
786 NS_IMETHODIMP
787 nsDocShellTreeOwner::SetWebBrowserChrome(
788 nsIWebBrowserChrome* aWebBrowserChrome) {
789 if (!aWebBrowserChrome) {
790 mWebBrowserChrome = nullptr;
791 mOwnerWin = nullptr;
792 mOwnerRequestor = nullptr;
793 mWebBrowserChromeWeak = nullptr;
794 } else {
795 nsCOMPtr<nsISupportsWeakReference> supportsweak =
796 do_QueryInterface(aWebBrowserChrome);
797 if (supportsweak) {
798 supportsweak->GetWeakReference(getter_AddRefs(mWebBrowserChromeWeak));
799 } else {
800 nsCOMPtr<nsIBaseWindow> ownerWin(do_QueryInterface(aWebBrowserChrome));
801 nsCOMPtr<nsIInterfaceRequestor> requestor(
802 do_QueryInterface(aWebBrowserChrome));
804 // it's ok for ownerWin or requestor to be null.
805 mWebBrowserChrome = aWebBrowserChrome;
806 mOwnerWin = ownerWin;
807 mOwnerRequestor = requestor;
811 if (mContentTreeOwner) {
812 mContentTreeOwner->SetWebBrowserChrome(aWebBrowserChrome);
815 return NS_OK;
818 // Hook up things to the chrome like context menus and tooltips, if the chrome
819 // has implemented the right interfaces.
820 NS_IMETHODIMP
821 nsDocShellTreeOwner::AddChromeListeners() {
822 nsresult rv = NS_OK;
824 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome();
825 if (!webBrowserChrome) {
826 return NS_ERROR_FAILURE;
829 // install tooltips
830 if (!mChromeTooltipListener) {
831 nsCOMPtr<nsITooltipListener> tooltipListener(
832 do_QueryInterface(webBrowserChrome));
833 if (tooltipListener) {
834 mChromeTooltipListener =
835 new ChromeTooltipListener(mWebBrowser, webBrowserChrome);
836 rv = mChromeTooltipListener->AddChromeListeners();
840 nsCOMPtr<EventTarget> target;
841 GetDOMEventTarget(mWebBrowser, getter_AddRefs(target));
843 // register dragover and drop event listeners with the listener manager
844 MOZ_ASSERT(target, "how does this happen? (see bug 1659758)");
845 if (target) {
846 if (EventListenerManager* elmP = target->GetOrCreateListenerManager()) {
847 elmP->AddEventListenerByType(this, u"dragover"_ns,
848 TrustedEventsAtSystemGroupBubble());
849 elmP->AddEventListenerByType(this, u"drop"_ns,
850 TrustedEventsAtSystemGroupBubble());
854 return rv;
857 NS_IMETHODIMP
858 nsDocShellTreeOwner::RemoveChromeListeners() {
859 if (mChromeTooltipListener) {
860 mChromeTooltipListener->RemoveChromeListeners();
861 mChromeTooltipListener = nullptr;
864 nsCOMPtr<EventTarget> piTarget;
865 GetDOMEventTarget(mWebBrowser, getter_AddRefs(piTarget));
866 if (!piTarget) {
867 return NS_OK;
870 EventListenerManager* elmP = piTarget->GetOrCreateListenerManager();
871 if (elmP) {
872 elmP->RemoveEventListenerByType(this, u"dragover"_ns,
873 TrustedEventsAtSystemGroupBubble());
874 elmP->RemoveEventListenerByType(this, u"drop"_ns,
875 TrustedEventsAtSystemGroupBubble());
878 return NS_OK;
881 NS_IMETHODIMP
882 nsDocShellTreeOwner::HandleEvent(Event* aEvent) {
883 DragEvent* dragEvent = aEvent ? aEvent->AsDragEvent() : nullptr;
884 if (NS_WARN_IF(!dragEvent)) {
885 return NS_ERROR_INVALID_ARG;
888 if (dragEvent->DefaultPrevented()) {
889 return NS_OK;
892 nsCOMPtr<nsIDroppedLinkHandler> handler =
893 do_GetService("@mozilla.org/content/dropped-link-handler;1");
894 if (!handler) {
895 return NS_OK;
898 nsAutoString eventType;
899 aEvent->GetType(eventType);
900 if (eventType.EqualsLiteral("dragover")) {
901 bool canDropLink = false;
902 handler->CanDropLink(dragEvent, false, &canDropLink);
903 if (canDropLink) {
904 aEvent->PreventDefault();
906 } else if (eventType.EqualsLiteral("drop")) {
907 nsCOMPtr<nsIWebNavigation> webnav =
908 static_cast<nsIWebNavigation*>(mWebBrowser);
910 // The page might have cancelled the dragover event itself, so check to
911 // make sure that the link can be dropped first.
912 bool canDropLink = false;
913 handler->CanDropLink(dragEvent, false, &canDropLink);
914 if (!canDropLink) {
915 return NS_OK;
918 nsTArray<RefPtr<nsIDroppedLinkItem>> links;
919 if (webnav && NS_SUCCEEDED(handler->DropLinks(dragEvent, true, links))) {
920 if (links.Length() >= 1) {
921 nsCOMPtr<nsIPrincipal> triggeringPrincipal;
922 handler->GetTriggeringPrincipal(dragEvent,
923 getter_AddRefs(triggeringPrincipal));
924 if (triggeringPrincipal) {
925 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome =
926 GetWebBrowserChrome();
927 if (webBrowserChrome) {
928 nsCOMPtr<nsIBrowserChild> browserChild =
929 do_QueryInterface(webBrowserChrome);
930 if (browserChild) {
931 nsresult rv = browserChild->RemoteDropLinks(links);
932 return rv;
935 nsAutoString url;
936 if (NS_SUCCEEDED(links[0]->GetUrl(url))) {
937 if (!url.IsEmpty()) {
938 #ifndef ANDROID
939 MOZ_ASSERT(triggeringPrincipal,
940 "nsDocShellTreeOwner::HandleEvent: Need a valid "
941 "triggeringPrincipal");
942 #endif
943 LoadURIOptions loadURIOptions;
944 loadURIOptions.mTriggeringPrincipal = triggeringPrincipal;
945 nsCOMPtr<nsIContentSecurityPolicy> csp;
946 handler->GetCsp(dragEvent, getter_AddRefs(csp));
947 loadURIOptions.mCsp = csp;
948 webnav->FixupAndLoadURIString(url, loadURIOptions);
953 } else {
954 aEvent->StopPropagation();
955 aEvent->PreventDefault();
959 return NS_OK;
962 already_AddRefed<nsIWebBrowserChrome>
963 nsDocShellTreeOwner::GetWebBrowserChrome() {
964 nsCOMPtr<nsIWebBrowserChrome> chrome;
965 if (mWebBrowserChromeWeak) {
966 chrome = do_QueryReferent(mWebBrowserChromeWeak);
967 } else if (mWebBrowserChrome) {
968 chrome = mWebBrowserChrome;
970 return chrome.forget();
973 already_AddRefed<nsIBaseWindow> nsDocShellTreeOwner::GetOwnerWin() {
974 nsCOMPtr<nsIBaseWindow> win;
975 if (mWebBrowserChromeWeak) {
976 win = do_QueryReferent(mWebBrowserChromeWeak);
977 } else if (mOwnerWin) {
978 win = mOwnerWin;
980 return win.forget();
983 already_AddRefed<nsIInterfaceRequestor>
984 nsDocShellTreeOwner::GetOwnerRequestor() {
985 nsCOMPtr<nsIInterfaceRequestor> req;
986 if (mWebBrowserChromeWeak) {
987 req = do_QueryReferent(mWebBrowserChromeWeak);
988 } else if (mOwnerRequestor) {
989 req = mOwnerRequestor;
991 return req.forget();
994 NS_IMPL_ISUPPORTS(ChromeTooltipListener, nsIDOMEventListener)
996 ChromeTooltipListener::ChromeTooltipListener(nsWebBrowser* aInBrowser,
997 nsIWebBrowserChrome* aInChrome)
998 : mWebBrowser(aInBrowser),
999 mWebBrowserChrome(aInChrome),
1000 mTooltipListenerInstalled(false),
1001 mShowingTooltip(false),
1002 mTooltipShownOnce(false) {}
1004 ChromeTooltipListener::~ChromeTooltipListener() {}
1006 nsITooltipTextProvider* ChromeTooltipListener::GetTooltipTextProvider() {
1007 if (!mTooltipTextProvider) {
1008 mTooltipTextProvider = do_GetService(NS_TOOLTIPTEXTPROVIDER_CONTRACTID);
1011 if (!mTooltipTextProvider) {
1012 mTooltipTextProvider =
1013 do_GetService(NS_DEFAULTTOOLTIPTEXTPROVIDER_CONTRACTID);
1016 return mTooltipTextProvider;
1019 // Hook up things to the chrome like context menus and tooltips, if the chrome
1020 // has implemented the right interfaces.
1021 NS_IMETHODIMP
1022 ChromeTooltipListener::AddChromeListeners() {
1023 if (!mEventTarget) {
1024 GetDOMEventTarget(mWebBrowser, getter_AddRefs(mEventTarget));
1027 // Register the appropriate events for tooltips, but only if
1028 // the embedding chrome cares.
1029 nsresult rv = NS_OK;
1030 nsCOMPtr<nsITooltipListener> tooltipListener(
1031 do_QueryInterface(mWebBrowserChrome));
1032 if (tooltipListener && !mTooltipListenerInstalled) {
1033 rv = AddTooltipListener();
1034 if (NS_FAILED(rv)) {
1035 return rv;
1039 return rv;
1042 // Subscribe to the events that will allow us to track tooltips. We need "mouse"
1043 // for mouseExit, "mouse motion" for mouseMove, and "key" for keyDown. As we
1044 // add the listeners, keep track of how many succeed so we can clean up
1045 // correctly in Release().
1046 NS_IMETHODIMP
1047 ChromeTooltipListener::AddTooltipListener() {
1048 if (mEventTarget) {
1049 MOZ_TRY(mEventTarget->AddSystemEventListener(u"keydown"_ns, this, false,
1050 false));
1051 MOZ_TRY(mEventTarget->AddSystemEventListener(u"mousedown"_ns, this, false,
1052 false));
1053 MOZ_TRY(mEventTarget->AddSystemEventListener(u"mouseout"_ns, this, false,
1054 false));
1055 MOZ_TRY(mEventTarget->AddSystemEventListener(u"mousemove"_ns, this, false,
1056 false));
1058 mTooltipListenerInstalled = true;
1061 return NS_OK;
1064 // Unsubscribe from the various things we've hooked up to the window root.
1065 NS_IMETHODIMP
1066 ChromeTooltipListener::RemoveChromeListeners() {
1067 HideTooltip();
1069 if (mTooltipListenerInstalled) {
1070 RemoveTooltipListener();
1073 mEventTarget = nullptr;
1075 // it really doesn't matter if these fail...
1076 return NS_OK;
1079 // Unsubscribe from all the various tooltip events that we were listening to.
1080 NS_IMETHODIMP
1081 ChromeTooltipListener::RemoveTooltipListener() {
1082 if (mEventTarget) {
1083 mEventTarget->RemoveSystemEventListener(u"keydown"_ns, this, false);
1084 mEventTarget->RemoveSystemEventListener(u"mousedown"_ns, this, false);
1085 mEventTarget->RemoveSystemEventListener(u"mouseout"_ns, this, false);
1086 mEventTarget->RemoveSystemEventListener(u"mousemove"_ns, this, false);
1087 mTooltipListenerInstalled = false;
1090 return NS_OK;
1093 NS_IMETHODIMP
1094 ChromeTooltipListener::HandleEvent(Event* aEvent) {
1095 nsAutoString eventType;
1096 aEvent->GetType(eventType);
1098 if (eventType.EqualsLiteral("mousedown")) {
1099 return HideTooltip();
1100 } else if (eventType.EqualsLiteral("keydown")) {
1101 WidgetKeyboardEvent* keyEvent = aEvent->WidgetEventPtr()->AsKeyboardEvent();
1102 if (nsXULTooltipListener::KeyEventHidesTooltip(*keyEvent)) {
1103 return HideTooltip();
1105 return NS_OK;
1106 } else if (eventType.EqualsLiteral("mouseout")) {
1107 // Reset flag so that tooltip will display on the next MouseMove
1108 mTooltipShownOnce = false;
1109 return HideTooltip();
1110 } else if (eventType.EqualsLiteral("mousemove")) {
1111 return MouseMove(aEvent);
1114 NS_ERROR("Unexpected event type");
1115 return NS_OK;
1118 // If we're a tooltip, fire off a timer to see if a tooltip should be shown. If
1119 // the timer fires, we cache the node in |mPossibleTooltipNode|.
1120 nsresult ChromeTooltipListener::MouseMove(Event* aMouseEvent) {
1121 if (!nsXULTooltipListener::ShowTooltips()) {
1122 return NS_OK;
1125 MouseEvent* mouseEvent = aMouseEvent->AsMouseEvent();
1126 if (!mouseEvent) {
1127 return NS_OK;
1130 // stash the coordinates of the event so that we can still get back to it from
1131 // within the timer callback. On win32, we'll get a MouseMove event even when
1132 // a popup goes away -- even when the mouse doesn't change position! To get
1133 // around this, we make sure the mouse has really moved before proceeding.
1134 CSSIntPoint newMouseClientPoint = mouseEvent->ClientPoint();
1135 if (mMouseClientPoint == newMouseClientPoint) {
1136 return NS_OK;
1139 // Filter out minor mouse movements.
1140 if (mShowingTooltip &&
1141 (abs(mMouseClientPoint.x - newMouseClientPoint.x) <=
1142 kTooltipMouseMoveTolerance) &&
1143 (abs(mMouseClientPoint.y - newMouseClientPoint.y) <=
1144 kTooltipMouseMoveTolerance)) {
1145 return NS_OK;
1148 mMouseClientPoint = newMouseClientPoint;
1149 mMouseScreenPoint = mouseEvent->ScreenPointLayoutDevicePix();
1151 if (mTooltipTimer) {
1152 mTooltipTimer->Cancel();
1153 mTooltipTimer = nullptr;
1156 if (!mShowingTooltip) {
1157 if (nsCOMPtr<EventTarget> eventTarget = aMouseEvent->GetOriginalTarget()) {
1158 mPossibleTooltipNode = nsINode::FromEventTarget(eventTarget);
1161 if (mPossibleTooltipNode) {
1162 nsresult rv = NS_NewTimerWithFuncCallback(
1163 getter_AddRefs(mTooltipTimer), sTooltipCallback, this,
1164 StaticPrefs::ui_tooltip_delay_ms(), nsITimer::TYPE_ONE_SHOT,
1165 "ChromeTooltipListener::MouseMove", GetMainThreadSerialEventTarget());
1166 if (NS_FAILED(rv)) {
1167 mPossibleTooltipNode = nullptr;
1168 NS_WARNING("Could not create a timer for tooltip tracking");
1171 } else {
1172 mTooltipShownOnce = true;
1173 return HideTooltip();
1176 return NS_OK;
1179 // Tell the registered chrome that they should show the tooltip.
1180 NS_IMETHODIMP
1181 ChromeTooltipListener::ShowTooltip(int32_t aInXCoords, int32_t aInYCoords,
1182 const nsAString& aInTipText,
1183 const nsAString& aTipDir) {
1184 nsresult rv = NS_OK;
1186 // do the work to call the client
1187 nsCOMPtr<nsITooltipListener> tooltipListener(
1188 do_QueryInterface(mWebBrowserChrome));
1189 if (tooltipListener) {
1190 rv = tooltipListener->OnShowTooltip(aInXCoords, aInYCoords, aInTipText,
1191 aTipDir);
1192 if (NS_SUCCEEDED(rv)) {
1193 mShowingTooltip = true;
1197 return rv;
1200 // Tell the registered chrome that they should rollup the tooltip
1201 // NOTE: This routine is safe to call even if the popup is already closed.
1202 NS_IMETHODIMP
1203 ChromeTooltipListener::HideTooltip() {
1204 nsresult rv = NS_OK;
1206 // shut down the relevant timers
1207 if (mTooltipTimer) {
1208 mTooltipTimer->Cancel();
1209 mTooltipTimer = nullptr;
1210 // release tooltip target
1211 mPossibleTooltipNode = nullptr;
1212 mLastDocshell = nullptr;
1215 // if we're showing the tip, tell the chrome to hide it
1216 if (mShowingTooltip) {
1217 nsCOMPtr<nsITooltipListener> tooltipListener(
1218 do_QueryInterface(mWebBrowserChrome));
1219 if (tooltipListener) {
1220 rv = tooltipListener->OnHideTooltip();
1221 if (NS_SUCCEEDED(rv)) {
1222 mShowingTooltip = false;
1227 return rv;
1230 bool ChromeTooltipListener::WebProgressShowedTooltip(
1231 nsIWebProgress* aWebProgress) {
1232 nsCOMPtr<nsIDocShell> docshell = do_QueryInterface(aWebProgress);
1233 nsCOMPtr<nsIDocShell> lastUsed = do_QueryReferent(mLastDocshell);
1234 while (lastUsed) {
1235 if (lastUsed == docshell) {
1236 return true;
1238 // We can't use the docshell hierarchy here, because when the parent
1239 // docshell is navigated, the child docshell is disconnected (ie its
1240 // references to the parent are nulled out) despite it still being
1241 // alive here. So we use the document hierarchy instead:
1242 Document* document = lastUsed->GetDocument();
1243 if (document) {
1244 document = document->GetInProcessParentDocument();
1246 if (!document) {
1247 break;
1249 lastUsed = document->GetDocShell();
1251 return false;
1254 // A timer callback, fired when the mouse has hovered inside of a frame for the
1255 // appropriate amount of time. Getting to this point means that we should show
1256 // the tooltip, but only after we determine there is an appropriate TITLE
1257 // element.
1259 // This relies on certain things being cached into the |aChromeTooltipListener|
1260 // object passed to us by the timer:
1261 // -- the x/y coordinates of the mouse (mMouseClientY, mMouseClientX)
1262 // -- the dom node the user hovered over (mPossibleTooltipNode)
1263 void ChromeTooltipListener::sTooltipCallback(nsITimer* aTimer,
1264 void* aChromeTooltipListener) {
1265 auto* self = static_cast<ChromeTooltipListener*>(aChromeTooltipListener);
1266 if (!self || !self->mPossibleTooltipNode) {
1267 return;
1269 // release tooltip target once done, no matter what we do here.
1270 auto cleanup = MakeScopeExit([&] { self->mPossibleTooltipNode = nullptr; });
1271 if (!self->mPossibleTooltipNode->IsInComposedDoc()) {
1272 return;
1274 // Check that the document or its ancestors haven't been replaced.
1276 Document* doc = self->mPossibleTooltipNode->OwnerDoc();
1277 while (doc) {
1278 if (!doc->IsCurrentActiveDocument()) {
1279 return;
1281 doc = doc->GetInProcessParentDocument();
1285 nsCOMPtr<nsIDocShell> docShell =
1286 do_GetInterface(static_cast<nsIWebBrowser*>(self->mWebBrowser));
1287 if (!docShell || !docShell->GetBrowsingContext()->IsActive()) {
1288 return;
1291 // if there is text associated with the node, show the tip and fire
1292 // off a timer to auto-hide it.
1293 nsITooltipTextProvider* tooltipProvider = self->GetTooltipTextProvider();
1294 if (!tooltipProvider) {
1295 return;
1297 nsString tooltipText;
1298 nsString directionText;
1299 bool textFound = false;
1300 tooltipProvider->GetNodeText(self->mPossibleTooltipNode,
1301 getter_Copies(tooltipText),
1302 getter_Copies(directionText), &textFound);
1304 if (textFound && (!self->mTooltipShownOnce ||
1305 tooltipText != self->mLastShownTooltipText)) {
1306 // ShowTooltip expects screen-relative position.
1307 self->ShowTooltip(self->mMouseScreenPoint.x, self->mMouseScreenPoint.y,
1308 tooltipText, directionText);
1309 self->mLastShownTooltipText = std::move(tooltipText);
1310 self->mLastDocshell = do_GetWeakReference(
1311 self->mPossibleTooltipNode->OwnerDoc()->GetDocShell());