Bug 289384 - Run the native key binding handlers from nsEditorEventListener; r=Neil,masayuki
authorEhsan Akhgari <ehsan@mozilla.com>
Wed, 22 Jan 2014 22:18:51 -0500
changeset 164780 30fefae6278f87adc35ba03d6470e70e19909831
parent 164779 9ba11d59bf3fdd04c6151d3893bcc60a1f378227
child 164781 0cc417a1f8438ea95766fe44c8330a2c01a7eb67
push id26060
push usercbook@mozilla.com
push dateThu, 23 Jan 2014 09:18:25 +0000
treeherdermozilla-central@d418cc97bacb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersNeil, masayuki
bugs289384
milestone29.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 289384 - Run the native key binding handlers from nsEditorEventListener; r=Neil,masayuki
dom/xbl/nsXBLWindowKeyHandler.cpp
dom/xbl/nsXBLWindowKeyHandler.h
editor/libeditor/base/nsEditorEventListener.cpp
editor/libeditor/base/nsEditorEventListener.h
editor/libeditor/html/tests/mochitest.ini
editor/libeditor/html/tests/test_bug289384.html
editor/libeditor/html/tests/test_htmleditor_keyevent_handling.html
layout/build/nsLayoutStatics.cpp
--- a/dom/xbl/nsXBLWindowKeyHandler.cpp
+++ b/dom/xbl/nsXBLWindowKeyHandler.cpp
@@ -9,38 +9,35 @@
 #include "nsIContent.h"
 #include "nsIAtom.h"
 #include "nsIDOMKeyEvent.h"
 #include "nsXBLService.h"
 #include "nsIServiceManager.h"
 #include "nsGkAtoms.h"
 #include "nsXBLDocumentInfo.h"
 #include "nsIDOMElement.h"
-#include "nsINativeKeyBindings.h"
-#include "nsIController.h"
 #include "nsFocusManager.h"
-#include "nsPIWindowRoot.h"
 #include "nsIURI.h"
 #include "nsNetUtil.h"
 #include "nsContentUtils.h"
 #include "nsXBLPrototypeBinding.h"
 #include "nsPIDOMWindow.h"
 #include "nsIDocShell.h"
 #include "nsIPresShell.h"
 #include "nsISelectionController.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/dom/Element.h"
 #include "nsEventStateManager.h"
