tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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(&currentSize.width, &currentSize.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 }