Bug 1333459 - part2-2: EventStateManager should check if it needs to wait reply from remote content before handling access keys r=smaug
authorMasayuki Nakano <masayuki@d-toybox.com>
Sat, 22 Jul 2017 10:50:41 +0900
changeset 421575 ce1fe9986ce932b2d19ee240548b91176568f065
parent 421574 69b23bbdb956726868c15d9ce46896cf58d5c329
child 421576 a1d8dca634549c9fc5cda42a842253b29c5b462c
push id1517
push userjlorenzo@mozilla.com
push dateThu, 14 Sep 2017 16:50:54 +0000
treeherdermozilla-release@3b41fd564418 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1333459
milestone56.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1333459 - part2-2: EventStateManager should check if it needs to wait reply from remote content before handling access keys r=smaug Currently, access key is handled in EventStateManager::PreHandleEvent() with eKeyPress event, i.e., before dispatching it into the DOM tree, if the access key is registered in EventStateManager. So, the main process does not check if the preceding eKeyDown event is consumed in focused remote process. When preceding eKeyDown event is consumed in the main process, eKeyPress event won't be dispatched by widget. However, if remote process has focus, it's impossible widget to stop dispatching eKeyPress event because preceding eKeyDown event hasn't been handled in the focused remote process yet. Therefore, main process needs to post eKeyPress event to check if preceding eKeyDown event was consumed. When eKeyPress event is marked as "waiting reply from remote process", TabChild sends it back to the main process only when preceding eKeyDown event wasn't consumed. So, only when eKeyPress event is back to the main process, main process should handle accesskey with it. This patch makes EventStateManager::PreHandleEvent() check if a remote target has focus before handling accesskey. If a remote process has accesskey and there is an accesskey matching with eKeyPress event, it marks the event as "waiting reply from remote content" and stop propagation in the process. Finally, when eKeyPress event is sent back to TabParent, TabParent::RecvReplyKeyEvent() calls EventStateManager::HandleAccessKey() before dispatching the reply event into the DOM tree. MozReview-Commit-ID: KsOkakaIVzb
dom/events/EventStateManager.cpp
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
dom/ipc/TabParent.cpp
layout/base/PresShell.cpp
layout/xul/nsMenuBarListener.cpp
widget/BasicEvents.h
widget/TextEvents.h
widget/nsGUIEventIPC.h
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -758,21 +758,44 @@ EventStateManager::PreHandleEvent(nsPres
     GenerateDragDropEnterExit(aPresContext, aEvent->AsDragEvent());
     break;
 
   case eKeyPress:
     {
       WidgetKeyboardEvent* keyEvent = aEvent->AsKeyboardEvent();
       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;
+        // If the eKeyPress event will be sent to a remote process, this
+        // process needs to wait reply from the remote process for checking if
+        // preceding eKeyDown event is consumed.  If preceding eKeyDown event
+        // is consumed in the remote process, TabChild won't send the event
+        // back to this process.  So, only when this process receives a reply
+        // eKeyPress event in TabParent, we should handle accesskey in this
+        // process.
+        if (IsRemoteTarget(GetFocusedContent())) {
+          // However, if there is no accesskey target for the key combination,
+          // we don't need to wait reply from the remote process.  Otherwise,
+          // Mark the event as waiting reply from remote process and stop
+          // propagation in this process.
+          if (CheckIfEventMatchesAccessKey(keyEvent, aPresContext)) {
+            keyEvent->StopPropagation();
+            keyEvent->MarkAsWaitingReplyFromRemoteProcess();
+          }
+        }
+        // If the event target is in this process, we can handle accesskey now
+        // since if preceding eKeyDown event was consumed, eKeyPress event
+        // won't be dispatched by widget.  So, coming eKeyPress event means
+        // that the preceding eKeyDown event wasn't consumed in this case.
+        else {
+          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:
     {
@@ -1097,17 +1120,23 @@ 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;
+    // Even if there is no target for the accesskey in this process,
+    // the event may match with a content accesskey.  If so, the keyboard
+    // event should be handled with reply event for preventing double action.
+    // (e.g., Alt+Shift+F on Windows may focus a content in remote and open
+    // "File" menu.)
+    accessKeyInfo->event->StopPropagation();
+    accessKeyInfo->event->MarkAsWaitingReplyFromRemoteProcess();
     aTabParent->HandleAccessKey(*accessKeyInfo->event,
                                 accessKeyInfo->charCodes);
     return true;
   }
 
   return false;
 }
 
@@ -1197,26 +1226,32 @@ EventStateManager::WalkESMTreeToHandleAc
       }
     }
   }// if end. bubble up process
 
   // If the content access key modifier is pressed, try remote children
   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 the focus is currently on a node with a TabParent, the key event