+#include "nsIEditor.h"
+#include "nsIHTMLEditor.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
-static nsINativeKeyBindings *sNativeEditorBindings = nullptr;
-
 class nsXBLSpecialDocInfo : public nsIObserver
 {
 public:
   nsRefPtr<nsXBLDocumentInfo> mHTMLBindings;
   nsRefPtr<nsXBLDocumentInfo> mUserHTMLBindings;
 
   static const char sHTMLBindingStr[];
   static const char sUserHTMLBindingStr[];
@@ -225,108 +222,62 @@ BuildHandlerChain(nsIContent* aContent, 
 
 //
 // EnsureHandlers
 //    
 // Lazily load the XBL handlers. Overridden to handle being attached
 // to a particular element rather than the document
 //
 nsresult
-nsXBLWindowKeyHandler::EnsureHandlers(bool *aIsEditor)
+nsXBLWindowKeyHandler::EnsureHandlers()
 {
   nsCOMPtr<Element> el = GetElement();
   NS_ENSURE_STATE(!mWeakPtrForElement || el);
   if (el) {
     // We are actually a XUL <keyset>.
-    if (aIsEditor)
-      *aIsEditor = false;
-
     if (mHandler)
       return NS_OK;
 
     nsCOMPtr<nsIContent> content(do_QueryInterface(el));
     BuildHandlerChain(content, &mHandler);
   } else { // We are an XBL file of handlers.
     if (!sXBLSpecialDocInfo) {
       sXBLSpecialDocInfo = new nsXBLSpecialDocInfo();
       NS_ADDREF(sXBLSpecialDocInfo);
     }
     sXBLSpecialDocInfo->LoadDocInfo();
 
     // Now determine which handlers we should be using.
-    bool isEditor = IsEditor();
-    if (isEditor) {
+    if (IsHTMLEditableFieldFocused()) {
       sXBLSpecialDocInfo->GetAllHandlers("editor", &mHandler, &mUserHandler);
     }
     else {
       sXBLSpecialDocInfo->GetAllHandlers("browser", &mHandler, &mUserHandler);
     }
-
-    if (aIsEditor)
-      *aIsEditor = isEditor;
   }
 
   return NS_OK;
 }
 
-static nsINativeKeyBindings*
-GetEditorKeyBindings()
-{
-  static bool noBindings = false;
-  if (!sNativeEditorBindings && !noBindings) {
-    CallGetService(NS_NATIVEKEYBINDINGS_CONTRACTID_PREFIX "editor",
-                   &sNativeEditorBindings);
-
-    if (!sNativeEditorBindings) {
-      noBindings = true;
-    }
-  }
-
-  return sNativeEditorBindings;
-}
-
-static void
-DoCommandCallback(const char *aCommand, void *aData)
-{
-  nsCOMPtr<nsPIWindowRoot> root = do_QueryInterface(static_cast<EventTarget*>(aData));
-  if (!root) {
-    return;
-  }
-
-  nsCOMPtr<nsIController> controller;
-  root->GetControllerForCommand(aCommand, getter_AddRefs(controller));
-  if (!controller) {
-    return;
-  }
-
-  bool commandEnabled;
-  nsresult rv = controller->IsCommandEnabled(aCommand, &commandEnabled);
-  NS_ENSURE_SUCCESS_VOID(rv);
-  if (commandEnabled) {
-    controller->DoCommand(aCommand);
-  }
-}
-
 nsresult
 nsXBLWindowKeyHandler::WalkHandlers(nsIDOMKeyEvent* aKeyEvent, nsIAtom* aEventType)
 {
   bool prevent;
   aKeyEvent->GetDefaultPrevented(&prevent);
   if (prevent)
     return NS_OK;
 
   bool trustedEvent = false;
   // Don't process the event if it was not dispatched from a trusted source
   aKeyEvent->GetIsTrusted(&trustedEvent);
 
   if (!trustedEvent)
     return NS_OK;
 
-  bool isEditor;
-  nsresult rv = EnsureHandlers(&isEditor);
+  nsresult rv = EnsureHandlers();
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<Element> el = GetElement();
   if (!el) {
     if (mUserHandler) {
       WalkHandlersInternal(aKeyEvent, aEventType, mUserHandler);
       aKeyEvent->GetDefaultPrevented(&prevent);
       if (prevent)
@@ -338,55 +289,16 @@ nsXBLWindowKeyHandler::WalkHandlers(nsID
   // skip keysets that are disabled
   if (content && content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled,
                                       nsGkAtoms::_true, eCaseMatters)) {
     return NS_OK;
   }
 
   WalkHandlersInternal(aKeyEvent, aEventType, mHandler);
 
-  aKeyEvent->GetDefaultPrevented(&prevent);
-  if (prevent) {
-    return NS_OK;
-  }
-
-  // XXX Shouldn't we prefer the native key binding rather than our key
-  //     bindings?  I.e., should we call WalkHandlersInternal() after this
-  //     block?
-  if (isEditor && GetEditorKeyBindings()) {
-    WidgetKeyboardEvent* keyEvent =
-      aKeyEvent->GetInternalNSEvent()->AsKeyboardEvent();
-    MOZ_ASSERT(keyEvent,
-               "DOM key event's internal event must be WidgetKeyboardEvent");
-    bool handled = false;
-    switch (keyEvent->message) {
-      case NS_KEY_PRESS:
-        handled = sNativeEditorBindings->KeyPress(*keyEvent,
-                                                  DoCommandCallback,
-                                                  mTarget);
-        break;
-      case NS_KEY_UP:
-        handled = sNativeEditorBindings->KeyUp(*keyEvent,
-                                               DoCommandCallback,
-                                               mTarget);
-        break;
-      case NS_KEY_DOWN:
-        handled = sNativeEditorBindings->KeyDown(*keyEvent,
-                                                 DoCommandCallback,
-                                                 mTarget);
-        break;
-      default:
-        MOZ_CRASH("Unknown key message");
-    }
-
-    if (handled)
-      aKeyEvent->PreventDefault();
-
-  }
-  
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsXBLWindowKeyHandler::HandleEvent(nsIDOMEvent* aEvent)
 {
   nsCOMPtr<nsIDOMKeyEvent> keyEvent(do_QueryInterface(aEvent));
   NS_ENSURE_TRUE(keyEvent, NS_ERROR_INVALID_ARG);
@@ -417,50 +329,55 @@ nsXBLWindowKeyHandler::EventMatched(nsXB
                                     nsIAtom* inEventType,
                                     nsIDOMKeyEvent* inEvent,
                                     uint32_t aCharCode, bool aIgnoreShiftKey)
 {
   return inHandler->KeyEventMatched(inEventType, inEvent, aCharCode,
                                     aIgnoreShiftKey);
 }
 
-/* static */ void
-nsXBLWindowKeyHandler::ShutDown()
+bool
+nsXBLWindowKeyHandler::IsHTMLEditableFieldFocused()
 {
-  NS_IF_RELEASE(sNativeEditorBindings);
-}
-
-//
-// IsEditor
-//
-// Determine if the document we're working with is Editor or Browser
-//
-bool
-nsXBLWindowKeyHandler::IsEditor()
-{
-  // XXXndeakin even though this is only used for key events which should be
-  // going to the focused frame anyway, this doesn't seem like the right way
-  // to determine if something is an editor.
   nsIFocusManager* fm = nsFocusManager::GetFocusManager();
   if (!fm)
     return false;
 
   nsCOMPtr<nsIDOMWindow> focusedWindow;
   fm->GetFocusedWindow(getter_AddRefs(focusedWindow));
   if (!focusedWindow)
     return false;
 
   nsCOMPtr<nsPIDOMWindow> piwin(do_QueryInterface(focusedWindow));
   nsIDocShell *docShell = piwin->GetDocShell();
-  nsCOMPtr<nsIPresShell> presShell;
-  if (docShell)
-    presShell = docShell->GetPresShell();
+  if (!docShell) {
+    return false;
+  }
+
+  nsCOMPtr<nsIEditor> editor;
+  docShell->GetEditor(getter_AddRefs(editor));
+  nsCOMPtr<nsIHTMLEditor> htmlEditor = do_QueryInterface(editor);
+  if (!htmlEditor) {
+    return false;
+  }
 
-  if (presShell) {
-    return presShell->GetSelectionFlags() == nsISelectionDisplay::DISPLAY_ALL;
+  nsCOMPtr<nsIDOMElement> focusedElement;
+  fm->GetFocusedElement(getter_AddRefs(focusedElement));
+  nsCOMPtr<nsINode> focusedNode = do_QueryInterface(focusedElement);
+  if (focusedNode) {
+    // If there is a focused element, make sure it's in the active editing host.
+    // Note that GetActiveEditingHost finds the current editing host based on
+    // the document's selection.  Even though the document selection is usually
+    // collapsed to where the focus is, but the page may modify the selection
+    // without our knowledge, in which case this check will do something useful.
+    nsCOMPtr<Element> activeEditingHost = htmlEditor->GetActiveEditingHost();
+    if (!activeEditingHost) {
+      return false;
+    }
+    return nsContentUtils::ContentIsDescendantOf(focusedNode, activeEditingHost);
   }
 
   return false;
 }
 
 //
 // WalkHandlersInternal and WalkHandlersAndExecute
 //
--- a/dom/xbl/nsXBLWindowKeyHandler.h
+++ b/dom/xbl/nsXBLWindowKeyHandler.h
@@ -26,43 +26,40 @@ class nsXBLWindowKeyHandler : public nsI
 {
 public:
   nsXBLWindowKeyHandler(nsIDOMElement* aElement, mozilla::dom::EventTarget* aTarget);
   virtual ~nsXBLWindowKeyHandler();
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIDOMEVENTLISTENER
 
-  // release globals
-  static NS_HIDDEN_(void) ShutDown();
-
 protected:
   nsresult WalkHandlers(nsIDOMKeyEvent* aKeyEvent, nsIAtom* aEventType);
 
   // walk the handlers, looking for one to handle the event
   nsresult WalkHandlersInternal(nsIDOMKeyEvent* aKeyEvent,
                                 nsIAtom* aEventType, 
                                 nsXBLPrototypeHandler* aHandler);
 
   // walk the handlers for aEvent, aCharCode and aIgnoreShiftKey
   bool WalkHandlersAndExecute(nsIDOMKeyEvent* aKeyEvent, nsIAtom* aEventType,
                                 nsXBLPrototypeHandler* aHandler,
                                 uint32_t aCharCode, bool aIgnoreShiftKey);
 
   // lazily load the handlers. Overridden to handle being attached
   // to a particular element rather than the document
-  nsresult EnsureHandlers(bool *aIsEditor);
+  nsresult EnsureHandlers();
 
   // check if the given handler cares about the given key event
   bool EventMatched(nsXBLPrototypeHandler* inHandler, nsIAtom* inEventType,
                       nsIDOMKeyEvent* inEvent, uint32_t aCharCode,
                       bool aIgnoreShiftKey);
 
-  // are we working with editor or browser?
-  bool IsEditor() ;
+  // Is an HTML editable element focused
+  bool IsHTMLEditableFieldFocused();
 
   // Returns the element which was passed as a parameter to the constructor,
   // unless the element has been removed from the document.
   already_AddRefed<mozilla::dom::Element> GetElement();
   // Using weak pointer to the DOM Element.
   nsWeakPtr              mWeakPtrForElement;
   mozilla::dom::EventTarget* mTarget; // weak ref
 
--- a/editor/libeditor/base/nsEditorEventListener.cpp
+++ b/editor/libeditor/base/nsEditorEventListener.cpp
@@ -12,16 +12,17 @@
 #include "nsDebug.h"                    // for NS_ENSURE_TRUE, etc
 #include "nsEditor.h"                   // for nsEditor, etc
 #include "nsEditorEventListener.h"
 #include "nsEventListenerManager.h"     // for nsEventListenerManager
 #include "nsFocusManager.h"             // for nsFocusManager
 #include "nsGkAtoms.h"                  // for nsGkAtoms, nsGkAtoms::input
 #include "nsIClipboard.h"               // for nsIClipboard, etc
 #include "nsIContent.h"                 // for nsIContent
+#include "nsIController.h"              // for nsIController
 #include "nsID.h"
 #include "nsIDOMDOMStringList.h"        // for nsIDOMDOMStringList
 #include "nsIDOMDataTransfer.h"         // for nsIDOMDataTransfer
 #include "nsIDOMDocument.h"             // for nsIDOMDocument
 #include "nsIDOMDragEvent.h"            // for nsIDOMDragEvent
 #include "nsIDOMElement.h"              // for nsIDOMElement
 #include "nsIDOMEvent.h"                // for nsIDOMEvent
 #include "nsIDOMEventTarget.h"          // for nsIDOMEventTarget
@@ -30,39 +31,87 @@
 #include "nsIDOMNode.h"                 // for nsIDOMNode
 #include "nsIDOMRange.h"                // for nsIDOMRange
 #include "nsIDocument.h"                // for nsIDocument
 #include "nsIEditor.h"                  // for nsEditor::GetSelection, etc
 #include "nsIEditorIMESupport.h"
 #include "nsIEditorMailSupport.h"       // for nsIEditorMailSupport
 #include "nsIFocusManager.h"            // for nsIFocusManager
 #include "nsIFormControl.h"             // for nsIFormControl, etc
+#include "nsIHTMLEditor.h"              // for nsIHTMLEditor
 #include "nsIMEStateManager.h"          // for nsIMEStateManager
+#include "nsINativeKeyBindings.h"       // for nsINativeKeyBindings
 #include "nsINode.h"                    // for nsINode, ::NODE_IS_EDITABLE, etc
 #include "nsIPlaintextEditor.h"         // for nsIPlaintextEditor, etc
 #include "nsIPresShell.h"               // for nsIPresShell
 #include "nsIPrivateTextEvent.h"        // for nsIPrivateTextEvent
 #include "nsIPrivateTextRange.h"        // for nsIPrivateTextRangeList
 #include "nsISelection.h"               // for nsISelection
 #include "nsISelectionController.h"     // for nsISelectionController, etc
 #include "nsISelectionPrivate.h"        // for nsISelectionPrivate
 #include "nsITransferable.h"            // for kFileMime, kHTMLMime, etc
 #include "nsLiteralString.h"            // for NS_LITERAL_STRING
+#include "nsPIWindowRoot.h"             // for nsPIWindowRoot
 #include "nsServiceManagerUtils.h"      // for do_GetService
 #include "nsString.h"                   // for nsAutoString
 #ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH
 #include "nsContentUtils.h"             // for nsContentUtils, etc
 #include "nsIBidiKeyboard.h"            // for nsIBidiKeyboard
 #endif
 
 class nsPresContext;
 
 using namespace mozilla;
 using mozilla::dom::EventTarget;
 
+static nsINativeKeyBindings *sNativeEditorBindings = nullptr;
+
+static nsINativeKeyBindings*
+GetEditorKeyBindings()
+{
+  static bool noBindings = false;
+  if (!sNativeEditorBindings && !noBindings) {
+    CallGetService(NS_NATIVEKEYBINDINGS_CONTRACTID_PREFIX "editor",
+                   &sNativeEditorBindings);
+
+    if (!sNativeEditorBindings) {
+      noBindings = true;
+    }
+  }
+
+  return sNativeEditorBindings;
+}
+
+static void
+DoCommandCallback(const char *aCommand, void *aData)
+{
+  nsIDocument* doc = static_cast<nsIDocument*>(aData);
+  nsPIDOMWindow* win = doc->GetWindow();
+  if (!win) {
+    return;
+  }
+  nsCOMPtr<nsPIWindowRoot> root = win->GetTopWindowRoot();
+  if (!root) {
+    return;
+  }
+
+  nsCOMPtr<nsIController> controller;
+  root->GetControllerForCommand(aCommand, getter_AddRefs(controller));
+  if (!controller) {
+    return;
+  }
+
+  bool commandEnabled;
+  nsresult rv = controller->IsCommandEnabled(aCommand, &commandEnabled);
+  NS_ENSURE_SUCCESS_VOID(rv);
+  if (commandEnabled) {
+    controller->DoCommand(aCommand);
+  }
+}
+
 nsEditorEventListener::nsEditorEventListener() :
   mEditor(nullptr), mCommitText(false),
   mInTransaction(false)
 #ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH
   , mHaveBidiKeyboards(false)
   , mShouldSwitchTextDirection(false)
   , mSwitchToRTL(false)
 #endif
@@ -72,16 +121,22 @@ nsEditorEventListener::nsEditorEventList
 nsEditorEventListener::~nsEditorEventListener() 
 {
   if (mEditor) {
     NS_WARNING("We're not uninstalled");
     Disconnect();
   }
 }
 
+/* static */ void
+nsEditorEventListener::ShutDown()
+{
+  NS_IF_RELEASE(sNativeEditorBindings);
+}
+
 nsresult
 nsEditorEventListener::Connect(nsEditor* aEditor)
 {
   NS_ENSURE_ARG(aEditor);
 
 #ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH
   nsIBidiKeyboard* bidiKeyboard = nsContentUtils::GetBidiKeyboard();
   if (bidiKeyboard) {
@@ -444,17 +499,43 @@ nsEditorEventListener::KeyPress(nsIDOMEv
   }
 
   nsCOMPtr<nsIDOMKeyEvent> keyEvent = do_QueryInterface(aKeyEvent);
   if (!keyEvent) {
     //non-key event passed to keypress.  bad things.
     return NS_OK;
   }
 
-  return mEditor->HandleKeyPressEvent(keyEvent);
+  nsresult rv = mEditor->HandleKeyPressEvent(keyEvent);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  aKeyEvent->GetDefaultPrevented(&defaultPrevented);
+  if (defaultPrevented) {
+    return NS_OK;
+  }
+
+  if (GetEditorKeyBindings() && ShouldHandleNativeKeyBindings(aKeyEvent)) {
+    // Now, ask the native key bindings to handle the event.
+    // XXX Note that we're not passing the keydown/keyup events to the native
+    // key bindings, which should be OK since those events are only handled on
+    // Windows for now, where we don't have native key bindings.
+    WidgetKeyboardEvent* keyEvent =
+      aKeyEvent->GetInternalNSEvent()->AsKeyboardEvent();
+    MOZ_ASSERT(keyEvent,
+               "DOM key event's internal event must be WidgetKeyboardEvent");
+    nsCOMPtr<nsIDocument> doc = mEditor->GetDocument();
+    bool handled = sNativeEditorBindings->KeyPress(*keyEvent,
+                                                   DoCommandCallback,
+                                                   doc);
+    if (handled) {
+      aKeyEvent->PreventDefault();
+    }
+  }
+
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsEditorEventListener::MouseClick(nsIDOMEvent* aMouseEvent)
 {
   NS_ENSURE_TRUE(mEditor, NS_ERROR_NOT_AVAILABLE);
 
   nsCOMPtr<nsIDOMMouseEvent> mouseEvent = do_QueryInterface(aMouseEvent);
@@ -924,8 +1005,40 @@ nsEditorEventListener::IsFileControlText
       nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(parent);
       MOZ_ASSERT(formControl);
       return formControl->GetType() == NS_FORM_INPUT_FILE;
     }
   }
   return false;
 }
 
+bool
+nsEditorEventListener::ShouldHandleNativeKeyBindings(nsIDOMEvent* aKeyEvent)
+{
+  // Only return true if the target of the event is a desendant of the active
+  // editing host in order to match the similar decision made in
+  // nsXBLWindowKeyHandler.
+  // Note that IsAcceptableInputEvent doesn't check for the active editing
+  // host for keyboard events, otherwise this check would have been
+  // unnecessary.  IsAcceptableInputEvent currently makes a similar check for
+  // mouse events.
+
+  nsCOMPtr<nsIDOMEventTarget> target;
+  aKeyEvent->GetTarget(getter_AddRefs(target));
+  nsCOMPtr<nsIContent> targetContent = do_QueryInterface(target);
+  if (!targetContent) {
+    return false;
+  }
+
+  nsCOMPtr<nsIHTMLEditor> htmlEditor =
+    do_QueryInterface(static_cast<nsIEditor*>(mEditor));
+  if (!htmlEditor) {
+    return false;
+  }
+
+  nsIContent* editingHost = htmlEditor->GetActiveEditingHost();
+  if (!editingHost) {
+    return false;
+  }
+
+  return nsContentUtils::ContentIsDescendantOf(targetContent, editingHost);
+}
+
--- a/editor/libeditor/base/nsEditorEventListener.h
+++ b/editor/libeditor/base/nsEditorEventListener.h
@@ -54,29 +54,32 @@ public:
   NS_IMETHOD MouseDown(nsIDOMEvent* aMouseEvent);
   NS_IMETHOD MouseUp(nsIDOMEvent* aMouseEvent) { return NS_OK; }
   NS_IMETHOD MouseClick(nsIDOMEvent* aMouseEvent);
   NS_IMETHOD Focus(nsIDOMEvent* aEvent);
   NS_IMETHOD Blur(nsIDOMEvent* aEvent);
 
   void SpellCheckIfNeeded();
 
+  static NS_HIDDEN_(void) ShutDown();
+
 protected:
   nsresult InstallToEditor();
   void UninstallFromEditor();
 
   bool CanDrop(nsIDOMDragEvent* aEvent);
   nsresult DragEnter(nsIDOMDragEvent* aDragEvent);
   nsresult DragOver(nsIDOMDragEvent* aDragEvent);
   nsresult DragExit(nsIDOMDragEvent* aDragEvent);
   nsresult Drop(nsIDOMDragEvent* aDragEvent);
   nsresult DragGesture(nsIDOMDragEvent* aDragEvent);
   void CleanupDragDropCaret();
   already_AddRefed<nsIPresShell> GetPresShell();
   bool IsFileControlTextBox();
+  bool ShouldHandleNativeKeyBindings(nsIDOMEvent* aKeyEvent);
 
 protected:
   nsEditor* mEditor; // weak
   nsRefPtr<nsCaret> mCaret;
   bool mCommitText;
   bool mInTransaction;
 #ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH
   bool mHaveBidiKeyboards;
--- a/editor/libeditor/html/tests/mochitest.ini
+++ b/editor/libeditor/html/tests/mochitest.ini
@@ -60,16 +60,18 @@ support-files =
   data/cfhtml-nocontext.txt
   file_bug549262.html
   file_bug674770-1.html
   file_select_all_without_body.html
   green.png
 
 [test_CF_HTML_clipboard.html]
 [test_bug200416.html]
+[test_bug289384.html]
+skip-if = os != "mac"
 [test_bug290026.html]
 [test_bug291780.html]
 [test_bug316447.html]
 [test_bug332636.html]
 [test_bug332636.html^headers^]
 [test_bug372345.html]
 [test_bug404320.html]
 [test_bug410986.html]
new file mode 100644
--- /dev/null
+++ b/editor/libeditor/html/tests/test_bug289384.html
@@ -0,0 +1,49 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=289384
+-->
+<head>
+  <title>Test for Bug 289384</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=289384">Mozilla Bug 289384</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+
+addLoadEvent(function() {
+  var win = window.open("data:text/html,<a href=\"data:text/html,<body contenteditable onload='opener.continueTest(window);'>foo bar</body>\">link</a>", "", "test-289384");
+  win.addEventListener("load", function onLoad() {
+    win.removeEventListener("load", onLoad);
+    win.document.querySelector("a").click();
+  }, false);
+});
+
+function continueTest(win) {
+  SimpleTest.waitForFocus(function() {
+    var doc = win.document;
+    var sel = win.getSelection();
+    doc.body.focus();
+    sel.collapse(doc.body.firstChild, 3);
+    SimpleTest.executeSoon(function() {
+      synthesizeKey("VK_LEFT", {accelKey: true}, win);
+      ok(sel.isCollapsed, "The selection must be collapsed");
+      is(sel.anchorNode, doc.body.firstChild, "The anchor node should be the body element's text node");
+      is(sel.anchorOffset, 0, "The anchor offset should be 0");
+      win.close();
+      SimpleTest.finish();
+    });
+  }, win);
+}
+
+</script>
+</pre>
+</body>
+</html>
--- a/editor/libeditor/html/tests/test_htmleditor_keyevent_handling.html
+++ b/editor/libeditor/html/tests/test_htmleditor_keyevent_handling.html
@@ -168,45 +168,45 @@ function runTests()
     check(aDescription + "Shift+Backspace", true, true, true);
 
     reset("");
     synthesizeKey("VK_BACK_SPACE", { ctrlKey: true });
     check(aDescription + "Ctrl+Backspace", true, true, aIsReadonly);
 
     reset("");
     synthesizeKey("VK_BACK_SPACE", { altKey: true });
-    check(aDescription + "Alt+Backspace", true, true, aIsReadonly);
+    check(aDescription + "Alt+Backspace", true, true, aIsReadonly || kIsMac);
 
     reset("");
     synthesizeKey("VK_BACK_SPACE", { metaKey: true });
     check(aDescription + "Meta+Backspace", true, true, aIsReadonly);
 
     reset("");
     synthesizeKey("VK_BACK_SPACE", { osKey: true });
     check(aDescription + "OS+Backspace", true, true, aIsReadonly);
 
     // Delete key:
     //   If editor is readonly, it doesn't consume.
     //   If editor is editable, delete is consumed.
     //   Otherwise, editor doesn't consume the event.
     reset("");
     synthesizeKey("VK_DELETE", { });
-    check(aDescription + "Delete", true, true, !aIsReadonly);
+    check(aDescription + "Delete", true, true, !aIsReadonly || kIsMac);
 
     reset("");
     synthesizeKey("VK_DELETE", { shiftKey: true });
-    check(aDescription + "Shift+Delete", true, true, false);
+    check(aDescription + "Shift+Delete", true, true, kIsMac);
 
     reset("");
     synthesizeKey("VK_DELETE", { ctrlKey: true });
     check(aDescription + "Ctrl+Delete", true, true, false);
 
     reset("");
     synthesizeKey("VK_DELETE", { altKey: true });
-    check(aDescription + "Alt+Delete", true, true, false);
+    check(aDescription + "Alt+Delete", true, true, kIsMac);
 
     reset("");
     synthesizeKey("VK_DELETE", { metaKey: true });
     check(aDescription + "Meta+Delete", true, true, false);
 
     reset("");
     synthesizeKey("VK_DELETE", { osKey: true });
     check(aDescription + "OS+Delete", true, true, false);
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -18,32 +18,32 @@
 #include "nsCSSKeywords.h"
 #include "nsCSSParser.h"
 #include "nsCSSProps.h"
 #include "nsCSSPseudoClasses.h"
 #include "nsCSSPseudoElements.h"
 #include "nsCSSRendering.h"
 #include "mozilla/dom/Attr.h"
 #include "nsDOMClassInfo.h"
+#include "nsEditorEventListener.h"
 #include "nsEventListenerManager.h"
 #include "nsFrame.h"
 #include "nsGlobalWindow.h"
 #include "nsGkAtoms.h"
 #include "nsImageFrame.h"
 #include "nsLayoutStylesheetCache.h"
 #include "nsNodeInfo.h"
 #include "nsRange.h"
 #include "nsRegion.h"
 #include "nsRepeatService.h"
 #include "nsFloatManager.h"
 #include "nsSprocketLayout.h"
 #include "nsStackLayout.h"
 #include "nsStyleSet.h"
 #include "nsTextControlFrame.h"
-#include "nsXBLWindowKeyHandler.h"
 #include "nsXBLService.h"
 #include "txMozillaXSLTProcessor.h"
 #include "nsTreeSanitizer.h"
 #include "nsCellMap.h"
 #include "nsTextFrame.h"
 #include "nsCCUncollectableMarker.h"
 #include "nsTextFragment.h"
 #include "nsCSSRuleProcessor.h"
@@ -345,17 +345,17 @@ nsLayoutStatics::Shutdown()
   nsContentUtils::Shutdown();
   nsLayoutStylesheetCache::Shutdown();
   NS_NameSpaceManagerShutdown();
 
   ShutdownJSEnvironment();
   nsGlobalWindow::ShutDown();
   nsDOMClassInfo::ShutDown();
   nsListControlFrame::Shutdown();
-  nsXBLWindowKeyHandler::ShutDown();
+  nsEditorEventListener::ShutDown();
   nsXBLService::Shutdown();
   nsAutoCopyListener::Shutdown();
   FrameLayerBuilder::Shutdown();
 
 #ifdef MOZ_MEDIA_PLUGINS
   MediaPluginHost::Shutdown();
 #endif