tor-browser

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

TouchEvent.cpp (12454B)


      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/dom/TouchEvent.h"
      8 
      9 #include "gfxPlatform.h"
     10 #include "mozilla/BasePrincipal.h"
     11 #include "mozilla/LookAndFeel.h"
     12 #include "mozilla/Preferences.h"
     13 #include "mozilla/StaticPrefs_dom.h"
     14 #include "mozilla/TouchEvents.h"
     15 #include "mozilla/dom/Navigator.h"
     16 #include "mozilla/dom/Touch.h"
     17 #include "mozilla/dom/TouchListBinding.h"
     18 #include "nsContentUtils.h"
     19 #include "nsExceptionHandler.h"
     20 #include "nsIDocShell.h"
     21 
     22 namespace mozilla::dom {
     23 
     24 /******************************************************************************
     25 * TouchList
     26 *****************************************************************************/
     27 
     28 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TouchList)
     29  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
     30  NS_INTERFACE_MAP_ENTRY(nsISupports)
     31 NS_INTERFACE_MAP_END
     32 
     33 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TouchList, mParent, mPoints)
     34 
     35 NS_IMPL_CYCLE_COLLECTING_ADDREF(TouchList)
     36 NS_IMPL_CYCLE_COLLECTING_RELEASE(TouchList)
     37 
     38 JSObject* TouchList::WrapObject(JSContext* aCx,
     39                                JS::Handle<JSObject*> aGivenProto) {
     40  return TouchList_Binding::Wrap(aCx, this, aGivenProto);
     41 }
     42 
     43 // static
     44 bool TouchList::PrefEnabled(JSContext* aCx, JSObject* aGlobal) {
     45  return TouchEvent::PrefEnabled(aCx, aGlobal);
     46 }
     47 
     48 /******************************************************************************
     49 * TouchEvent
     50 *****************************************************************************/
     51 
     52 TouchEvent::TouchEvent(EventTarget* aOwner, nsPresContext* aPresContext,
     53                       WidgetTouchEvent* aEvent)
     54    : UIEvent(
     55          aOwner, aPresContext,
     56          aEvent ? aEvent : new WidgetTouchEvent(false, eVoidEvent, nullptr)) {
     57  if (aEvent) {
     58    mEventIsInternal = false;
     59 
     60    for (uint32_t i = 0; i < aEvent->mTouches.Length(); ++i) {
     61      Touch* touch = aEvent->mTouches[i];
     62      touch->InitializePoints(mPresContext, aEvent);
     63    }
     64  } else {
     65    mEventIsInternal = true;
     66  }
     67 }
     68 
     69 NS_IMPL_CYCLE_COLLECTION_INHERITED(TouchEvent, UIEvent,
     70                                   mEvent->AsTouchEvent()->mTouches, mTouches,
     71                                   mTargetTouches, mChangedTouches)
     72 
     73 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TouchEvent)
     74 NS_INTERFACE_MAP_END_INHERITING(UIEvent)
     75 
     76 NS_IMPL_ADDREF_INHERITED(TouchEvent, UIEvent)
     77 NS_IMPL_RELEASE_INHERITED(TouchEvent, UIEvent)
     78 
     79 void TouchEvent::InitTouchEvent(const nsAString& aType, bool aCanBubble,
     80                                bool aCancelable, nsGlobalWindowInner* aView,
     81                                int32_t aDetail, bool aCtrlKey, bool aAltKey,
     82                                bool aShiftKey, bool aMetaKey,
     83                                TouchList* aTouches, TouchList* aTargetTouches,
     84                                TouchList* aChangedTouches) {
     85  NS_ENSURE_TRUE_VOID(!mEvent->mFlags.mIsBeingDispatched);
     86 
     87  UIEvent::InitUIEvent(aType, aCanBubble, aCancelable, aView, aDetail);
     88  mEvent->AsInputEvent()->InitBasicModifiers(aCtrlKey, aAltKey, aShiftKey,
     89                                             aMetaKey);
     90 
     91  mEvent->AsTouchEvent()->mTouches.Clear();
     92 
     93  // To support touch.target retargeting also when the event is
     94  // created by JS, we need to copy Touch objects to the widget event.
     95  // In order to not affect targetTouches, we don't check duplicates in that
     96  // list.
     97  mTargetTouches = aTargetTouches;
     98  AssignTouchesToWidgetEvent(mTargetTouches, false);
     99  mTouches = aTouches;
    100  AssignTouchesToWidgetEvent(mTouches, true);
    101  mChangedTouches = aChangedTouches;
    102  AssignTouchesToWidgetEvent(mChangedTouches, true);
    103 }
    104 
    105 void TouchEvent::AssignTouchesToWidgetEvent(TouchList* aList,
    106                                            bool aCheckDuplicates) {
    107  if (!aList) {
    108    return;
    109  }
    110  WidgetTouchEvent* widgetTouchEvent = mEvent->AsTouchEvent();
    111  for (uint32_t i = 0; i < aList->Length(); ++i) {
    112    Touch* touch = aList->Item(i);
    113    if (touch &&
    114        (!aCheckDuplicates || !widgetTouchEvent->mTouches.Contains(touch))) {
    115      widgetTouchEvent->mTouches.AppendElement(touch);
    116    }
    117  }
    118 }
    119 
    120 TouchList* TouchEvent::Touches() {
    121  if (!mTouches) {
    122    WidgetTouchEvent* touchEvent = mEvent->AsTouchEvent();
    123    if (mEvent->mMessage == eTouchEnd || mEvent->mMessage == eTouchCancel) {
    124      // for touchend events, remove any changed touches from mTouches
    125      WidgetTouchEvent::AutoTouchArray unchangedTouches;
    126      const WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
    127      for (uint32_t i = 0; i < touches.Length(); ++i) {
    128        if (!touches[i]->mChanged) {
    129          unchangedTouches.AppendElement(touches[i]);
    130        }
    131      }
    132      mTouches = new TouchList(ToSupports(this), unchangedTouches);
    133    } else {
    134      mTouches = new TouchList(ToSupports(this), touchEvent->mTouches);
    135    }
    136  }
    137  return mTouches;
    138 }
    139 
    140 TouchList* TouchEvent::TargetTouches() {
    141  if (!mTargetTouches || !mTargetTouches->Length()) {
    142    WidgetTouchEvent* touchEvent = mEvent->AsTouchEvent();
    143    if (!mTargetTouches) {
    144      mTargetTouches = new TouchList(ToSupports(this));
    145    }
    146    const WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
    147    for (uint32_t i = 0; i < touches.Length(); ++i) {
    148      // for touchend/cancel events, don't append to the target list if this is
    149      // a touch that is ending
    150      if ((mEvent->mMessage != eTouchEnd && mEvent->mMessage != eTouchCancel) ||
    151          !touches[i]->mChanged) {
    152        bool equalTarget = touches[i]->mTarget == mEvent->mTarget;
    153        if (!equalTarget) {
    154          // Need to still check if we're inside native anonymous content
    155          // and the non-NAC target would be the same.
    156          nsIContent* touchTarget =
    157              nsIContent::FromEventTargetOrNull(touches[i]->mTarget);
    158          nsIContent* eventTarget =
    159              nsIContent::FromEventTargetOrNull(mEvent->mTarget);
    160          equalTarget = touchTarget && eventTarget &&
    161                        touchTarget->FindFirstNonChromeOnlyAccessContent() ==
    162                            eventTarget->FindFirstNonChromeOnlyAccessContent();
    163        }
    164        if (equalTarget) {
    165          mTargetTouches->Append(touches[i]);
    166        }
    167      }
    168    }
    169  }
    170  return mTargetTouches;
    171 }
    172 
    173 TouchList* TouchEvent::ChangedTouches() {
    174  if (!mChangedTouches) {
    175    WidgetTouchEvent::AutoTouchArray changedTouches;
    176    WidgetTouchEvent* touchEvent = mEvent->AsTouchEvent();
    177    const WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
    178    for (uint32_t i = 0; i < touches.Length(); ++i) {
    179      if (touches[i]->mChanged) {
    180        changedTouches.AppendElement(touches[i]);
    181      }
    182    }
    183    mChangedTouches = new TouchList(ToSupports(this), changedTouches);
    184  }
    185  return mChangedTouches;
    186 }
    187 
    188 // static
    189 bool TouchEvent::PrefEnabled(JSContext* aCx, JSObject* aGlobal) {
    190  nsIDocShell* docShell = nullptr;
    191  if (aGlobal) {
    192    nsGlobalWindowInner* win = xpc::WindowOrNull(aGlobal);
    193    if (win) {
    194      docShell = win->GetDocShell();
    195    }
    196  }
    197  return PrefEnabled(docShell);
    198 }
    199 
    200 static bool PlatformSupportsTouch() {
    201  // Touch events are only actually supported if APZ is enabled. If APZ is
    202  // disabled globally, we can check that once and incorporate that into the
    203  // cached state. If APZ is enabled, we need to further check based on the
    204  // widget, which we do in PrefEnabled (and don't cache that result).
    205  static bool sIsTouchDeviceSupportPresent =
    206      !!LookAndFeel::GetInt(LookAndFeel::IntID::TouchDeviceSupportPresent) &&
    207      gfxPlatform::AsyncPanZoomEnabled();
    208 
    209  return sIsTouchDeviceSupportPresent;
    210 }
    211 
    212 // static
    213 bool TouchEvent::PrefEnabled(nsIDocShell* aDocShell) {
    214  MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
    215 
    216  auto touchEventsOverride = mozilla::dom::TouchEventsOverride::None;
    217  if (aDocShell) {
    218    if (BrowsingContext* bc = aDocShell->GetBrowsingContext()) {
    219      touchEventsOverride = bc->TouchEventsOverride();
    220    }
    221  }
    222 
    223  bool enabled = false;
    224  if (touchEventsOverride == mozilla::dom::TouchEventsOverride::Enabled) {
    225    enabled = true;
    226  } else if (touchEventsOverride ==
    227             mozilla::dom::TouchEventsOverride::Disabled) {
    228    enabled = false;
    229  } else if (nsContentUtils::ShouldResistFingerprinting(
    230                 aDocShell, RFPTarget::PointerEvents)) {
    231 #ifdef MOZ_WIDGET_COCOA
    232    enabled = false;
    233 #else
    234    enabled = true;
    235 #endif
    236  } else {
    237    const int32_t prefValue = StaticPrefs::dom_w3c_touch_events_enabled();
    238    if (prefValue == 2) {
    239      enabled = PlatformSupportsTouch();
    240 
    241      static bool firstTime = true;
    242      // The touch screen data seems to be inaccurate in the parent process,
    243      // and we really need the crash annotation in child processes.
    244      if (firstTime && !XRE_IsParentProcess()) {
    245        CrashReporter::RecordAnnotationBool(
    246            CrashReporter::Annotation::HasDeviceTouchScreen, enabled);
    247        firstTime = false;
    248      }
    249 
    250 #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
    251      if (enabled && aDocShell) {
    252        // APZ might be disabled on this particular widget, in which case
    253        // TouchEvent support will also be disabled. Try to detect that.
    254        if (RefPtr<nsPresContext> pc = aDocShell->GetPresContext()) {
    255          if (nsCOMPtr<nsIWidget> widget = pc->GetRootWidget()) {
    256            enabled &= widget->AsyncPanZoomEnabled();
    257          }
    258        }
    259      }
    260 #endif
    261    } else {
    262      enabled = !!prefValue;
    263    }
    264  }
    265 
    266  if (enabled) {
    267    nsContentUtils::InitializeTouchEventTable();
    268  }
    269  return enabled;
    270 }
    271 
    272 // static
    273 bool TouchEvent::LegacyAPIEnabled(JSContext* aCx, JSObject* aGlobal) {
    274  nsIPrincipal* principal = nsContentUtils::SubjectPrincipal(aCx);
    275  bool isSystem = principal && principal->IsSystemPrincipal();
    276 
    277  nsIDocShell* docShell = nullptr;
    278  if (aGlobal) {
    279    nsGlobalWindowInner* win = xpc::WindowOrNull(aGlobal);
    280    if (win) {
    281      docShell = win->GetDocShell();
    282    }
    283  }
    284  return LegacyAPIEnabled(docShell, isSystem);
    285 }
    286 
    287 // static
    288 bool TouchEvent::LegacyAPIEnabled(nsIDocShell* aDocShell,
    289                                  bool aCallerIsSystem) {
    290  return (aCallerIsSystem ||
    291          StaticPrefs::dom_w3c_touch_events_legacy_apis_enabled() ||
    292          (aDocShell && aDocShell->GetBrowsingContext() &&
    293           aDocShell->GetBrowsingContext()->TouchEventsOverride() ==
    294               mozilla::dom::TouchEventsOverride::Enabled)) &&
    295         PrefEnabled(aDocShell);
    296 }
    297 
    298 // static
    299 already_AddRefed<TouchEvent> TouchEvent::Constructor(
    300    const GlobalObject& aGlobal, const nsAString& aType,
    301    const TouchEventInit& aParam) {
    302  nsCOMPtr<EventTarget> t = do_QueryInterface(aGlobal.GetAsSupports());
    303  RefPtr<TouchEvent> e = new TouchEvent(t, nullptr, nullptr);
    304  bool trusted = e->Init(t);
    305  RefPtr<TouchList> touches = e->CopyTouches(aParam.mTouches);
    306  RefPtr<TouchList> targetTouches = e->CopyTouches(aParam.mTargetTouches);
    307  RefPtr<TouchList> changedTouches = e->CopyTouches(aParam.mChangedTouches);
    308  e->InitTouchEvent(aType, aParam.mBubbles, aParam.mCancelable, aParam.mView,
    309                    aParam.mDetail, aParam.mCtrlKey, aParam.mAltKey,
    310                    aParam.mShiftKey, aParam.mMetaKey, touches, targetTouches,
    311                    changedTouches);
    312  e->SetTrusted(trusted);
    313  e->SetComposed(aParam.mComposed);
    314  return e.forget();
    315 }
    316 
    317 already_AddRefed<TouchList> TouchEvent::CopyTouches(
    318    const Sequence<OwningNonNull<Touch>>& aTouches) {
    319  RefPtr<TouchList> list = new TouchList(GetParentObject());
    320  size_t len = aTouches.Length();
    321  for (size_t i = 0; i < len; ++i) {
    322    list->Append(aTouches[i]);
    323  }
    324  return list.forget();
    325 }
    326 
    327 bool TouchEvent::AltKey() { return mEvent->AsTouchEvent()->IsAlt(); }
    328 
    329 bool TouchEvent::MetaKey() { return mEvent->AsTouchEvent()->IsMeta(); }
    330 
    331 bool TouchEvent::CtrlKey() { return mEvent->AsTouchEvent()->IsControl(); }
    332 
    333 bool TouchEvent::ShiftKey() { return mEvent->AsTouchEvent()->IsShift(); }
    334 
    335 }  // namespace mozilla::dom
    336 
    337 using namespace mozilla;
    338 using namespace mozilla::dom;
    339 
    340 already_AddRefed<TouchEvent> NS_NewDOMTouchEvent(EventTarget* aOwner,
    341                                                 nsPresContext* aPresContext,
    342                                                 WidgetTouchEvent* aEvent) {
    343  RefPtr<TouchEvent> it = new TouchEvent(aOwner, aPresContext, aEvent);
    344  return it.forget();
    345 }