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/. */
8 #include "nsDocShellTreeOwner.h"
9 #include "nsWebBrowser.h"
12 #include "nsContentUtils.h"
14 #include "mozilla/ReflowInput.h"
15 #include "mozilla/ScopeExit.h"
16 #include "nsComponentManagerUtils.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"
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"
42 #include "nsIContent.h"
43 #include "nsServiceManagerUtils.h"
44 #include "nsViewManager.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
) {
67 return NS_ERROR_INVALID_POINTER
;
70 nsCOMPtr
<mozIDOMWindowProxy
> domWindow
;
71 aInBrowser
->GetContentDOMWindow(getter_AddRefs(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
);
86 nsDocShellTreeOwner::nsDocShellTreeOwner()
87 : mWebBrowser(nullptr),
89 mPrimaryContentShell(nullptr),
90 mWebBrowserChrome(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
)
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
{
114 virtual ~ChromeTooltipListener();
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
);
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 //*****************************************************************************
191 nsDocShellTreeOwner::GetInterface(const nsIID
& aIID
, void** aSink
) {
192 NS_ENSURE_ARG_POINTER(aSink
);
194 if (NS_SUCCEEDED(QueryInterface(aIID
, aSink
))) {
198 if (aIID
.Equals(NS_GET_IID(nsIPrompt
))) {
199 nsCOMPtr
<nsIPrompt
> prompt
;
203 prompt
.forget(aSink
);
206 return NS_NOINTERFACE
;
209 if (aIID
.Equals(NS_GET_IID(nsIAuthPrompt
))) {
210 nsCOMPtr
<nsIAuthPrompt
> prompt
;
211 EnsureAuthPrompter();
212 prompt
= mAuthPrompter
;
214 prompt
.forget(aSink
);
217 return NS_NOINTERFACE
;
220 nsCOMPtr
<nsIInterfaceRequestor
> req
= GetOwnerRequestor();
222 return req
->GetInterface(aIID
, aSink
);
225 return NS_NOINTERFACE
;
228 //*****************************************************************************
229 // nsDocShellTreeOwner::nsIDocShellTreeOwner
230 //*****************************************************************************
232 void nsDocShellTreeOwner::EnsurePrompter() {
237 nsCOMPtr
<nsIWindowWatcher
> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID
));
238 if (wwatch
&& mWebBrowser
) {
239 nsCOMPtr
<mozIDOMWindowProxy
> domWindow
;
240 mWebBrowser
->GetContentDOMWindow(getter_AddRefs(domWindow
));
242 wwatch
->GetNewPrompter(domWindow
, getter_AddRefs(mPrompter
));
247 void nsDocShellTreeOwner::EnsureAuthPrompter() {
252 nsCOMPtr
<nsIWindowWatcher
> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID
));
253 if (wwatch
&& mWebBrowser
) {
254 nsCOMPtr
<mozIDOMWindowProxy
> domWindow
;
255 mWebBrowser
->GetContentDOMWindow(getter_AddRefs(domWindow
));
257 wwatch
->GetNewAuthPrompter(domWindow
, getter_AddRefs(mAuthPrompter
));
262 void nsDocShellTreeOwner::AddToWatcher() {
264 nsCOMPtr
<mozIDOMWindowProxy
> domWindow
;
265 mWebBrowser
->GetContentDOMWindow(getter_AddRefs(domWindow
));
267 nsCOMPtr
<nsPIWindowWatcher
> wwatch(
268 do_GetService(NS_WINDOWWATCHER_CONTRACTID
));
270 nsCOMPtr
<nsIWebBrowserChrome
> webBrowserChrome
= GetWebBrowserChrome();
271 if (webBrowserChrome
) {
272 wwatch
->AddWindow(domWindow
, webBrowserChrome
);
279 void nsDocShellTreeOwner::RemoveFromWatcher() {
281 nsCOMPtr
<mozIDOMWindowProxy
> domWindow
;
282 mWebBrowser
->GetContentDOMWindow(getter_AddRefs(domWindow
));
284 nsCOMPtr
<nsPIWindowWatcher
> wwatch(
285 do_GetService(NS_WINDOWWATCHER_CONTRACTID
));
287 wwatch
->RemoveWindow(domWindow
);
293 void nsDocShellTreeOwner::EnsureContentTreeOwner() {
294 if (mContentTreeOwner
) {
298 mContentTreeOwner
= new nsDocShellTreeOwner();
299 nsCOMPtr
<nsIWebBrowserChrome
> browserChrome
= GetWebBrowserChrome();
301 mContentTreeOwner
->SetWebBrowserChrome(browserChrome
);
305 mContentTreeOwner
->WebBrowser(mWebBrowser
);
310 nsDocShellTreeOwner::ContentShellAdded(nsIDocShellTreeItem
* aContentShell
,
312 if (mTreeOwner
) return mTreeOwner
->ContentShellAdded(aContentShell
, aPrimary
);
314 EnsureContentTreeOwner();
315 aContentShell
->SetTreeOwner(mContentTreeOwner
);
318 mPrimaryContentShell
= aContentShell
;
319 mPrimaryRemoteTab
= nullptr;
325 nsDocShellTreeOwner::ContentShellRemoved(nsIDocShellTreeItem
* aContentShell
) {
327 return mTreeOwner
->ContentShellRemoved(aContentShell
);
330 if (mPrimaryContentShell
== aContentShell
) {
331 mPrimaryContentShell
= nullptr;
338 nsDocShellTreeOwner::GetPrimaryContentShell(nsIDocShellTreeItem
** aShell
) {
339 NS_ENSURE_ARG_POINTER(aShell
);
342 return mTreeOwner
->GetPrimaryContentShell(aShell
);
345 nsCOMPtr
<nsIDocShellTreeItem
> shell
;
346 if (!mPrimaryRemoteTab
) {
348 mPrimaryContentShell
? mPrimaryContentShell
: mWebBrowser
->mDocShell
;
350 shell
.forget(aShell
);
356 nsDocShellTreeOwner::RemoteTabAdded(nsIRemoteTab
* aTab
, bool aPrimary
) {
358 return mTreeOwner
->RemoteTabAdded(aTab
, aPrimary
);
362 mPrimaryRemoteTab
= aTab
;
363 mPrimaryContentShell
= nullptr;
364 } else if (mPrimaryRemoteTab
== aTab
) {
365 mPrimaryRemoteTab
= nullptr;
372 nsDocShellTreeOwner::RemoteTabRemoved(nsIRemoteTab
* aTab
) {
374 return mTreeOwner
->RemoteTabRemoved(aTab
);
377 if (aTab
== mPrimaryRemoteTab
) {
378 mPrimaryRemoteTab
= nullptr;
385 nsDocShellTreeOwner::GetPrimaryRemoteTab(nsIRemoteTab
** aTab
) {
387 return mTreeOwner
->GetPrimaryRemoteTab(aTab
);
390 nsCOMPtr
<nsIRemoteTab
> tab
= mPrimaryRemoteTab
;
396 nsDocShellTreeOwner::GetPrimaryContentBrowsingContext(
397 mozilla::dom::BrowsingContext
** aBc
) {
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
);
415 nsDocShellTreeOwner::GetPrimaryContentSize(int32_t* aWidth
, int32_t* aHeight
) {
416 return NS_ERROR_NOT_IMPLEMENTED
;
420 nsDocShellTreeOwner::SetPrimaryContentSize(int32_t aWidth
, int32_t aHeight
) {
421 return NS_ERROR_NOT_IMPLEMENTED
;
425 nsDocShellTreeOwner::GetRootShellSize(int32_t* aWidth
, int32_t* aHeight
) {
426 return NS_ERROR_NOT_IMPLEMENTED
;
430 nsDocShellTreeOwner::SetRootShellSize(int32_t aWidth
, int32_t aHeight
) {
431 return NS_ERROR_NOT_IMPLEMENTED
;
435 nsDocShellTreeOwner::SizeShellTo(nsIDocShellTreeItem
* aShellItem
, int32_t aCX
,
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
);
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(¤tSize
.width
, ¤tSize
.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
468 return NS_ERROR_NOT_IMPLEMENTED
;
471 MOZ_ASSERT_UNREACHABLE("This is unimplemented, API should be cleaned up");
472 return NS_ERROR_NOT_IMPLEMENTED
;
476 nsDocShellTreeOwner::SetPersistence(bool aPersistPosition
, bool aPersistSize
,
477 bool aPersistSizeMode
) {
478 return NS_ERROR_NOT_IMPLEMENTED
;
482 nsDocShellTreeOwner::GetPersistence(bool* aPersistPosition
, bool* aPersistSize
,
483 bool* aPersistSizeMode
) {
484 return NS_ERROR_NOT_IMPLEMENTED
;
488 nsDocShellTreeOwner::GetTabCount(uint32_t* aResult
) {
490 return mTreeOwner
->GetTabCount(aResult
);
498 nsDocShellTreeOwner::GetHasPrimaryContent(bool* aResult
) {
499 *aResult
= mPrimaryRemoteTab
|| mPrimaryContentShell
;
503 //*****************************************************************************
504 // nsDocShellTreeOwner::nsIBaseWindow
505 //*****************************************************************************
508 nsDocShellTreeOwner::InitWindow(nsIWidget
* aParentWidget
, int32_t aX
,
509 int32_t aY
, int32_t aCX
, int32_t aCY
) {
510 return NS_ERROR_NULL_POINTER
;
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;
531 nsDocShellTreeOwner::GetDevicePixelsPerDesktopPixel(double* aScale
) {
533 return mWebBrowser
->GetDevicePixelsPerDesktopPixel(aScale
);
541 nsDocShellTreeOwner::SetPositionDesktopPix(int32_t aX
, int32_t aY
) {
543 nsresult rv
= mWebBrowser
->SetPositionDesktopPix(aX
, aY
);
544 NS_ENSURE_SUCCESS(rv
, rv
);
548 GetDevicePixelsPerDesktopPixel(&scale
);
549 return SetPosition(NSToIntRound(aX
* scale
), NSToIntRound(aY
* scale
));
553 nsDocShellTreeOwner::SetPosition(int32_t aX
, int32_t aY
) {
554 return SetDimensions(
555 {DimensionKind::Outer
, Some(aX
), Some(aY
), Nothing(), Nothing()});
559 nsDocShellTreeOwner::GetPosition(int32_t* aX
, int32_t* aY
) {
560 return GetDimensions(DimensionKind::Outer
, aX
, aY
, nullptr, nullptr);
564 nsDocShellTreeOwner::SetSize(int32_t aCX
, int32_t aCY
, bool aRepaint
) {
565 return SetDimensions(
566 {DimensionKind::Outer
, Nothing(), Nothing(), Some(aCX
), Some(aCY
)});
570 nsDocShellTreeOwner::GetSize(int32_t* aCX
, int32_t* aCY
) {
571 return GetDimensions(DimensionKind::Outer
, nullptr, nullptr, aCX
, aCY
);
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
)});
582 nsDocShellTreeOwner::GetPositionAndSize(int32_t* aX
, int32_t* aY
, int32_t* aCX
,
584 return GetDimensions(DimensionKind::Outer
, aX
, aY
, aCX
, aCY
);
588 nsDocShellTreeOwner::SetDimensions(DimensionRequest
&& aRequest
) {
589 nsCOMPtr
<nsIBaseWindow
> ownerWin
= GetOwnerWin();
591 return ownerWin
->SetDimensions(std::move(aRequest
));
594 nsCOMPtr
<nsIWebBrowserChrome
> webBrowserChrome
= GetWebBrowserChrome();
595 NS_ENSURE_STATE(webBrowserChrome
);
596 return webBrowserChrome
->SetDimensions(std::move(aRequest
));
600 nsDocShellTreeOwner::GetDimensions(DimensionKind aDimensionKind
, int32_t* aX
,
601 int32_t* aY
, int32_t* aCX
, int32_t* aCY
) {
602 nsCOMPtr
<nsIBaseWindow
> ownerWin
= GetOwnerWin();
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
);
613 nsDocShellTreeOwner::Repaint(bool aForce
) { return NS_ERROR_NULL_POINTER
; }
616 nsDocShellTreeOwner::GetParentWidget(nsIWidget
** aParentWidget
) {
617 return NS_ERROR_NULL_POINTER
;
621 nsDocShellTreeOwner::SetParentWidget(nsIWidget
* aParentWidget
) {
622 return NS_ERROR_NULL_POINTER
;
626 nsDocShellTreeOwner::GetNativeHandle(nsAString
& aNativeHandle
) {
627 // the nativeHandle should be accessed from nsIAppWindow
628 return NS_ERROR_NOT_IMPLEMENTED
;
632 nsDocShellTreeOwner::GetVisibility(bool* aVisibility
) {
633 nsCOMPtr
<nsIBaseWindow
> ownerWin
= GetOwnerWin();
635 return ownerWin
->GetVisibility(aVisibility
);
638 return NS_ERROR_NOT_IMPLEMENTED
;
642 nsDocShellTreeOwner::SetVisibility(bool aVisibility
) {
643 nsCOMPtr
<nsIBaseWindow
> ownerWin
= GetOwnerWin();
645 return ownerWin
->SetVisibility(aVisibility
);
647 return NS_ERROR_NULL_POINTER
;
651 nsDocShellTreeOwner::GetEnabled(bool* aEnabled
) {
652 NS_ENSURE_ARG_POINTER(aEnabled
);
654 return NS_ERROR_NOT_IMPLEMENTED
;
658 nsDocShellTreeOwner::SetEnabled(bool aEnabled
) {
659 return NS_ERROR_NOT_IMPLEMENTED
;
663 nsDocShellTreeOwner::GetMainWidget(nsIWidget
** aMainWidget
) {
664 return NS_ERROR_NULL_POINTER
;
668 nsDocShellTreeOwner::GetTitle(nsAString
& aTitle
) {
669 nsCOMPtr
<nsIBaseWindow
> ownerWin
= GetOwnerWin();
671 return ownerWin
->GetTitle(aTitle
);
673 return NS_ERROR_NULL_POINTER
;
677 nsDocShellTreeOwner::SetTitle(const nsAString
& aTitle
) {
678 nsCOMPtr
<nsIBaseWindow
> ownerWin
= GetOwnerWin();
680 return ownerWin
->SetTitle(aTitle
);
682 return NS_ERROR_NULL_POINTER
;
685 //*****************************************************************************
686 // nsDocShellTreeOwner::nsIWebProgressListener
687 //*****************************************************************************
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
699 return AddChromeListeners();
703 nsDocShellTreeOwner::OnStateChange(nsIWebProgress
* aProgress
,
704 nsIRequest
* aRequest
,
705 uint32_t aProgressStateFlags
,
711 nsDocShellTreeOwner::OnLocationChange(nsIWebProgress
* aWebProgress
,
712 nsIRequest
* aRequest
, nsIURI
* aURI
,
714 if (mChromeTooltipListener
&& aWebProgress
&&
715 !(aFlags
& nsIWebProgressListener::LOCATION_CHANGE_SAME_DOCUMENT
) &&
716 mChromeTooltipListener
->WebProgressShowedTooltip(aWebProgress
)) {
717 mChromeTooltipListener
->HideTooltip();
723 nsDocShellTreeOwner::OnStatusChange(nsIWebProgress
* aWebProgress
,
724 nsIRequest
* aRequest
, nsresult aStatus
,
725 const char16_t
* aMessage
) {
730 nsDocShellTreeOwner::OnSecurityChange(nsIWebProgress
* aWebProgress
,
731 nsIRequest
* aRequest
, uint32_t aState
) {
736 nsDocShellTreeOwner::OnContentBlockingEvent(nsIWebProgress
* aWebProgress
,
737 nsIRequest
* aRequest
,
742 //*****************************************************************************
743 // nsDocShellTreeOwner: Accessors
744 //*****************************************************************************
746 void nsDocShellTreeOwner::WebBrowser(nsWebBrowser
* aWebBrowser
) {
748 RemoveChromeListeners();
750 if (aWebBrowser
!= mWebBrowser
) {
752 mAuthPrompter
= nullptr;
755 mWebBrowser
= aWebBrowser
;
757 if (mContentTreeOwner
) {
758 mContentTreeOwner
->WebBrowser(aWebBrowser
);
760 mContentTreeOwner
= nullptr;
765 nsWebBrowser
* nsDocShellTreeOwner::WebBrowser() { return mWebBrowser
; }
768 nsDocShellTreeOwner::SetTreeOwner(nsIDocShellTreeOwner
* 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
;
776 mTreeOwner
= nullptr;
777 nsCOMPtr
<nsIWebBrowserChrome
> webBrowserChrome
= GetWebBrowserChrome();
778 if (!webBrowserChrome
) {
779 NS_ENSURE_SUCCESS(SetWebBrowserChrome(nullptr), NS_ERROR_FAILURE
);
787 nsDocShellTreeOwner::SetWebBrowserChrome(
788 nsIWebBrowserChrome
* aWebBrowserChrome
) {
789 if (!aWebBrowserChrome
) {
790 mWebBrowserChrome
= nullptr;
792 mOwnerRequestor
= nullptr;
793 mWebBrowserChromeWeak
= nullptr;
795 nsCOMPtr
<nsISupportsWeakReference
> supportsweak
=
796 do_QueryInterface(aWebBrowserChrome
);
798 supportsweak
->GetWeakReference(getter_AddRefs(mWebBrowserChromeWeak
));
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
);
818 // Hook up things to the chrome like context menus and tooltips, if the chrome
819 // has implemented the right interfaces.
821 nsDocShellTreeOwner::AddChromeListeners() {
824 nsCOMPtr
<nsIWebBrowserChrome
> webBrowserChrome
= GetWebBrowserChrome();
825 if (!webBrowserChrome
) {
826 return NS_ERROR_FAILURE
;
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)");
846 if (EventListenerManager
* elmP
= target
->GetOrCreateListenerManager()) {
847 elmP
->AddEventListenerByType(this, u
"dragover"_ns
,
848 TrustedEventsAtSystemGroupBubble());
849 elmP
->AddEventListenerByType(this, u
"drop"_ns
,
850 TrustedEventsAtSystemGroupBubble());
858 nsDocShellTreeOwner::RemoveChromeListeners() {
859 if (mChromeTooltipListener
) {
860 mChromeTooltipListener
->RemoveChromeListeners();
861 mChromeTooltipListener
= nullptr;
864 nsCOMPtr
<EventTarget
> piTarget
;
865 GetDOMEventTarget(mWebBrowser
, getter_AddRefs(piTarget
));
870 EventListenerManager
* elmP
= piTarget
->GetOrCreateListenerManager();
872 elmP
->RemoveEventListenerByType(this, u
"dragover"_ns
,
873 TrustedEventsAtSystemGroupBubble());
874 elmP
->RemoveEventListenerByType(this, u
"drop"_ns
,
875 TrustedEventsAtSystemGroupBubble());
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()) {
892 nsCOMPtr
<nsIDroppedLinkHandler
> handler
=
893 do_GetService("@mozilla.org/content/dropped-link-handler;1");
898 nsAutoString eventType
;
899 aEvent
->GetType(eventType
);
900 if (eventType
.EqualsLiteral("dragover")) {
901 bool canDropLink
= false;
902 handler
->CanDropLink(dragEvent
, false, &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
);
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
);
931 nsresult rv
= browserChild
->RemoteDropLinks(links
);
936 if (NS_SUCCEEDED(links
[0]->GetUrl(url
))) {
937 if (!url
.IsEmpty()) {
939 MOZ_ASSERT(triggeringPrincipal
,
940 "nsDocShellTreeOwner::HandleEvent: Need a valid "
941 "triggeringPrincipal");
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
);
954 aEvent
->StopPropagation();
955 aEvent
->PreventDefault();
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
) {
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
;
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.
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
)) {
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().
1047 ChromeTooltipListener::AddTooltipListener() {
1049 MOZ_TRY(mEventTarget
->AddSystemEventListener(u
"keydown"_ns
, this, false,
1051 MOZ_TRY(mEventTarget
->AddSystemEventListener(u
"mousedown"_ns
, this, false,
1053 MOZ_TRY(mEventTarget
->AddSystemEventListener(u
"mouseout"_ns
, this, false,
1055 MOZ_TRY(mEventTarget
->AddSystemEventListener(u
"mousemove"_ns
, this, false,
1058 mTooltipListenerInstalled
= true;
1064 // Unsubscribe from the various things we've hooked up to the window root.
1066 ChromeTooltipListener::RemoveChromeListeners() {
1069 if (mTooltipListenerInstalled
) {
1070 RemoveTooltipListener();
1073 mEventTarget
= nullptr;
1075 // it really doesn't matter if these fail...
1079 // Unsubscribe from all the various tooltip events that we were listening to.
1081 ChromeTooltipListener::RemoveTooltipListener() {
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;
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();
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");
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()) {
1125 MouseEvent
* mouseEvent
= aMouseEvent
->AsMouseEvent();
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
) {
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
)) {
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");
1172 mTooltipShownOnce
= true;
1173 return HideTooltip();
1179 // Tell the registered chrome that they should show the tooltip.
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
,
1192 if (NS_SUCCEEDED(rv
)) {
1193 mShowingTooltip
= true;
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.
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;
1230 bool ChromeTooltipListener::WebProgressShowedTooltip(
1231 nsIWebProgress
* aWebProgress
) {
1232 nsCOMPtr
<nsIDocShell
> docshell
= do_QueryInterface(aWebProgress
);
1233 nsCOMPtr
<nsIDocShell
> lastUsed
= do_QueryReferent(mLastDocshell
);
1235 if (lastUsed
== docshell
) {
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();
1244 document
= document
->GetInProcessParentDocument();
1249 lastUsed
= document
->GetDocShell();
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
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
) {
1269 // release tooltip target once done, no matter what we do here.
1270 auto cleanup
= MakeScopeExit([&] { self
->mPossibleTooltipNode
= nullptr; });
1271 if (!self
->mPossibleTooltipNode
->IsInComposedDoc()) {
1274 // Check that the document or its ancestors haven't been replaced.
1276 Document
* doc
= self
->mPossibleTooltipNode
->OwnerDoc();
1278 if (!doc
->IsCurrentActiveDocument()) {
1281 doc
= doc
->GetInProcessParentDocument();
1285 nsCOMPtr
<nsIDocShell
> docShell
=
1286 do_GetInterface(static_cast<nsIWebBrowser
*>(self
->mWebBrowser
));
1287 if (!docShell
|| !docShell
->GetBrowsingContext()->IsActive()) {
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
) {
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());