tor-browser

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

FocusTarget.cpp (8137B)


      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 #include "mozilla/layers/FocusTarget.h"
      8 #include "mozilla/dom/BrowserBridgeChild.h"  // for BrowserBridgeChild
      9 #include "mozilla/dom/EventTarget.h"         // for EventTarget
     10 #include "mozilla/dom/RemoteBrowser.h"       // For RemoteBrowser
     11 #include "mozilla/EventDispatcher.h"         // for EventDispatcher
     12 #include "mozilla/PresShell.h"               // For PresShell
     13 #include "mozilla/StaticPrefs_apz.h"
     14 #include "nsIContentInlines.h"  // for nsINode::IsEditable()
     15 #include "nsLayoutUtils.h"      // for nsLayoutUtils
     16 
     17 static mozilla::LazyLogModule sApzFtgLog("apz.focustarget");
     18 #define FT_LOG(...) MOZ_LOG(sApzFtgLog, LogLevel::Debug, (__VA_ARGS__))
     19 
     20 using namespace mozilla::dom;
     21 using namespace mozilla::layout;
     22 
     23 namespace mozilla {
     24 namespace layers {
     25 
     26 static PresShell* GetRetargetEventPresShell(PresShell* aRootPresShell) {
     27  MOZ_ASSERT(aRootPresShell);
     28 
     29  // Use the last focused window in this PresShell and its
     30  // associated PresShell
     31  nsCOMPtr<nsPIDOMWindowOuter> window =
     32      aRootPresShell->GetFocusedDOMWindowInOurWindow();
     33  if (!window) {
     34    return nullptr;
     35  }
     36 
     37  RefPtr<Document> retargetEventDoc = window->GetExtantDoc();
     38  if (!retargetEventDoc) {
     39    return nullptr;
     40  }
     41 
     42  return retargetEventDoc->GetPresShell();
     43 }
     44 
     45 // _BOUNDARY because Dispatch() with `targets` must not handle the event.
     46 MOZ_CAN_RUN_SCRIPT_BOUNDARY static bool HasListenersForKeyEvents(
     47    nsIContent* aContent) {
     48  if (!aContent) {
     49    return false;
     50  }
     51 
     52  WidgetEvent event(true, eVoidEvent);
     53  nsTArray<EventTarget*> targets;
     54  nsresult rv = EventDispatcher::Dispatch(aContent, nullptr, &event, nullptr,
     55                                          nullptr, nullptr, &targets);
     56  NS_ENSURE_SUCCESS(rv, false);
     57  for (size_t i = 0; i < targets.Length(); i++) {
     58    if (targets[i]->HasNonSystemGroupListenersForUntrustedKeyEvents()) {
     59      return true;
     60    }
     61  }
     62  return false;
     63 }
     64 
     65 // _BOUNDARY because Dispatch() with `targets` must not handle the event.
     66 MOZ_CAN_RUN_SCRIPT_BOUNDARY static bool HasListenersForNonPassiveKeyEvents(
     67    nsIContent* aContent) {
     68  if (!aContent) {
     69    return false;
     70  }
     71 
     72  WidgetEvent event(true, eVoidEvent);
     73  nsTArray<EventTarget*> targets;
     74  nsresult rv = EventDispatcher::Dispatch(aContent, nullptr, &event, nullptr,
     75                                          nullptr, nullptr, &targets);
     76  NS_ENSURE_SUCCESS(rv, false);
     77  for (size_t i = 0; i < targets.Length(); i++) {
     78    if (targets[i]
     79            ->HasNonPassiveNonSystemGroupListenersForUntrustedKeyEvents()) {
     80      return true;
     81    }
     82  }
     83  return false;
     84 }
     85 
     86 static bool IsEditableNode(nsINode* aNode) {
     87  return aNode && aNode->IsEditable();
     88 }
     89 
     90 FocusTarget::FocusTarget()
     91    : mSequenceNumber(0),
     92      mFocusHasKeyEventListeners(false),
     93      mData(AsVariant(NoFocusTarget())) {}
     94 
     95 FocusTarget::FocusTarget(PresShell* aRootPresShell,
     96                         uint64_t aFocusSequenceNumber)
     97    : mSequenceNumber(aFocusSequenceNumber),
     98      mFocusHasKeyEventListeners(false),
     99      mData(AsVariant(NoFocusTarget())) {
    100  MOZ_ASSERT(aRootPresShell);
    101  MOZ_ASSERT(NS_IsMainThread());
    102 
    103  // Key events can be retargeted to a child PresShell when there is an iframe
    104  RefPtr<PresShell> presShell = GetRetargetEventPresShell(aRootPresShell);
    105 
    106  if (!presShell) {
    107    FT_LOG("Creating nil target with seq=%" PRIu64
    108           " (can't find retargeted presshell)\n",
    109           aFocusSequenceNumber);
    110 
    111    return;
    112  }
    113 
    114  RefPtr<Document> document = presShell->GetDocument();
    115  if (!document) {
    116    FT_LOG("Creating nil target with seq=%" PRIu64 " (no document)\n",
    117           aFocusSequenceNumber);
    118 
    119    return;
    120  }
    121 
    122  // Find the focused content and use it to determine whether there are key
    123  // event listeners or whether key events will be targeted at a different
    124  // process through a remote browser.
    125  nsCOMPtr<nsIContent> focusedContent =
    126      presShell->GetFocusedContentInOurWindow();
    127  nsCOMPtr<nsIContent> keyEventTarget = focusedContent;
    128 
    129  // If there is no focused element then event dispatch goes to the body of
    130  // the page if it exists or the root element.
    131  if (!keyEventTarget) {
    132    keyEventTarget = document->GetUnfocusedKeyEventTarget();
    133  }
    134 
    135  // Check if there are key event listeners that could prevent default or change
    136  // the focus or selection of the page.
    137  if (StaticPrefs::apz_keyboard_passive_listeners()) {
    138    mFocusHasKeyEventListeners =
    139        HasListenersForNonPassiveKeyEvents(keyEventTarget.get());
    140  } else {
    141    mFocusHasKeyEventListeners = HasListenersForKeyEvents(keyEventTarget.get());
    142  }
    143 
    144  // Check if the key event target is content editable or if the document
    145  // is in design mode.
    146  if (IsEditableNode(keyEventTarget) || IsEditableNode(document)) {
    147    FT_LOG("Creating nil target with seq=%" PRIu64
    148           ", kl=%d (disabling for editable node)\n",
    149           aFocusSequenceNumber, static_cast<int>(mFocusHasKeyEventListeners));
    150 
    151    return;
    152  }
    153 
    154  // Check if the key event target is a remote browser
    155  if (RemoteBrowser* remoteBrowser = RemoteBrowser::GetFrom(keyEventTarget)) {
    156    LayersId layersId = remoteBrowser->GetLayersId();
    157 
    158    // The globally focused element for scrolling is in a remote layer tree
    159    if (layersId.IsValid()) {
    160      FT_LOG("Creating reflayer target with seq=%" PRIu64 ", kl=%d, lt=%" PRIu64
    161             "\n",
    162             aFocusSequenceNumber, mFocusHasKeyEventListeners, layersId.mId);
    163 
    164      mData = AsVariant<LayersId>(std::move(layersId));
    165      return;
    166    }
    167 
    168    FT_LOG("Creating nil target with seq=%" PRIu64
    169           ", kl=%d (remote browser missing layers id)\n",
    170           aFocusSequenceNumber, mFocusHasKeyEventListeners);
    171 
    172    return;
    173  }
    174 
    175  // The content to scroll is either the focused element or the focus node of
    176  // the selection. It's difficult to determine if an element is an interactive
    177  // element requiring async keyboard scrolling to be disabled. So we only
    178  // allow async key scrolling based on the selection, which doesn't have
    179  // this problem and is more common.
    180  if (focusedContent) {
    181    FT_LOG("Creating nil target with seq=%" PRIu64
    182           ", kl=%d (disabling for focusing an element)\n",
    183           aFocusSequenceNumber, mFocusHasKeyEventListeners);
    184 
    185    return;
    186  }
    187 
    188  nsCOMPtr<nsIContent> selectedContent =
    189      presShell->GetSelectedContentForScrolling();
    190 
    191  // Gather the scroll container frames that would be scrolled in each direction
    192  // for this scroll target
    193  ScrollContainerFrame* horizontal =
    194      presShell->GetScrollContainerFrameToScrollForContent(
    195          selectedContent.get(), HorizontalScrollDirection);
    196  ScrollContainerFrame* vertical =
    197      presShell->GetScrollContainerFrameToScrollForContent(
    198          selectedContent.get(), VerticalScrollDirection);
    199 
    200  // We might have the globally focused element for scrolling. Gather a ViewID
    201  // for the horizontal and vertical scroll targets of this element.
    202  ScrollTargets target;
    203  target.mHorizontal = nsLayoutUtils::FindIDForScrollContainerFrame(horizontal);
    204  target.mVertical = nsLayoutUtils::FindIDForScrollContainerFrame(vertical);
    205  mData = AsVariant(target);
    206 
    207  FT_LOG("Creating scroll target with seq=%" PRIu64 ", kl=%d, h=%" PRIu64
    208         ", v=%" PRIu64 "\n",
    209         aFocusSequenceNumber, mFocusHasKeyEventListeners, target.mHorizontal,
    210         target.mVertical);
    211 }
    212 
    213 bool FocusTarget::operator==(const FocusTarget& aRhs) const {
    214  return mSequenceNumber == aRhs.mSequenceNumber &&
    215         mFocusHasKeyEventListeners == aRhs.mFocusHasKeyEventListeners &&
    216         mData == aRhs.mData;
    217 }
    218 
    219 const char* FocusTarget::Type() const {
    220  if (mData.is<LayersId>()) {
    221    return "LayersId";
    222  }
    223  if (mData.is<ScrollTargets>()) {
    224    return "ScrollTargets";
    225  }
    226  if (mData.is<NoFocusTarget>()) {
    227    return "NoFocusTarget";
    228  }
    229  return "<unknown>";
    230 }
    231 
    232 }  // namespace layers
    233 }  // namespace mozilla