Bug 1333459 - part2-1: EventStateManager should have a way to check if there is accesskey which is executed by a specific keyboard event r?smaug draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Thu, 20 Jul 2017 17:33:53 +0900
changeset 613981 596b6bb584e4738d138aac2eb134c568aa63df5a
parent 613980 c1111cd8b073c9203920802e2bd711791faa6500
child 613982 5d078048caa4c28c41e44579e1ec622a4869686e
push id69882
push usermasayuki@d-toybox.com
push dateSun, 23 Jul 2017 15:20:52 +0000
reviewerssmaug
bugs1333459
milestone56.0a1
Bug 1333459 - part2-1: EventStateManager should have a way to check if there is accesskey which is executed by a specific keyboard event r?smaug Protected EventStateManager::HandleAccessKey() walks ESMs to handle access key and EventStateManager::ExecuteAccessKey() looks for an accesskey which matches given char code values and execute an accesskey if it finds a target. These names are hard to understand what they do and we need an option not to execute accesskey but looks for a target. Therefore, this patch renames the former to WalkESMTreeToHandleAccessKey() and the latter to LookForAccessKeyAndExecute(). Then, they take a new bool argument, aExecute. When it's true, LookForAccessKeyAndExecute() executes found accesskey. Otherwise, i.e., it's false, they return true if they find an accesskey target for the given event in the process. MozReview-Commit-ID: ETYbNmtTMGj
dom/events/EventStateManager.cpp
dom/events/EventStateManager.h
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -959,18 +959,32 @@ IsAccessKeyTarget(nsIContent* aContent, 
   if (aContent->IsXULElement(nsGkAtoms::label)) {
     return true;
   }
 
   return false;
 }
 
 bool
