LCOV - code coverage report
Current view: top level - gfx/layers/apz/src - FocusTarget.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 27 72 37.5 %
Date: 2018-08-07 16:35:00 Functions: 0 0 -
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13-14-ga5dd952