/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// Local Includes #include"nsDocShellTreeOwner.h" #include"nsWebBrowser.h"
// A helper routine that navigates the tricky path from a |nsWebBrowser| to // a |EventTarget| via the window root and chrome event handler. static nsresult GetDOMEventTarget(nsWebBrowser* aInBrowser,
EventTarget** aTarget) { if (!aInBrowser) { return NS_ERROR_INVALID_POINTER;
}
nsCOMPtr<mozIDOMWindowProxy> domWindow;
aInBrowser->GetContentDOMWindow(getter_AddRefs(domWindow)); if (!domWindow) { return NS_ERROR_FAILURE;
}
// The class that listens to the chrome events and tells the embedding chrome to // show tooltips, as appropriate. Handles registering itself with the DOM with // AddChromeListeners() and removing itself with RemoveChromeListeners(). class ChromeTooltipListener final : public nsIDOMEventListener { protected: virtual ~ChromeTooltipListener();
// Add/remove the relevant listeners, based on what interfaces the embedding // chrome implements.
NS_IMETHOD AddChromeListeners();
NS_IMETHOD RemoveChromeListeners();
// This must be a strong ref in order to make sure we can hide the tooltip if // the window goes away while we're displaying one. If we don't hold a strong // ref, the chrome might have been disposed of before we get a chance to tell // it, and no one would ever tell us of that fact.
nsCOMPtr<nsIWebBrowserChrome> mWebBrowserChrome;
// Mouse coordinates for last mousemove event we saw
CSSIntPoint mMouseClientPoint;
// Mouse coordinates for tooltip event
LayoutDeviceIntPoint mMouseScreenPoint;
bool mShowingTooltip;
bool mTooltipShownOnce;
// The string of text that we last displayed.
nsString mLastShownTooltipText;
nsWeakPtr mLastDocshell;
// The node hovered over that fired the timer. This may turn into the node // that triggered the tooltip, but only if the timer ever gets around to // firing. This is a strong reference, because the tooltip content can be // destroyed while we're waiting for the tooltip to pop up, and we need to // detect that. It's set only when the tooltip timer is created and launched. // The timer must either fire or be cancelled (or possibly released?), and we // release this reference in each of those cases. So we don't leak.
nsCOMPtr<nsINode> mPossibleTooltipNode;
};
if (aShellItem == mWebBrowser->mDocShell) {
nsCOMPtr<nsIBrowserChild> browserChild =
do_QueryInterface(webBrowserChrome); if (browserChild) { // The XUL window to resize is in the parent process, but there we // won't be able to get the size of aShellItem. We can ask the parent // process to change our size instead.
nsCOMPtr<nsIBaseWindow> shellAsWin(do_QueryInterface(aShellItem));
NS_ENSURE_TRUE(shellAsWin, NS_ERROR_FAILURE);
LayoutDeviceIntSize newSize = currentSize + deltaSize; return SetSize(newSize.width, newSize.height, true);
} // XXX: this is weird, but we used to call a method here // (webBrowserChrome->SizeBrowserTo()) whose implementations all failed // like this, so... return NS_ERROR_NOT_IMPLEMENTED;
}
MOZ_ASSERT_UNREACHABLE("This is unimplemented, API should be cleaned up"); return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsDocShellTreeOwner::Destroy() {
nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome(); if (webBrowserChrome) { // XXX: this is weird, but we used to call a method here // (webBrowserChrome->DestroyBrowserWindow()) whose implementations all // failed like this, so... return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsDocShellTreeOwner::GetNativeHandle(nsAString& aNativeHandle) { // the nativeHandle should be accessed from nsIAppWindow return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsDocShellTreeOwner::OnProgressChange(nsIWebProgress* aProgress,
nsIRequest* aRequest,
int32_t aCurSelfProgress,
int32_t aMaxSelfProgress,
int32_t aCurTotalProgress,
int32_t aMaxTotalProgress) { // In the absence of DOM document creation event, this method is the // most convenient place to install the mouse listener on the // DOM document. return AddChromeListeners();
}
// it's ok for ownerWin or requestor to be null.
mWebBrowserChrome = aWebBrowserChrome;
mOwnerWin = ownerWin;
mOwnerRequestor = requestor;
}
}
if (mContentTreeOwner) {
mContentTreeOwner->SetWebBrowserChrome(aWebBrowserChrome);
}
return NS_OK;
}
// Hook up things to the chrome like context menus and tooltips, if the chrome // has implemented the right interfaces.
NS_IMETHODIMP
nsDocShellTreeOwner::AddChromeListeners() {
nsresult rv = NS_OK;
nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome(); if (!webBrowserChrome) { return NS_ERROR_FAILURE;
}
// install tooltips if (!mChromeTooltipListener) {
nsCOMPtr<nsITooltipListener> tooltipListener(
do_QueryInterface(webBrowserChrome)); if (tooltipListener) {
mChromeTooltipListener = new ChromeTooltipListener(mWebBrowser, webBrowserChrome);
rv = mChromeTooltipListener->AddChromeListeners();
}
}
// register dragover and drop event listeners with the listener manager
MOZ_ASSERT(target, "how does this happen? (see bug 1659758)"); if (target) { if (EventListenerManager* elmP = target->GetOrCreateListenerManager()) {
elmP->AddEventListenerByType(this, u"dragover"_ns,
TrustedEventsAtSystemGroupBubble());
elmP->AddEventListenerByType(this, u"drop"_ns,
TrustedEventsAtSystemGroupBubble());
}
}
// The page might have cancelled the dragover event itself, so check to // make sure that the link can be dropped first. bool canDropLink = false;
handler->CanDropLink(dragEvent, false, &canDropLink); if (!canDropLink) { return NS_OK;
}
nsTArray<RefPtr<nsIDroppedLinkItem>> links; if (webnav && NS_SUCCEEDED(handler->DropLinks(dragEvent, true, links))) { if (links.Length() >= 1) {
nsCOMPtr<nsIPrincipal> triggeringPrincipal;
handler->GetTriggeringPrincipal(dragEvent,
getter_AddRefs(triggeringPrincipal)); if (triggeringPrincipal) {
nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome =
GetWebBrowserChrome(); if (webBrowserChrome) {
nsCOMPtr<nsIBrowserChild> browserChild =
do_QueryInterface(webBrowserChrome); if (browserChild) {
nsresult rv = browserChild->RemoteDropLinks(links); return rv;
}
}
nsAutoString url; if (NS_SUCCEEDED(links[0]->GetUrl(url))) { if (!url.IsEmpty()) { #ifndef ANDROID
MOZ_ASSERT(triggeringPrincipal, "nsDocShellTreeOwner::HandleEvent: Need a valid " "triggeringPrincipal"); #endif
LoadURIOptions loadURIOptions;
loadURIOptions.mTriggeringPrincipal = triggeringPrincipal;
nsCOMPtr<nsIContentSecurityPolicy> csp;
handler->GetCsp(dragEvent, getter_AddRefs(csp));
loadURIOptions.mCsp = csp;
webnav->FixupAndLoadURIString(url, loadURIOptions);
}
}
}
}
} else {
aEvent->StopPropagation();
aEvent->PreventDefault();
}
}
nsITooltipTextProvider* ChromeTooltipListener::GetTooltipTextProvider() { if (!mTooltipTextProvider) {
mTooltipTextProvider = do_GetService(NS_TOOLTIPTEXTPROVIDER_CONTRACTID);
}
if (!mTooltipTextProvider) {
mTooltipTextProvider =
do_GetService(NS_DEFAULTTOOLTIPTEXTPROVIDER_CONTRACTID);
}
return mTooltipTextProvider;
}
// Hook up things to the chrome like context menus and tooltips, if the chrome // has implemented the right interfaces.
NS_IMETHODIMP
ChromeTooltipListener::AddChromeListeners() { if (!mEventTarget) {
GetDOMEventTarget(mWebBrowser, getter_AddRefs(mEventTarget));
}
// Register the appropriate events for tooltips, but only if // the embedding chrome cares.
nsresult rv = NS_OK;
nsCOMPtr<nsITooltipListener> tooltipListener(
do_QueryInterface(mWebBrowserChrome)); if (tooltipListener && !mTooltipListenerInstalled) {
rv = AddTooltipListener(); if (NS_FAILED(rv)) { return rv;
}
}
return rv;
}
// Subscribe to the events that will allow us to track tooltips. We need "mouse" // for mouseExit, "mouse motion" for mouseMove, and "key" for keyDown. As we // add the listeners, keep track of how many succeed so we can clean up // correctly in Release().
NS_IMETHODIMP
ChromeTooltipListener::AddTooltipListener() { if (mEventTarget) {
MOZ_TRY(mEventTarget->AddSystemEventListener(u"keydown"_ns, this, false, false));
MOZ_TRY(mEventTarget->AddSystemEventListener(u"mousedown"_ns, this, false, false));
MOZ_TRY(mEventTarget->AddSystemEventListener(u"mouseout"_ns, this, false, false));
MOZ_TRY(mEventTarget->AddSystemEventListener(u"mousemove"_ns, this, false, false));
mTooltipListenerInstalled = true;
}
return NS_OK;
}
// Unsubscribe from the various things we've hooked up to the window root.
NS_IMETHODIMP
ChromeTooltipListener::RemoveChromeListeners() {
HideTooltip();
if (mTooltipListenerInstalled) {
RemoveTooltipListener();
}
mEventTarget = nullptr;
// it really doesn't matter if these fail... return NS_OK;
}
// Unsubscribe from all the various tooltip events that we were listening to.
NS_IMETHODIMP
ChromeTooltipListener::RemoveTooltipListener() { if (mEventTarget) {
mEventTarget->RemoveSystemEventListener(u"keydown"_ns, this, false);
mEventTarget->RemoveSystemEventListener(u"mousedown"_ns, this, false);
mEventTarget->RemoveSystemEventListener(u"mouseout"_ns, this, false);
mEventTarget->RemoveSystemEventListener(u"mousemove"_ns, this, false);
mTooltipListenerInstalled = false;
}
// If we're a tooltip, fire off a timer to see if a tooltip should be shown. If // the timer fires, we cache the node in |mPossibleTooltipNode|.
nsresult ChromeTooltipListener::MouseMove(Event* aMouseEvent) { if (!nsXULTooltipListener::ShowTooltips()) { return NS_OK;
}
MouseEvent* mouseEvent = aMouseEvent->AsMouseEvent(); if (!mouseEvent) { return NS_OK;
}
// stash the coordinates of the event so that we can still get back to it from // within the timer callback. On win32, we'll get a MouseMove event even when // a popup goes away -- even when the mouse doesn't change position! To get // around this, we make sure the mouse has really moved before proceeding. const CSSIntPoint newMouseClientPoint =
RoundedToInt(mouseEvent->ClientPoint()); if (mMouseClientPoint == newMouseClientPoint) { return NS_OK;
}
// Filter out minor mouse movements. if (mShowingTooltip &&
(abs(mMouseClientPoint.x - newMouseClientPoint.x) <=
kTooltipMouseMoveTolerance) &&
(abs(mMouseClientPoint.y - newMouseClientPoint.y) <=
kTooltipMouseMoveTolerance)) { return NS_OK;
}
if (mTooltipTimer) {
mTooltipTimer->Cancel();
mTooltipTimer = nullptr;
}
if (!mShowingTooltip) { if (nsCOMPtr<EventTarget> eventTarget = aMouseEvent->GetOriginalTarget()) {
mPossibleTooltipNode = nsINode::FromEventTarget(eventTarget);
}
if (mPossibleTooltipNode) {
nsresult rv = NS_NewTimerWithFuncCallback(
getter_AddRefs(mTooltipTimer), sTooltipCallback, this,
StaticPrefs::ui_tooltip_delay_ms(), nsITimer::TYPE_ONE_SHOT, "ChromeTooltipListener::MouseMove", GetMainThreadSerialEventTarget()); if (NS_FAILED(rv)) {
mPossibleTooltipNode = nullptr;
NS_WARNING("Could not create a timer for tooltip tracking");
}
}
} else {
mTooltipShownOnce = true; return HideTooltip();
}
return NS_OK;
}
// Tell the registered chrome that they should show the tooltip.
NS_IMETHODIMP
ChromeTooltipListener::ShowTooltip(int32_t aInXCoords, int32_t aInYCoords, const nsAString& aInTipText, const nsAString& aTipDir) {
nsresult rv = NS_OK;
// do the work to call the client
nsCOMPtr<nsITooltipListener> tooltipListener(
do_QueryInterface(mWebBrowserChrome)); if (tooltipListener) {
rv = tooltipListener->OnShowTooltip(aInXCoords, aInYCoords, aInTipText,
aTipDir); if (NS_SUCCEEDED(rv)) {
mShowingTooltip = true;
}
}
return rv;
}
// Tell the registered chrome that they should rollup the tooltip // NOTE: This routine is safe to call even if the popup is already closed.
NS_IMETHODIMP
ChromeTooltipListener::HideTooltip() {
nsresult rv = NS_OK;
// shut down the relevant timers if (mTooltipTimer) {
mTooltipTimer->Cancel();
mTooltipTimer = nullptr; // release tooltip target
mPossibleTooltipNode = nullptr;
mLastDocshell = nullptr;
}
// if we're showing the tip, tell the chrome to hide it if (mShowingTooltip) {
nsCOMPtr<nsITooltipListener> tooltipListener(
do_QueryInterface(mWebBrowserChrome)); if (tooltipListener) {
rv = tooltipListener->OnHideTooltip(); if (NS_SUCCEEDED(rv)) {
mShowingTooltip = false;
}
}
}
return rv;
}
bool ChromeTooltipListener::WebProgressShowedTooltip(
nsIWebProgress* aWebProgress) {
nsCOMPtr<nsIDocShell> docshell = do_QueryInterface(aWebProgress);
nsCOMPtr<nsIDocShell> lastUsed = do_QueryReferent(mLastDocshell); while (lastUsed) { if (lastUsed == docshell) { returntrue;
} // We can't use the docshell hierarchy here, because when the parent // docshell is navigated, the child docshell is disconnected (ie its // references to the parent are nulled out) despite it still being // alive here. So we use the document hierarchy instead:
Document* document = lastUsed->GetDocument(); if (document) {
document = document->GetInProcessParentDocument();
} if (!document) { break;
}
lastUsed = document->GetDocShell();
} returnfalse;
}
// A timer callback, fired when the mouse has hovered inside of a frame for the // appropriate amount of time. Getting to this point means that we should show // the tooltip, but only after we determine there is an appropriate TITLE // element. // // This relies on certain things being cached into the |aChromeTooltipListener| // object passed to us by the timer: // -- the x/y coordinates of the mouse (mMouseClientY, mMouseClientX) // -- the dom node the user hovered over (mPossibleTooltipNode) void ChromeTooltipListener::sTooltipCallback(nsITimer* aTimer, void* aChromeTooltipListener) { auto* self = static_cast<ChromeTooltipListener*>(aChromeTooltipListener); if (!self || !self->mPossibleTooltipNode) { return;
} // release tooltip target once done, no matter what we do here. auto cleanup = MakeScopeExit([&] { self->mPossibleTooltipNode = nullptr; }); if (!self->mPossibleTooltipNode->IsInComposedDoc()) { return;
} // Check that the document or its ancestors haven't been replaced.
{
Document* doc = self->mPossibleTooltipNode->OwnerDoc(); while (doc) { if (!doc->IsCurrentActiveDocument()) { return;
}
doc = doc->GetInProcessParentDocument();
}
}
// if there is text associated with the node, show the tip and fire // off a timer to auto-hide it.
nsITooltipTextProvider* tooltipProvider = self->GetTooltipTextProvider(); if (!tooltipProvider) { return;
}
nsString tooltipText;
nsString directionText; bool textFound = false;
tooltipProvider->GetNodeText(self->mPossibleTooltipNode,
getter_Copies(tooltipText),
getter_Copies(directionText), &textFound);
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.23Bemerkung:
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.