nsDocShellTreeOwner.cpp (40376B)
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/. */ 6 7 // Local Includes 8 #include "nsDocShellTreeOwner.h" 9 #include "nsWebBrowser.h" 10 11 // Helper Classes 12 #include "nsContentUtils.h" 13 #include "nsSize.h" 14 #include "mozilla/ReflowInput.h" 15 #include "mozilla/ScopeExit.h" 16 #include "nsComponentManagerUtils.h" 17 #include "nsString.h" 18 #include "nsAtom.h" 19 #include "nsReadableUtils.h" 20 #include "nsUnicharUtils.h" 21 #include "mozilla/StaticPrefs_ui.h" 22 23 // Interfaces needed to be included 24 #include "nsPresContext.h" 25 #include "nsITooltipListener.h" 26 #include "nsINode.h" 27 #include "Link.h" 28 #include "mozilla/dom/Document.h" 29 #include "mozilla/dom/Element.h" 30 #include "mozilla/dom/MouseEvent.h" 31 #include "mozilla/dom/SVGTitleElement.h" 32 #include "nsIFormControl.h" 33 #include "nsIWebNavigation.h" 34 #include "nsPIDOMWindow.h" 35 #include "nsPIWindowRoot.h" 36 #include "nsIWindowWatcher.h" 37 #include "nsPIWindowWatcher.h" 38 #include "nsIPrompt.h" 39 #include "nsIRemoteTab.h" 40 #include "nsIBrowserChild.h" 41 #include "nsRect.h" 42 #include "nsIContent.h" 43 #include "nsServiceManagerUtils.h" 44 #include "nsXULTooltipListener.h" 45 #include "nsIConstraintValidation.h" 46 #include "mozilla/EventListenerManager.h" 47 #include "mozilla/Try.h" 48 #include "mozilla/dom/DragEvent.h" 49 #include "mozilla/dom/Event.h" // for Event 50 #include "mozilla/dom/File.h" // for input type=file 51 #include "mozilla/dom/FileList.h" // for input type=file 52 #include "mozilla/dom/LoadURIOptionsBinding.h" 53 #include "mozilla/PresShell.h" 54 #include "mozilla/TextEvents.h" 55 56 using namespace mozilla; 57 using namespace mozilla::dom; 58 59 // A helper routine that navigates the tricky path from a |nsWebBrowser| to 60 // a |EventTarget| via the window root and chrome event handler. 61 static nsresult GetDOMEventTarget(nsWebBrowser* aInBrowser, 62 EventTarget** aTarget) { 63 if (!aInBrowser) { 64 return NS_ERROR_INVALID_POINTER; 65 } 66 67 nsCOMPtr<mozIDOMWindowProxy> domWindow; 68 aInBrowser->GetContentDOMWindow(getter_AddRefs(domWindow)); 69 if (!domWindow) { 70 return NS_ERROR_FAILURE; 71 } 72 73 auto* outerWindow = nsPIDOMWindowOuter::From(domWindow); 74 nsPIDOMWindowOuter* rootWindow = outerWindow->GetPrivateRoot(); 75 NS_ENSURE_TRUE(rootWindow, NS_ERROR_FAILURE); 76 nsCOMPtr<EventTarget> target = rootWindow->GetChromeEventHandler(); 77 NS_ENSURE_TRUE(target, NS_ERROR_FAILURE); 78 target.forget(aTarget); 79 80 return NS_OK; 81 } 82 83 nsDocShellTreeOwner::nsDocShellTreeOwner() 84 : mWebBrowser(nullptr), 85 mTreeOwner(nullptr), 86 mPrimaryContentShell(nullptr), 87 mWebBrowserChrome(nullptr), 88 mOwnerWin(nullptr), 89 mOwnerRequestor(nullptr) {} 90 91 nsDocShellTreeOwner::~nsDocShellTreeOwner() { RemoveChromeListeners(); } 92 93 NS_IMPL_ADDREF(nsDocShellTreeOwner) 94 NS_IMPL_RELEASE(nsDocShellTreeOwner) 95 96 NS_INTERFACE_MAP_BEGIN(nsDocShellTreeOwner) 97 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDocShellTreeOwner) 98 NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeOwner) 99 NS_INTERFACE_MAP_ENTRY(nsIBaseWindow) 100 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor) 101 NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener) 102 NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener) 103 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) 104 NS_INTERFACE_MAP_END 105 106 // The class that listens to the chrome events and tells the embedding chrome to 107 // show tooltips, as appropriate. Handles registering itself with the DOM with 108 // AddChromeListeners() and removing itself with RemoveChromeListeners(). 109 class ChromeTooltipListener final : public nsIDOMEventListener { 110 protected: 111 virtual ~ChromeTooltipListener(); 112 113 public: 114 NS_DECL_ISUPPORTS 115 116 ChromeTooltipListener(nsWebBrowser* aInBrowser, 117 nsIWebBrowserChrome* aInChrome); 118 119 NS_DECL_NSIDOMEVENTLISTENER 120 NS_IMETHOD MouseMove(mozilla::dom::Event* aMouseEvent); 121 122 // Add/remove the relevant listeners, based on what interfaces the embedding 123 // chrome implements. 124 NS_IMETHOD AddChromeListeners(); 125 NS_IMETHOD RemoveChromeListeners(); 126 127 NS_IMETHOD HideTooltip(); 128 129 bool WebProgressShowedTooltip(nsIWebProgress* aWebProgress); 130 131 private: 132 // pixel tolerance for mousemove event 133 static constexpr CSSIntCoord kTooltipMouseMoveTolerance = 7; 134 135 NS_IMETHOD AddTooltipListener(); 136 NS_IMETHOD RemoveTooltipListener(); 137 138 NS_IMETHOD ShowTooltip(int32_t aInXCoords, int32_t aInYCoords, 139 const nsAString& aInTipText, 140 const nsAString& aDirText); 141 nsITooltipTextProvider* GetTooltipTextProvider(); 142 143 nsWebBrowser* mWebBrowser; 144 nsCOMPtr<mozilla::dom::EventTarget> mEventTarget; 145 nsCOMPtr<nsITooltipTextProvider> mTooltipTextProvider; 146 147 // This must be a strong ref in order to make sure we can hide the tooltip if 148 // the window goes away while we're displaying one. If we don't hold a strong 149 // ref, the chrome might have been disposed of before we get a chance to tell 150 // it, and no one would ever tell us of that fact. 151 nsCOMPtr<nsIWebBrowserChrome> mWebBrowserChrome; 152 153 bool mTooltipListenerInstalled; 154 155 nsCOMPtr<nsITimer> mTooltipTimer; 156 static void sTooltipCallback(nsITimer* aTimer, void* aListener); 157 158 // Mouse coordinates for last mousemove event we saw 159 CSSIntPoint mMouseClientPoint; 160 161 // Mouse coordinates for tooltip event 162 LayoutDeviceIntPoint mMouseScreenPoint; 163 164 bool mShowingTooltip; 165 166 bool mTooltipShownOnce; 167 168 // The string of text that we last displayed. 169 nsString mLastShownTooltipText; 170 171 nsWeakPtr mLastDocshell; 172 173 // The node hovered over that fired the timer. This may turn into the node 174 // that triggered the tooltip, but only if the timer ever gets around to 175 // firing. This is a strong reference, because the tooltip content can be 176 // destroyed while we're waiting for the tooltip to pop up, and we need to 177 // detect that. It's set only when the tooltip timer is created and launched. 178 // The timer must either fire or be cancelled (or possibly released?), and we 179 // release this reference in each of those cases. So we don't leak. 180 nsCOMPtr<nsINode> mPossibleTooltipNode; 181 }; 182 183 //***************************************************************************** 184 // nsDocShellTreeOwner::nsIInterfaceRequestor 185 //***************************************************************************** 186 187 NS_IMETHODIMP 188 nsDocShellTreeOwner::GetInterface(const nsIID& aIID, void** aSink) { 189 NS_ENSURE_ARG_POINTER(aSink); 190 191 if (NS_SUCCEEDED(QueryInterface(aIID, aSink))) { 192 return NS_OK; 193 } 194 195 if (aIID.Equals(NS_GET_IID(nsIPrompt))) { 196 nsCOMPtr<nsIPrompt> prompt; 197 EnsurePrompter(); 198 prompt = mPrompter; 199 if (prompt) { 200 prompt.forget(aSink); 201 return NS_OK; 202 } 203 return NS_NOINTERFACE; 204 } 205 206 if (aIID.Equals(NS_GET_IID(nsIAuthPrompt))) { 207 nsCOMPtr<nsIAuthPrompt> prompt; 208 EnsureAuthPrompter(); 209 prompt = mAuthPrompter; 210 if (prompt) { 211 prompt.forget(aSink); 212 return NS_OK; 213 } 214 return NS_NOINTERFACE; 215 } 216 217 nsCOMPtr<nsIInterfaceRequestor> req = GetOwnerRequestor(); 218 if (req) { 219 return req->GetInterface(aIID, aSink); 220 } 221 222 return NS_NOINTERFACE; 223 } 224 225 //***************************************************************************** 226 // nsDocShellTreeOwner::nsIDocShellTreeOwner 227 //***************************************************************************** 228 229 void nsDocShellTreeOwner::EnsurePrompter() { 230 if (mPrompter) { 231 return; 232 } 233 234 nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID)); 235 if (wwatch && mWebBrowser) { 236 nsCOMPtr<mozIDOMWindowProxy> domWindow; 237 mWebBrowser->GetContentDOMWindow(getter_AddRefs(domWindow)); 238 if (domWindow) { 239 wwatch->GetNewPrompter(domWindow, getter_AddRefs(mPrompter)); 240 } 241 } 242 } 243 244 void nsDocShellTreeOwner::EnsureAuthPrompter() { 245 if (mAuthPrompter) { 246 return; 247 } 248 249 nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID)); 250 if (wwatch && mWebBrowser) { 251 nsCOMPtr<mozIDOMWindowProxy> domWindow; 252 mWebBrowser->GetContentDOMWindow(getter_AddRefs(domWindow)); 253 if (domWindow) { 254 wwatch->GetNewAuthPrompter(domWindow, getter_AddRefs(mAuthPrompter)); 255 } 256 } 257 } 258 259 void nsDocShellTreeOwner::AddToWatcher() { 260 if (mWebBrowser) { 261 nsCOMPtr<mozIDOMWindowProxy> domWindow; 262 mWebBrowser->GetContentDOMWindow(getter_AddRefs(domWindow)); 263 if (domWindow) { 264 nsCOMPtr<nsPIWindowWatcher> wwatch( 265 do_GetService(NS_WINDOWWATCHER_CONTRACTID)); 266 if (wwatch) { 267 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome(); 268 if (webBrowserChrome) { 269 wwatch->AddWindow(domWindow, webBrowserChrome); 270 } 271 } 272 } 273 } 274 } 275 276 void nsDocShellTreeOwner::RemoveFromWatcher() { 277 if (mWebBrowser) { 278 nsCOMPtr<mozIDOMWindowProxy> domWindow; 279 mWebBrowser->GetContentDOMWindow(getter_AddRefs(domWindow)); 280 if (domWindow) { 281 nsCOMPtr<nsPIWindowWatcher> wwatch( 282 do_GetService(NS_WINDOWWATCHER_CONTRACTID)); 283 if (wwatch) { 284 wwatch->RemoveWindow(domWindow); 285 } 286 } 287 } 288 } 289 290 void nsDocShellTreeOwner::EnsureContentTreeOwner() { 291 if (mContentTreeOwner) { 292 return; 293 } 294 295 mContentTreeOwner = new nsDocShellTreeOwner(); 296 nsCOMPtr<nsIWebBrowserChrome> browserChrome = GetWebBrowserChrome(); 297 if (browserChrome) { 298 mContentTreeOwner->SetWebBrowserChrome(browserChrome); 299 } 300 301 if (mWebBrowser) { 302 mContentTreeOwner->WebBrowser(mWebBrowser); 303 } 304 } 305 306 NS_IMETHODIMP 307 nsDocShellTreeOwner::ContentShellAdded(nsIDocShellTreeItem* aContentShell, 308 bool aPrimary) { 309 if (mTreeOwner) return mTreeOwner->ContentShellAdded(aContentShell, aPrimary); 310 311 EnsureContentTreeOwner(); 312 aContentShell->SetTreeOwner(mContentTreeOwner); 313 314 if (aPrimary) { 315 mPrimaryContentShell = aContentShell; 316 mPrimaryRemoteTab = nullptr; 317 } 318 return NS_OK; 319 } 320 321 NS_IMETHODIMP 322 nsDocShellTreeOwner::ContentShellRemoved(nsIDocShellTreeItem* aContentShell) { 323 if (mTreeOwner) { 324 return mTreeOwner->ContentShellRemoved(aContentShell); 325 } 326 327 if (mPrimaryContentShell == aContentShell) { 328 mPrimaryContentShell = nullptr; 329 } 330 331 return NS_OK; 332 } 333 334 NS_IMETHODIMP 335 nsDocShellTreeOwner::GetPrimaryContentShell(nsIDocShellTreeItem** aShell) { 336 NS_ENSURE_ARG_POINTER(aShell); 337 338 if (mTreeOwner) { 339 return mTreeOwner->GetPrimaryContentShell(aShell); 340 } 341 342 nsCOMPtr<nsIDocShellTreeItem> shell; 343 if (!mPrimaryRemoteTab) { 344 shell = 345 mPrimaryContentShell ? mPrimaryContentShell : mWebBrowser->mDocShell; 346 } 347 shell.forget(aShell); 348 349 return NS_OK; 350 } 351 352 NS_IMETHODIMP 353 nsDocShellTreeOwner::RemoteTabAdded(nsIRemoteTab* aTab, bool aPrimary) { 354 if (mTreeOwner) { 355 return mTreeOwner->RemoteTabAdded(aTab, aPrimary); 356 } 357 358 if (aPrimary) { 359 mPrimaryRemoteTab = aTab; 360 mPrimaryContentShell = nullptr; 361 } else if (mPrimaryRemoteTab == aTab) { 362 mPrimaryRemoteTab = nullptr; 363 } 364 365 return NS_OK; 366 } 367 368 NS_IMETHODIMP 369 nsDocShellTreeOwner::RemoteTabRemoved(nsIRemoteTab* aTab) { 370 if (mTreeOwner) { 371 return mTreeOwner->RemoteTabRemoved(aTab); 372 } 373 374 if (aTab == mPrimaryRemoteTab) { 375 mPrimaryRemoteTab = nullptr; 376 } 377 378 return NS_OK; 379 } 380 381 NS_IMETHODIMP 382 nsDocShellTreeOwner::GetPrimaryRemoteTab(nsIRemoteTab** aTab) { 383 if (mTreeOwner) { 384 return mTreeOwner->GetPrimaryRemoteTab(aTab); 385 } 386 387 nsCOMPtr<nsIRemoteTab> tab = mPrimaryRemoteTab; 388 tab.forget(aTab); 389 return NS_OK; 390 } 391 392 NS_IMETHODIMP 393 nsDocShellTreeOwner::GetPrimaryContentBrowsingContext( 394 mozilla::dom::BrowsingContext** aBc) { 395 if (mTreeOwner) { 396 return mTreeOwner->GetPrimaryContentBrowsingContext(aBc); 397 } 398 if (mPrimaryRemoteTab) { 399 return mPrimaryRemoteTab->GetBrowsingContext(aBc); 400 } 401 if (mPrimaryContentShell) { 402 return mPrimaryContentShell->GetBrowsingContextXPCOM(aBc); 403 } 404 if (mWebBrowser->mDocShell) { 405 return mWebBrowser->mDocShell->GetBrowsingContextXPCOM(aBc); 406 } 407 *aBc = nullptr; 408 return NS_OK; 409 } 410 411 NS_IMETHODIMP 412 nsDocShellTreeOwner::GetPrimaryContentSize(int32_t* aWidth, int32_t* aHeight) { 413 return NS_ERROR_NOT_IMPLEMENTED; 414 } 415 416 NS_IMETHODIMP 417 nsDocShellTreeOwner::SetPrimaryContentSize(int32_t aWidth, int32_t aHeight) { 418 return NS_ERROR_NOT_IMPLEMENTED; 419 } 420 421 NS_IMETHODIMP 422 nsDocShellTreeOwner::GetRootShellSize(int32_t* aWidth, int32_t* aHeight) { 423 return NS_ERROR_NOT_IMPLEMENTED; 424 } 425 426 NS_IMETHODIMP 427 nsDocShellTreeOwner::SetRootShellSize(int32_t aWidth, int32_t aHeight) { 428 return NS_ERROR_NOT_IMPLEMENTED; 429 } 430 431 NS_IMETHODIMP 432 nsDocShellTreeOwner::SizeShellTo(nsIDocShellTreeItem* aShellItem, int32_t aCX, 433 int32_t aCY) { 434 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome(); 435 436 NS_ENSURE_STATE(mTreeOwner || webBrowserChrome); 437 438 if (nsCOMPtr<nsIDocShellTreeOwner> treeOwner = mTreeOwner) { 439 return treeOwner->SizeShellTo(aShellItem, aCX, aCY); 440 } 441 442 if (aShellItem == mWebBrowser->mDocShell) { 443 nsCOMPtr<nsIBrowserChild> browserChild = 444 do_QueryInterface(webBrowserChrome); 445 if (browserChild) { 446 // The XUL window to resize is in the parent process, but there we 447 // won't be able to get the size of aShellItem. We can ask the parent 448 // process to change our size instead. 449 nsCOMPtr<nsIBaseWindow> shellAsWin(do_QueryInterface(aShellItem)); 450 NS_ENSURE_TRUE(shellAsWin, NS_ERROR_FAILURE); 451 452 LayoutDeviceIntSize shellSize; 453 shellAsWin->GetSize(&shellSize.width, &shellSize.height); 454 LayoutDeviceIntSize deltaSize = LayoutDeviceIntSize(aCX, aCY) - shellSize; 455 456 LayoutDeviceIntSize currentSize; 457 GetSize(¤tSize.width, ¤tSize.height); 458 459 LayoutDeviceIntSize newSize = currentSize + deltaSize; 460 return SetSize(newSize.width, newSize.height, true); 461 } 462 // XXX: this is weird, but we used to call a method here 463 // (webBrowserChrome->SizeBrowserTo()) whose implementations all failed 464 // like this, so... 465 return NS_ERROR_NOT_IMPLEMENTED; 466 } 467 468 MOZ_ASSERT_UNREACHABLE("This is unimplemented, API should be cleaned up"); 469 return NS_ERROR_NOT_IMPLEMENTED; 470 } 471 472 NS_IMETHODIMP 473 nsDocShellTreeOwner::SetPersistence(bool aPersistPosition, bool aPersistSize, 474 bool aPersistSizeMode) { 475 return NS_ERROR_NOT_IMPLEMENTED; 476 } 477 478 NS_IMETHODIMP 479 nsDocShellTreeOwner::GetPersistence(bool* aPersistPosition, bool* aPersistSize, 480 bool* aPersistSizeMode) { 481 return NS_ERROR_NOT_IMPLEMENTED; 482 } 483 484 NS_IMETHODIMP 485 nsDocShellTreeOwner::GetHasPrimaryContent(bool* aResult) { 486 *aResult = mPrimaryRemoteTab || mPrimaryContentShell; 487 return NS_OK; 488 } 489 490 //***************************************************************************** 491 // nsDocShellTreeOwner::nsIBaseWindow 492 //***************************************************************************** 493 494 NS_IMETHODIMP 495 nsDocShellTreeOwner::Destroy() { 496 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome(); 497 if (webBrowserChrome) { 498 // XXX: this is weird, but we used to call a method here 499 // (webBrowserChrome->DestroyBrowserWindow()) whose implementations all 500 // failed like this, so... 501 return NS_ERROR_NOT_IMPLEMENTED; 502 } 503 504 return NS_ERROR_NULL_POINTER; 505 } 506 507 double nsDocShellTreeOwner::GetWidgetCSSToDeviceScale() { 508 return mWebBrowser ? mWebBrowser->GetWidgetCSSToDeviceScale() : 1.0; 509 } 510 511 NS_IMETHODIMP 512 nsDocShellTreeOwner::GetDevicePixelsPerDesktopPixel(double* aScale) { 513 if (mWebBrowser) { 514 return mWebBrowser->GetDevicePixelsPerDesktopPixel(aScale); 515 } 516 517 *aScale = 1.0; 518 return NS_OK; 519 } 520 521 NS_IMETHODIMP 522 nsDocShellTreeOwner::SetPositionDesktopPix(int32_t aX, int32_t aY) { 523 if (mWebBrowser) { 524 nsresult rv = mWebBrowser->SetPositionDesktopPix(aX, aY); 525 NS_ENSURE_SUCCESS(rv, rv); 526 } 527 528 double scale = 1.0; 529 GetDevicePixelsPerDesktopPixel(&scale); 530 return SetPosition(NSToIntRound(aX * scale), NSToIntRound(aY * scale)); 531 } 532 533 NS_IMETHODIMP 534 nsDocShellTreeOwner::SetPosition(int32_t aX, int32_t aY) { 535 return SetDimensions( 536 {DimensionKind::Outer, Some(aX), Some(aY), Nothing(), Nothing()}); 537 } 538 539 NS_IMETHODIMP 540 nsDocShellTreeOwner::GetPosition(int32_t* aX, int32_t* aY) { 541 return GetDimensions(DimensionKind::Outer, aX, aY, nullptr, nullptr); 542 } 543 544 NS_IMETHODIMP 545 nsDocShellTreeOwner::SetSize(int32_t aCX, int32_t aCY, bool aRepaint) { 546 return SetDimensions( 547 {DimensionKind::Outer, Nothing(), Nothing(), Some(aCX), Some(aCY)}); 548 } 549 550 NS_IMETHODIMP 551 nsDocShellTreeOwner::GetSize(int32_t* aCX, int32_t* aCY) { 552 return GetDimensions(DimensionKind::Outer, nullptr, nullptr, aCX, aCY); 553 } 554 555 NS_IMETHODIMP 556 nsDocShellTreeOwner::SetPositionAndSize(int32_t aX, int32_t aY, int32_t aCX, 557 int32_t aCY, uint32_t aFlags) { 558 return SetDimensions( 559 {DimensionKind::Outer, Some(aX), Some(aY), Some(aCX), Some(aCY)}); 560 } 561 562 NS_IMETHODIMP 563 nsDocShellTreeOwner::GetPositionAndSize(int32_t* aX, int32_t* aY, int32_t* aCX, 564 int32_t* aCY) { 565 return GetDimensions(DimensionKind::Outer, aX, aY, aCX, aCY); 566 } 567 568 NS_IMETHODIMP 569 nsDocShellTreeOwner::SetDimensions(DimensionRequest&& aRequest) { 570 nsCOMPtr<nsIBaseWindow> ownerWin = GetOwnerWin(); 571 if (ownerWin) { 572 return ownerWin->SetDimensions(std::move(aRequest)); 573 } 574 575 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome(); 576 NS_ENSURE_STATE(webBrowserChrome); 577 return webBrowserChrome->SetDimensions(std::move(aRequest)); 578 } 579 580 NS_IMETHODIMP 581 nsDocShellTreeOwner::GetDimensions(DimensionKind aDimensionKind, int32_t* aX, 582 int32_t* aY, int32_t* aCX, int32_t* aCY) { 583 nsCOMPtr<nsIBaseWindow> ownerWin = GetOwnerWin(); 584 if (ownerWin) { 585 return ownerWin->GetDimensions(aDimensionKind, aX, aY, aCX, aCY); 586 } 587 588 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome(); 589 NS_ENSURE_STATE(webBrowserChrome); 590 return webBrowserChrome->GetDimensions(aDimensionKind, aX, aY, aCX, aCY); 591 } 592 593 NS_IMETHODIMP 594 nsDocShellTreeOwner::GetParentWidget(nsIWidget** aParentWidget) { 595 return NS_ERROR_NULL_POINTER; 596 } 597 598 NS_IMETHODIMP 599 nsDocShellTreeOwner::SetParentWidget(nsIWidget* aParentWidget) { 600 return NS_ERROR_NULL_POINTER; 601 } 602 603 NS_IMETHODIMP 604 nsDocShellTreeOwner::GetNativeHandle(nsAString& aNativeHandle) { 605 // the nativeHandle should be accessed from nsIAppWindow 606 return NS_ERROR_NOT_IMPLEMENTED; 607 } 608 609 NS_IMETHODIMP 610 nsDocShellTreeOwner::GetVisibility(bool* aVisibility) { 611 nsCOMPtr<nsIBaseWindow> ownerWin = GetOwnerWin(); 612 if (ownerWin) { 613 return ownerWin->GetVisibility(aVisibility); 614 } 615 616 return NS_ERROR_NOT_IMPLEMENTED; 617 } 618 619 NS_IMETHODIMP 620 nsDocShellTreeOwner::SetVisibility(bool aVisibility) { 621 nsCOMPtr<nsIBaseWindow> ownerWin = GetOwnerWin(); 622 if (ownerWin) { 623 return ownerWin->SetVisibility(aVisibility); 624 } 625 return NS_ERROR_NULL_POINTER; 626 } 627 628 NS_IMETHODIMP 629 nsDocShellTreeOwner::GetEnabled(bool* aEnabled) { 630 NS_ENSURE_ARG_POINTER(aEnabled); 631 *aEnabled = true; 632 return NS_ERROR_NOT_IMPLEMENTED; 633 } 634 635 NS_IMETHODIMP 636 nsDocShellTreeOwner::SetEnabled(bool aEnabled) { 637 return NS_ERROR_NOT_IMPLEMENTED; 638 } 639 640 NS_IMETHODIMP 641 nsDocShellTreeOwner::GetMainWidget(nsIWidget** aMainWidget) { 642 return NS_ERROR_NULL_POINTER; 643 } 644 645 NS_IMETHODIMP 646 nsDocShellTreeOwner::GetTitle(nsAString& aTitle) { 647 nsCOMPtr<nsIBaseWindow> ownerWin = GetOwnerWin(); 648 if (ownerWin) { 649 return ownerWin->GetTitle(aTitle); 650 } 651 return NS_ERROR_NULL_POINTER; 652 } 653 654 NS_IMETHODIMP 655 nsDocShellTreeOwner::SetTitle(const nsAString& aTitle) { 656 nsCOMPtr<nsIBaseWindow> ownerWin = GetOwnerWin(); 657 if (ownerWin) { 658 return ownerWin->SetTitle(aTitle); 659 } 660 return NS_ERROR_NULL_POINTER; 661 } 662 663 //***************************************************************************** 664 // nsDocShellTreeOwner::nsIWebProgressListener 665 //***************************************************************************** 666 667 NS_IMETHODIMP 668 nsDocShellTreeOwner::OnProgressChange(nsIWebProgress* aProgress, 669 nsIRequest* aRequest, 670 int32_t aCurSelfProgress, 671 int32_t aMaxSelfProgress, 672 int32_t aCurTotalProgress, 673 int32_t aMaxTotalProgress) { 674 // In the absence of DOM document creation event, this method is the 675 // most convenient place to install the mouse listener on the 676 // DOM document. 677 return AddChromeListeners(); 678 } 679 680 NS_IMETHODIMP 681 nsDocShellTreeOwner::OnStateChange(nsIWebProgress* aProgress, 682 nsIRequest* aRequest, 683 uint32_t aProgressStateFlags, 684 nsresult aStatus) { 685 return NS_OK; 686 } 687 688 NS_IMETHODIMP 689 nsDocShellTreeOwner::OnLocationChange(nsIWebProgress* aWebProgress, 690 nsIRequest* aRequest, nsIURI* aURI, 691 uint32_t aFlags) { 692 if (mChromeTooltipListener && aWebProgress && 693 !(aFlags & nsIWebProgressListener::LOCATION_CHANGE_SAME_DOCUMENT) && 694 mChromeTooltipListener->WebProgressShowedTooltip(aWebProgress)) { 695 mChromeTooltipListener->HideTooltip(); 696 } 697 return NS_OK; 698 } 699 700 NS_IMETHODIMP 701 nsDocShellTreeOwner::OnStatusChange(nsIWebProgress* aWebProgress, 702 nsIRequest* aRequest, nsresult aStatus, 703 const char16_t* aMessage) { 704 return NS_OK; 705 } 706 707 NS_IMETHODIMP 708 nsDocShellTreeOwner::OnSecurityChange(nsIWebProgress* aWebProgress, 709 nsIRequest* aRequest, uint32_t aState) { 710 return NS_OK; 711 } 712 713 NS_IMETHODIMP 714 nsDocShellTreeOwner::OnContentBlockingEvent(nsIWebProgress* aWebProgress, 715 nsIRequest* aRequest, 716 uint32_t aEvent) { 717 return NS_OK; 718 } 719 720 //***************************************************************************** 721 // nsDocShellTreeOwner: Accessors 722 //***************************************************************************** 723 724 void nsDocShellTreeOwner::WebBrowser(nsWebBrowser* aWebBrowser) { 725 if (!aWebBrowser) { 726 RemoveChromeListeners(); 727 } 728 if (aWebBrowser != mWebBrowser) { 729 mPrompter = nullptr; 730 mAuthPrompter = nullptr; 731 } 732 733 mWebBrowser = aWebBrowser; 734 735 if (mContentTreeOwner) { 736 mContentTreeOwner->WebBrowser(aWebBrowser); 737 if (!aWebBrowser) { 738 mContentTreeOwner = nullptr; 739 } 740 } 741 } 742 743 nsWebBrowser* nsDocShellTreeOwner::WebBrowser() { return mWebBrowser; } 744 745 NS_IMETHODIMP 746 nsDocShellTreeOwner::SetTreeOwner(nsIDocShellTreeOwner* aTreeOwner) { 747 if (aTreeOwner) { 748 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome(do_GetInterface(aTreeOwner)); 749 NS_ENSURE_TRUE(webBrowserChrome, NS_ERROR_INVALID_ARG); 750 NS_ENSURE_SUCCESS(SetWebBrowserChrome(webBrowserChrome), 751 NS_ERROR_INVALID_ARG); 752 mTreeOwner = aTreeOwner; 753 } else { 754 mTreeOwner = nullptr; 755 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome(); 756 if (!webBrowserChrome) { 757 NS_ENSURE_SUCCESS(SetWebBrowserChrome(nullptr), NS_ERROR_FAILURE); 758 } 759 } 760 761 return NS_OK; 762 } 763 764 NS_IMETHODIMP 765 nsDocShellTreeOwner::SetWebBrowserChrome( 766 nsIWebBrowserChrome* aWebBrowserChrome) { 767 if (!aWebBrowserChrome) { 768 mWebBrowserChrome = nullptr; 769 mOwnerWin = nullptr; 770 mOwnerRequestor = nullptr; 771 mWebBrowserChromeWeak = nullptr; 772 } else { 773 nsCOMPtr<nsISupportsWeakReference> supportsweak = 774 do_QueryInterface(aWebBrowserChrome); 775 if (supportsweak) { 776 supportsweak->GetWeakReference(getter_AddRefs(mWebBrowserChromeWeak)); 777 } else { 778 nsCOMPtr<nsIBaseWindow> ownerWin(do_QueryInterface(aWebBrowserChrome)); 779 nsCOMPtr<nsIInterfaceRequestor> requestor( 780 do_QueryInterface(aWebBrowserChrome)); 781 782 // it's ok for ownerWin or requestor to be null. 783 mWebBrowserChrome = aWebBrowserChrome; 784 mOwnerWin = ownerWin; 785 mOwnerRequestor = requestor; 786 } 787 } 788 789 if (mContentTreeOwner) { 790 mContentTreeOwner->SetWebBrowserChrome(aWebBrowserChrome); 791 } 792 793 return NS_OK; 794 } 795 796 // Hook up things to the chrome like context menus and tooltips, if the chrome 797 // has implemented the right interfaces. 798 NS_IMETHODIMP 799 nsDocShellTreeOwner::AddChromeListeners() { 800 nsresult rv = NS_OK; 801 802 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome(); 803 if (!webBrowserChrome) { 804 return NS_ERROR_FAILURE; 805 } 806 807 // install tooltips 808 if (!mChromeTooltipListener) { 809 nsCOMPtr<nsITooltipListener> tooltipListener( 810 do_QueryInterface(webBrowserChrome)); 811 if (tooltipListener) { 812 mChromeTooltipListener = 813 new ChromeTooltipListener(mWebBrowser, webBrowserChrome); 814 rv = mChromeTooltipListener->AddChromeListeners(); 815 } 816 } 817 818 nsCOMPtr<EventTarget> target; 819 GetDOMEventTarget(mWebBrowser, getter_AddRefs(target)); 820 821 // register dragover and drop event listeners with the listener manager 822 MOZ_ASSERT(target, "how does this happen? (see bug 1659758)"); 823 if (target) { 824 if (EventListenerManager* elmP = target->GetOrCreateListenerManager()) { 825 elmP->AddEventListenerByType(this, u"dragover"_ns, 826 TrustedEventsAtSystemGroupBubble()); 827 elmP->AddEventListenerByType(this, u"drop"_ns, 828 TrustedEventsAtSystemGroupBubble()); 829 } 830 } 831 832 return rv; 833 } 834 835 NS_IMETHODIMP 836 nsDocShellTreeOwner::RemoveChromeListeners() { 837 if (mChromeTooltipListener) { 838 mChromeTooltipListener->RemoveChromeListeners(); 839 mChromeTooltipListener = nullptr; 840 } 841 842 nsCOMPtr<EventTarget> piTarget; 843 GetDOMEventTarget(mWebBrowser, getter_AddRefs(piTarget)); 844 if (!piTarget) { 845 return NS_OK; 846 } 847 848 EventListenerManager* elmP = piTarget->GetOrCreateListenerManager(); 849 if (elmP) { 850 elmP->RemoveEventListenerByType(this, u"dragover"_ns, 851 TrustedEventsAtSystemGroupBubble()); 852 elmP->RemoveEventListenerByType(this, u"drop"_ns, 853 TrustedEventsAtSystemGroupBubble()); 854 } 855 856 return NS_OK; 857 } 858 859 NS_IMETHODIMP 860 nsDocShellTreeOwner::HandleEvent(Event* aEvent) { 861 DragEvent* dragEvent = aEvent ? aEvent->AsDragEvent() : nullptr; 862 if (NS_WARN_IF(!dragEvent)) { 863 return NS_ERROR_INVALID_ARG; 864 } 865 866 if (dragEvent->DefaultPrevented()) { 867 return NS_OK; 868 } 869 870 nsCOMPtr<nsIDroppedLinkHandler> handler = 871 do_GetService("@mozilla.org/content/dropped-link-handler;1"); 872 if (!handler) { 873 return NS_OK; 874 } 875 876 nsAutoString eventType; 877 aEvent->GetType(eventType); 878 if (eventType.EqualsLiteral("dragover")) { 879 bool canDropLink = false; 880 handler->CanDropLink(dragEvent, false, &canDropLink); 881 if (canDropLink) { 882 aEvent->PreventDefault(); 883 } 884 } else if (eventType.EqualsLiteral("drop")) { 885 nsCOMPtr<nsIWebNavigation> webnav = 886 static_cast<nsIWebNavigation*>(mWebBrowser); 887 888 // The page might have cancelled the dragover event itself, so check to 889 // make sure that the link can be dropped first. 890 bool canDropLink = false; 891 handler->CanDropLink(dragEvent, false, &canDropLink); 892 if (!canDropLink) { 893 return NS_OK; 894 } 895 896 nsTArray<RefPtr<nsIDroppedLinkItem>> links; 897 if (webnav && NS_SUCCEEDED(handler->DropLinks(dragEvent, true, links))) { 898 if (links.Length() >= 1) { 899 nsCOMPtr<nsIPrincipal> triggeringPrincipal; 900 handler->GetTriggeringPrincipal(dragEvent, 901 getter_AddRefs(triggeringPrincipal)); 902 if (triggeringPrincipal) { 903 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = 904 GetWebBrowserChrome(); 905 if (webBrowserChrome) { 906 nsCOMPtr<nsIBrowserChild> browserChild = 907 do_QueryInterface(webBrowserChrome); 908 if (browserChild) { 909 nsresult rv = browserChild->RemoteDropLinks(links); 910 return rv; 911 } 912 } 913 nsAutoString url; 914 if (NS_SUCCEEDED(links[0]->GetUrl(url))) { 915 if (!url.IsEmpty()) { 916 #ifndef ANDROID 917 MOZ_ASSERT(triggeringPrincipal, 918 "nsDocShellTreeOwner::HandleEvent: Need a valid " 919 "triggeringPrincipal"); 920 #endif 921 LoadURIOptions loadURIOptions; 922 loadURIOptions.mTriggeringPrincipal = triggeringPrincipal; 923 nsCOMPtr<nsIPolicyContainer> policyContainer; 924 handler->GetPolicyContainer(dragEvent, 925 getter_AddRefs(policyContainer)); 926 loadURIOptions.mPolicyContainer = policyContainer; 927 webnav->FixupAndLoadURIString(url, loadURIOptions); 928 } 929 } 930 } 931 } 932 } else { 933 aEvent->StopPropagation(); 934 aEvent->PreventDefault(); 935 } 936 } 937 938 return NS_OK; 939 } 940 941 already_AddRefed<nsIWebBrowserChrome> 942 nsDocShellTreeOwner::GetWebBrowserChrome() { 943 nsCOMPtr<nsIWebBrowserChrome> chrome; 944 if (mWebBrowserChromeWeak) { 945 chrome = do_QueryReferent(mWebBrowserChromeWeak); 946 } else if (mWebBrowserChrome) { 947 chrome = mWebBrowserChrome; 948 } 949 return chrome.forget(); 950 } 951 952 already_AddRefed<nsIBaseWindow> nsDocShellTreeOwner::GetOwnerWin() { 953 nsCOMPtr<nsIBaseWindow> win; 954 if (mWebBrowserChromeWeak) { 955 win = do_QueryReferent(mWebBrowserChromeWeak); 956 } else if (mOwnerWin) { 957 win = mOwnerWin; 958 } 959 return win.forget(); 960 } 961 962 already_AddRefed<nsIInterfaceRequestor> 963 nsDocShellTreeOwner::GetOwnerRequestor() { 964 nsCOMPtr<nsIInterfaceRequestor> req; 965 if (mWebBrowserChromeWeak) { 966 req = do_QueryReferent(mWebBrowserChromeWeak); 967 } else if (mOwnerRequestor) { 968 req = mOwnerRequestor; 969 } 970 return req.forget(); 971 } 972 973 NS_IMPL_ISUPPORTS(ChromeTooltipListener, nsIDOMEventListener) 974 975 ChromeTooltipListener::ChromeTooltipListener(nsWebBrowser* aInBrowser, 976 nsIWebBrowserChrome* aInChrome) 977 : mWebBrowser(aInBrowser), 978 mWebBrowserChrome(aInChrome), 979 mTooltipListenerInstalled(false), 980 mShowingTooltip(false), 981 mTooltipShownOnce(false) {} 982 983 ChromeTooltipListener::~ChromeTooltipListener() {} 984 985 nsITooltipTextProvider* ChromeTooltipListener::GetTooltipTextProvider() { 986 if (!mTooltipTextProvider) { 987 mTooltipTextProvider = do_GetService(NS_TOOLTIPTEXTPROVIDER_CONTRACTID); 988 } 989 990 if (!mTooltipTextProvider) { 991 mTooltipTextProvider = 992 do_GetService(NS_DEFAULTTOOLTIPTEXTPROVIDER_CONTRACTID); 993 } 994 995 return mTooltipTextProvider; 996 } 997 998 // Hook up things to the chrome like context menus and tooltips, if the chrome 999 // has implemented the right interfaces. 1000 NS_IMETHODIMP 1001 ChromeTooltipListener::AddChromeListeners() { 1002 if (!mEventTarget) { 1003 GetDOMEventTarget(mWebBrowser, getter_AddRefs(mEventTarget)); 1004 } 1005 1006 // Register the appropriate events for tooltips, but only if 1007 // the embedding chrome cares. 1008 nsresult rv = NS_OK; 1009 nsCOMPtr<nsITooltipListener> tooltipListener( 1010 do_QueryInterface(mWebBrowserChrome)); 1011 if (tooltipListener && !mTooltipListenerInstalled) { 1012 rv = AddTooltipListener(); 1013 if (NS_FAILED(rv)) { 1014 return rv; 1015 } 1016 } 1017 1018 return rv; 1019 } 1020 1021 // Subscribe to the events that will allow us to track tooltips. We need "mouse" 1022 // for mouseExit, "mouse motion" for mouseMove, and "key" for keyDown. As we 1023 // add the listeners, keep track of how many succeed so we can clean up 1024 // correctly in Release(). 1025 NS_IMETHODIMP 1026 ChromeTooltipListener::AddTooltipListener() { 1027 if (mEventTarget) { 1028 MOZ_TRY(mEventTarget->AddSystemEventListener(u"keydown"_ns, this, false, 1029 false)); 1030 MOZ_TRY(mEventTarget->AddSystemEventListener(u"mousedown"_ns, this, false, 1031 false)); 1032 MOZ_TRY(mEventTarget->AddSystemEventListener(u"mouseout"_ns, this, false, 1033 false)); 1034 MOZ_TRY(mEventTarget->AddSystemEventListener(u"mousemove"_ns, this, false, 1035 false)); 1036 1037 mTooltipListenerInstalled = true; 1038 } 1039 1040 return NS_OK; 1041 } 1042 1043 // Unsubscribe from the various things we've hooked up to the window root. 1044 NS_IMETHODIMP 1045 ChromeTooltipListener::RemoveChromeListeners() { 1046 HideTooltip(); 1047 1048 if (mTooltipListenerInstalled) { 1049 RemoveTooltipListener(); 1050 } 1051 1052 mEventTarget = nullptr; 1053 1054 // it really doesn't matter if these fail... 1055 return NS_OK; 1056 } 1057 1058 // Unsubscribe from all the various tooltip events that we were listening to. 1059 NS_IMETHODIMP 1060 ChromeTooltipListener::RemoveTooltipListener() { 1061 if (mEventTarget) { 1062 mEventTarget->RemoveSystemEventListener(u"keydown"_ns, this, false); 1063 mEventTarget->RemoveSystemEventListener(u"mousedown"_ns, this, false); 1064 mEventTarget->RemoveSystemEventListener(u"mouseout"_ns, this, false); 1065 mEventTarget->RemoveSystemEventListener(u"mousemove"_ns, this, false); 1066 mTooltipListenerInstalled = false; 1067 } 1068 1069 return NS_OK; 1070 } 1071 1072 NS_IMETHODIMP 1073 ChromeTooltipListener::HandleEvent(Event* aEvent) { 1074 nsAutoString eventType; 1075 aEvent->GetType(eventType); 1076 1077 if (eventType.EqualsLiteral("mousedown")) { 1078 return HideTooltip(); 1079 } else if (eventType.EqualsLiteral("keydown")) { 1080 WidgetKeyboardEvent* keyEvent = aEvent->WidgetEventPtr()->AsKeyboardEvent(); 1081 if (nsXULTooltipListener::KeyEventHidesTooltip(*keyEvent)) { 1082 return HideTooltip(); 1083 } 1084 return NS_OK; 1085 } else if (eventType.EqualsLiteral("mouseout")) { 1086 // Reset flag so that tooltip will display on the next MouseMove 1087 mTooltipShownOnce = false; 1088 return HideTooltip(); 1089 } else if (eventType.EqualsLiteral("mousemove")) { 1090 return MouseMove(aEvent); 1091 } 1092 1093 NS_ERROR("Unexpected event type"); 1094 return NS_OK; 1095 } 1096 1097 // If we're a tooltip, fire off a timer to see if a tooltip should be shown. If 1098 // the timer fires, we cache the node in |mPossibleTooltipNode|. 1099 nsresult ChromeTooltipListener::MouseMove(Event* aMouseEvent) { 1100 if (!nsXULTooltipListener::ShowTooltips()) { 1101 return NS_OK; 1102 } 1103 1104 MouseEvent* mouseEvent = aMouseEvent->AsMouseEvent(); 1105 if (!mouseEvent) { 1106 return NS_OK; 1107 } 1108 1109 // stash the coordinates of the event so that we can still get back to it from 1110 // within the timer callback. On win32, we'll get a MouseMove event even when 1111 // a popup goes away -- even when the mouse doesn't change position! To get 1112 // around this, we make sure the mouse has really moved before proceeding. 1113 const CSSIntPoint newMouseClientPoint = 1114 RoundedToInt(mouseEvent->ClientPoint()); 1115 if (mMouseClientPoint == newMouseClientPoint) { 1116 return NS_OK; 1117 } 1118 1119 // Filter out minor mouse movements. 1120 if (mShowingTooltip && 1121 (abs(mMouseClientPoint.x - newMouseClientPoint.x) <= 1122 kTooltipMouseMoveTolerance) && 1123 (abs(mMouseClientPoint.y - newMouseClientPoint.y) <= 1124 kTooltipMouseMoveTolerance)) { 1125 return NS_OK; 1126 } 1127 1128 mMouseClientPoint = newMouseClientPoint; 1129 mMouseScreenPoint = mouseEvent->ScreenPointLayoutDevicePix(); 1130 1131 if (mTooltipTimer) { 1132 mTooltipTimer->Cancel(); 1133 mTooltipTimer = nullptr; 1134 } 1135 1136 if (!mShowingTooltip) { 1137 if (nsCOMPtr<EventTarget> eventTarget = aMouseEvent->GetOriginalTarget()) { 1138 mPossibleTooltipNode = nsINode::FromEventTarget(eventTarget); 1139 } 1140 1141 if (mPossibleTooltipNode) { 1142 nsresult rv = NS_NewTimerWithFuncCallback( 1143 getter_AddRefs(mTooltipTimer), sTooltipCallback, this, 1144 StaticPrefs::ui_tooltip_delay_ms(), nsITimer::TYPE_ONE_SHOT, 1145 "ChromeTooltipListener::MouseMove"_ns, 1146 GetMainThreadSerialEventTarget()); 1147 if (NS_FAILED(rv)) { 1148 mPossibleTooltipNode = nullptr; 1149 NS_WARNING("Could not create a timer for tooltip tracking"); 1150 } 1151 } 1152 } else { 1153 mTooltipShownOnce = true; 1154 return HideTooltip(); 1155 } 1156 1157 return NS_OK; 1158 } 1159 1160 // Tell the registered chrome that they should show the tooltip. 1161 NS_IMETHODIMP 1162 ChromeTooltipListener::ShowTooltip(int32_t aInXCoords, int32_t aInYCoords, 1163 const nsAString& aInTipText, 1164 const nsAString& aTipDir) { 1165 nsresult rv = NS_OK; 1166 1167 // do the work to call the client 1168 nsCOMPtr<nsITooltipListener> tooltipListener( 1169 do_QueryInterface(mWebBrowserChrome)); 1170 if (tooltipListener) { 1171 rv = tooltipListener->OnShowTooltip(aInXCoords, aInYCoords, aInTipText, 1172 aTipDir); 1173 if (NS_SUCCEEDED(rv)) { 1174 mShowingTooltip = true; 1175 } 1176 } 1177 1178 return rv; 1179 } 1180 1181 // Tell the registered chrome that they should rollup the tooltip 1182 // NOTE: This routine is safe to call even if the popup is already closed. 1183 NS_IMETHODIMP 1184 ChromeTooltipListener::HideTooltip() { 1185 nsresult rv = NS_OK; 1186 1187 // shut down the relevant timers 1188 if (mTooltipTimer) { 1189 mTooltipTimer->Cancel(); 1190 mTooltipTimer = nullptr; 1191 // release tooltip target 1192 mPossibleTooltipNode = nullptr; 1193 mLastDocshell = nullptr; 1194 } 1195 1196 // if we're showing the tip, tell the chrome to hide it 1197 if (mShowingTooltip) { 1198 nsCOMPtr<nsITooltipListener> tooltipListener( 1199 do_QueryInterface(mWebBrowserChrome)); 1200 if (tooltipListener) { 1201 rv = tooltipListener->OnHideTooltip(); 1202 if (NS_SUCCEEDED(rv)) { 1203 mShowingTooltip = false; 1204 } 1205 } 1206 } 1207 1208 return rv; 1209 } 1210 1211 bool ChromeTooltipListener::WebProgressShowedTooltip( 1212 nsIWebProgress* aWebProgress) { 1213 nsCOMPtr<nsIDocShell> docshell = do_QueryInterface(aWebProgress); 1214 nsCOMPtr<nsIDocShell> lastUsed = do_QueryReferent(mLastDocshell); 1215 while (lastUsed) { 1216 if (lastUsed == docshell) { 1217 return true; 1218 } 1219 // We can't use the docshell hierarchy here, because when the parent 1220 // docshell is navigated, the child docshell is disconnected (ie its 1221 // references to the parent are nulled out) despite it still being 1222 // alive here. So we use the document hierarchy instead: 1223 Document* document = lastUsed->GetDocument(); 1224 if (document) { 1225 document = document->GetInProcessParentDocument(); 1226 } 1227 if (!document) { 1228 break; 1229 } 1230 lastUsed = document->GetDocShell(); 1231 } 1232 return false; 1233 } 1234 1235 // A timer callback, fired when the mouse has hovered inside of a frame for the 1236 // appropriate amount of time. Getting to this point means that we should show 1237 // the tooltip, but only after we determine there is an appropriate TITLE 1238 // element. 1239 // 1240 // This relies on certain things being cached into the |aChromeTooltipListener| 1241 // object passed to us by the timer: 1242 // -- the x/y coordinates of the mouse (mMouseClientY, mMouseClientX) 1243 // -- the dom node the user hovered over (mPossibleTooltipNode) 1244 void ChromeTooltipListener::sTooltipCallback(nsITimer* aTimer, 1245 void* aChromeTooltipListener) { 1246 auto* self = static_cast<ChromeTooltipListener*>(aChromeTooltipListener); 1247 if (!self || !self->mPossibleTooltipNode) { 1248 return; 1249 } 1250 // release tooltip target once done, no matter what we do here. 1251 auto cleanup = MakeScopeExit([&] { self->mPossibleTooltipNode = nullptr; }); 1252 if (!self->mPossibleTooltipNode->IsInComposedDoc()) { 1253 return; 1254 } 1255 // Check that the document or its ancestors haven't been replaced. 1256 { 1257 Document* doc = self->mPossibleTooltipNode->OwnerDoc(); 1258 while (doc) { 1259 if (!doc->IsCurrentActiveDocument()) { 1260 return; 1261 } 1262 doc = doc->GetInProcessParentDocument(); 1263 } 1264 } 1265 1266 nsCOMPtr<nsIDocShell> docShell = 1267 do_GetInterface(static_cast<nsIWebBrowser*>(self->mWebBrowser)); 1268 if (!docShell || !docShell->GetBrowsingContext()->IsActive()) { 1269 return; 1270 } 1271 1272 // if there is text associated with the node, show the tip and fire 1273 // off a timer to auto-hide it. 1274 nsITooltipTextProvider* tooltipProvider = self->GetTooltipTextProvider(); 1275 if (!tooltipProvider) { 1276 return; 1277 } 1278 nsString tooltipText; 1279 nsString directionText; 1280 bool textFound = false; 1281 tooltipProvider->GetNodeText(self->mPossibleTooltipNode, 1282 getter_Copies(tooltipText), 1283 getter_Copies(directionText), &textFound); 1284 1285 if (textFound && (!self->mTooltipShownOnce || 1286 tooltipText != self->mLastShownTooltipText)) { 1287 // ShowTooltip expects screen-relative position. 1288 self->ShowTooltip(self->mMouseScreenPoint.x, self->mMouseScreenPoint.y, 1289 tooltipText, directionText); 1290 self->mLastShownTooltipText = std::move(tooltipText); 1291 self->mLastDocshell = do_GetWeakReference( 1292 self->mPossibleTooltipNode->OwnerDoc()->GetDocShell()); 1293 } 1294 }