tor-browser

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

KeyboardEvent.cpp (15723B)


      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/KeyboardEvent.h"
      8 
      9 #include "mozilla/BasicEvents.h"
     10 #include "mozilla/LookAndFeel.h"
     11 #include "mozilla/StaticPrefs_dom.h"
     12 #include "mozilla/TextEvents.h"
     13 #include "mozilla/dom/Document.h"
     14 #include "nsContentUtils.h"
     15 #include "nsIPrincipal.h"
     16 #include "nsRFPService.h"
     17 #include "prtime.h"
     18 
     19 namespace mozilla::dom {
     20 
     21 KeyboardEvent::KeyboardEvent(EventTarget* aOwner, nsPresContext* aPresContext,
     22                             WidgetKeyboardEvent* aEvent)
     23    : UIEvent(aOwner, aPresContext,
     24              aEvent ? aEvent
     25                     : new WidgetKeyboardEvent(false, eVoidEvent, nullptr)),
     26      mInitializedByJS(false),
     27      mInitializedByCtor(false),
     28      mInitializedWhichValue(0) {
     29  if (aEvent) {
     30    mEventIsInternal = false;
     31  } else {
     32    mEventIsInternal = true;
     33    mEvent->AsKeyboardEvent()->mKeyNameIndex = KEY_NAME_INDEX_USE_STRING;
     34  }
     35 }
     36 
     37 bool KeyboardEvent::IsMenuAccessKeyPressed() const {
     38  Modifiers mask = LookAndFeel::GetMenuAccessKeyModifiers();
     39  Modifiers modifiers = GetModifiersForMenuAccessKey();
     40  return mask != MODIFIER_SHIFT && (modifiers & mask) &&
     41         (modifiers & ~(mask | MODIFIER_SHIFT)) == 0;
     42 }
     43 
     44 static constexpr Modifiers kPossibleModifiersForAccessKey =
     45    MODIFIER_SHIFT | MODIFIER_CONTROL | MODIFIER_ALT | MODIFIER_META;
     46 
     47 Modifiers KeyboardEvent::GetModifiersForMenuAccessKey() const {
     48  const WidgetInputEvent* inputEvent = WidgetEventPtr()->AsInputEvent();
     49  MOZ_ASSERT(inputEvent);
     50  return inputEvent->mModifiers & kPossibleModifiersForAccessKey;
     51 }
     52 
     53 bool KeyboardEvent::AltKey(CallerType aCallerType) {
     54  bool altState = mEvent->AsKeyboardEvent()->IsAlt();
     55 
     56  if (!ShouldResistFingerprinting(aCallerType)) {
     57    return altState;
     58  }
     59 
     60  // We need to give a spoofed state for Alt key since it could be used as a
     61  // modifier key in certain keyboard layout. For example, the '@' key for
     62  // German keyboard for MAC is Alt+L.
     63  return GetSpoofedModifierStates(Modifier::MODIFIER_ALT, altState);
     64 }
     65 
     66 bool KeyboardEvent::CtrlKey(CallerType aCallerType) {
     67  // We don't spoof this key when privacy.resistFingerprinting
     68  // is enabled, because it is often used for command key
     69  // combinations in web apps.
     70  return mEvent->AsKeyboardEvent()->IsControl();
     71 }
     72 
     73 bool KeyboardEvent::ShiftKey(CallerType aCallerType) {
     74  bool shiftState = mEvent->AsKeyboardEvent()->IsShift();
     75 
     76  if (!ShouldResistFingerprinting(aCallerType)) {
     77    return shiftState;
     78  }
     79 
     80  return GetSpoofedModifierStates(Modifier::MODIFIER_SHIFT, shiftState);
     81 }
     82 
     83 bool KeyboardEvent::MetaKey() {
     84  // We don't spoof this key when privacy.resistFingerprinting
     85  // is enabled, because it is often used for command key
     86  // combinations in web apps.
     87  return mEvent->AsKeyboardEvent()->IsMeta();
     88 }
     89 
     90 bool KeyboardEvent::Repeat() { return mEvent->AsKeyboardEvent()->mIsRepeat; }
     91 
     92 bool KeyboardEvent::IsComposing() {
     93  return mEvent->AsKeyboardEvent()->mIsComposing;
     94 }
     95 
     96 void KeyboardEvent::GetKey(nsAString& aKeyName) const {
     97  mEvent->AsKeyboardEvent()->GetDOMKeyName(aKeyName);
     98 }
     99 
    100 void KeyboardEvent::GetCode(nsAString& aCodeName, CallerType aCallerType) {
    101  if (!ShouldResistFingerprinting(aCallerType)) {
    102    mEvent->AsKeyboardEvent()->GetDOMCodeName(aCodeName);
    103    return;
    104  }
    105 
    106  // When fingerprinting resistance is enabled, we will give a spoofed code
    107  // according to the content-language of the document.
    108  nsCOMPtr<Document> doc = GetDocument();
    109 
    110  nsRFPService::GetSpoofedCode(doc, mEvent->AsKeyboardEvent(), aCodeName);
    111 }
    112 
    113 void KeyboardEvent::GetInitDict(KeyboardEventInit& aParam) {
    114  GetKey(aParam.mKey);
    115  GetCode(aParam.mCode);
    116  aParam.mLocation = Location();
    117  aParam.mRepeat = Repeat();
    118  aParam.mIsComposing = IsComposing();
    119 
    120  // legacy attributes
    121  aParam.mKeyCode = KeyCode();
    122  aParam.mCharCode = CharCode();
    123  aParam.mWhich = Which();
    124 
    125  // modifiers from EventModifierInit
    126  aParam.mCtrlKey = CtrlKey();
    127  aParam.mShiftKey = ShiftKey();
    128  aParam.mAltKey = AltKey();
    129  aParam.mMetaKey = MetaKey();
    130 
    131  WidgetKeyboardEvent* internalEvent = mEvent->AsKeyboardEvent();
    132  aParam.mModifierAltGraph = internalEvent->IsAltGraph();
    133  aParam.mModifierCapsLock = internalEvent->IsCapsLocked();
    134  aParam.mModifierFn = internalEvent->IsFn();
    135  aParam.mModifierFnLock = internalEvent->IsFnLocked();
    136  aParam.mModifierNumLock = internalEvent->IsNumLocked();
    137  aParam.mModifierScrollLock = internalEvent->IsScrollLocked();
    138  aParam.mModifierSymbol = internalEvent->IsSymbol();
    139  aParam.mModifierSymbolLock = internalEvent->IsSymbolLocked();
    140 
    141  // EventInit
    142  aParam.mBubbles = internalEvent->mFlags.mBubbles;
    143  aParam.mCancelable = internalEvent->mFlags.mCancelable;
    144 }
    145 
    146 bool KeyboardEvent::ShouldUseSameValueForCharCodeAndKeyCode(
    147    const WidgetKeyboardEvent& aWidgetKeyboardEvent,
    148    CallerType aCallerType) const {
    149  // - If this event is initialized by JS, we don't need to return same value
    150  //   for keyCode and charCode since they can be initialized separately.
    151  // - If this is not a keypress event, we shouldn't return same value for
    152  //   keyCode and charCode.
    153  // - If we need to return legacy keyCode and charCode values for the web
    154  //   app due to in the blacklist.
    155  // - If this event is referred by default handler, i.e., the caller is
    156  //   system or this event is now in the system group, we don't need to use
    157  //   hack for web-compat.
    158  if (mInitializedByJS || aWidgetKeyboardEvent.mMessage != eKeyPress ||
    159      aWidgetKeyboardEvent.mUseLegacyKeyCodeAndCharCodeValues ||
    160      aCallerType == CallerType::System ||
    161      aWidgetKeyboardEvent.mFlags.mInSystemGroup) {
    162    return false;
    163  }
    164 
    165  MOZ_ASSERT(aCallerType == CallerType::NonSystem);
    166 
    167  return StaticPrefs::
    168      dom_keyboardevent_keypress_set_keycode_and_charcode_to_same_value();
    169 }
    170 
    171 uint32_t KeyboardEvent::CharCode(CallerType aCallerType) {
    172  WidgetKeyboardEvent* widgetKeyboardEvent = mEvent->AsKeyboardEvent();
    173  if (mInitializedByJS) {
    174    // If this is initialized by Ctor, we should return the initialized value.
    175    if (mInitializedByCtor) {
    176      return widgetKeyboardEvent->mCharCode;
    177    }
    178    // Otherwise, i.e., initialized by InitKey*Event(), we should return the
    179    // initialized value only when eKeyPress or eAccessKeyNotFound event.
    180    // Although this is odd, but our traditional behavior.
    181    return widgetKeyboardEvent->mMessage == eKeyPress ||
    182                   widgetKeyboardEvent->mMessage == eAccessKeyNotFound
    183               ? widgetKeyboardEvent->mCharCode
    184               : 0;
    185  }
    186 
    187  // If the key is a function key, we should return the result of KeyCode()
    188  // even from CharCode().  Otherwise, i.e., the key may be a printable
    189  // key or actually a printable key, we should return the given charCode
    190  // value.
    191 
    192  if (widgetKeyboardEvent->mKeyNameIndex != KEY_NAME_INDEX_USE_STRING &&
    193      ShouldUseSameValueForCharCodeAndKeyCode(*widgetKeyboardEvent,
    194                                              aCallerType)) {
    195    return ComputeTraditionalKeyCode(*widgetKeyboardEvent, aCallerType);
    196  }
    197 
    198  return widgetKeyboardEvent->mCharCode;
    199 }
    200 
    201 uint32_t KeyboardEvent::KeyCode(CallerType aCallerType) {
    202  WidgetKeyboardEvent* widgetKeyboardEvent = mEvent->AsKeyboardEvent();
    203  if (mInitializedByJS) {
    204    // If this is initialized by Ctor, we should return the initialized value.
    205    if (mInitializedByCtor) {
    206      return widgetKeyboardEvent->mKeyCode;
    207    }
    208    // Otherwise, i.e., initialized by InitKey*Event(), we should return the
    209    // initialized value only when the event message is a valid keyboard event
    210    // message.  Although this is odd, but our traditional behavior.
    211    // NOTE: The fix of bug 1222285 changed the behavior temporarily if
    212    //       spoofing is enabled.  However, the behavior does not make sense
    213    //       since if the event is generated by JS, the behavior shouldn't
    214    //       be changed by whether spoofing is enabled or not.  Therefore,
    215    //       we take back the original behavior.
    216    return widgetKeyboardEvent->HasKeyEventMessage()
    217               ? widgetKeyboardEvent->mKeyCode
    218               : 0;
    219  }
    220 
    221  // If the key is not a function key, i.e., the key may be a printable key
    222  // or a function key mapped as a printable key, we should use charCode value
    223  // for keyCode value if this is a "keypress" event.
    224 
    225  if (widgetKeyboardEvent->mKeyNameIndex == KEY_NAME_INDEX_USE_STRING &&
    226      ShouldUseSameValueForCharCodeAndKeyCode(*widgetKeyboardEvent,
    227                                              aCallerType)) {
    228    return widgetKeyboardEvent->mCharCode;
    229  }
    230 
    231  return ComputeTraditionalKeyCode(*widgetKeyboardEvent, aCallerType);
    232 }
    233 
    234 uint32_t KeyboardEvent::ComputeTraditionalKeyCode(
    235    WidgetKeyboardEvent& aKeyboardEvent, CallerType aCallerType) {
    236  if (!ShouldResistFingerprinting(aCallerType)) {
    237    return aKeyboardEvent.mKeyCode;
    238  }
    239 
    240  // In Netscape style (i.e., traditional behavior of Gecko), the keyCode
    241  // should be zero if the char code is given.
    242  if ((mEvent->mMessage == eKeyPress ||
    243       mEvent->mMessage == eAccessKeyNotFound) &&
    244      aKeyboardEvent.mCharCode) {
    245    return 0;
    246  }
    247 
    248  // When fingerprinting resistance is enabled, we will give a spoofed keyCode
    249  // according to the content-language of the document.
    250  nsCOMPtr<Document> doc = GetDocument();
    251  uint32_t spoofedKeyCode;
    252 
    253  if (nsRFPService::GetSpoofedKeyCode(doc, &aKeyboardEvent, spoofedKeyCode)) {
    254    return spoofedKeyCode;
    255  }
    256 
    257  return 0;
    258 }
    259 
    260 uint32_t KeyboardEvent::Which(CallerType aCallerType) {
    261  // If this event is initialized with ctor, which can have independent value.
    262  if (mInitializedByCtor) {
    263    return mInitializedWhichValue;
    264  }
    265 
    266  switch (mEvent->mMessage) {
    267    case eKeyDown:
    268    case eKeyUp:
    269      return KeyCode(aCallerType);
    270    case eKeyPress:
    271      // Special case for 4xp bug 62878.  Try to make value of which
    272      // more closely mirror the values that 4.x gave for RETURN and BACKSPACE
    273      {
    274        uint32_t keyCode = mEvent->AsKeyboardEvent()->mKeyCode;
    275        if (keyCode == NS_VK_RETURN || keyCode == NS_VK_BACK) {
    276          return keyCode;
    277        }
    278        return CharCode();
    279      }
    280    default:
    281      break;
    282  }
    283 
    284  return 0;
    285 }
    286 
    287 uint32_t KeyboardEvent::Location() {
    288  return mEvent->AsKeyboardEvent()->mLocation;
    289 }
    290 
    291 // static
    292 already_AddRefed<KeyboardEvent> KeyboardEvent::ConstructorJS(
    293    const GlobalObject& aGlobal, const nsAString& aType,
    294    const KeyboardEventInit& aParam) {
    295  nsCOMPtr<EventTarget> target = do_QueryInterface(aGlobal.GetAsSupports());
    296  RefPtr<KeyboardEvent> newEvent = new KeyboardEvent(target, nullptr, nullptr);
    297  newEvent->InitWithKeyboardEventInit(target, aType, aParam);
    298 
    299  return newEvent.forget();
    300 }
    301 
    302 void KeyboardEvent::InitWithKeyboardEventInit(EventTarget* aOwner,
    303                                              const nsAString& aType,
    304                                              const KeyboardEventInit& aParam) {
    305  bool trusted = Init(aOwner);
    306  InitKeyEventJS(aType, aParam.mBubbles, aParam.mCancelable, aParam.mView,
    307                 false, false, false, false, aParam.mKeyCode, aParam.mCharCode);
    308  InitModifiers(aParam);
    309  SetTrusted(trusted);
    310  mDetail = aParam.mDetail;
    311  mInitializedByJS = true;
    312  mInitializedByCtor = true;
    313  mInitializedWhichValue = aParam.mWhich;
    314 
    315  WidgetKeyboardEvent* internalEvent = mEvent->AsKeyboardEvent();
    316  internalEvent->mLocation = aParam.mLocation;
    317  internalEvent->mIsRepeat = aParam.mRepeat;
    318  internalEvent->mIsComposing = aParam.mIsComposing;
    319  internalEvent->mKeyNameIndex =
    320      WidgetKeyboardEvent::GetKeyNameIndex(aParam.mKey);
    321  if (internalEvent->mKeyNameIndex == KEY_NAME_INDEX_USE_STRING) {
    322    internalEvent->mKeyValue = aParam.mKey;
    323  }
    324  internalEvent->mCodeNameIndex =
    325      WidgetKeyboardEvent::GetCodeNameIndex(aParam.mCode);
    326  if (internalEvent->mCodeNameIndex == CODE_NAME_INDEX_USE_STRING) {
    327    internalEvent->mCodeValue = aParam.mCode;
    328  }
    329 }
    330 
    331 // static
    332 bool KeyboardEvent::IsInitKeyEventAvailable(JSContext* aCx, JSObject*) {
    333  if (StaticPrefs::dom_keyboardevent_init_key_event_enabled()) {
    334    return true;
    335  }
    336  if (!StaticPrefs::dom_keyboardevent_init_key_event_enabled_in_addons()) {
    337    return false;
    338  }
    339  nsIPrincipal* principal = nsContentUtils::SubjectPrincipal(aCx);
    340  return principal && principal->GetIsAddonOrExpandedAddonPrincipal();
    341 }
    342 
    343 void KeyboardEvent::InitKeyEventJS(const nsAString& aType, bool aCanBubble,
    344                                   bool aCancelable, nsGlobalWindowInner* aView,
    345                                   bool aCtrlKey, bool aAltKey, bool aShiftKey,
    346                                   bool aMetaKey, uint32_t aKeyCode,
    347                                   uint32_t aCharCode) {
    348  NS_ENSURE_TRUE_VOID(!mEvent->mFlags.mIsBeingDispatched);
    349  mInitializedByJS = true;
    350  mInitializedByCtor = false;
    351 
    352  UIEvent::InitUIEvent(aType, aCanBubble, aCancelable, aView, 0);
    353 
    354  WidgetKeyboardEvent* keyEvent = mEvent->AsKeyboardEvent();
    355  keyEvent->InitBasicModifiers(aCtrlKey, aAltKey, aShiftKey, aMetaKey);
    356  keyEvent->mKeyCode = aKeyCode;
    357  keyEvent->mCharCode = aCharCode;
    358 }
    359 
    360 void KeyboardEvent::InitKeyboardEventJS(
    361    const nsAString& aType, bool aCanBubble, bool aCancelable,
    362    nsGlobalWindowInner* aView, const nsAString& aKey, uint32_t aLocation,
    363    bool aCtrlKey, bool aAltKey, bool aShiftKey, bool aMetaKey) {
    364  NS_ENSURE_TRUE_VOID(!mEvent->mFlags.mIsBeingDispatched);
    365  mInitializedByJS = true;
    366  mInitializedByCtor = false;
    367 
    368  UIEvent::InitUIEvent(aType, aCanBubble, aCancelable, aView, 0);
    369 
    370  WidgetKeyboardEvent* keyEvent = mEvent->AsKeyboardEvent();
    371  keyEvent->InitBasicModifiers(aCtrlKey, aAltKey, aShiftKey, aMetaKey);
    372  keyEvent->mLocation = aLocation;
    373  keyEvent->mKeyNameIndex = KEY_NAME_INDEX_USE_STRING;
    374  keyEvent->mKeyValue = aKey;
    375 }
    376 
    377 bool KeyboardEvent::ShouldResistFingerprinting(CallerType aCallerType) {
    378  // There are five situations we don't need to spoof this keyboard event.
    379  //   1. The pref privcy.resistFingerprinting' is false, we fast return here.
    380  //   2. This event is initialized by scripts.
    381  //   3. This event is from Numpad.
    382  //   4. This event is in the system group.
    383  //   5. The caller type is system.
    384  if (!nsContentUtils::ShouldResistFingerprinting("Efficiency Check",
    385                                                  RFPTarget::KeyboardEvents) ||
    386      mInitializedByJS || aCallerType == CallerType::System ||
    387      mEvent->mFlags.mInSystemGroup ||
    388      mEvent->AsKeyboardEvent()->mLocation ==
    389          KeyboardEvent_Binding::DOM_KEY_LOCATION_NUMPAD) {
    390    return false;
    391  }
    392 
    393  nsCOMPtr<Document> doc = GetDocument();
    394  // We've checked the pref above, so use true as fallback if doc is null.
    395  return doc ? doc->ShouldResistFingerprinting(RFPTarget::KeyboardEvents)
    396             : true;
    397 }
    398 
    399 bool KeyboardEvent::GetSpoofedModifierStates(const Modifiers aModifierKey,
    400                                             const bool aRawModifierState) {
    401  bool spoofedState;
    402  nsCOMPtr<Document> doc = GetDocument();
    403 
    404  if (nsRFPService::GetSpoofedModifierStates(doc, mEvent->AsKeyboardEvent(),
    405                                             aModifierKey, spoofedState)) {
    406    return spoofedState;
    407  }
    408 
    409  return aRawModifierState;
    410 }
    411 
    412 }  // namespace mozilla::dom
    413 
    414 using namespace mozilla;
    415 using namespace mozilla::dom;
    416 
    417 already_AddRefed<KeyboardEvent> NS_NewDOMKeyboardEvent(
    418    EventTarget* aOwner, nsPresContext* aPresContext,
    419    WidgetKeyboardEvent* aEvent) {
    420  RefPtr<KeyboardEvent> it = new KeyboardEvent(aOwner, aPresContext, aEvent);
    421  return it.forget();
    422 }