+    // should've gotten forwarded to the child process and HandleAccessKey
+    // called from there.
+    if (TabParent::GetFrom(GetFocusedContent())) {
+      // If access key may be only in remote contents, this method won't handle
+      // access key synchronously.  In this case, only reply event should reach
+      // here.
+      MOZ_ASSERT(aEvent->IsHandledInRemoteProcess() ||
+                 !aEvent->IsWaitingReplyFromRemoteProcess());
+    }
     // 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 {
+    // However, if the event has already been handled in a remote process,
+    // then, focus is moved from the remote process after posting the event.
+    // In such case, we shouldn't retry to handle access keys in remote
+    // processes.
+    else if (!aEvent->IsHandledInRemoteProcess()) {
       AccessKeyInfo accessKeyInfo(aEvent, aAccessCharCodes);
       nsContentUtils::CallOnAllRemoteChildren(mDocument->GetWindow(),
                                               HandleAccessKeyInRemoteChild,
                                               &accessKeyInfo);
     }
   }
 
   return false;
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -1594,17 +1594,17 @@ TabChild::RecvRealMouseButtonEvent(const
   if (pendingLayerization) {
     context.SetPendingLayerization();
   }
 
   WidgetMouseEvent localEvent(aEvent);
   localEvent.mWidget = mPuppetWidget;
   APZCCallbackHelper::ApplyCallbackTransform(localEvent, aGuid,
       mPuppetWidget->GetDefaultScale());
-  APZCCallbackHelper::DispatchWidgetEvent(localEvent);
+  DispatchWidgetEventViaAPZ(localEvent);
 
   if (aInputBlockId && aEvent.mFlags.mHandledByAPZ) {
     mAPZEventState->ProcessMouseEvent(aEvent, aGuid, aInputBlockId);
   }
   return IPC_OK();
 }
 
 
@@ -1643,16 +1643,23 @@ TabChild::MaybeCoalesceWheelEvent(const 
          mCoalescedWheelData.CanCoalesce(aEvent, aGuid, aInputBlockId))) {
       mCoalescedWheelData.Coalesce(aEvent, aGuid, aInputBlockId);
       return true;
     }
   }
   return false;
 }
 
