Bug 1333459 - part1: Move methods of EventStateManager which check modifiers of access key to WidgetKeyboardEvent r?smaug draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Thu, 06 Jul 2017 17:36:19 +0900
changeset 613980 c1111cd8b073c9203920802e2bd711791faa6500
parent 613973 23d9d005d92afdb5afabeae2ad672323c7d903ad
child 613981 596b6bb584e4738d138aac2eb134c568aa63df5a
push id69882
push usermasayuki@d-toybox.com
push dateSun, 23 Jul 2017 15:20:52 +0000
reviewerssmaug
bugs1333459
milestone56.0a1
Bug 1333459 - part1: Move methods of EventStateManager which check modifiers of access key to WidgetKeyboardEvent r?smaug EventStateManager checks if every keypress event's modifiers match with access key modifiers which are in prefs. Moving related methods of this to WidgetKeyboardEvent makes EventStateManager simpler and we can hide the NS_MODIFIER_* constants (they may make developers confused between Modifiers of WidgetInputEvent) into WidgetEventImpl.cpp. MozReview-Commit-ID: 23NUQ51lJ1M
dom/events/EventStateManager.cpp
dom/events/EventStateManager.h
dom/ipc/PBrowser.ipdl
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
dom/ipc/TabParent.cpp
dom/ipc/TabParent.h
widget/EventForwards.h
widget/TextEvents.h
widget/WidgetEventImpl.cpp
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -181,23 +181,16 @@ PrintDocTreeAll(nsIDocShellTreeItem* aIt
       break;
     item = parent;
   }
 
   PrintDocTree(item, 0);
 }
 #endif
 
