Bug 1833854 - Part 2: Common up GCSchedulingTunables invariant checks r=sfink
[gecko.git] / docshell / base / nsDocShellTreeOwner.cpp
blob42c8a073005a09934cdb2f3041c934a3e5488a9f
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();
928 } else if (eventType.EqualsLiteral("drop")) {
929 nsIWebNavigation* webnav = static_cast<nsIWebNavigation*>(mWebBrowser);
931 // The page might have cancelled the dragover event itself, so check to
932 // make sure that the link can be dropped first.
933 bool canDropLink = false;
934 handler->CanDropLink(dragEvent, false, &canDropLink);
935 if (!canDropLink) {
936 return NS_OK;
939 nsTArray<RefPtr<nsIDroppedLinkItem>> links;
940 if (webnav && NS_SUCCEEDED(handler->DropLinks(dragEvent, true, links))) {
941 if (links.Length() >= 1) {
942 nsCOMPtr<nsIPrincipal> triggeringPrincipal;
943 handler->GetTriggeringPrincipal(dragEvent,
944 getter_AddRefs(triggeringPrincipal));
945 if (triggeringPrincipal) {
946 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome =
947 GetWebBrowserChrome();
948 if (webBrowserChrome) {
949 nsCOMPtr<nsIBrowserChild> browserChild =
950 do_QueryInterface(webBrowserChrome);
951 if (browserChild) {
952 nsresult rv = browserChild->RemoteDropLinks(links);
953 return rv;
956 nsAutoString url;
957 if (NS_SUCCEEDED(links[0]->GetUrl(url))) {
958 if (!url.IsEmpty()) {
959 #ifndef ANDROID
960 MOZ_ASSERT(triggeringPrincipal,
961 "nsDocShellTreeOwner::HandleEvent: Need a valid "
962 "triggeringPrincipal");
963 #endif
964 LoadURIOptions loadURIOptions;
965 loadURIOptions.mTriggeringPrincipal = triggeringPrincipal;
966 nsCOMPtr<nsIContentSecurityPolicy> csp;
967 handler->GetCsp(dragEvent, getter_AddRefs(csp));
968 loadURIOptions.mCsp = csp;
969 webnav->FixupAndLoadURIString(url, loadURIOptions);
974 } else {
975 aEvent->StopPropagation();
976 aEvent->PreventDefault();
980 return NS_OK;
983 already_AddRefed<nsIWebBrowserChrome>
984 nsDocShellTreeOwner::GetWebBrowserChrome() {
985 nsCOMPtr<nsIWebBrowserChrome> chrome;
986 if (mWebBrowserChromeWeak) {
987 chrome = do_QueryReferent(mWebBrowserChromeWeak);
988 } else if (mWebBrowserChrome) {
989 chrome = mWebBrowserChrome;
991 return chrome.forget();
994 already_AddRefed<nsIBaseWindow> nsDocShellTreeOwner::GetOwnerWin() {
995 nsCOMPtr<nsIBaseWindow> win;
996 if (mWebBrowserChromeWeak) {
997 win = do_QueryReferent(mWebBrowserChromeWeak);
998 } else if (mOwnerWin) {
999 win = mOwnerWin;
1001 return win.forget();
1004 already_AddRefed<nsIInterfaceRequestor>
1005 nsDocShellTreeOwner::GetOwnerRequestor() {
1006 nsCOMPtr<nsIInterfaceRequestor> req;
1007 if (mWebBrowserChromeWeak) {
1008 req = do_QueryReferent(mWebBrowserChromeWeak);
1009 } else if (mOwnerRequestor) {
1010 req = mOwnerRequestor;
1012 return req.forget();
1015 NS_IMPL_ISUPPORTS(ChromeTooltipListener, nsIDOMEventListener)
1017 ChromeTooltipListener::ChromeTooltipListener(nsWebBrowser* aInBrowser,
1018 nsIWebBrowserChrome* aInChrome)
1019 : mWebBrowser(aInBrowser),
1020 mWebBrowserChrome(aInChrome),
1021 mTooltipListenerInstalled(false),
1022 mShowingTooltip(false),
1023 mTooltipShownOnce(false) {}
1025 ChromeTooltipListener::~ChromeTooltipListener() {}
1027 nsITooltipTextProvider* ChromeTooltipListener::GetTooltipTextProvider() {
1028 if (!mTooltipTextProvider) {
1029 mTooltipTextProvider = do_GetService(NS_TOOLTIPTEXTPROVIDER_CONTRACTID);
1032 if (!mTooltipTextProvider) {
1033 mTooltipTextProvider =
1034 do_GetService(NS_DEFAULTTOOLTIPTEXTPROVIDER_CONTRACTID);
1037 return mTooltipTextProvider;
1040 // Hook up things to the chrome like context menus and tooltips, if the chrome
1041 // has implemented the right interfaces.
1042 NS_IMETHODIMP
1043 ChromeTooltipListener::AddChromeListeners() {
1044 if (!mEventTarget) {
1045 GetDOMEventTarget(mWebBrowser, getter_AddRefs(mEventTarget));
1048 // Register the appropriate events for tooltips, but only if
1049 // the embedding chrome cares.
1050 nsresult rv = NS_OK;
1051 nsCOMPtr<nsITooltipListener> tooltipListener(
1052 do_QueryInterface(mWebBrowserChrome));
1053 if (tooltipListener && !mTooltipListenerInstalled) {
1054 rv = AddTooltipListener();
1055 if (NS_FAILED(rv)) {
1056 return rv;
1060 return rv;
1063 // Subscribe to the events that will allow us to track tooltips. We need "mouse"
1064 // for mouseExit, "mouse motion" for mouseMove, and "key" for keyDown. As we
1065 // add the listeners, keep track of how many succeed so we can clean up
1066 // correctly in Release().
1067 NS_IMETHODIMP
1068 ChromeTooltipListener::AddTooltipListener() {
1069 if (mEventTarget) {
1070 MOZ_TRY(mEventTarget->AddSystemEventListener(u"keydown"_ns, this, false,
1071 false));
1072 MOZ_TRY(mEventTarget->AddSystemEventListener(u"mousedown"_ns, this, false,
1073 false));
1074 MOZ_TRY(mEventTarget->AddSystemEventListener(u"mouseout"_ns, this, false,
1075 false));
1076 MOZ_TRY(mEventTarget->AddSystemEventListener(u"mousemove"_ns, this, false,
1077 false));
1079 mTooltipListenerInstalled = true;
1082 return NS_OK;
1085 // Unsubscribe from the various things we've hooked up to the window root.
1086 NS_IMETHODIMP
1087 ChromeTooltipListener::RemoveChromeListeners() {
1088 HideTooltip();
1090 if (mTooltipListenerInstalled) {
1091 RemoveTooltipListener();
1094 mEventTarget = nullptr;
1096 // it really doesn't matter if these fail...
1097 return NS_OK;
1100 // Unsubscribe from all the various tooltip events that we were listening to.
1101 NS_IMETHODIMP
1102 ChromeTooltipListener::RemoveTooltipListener() {
1103 if (mEventTarget) {
1104 mEventTarget->RemoveSystemEventListener(u"keydown"_ns, this, false);
1105 mEventTarget->RemoveSystemEventListener(u"mousedown"_ns, this, false);
1106 mEventTarget->RemoveSystemEventListener(u"mouseout"_ns, this, false);
1107 mEventTarget->RemoveSystemEventListener(u"mousemove"_ns, this, false);
1108 mTooltipListenerInstalled = false;
1111 return NS_OK;
1114 NS_IMETHODIMP
1115 ChromeTooltipListener::HandleEvent(Event* aEvent) {
1116 nsAutoString eventType;
1117 aEvent->GetType(eventType);
1119 if (eventType.EqualsLiteral("mousedown")) {
1120 return HideTooltip();
1121 } else if (eventType.EqualsLiteral("keydown")) {
1122 WidgetKeyboardEvent* keyEvent = aEvent->WidgetEventPtr()->AsKeyboardEvent();
1123 if (nsXULTooltipListener::KeyEventHidesTooltip(*keyEvent)) {
1124 return HideTooltip();
1126 return NS_OK;
1127 } else if (eventType.EqualsLiteral("mouseout")) {
1128 // Reset flag so that tooltip will display on the next MouseMove
1129 mTooltipShownOnce = false;
1130 return HideTooltip();
1131 } else if (eventType.EqualsLiteral("mousemove")) {
1132 return MouseMove(aEvent);
1135 NS_ERROR("Unexpected event type");
1136 return NS_OK;
1139 // If we're a tooltip, fire off a timer to see if a tooltip should be shown. If
1140 // the timer fires, we cache the node in |mPossibleTooltipNode|.
1141 nsresult ChromeTooltipListener::MouseMove(Event* aMouseEvent) {
1142 if (!nsXULTooltipListener::ShowTooltips()) {
1143 return NS_OK;
1146 MouseEvent* mouseEvent = aMouseEvent->AsMouseEvent();
1147 if (!mouseEvent) {
1148 return NS_OK;
1151 // stash the coordinates of the event so that we can still get back to it from
1152 // within the timer callback. On win32, we'll get a MouseMove event even when
1153 // a popup goes away -- even when the mouse doesn't change position! To get
1154 // around this, we make sure the mouse has really moved before proceeding.
1155 CSSIntPoint newMouseClientPoint = mouseEvent->ClientPoint();
1156 if (mMouseClientPoint == newMouseClientPoint) {
1157 return NS_OK;
1160 // Filter out minor mouse movements.
1161 if (mShowingTooltip &&
1162 (abs(mMouseClientPoint.x - newMouseClientPoint.x) <=
1163 kTooltipMouseMoveTolerance) &&
1164 (abs(mMouseClientPoint.y - newMouseClientPoint.y) <=
1165 kTooltipMouseMoveTolerance)) {
1166 return NS_OK;
1169 mMouseClientPoint = newMouseClientPoint;
1170 mMouseScreenPoint = mouseEvent->ScreenPointLayoutDevicePix();
1172 if (mTooltipTimer) {
1173 mTooltipTimer->Cancel();
1174 mTooltipTimer = nullptr;
1177 if (!mShowingTooltip) {
1178 nsIEventTarget* target = nullptr;
1179 if (nsCOMPtr<EventTarget> eventTarget = aMouseEvent->GetComposedTarget()) {
1180 mPossibleTooltipNode = nsINode::FromEventTarget(eventTarget);
1181 nsCOMPtr<nsIGlobalObject> global(eventTarget->GetOwnerGlobal());
1182 if (global) {
1183 target = global->EventTargetFor(TaskCategory::UI);
1187 if (mPossibleTooltipNode) {
1188 nsresult rv = NS_NewTimerWithFuncCallback(
1189 getter_AddRefs(mTooltipTimer), sTooltipCallback, this,
1190 LookAndFeel::GetInt(LookAndFeel::IntID::TooltipDelay, 500),
1191 nsITimer::TYPE_ONE_SHOT, "ChromeTooltipListener::MouseMove", target);
1192 if (NS_FAILED(rv)) {
1193 mPossibleTooltipNode = nullptr;
1194 NS_WARNING("Could not create a timer for tooltip tracking");
1197 } else {
1198 mTooltipShownOnce = true;
1199 return HideTooltip();
1202 return NS_OK;
1205 // Tell the registered chrome that they should show the tooltip.
1206 NS_IMETHODIMP
1207 ChromeTooltipListener::ShowTooltip(int32_t aInXCoords, int32_t aInYCoords,
1208 const nsAString& aInTipText,
1209 const nsAString& aTipDir) {
1210 nsresult rv = NS_OK;
1212 // do the work to call the client
1213 nsCOMPtr<nsITooltipListener> tooltipListener(
1214 do_QueryInterface(mWebBrowserChrome));
1215 if (tooltipListener) {
1216 rv = tooltipListener->OnShowTooltip(aInXCoords, aInYCoords, aInTipText,
1217 aTipDir);
1218 if (NS_SUCCEEDED(rv)) {
1219 mShowingTooltip = true;
1223 return rv;
1226 // Tell the registered chrome that they should rollup the tooltip
1227 // NOTE: This routine is safe to call even if the popup is already closed.
1228 NS_IMETHODIMP
1229 ChromeTooltipListener::HideTooltip() {
1230 nsresult rv = NS_OK;
1232 // shut down the relevant timers
1233 if (mTooltipTimer) {
1234 mTooltipTimer->Cancel();
1235 mTooltipTimer = nullptr;
1236 // release tooltip target
1237 mPossibleTooltipNode = nullptr;
1238 mLastDocshell = nullptr;
1241 // if we're showing the tip, tell the chrome to hide it
1242 if (mShowingTooltip) {
1243 nsCOMPtr<nsITooltipListener> tooltipListener(
1244 do_QueryInterface(mWebBrowserChrome));
1245 if (tooltipListener) {
1246 rv = tooltipListener->OnHideTooltip();
1247 if (NS_SUCCEEDED(rv)) {
1248 mShowingTooltip = false;
1253 return rv;
1256 bool ChromeTooltipListener::WebProgressShowedTooltip(
1257 nsIWebProgress* aWebProgress) {
1258 nsCOMPtr<nsIDocShell> docshell = do_QueryInterface(aWebProgress);
1259 nsCOMPtr<nsIDocShell> lastUsed = do_QueryReferent(mLastDocshell);
1260 while (lastUsed) {
1261 if (lastUsed == docshell) {
1262 return true;
1264 // We can't use the docshell hierarchy here, because when the parent
1265 // docshell is navigated, the child docshell is disconnected (ie its
1266 // references to the parent are nulled out) despite it still being
1267 // alive here. So we use the document hierarchy instead:
1268 Document* document = lastUsed->GetDocument();
1269 if (document) {
1270 document = document->GetInProcessParentDocument();
1272 if (!document) {
1273 break;
1275 lastUsed = document->GetDocShell();
1277 return false;
1280 // A timer callback, fired when the mouse has hovered inside of a frame for the
1281 // appropriate amount of time. Getting to this point means that we should show
1282 // the tooltip, but only after we determine there is an appropriate TITLE
1283 // element.
1285 // This relies on certain things being cached into the |aChromeTooltipListener|
1286 // object passed to us by the timer:
1287 // -- the x/y coordinates of the mouse (mMouseClientY, mMouseClientX)
1288 // -- the dom node the user hovered over (mPossibleTooltipNode)
1289 void ChromeTooltipListener::sTooltipCallback(nsITimer* aTimer,
1290 void* aChromeTooltipListener) {
1291 auto* self = static_cast<ChromeTooltipListener*>(aChromeTooltipListener);
1292 if (!self || !self->mPossibleTooltipNode) {
1293 return;
1295 // release tooltip target once done, no matter what we do here.
1296 auto cleanup = MakeScopeExit([&] { self->mPossibleTooltipNode = nullptr; });
1297 if (!self->mPossibleTooltipNode->IsInComposedDoc()) {
1298 return;
1300 // Check that the document or its ancestors haven't been replaced.
1302 Document* doc = self->mPossibleTooltipNode->OwnerDoc();
1303 while (doc) {
1304 if (!doc->IsCurrentActiveDocument()) {
1305 return;
1307 doc = doc->GetInProcessParentDocument();
1311 nsCOMPtr<nsIDocShell> docShell =
1312 do_GetInterface(static_cast<nsIWebBrowser*>(self->mWebBrowser));
1313 if (!docShell || !docShell->GetBrowsingContext()->IsActive()) {
1314 return;
1317 // if there is text associated with the node, show the tip and fire
1318 // off a timer to auto-hide it.
1319 nsITooltipTextProvider* tooltipProvider = self->GetTooltipTextProvider();
1320 if (!tooltipProvider) {
1321 return;
1323 nsString tooltipText;
1324 nsString directionText;
1325 bool textFound = false;
1326 tooltipProvider->GetNodeText(self->mPossibleTooltipNode,
1327 getter_Copies(tooltipText),
1328 getter_Copies(directionText), &textFound);
1330 if (textFound && (!self->mTooltipShownOnce ||
1331 tooltipText != self->mLastShownTooltipText)) {
1332 // ShowTooltip expects screen-relative position.
1333 self->ShowTooltip(self->mMouseScreenPoint.x, self->mMouseScreenPoint.y,
1334 tooltipText, directionText);
1335 self->mLastShownTooltipText = std::move(tooltipText);
1336 self->mLastDocshell = do_GetWeakReference(
1337 self->mPossibleTooltipNode->OwnerDoc()->GetDocShell());