+nsEventStatus
+TabChild::DispatchWidgetEventViaAPZ(WidgetGUIEvent& aEvent)
+{
+  aEvent.ResetWaitingReplyFromRemoteProcessState();
+  return APZCCallbackHelper::DispatchWidgetEvent(aEvent);
+}
+
 void
 TabChild::MaybeDispatchCoalescedWheelEvent()
 {
   if (mCoalescedWheelData.IsEmpty()) {
     return;
   }
   const WidgetWheelEvent* wheelEvent =
     mCoalescedWheelData.GetCoalescedWheelEvent();
@@ -1673,17 +1680,17 @@ TabChild::DispatchWheelEvent(const Widge
     nsCOMPtr<nsIDocument> document(GetDocument());
     APZCCallbackHelper::SendSetTargetAPZCNotification(
       mPuppetWidget, document, aEvent, aGuid, aInputBlockId);
   }
 
   localEvent.mWidget = mPuppetWidget;
   APZCCallbackHelper::ApplyCallbackTransform(localEvent, aGuid,
                                              mPuppetWidget->GetDefaultScale());
-  APZCCallbackHelper::DispatchWidgetEvent(localEvent);
+  DispatchWidgetEventViaAPZ(localEvent);
 
   if (localEvent.mCanTriggerSwipe) {
     SendRespondStartSwipeEvent(aInputBlockId, localEvent.TriggersSwipe());
   }
 
   if (aInputBlockId && aEvent.mFlags.mHandledByAPZ) {
     mAPZEventState->ProcessWheelEvent(localEvent, aGuid, aInputBlockId);
   }
@@ -1740,17 +1747,17 @@ TabChild::RecvRealTouchEvent(const Widge
         mSetAllowedTouchBehaviorCallback);
     }
     APZCCallbackHelper::SendSetTargetAPZCNotification(mPuppetWidget, document,
                                                       localEvent, aGuid,
                                                       aInputBlockId);
   }
 
   // Dispatch event to content (potentially a long-running operation)
-  nsEventStatus status = APZCCallbackHelper::DispatchWidgetEvent(localEvent);
+  nsEventStatus status = DispatchWidgetEventViaAPZ(localEvent);
 
   if (!AsyncPanZoomEnabled()) {
     // We shouldn't have any e10s platforms that have touch events enabled
     // without APZ.
     MOZ_ASSERT(false);
     return IPC_OK();
   }
 
@@ -1800,26 +1807,26 @@ TabChild::RecvRealDragEvent(const Widget
       do_GetService("@mozilla.org/widget/dragservice;1");
     if (dragService) {
       // This will dispatch 'drag' event at the source if the
       // drag transaction started in this process.
       dragService->FireDragEventAtSource(eDrag, aEvent.mModifiers);
     }
   }
 
-  APZCCallbackHelper::DispatchWidgetEvent(localEvent);
+  DispatchWidgetEventViaAPZ(localEvent);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 TabChild::RecvPluginEvent(const WidgetPluginEvent& aEvent)
 {
   WidgetPluginEvent localEvent(aEvent);
   localEvent.mWidget = mPuppetWidget;
-  nsEventStatus status = APZCCallbackHelper::DispatchWidgetEvent(localEvent);
+  nsEventStatus status = DispatchWidgetEventViaAPZ(localEvent);
   if (status != nsEventStatus_eConsumeNoDefault) {
     // If not consumed, we should call default action
     SendDefaultProcOfPluginEvent(aEvent);
   }
   return IPC_OK();
 }
 
 void