-EventStateManager::ExecuteAccessKey(nsTArray<uint32_t>& aAccessCharCodes,
-                                    bool aIsTrustedEvent)
+EventStateManager::CheckIfEventMatchesAccessKey(WidgetKeyboardEvent* aEvent,
+                                                nsPresContext* aPresContext)
+{
+  AutoTArray<uint32_t, 10> accessCharCodes;
+  aEvent->GetAccessKeyCandidates(accessCharCodes);
+  return WalkESMTreeToHandleAccessKey(const_cast<WidgetKeyboardEvent*>(aEvent),
+                                      aPresContext, accessCharCodes,
+                                      nullptr, eAccessKeyProcessingNormal,
+                                      false);
+}
+
+bool
+EventStateManager::LookForAccessKeyAndExecute(
+                     nsTArray<uint32_t>& aAccessCharCodes,
+                     bool aIsTrustedEvent,
+                     bool aExecute)
 {
   int32_t count, start = -1;
   nsIContent* focusedContent = GetFocusedContent();
   if (focusedContent) {
     start = mAccessKeys.IndexOf(focusedContent);
     if (start == -1 && focusedContent->GetBindingParent())
       start = mAccessKeys.IndexOf(focusedContent->GetBindingParent());
   }
@@ -980,16 +994,19 @@ EventStateManager::ExecuteAccessKey(nsTA
   for (uint32_t i = 0; i < aAccessCharCodes.Length(); ++i) {
     uint32_t ch = aAccessCharCodes[i];
     nsAutoString accessKey;
     AppendUCS4ToUTF16(ch, accessKey);
     for (count = 1; count <= length; ++count) {
       content = mAccessKeys[(start + count) % length];
       frame = content->GetPrimaryFrame();
       if (IsAccessKeyTarget(content, frame, accessKey)) {
+        if (!aExecute) {
+          return true;
+        }
         bool shouldActivate = Prefs::KeyCausesActivation();
         while (shouldActivate && ++count <= length) {
           nsIContent *oc = mAccessKeys[(start + count) % length];
           nsIFrame *of = oc->GetPrimaryFrame();
           if (IsAccessKeyTarget(oc, of, accessKey))
             shouldActivate = false;
         }
 
@@ -1090,36 +1107,39 @@ HandleAccessKeyInRemoteChild(TabParent* 
                                 accessKeyInfo->charCodes);
     return true;
   }
 
   return false;
 }
 
 bool
-EventStateManager::HandleAccessKey(WidgetKeyboardEvent* aEvent,
-                                   nsPresContext* aPresContext,
-                                   nsTArray<uint32_t>& aAccessCharCodes,
-                                   nsIDocShellTreeItem* aBubbledFrom,
-                                   ProcessingAccessKeyState aAccessKeyState)
+EventStateManager::WalkESMTreeToHandleAccessKey(
+                     WidgetKeyboardEvent* aEvent,
+                     nsPresContext* aPresContext,
+                     nsTArray<uint32_t>& aAccessCharCodes,
+                     nsIDocShellTreeItem* aBubbledFrom,
+                     ProcessingAccessKeyState aAccessKeyState,
+                     bool aExecute)
 {
   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 &&
       aEvent->ModifiersMatchWithAccessKey(accessKeyType)) {
     // Someone registered an accesskey.  Find and activate it.
-    if (ExecuteAccessKey(aAccessCharCodes, aEvent->IsTrusted())) {
+    if (LookForAccessKeyAndExecute(aAccessCharCodes,
+                                   aEvent->IsTrusted(), aExecute)) {
       return true;
     }
   }
 
   int32_t childCount;
   docShell->GetChildCount(&childCount);
   for (int32_t counter = 0; counter < childCount; counter++) {
     // Not processing the child which bubbles up the handling
@@ -1142,18 +1162,19 @@ EventStateManager::HandleAccessKey(Widge
       }
 
       nsPresContext *subPC = subPS->GetPresContext();
 
       EventStateManager* esm =
         static_cast<EventStateManager*>(subPC->EventStateManager());
 
       if (esm &&
-          esm->HandleAccessKey(aEvent, subPC, aAccessCharCodes,
-                               nullptr, eAccessKeyProcessingDown)) {
+          esm->WalkESMTreeToHandleAccessKey(aEvent, subPC, aAccessCharCodes,
+                                            nullptr, eAccessKeyProcessingDown,
+                                            aExecute)) {
         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;
@@ -1164,25 +1185,27 @@ EventStateManager::HandleAccessKey(Widge
       NS_ASSERTION(parentPS, "Our PresShell exists but the parent's does not?");
 
       nsPresContext *parentPC = parentPS->GetPresContext();
       NS_ASSERTION(parentPC, "PresShell without PresContext");
 
       EventStateManager* esm =
         static_cast<EventStateManager*>(parentPC->EventStateManager());
       if (esm &&
-          esm->HandleAccessKey(aEvent, parentPC, aAccessCharCodes,
-                               docShell, eAccessKeyProcessingDown)) {
+          esm->WalkESMTreeToHandleAccessKey(aEvent, parentPC, aAccessCharCodes,
+                                            docShell, eAccessKeyProcessingDown,
+                                            aExecute)) {
         return true;
       }
     }
   }// if end. bubble up process
 
   // If the content access key modifier is pressed, try remote children
-  if (aEvent->ModifiersMatchWithAccessKey(AccessKeyType::eContent) &&
+  if (aExecute &&
+      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
--- a/dom/events/EventStateManager.h
+++ b/dom/events/EventStateManager.h
@@ -179,24 +179,57 @@ public:
    *
    * @param  aContent  the given element (must not be null)
    * @return           registered accesskey
    */
   uint32_t GetRegisteredAccessKey(nsIContent* aContent);
 
   static void GetAccessKeyLabelPrefix(dom::Element* aElement, nsAString& aPrefix);
 
+  /**
+   * HandleAccessKey() looks for access keys which matches with aEvent and
+   * execute when it matches with a chrome access key or some content access
+   * keys.
+   * If the event may match chrome access keys, this handles the access key
+   * synchronously (if there are nested ESMs, their HandleAccessKey() are
+   * also called recursively).
+   * If the event may match content access keys and focused target is a remote
+   * process, this does nothing for the content because when this is called,
+   * it should already have been handled in the remote process.
+   * If the event may match content access keys and focused target is not in
+   * remote process but there are some remote children, this will post
+   * HandleAccessKey messages to all remote children.
+   *
+   * @return            true if there is accesskey which aEvent and
+   *                    aAccessCharCodes match with.  Otherwise, false.
+   *                    I.e., when this returns true, a target is executed
+   *                    or focused.
+   *                    Note that even if this returns false, a target in
+   *                    remote process may be executed or focused
+   *                    asynchronously.
+   */
   bool HandleAccessKey(WidgetKeyboardEvent* aEvent,
                        nsPresContext* aPresContext,
                        nsTArray<uint32_t>& aAccessCharCodes)
   {
-    return HandleAccessKey(aEvent, aPresContext, aAccessCharCodes,
-                           nullptr, eAccessKeyProcessingNormal);
+    return WalkESMTreeToHandleAccessKey(aEvent, aPresContext, aAccessCharCodes,
+                                        nullptr, eAccessKeyProcessingNormal,
+                                        true);
   }
 
+  /**
+   * CheckIfEventMatchesAccessKey() looks for access key which matches with
+   * aEvent in the process but won't execute it.
+   *
+   * @return            true if there is accesskey which aEvent matches with
+   *                    in this process.  Otherwise, false.
+   */
+  bool CheckIfEventMatchesAccessKey(WidgetKeyboardEvent* aEvent,
+                                    nsPresContext* aPresContext);
+
   nsresult SetCursor(int32_t aCursor, imgIContainer* aContainer,
                      bool aHaveHotspot, float aHotspotX, float aHotspotY,
                      nsIWidget* aWidget, bool aLockCursor);
 
   static void StartHandlingUserInput()
   {
     ++sUserInputEventDepth;
     ++sUserInputCounter;
@@ -412,50 +445,74 @@ protected:
                                             bool aNoContentDispatch);
   nsresult SetClickCount(WidgetMouseEvent* aEvent, nsEventStatus* aStatus);
   nsresult CheckForAndDispatchClick(WidgetMouseEvent* aEvent,
                                     nsEventStatus* aStatus);
   void EnsureDocument(nsPresContext* aPresContext);
   void FlushPendingEvents(nsPresContext* aPresContext);
 
   /**
-   * The phases of HandleAccessKey processing. See below.
+   * The phases of WalkESMTreeToHandleAccessKey processing. See below.
    */
   typedef enum {
     eAccessKeyProcessingNormal = 0,
     eAccessKeyProcessingUp,
     eAccessKeyProcessingDown
   } ProcessingAccessKeyState;
 
   /**
-   * Access key handling.  If there is registered content for the accesskey
-   * 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.
+   * Walk EMS to look for access key and execute found access key when aExecute
+   * is true.
+   * If there is registered content for the accesskey given by the key event
+   * and modifier mask then call content.PerformAccesskey(), otherwise call
+   * WalkESMTreeToHandleAccessKey() 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 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 aBubbledFrom is used by an ancestor to avoid calling
+   *        WalkESMTreeToHandleAccessKey() 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 aExecute is true, execute an accesskey if it's found.  Otherwise,
+   *        found accesskey won't be executed.
+   *
+   * @return            true if there is a target which aEvent and
+   *                    aAccessCharCodes match with in this process.
+   *                    Otherwise, false.  I.e., when this returns true and
+   *                    aExecute is true, a target is executed or focused.
+   *                    Note that even if this returns false, a target in
+   *                    remote process may be executed or focused
+   *                    asynchronously.
    */
-  bool HandleAccessKey(WidgetKeyboardEvent* aEvent,
-                       nsPresContext* aPresContext,
-                       nsTArray<uint32_t>& aAccessCharCodes,
-                       nsIDocShellTreeItem* aBubbledFrom,
-                       ProcessingAccessKeyState aAccessKeyState);
+  bool WalkESMTreeToHandleAccessKey(WidgetKeyboardEvent* aEvent,
+                                    nsPresContext* aPresContext,
+                                    nsTArray<uint32_t>& aAccessCharCodes,
+                                    nsIDocShellTreeItem* aBubbledFrom,
+                                    ProcessingAccessKeyState aAccessKeyState,
+                                    bool aExecute);
 
-  bool ExecuteAccessKey(nsTArray<uint32_t>& aAccessCharCodes,
-                        bool aIsTrustedEvent);
+  /**
+   * Look for access key and execute found access key if aExecute is true in
+   * the instance.
+   *
+   * @return            true if there is a target which matches with
+   *                    aAccessCharCodes and aIsTrustedEvent.  Otherwise,
+   *                    false.  I.e., when this returns true and aExecute
+   *                    is true, a target is executed or focused.
+   */
+  bool LookForAccessKeyAndExecute(nsTArray<uint32_t>& aAccessCharCodes,
+                                  bool aIsTrustedEvent,
+                                  bool aExecute);
 
   //---------------------------------------------
   // DocShell Focus Traversal Methods
   //---------------------------------------------
 
   nsIContent* GetFocusedContent();
   bool IsShellVisible(nsIDocShell* aShell);