-// mask values for ui.key.chromeAccess and ui.key.contentAccess
-#define NS_MODIFIER_SHIFT    1
-#define NS_MODIFIER_CONTROL  2
-#define NS_MODIFIER_ALT      4
-#define NS_MODIFIER_META     8
-#define NS_MODIFIER_OS       16
-
 /******************************************************************/
 /* mozilla::UITimerCallback                                       */
 /******************************************************************/
 
 class UITimerCallback final :
     public nsITimerCallback,
     public nsINamed
 {
@@ -763,42 +756,23 @@ EventStateManager::PreHandleEvent(nsPres
   case eDragOver:
     // Send the enter/exit events before eDrop.
     GenerateDragDropEnterExit(aPresContext, aEvent->AsDragEvent());
     break;
 
   case eKeyPress:
     {
       WidgetKeyboardEvent* keyEvent = aEvent->AsKeyboardEvent();
-
-      int32_t modifierMask = 0;
-      if (keyEvent->IsShift())
-        modifierMask |= NS_MODIFIER_SHIFT;
-      if (keyEvent->IsControl())
-        modifierMask |= NS_MODIFIER_CONTROL;
-      if (keyEvent->IsAlt())
-        modifierMask |= NS_MODIFIER_ALT;
-      if (keyEvent->IsMeta())
-        modifierMask |= NS_MODIFIER_META;
-      if (keyEvent->IsOS())
-        modifierMask |= NS_MODIFIER_OS;
-
-      // Prevent keyboard scrolling while an accesskey modifier is in use.
-      if (modifierMask) {
-        bool matchesContentAccessKey = (modifierMask == Prefs::ContentAccessModifierMask());
-
-        if (modifierMask == Prefs::ChromeAccessModifierMask() ||
-            matchesContentAccessKey) {
-          AutoTArray<uint32_t, 10> accessCharCodes;
-          keyEvent->GetAccessKeyCandidates(accessCharCodes);
-
-          if (HandleAccessKey(keyEvent, aPresContext, accessCharCodes,
-                              modifierMask, matchesContentAccessKey)) {
-            *aStatus = nsEventStatus_eConsumeNoDefault;
-          }
+      if (keyEvent->ModifiersMatchWithAccessKey(AccessKeyType::eChrome) ||
+          keyEvent->ModifiersMatchWithAccessKey(AccessKeyType::eContent)) {
+        AutoTArray<uint32_t, 10> accessCharCodes;
+        keyEvent->GetAccessKeyCandidates(accessCharCodes);
+
+        if (HandleAccessKey(keyEvent, aPresContext, accessCharCodes)) {
+          *aStatus = nsEventStatus_eConsumeNoDefault;
         }
       }
     }
     // then fall through...
     MOZ_FALLTHROUGH;
   case eKeyDown:
   case eKeyUp:
     {
@@ -919,33 +893,31 @@ EventStateManager::HandleQueryContentEve
     contentObserver->HandleQueryContentEvent(aEvent);
     return;
   }
 
   ContentEventHandler handler(mPresContext);
   handler.HandleQueryContentEvent(aEvent);
 }
 
-// static
-int32_t
-EventStateManager::GetAccessModifierMaskFor(nsISupports* aDocShell)
+static AccessKeyType
+GetAccessKeyTypeFor(nsISupports* aDocShell)
 {
   nsCOMPtr<nsIDocShellTreeItem> treeItem(do_QueryInterface(aDocShell));
-  if (!treeItem)
-    return -1; // invalid modifier
+  if (!treeItem) {
+    return AccessKeyType::eNone;
+  }
 
   switch (treeItem->ItemType()) {
-  case nsIDocShellTreeItem::typeChrome:
-    return Prefs::ChromeAccessModifierMask();
-
-  case nsIDocShellTreeItem::typeContent:
-    return Prefs::ContentAccessModifierMask();
-
-  default:
-    return -1; // invalid modifier
+    case nsIDocShellTreeItem::typeChrome:
+      return AccessKeyType::eChrome;
+    case nsIDocShellTreeItem::typeContent:
+      return AccessKeyType::eContent;
+    default:
+      return AccessKeyType::eNone;
   }
 }
 
 static bool
 IsAccessKeyTarget(nsIContent* aContent, nsIFrame* aFrame, nsAString& aKey)
 {
   // Use GetAttr because we want Unicode case=insensitive matching
   // XXXbz shouldn't this be case-sensitive, per spec?
@@ -1054,96 +1026,98 @@ EventStateManager::ExecuteAccessKey(nsTA
 // static
 void
 EventStateManager::GetAccessKeyLabelPrefix(Element* aElement, nsAString& aPrefix)
 {
   aPrefix.Truncate();
   nsAutoString separator, modifierText;
   nsContentUtils::GetModifierSeparatorText(separator);
 
-  nsCOMPtr<nsISupports> container = aElement->OwnerDoc()->GetDocShell();
-  int32_t modifierMask = GetAccessModifierMaskFor(container);
-
-  if (modifierMask == -1) {
+  AccessKeyType accessKeyType =
+    GetAccessKeyTypeFor(aElement->OwnerDoc()->GetDocShell());
+  if (accessKeyType == AccessKeyType::eNone) {
     return;
   }
-
-  if (modifierMask & NS_MODIFIER_CONTROL) {
+  Modifiers modifiers = WidgetKeyboardEvent::AccessKeyModifiers(accessKeyType);
+  if (modifiers == MODIFIER_NONE) {
+    return;
+  }
+
+  if (modifiers & MODIFIER_CONTROL) {
     nsContentUtils::GetControlText(modifierText);
     aPrefix.Append(modifierText + separator);
   }
-  if (modifierMask & NS_MODIFIER_META) {
+  if (modifiers & MODIFIER_META) {
     nsContentUtils::GetMetaText(modifierText);
     aPrefix.Append(modifierText + separator);
   }
-  if (modifierMask & NS_MODIFIER_OS) {
+  if (modifiers & MODIFIER_OS) {
     nsContentUtils::GetOSText(modifierText);
     aPrefix.Append(modifierText + separator);
   }
-  if (modifierMask & NS_MODIFIER_ALT) {
+  if (modifiers & MODIFIER_ALT) {
     nsContentUtils::GetAltText(modifierText);
     aPrefix.Append(modifierText + separator);
   }
-  if (modifierMask & NS_MODIFIER_SHIFT) {
+  if (modifiers & MODIFIER_SHIFT) {
     nsContentUtils::GetShiftText(modifierText);
     aPrefix.Append(modifierText + separator);
   }
 }
 
 struct MOZ_STACK_CLASS AccessKeyInfo
 {
   WidgetKeyboardEvent* event;
   nsTArray<uint32_t>& charCodes;
-  int32_t modifierMask;
-
-  AccessKeyInfo(WidgetKeyboardEvent* aEvent, nsTArray<uint32_t>& aCharCodes, int32_t aModifierMask)
+
+  AccessKeyInfo(WidgetKeyboardEvent* aEvent,
+                nsTArray<uint32_t>& aCharCodes)
     : event(aEvent)
     , charCodes(aCharCodes)
-    , modifierMask(aModifierMask)
   {
   }
 };
 
 static bool
 HandleAccessKeyInRemoteChild(TabParent* aTabParent, void* aArg)
 {
   AccessKeyInfo* accessKeyInfo = static_cast<AccessKeyInfo*>(aArg);
 
   // Only forward accesskeys for the active tab.
   bool active;
   aTabParent->GetDocShellIsActive(&active);
   if (active) {
     accessKeyInfo->event->mAccessKeyForwardedToChild = true;
     aTabParent->HandleAccessKey(*accessKeyInfo->event,
-                                accessKeyInfo->charCodes,
-                                accessKeyInfo->modifierMask);
+                                accessKeyInfo->charCodes);
     return true;
   }
 
   return false;
 }
 
 bool
 EventStateManager::HandleAccessKey(WidgetKeyboardEvent* aEvent,
                                    nsPresContext* aPresContext,
                                    nsTArray<uint32_t>& aAccessCharCodes,
-                                   bool aMatchesContentAccessKey,
                                    nsIDocShellTreeItem* aBubbledFrom,
-                                   ProcessingAccessKeyState aAccessKeyState,
-                                   int32_t aModifierMask)
+                                   ProcessingAccessKeyState aAccessKeyState)
 {
   EnsureDocument(mPresContext);
   nsCOMPtr<nsIDocShell> docShell = aPresContext->GetDocShell();
   if (NS_WARN_IF(!docShell) || NS_WARN_IF(!mDocument)) {
     return false;
   }
-
+  AccessKeyType accessKeyType = GetAccessKeyTypeFor(docShell);
+  if (accessKeyType == AccessKeyType::eNone) {
+    return false;
+  }
   // Alt or other accesskey modifier is down, we may need to do an accesskey.
   if (mAccessKeys.Count() > 0 &&
-      aModifierMask == GetAccessModifierMaskFor(docShell)) {
+      aEvent->ModifiersMatchWithAccessKey(accessKeyType)) {
     // Someone registered an accesskey.  Find and activate it.
     if (ExecuteAccessKey(aAccessCharCodes, aEvent->IsTrusted())) {
       return true;
     }
   }
 
   int32_t childCount;
   docShell->GetChildCount(&childCount);
@@ -1169,18 +1143,17 @@ EventStateManager::HandleAccessKey(Widge
 
       nsPresContext *subPC = subPS->GetPresContext();
 
       EventStateManager* esm =
         static_cast<EventStateManager*>(subPC->EventStateManager());
 
       if (esm &&
           esm->HandleAccessKey(aEvent, subPC, aAccessCharCodes,
-                               aMatchesContentAccessKey, nullptr,
-                               eAccessKeyProcessingDown, aModifierMask)) {
+                               nullptr, eAccessKeyProcessingDown)) {
         return true;
       }
     }
   }// if end . checking all sub docshell ends here.
 
   // bubble up the process to the parent docshell if necessary
   if (eAccessKeyProcessingDown != aAccessKeyState) {
     nsCOMPtr<nsIDocShellTreeItem> parentShellItem;
@@ -1192,38 +1165,39 @@ EventStateManager::HandleAccessKey(Widge
 
       nsPresContext *parentPC = parentPS->GetPresContext();
       NS_ASSERTION(parentPC, "PresShell without PresContext");
 
       EventStateManager* esm =
         static_cast<EventStateManager*>(parentPC->EventStateManager());
       if (esm &&
           esm->HandleAccessKey(aEvent, parentPC, aAccessCharCodes,
-                               aMatchesContentAccessKey, docShell,
-                               eAccessKeyProcessingDown, aModifierMask)) {
+                               docShell, eAccessKeyProcessingDown)) {
         return true;
       }
     }
   }// if end. bubble up process
 
   // If the content access key modifier is pressed, try remote children
-  if (aMatchesContentAccessKey && mDocument && mDocument->GetWindow()) {
+  if (aEvent->ModifiersMatchWithAccessKey(AccessKeyType::eContent) &&
+      mDocument && mDocument->GetWindow()) {
     // If the focus is currently on a node with a TabParent, the key event will
     // get forwarded to the child process and HandleAccessKey called from there.
     // If focus is somewhere else, then we need to check the remote children.
     nsFocusManager* fm = nsFocusManager::GetFocusManager();
     nsIContent* focusedContent = fm ? fm->GetFocusedContent() : nullptr;
     if (TabParent::GetFrom(focusedContent)) {
       // A remote child process is focused. The key event should get sent to
       // the child process.
       aEvent->mAccessKeyForwardedToChild = true;
     } else {
-      AccessKeyInfo accessKeyInfo(aEvent, aAccessCharCodes, aModifierMask);
+      AccessKeyInfo accessKeyInfo(aEvent, aAccessCharCodes);
       nsContentUtils::CallOnAllRemoteChildren(mDocument->GetWindow(),
-                                              HandleAccessKeyInRemoteChild, &accessKeyInfo);
+                                              HandleAccessKeyInRemoteChild,
+                                              &accessKeyInfo);
     }
   }
 
   return false;
 }// end of HandleAccessKey
 
 void
 EventStateManager::DispatchCrossProcessEvent(WidgetEvent* aEvent,
@@ -5814,19 +5788,16 @@ EventStateManager::WheelPrefs::IsOverOne
 }
 
 /******************************************************************/
 /* mozilla::EventStateManager::Prefs                              */
 /******************************************************************/
 
 bool EventStateManager::Prefs::sKeyCausesActivation = true;
 bool EventStateManager::Prefs::sClickHoldContextMenu = false;
-int32_t EventStateManager::Prefs::sGenericAccessModifierKey = -1;
-int32_t EventStateManager::Prefs::sChromeAccessModifierMask = 0;
-int32_t EventStateManager::Prefs::sContentAccessModifierMask = 0;
 
 // static
 void
 EventStateManager::Prefs::Init()
 {
   DebugOnly<nsresult> rv = Preferences::RegisterCallback(OnChange, "dom.popup_allowed_events");
   MOZ_ASSERT(NS_SUCCEEDED(rv),
              "Failed to observe \"dom.popup_allowed_events\"");
@@ -5841,31 +5812,16 @@ EventStateManager::Prefs::Init()
                                     sKeyCausesActivation);
   MOZ_ASSERT(NS_SUCCEEDED(rv),
              "Failed to observe \"accessibility.accesskeycausesactivation\"");
   rv = Preferences::AddBoolVarCache(&sClickHoldContextMenu,
                                     "ui.click_hold_context_menus",
                                     sClickHoldContextMenu);
   MOZ_ASSERT(NS_SUCCEEDED(rv),
              "Failed to observe \"ui.click_hold_context_menus\"");
-  rv = Preferences::AddIntVarCache(&sGenericAccessModifierKey,
-                                   "ui.key.generalAccessKey",
-                                   sGenericAccessModifierKey);
-  MOZ_ASSERT(NS_SUCCEEDED(rv),
-             "Failed to observe \"ui.key.generalAccessKey\"");
-  rv = Preferences::AddIntVarCache(&sChromeAccessModifierMask,
-                                   "ui.key.chromeAccess",
-                                   sChromeAccessModifierMask);
-  MOZ_ASSERT(NS_SUCCEEDED(rv),
-             "Failed to observe \"ui.key.chromeAccess\"");
-  rv = Preferences::AddIntVarCache(&sContentAccessModifierMask,
-                                   "ui.key.contentAccess",
-                                   sContentAccessModifierMask);
-  MOZ_ASSERT(NS_SUCCEEDED(rv),
-             "Failed to observe \"ui.key.contentAccess\"");
   sPrefsAlreadyCached = true;
 }
 
 // static
 void
 EventStateManager::Prefs::OnChange(const char* aPrefName, void*)
 {
   nsDependentCString prefName(aPrefName);
@@ -5876,54 +5832,16 @@ EventStateManager::Prefs::OnChange(const
 
 // static
 void
 EventStateManager::Prefs::Shutdown()
 {
   Preferences::UnregisterCallback(OnChange, "dom.popup_allowed_events");
 }
 
-// static
-int32_t
-EventStateManager::Prefs::ChromeAccessModifierMask()
-{
-  return GetAccessModifierMask(nsIDocShellTreeItem::typeChrome);
-}
-
-// static
-int32_t
-EventStateManager::Prefs::ContentAccessModifierMask()
-{
-  return GetAccessModifierMask(nsIDocShellTreeItem::typeContent);
-}
-
-// static
-int32_t
-EventStateManager::Prefs::GetAccessModifierMask(int32_t aItemType)
-{
-  switch (sGenericAccessModifierKey) {
-    case -1:                             break; // use the individual prefs
-    case nsIDOMKeyEvent::DOM_VK_SHIFT:   return NS_MODIFIER_SHIFT;
-    case nsIDOMKeyEvent::DOM_VK_CONTROL: return NS_MODIFIER_CONTROL;
-    case nsIDOMKeyEvent::DOM_VK_ALT:     return NS_MODIFIER_ALT;
-    case nsIDOMKeyEvent::DOM_VK_META:    return NS_MODIFIER_META;
-    case nsIDOMKeyEvent::DOM_VK_WIN:     return NS_MODIFIER_OS;
-    default:                             return 0;
-  }
-
-  switch (aItemType) {
-    case nsIDocShellTreeItem::typeChrome:
-      return sChromeAccessModifierMask;
-    case nsIDocShellTreeItem::typeContent:
-      return sContentAccessModifierMask;
-    default:
-      return 0;
-  }
-}
-
 /******************************************************************/
 /* mozilla::AutoHandlingUserInputStatePusher                      */
 /******************************************************************/
 
 AutoHandlingUserInputStatePusher::AutoHandlingUserInputStatePusher(
                                     bool aIsHandlingUserInput,
                                     WidgetEvent* aEvent,
                                     nsIDocument* aDocument) :
--- a/dom/events/EventStateManager.h
+++ b/dom/events/EventStateManager.h
@@ -181,23 +181,20 @@ public:
    * @return           registered accesskey
    */
   uint32_t GetRegisteredAccessKey(nsIContent* aContent);
 
   static void GetAccessKeyLabelPrefix(dom::Element* aElement, nsAString& aPrefix);
 
   bool HandleAccessKey(WidgetKeyboardEvent* aEvent,
                        nsPresContext* aPresContext,
-                       nsTArray<uint32_t>& aAccessCharCodes,
-                       int32_t aModifierMask,
-                       bool aMatchesContentAccessKey)
+                       nsTArray<uint32_t>& aAccessCharCodes)
   {
     return HandleAccessKey(aEvent, aPresContext, aAccessCharCodes,
-                           aMatchesContentAccessKey, nullptr,
-                           eAccessKeyProcessingNormal, aModifierMask);
+                           nullptr, eAccessKeyProcessingNormal);
   }
 
   nsresult SetCursor(int32_t aCursor, imgIContainer* aContainer,
                      bool aHaveHotspot, float aHotspotX, float aHotspotY,
                      nsIWidget* aWidget, bool aLockCursor);
 
   static void StartHandlingUserInput()
   {
@@ -311,39 +308,28 @@ protected:
   /**
    * Prefs class capsules preference management.
    */
   class Prefs
   {
   public:
     static bool KeyCausesActivation() { return sKeyCausesActivation; }
     static bool ClickHoldContextMenu() { return sClickHoldContextMenu; }
-    static int32_t ChromeAccessModifierMask();
-    static int32_t ContentAccessModifierMask();
 
     static void Init();
     static void OnChange(const char* aPrefName, void*);
     static void Shutdown();
 
   private:
     static bool sKeyCausesActivation;
     static bool sClickHoldContextMenu;
-    static int32_t sGenericAccessModifierKey;
-    static int32_t sChromeAccessModifierMask;
-    static int32_t sContentAccessModifierMask;
 
     static int32_t GetAccessModifierMask(int32_t aItemType);
   };
 
-  /**
-   * Get appropriate access modifier mask for the aDocShell.  Returns -1 if
-   * access key isn't available.
-   */
-  static int32_t GetAccessModifierMaskFor(nsISupports* aDocShell);
-
   /*
    * If aTargetFrame's widget has a cached cursor value, resets the cursor
    * such that the next call to SetCursor on the widget will force an update
    * of the native cursor. For use in getting puppet widget to update its
    * cursor between mouse exit / enter transitions. This call basically wraps
    * nsIWidget ClearCachedCursor.
    */
   void ClearCachedWidgetCursor(nsIFrame* aTargetFrame);
@@ -444,33 +430,29 @@ protected:
    * given by the key event and modifier mask then call
    * content.PerformAccesskey(), otherwise call HandleAccessKey() recursively,
    * on descendant docshells first, then on the ancestor (with |aBubbledFrom|
    * set to the docshell associated with |this|), until something matches.
    *
    * @param aEvent the keyboard event triggering the acccess key
    * @param aPresContext the presentation context
    * @param aAccessCharCodes list of charcode candidates
-   * @param aMatchesContentAccessKey true if the content accesskey modifier is pressed
    * @param aBubbledFrom is used by an ancestor to avoid calling HandleAccessKey()
    *        on the child the call originally came from, i.e. this is the child
    *        that recursively called us in its Up phase. The initial caller
    *        passes |nullptr| here. This is to avoid an infinite loop.
    * @param aAccessKeyState Normal, Down or Up processing phase (see enums
    *        above). The initial event receiver uses 'normal', then 'down' when
    *        processing children and Up when recursively calling its ancestor.
-   * @param aModifierMask modifier mask for the key event
    */
   bool HandleAccessKey(WidgetKeyboardEvent* aEvent,
                        nsPresContext* aPresContext,
                        nsTArray<uint32_t>& aAccessCharCodes,
-                       bool aMatchesContentAccessKey,
                        nsIDocShellTreeItem* aBubbledFrom,
-                       ProcessingAccessKeyState aAccessKeyState,
-                       int32_t aModifierMask);
+                       ProcessingAccessKeyState aAccessKeyState);
 
   bool ExecuteAccessKey(nsTArray<uint32_t>& aAccessCharCodes,
                         bool aIsTrustedEvent);
 
   //---------------------------------------------
   // DocShell Focus Traversal Methods
   //---------------------------------------------
 
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -801,20 +801,19 @@ child:
     async SwappedWithOtherRemoteLoader(IPCTabContext context);
 
     /**
      * A potential accesskey was just pressed. Look for accesskey targets
      * using the list of provided charCodes.
      *
      * @param event keyboard event
      * @param isTrusted true if triggered by a trusted key event
-     * @param modifierMask indicates which accesskey modifiers are pressed
      */
     async HandleAccessKey(WidgetKeyboardEvent event,
-                          uint32_t[] charCodes, int32_t modifierMask);
+                          uint32_t[] charCodes);
 
     /**
      * Tells the root child docShell whether or not to use
      * global history. This is sent right after the PBrowser
      * is bound to a frameloader element.
      */
     async SetUseGlobalHistory(bool aUse);
 
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -2239,28 +2239,26 @@ TabChild::RecvSwappedWithOtherRemoteLoad
 
   docShell->SetInFrameSwap(false);
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 TabChild::RecvHandleAccessKey(const WidgetKeyboardEvent& aEvent,
-                              nsTArray<uint32_t>&& aCharCodes,
-                              const int32_t& aModifierMask)
+                              nsTArray<uint32_t>&& aCharCodes)
 {
   nsCOMPtr<nsIDocument> document(GetDocument());
   nsCOMPtr<nsIPresShell> presShell = document->GetShell();
   if (presShell) {
     nsPresContext* pc = presShell->GetPresContext();
     if (pc) {
       if (!pc->EventStateManager()->
                  HandleAccessKey(&(const_cast<WidgetKeyboardEvent&>(aEvent)),
-                                 pc, aCharCodes,
-                                 aModifierMask, true)) {
+                                 pc, aCharCodes)) {
         // If no accesskey was found, inform the parent so that accesskeys on
         // menus can be handled.
         WidgetKeyboardEvent localEvent(aEvent);
         localEvent.mWidget = mPuppetWidget;
         SendAccessKeyNotHandled(localEvent);
       }
     }
   }
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -590,19 +590,19 @@ public:
 
   virtual mozilla::ipc::IPCResult RecvUIResolutionChanged(const float& aDpi,
                                                           const int32_t& aRounding,
                                                           const double& aScale) override;
 
   virtual mozilla::ipc::IPCResult
   RecvThemeChanged(nsTArray<LookAndFeelInt>&& aLookAndFeelIntCache) override;
 
-  virtual mozilla::ipc::IPCResult RecvHandleAccessKey(const WidgetKeyboardEvent& aEvent,
-                                                      nsTArray<uint32_t>&& aCharCodes,
-                                                      const int32_t& aModifierMask) override;
+  virtual mozilla::ipc::IPCResult
+  RecvHandleAccessKey(const WidgetKeyboardEvent& aEvent,
+                      nsTArray<uint32_t>&& aCharCodes) override;
 
   virtual mozilla::ipc::IPCResult RecvSetUseGlobalHistory(const bool& aUse) override;
 
   virtual mozilla::ipc::IPCResult RecvHandledWindowedPluginKeyEvent(
     const mozilla::NativeEventData& aKeyEventData,
     const bool& aIsConsumed) override;
 
   virtual mozilla::ipc::IPCResult RecvPrint(const uint64_t& aOuterWindowID,
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -812,25 +812,24 @@ TabParent::ThemeChanged()
     // send down to the child. We do this for every remote tab for now,
     // but bug 1156934 has been filed to do it once per content process.
     Unused << SendThemeChanged(LookAndFeel::GetIntCache());
   }
 }
 
 void
 TabParent::HandleAccessKey(const WidgetKeyboardEvent& aEvent,
-                           nsTArray<uint32_t>& aCharCodes,
-                           const int32_t& aModifierMask)
+                           nsTArray<uint32_t>& aCharCodes)
 {
   if (!mIsDestroyed) {
     // Note that we don't need to mark aEvent is posted to a remote process
     // because the event may be dispatched to it as normal keyboard event.
     // Therefore, we should use local copy to send it.
     WidgetKeyboardEvent localEvent(aEvent);
-    Unused << SendHandleAccessKey(localEvent, aCharCodes, aModifierMask);
+    Unused << SendHandleAccessKey(localEvent, aCharCodes);
   }
 }
 
 void
 TabParent::Activate()
 {
   if (!mIsDestroyed) {
     Unused << Manager()->SendActivate(this);
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -356,18 +356,17 @@ public:
 
   void SizeModeChanged(const nsSizeMode& aSizeMode);
 
   void UIResolutionChanged();
 
   void ThemeChanged();
 
   void HandleAccessKey(const WidgetKeyboardEvent& aEvent,
-                       nsTArray<uint32_t>& aCharCodes,
-                       const int32_t& aModifierMask);
+                       nsTArray<uint32_t>& aCharCodes);
 
   void Activate();
 
   void Deactivate();
 
   bool MapEventCoordinatesForChildProcess(mozilla::WidgetEvent* aEvent);
 
   void MapEventCoordinatesForChildProcess(const LayoutDeviceIntPoint& aOffset,
--- a/widget/EventForwards.h
+++ b/widget/EventForwards.h
@@ -145,16 +145,18 @@ namespace mozilla {
 struct BaseEventFlags;
 struct EventFlags;
 
 class WidgetEventTime;
 
 class NativeEventData;
 
 // TextEvents.h
+enum class AccessKeyType;
+
 struct AlternativeCharCode;
 struct ShortcutKeyCandidate;
 
 typedef nsTArray<ShortcutKeyCandidate> ShortcutKeyCandidateArray;
 typedef AutoTArray<ShortcutKeyCandidate, 10> AutoShortcutKeyCandidateArray;
 
 // TextRange.h
 typedef uint8_t RawTextRangeType;
--- a/widget/TextEvents.h
+++ b/widget/TextEvents.h
@@ -54,16 +54,26 @@ const nsCString GetDOMKeyCodeName(uint32
 namespace dom {
   class PBrowserParent;
   class PBrowserChild;
 } // namespace dom
 namespace plugins {
   class PPluginInstanceChild;
 } // namespace plugins
 
+enum class AccessKeyType
+{
+  // Handle access key for chrome.
+  eChrome,
+  // Handle access key for content.
+  eContent,
+  // Don't handle access key.
+  eNone
+};
+
 /******************************************************************************
  * mozilla::AlternativeCharCode
  *
  * This stores alternative charCode values of a key event with some modifiers.
  * The stored values proper for testing shortcut key or access key.
  ******************************************************************************/
 
 struct AlternativeCharCode
@@ -426,16 +436,34 @@ public:
   /**
    * Get the candidates for access key.
    *
    * @param aCandidates [out] the candidate access key list.
    *                          the first item is most preferred.
    */
   void GetAccessKeyCandidates(nsTArray<uint32_t>& aCandidates) const;
 
+  /**
+   * Check whether the modifiers match with chrome access key or
+   * content access key.
+   */
+  bool ModifiersMatchWithAccessKey(AccessKeyType aType) const;
+
+  /**
+   * Return active modifiers which may match with access key.
+   * For example, even if Alt is access key modifier, then, when Control,
+   * CapseLock and NumLock are active, this returns only MODIFIER_CONTROL.
+   */
+  Modifiers ModifiersForAccessKeyMatching() const;
+
+  /**
+   * Return access key modifiers.
+   */
+  static Modifiers AccessKeyModifiers(AccessKeyType aType);
+
   static void Shutdown();
 
   /**
    * ComputeLocationFromCodeValue() returns one of .mLocation value
    * (eKeyLocation*) which is the most preferred value for the specified code
    * value.
    */
   static uint32_t ComputeLocationFromCodeValue(CodeNameIndex aCodeNameIndex);
@@ -560,16 +588,20 @@ private:
         return mEditCommandsForMultiLineEditorInitialized;
       case nsIWidget::NativeKeyBindingsForRichTextEditor:
         return mEditCommandsForRichTextEditorInitialized;
       default:
         MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE(
           "Invalid native key binding type");
     }
   }
+
+  static int32_t GenericAccessModifierKeyPref();
+  static int32_t ChromeAccessModifierMaskPref();
+  static int32_t ContentAccessModifierMaskPref();
 };
 
 /******************************************************************************
  * mozilla::WidgetCompositionEvent
  ******************************************************************************/
 
 class WidgetCompositionEvent : public WidgetGUIEvent
 {
--- a/widget/WidgetEventImpl.cpp
+++ b/widget/WidgetEventImpl.cpp
@@ -864,16 +864,138 @@ WidgetKeyboardEvent::GetAccessKeyCandida
   // press.  However, if the space key is assigned to a function key, it
   // shouldn't work as a space key.
   if (mKeyNameIndex == KEY_NAME_INDEX_USE_STRING &&
       mCodeNameIndex == CODE_NAME_INDEX_Space && mCharCode != ' ') {
     aCandidates.AppendElement(' ');
   }
 }
 
+// mask values for ui.key.chromeAccess and ui.key.contentAccess
+#define NS_MODIFIER_SHIFT    1
+#define NS_MODIFIER_CONTROL  2
+#define NS_MODIFIER_ALT      4
+#define NS_MODIFIER_META     8
+#define NS_MODIFIER_OS       16
+
+static Modifiers PrefFlagsToModifiers(int32_t aPrefFlags)
+{
+  Modifiers result = 0;
+  if (aPrefFlags & NS_MODIFIER_SHIFT) {
+    result |= MODIFIER_SHIFT;
+  }
+  if (aPrefFlags & NS_MODIFIER_CONTROL) {
+    result |= MODIFIER_CONTROL;
+  }
+  if (aPrefFlags & NS_MODIFIER_ALT) {
+    result |= MODIFIER_ALT;
+  }
+  if (aPrefFlags & NS_MODIFIER_META) {
+    result |= MODIFIER_META;
+  }
+  if (aPrefFlags & NS_MODIFIER_OS) {
+    result |= MODIFIER_OS;
+  }
+  return result;
+}
+
+bool
+WidgetKeyboardEvent::ModifiersMatchWithAccessKey(AccessKeyType aType) const
+{
+  if (!ModifiersForAccessKeyMatching()) {
+    return false;
+  }
+  return ModifiersForAccessKeyMatching() == AccessKeyModifiers(aType);
+}
+
+Modifiers
+WidgetKeyboardEvent::ModifiersForAccessKeyMatching() const
+{
+  static const Modifiers kModifierMask =
+    MODIFIER_SHIFT | MODIFIER_CONTROL |
+    MODIFIER_ALT | MODIFIER_META | MODIFIER_OS;
+  return mModifiers & kModifierMask;
+}
+
+/* static */
+Modifiers
+WidgetKeyboardEvent::AccessKeyModifiers(AccessKeyType aType)
+{
+  switch (GenericAccessModifierKeyPref()) {
+    case -1:
+      break; // use the individual prefs
+    case NS_VK_SHIFT:
+      return MODIFIER_SHIFT;
+    case NS_VK_CONTROL:
+      return MODIFIER_CONTROL;
+    case NS_VK_ALT:
+      return MODIFIER_ALT;
+    case NS_VK_META:
+      return MODIFIER_META;
+    case NS_VK_WIN:
+      return MODIFIER_OS;
+    default:
+      return MODIFIER_NONE;
+  }
+
+  switch (aType) {
+    case AccessKeyType::eChrome:
+      return PrefFlagsToModifiers(ChromeAccessModifierMaskPref());
+    case AccessKeyType::eContent:
+      return PrefFlagsToModifiers(ContentAccessModifierMaskPref());
+    default:
+      return MODIFIER_NONE;
+  }
+}
+
+/* static */
+int32_t
+WidgetKeyboardEvent::GenericAccessModifierKeyPref()
+{
+  static bool sInitialized = false;
+  static int32_t sValue = -1;
+  if (!sInitialized) {
+    nsresult rv =
+      Preferences::AddIntVarCache(&sValue, "ui.key.generalAccessKey", sValue);
+    sInitialized = NS_SUCCEEDED(rv);
+    MOZ_ASSERT(sInitialized);
+  }
+  return sValue;
+}
+
+/* static */
+int32_t
+WidgetKeyboardEvent::ChromeAccessModifierMaskPref()
+{
+  static bool sInitialized = false;
+  static int32_t sValue = 0;
+  if (!sInitialized) {
+    nsresult rv =
+      Preferences::AddIntVarCache(&sValue, "ui.key.chromeAccess", sValue);
+    sInitialized = NS_SUCCEEDED(rv);
+    MOZ_ASSERT(sInitialized);
+  }
+  return sValue;
+}
+
+/* static */
+int32_t
+WidgetKeyboardEvent::ContentAccessModifierMaskPref()
+{
+  static bool sInitialized = false;
+  static int32_t sValue = 0;
+  if (!sInitialized) {
+    nsresult rv =
+      Preferences::AddIntVarCache(&sValue, "ui.key.contentAccess", sValue);
+    sInitialized = NS_SUCCEEDED(rv);
+    MOZ_ASSERT(sInitialized);
+  }
+  return sValue;
+}
+
 /* static */ void
 WidgetKeyboardEvent::Shutdown()
 {
   delete sKeyNameIndexHashtable;
   sKeyNameIndexHashtable = nullptr;
   delete sCodeNameIndexHashtable;
   sCodeNameIndexHashtable = nullptr;
 }