tor-browser

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

KeyEventHandler.cpp (21028B)


      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 "KeyEventHandler.h"
      8 
      9 #include "ErrorList.h"
     10 #include "mozilla/BasicEvents.h"
     11 #include "mozilla/JSEventHandler.h"
     12 #include "mozilla/LookAndFeel.h"
     13 #include "mozilla/Preferences.h"
     14 #include "mozilla/TextEvents.h"
     15 #include "mozilla/dom/Document.h"
     16 #include "mozilla/dom/Element.h"
     17 #include "mozilla/dom/Event.h"
     18 #include "mozilla/dom/EventHandlerBinding.h"
     19 #include "mozilla/dom/HTMLInputElement.h"
     20 #include "mozilla/dom/HTMLTextAreaElement.h"
     21 #include "mozilla/dom/KeyboardEvent.h"
     22 #include "mozilla/dom/KeyboardEventBinding.h"
     23 #include "mozilla/dom/ScriptSettings.h"
     24 #include "mozilla/layers/KeyboardMap.h"
     25 #include "nsAtom.h"
     26 #include "nsCOMPtr.h"
     27 #include "nsCRT.h"
     28 #include "nsContentUtils.h"
     29 #include "nsDOMCID.h"
     30 #include "nsFocusManager.h"
     31 #include "nsGkAtoms.h"
     32 #include "nsGlobalWindowCommands.h"
     33 #include "nsIContent.h"
     34 #include "nsIController.h"
     35 #include "nsIControllers.h"
     36 #include "nsIFormControl.h"
     37 #include "nsIScriptError.h"
     38 #include "nsIWeakReferenceUtils.h"
     39 #include "nsJSUtils.h"
     40 #include "nsNameSpaceManager.h"
     41 #include "nsPIDOMWindow.h"
     42 #include "nsPIWindowRoot.h"
     43 #include "nsQueryObject.h"
     44 #include "nsReadableUtils.h"
     45 #include "nsString.h"
     46 #include "nsUnicharUtils.h"
     47 #include "nsXULElement.h"
     48 #include "xpcpublic.h"
     49 
     50 namespace mozilla {
     51 
     52 using namespace mozilla::layers;
     53 
     54 uint32_t KeyEventHandler::gRefCnt = 0;
     55 
     56 const int32_t KeyEventHandler::cShift = (1 << 0);
     57 const int32_t KeyEventHandler::cAlt = (1 << 1);
     58 const int32_t KeyEventHandler::cControl = (1 << 2);
     59 const int32_t KeyEventHandler::cMeta = (1 << 3);
     60 
     61 const int32_t KeyEventHandler::cShiftMask = (1 << 5);
     62 const int32_t KeyEventHandler::cAltMask = (1 << 6);
     63 const int32_t KeyEventHandler::cControlMask = (1 << 7);
     64 const int32_t KeyEventHandler::cMetaMask = (1 << 8);
     65 
     66 const int32_t KeyEventHandler::cAllModifiers =
     67    cShiftMask | cAltMask | cControlMask | cMetaMask;
     68 
     69 KeyEventHandler::KeyEventHandler(dom::Element* aHandlerElement,
     70                                 ReservedKey aReserved)
     71    : mHandlerElement(nullptr),
     72      mIsXULKey(true),
     73      mReserved(aReserved),
     74      mNextHandler(nullptr) {
     75  Init();
     76 
     77  // Make sure our prototype is initialized.
     78  ConstructPrototype(aHandlerElement);
     79 }
     80 
     81 KeyEventHandler::KeyEventHandler(ShortcutKeyData* aKeyData)
     82    : mCommand(nullptr),
     83      mIsXULKey(false),
     84      mReserved(ReservedKey_False),
     85      mNextHandler(nullptr) {
     86  Init();
     87 
     88  ConstructPrototype(nullptr, aKeyData->event, aKeyData->command,
     89                     aKeyData->keycode, aKeyData->key, aKeyData->modifiers);
     90 }
     91 
     92 KeyEventHandler::~KeyEventHandler() {
     93  --gRefCnt;
     94  if (mIsXULKey) {
     95    NS_IF_RELEASE(mHandlerElement);
     96  } else if (mCommand) {
     97    free(mCommand);
     98  }
     99 
    100  // We own the next handler in the chain, so delete it now.
    101  NS_CONTENT_DELETE_LIST_MEMBER(KeyEventHandler, this, mNextHandler);
    102 }
    103 
    104 void KeyEventHandler::GetCommand(nsAString& aCommand) const {
    105  MOZ_ASSERT(aCommand.IsEmpty());
    106  if (mIsXULKey) {
    107    MOZ_ASSERT_UNREACHABLE("Not yet implemented");
    108    return;
    109  }
    110  if (mCommand) {
    111    aCommand.Assign(mCommand);
    112  }
    113 }
    114 
    115 bool KeyEventHandler::TryConvertToKeyboardShortcut(
    116    KeyboardShortcut* aOut) const {
    117  // Convert the event type
    118  KeyboardInput::KeyboardEventType eventType;
    119 
    120  if (mEventName == nsGkAtoms::keydown) {
    121    eventType = KeyboardInput::KEY_DOWN;
    122  } else if (mEventName == nsGkAtoms::keypress) {
    123    eventType = KeyboardInput::KEY_PRESS;
    124  } else if (mEventName == nsGkAtoms::keyup) {
    125    eventType = KeyboardInput::KEY_UP;
    126  } else {
    127    return false;
    128  }
    129 
    130  // Convert the modifiers
    131  Modifiers modifiersMask = GetModifiersMask();
    132  Modifiers modifiers = GetModifiers();
    133 
    134  // Mask away any bits that won't be compared
    135  modifiers &= modifiersMask;
    136 
    137  // Convert the keyCode or charCode
    138  uint32_t keyCode;
    139  uint32_t charCode;
    140 
    141  if (mMisc) {
    142    keyCode = 0;
    143    charCode = static_cast<uint32_t>(mDetail);
    144  } else {
    145    keyCode = static_cast<uint32_t>(mDetail);
    146    charCode = 0;
    147  }
    148 
    149  NS_LossyConvertUTF16toASCII commandText(mCommand);
    150  KeyboardScrollAction action;
    151  if (!nsGlobalWindowCommands::FindScrollCommand(commandText, &action)) {
    152    // This action doesn't represent a scroll so we need to create a dispatch
    153    // to content keyboard shortcut so APZ handles this command correctly
    154    *aOut = KeyboardShortcut(eventType, keyCode, charCode, modifiers,
    155                             modifiersMask);
    156    return true;
    157  }
    158 
    159  // This prototype is a command which represents a scroll action, so create
    160  // a keyboard shortcut to handle it
    161  *aOut = KeyboardShortcut(eventType, keyCode, charCode, modifiers,
    162                           modifiersMask, action);
    163  return true;
    164 }
    165 
    166 bool KeyEventHandler::KeyElementIsDisabled() const {
    167  RefPtr<dom::Element> keyElement = GetHandlerElement();
    168  return keyElement &&
    169         keyElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled,
    170                                 nsGkAtoms::_true, eCaseMatters);
    171 }
    172 
    173 already_AddRefed<dom::Element> KeyEventHandler::GetHandlerElement() const {
    174  if (mIsXULKey) {
    175    nsCOMPtr<dom::Element> element = do_QueryReferent(mHandlerElement);
    176    return element.forget();
    177  }
    178 
    179  return nullptr;
    180 }
    181 
    182 nsresult KeyEventHandler::ExecuteHandler(dom::EventTarget* aTarget,
    183                                         dom::Event* aEvent) {
    184  // In both cases the union should be defined.
    185  if (!mHandlerElement) {
    186    return NS_ERROR_FAILURE;
    187  }
    188 
    189  // XUL handlers and commands shouldn't be triggered by non-trusted
    190  // events.
    191  if (!aEvent->IsTrusted()) {
    192    return NS_OK;
    193  }
    194 
    195  if (mIsXULKey) {
    196    return DispatchXULKeyCommand(aEvent);
    197  }
    198 
    199  return DispatchXBLCommand(aTarget, aEvent);
    200 }
    201 
    202 nsresult KeyEventHandler::DispatchXBLCommand(dom::EventTarget* aTarget,
    203                                             dom::Event* aEvent) {
    204  // This is a special-case optimization to make command handling fast.
    205  // It isn't really a part of XBL, but it helps speed things up.
    206 
    207  if (aEvent) {
    208    // See if preventDefault has been set.  If so, don't execute.
    209    if (aEvent->DefaultPrevented()) {
    210      return NS_OK;
    211    }
    212    bool dispatchStopped = aEvent->IsDispatchStopped();
    213    if (dispatchStopped) {
    214      return NS_OK;
    215    }
    216  }
    217 
    218  // Instead of executing JS, let's get the controller for the bound
    219  // element and call doCommand on it.
    220  nsCOMPtr<nsIController> controller;
    221 
    222  nsCOMPtr<nsPIDOMWindowOuter> privateWindow;
    223  nsCOMPtr<nsPIWindowRoot> windowRoot =
    224      nsPIWindowRoot::FromEventTargetOrNull(aTarget);
    225  if (windowRoot) {
    226    privateWindow = windowRoot->GetWindow();
    227  } else {
    228    privateWindow = nsPIDOMWindowOuter::FromEventTargetOrNull(aTarget);
    229    if (!privateWindow) {
    230      nsCOMPtr<dom::Document> doc;
    231      // XXXbz sXBL/XBL2 issue -- this should be the "scope doc" or
    232      // something... whatever we use when wrapping DOM nodes
    233      // normally.  It's not clear that the owner doc is the right
    234      // thing.
    235      if (nsIContent* content = nsIContent::FromEventTargetOrNull(aTarget)) {
    236        doc = content->OwnerDoc();
    237      }
    238 
    239      if (!doc) {
    240        if (nsINode* node = nsINode::FromEventTargetOrNull(aTarget)) {
    241          if (node->IsDocument()) {
    242            doc = node->AsDocument();
    243          }
    244        }
    245      }
    246 
    247      if (!doc) {
    248        return NS_ERROR_FAILURE;
    249      }
    250 
    251      privateWindow = doc->GetWindow();
    252      if (!privateWindow) {
    253        return NS_ERROR_FAILURE;
    254      }
    255    }
    256 
    257    windowRoot = privateWindow->GetTopWindowRoot();
    258  }
    259 
    260  NS_LossyConvertUTF16toASCII command(mCommand);
    261  if (windowRoot) {
    262    // If user tries to do something, user must try to do it in visible window.
    263    // So, let's retrieve controller of visible window.
    264    windowRoot->GetControllerForCommand(command.get(), true,
    265                                        getter_AddRefs(controller));
    266  } else {
    267    controller =
    268        GetController(aTarget);  // We're attached to the receiver possibly.
    269  }
    270 
    271  // We are the default action for this command.
    272  // Stop any other default action from executing.
    273  aEvent->PreventDefault();
    274 
    275  if (mEventName == nsGkAtoms::keypress &&
    276      mDetail == dom::KeyboardEvent_Binding::DOM_VK_SPACE && mMisc == 1) {
    277    // get the focused element so that we can pageDown only at
    278    // certain times.
    279 
    280    nsCOMPtr<nsPIDOMWindowOuter> windowToCheck;
    281    if (windowRoot) {
    282      windowToCheck = windowRoot->GetWindow();
    283    } else {
    284      windowToCheck = privateWindow->GetPrivateRoot();
    285    }
    286 
    287    nsCOMPtr<nsIContent> focusedContent;
    288    if (windowToCheck) {
    289      nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
    290      focusedContent = nsFocusManager::GetFocusedDescendant(
    291          windowToCheck, nsFocusManager::eIncludeAllDescendants,
    292          getter_AddRefs(focusedWindow));
    293    }
    294 
    295    // If the focus is in an editable region, don't scroll.
    296    if (focusedContent && focusedContent->IsEditable()) {
    297      return NS_OK;
    298    }
    299 
    300    // If the focus is in a form control, don't scroll.
    301    for (nsIContent* c = focusedContent; c; c = c->GetParent()) {
    302      if (nsIFormControl::FromNode(c)) {
    303        return NS_OK;
    304      }
    305    }
    306  }
    307 
    308  if (controller) {
    309    controller->DoCommand(command.get());
    310  }
    311 
    312  return NS_OK;
    313 }
    314 
    315 nsresult KeyEventHandler::DispatchXULKeyCommand(dom::Event* aEvent) {
    316  nsCOMPtr<dom::Element> handlerElement = GetHandlerElement();
    317  NS_ENSURE_STATE(handlerElement);
    318  if (handlerElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled,
    319                                  nsGkAtoms::_true, eCaseMatters)) {
    320    // Don't dispatch command events for disabled keys.
    321    return NS_SUCCESS_DOM_NO_OPERATION;
    322  }
    323 
    324  aEvent->PreventDefault();
    325 
    326  // Copy the modifiers from the key event.
    327  RefPtr<dom::KeyboardEvent> domKeyboardEvent = aEvent->AsKeyboardEvent();
    328  if (!domKeyboardEvent) {
    329    NS_ERROR("Trying to execute a key handler for a non-key event!");
    330    return NS_ERROR_FAILURE;
    331  }
    332 
    333  // XXX We should use mozilla::Modifiers for supporting all modifiers.
    334 
    335  bool isAlt = domKeyboardEvent->AltKey();
    336  bool isControl = domKeyboardEvent->CtrlKey();
    337  bool isShift = domKeyboardEvent->ShiftKey();
    338  bool isMeta = domKeyboardEvent->MetaKey();
    339 
    340  nsContentUtils::DispatchXULCommand(handlerElement, true, nullptr, nullptr,
    341                                     isControl, isAlt, isShift, isMeta);
    342  return NS_OK;
    343 }
    344 
    345 Modifiers KeyEventHandler::GetModifiers() const {
    346  Modifiers modifiers = 0;
    347 
    348  if (mKeyMask & cMeta) {
    349    modifiers |= MODIFIER_META;
    350  }
    351  if (mKeyMask & cShift) {
    352    modifiers |= MODIFIER_SHIFT;
    353  }
    354  if (mKeyMask & cAlt) {
    355    modifiers |= MODIFIER_ALT;
    356  }
    357  if (mKeyMask & cControl) {
    358    modifiers |= MODIFIER_CONTROL;
    359  }
    360 
    361  return modifiers;
    362 }
    363 
    364 Modifiers KeyEventHandler::GetModifiersMask() const {
    365  Modifiers modifiersMask = 0;
    366 
    367  if (mKeyMask & cMetaMask) {
    368    modifiersMask |= MODIFIER_META;
    369  }
    370  if (mKeyMask & cShiftMask) {
    371    modifiersMask |= MODIFIER_SHIFT;
    372  }
    373  if (mKeyMask & cAltMask) {
    374    modifiersMask |= MODIFIER_ALT;
    375  }
    376  if (mKeyMask & cControlMask) {
    377    modifiersMask |= MODIFIER_CONTROL;
    378  }
    379 
    380  return modifiersMask;
    381 }
    382 
    383 already_AddRefed<nsIController> KeyEventHandler::GetController(
    384    dom::EventTarget* aTarget) {
    385  if (!aTarget) {
    386    return nullptr;
    387  }
    388 
    389  // XXX Fix this so there's a generic interface that describes controllers,
    390  // This code should have no special knowledge of what objects might have
    391  // controllers.
    392  nsCOMPtr<nsIControllers> controllers;
    393  if (nsIContent* targetContent = nsIContent::FromEventTarget(aTarget)) {
    394    if (auto* xulElement = nsXULElement::FromNode(targetContent)) {
    395      controllers = xulElement->GetExtantControllers();
    396    } else if (auto* htmlTextArea =
    397                   dom::HTMLTextAreaElement::FromNode(targetContent)) {
    398      htmlTextArea->GetControllers(getter_AddRefs(controllers));
    399    } else if (auto* htmlInput =
    400                   dom::HTMLInputElement::FromNode(targetContent)) {
    401      htmlInput->GetControllers(getter_AddRefs(controllers));
    402    }
    403  }
    404 
    405  if (!controllers) {
    406    if (nsCOMPtr<nsPIDOMWindowOuter> domWindow =
    407            nsPIDOMWindowOuter::FromEventTarget(aTarget)) {
    408      domWindow->GetControllers(getter_AddRefs(controllers));
    409    }
    410  }
    411 
    412  // Return the first controller.
    413  // XXX This code should be checking the command name and using supportscommand
    414  // and iscommandenabled.
    415  nsCOMPtr<nsIController> controller;
    416  if (controllers) {
    417    controllers->GetControllerAt(0, getter_AddRefs(controller));
    418  }
    419 
    420  return controller.forget();
    421 }
    422 
    423 bool KeyEventHandler::KeyEventMatched(
    424    dom::KeyboardEvent* aDomKeyboardEvent, uint32_t aCharCode,
    425    const IgnoreModifierState& aIgnoreModifierState) {
    426  if (mDetail != -1) {
    427    // Get the keycode or charcode of the key event.
    428    uint32_t code;
    429 
    430    if (mMisc) {
    431      if (aCharCode) {
    432        code = aCharCode;
    433      } else {
    434        code = aDomKeyboardEvent->CharCode();
    435      }
    436      if (IS_IN_BMP(code)) {
    437        code = ToLowerCase(char16_t(code));
    438      }
    439    } else {
    440      code = aDomKeyboardEvent->KeyCode();
    441    }
    442 
    443    if (code != static_cast<uint32_t>(mDetail)) {
    444      return false;
    445    }
    446  }
    447 
    448  return ModifiersMatchMask(aDomKeyboardEvent, aIgnoreModifierState);
    449 }
    450 
    451 struct keyCodeData {
    452  const char* str;
    453  uint16_t strlength;
    454  uint16_t keycode;
    455 };
    456 
    457 // All of these must be uppercase, since the function below does
    458 // case-insensitive comparison by converting to uppercase.
    459 // XXX: be sure to check this periodically for new symbol additions!
    460 static const keyCodeData gKeyCodes[] = {
    461 
    462 #define NS_DEFINE_VK(aDOMKeyName, aDOMKeyCode) \
    463  {#aDOMKeyName, sizeof(#aDOMKeyName) - 1, aDOMKeyCode},
    464 #include "mozilla/VirtualKeyCodeList.h"
    465 #undef NS_DEFINE_VK
    466 
    467    {nullptr, 0, 0}};
    468 
    469 int32_t KeyEventHandler::GetMatchingKeyCode(const nsAString& aKeyName) {
    470  nsAutoCString keyName;
    471  LossyCopyUTF16toASCII(aKeyName, keyName);
    472  ToUpperCase(keyName);  // We want case-insensitive comparison with data
    473                         // stored as uppercase.
    474 
    475  uint32_t keyNameLength = keyName.Length();
    476  const char* keyNameStr = keyName.get();
    477  for (unsigned long i = 0; i < std::size(gKeyCodes) - 1; ++i) {
    478    if (keyNameLength == gKeyCodes[i].strlength &&
    479        !nsCRT::strcmp(gKeyCodes[i].str, keyNameStr)) {
    480      return gKeyCodes[i].keycode;
    481    }
    482  }
    483 
    484  return 0;
    485 }
    486 
    487 int32_t KeyEventHandler::KeyToMask(uint32_t key) {
    488  switch (key) {
    489    case dom::KeyboardEvent_Binding::DOM_VK_META:
    490    case dom::KeyboardEvent_Binding::DOM_VK_WIN:
    491      return cMeta | cMetaMask;
    492 
    493    case dom::KeyboardEvent_Binding::DOM_VK_ALT:
    494      return cAlt | cAltMask;
    495 
    496    case dom::KeyboardEvent_Binding::DOM_VK_CONTROL:
    497    default:
    498      return cControl | cControlMask;
    499  }
    500 }
    501 
    502 // static
    503 int32_t KeyEventHandler::AccelKeyMask() {
    504  switch (WidgetInputEvent::AccelModifier()) {
    505    case MODIFIER_ALT:
    506      return KeyToMask(dom::KeyboardEvent_Binding::DOM_VK_ALT);
    507    case MODIFIER_CONTROL:
    508      return KeyToMask(dom::KeyboardEvent_Binding::DOM_VK_CONTROL);
    509    case MODIFIER_META:
    510      return KeyToMask(dom::KeyboardEvent_Binding::DOM_VK_META);
    511    default:
    512      MOZ_CRASH("Handle the new result of WidgetInputEvent::AccelModifier()");
    513      return 0;
    514  }
    515 }
    516 
    517 void KeyEventHandler::GetEventType(nsAString& aEvent) {
    518  nsCOMPtr<dom::Element> handlerElement = GetHandlerElement();
    519  if (!handlerElement) {
    520    aEvent.Truncate();
    521    return;
    522  }
    523  handlerElement->GetAttr(nsGkAtoms::event, aEvent);
    524 
    525  if (aEvent.IsEmpty() && mIsXULKey) {
    526    // If no type is specified for a XUL <key> element, let's assume that we're
    527    // "keypress".
    528    aEvent.AssignLiteral("keypress");
    529  }
    530 }
    531 
    532 void KeyEventHandler::ConstructPrototype(dom::Element* aKeyElement,
    533                                         const char16_t* aEvent,
    534                                         const char16_t* aCommand,
    535                                         const char16_t* aKeyCode,
    536                                         const char16_t* aCharCode,
    537                                         const char16_t* aModifiers) {
    538  mDetail = -1;
    539  mMisc = 0;
    540  mKeyMask = 0;
    541  nsAutoString modifiers;
    542 
    543  if (mIsXULKey) {
    544    nsWeakPtr weak = do_GetWeakReference(aKeyElement);
    545    if (!weak) {
    546      return;
    547    }
    548    weak.swap(mHandlerElement);
    549 
    550    nsAutoString event;
    551    GetEventType(event);
    552    if (event.IsEmpty()) {
    553      return;
    554    }
    555    mEventName = NS_Atomize(event);
    556 
    557    aKeyElement->GetAttr(nsGkAtoms::modifiers, modifiers);
    558  } else {
    559    mCommand = ToNewUnicode(nsDependentString(aCommand));
    560    mEventName = NS_Atomize(aEvent);
    561    modifiers = aModifiers;
    562  }
    563 
    564  BuildModifiers(modifiers);
    565 
    566  nsAutoString key(aCharCode);
    567  if (key.IsEmpty()) {
    568    if (mIsXULKey) {
    569      aKeyElement->GetAttr(nsGkAtoms::key, key);
    570      if (key.IsEmpty()) {
    571        aKeyElement->GetAttr(nsGkAtoms::charcode, key);
    572      }
    573    }
    574  }
    575 
    576  if (!key.IsEmpty()) {
    577    if (mKeyMask == 0) {
    578      mKeyMask = cAllModifiers;
    579    }
    580    ToLowerCase(key);
    581 
    582    // We have a charcode.
    583    mMisc = 1;
    584    mDetail = key[0];
    585    const uint8_t GTK2Modifiers = cShift | cControl | cShiftMask | cControlMask;
    586    if (mIsXULKey && (mKeyMask & GTK2Modifiers) == GTK2Modifiers &&
    587        modifiers.First() != char16_t(',') &&
    588        (mDetail == 'u' || mDetail == 'U')) {
    589      ReportKeyConflict(key.get(), modifiers.get(), aKeyElement,
    590                        "GTK2Conflict2");
    591    }
    592    const uint8_t WinModifiers = cControl | cAlt | cControlMask | cAltMask;
    593    if (mIsXULKey && (mKeyMask & WinModifiers) == WinModifiers &&
    594        modifiers.First() != char16_t(',') &&
    595        (('A' <= mDetail && mDetail <= 'Z') ||
    596         ('a' <= mDetail && mDetail <= 'z'))) {
    597      ReportKeyConflict(key.get(), modifiers.get(), aKeyElement,
    598                        "WinConflict2");
    599    }
    600  } else {
    601    key.Assign(aKeyCode);
    602    if (mIsXULKey) {
    603      aKeyElement->GetAttr(nsGkAtoms::keycode, key);
    604    }
    605 
    606    if (!key.IsEmpty()) {
    607      if (mKeyMask == 0) {
    608        mKeyMask = cAllModifiers;
    609      }
    610      mDetail = GetMatchingKeyCode(key);
    611    }
    612  }
    613 }
    614 
    615 void KeyEventHandler::BuildModifiers(nsAString& aModifiers) {
    616  if (!aModifiers.IsEmpty()) {
    617    mKeyMask = cAllModifiers;
    618    char* str = ToNewCString(aModifiers);
    619    char* newStr;
    620    char* token = nsCRT::strtok(str, ", \t", &newStr);
    621    while (token != nullptr) {
    622      if (strcmp(token, "shift") == 0) {
    623        mKeyMask |= cShift | cShiftMask;
    624      } else if (strcmp(token, "alt") == 0) {
    625        mKeyMask |= cAlt | cAltMask;
    626      } else if (strcmp(token, "meta") == 0) {
    627        mKeyMask |= cMeta | cMetaMask;
    628      } else if (strcmp(token, "control") == 0) {
    629        mKeyMask |= cControl | cControlMask;
    630      } else if (strcmp(token, "accel") == 0) {
    631        mKeyMask |= AccelKeyMask();
    632      } else if (strcmp(token, "access") == 0) {
    633        mKeyMask |= KeyToMask(LookAndFeel::GetMenuAccessKey());
    634      } else if (strcmp(token, "any") == 0) {
    635        mKeyMask &= ~(mKeyMask << 5);
    636      }
    637 
    638      token = nsCRT::strtok(newStr, ", \t", &newStr);
    639    }
    640 
    641    free(str);
    642  }
    643 }
    644 
    645 void KeyEventHandler::ReportKeyConflict(const char16_t* aKey,
    646                                        const char16_t* aModifiers,
    647                                        dom::Element* aKeyElement,
    648                                        const char* aMessageName) {
    649  nsCOMPtr<dom::Document> doc = aKeyElement->OwnerDoc();
    650 
    651  nsAutoString id;
    652  aKeyElement->GetAttr(nsGkAtoms::id, id);
    653  AutoTArray<nsString, 3> params;
    654  params.AppendElement(aKey);
    655  params.AppendElement(aModifiers);
    656  params.AppendElement(id);
    657  nsContentUtils::ReportToConsole(
    658      nsIScriptError::warningFlag, "Key dom::Event Handler"_ns, doc,
    659      nsContentUtils::eDOM_PROPERTIES, aMessageName, params);
    660 }
    661 
    662 bool KeyEventHandler::ModifiersMatchMask(
    663    dom::UIEvent* aEvent, const IgnoreModifierState& aIgnoreModifierState) {
    664  WidgetInputEvent* inputEvent = aEvent->WidgetEventPtr()->AsInputEvent();
    665  NS_ENSURE_TRUE(inputEvent, false);
    666 
    667  if ((mKeyMask & cMetaMask) && !aIgnoreModifierState.mMeta) {
    668    if (inputEvent->IsMeta() != ((mKeyMask & cMeta) != 0)) {
    669      return false;
    670    }
    671  }
    672 
    673  if ((mKeyMask & cShiftMask) && !aIgnoreModifierState.mShift) {
    674    if (inputEvent->IsShift() != ((mKeyMask & cShift) != 0)) {
    675      return false;
    676    }
    677  }
    678 
    679  if (mKeyMask & cAltMask) {
    680    if (inputEvent->IsAlt() != ((mKeyMask & cAlt) != 0)) {
    681      return false;
    682    }
    683  }
    684 
    685  if (mKeyMask & cControlMask) {
    686    if (inputEvent->IsControl() != ((mKeyMask & cControl) != 0)) {
    687      return false;
    688    }
    689  }
    690 
    691  return true;
    692 }
    693 
    694 size_t KeyEventHandler::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
    695  size_t n = 0;
    696  for (const KeyEventHandler* handler = this; handler;
    697       handler = handler->mNextHandler) {
    698    n += aMallocSizeOf(handler);
    699    if (!mIsXULKey) {
    700      n += aMallocSizeOf(handler->mCommand);
    701    }
    702  }
    703  return n;
    704 }
    705 
    706 }  // namespace mozilla