@@ -1911,41 +1918,46 @@ TabChild::RecvRealKeyEvent(const WidgetK
   // want to process any following keypress events.
   if (aEvent.mMessage == eKeyPress && mIgnoreKeyPressEvent) {
     return IPC_OK();
   }
 
   WidgetKeyboardEvent localEvent(aEvent);
   localEvent.mWidget = mPuppetWidget;
   localEvent.mUniqueId = aEvent.mUniqueId;
-  nsEventStatus status = APZCCallbackHelper::DispatchWidgetEvent(localEvent);
+  nsEventStatus status = DispatchWidgetEventViaAPZ(localEvent);
 
   // Update the end time of the possible repeated event so that we can skip
   // some incoming events in case event handling took long time.
   UpdateRepeatedKeyEventEndTime(localEvent);
 
   if (aEvent.mMessage == eKeyDown) {
     mIgnoreKeyPressEvent = status == nsEventStatus_eConsumeNoDefault;
   }
 
   if (localEvent.mFlags.mIsSuppressedOrDelayed) {
     localEvent.PreventDefault();
   }
 
   // If a response is desired from the content process, resend the key event.
-  // If mAccessKeyForwardedToChild is set, then don't resend the key event yet
-  // as RecvHandleAccessKey will do this.
-  if (localEvent.WantReplyFromContentProcess()) {
+  if (aEvent.WantReplyFromContentProcess()) {
+    // If the event's default isn't prevented but the status is no default,
+    // That means that the event was consumed by EventStateManager or something
+    // which is not a usual event handler.  In such case, prevent its default
+    // as a default handler.  For example, when an eKeyPress event matches
+    // with a content accesskey, and it's executed, peventDefault() of the
+    // event won't be called but the status is set to "no default".  Then,
+    // the event shouldn't be handled by nsMenuBarListener in the main process.
+    if (!localEvent.DefaultPrevented() &&
+        status == nsEventStatus_eConsumeNoDefault) {
+      localEvent.PreventDefault();
+    }
     SendReplyKeyEvent(localEvent);
   }
 
-  if (localEvent.mAccessKeyForwardedToChild) {
-    SendAccessKeyNotHandled(localEvent);
-  }
-
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 TabChild::RecvKeyEvent(const nsString& aType,
                        const int32_t& aKeyCode,
                        const int32_t& aCharCode,
                        const int32_t& aModifiers,
@@ -1957,27 +1969,27 @@ TabChild::RecvKeyEvent(const nsString& a
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 TabChild::RecvCompositionEvent(const WidgetCompositionEvent& aEvent)
 {
   WidgetCompositionEvent localEvent(aEvent);
   localEvent.mWidget = mPuppetWidget;
-  APZCCallbackHelper::DispatchWidgetEvent(localEvent);
+  DispatchWidgetEventViaAPZ(localEvent);
   Unused << SendOnEventNeedingAckHandled(aEvent.mMessage);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 TabChild::RecvSelectionEvent(const WidgetSelectionEvent& aEvent)
 {
   WidgetSelectionEvent localEvent(aEvent);
   localEvent.mWidget = mPuppetWidget;
-  APZCCallbackHelper::DispatchWidgetEvent(localEvent);
+  DispatchWidgetEventViaAPZ(localEvent);
   Unused << SendOnEventNeedingAckHandled(aEvent.mMessage);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 TabChild::RecvPasteTransferable(const IPCDataTransfer& aDataTransfer,
                                 const bool& aIsPrivateData,
                                 const IPC::Principal& aRequestingPrincipal)
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -797,16 +797,21 @@ private:
 
   bool MaybeCoalesceWheelEvent(const WidgetWheelEvent& aEvent,
                                const ScrollableLayerGuid& aGuid,
                                const uint64_t& aInputBlockId,
                                bool* aIsNextWheelEvent);
 
   void MaybeDispatchCoalescedWheelEvent();
 
+  /**
+   * Dispatch aEvent on aEvent.mWidget.
+   */
+  nsEventStatus DispatchWidgetEventViaAPZ(WidgetGUIEvent& aEvent);
+
   void DispatchWheelEvent(const WidgetWheelEvent& aEvent,
                           const ScrollableLayerGuid& aGuid,
                           const uint64_t& aInputBlockId);
 
   void InternalSetDocShellIsActive(bool aIsActive,
                                    bool aPreserveLayers);
 
   class DelayedDeleteRunnable;
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -2041,17 +2041,33 @@ TabParent::RecvReplyKeyEvent(const Widge
   nsIPresShell* presShell = doc->GetShell();
   NS_ENSURE_TRUE(presShell, IPC_OK());
   nsPresContext* presContext = presShell->GetPresContext();
   NS_ENSURE_TRUE(presContext, IPC_OK());
 
   AutoHandlingUserInputStatePusher userInpStatePusher(localEvent.IsTrusted(),
                                                       &localEvent, doc);
 
-  EventDispatcher::Dispatch(mFrameElement, presContext, &localEvent);
+  nsEventStatus status = nsEventStatus_eIgnore;
+
+  // Handle access key in this process before dispatching reply event because
+  // ESM handles it before dispatching the event to the DOM tree.
+  if (localEvent.mMessage == eKeyPress &&
+      (localEvent.ModifiersMatchWithAccessKey(AccessKeyType::eChrome) ||
+       localEvent.ModifiersMatchWithAccessKey(AccessKeyType::eContent))) {
+    RefPtr<EventStateManager> esm = presContext->EventStateManager();
+    AutoTArray<uint32_t, 10> accessCharCodes;
+    localEvent.GetAccessKeyCandidates(accessCharCodes);
+    if (esm->HandleAccessKey(&localEvent, presContext, accessCharCodes)) {
+      status = nsEventStatus_eConsumeNoDefault;
+    }
+  }
+
+  EventDispatcher::Dispatch(mFrameElement, presContext, &localEvent, nullptr,
+                            &status);
 
   if (!localEvent.DefaultPrevented() &&
       !localEvent.mFlags.mIsSynthesizedForTests) {
     nsCOMPtr<nsIWidget> widget = GetWidget();
     if (widget) {
       widget->PostHandleKeyEvent(&localEvent);
       localEvent.StopPropagation();
     }
@@ -2060,20 +2076,26 @@ TabParent::RecvReplyKeyEvent(const Widge
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 TabParent::RecvAccessKeyNotHandled(const WidgetKeyboardEvent& aEvent)
 {
   NS_ENSURE_TRUE(mFrameElement, IPC_OK());
 
+  // This is called only when this process had focus and HandleAccessKey
+  // message was posted to all remote process and each remote process didn't
+  // execute any content access keys.
+  // XXX If there were two or more remote processes, this may be called
+  //     twice or more for a keyboard event, that must be a bug.  But how to
+  //     detect if received event has already been handled?
+
   WidgetKeyboardEvent localEvent(aEvent);
   localEvent.MarkAsHandledInRemoteProcess();
   localEvent.mMessage = eAccessKeyNotFound;
-  localEvent.mAccessKeyForwardedToChild = false;
 
   // Here we convert the WidgetEvent that we received to an nsIDOMEvent
   // to be able to dispatch it to the <browser> element as the target element.
   nsIDocument* doc = mFrameElement->OwnerDoc();
   nsIPresShell* presShell = doc->GetShell();
   NS_ENSURE_TRUE(presShell, IPC_OK());
 
   if (presShell->CanDispatchEvent()) {
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -8156,17 +8156,21 @@ PresShell::HandleEventInternal(WidgetEve
 
     // 2. Give event to the DOM for third party and JS use.
     if (NS_SUCCEEDED(rv)) {
       bool wasHandlingKeyBoardEvent =
         nsContentUtils::IsHandlingKeyBoardEvent();
       if (aEvent->mClass == eKeyboardEventClass) {
         nsContentUtils::SetIsHandlingKeyBoardEvent(true);
       }
-      if (aEvent->IsAllowedToDispatchDOMEvent()) {
+      // If EventStateManager or something wants reply from remote process,
+      // PresShell shouldn't dispatch the event into the DOM tree because they
+      // don't have a chance to stop propagation in the system event group.
+      if (aEvent->IsAllowedToDispatchDOMEvent() &&
+          !aEvent->IsWaitingReplyFromRemoteProcess()) {
         MOZ_ASSERT(nsContentUtils::IsSafeToRunScript(),
           "Somebody changed aEvent to cause a DOM event!");
         nsPresShellEventCB eventCB(this);
         if (nsIFrame* target = GetCurrentEventFrame()) {
           if (target->OnlySystemGroupDispatch(aEvent->mMessage)) {
               aEvent->StopPropagation();
           }
         }
--- a/layout/xul/nsMenuBarListener.cpp
+++ b/layout/xul/nsMenuBarListener.cpp
@@ -266,18 +266,17 @@ nsMenuBarListener::KeyPress(nsIDOMEvent*
   InitAccessKey();
 
   if (mAccessKey)
   {
     // If accesskey handling was forwarded to a child process, wait for
     // the mozaccesskeynotfound event before handling accesskeys.
     WidgetKeyboardEvent* nativeKeyEvent =
       aKeyEvent->WidgetEventPtr()->AsKeyboardEvent();
-    if (!nativeKeyEvent ||
-        (nativeKeyEvent && nativeKeyEvent->mAccessKeyForwardedToChild)) {
+    if (!nativeKeyEvent) {
       return NS_OK;
     }
 
     nsCOMPtr<nsIDOMKeyEvent> keyEvent = do_QueryInterface(aKeyEvent);
     uint32_t keyCode, charCode;
     keyEvent->GetKeyCode(&keyCode);
     keyEvent->GetCharCode(&charCode);
 
@@ -296,16 +295,19 @@ nsMenuBarListener::KeyPress(nsIDOMEvent*
     }
 
     if (IsAccessKeyPressed(keyEvent) && hasAccessKeyCandidates) {
       // Do shortcut navigation.
       // A letter was pressed. We want to see if a shortcut gets matched. If
       // so, we'll know the menu got activated.
       nsMenuFrame* result = mMenuBarFrame->FindMenuWithShortcut(keyEvent);
       if (result) {
+        // TODO: If the event target is a remote process and not stopped
+        //       sending the event to it, we need to mark the event as
+        //       waiting reply from remote process and stop handling the event.
         mMenuBarFrame->SetActiveByKeyboard();
         mMenuBarFrame->SetActive(true);
         result->OpenMenu(true);
 
         // The opened menu will listen next keyup event.
         // Therefore, we should clear the keydown flags here.
         mAccessKeyDown = mAccessKeyDownCanceled = false;
 
--- a/widget/BasicEvents.h
+++ b/widget/BasicEvents.h
@@ -248,16 +248,30 @@ public:
     // a reply from content in a remote process.  So, callers should stop
     // propagation in this process first.
     NS_ASSERTION(PropagationStopped(),
                  "Why didn't you stop propagation in this process?");
     mNoRemoteProcessDispatch = false;
     mWantReplyFromContentProcess = true;
   }
   /**
+   * Reset "waiting reply from remote process" state.  This is useful when
+   * you dispatch a copy of an event coming from different process.
+   */
+  inline void ResetWaitingReplyFromRemoteProcessState()
+  {
+    if (IsWaitingReplyFromRemoteProcess()) {
+      // FYI: mWantReplyFromContentProcess is also used for indicating
+      //      "handled in remote process" state.  Therefore, only when
+      //      IsWaitingReplyFromRemoteProcess() returns true, this should
+      //      reset the flag.
+      mWantReplyFromContentProcess = false;
+    }
+  }
+  /**
    * Return true if the event handler should wait reply event.  I.e., if this
    * returns true, any event handler should do nothing with the event.
    */
   inline bool IsWaitingReplyFromRemoteProcess() const
   {
     return !mNoRemoteProcessDispatch && mWantReplyFromContentProcess;
   }
   /**
@@ -296,16 +310,22 @@ public:
   /**
    * Reset the cross process dispatching state.  This should be used when a
    * process receives the event because the state is in the sender.
    */
   inline void ResetCrossProcessDispatchingState()
   {
     MOZ_ASSERT(!IsCrossProcessForwardingStopped());
     mPostedToRemoteProcess = false;
+    // Ignore propagation state in the different process if it's marked as
+    // "waiting reply from remote process" because the process needs to
+    // stop propagation in the process until receiving a reply event.
+    if (IsWaitingReplyFromRemoteProcess()) {
+      mPropagationStopped = mImmediatePropagationStopped = false;
+    }
   }
   /**
    * Return true if the event has been posted to a remote process.
    * Note that MarkAsPostedToRemoteProcess() is called by
    * ParamTraits<mozilla::WidgetEvent>.  Therefore, it *might* be possible
    * that posting the event failed even if this returns true.  But that must
    * really rare.  If that'd be problem for you, you should unmark this in
    * TabParent or somewhere.
@@ -625,16 +645,24 @@ public:
    * Mark the event as waiting reply from remote process.
    * Note that this also stops immediate propagation in current process.
    */
   inline void MarkAsWaitingReplyFromRemoteProcess()
   {
     mFlags.MarkAsWaitingReplyFromRemoteProcess();
   }
   /**
+   * Reset "waiting reply from remote process" state.  This is useful when
+   * you dispatch a copy of an event coming from different process.
+   */
+  inline void ResetWaitingReplyFromRemoteProcessState()
+  {
+    mFlags.ResetWaitingReplyFromRemoteProcessState();
+  }
+  /**
    * Return true if the event handler should wait reply event.  I.e., if this
    * returns true, any event handler should do nothing with the event.
    */
   inline bool IsWaitingReplyFromRemoteProcess() const
   {
     return mFlags.IsWaitingReplyFromRemoteProcess();
   }
   /**
--- a/widget/TextEvents.h
+++ b/widget/TextEvents.h
@@ -149,17 +149,16 @@ private:
 
 protected:
   WidgetKeyboardEvent()
     : mNativeKeyEvent(nullptr)
     , mKeyCode(0)
     , mCharCode(0)
     , mPseudoCharCode(0)
     , mLocation(eKeyLocationStandard)
-    , mAccessKeyForwardedToChild(false)
     , mUniqueId(0)
 #ifdef XP_MACOSX
     , mNativeModifierFlags(0)
     , mNativeKeyCode(0)
 #endif // #ifdef XP_MACOSX
     , mKeyNameIndex(KEY_NAME_INDEX_Unidentified)
     , mCodeNameIndex(CODE_NAME_INDEX_UNKNOWN)
     , mIsRepeat(false)
@@ -178,17 +177,16 @@ public:
                       nsIWidget* aWidget,
                       EventClassID aEventClassID = eKeyboardEventClass)
     : WidgetInputEvent(aIsTrusted, aMessage, aWidget, aEventClassID)
     , mNativeKeyEvent(nullptr)
     , mKeyCode(0)
     , mCharCode(0)
     , mPseudoCharCode(0)
     , mLocation(eKeyLocationStandard)
-    , mAccessKeyForwardedToChild(false)
     , mUniqueId(0)
 #ifdef XP_MACOSX
     , mNativeModifierFlags(0)
     , mNativeKeyCode(0)
 #endif // #ifdef XP_MACOSX
     , mKeyNameIndex(KEY_NAME_INDEX_Unidentified)
     , mCodeNameIndex(CODE_NAME_INDEX_UNKNOWN)
     , mIsRepeat(false)
@@ -274,21 +272,16 @@ public:
   // unmodified value except Shift and AltGr.
   uint32_t mCharCode;
   // mPseudoCharCode is valid only when mMessage is an eKeyDown event.
   // This stores mCharCode value of keypress event which is fired with same
   // key value and same modifier state.
   uint32_t mPseudoCharCode;
   // One of eKeyLocation*
   uint32_t mLocation;
-  // True if accesskey handling was forwarded to the child via
-  // TabParent::HandleAccessKey. In this case, parent process menu access key
-  // handling should be delayed until it is determined that there exists no
-  // overriding access key in the content process.
-  bool mAccessKeyForwardedToChild;
   // Unique id associated with a keydown / keypress event. It's ok if this wraps
   // over long periods.
   uint32_t mUniqueId;
 
 #ifdef XP_MACOSX
   // Values given by a native NSEvent, for use with Cocoa NPAPI plugins.
   uint32_t mNativeModifierFlags;
   uint16_t mNativeKeyCode;
@@ -504,17 +497,16 @@ public:
 
     mKeyCode = aEvent.mKeyCode;
     mCharCode = aEvent.mCharCode;
     mPseudoCharCode = aEvent.mPseudoCharCode;
     mLocation = aEvent.mLocation;
     mAlternativeCharCodes = aEvent.mAlternativeCharCodes;
     mIsRepeat = aEvent.mIsRepeat;
     mIsComposing = aEvent.mIsComposing;
-    mAccessKeyForwardedToChild = aEvent.mAccessKeyForwardedToChild;
     mKeyNameIndex = aEvent.mKeyNameIndex;
     mCodeNameIndex = aEvent.mCodeNameIndex;
     mKeyValue = aEvent.mKeyValue;
     mCodeValue = aEvent.mCodeValue;
     // Don't copy mNativeKeyEvent because it may be referred after its instance
     // is destroyed.
     mNativeKeyEvent = nullptr;
     mUniqueId = aEvent.mUniqueId;
--- a/widget/nsGUIEventIPC.h
+++ b/widget/nsGUIEventIPC.h
@@ -444,17 +444,16 @@ struct ParamTraits<mozilla::WidgetKeyboa
                static_cast<mozilla::CodeNameIndexType>(aParam.mCodeNameIndex));
     WriteParam(aMsg, aParam.mKeyValue);
     WriteParam(aMsg, aParam.mCodeValue);
     WriteParam(aMsg, aParam.mKeyCode);
     WriteParam(aMsg, aParam.mCharCode);
     WriteParam(aMsg, aParam.mPseudoCharCode);
     WriteParam(aMsg, aParam.mAlternativeCharCodes);
     WriteParam(aMsg, aParam.mIsRepeat);
-    WriteParam(aMsg, aParam.mAccessKeyForwardedToChild);
     WriteParam(aMsg, aParam.mLocation);
     WriteParam(aMsg, aParam.mUniqueId);
     WriteParam(aMsg, aParam.mIsSynthesizedByTIP);
 #ifdef XP_MACOSX
     WriteParam(aMsg, aParam.mNativeKeyCode);
     WriteParam(aMsg, aParam.mNativeModifierFlags);
     WriteParam(aMsg, aParam.mNativeCharacters);
     WriteParam(aMsg, aParam.mNativeCharactersIgnoringModifiers);
@@ -482,17 +481,16 @@ struct ParamTraits<mozilla::WidgetKeyboa
         ReadParam(aMsg, aIter, &codeNameIndex) &&
         ReadParam(aMsg, aIter, &aResult->mKeyValue) &&
         ReadParam(aMsg, aIter, &aResult->mCodeValue) &&
         ReadParam(aMsg, aIter, &aResult->mKeyCode) &&
         ReadParam(aMsg, aIter, &aResult->mCharCode) &&
         ReadParam(aMsg, aIter, &aResult->mPseudoCharCode) &&
         ReadParam(aMsg, aIter, &aResult->mAlternativeCharCodes) &&
         ReadParam(aMsg, aIter, &aResult->mIsRepeat) &&
-        ReadParam(aMsg, aIter, &aResult->mAccessKeyForwardedToChild) &&
         ReadParam(aMsg, aIter, &aResult->mLocation) &&
         ReadParam(aMsg, aIter, &aResult->mUniqueId) &&
         ReadParam(aMsg, aIter, &aResult->mIsSynthesizedByTIP) &&
 #ifdef XP_MACOSX
         ReadParam(aMsg, aIter, &aResult->mNativeKeyCode) &&
         ReadParam(aMsg, aIter, &aResult->mNativeModifierFlags) &&
         ReadParam(aMsg, aIter, &aResult->mNativeCharacters) &&
         ReadParam(aMsg, aIter, &aResult->mNativeCharactersIgnoringModifiers) &&