no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / docshell / base / nsDocShellTreeOwner.cpp
blob9f1ab23a6c47a597b6ab9e62e345456468cd9ddf
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/Try.h"
52 #include "mozilla/dom/DragEvent.h"
53 #include "mozilla/dom/Event.h" // for Event
54 #include "mozilla/dom/File.h" // for input type=file
55 #include "mozilla/dom/FileList.h" // for input type=file
56 #include "mozilla/dom/LoadURIOptionsBinding.h"
57 #include "mozilla/PresShell.h"
58 #include "mozilla/TextEvents.h"
60 using namespace mozilla;
61 using namespace mozilla::dom;
63 // A helper routine that navigates the tricky path from a |nsWebBrowser| to
64 // a |EventTarget| via the window root and chrome event handler.
65 static nsresult GetDOMEventTarget(nsWebBrowser* aInBrowser,
66 EventTarget** aTarget) {
67 if (!aInBrowser) {
68 return NS_ERROR_INVALID_POINTER;
71 nsCOMPtr<mozIDOMWindowProxy> domWindow;
72 aInBrowser->GetContentDOMWindow(getter_AddRefs(domWindow));
73 if (!domWindow) {
74 return NS_ERROR_FAILURE;
77 auto* outerWindow = nsPIDOMWindowOuter::From(domWindow);
78 nsPIDOMWindowOuter* rootWindow = outerWindow->GetPrivateRoot();
79 NS_ENSURE_TRUE(rootWindow, NS_ERROR_FAILURE);
80 nsCOMPtr<EventTarget> target = rootWindow->GetChromeEventHandler();
81 NS_ENSURE_TRUE(target, NS_ERROR_FAILURE);
82 target.forget(aTarget);
84 return NS_OK;
87 nsDocShellTreeOwner::nsDocShellTreeOwner()
88 : mWebBrowser(nullptr),
89 mTreeOwner(nullptr),
90 mPrimaryContentShell(nullptr),
91 mWebBrowserChrome(nullptr),
92 mOwnerWin(nullptr),
93 mOwnerRequestor(nullptr) {}
95 nsDocShellTreeOwner::~nsDocShellTreeOwner() { RemoveChromeListeners(); }
97 NS_IMPL_ADDREF(nsDocShellTreeOwner)
98 NS_IMPL_RELEASE(nsDocShellTreeOwner)
100 NS_INTERFACE_MAP_BEGIN(nsDocShellTreeOwner)
101 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDocShellTreeOwner)
102 NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeOwner)
103 NS_INTERFACE_MAP_ENTRY(nsIBaseWindow)
104 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
105 NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
106 NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
107 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
108 NS_INTERFACE_MAP_END
110 // The class that listens to the chrome events and tells the embedding chrome to
111 // show tooltips, as appropriate. Handles registering itself with the DOM with
112 // AddChromeListeners() and removing itself with RemoveChromeListeners().
113 class ChromeTooltipListener final : public nsIDOMEventListener {
114 protected:
115 virtual ~ChromeTooltipListener();
117 public:
118 NS_DECL_ISUPPORTS
120 ChromeTooltipListener(nsWebBrowser* aInBrowser,
121 nsIWebBrowserChrome* aInChrome);
123 NS_DECL_NSIDOMEVENTLISTENER
124 NS_IMETHOD MouseMove(mozilla::dom::Event* aMouseEvent);
126 // Add/remove the relevant listeners, based on what interfaces the embedding
127 // chrome implements.
128 NS_IMETHOD AddChromeListeners();
129 NS_IMETHOD RemoveChromeListeners();
131 NS_IMETHOD HideTooltip();
133 bool WebProgressShowedTooltip(nsIWebProgress* aWebProgress);
135 private:
136 // pixel tolerance for mousemove event
137 static constexpr CSSIntCoord kTooltipMouseMoveTolerance = 7;
139 NS_IMETHOD AddTooltipListener();
140 NS_IMETHOD RemoveTooltipListener();
142 NS_IMETHOD ShowTooltip(int32_t aInXCoords, int32_t aInYCoords,
143 const nsAString& aInTipText,
144 const nsAString& aDirText);
145 nsITooltipTextProvider* GetTooltipTextProvider();
147 nsWebBrowser* mWebBrowser;
148 nsCOMPtr<mozilla::dom::EventTarget> mEventTarget;
149 nsCOMPtr<nsITooltipTextProvider> mTooltipTextProvider;
151 // This must be a strong ref in order to make sure we can hide the tooltip if
152 // the window goes away while we're displaying one. If we don't hold a strong
153 // ref, the chrome might have been disposed of before we get a chance to tell
154 // it, and no one would ever tell us of that fact.
155 nsCOMPtr<nsIWebBrowserChrome> mWebBrowserChrome;
157 bool mTooltipListenerInstalled;
159 nsCOMPtr<nsITimer> mTooltipTimer;
160 static void sTooltipCallback(nsITimer* aTimer, void* aListener);
162 // Mouse coordinates for last mousemove event we saw
163 CSSIntPoint mMouseClientPoint;
165 // Mouse coordinates for tooltip event
166 LayoutDeviceIntPoint mMouseScreenPoint;
168 bool mShowingTooltip;
170 bool mTooltipShownOnce;
172 // The string of text that we last displayed.
173 nsString mLastShownTooltipText;
175 nsWeakPtr mLastDocshell;
177 // The node hovered over that fired the timer. This may turn into the node
178 // that triggered the tooltip, but only if the timer ever gets around to
179 // firing. This is a strong reference, because the tooltip content can be
180 // destroyed while we're waiting for the tooltip to pop up, and we need to
181 // detect that. It's set only when the tooltip timer is created and launched.
182 // The timer must either fire or be cancelled (or possibly released?), and we
183 // release this reference in each of those cases. So we don't leak.
184 nsCOMPtr<nsINode> mPossibleTooltipNode;
187 //*****************************************************************************
188 // nsDocShellTreeOwner::nsIInterfaceRequestor
189 //*****************************************************************************
191 NS_IMETHODIMP
192 nsDocShellTreeOwner::GetInterface(const nsIID& aIID, void** aSink) {
193 NS_ENSURE_ARG_POINTER(aSink);
195 if (NS_SUCCEEDED(QueryInterface(aIID, aSink))) {
196 return NS_OK;
199 if (aIID.Equals(NS_GET_IID(nsIWebBrowserChromeFocus))) {
200 if (mWebBrowserChromeWeak != nullptr) {
201 return mWebBrowserChromeWeak->QueryReferent(aIID, aSink);
203 return mOwnerWin->QueryInterface(aIID, aSink);
206 if (aIID.Equals(NS_GET_IID(nsIPrompt))) {
207 nsCOMPtr<nsIPrompt> prompt;
208 EnsurePrompter();
209 prompt = mPrompter;
210 if (prompt) {
211 prompt.forget(aSink);
212 return NS_OK;
214 return NS_NOINTERFACE;
217 if (aIID.Equals(NS_GET_IID(nsIAuthPrompt))) {
218 nsCOMPtr<nsIAuthPrompt> prompt;
219 EnsureAuthPrompter();
220 prompt = mAuthPrompter;
221 if (prompt) {
222 prompt.forget(aSink);
223 return NS_OK;
225 return NS_NOINTERFACE;
228 nsCOMPtr<nsIInterfaceRequestor> req = GetOwnerRequestor();
229 if (req) {
230 return req->GetInterface(aIID, aSink);
233 return NS_NOINTERFACE;
236 //*****************************************************************************
237 // nsDocShellTreeOwner::nsIDocShellTreeOwner
238 //*****************************************************************************
240 void nsDocShellTreeOwner::EnsurePrompter() {
241 if (mPrompter) {
242 return;
245 nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
246 if (wwatch && mWebBrowser) {
247 nsCOMPtr<mozIDOMWindowProxy> domWindow;
248 mWebBrowser->GetContentDOMWindow(getter_AddRefs(domWindow));
249 if (domWindow) {
250 wwatch->GetNewPrompter(domWindow, getter_AddRefs(mPrompter));
255 void nsDocShellTreeOwner::EnsureAuthPrompter() {
256 if (mAuthPrompter) {
257 return;
260 nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
261 if (wwatch && mWebBrowser) {
262 nsCOMPtr<mozIDOMWindowProxy> domWindow;
263 mWebBrowser->GetContentDOMWindow(getter_AddRefs(domWindow));
264 if (domWindow) {
265 wwatch->GetNewAuthPrompter(domWindow, getter_AddRefs(mAuthPrompter));
270 void nsDocShellTreeOwner::AddToWatcher() {
271 if (mWebBrowser) {
272 nsCOMPtr<mozIDOMWindowProxy> domWindow;
273 mWebBrowser->GetContentDOMWindow(getter_AddRefs(domWindow));
274 if (domWindow) {
275 nsCOMPtr<nsPIWindowWatcher> wwatch(
276 do_GetService(NS_WINDOWWATCHER_CONTRACTID));
277 if (wwatch) {
278 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome();
279 if (webBrowserChrome) {
280 wwatch->AddWindow(domWindow, webBrowserChrome);
287 void nsDocShellTreeOwner::RemoveFromWatcher() {
288 if (mWebBrowser) {
289 nsCOMPtr<mozIDOMWindowProxy> domWindow;
290 mWebBrowser->GetContentDOMWindow(getter_AddRefs(domWindow));
291 if (domWindow) {
292 nsCOMPtr<nsPIWindowWatcher> wwatch(
293 do_GetService(NS_WINDOWWATCHER_CONTRACTID));
294 if (wwatch) {
295 wwatch->RemoveWindow(domWindow);
301 void nsDocShellTreeOwner::EnsureContentTreeOwner() {
302 if (mContentTreeOwner) {
303 return;
306 mContentTreeOwner = new nsDocShellTreeOwner();
307 nsCOMPtr<nsIWebBrowserChrome> browserChrome = GetWebBrowserChrome();
308 if (browserChrome) {
309 mContentTreeOwner->SetWebBrowserChrome(browserChrome);
312 if (mWebBrowser) {
313 mContentTreeOwner->WebBrowser(mWebBrowser);
317 NS_IMETHODIMP
318 nsDocShellTreeOwner::ContentShellAdded(nsIDocShellTreeItem* aContentShell,
319 bool aPrimary) {
320 if (mTreeOwner) return mTreeOwner->ContentShellAdded(aContentShell, aPrimary);
322 EnsureContentTreeOwner();
323 aContentShell->SetTreeOwner(mContentTreeOwner);
325 if (aPrimary) {
326 mPrimaryContentShell = aContentShell;
327 mPrimaryRemoteTab = nullptr;
329 return NS_OK;
332 NS_IMETHODIMP
333 nsDocShellTreeOwner::ContentShellRemoved(nsIDocShellTreeItem* aContentShell) {
334 if (mTreeOwner) {
335 return mTreeOwner->ContentShellRemoved(aContentShell);
338 if (mPrimaryContentShell == aContentShell) {
339 mPrimaryContentShell = nullptr;
342 return NS_OK;
345 NS_IMETHODIMP
346 nsDocShellTreeOwner::GetPrimaryContentShell(nsIDocShellTreeItem** aShell) {
347 NS_ENSURE_ARG_POINTER(aShell);
349 if (mTreeOwner) {
350 return mTreeOwner->GetPrimaryContentShell(aShell);
353 nsCOMPtr<nsIDocShellTreeItem> shell;
354 if (!mPrimaryRemoteTab) {
355 shell =
356 mPrimaryContentShell ? mPrimaryContentShell : mWebBrowser->mDocShell;
358 shell.forget(aShell);
360 return NS_OK;
363 NS_IMETHODIMP
364 nsDocShellTreeOwner::RemoteTabAdded(nsIRemoteTab* aTab, bool aPrimary) {
365 if (mTreeOwner) {
366 return mTreeOwner->RemoteTabAdded(aTab, aPrimary);
369 if (aPrimary) {
370 mPrimaryRemoteTab = aTab;
371 mPrimaryContentShell = nullptr;
372 } else if (mPrimaryRemoteTab == aTab) {
373 mPrimaryRemoteTab = nullptr;
376 return NS_OK;
379 NS_IMETHODIMP
380 nsDocShellTreeOwner::RemoteTabRemoved(nsIRemoteTab* aTab) {
381 if (mTreeOwner) {
382 return mTreeOwner->RemoteTabRemoved(aTab);
385 if (aTab == mPrimaryRemoteTab) {
386 mPrimaryRemoteTab = nullptr;
389 return NS_OK;
392 NS_IMETHODIMP
393 nsDocShellTreeOwner::GetPrimaryRemoteTab(nsIRemoteTab** aTab) {
394 if (mTreeOwner) {
395 return mTreeOwner->GetPrimaryRemoteTab(aTab);
398 nsCOMPtr<nsIRemoteTab> tab = mPrimaryRemoteTab;
399 tab.forget(aTab);
400 return NS_OK;
403 NS_IMETHODIMP
404 nsDocShellTreeOwner::GetPrimaryContentBrowsingContext(
405 mozilla::dom::BrowsingContext** aBc) {
406 if (mTreeOwner) {
407 return mTreeOwner->GetPrimaryContentBrowsingContext(aBc);
409 if (mPrimaryRemoteTab) {
410 return mPrimaryRemoteTab->GetBrowsingContext(aBc);
412 if (mPrimaryContentShell) {
413 return mPrimaryContentShell->GetBrowsingContextXPCOM(aBc);
415 if (mWebBrowser->mDocShell) {
416 return mWebBrowser->mDocShell->GetBrowsingContextXPCOM(aBc);
418 *aBc = nullptr;
419 return NS_OK;
422 NS_IMETHODIMP
423 nsDocShellTreeOwner::GetPrimaryContentSize(int32_t* aWidth, int32_t* aHeight) {
424 return NS_ERROR_NOT_IMPLEMENTED;
427 NS_IMETHODIMP
428 nsDocShellTreeOwner::SetPrimaryContentSize(int32_t aWidth, int32_t aHeight) {
429 return NS_ERROR_NOT_IMPLEMENTED;
432 NS_IMETHODIMP
433 nsDocShellTreeOwner::GetRootShellSize(int32_t* aWidth, int32_t* aHeight) {
434 return NS_ERROR_NOT_IMPLEMENTED;
437 NS_IMETHODIMP
438 nsDocShellTreeOwner::SetRootShellSize(int32_t aWidth, int32_t aHeight) {
439 return NS_ERROR_NOT_IMPLEMENTED;
442 NS_IMETHODIMP
443 nsDocShellTreeOwner::SizeShellTo(nsIDocShellTreeItem* aShellItem, int32_t aCX,
444 int32_t aCY) {
445 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome();
447 NS_ENSURE_STATE(mTreeOwner || webBrowserChrome);
449 if (nsCOMPtr<nsIDocShellTreeOwner> treeOwner = mTreeOwner) {
450 return treeOwner->SizeShellTo(aShellItem, aCX, aCY);
453 if (aShellItem == mWebBrowser->mDocShell) {
454 nsCOMPtr<nsIBrowserChild> browserChild =
455 do_QueryInterface(webBrowserChrome);
456 if (browserChild) {
457 // The XUL window to resize is in the parent process, but there we
458 // won't be able to get the size of aShellItem. We can ask the parent
459 // process to change our size instead.
460 nsCOMPtr<nsIBaseWindow> shellAsWin(do_QueryInterface(aShellItem));
461 NS_ENSURE_TRUE(shellAsWin, NS_ERROR_FAILURE);
463 LayoutDeviceIntSize shellSize;
464 shellAsWin->GetSize(&shellSize.width, &shellSize.height);
465 LayoutDeviceIntSize deltaSize = LayoutDeviceIntSize(aCX, aCY) - shellSize;
467 LayoutDeviceIntSize currentSize;
468 GetSize(&currentSize.width, &currentSize.height);
470 LayoutDeviceIntSize newSize = currentSize + deltaSize;
471 return SetSize(newSize.width, newSize.height, true);
473 // XXX: this is weird, but we used to call a method here
474 // (webBrowserChrome->SizeBrowserTo()) whose implementations all failed
475 // like this, so...
476 return NS_ERROR_NOT_IMPLEMENTED;
479 MOZ_ASSERT_UNREACHABLE("This is unimplemented, API should be cleaned up");
480 return NS_ERROR_NOT_IMPLEMENTED;
483 NS_IMETHODIMP
484 nsDocShellTreeOwner::SetPersistence(bool aPersistPosition, bool aPersistSize,
485 bool aPersistSizeMode) {
486 return NS_ERROR_NOT_IMPLEMENTED;
489 NS_IMETHODIMP
490 nsDocShellTreeOwner::GetPersistence(bool* aPersistPosition, bool* aPersistSize,
491 bool* aPersistSizeMode) {
492 return NS_ERROR_NOT_IMPLEMENTED;
495 NS_IMETHODIMP
496 nsDocShellTreeOwner::GetTabCount(uint32_t* aResult) {
497 if (mTreeOwner) {
498 return mTreeOwner->GetTabCount(aResult);
501 *aResult = 0;
502 return NS_OK;
505 NS_IMETHODIMP
506 nsDocShellTreeOwner::GetHasPrimaryContent(bool* aResult) {
507 *aResult = mPrimaryRemoteTab || mPrimaryContentShell;
508 return NS_OK;
511 //*****************************************************************************
512 // nsDocShellTreeOwner::nsIBaseWindow
513 //*****************************************************************************
515 NS_IMETHODIMP
516 nsDocShellTreeOwner::InitWindow(nativeWindow aParentNativeWindow,
517 nsIWidget* aParentWidget, int32_t aX,
518 int32_t aY, int32_t aCX, int32_t aCY) {
519 return NS_ERROR_NULL_POINTER;
522 NS_IMETHODIMP
523 nsDocShellTreeOwner::Destroy() {
524 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome();
525 if (webBrowserChrome) {
526 // XXX: this is weird, but we used to call a method here
527 // (webBrowserChrome->DestroyBrowserWindow()) whose implementations all
528 // failed like this, so...
529 return NS_ERROR_NOT_IMPLEMENTED;
532 return NS_ERROR_NULL_POINTER;
535 double nsDocShellTreeOwner::GetWidgetCSSToDeviceScale() {
536 return mWebBrowser ? mWebBrowser->GetWidgetCSSToDeviceScale() : 1.0;
539 NS_IMETHODIMP
540 nsDocShellTreeOwner::GetDevicePixelsPerDesktopPixel(double* aScale) {
541 if (mWebBrowser) {
542 return mWebBrowser->GetDevicePixelsPerDesktopPixel(aScale);
545 *aScale = 1.0;
546 return NS_OK;
549 NS_IMETHODIMP
550 nsDocShellTreeOwner::SetPositionDesktopPix(int32_t aX, int32_t aY) {
551 if (mWebBrowser) {
552 nsresult rv = mWebBrowser->SetPositionDesktopPix(aX, aY);
553 NS_ENSURE_SUCCESS(rv, rv);
556 double scale = 1.0;
557 GetDevicePixelsPerDesktopPixel(&scale);
558 return SetPosition(NSToIntRound(aX * scale), NSToIntRound(aY * scale));
561 NS_IMETHODIMP
562 nsDocShellTreeOwner::SetPosition(int32_t aX, int32_t aY) {
563 return SetDimensions(
564 {DimensionKind::Outer, Some(aX), Some(aY), Nothing(), Nothing()});
567 NS_IMETHODIMP
568 nsDocShellTreeOwner::GetPosition(int32_t* aX, int32_t* aY) {
569 return GetDimensions(DimensionKind::Outer, aX, aY, nullptr, nullptr);
572 NS_IMETHODIMP
573 nsDocShellTreeOwner::SetSize(int32_t aCX, int32_t aCY, bool aRepaint) {
574 return SetDimensions(
575 {DimensionKind::Outer, Nothing(), Nothing(), Some(aCX), Some(aCY)});
578 NS_IMETHODIMP
579 nsDocShellTreeOwner::GetSize(int32_t* aCX, int32_t* aCY) {
580 return GetDimensions(DimensionKind::Outer, nullptr, nullptr, aCX, aCY);
583 NS_IMETHODIMP
584 nsDocShellTreeOwner::SetPositionAndSize(int32_t aX, int32_t aY, int32_t aCX,
585 int32_t aCY, uint32_t aFlags) {
586 return SetDimensions(
587 {DimensionKind::Outer, Some(aX), Some(aY), Some(aCX), Some(aCY)});
590 NS_IMETHODIMP
591 nsDocShellTreeOwner::GetPositionAndSize(int32_t* aX, int32_t* aY, int32_t* aCX,
592 int32_t* aCY) {
593 return GetDimensions(DimensionKind::Outer, aX, aY, aCX, aCY);
596 NS_IMETHODIMP
597 nsDocShellTreeOwner::SetDimensions(DimensionRequest&& aRequest) {
598 nsCOMPtr<nsIBaseWindow> ownerWin = GetOwnerWin();
599 if (ownerWin) {
600 return ownerWin->SetDimensions(std::move(aRequest));
603 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome();
604 NS_ENSURE_STATE(webBrowserChrome);
605 return webBrowserChrome->SetDimensions(std::move(aRequest));
608 NS_IMETHODIMP
609 nsDocShellTreeOwner::GetDimensions(DimensionKind aDimensionKind, int32_t* aX,
610 int32_t* aY, int32_t* aCX, int32_t* aCY) {
611 nsCOMPtr<nsIBaseWindow> ownerWin = GetOwnerWin();
612 if (ownerWin) {
613 return ownerWin->GetDimensions(aDimensionKind, aX, aY, aCX, aCY);
616 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome();
617 NS_ENSURE_STATE(webBrowserChrome);
618 return webBrowserChrome->GetDimensions(aDimensionKind, aX, aY, aCX, aCY);
621 NS_IMETHODIMP
622 nsDocShellTreeOwner::Repaint(bool aForce) { return NS_ERROR_NULL_POINTER; }
624 NS_IMETHODIMP
625 nsDocShellTreeOwner::GetParentWidget(nsIWidget** aParentWidget) {
626 return NS_ERROR_NULL_POINTER;
629 NS_IMETHODIMP
630 nsDocShellTreeOwner::SetParentWidget(nsIWidget* aParentWidget) {
631 return NS_ERROR_NULL_POINTER;
634 NS_IMETHODIMP
635 nsDocShellTreeOwner::GetParentNativeWindow(nativeWindow* aParentNativeWindow) {
636 nsCOMPtr<nsIBaseWindow> ownerWin = GetOwnerWin();
637 if (ownerWin) {
638 return ownerWin->GetParentNativeWindow(aParentNativeWindow);
640 return NS_ERROR_NULL_POINTER;
643 NS_IMETHODIMP
644 nsDocShellTreeOwner::SetParentNativeWindow(nativeWindow aParentNativeWindow) {
645 return NS_ERROR_NULL_POINTER;
648 NS_IMETHODIMP
649 nsDocShellTreeOwner::GetNativeHandle(nsAString& aNativeHandle) {
650 // the nativeHandle should be accessed from nsIAppWindow
651 return NS_ERROR_NOT_IMPLEMENTED;
654 NS_IMETHODIMP
655 nsDocShellTreeOwner::GetVisibility(bool* aVisibility) {
656 nsCOMPtr<nsIBaseWindow> ownerWin = GetOwnerWin();
657 if (ownerWin) {
658 return ownerWin->GetVisibility(aVisibility);
661 return NS_ERROR_NOT_IMPLEMENTED;
664 NS_IMETHODIMP
665 nsDocShellTreeOwner::SetVisibility(bool aVisibility) {
666 nsCOMPtr<nsIBaseWindow> ownerWin = GetOwnerWin();
667 if (ownerWin) {
668 return ownerWin->SetVisibility(aVisibility);
670 return NS_ERROR_NULL_POINTER;
673 NS_IMETHODIMP
674 nsDocShellTreeOwner::GetEnabled(bool* aEnabled) {
675 NS_ENSURE_ARG_POINTER(aEnabled);
676 *aEnabled = true;
677 return NS_ERROR_NOT_IMPLEMENTED;
680 NS_IMETHODIMP
681 nsDocShellTreeOwner::SetEnabled(bool aEnabled) {
682 return NS_ERROR_NOT_IMPLEMENTED;
685 NS_IMETHODIMP
686 nsDocShellTreeOwner::GetMainWidget(nsIWidget** aMainWidget) {
687 return NS_ERROR_NULL_POINTER;
690 NS_IMETHODIMP
691 nsDocShellTreeOwner::GetTitle(nsAString& aTitle) {
692 nsCOMPtr<nsIBaseWindow> ownerWin = GetOwnerWin();
693 if (ownerWin) {
694 return ownerWin->GetTitle(aTitle);
696 return NS_ERROR_NULL_POINTER;
699 NS_IMETHODIMP
700 nsDocShellTreeOwner::SetTitle(const nsAString& aTitle) {
701 nsCOMPtr<nsIBaseWindow> ownerWin = GetOwnerWin();
702 if (ownerWin) {
703 return ownerWin->SetTitle(aTitle);
705 return NS_ERROR_NULL_POINTER;
708 //*****************************************************************************
709 // nsDocShellTreeOwner::nsIWebProgressListener
710 //*****************************************************************************
712 NS_IMETHODIMP
713 nsDocShellTreeOwner::OnProgressChange(nsIWebProgress* aProgress,
714 nsIRequest* aRequest,
715 int32_t aCurSelfProgress,
716 int32_t aMaxSelfProgress,
717 int32_t aCurTotalProgress,
718 int32_t aMaxTotalProgress) {
719 // In the absence of DOM document creation event, this method is the
720 // most convenient place to install the mouse listener on the
721 // DOM document.
722 return AddChromeListeners();
725 NS_IMETHODIMP
726 nsDocShellTreeOwner::OnStateChange(nsIWebProgress* aProgress,
727 nsIRequest* aRequest,
728 uint32_t aProgressStateFlags,
729 nsresult aStatus) {
730 return NS_OK;
733 NS_IMETHODIMP
734 nsDocShellTreeOwner::OnLocationChange(nsIWebProgress* aWebProgress,
735 nsIRequest* aRequest, nsIURI* aURI,
736 uint32_t aFlags) {
737 if (mChromeTooltipListener && aWebProgress &&
738 !(aFlags & nsIWebProgressListener::LOCATION_CHANGE_SAME_DOCUMENT) &&
739 mChromeTooltipListener->WebProgressShowedTooltip(aWebProgress)) {
740 mChromeTooltipListener->HideTooltip();
742 return NS_OK;
745 NS_IMETHODIMP
746 nsDocShellTreeOwner::OnStatusChange(nsIWebProgress* aWebProgress,
747 nsIRequest* aRequest, nsresult aStatus,
748 const char16_t* aMessage) {
749 return NS_OK;
752 NS_IMETHODIMP
753 nsDocShellTreeOwner::OnSecurityChange(nsIWebProgress* aWebProgress,
754 nsIRequest* aRequest, uint32_t aState) {
755 return NS_OK;
758 NS_IMETHODIMP
759 nsDocShellTreeOwner::OnContentBlockingEvent(nsIWebProgress* aWebProgress,
760 nsIRequest* aRequest,
761 uint32_t aEvent) {
762 return NS_OK;
765 //*****************************************************************************
766 // nsDocShellTreeOwner: Accessors
767 //*****************************************************************************
769 void nsDocShellTreeOwner::WebBrowser(nsWebBrowser* aWebBrowser) {
770 if (!aWebBrowser) {
771 RemoveChromeListeners();
773 if (aWebBrowser != mWebBrowser) {
774 mPrompter = nullptr;
775 mAuthPrompter = nullptr;
778 mWebBrowser = aWebBrowser;
780 if (mContentTreeOwner) {
781 mContentTreeOwner->WebBrowser(aWebBrowser);
782 if (!aWebBrowser) {
783 mContentTreeOwner = nullptr;
788 nsWebBrowser* nsDocShellTreeOwner::WebBrowser() { return mWebBrowser; }
790 NS_IMETHODIMP
791 nsDocShellTreeOwner::SetTreeOwner(nsIDocShellTreeOwner* aTreeOwner) {
792 if (aTreeOwner) {
793 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome(do_GetInterface(aTreeOwner));
794 NS_ENSURE_TRUE(webBrowserChrome, NS_ERROR_INVALID_ARG);
795 NS_ENSURE_SUCCESS(SetWebBrowserChrome(webBrowserChrome),
796 NS_ERROR_INVALID_ARG);
797 mTreeOwner = aTreeOwner;
798 } else {
799 mTreeOwner = nullptr;
800 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome();
801 if (!webBrowserChrome) {
802 NS_ENSURE_SUCCESS(SetWebBrowserChrome(nullptr), NS_ERROR_FAILURE);
806 return NS_OK;
809 NS_IMETHODIMP
810 nsDocShellTreeOwner::SetWebBrowserChrome(
811 nsIWebBrowserChrome* aWebBrowserChrome) {
812 if (!aWebBrowserChrome) {
813 mWebBrowserChrome = nullptr;
814 mOwnerWin = nullptr;
815 mOwnerRequestor = nullptr;
816 mWebBrowserChromeWeak = nullptr;
817 } else {
818 nsCOMPtr<nsISupportsWeakReference> supportsweak =
819 do_QueryInterface(aWebBrowserChrome);
820 if (supportsweak) {
821 supportsweak->GetWeakReference(getter_AddRefs(mWebBrowserChromeWeak));
822 } else {
823 nsCOMPtr<nsIBaseWindow> ownerWin(do_QueryInterface(aWebBrowserChrome));
824 nsCOMPtr<nsIInterfaceRequestor> requestor(
825 do_QueryInterface(aWebBrowserChrome));
827 // it's ok for ownerWin or requestor to be null.
828 mWebBrowserChrome = aWebBrowserChrome;
829 mOwnerWin = ownerWin;
830 mOwnerRequestor = requestor;
834 if (mContentTreeOwner) {
835 mContentTreeOwner->SetWebBrowserChrome(aWebBrowserChrome);
838 return NS_OK;
841 // Hook up things to the chrome like context menus and tooltips, if the chrome
842 // has implemented the right interfaces.
843 NS_IMETHODIMP
844 nsDocShellTreeOwner::AddChromeListeners() {
845 nsresult rv = NS_OK;
847 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome();
848 if (!webBrowserChrome) {
849 return NS_ERROR_FAILURE;
852 // install tooltips
853 if (!mChromeTooltipListener) {
854 nsCOMPtr<nsITooltipListener> tooltipListener(
855 do_QueryInterface(webBrowserChrome));
856 if (tooltipListener) {
857 mChromeTooltipListener =
858 new ChromeTooltipListener(mWebBrowser, webBrowserChrome);
859 rv = mChromeTooltipListener->AddChromeListeners();
863 nsCOMPtr<EventTarget> target;
864 GetDOMEventTarget(mWebBrowser, getter_AddRefs(target));
866 // register dragover and drop event listeners with the listener manager
867 MOZ_ASSERT(target, "how does this happen? (see bug 1659758)");
868 if (target) {
869 if (EventListenerManager* elmP = target->GetOrCreateListenerManager()) {
870 elmP->AddEventListenerByType(this, u"dragover"_ns,
871 TrustedEventsAtSystemGroupBubble());
872 elmP->AddEventListenerByType(this, u"drop"_ns,
873 TrustedEventsAtSystemGroupBubble());
877 return rv;
880 NS_IMETHODIMP
881 nsDocShellTreeOwner::RemoveChromeListeners() {
882 if (mChromeTooltipListener) {
883 mChromeTooltipListener->RemoveChromeListeners();
884 mChromeTooltipListener = nullptr;
887 nsCOMPtr<EventTarget> piTarget;
888 GetDOMEventTarget(mWebBrowser, getter_AddRefs(piTarget));
889 if (!piTarget) {
890 return NS_OK;
893 EventListenerManager* elmP = piTarget->GetOrCreateListenerManager();
894 if (elmP) {
895 elmP->RemoveEventListenerByType(this, u"dragover"_ns,
896 TrustedEventsAtSystemGroupBubble());
897 elmP->RemoveEventListenerByType(this, u"drop"_ns,
898 TrustedEventsAtSystemGroupBubble());
901 return NS_OK;
904 NS_IMETHODIMP
905 nsDocShellTreeOwner::HandleEvent(Event* aEvent) {
906 DragEvent* dragEvent = aEvent ? aEvent->AsDragEvent() : nullptr;
907 if (NS_WARN_IF(!dragEvent)) {
908 return NS_ERROR_INVALID_ARG;
911 if (dragEvent->DefaultPrevented()) {
912 return NS_OK;
915 nsCOMPtr<nsIDroppedLinkHandler> handler =
916 do_GetService("@mozilla.org/content/dropped-link-handler;1");
917 if (!handler) {
918 return NS_OK;
921 nsAutoString eventType;
922 aEvent->GetType(eventType);
923 if (eventType.EqualsLiteral("dragover")) {
924 bool canDropLink = false;
925 handler->CanDropLink(dragEvent, false, &canDropLink);
926 if (canDropLink) {
927 aEvent->PreventDefault();
929 } else if (eventType.EqualsLiteral("drop")) {
930 nsCOMPtr<nsIWebNavigation> webnav =
931 static_cast<nsIWebNavigation*>(mWebBrowser);
933 // The page might have cancelled the dragover event itself, so check to
934 // make sure that the link can be dropped first.
935 bool canDropLink = false;
936 handler->CanDropLink(dragEvent, false, &canDropLink);
937 if (!canDropLink) {
938 return NS_OK;
941 nsTArray<RefPtr<nsIDroppedLinkItem>> links;
942 if (webnav && NS_SUCCEEDED(handler->DropLinks(dragEvent, true, links))) {
943 if (links.Length() >= 1) {
944 nsCOMPtr<nsIPrincipal> triggeringPrincipal;
945 handler->GetTriggeringPrincipal(dragEvent,
946 getter_AddRefs(triggeringPrincipal));
947 if (triggeringPrincipal) {
948 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome =
949 GetWebBrowserChrome();
950 if (webBrowserChrome) {
951 nsCOMPtr<nsIBrowserChild> browserChild =
952 do_QueryInterface(webBrowserChrome);
953 if (browserChild) {
954 nsresult rv = browserChild->RemoteDropLinks(links);
955 return rv;
958 nsAutoString url;
959 if (NS_SUCCEEDED(links[0]->GetUrl(url))) {
960 if (!url.IsEmpty()) {
961 #ifndef ANDROID
962 MOZ_ASSERT(triggeringPrincipal,
963 "nsDocShellTreeOwner::HandleEvent: Need a valid "
964 "triggeringPrincipal");
965 #endif
966 LoadURIOptions loadURIOptions;
967 loadURIOptions.mTriggeringPrincipal = triggeringPrincipal;
968 nsCOMPtr<nsIContentSecurityPolicy> csp;
969 handler->GetCsp(dragEvent, getter_AddRefs(csp));
970 loadURIOptions.mCsp = csp;
971 webnav->FixupAndLoadURIString(url, loadURIOptions);
976 } else {
977 aEvent->StopPropagation();
978 aEvent->PreventDefault();
982 return NS_OK;
985 already_AddRefed<nsIWebBrowserChrome>
986 nsDocShellTreeOwner::GetWebBrowserChrome() {
987 nsCOMPtr<nsIWebBrowserChrome> chrome;
988 if (mWebBrowserChromeWeak) {
989 chrome = do_QueryReferent(mWebBrowserChromeWeak);
990 } else if (mWebBrowserChrome) {
991 chrome = mWebBrowserChrome;
993 return chrome.forget();
996 already_AddRefed<nsIBaseWindow> nsDocShellTreeOwner::GetOwnerWin() {
997 nsCOMPtr<nsIBaseWindow> win;
998 if (mWebBrowserChromeWeak) {
999 win = do_QueryReferent(mWebBrowserChromeWeak);
1000 } else if (mOwnerWin) {
1001 win = mOwnerWin;
1003 return win.forget();
1006 already_AddRefed<nsIInterfaceRequestor>
1007 nsDocShellTreeOwner::GetOwnerRequestor() {
1008 nsCOMPtr<nsIInterfaceRequestor> req;
1009 if (mWebBrowserChromeWeak) {
1010 req = do_QueryReferent(mWebBrowserChromeWeak);
1011 } else if (mOwnerRequestor) {
1012 req = mOwnerRequestor;
1014 return req.forget();
1017 NS_IMPL_ISUPPORTS(ChromeTooltipListener, nsIDOMEventListener)
1019 ChromeTooltipListener::ChromeTooltipListener(nsWebBrowser* aInBrowser,
1020 nsIWebBrowserChrome* aInChrome)
1021 : mWebBrowser(aInBrowser),
1022 mWebBrowserChrome(aInChrome),
1023 mTooltipListenerInstalled(false),
1024 mShowingTooltip(false),
1025 mTooltipShownOnce(false) {}
1027 ChromeTooltipListener::~ChromeTooltipListener() {}
1029 nsITooltipTextProvider* ChromeTooltipListener::GetTooltipTextProvider() {
1030 if (!mTooltipTextProvider) {
1031 mTooltipTextProvider = do_GetService(NS_TOOLTIPTEXTPROVIDER_CONTRACTID);
1034 if (!mTooltipTextProvider) {
1035 mTooltipTextProvider =
1036 do_GetService(NS_DEFAULTTOOLTIPTEXTPROVIDER_CONTRACTID);
1039 return mTooltipTextProvider;
1042 // Hook up things to the chrome like context menus and tooltips, if the chrome
1043 // has implemented the right interfaces.
1044 NS_IMETHODIMP
1045 ChromeTooltipListener::AddChromeListeners() {
1046 if (!mEventTarget) {
1047 GetDOMEventTarget(mWebBrowser, getter_AddRefs(mEventTarget));
1050 // Register the appropriate events for tooltips, but only if
1051 // the embedding chrome cares.
1052 nsresult rv = NS_OK;
1053 nsCOMPtr<nsITooltipListener> tooltipListener(
1054 do_QueryInterface(mWebBrowserChrome));
1055 if (tooltipListener && !mTooltipListenerInstalled) {
1056 rv = AddTooltipListener();
1057 if (NS_FAILED(rv)) {
1058 return rv;
1062 return rv;
1065 // Subscribe to the events that will allow us to track tooltips. We need "mouse"
1066 // for mouseExit, "mouse motion" for mouseMove, and "key" for keyDown. As we
1067 // add the listeners, keep track of how many succeed so we can clean up
1068 // correctly in Release().
1069 NS_IMETHODIMP
1070 ChromeTooltipListener::AddTooltipListener() {
1071 if (mEventTarget) {
1072 MOZ_TRY(mEventTarget->AddSystemEventListener(u"keydown"_ns, this, false,
1073 false));
1074 MOZ_TRY(mEventTarget->AddSystemEventListener(u"mousedown"_ns, this, false,
1075 false));
1076 MOZ_TRY(mEventTarget->AddSystemEventListener(u"mouseout"_ns, this, false,
1077 false));
1078 MOZ_TRY(mEventTarget->AddSystemEventListener(u"mousemove"_ns, this, false,
1079 false));
1081 mTooltipListenerInstalled = true;
1084 return NS_OK;
1087 // Unsubscribe from the various things we've hooked up to the window root.
1088 NS_IMETHODIMP
1089 ChromeTooltipListener::RemoveChromeListeners() {
1090 HideTooltip();
1092 if (mTooltipListenerInstalled) {
1093 RemoveTooltipListener();
1096 mEventTarget = nullptr;
1098 // it really doesn't matter if these fail...
1099 return NS_OK;
1102 // Unsubscribe from all the various tooltip events that we were listening to.
1103 NS_IMETHODIMP
1104 ChromeTooltipListener::RemoveTooltipListener() {
1105 if (mEventTarget) {
1106 mEventTarget->RemoveSystemEventListener(u"keydown"_ns, this, false);
1107 mEventTarget->RemoveSystemEventListener(u"mousedown"_ns, this, false);
1108 mEventTarget->RemoveSystemEventListener(u"mouseout"_ns, this, false);
1109 mEventTarget->RemoveSystemEventListener(u"mousemove"_ns, this, false);
1110 mTooltipListenerInstalled = false;
1113 return NS_OK;
1116 NS_IMETHODIMP
1117 ChromeTooltipListener::HandleEvent(Event* aEvent) {
1118 nsAutoString eventType;
1119 aEvent->GetType(eventType);
1121 if (eventType.EqualsLiteral("mousedown")) {
1122 return HideTooltip();
1123 } else if (eventType.EqualsLiteral("keydown")) {
1124 WidgetKeyboardEvent* keyEvent = aEvent->WidgetEventPtr()->AsKeyboardEvent();
1125 if (nsXULTooltipListener::KeyEventHidesTooltip(*keyEvent)) {
1126 return HideTooltip();
1128 return NS_OK;
1129 } else if (eventType.EqualsLiteral("mouseout")) {
1130 // Reset flag so that tooltip will display on the next MouseMove
1131 mTooltipShownOnce = false;
1132 return HideTooltip();
1133 } else if (eventType.EqualsLiteral("mousemove")) {
1134 return MouseMove(aEvent);
1137 NS_ERROR("Unexpected event type");
1138 return NS_OK;
1141 // If we're a tooltip, fire off a timer to see if a tooltip should be shown. If
1142 // the timer fires, we cache the node in |mPossibleTooltipNode|.
1143 nsresult ChromeTooltipListener::MouseMove(Event* aMouseEvent) {
1144 if (!nsXULTooltipListener::ShowTooltips()) {
1145 return NS_OK;
1148 MouseEvent* mouseEvent = aMouseEvent->AsMouseEvent();
1149 if (!mouseEvent) {
1150 return NS_OK;
1153 // stash the coordinates of the event so that we can still get back to it from
1154 // within the timer callback. On win32, we'll get a MouseMove event even when
1155 // a popup goes away -- even when the mouse doesn't change position! To get
1156 // around this, we make sure the mouse has really moved before proceeding.
1157 CSSIntPoint newMouseClientPoint = mouseEvent->ClientPoint();
1158 if (mMouseClientPoint == newMouseClientPoint) {
1159 return NS_OK;
1162 // Filter out minor mouse movements.
1163 if (mShowingTooltip &&
1164 (abs(mMouseClientPoint.x - newMouseClientPoint.x) <=
1165 kTooltipMouseMoveTolerance) &&
1166 (abs(mMouseClientPoint.y - newMouseClientPoint.y) <=
1167 kTooltipMouseMoveTolerance)) {
1168 return NS_OK;
1171 mMouseClientPoint = newMouseClientPoint;
1172 mMouseScreenPoint = mouseEvent->ScreenPointLayoutDevicePix();
1174 if (mTooltipTimer) {
1175 mTooltipTimer->Cancel();
1176 mTooltipTimer = nullptr;
1179 if (!mShowingTooltip) {
1180 if (nsCOMPtr<EventTarget> eventTarget = aMouseEvent->GetComposedTarget()) {
1181 mPossibleTooltipNode = nsINode::FromEventTarget(eventTarget);
1184 if (mPossibleTooltipNode) {
1185 nsresult rv = NS_NewTimerWithFuncCallback(
1186 getter_AddRefs(mTooltipTimer), sTooltipCallback, this,
1187 LookAndFeel::GetInt(LookAndFeel::IntID::TooltipDelay, 500),
1188 nsITimer::TYPE_ONE_SHOT, "ChromeTooltipListener::MouseMove",
1189 GetMainThreadSerialEventTarget());
1190 if (NS_FAILED(rv)) {
1191 mPossibleTooltipNode = nullptr;
1192 NS_WARNING("Could not create a timer for tooltip tracking");
1195 } else {
1196 mTooltipShownOnce = true;
1197 return HideTooltip();
1200 return NS_OK;
1203 // Tell the registered chrome that they should show the tooltip.
1204 NS_IMETHODIMP
1205 ChromeTooltipListener::ShowTooltip(int32_t aInXCoords, int32_t aInYCoords,
1206 const nsAString& aInTipText,
1207 const nsAString& aTipDir) {
1208 nsresult rv = NS_OK;
1210 // do the work to call the client
1211 nsCOMPtr<nsITooltipListener> tooltipListener(
1212 do_QueryInterface(mWebBrowserChrome));
1213 if (tooltipListener) {
1214 rv = tooltipListener->OnShowTooltip(aInXCoords, aInYCoords, aInTipText,
1215 aTipDir);
1216 if (NS_SUCCEEDED(rv)) {
1217 mShowingTooltip = true;
1221 return rv;
1224 // Tell the registered chrome that they should rollup the tooltip
1225 // NOTE: This routine is safe to call even if the popup is already closed.
1226 NS_IMETHODIMP
1227 ChromeTooltipListener::HideTooltip() {
1228 nsresult rv = NS_OK;
1230 // shut down the relevant timers
1231 if (mTooltipTimer) {
1232 mTooltipTimer->Cancel();
1233 mTooltipTimer = nullptr;
1234 // release tooltip target
1235 mPossibleTooltipNode = nullptr;
1236 mLastDocshell = nullptr;
1239 // if we're showing the tip, tell the chrome to hide it
1240 if (mShowingTooltip) {
1241 nsCOMPtr<nsITooltipListener> tooltipListener(
1242 do_QueryInterface(mWebBrowserChrome));
1243 if (tooltipListener) {
1244 rv = tooltipListener->OnHideTooltip();
1245 if (NS_SUCCEEDED(rv)) {
1246 mShowingTooltip = false;
1251 return rv;
1254 bool ChromeTooltipListener::WebProgressShowedTooltip(
1255 nsIWebProgress* aWebProgress) {
1256 nsCOMPtr<nsIDocShell> docshell = do_QueryInterface(aWebProgress);
1257 nsCOMPtr<nsIDocShell> lastUsed = do_QueryReferent(mLastDocshell);
1258 while (lastUsed) {
1259 if (lastUsed == docshell) {
1260 return true;
1262 // We can't use the docshell hierarchy here, because when the parent
1263 // docshell is navigated, the child docshell is disconnected (ie its
1264 // references to the parent are nulled out) despite it still being
1265 // alive here. So we use the document hierarchy instead:
1266 Document* document = lastUsed->GetDocument();
1267 if (document) {
1268 document = document->GetInProcessParentDocument();
1270 if (!document) {
1271 break;
1273 lastUsed = document->GetDocShell();
1275 return false;
1278 // A timer callback, fired when the mouse has hovered inside of a frame for the
1279 // appropriate amount of time. Getting to this point means that we should show
1280 // the tooltip, but only after we determine there is an appropriate TITLE
1281 // element.
1283 // This relies on certain things being cached into the |aChromeTooltipListener|
1284 // object passed to us by the timer:
1285 // -- the x/y coordinates of the mouse (mMouseClientY, mMouseClientX)
1286 // -- the dom node the user hovered over (mPossibleTooltipNode)
1287 void ChromeTooltipListener::sTooltipCallback(nsITimer* aTimer,
1288 void* aChromeTooltipListener) {
1289 auto* self = static_cast<ChromeTooltipListener*>(aChromeTooltipListener);
1290 if (!self || !self->mPossibleTooltipNode) {
1291 return;
1293 // release tooltip target once done, no matter what we do here.
1294 auto cleanup = MakeScopeExit([&] { self->mPossibleTooltipNode = nullptr; });
1295 if (!self->mPossibleTooltipNode->IsInComposedDoc()) {
1296 return;
1298 // Check that the document or its ancestors haven't been replaced.
1300 Document* doc = self->mPossibleTooltipNode->OwnerDoc();
1301 while (doc) {
1302 if (!doc->IsCurrentActiveDocument()) {
1303 return;
1305 doc = doc->GetInProcessParentDocument();
1309 nsCOMPtr<nsIDocShell> docShell =
1310 do_GetInterface(static_cast<nsIWebBrowser*>(self->mWebBrowser));
1311 if (!docShell || !docShell->GetBrowsingContext()->IsActive()) {
1312 return;
1315 // if there is text associated with the node, show the tip and fire
1316 // off a timer to auto-hide it.
1317 nsITooltipTextProvider* tooltipProvider = self->GetTooltipTextProvider();
1318 if (!tooltipProvider) {
1319 return;
1321 nsString tooltipText;
1322 nsString directionText;
1323 bool textFound = false;
1324 tooltipProvider->GetNodeText(self->mPossibleTooltipNode,
1325 getter_Copies(tooltipText),
1326 getter_Copies(directionText), &textFound);
1328 if (textFound && (!self->mTooltipShownOnce ||
1329 tooltipText != self->mLastShownTooltipText)) {
1330 // ShowTooltip expects screen-relative position.
1331 self->ShowTooltip(self->mMouseScreenPoint.x, self->mMouseScreenPoint.y,
1332 tooltipText, directionText);
1333 self->mLastShownTooltipText = std::move(tooltipText);
1334 self->mLastDocshell = do_GetWeakReference(
1335 self->mPossibleTooltipNode->OwnerDoc()->GetDocShell());