Bug 1119609 part.7 Make TextInputProcerros possible to share modifier state r=smaug, sr=smaug
authorMasayuki Nakano <masayuki@d-toybox.com>
Thu, 19 Feb 2015 15:50:19 +0900
changeset 256961 87ed00d8ae5cfc0dacd1264f644834d8e61cede8
parent 256960 d97c2fcf2bf8155a843123d294a851600e41ea55
child 256962 cd6067b851b44adac4be50f0b00d2ea07b9ad5fc
push id4610
push userjlund@mozilla.com
push dateMon, 30 Mar 2015 18:32:55 +0000
treeherdermozilla-beta@4df54044d9ef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug, smaug
bugs1119609
milestone38.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 1119609 part.7 Make TextInputProcerros possible to share modifier state r=smaug, sr=smaug
dom/base/TextInputProcessor.cpp
dom/base/TextInputProcessor.h
dom/base/test/chrome/window_nsITextInputProcessor.xul
dom/events/UIEvent.cpp
dom/interfaces/base/nsITextInputProcessor.idl
widget/BasicEvents.h
widget/WidgetEventImpl.cpp
--- a/dom/base/TextInputProcessor.cpp
+++ b/dom/base/TextInputProcessor.cpp
@@ -591,16 +591,48 @@ TextInputProcessor::Keyup(nsIDOMKeyEvent
 
   nsEventStatus status = *aDoDefault ? nsEventStatus_eIgnore :
                                        nsEventStatus_eConsumeNoDefault;
   mDispatcher->DispatchKeyboardEvent(NS_KEY_UP, keyEvent, status);
   *aDoDefault = (status != nsEventStatus_eConsumeNoDefault);
   return NS_OK;
 }
 
+NS_IMETHODIMP
+TextInputProcessor::GetModifierState(const nsAString& aModifierKeyName,
+                                     bool* aActive)
+{
+  MOZ_RELEASE_ASSERT(aActive, "aActive must not be null");
+  MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
+  if (!mModifierKeyDataArray) {
+    *aActive = false;
+    return NS_OK;
+  }
+  Modifiers activeModifiers = mModifierKeyDataArray->GetActiveModifiers();
+  Modifiers modifier = WidgetInputEvent::GetModifier(aModifierKeyName);
+  *aActive = ((activeModifiers & modifier) != 0);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TextInputProcessor::ShareModifierStateOf(nsITextInputProcessor* aOther)
+{
+  MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
+  if (!aOther) {
+    mModifierKeyDataArray = nullptr;
+    return NS_OK;
+  }
+  TextInputProcessor* other = static_cast<TextInputProcessor*>(aOther);
+  if (!other->mModifierKeyDataArray) {
+    other->mModifierKeyDataArray = new ModifierKeyDataArray();
+  }
+  mModifierKeyDataArray = other->mModifierKeyDataArray;
+  return NS_OK;
+}
+
 /******************************************************************************
  * TextInputProcessor::ModifierKeyData
  ******************************************************************************/
 TextInputProcessor::ModifierKeyData::ModifierKeyData(
   const WidgetKeyboardEvent& aKeyboardEvent)
   : mKeyNameIndex(aKeyboardEvent.mKeyNameIndex)
   , mCodeNameIndex(aKeyboardEvent.mCodeNameIndex)
 {
--- a/dom/base/TextInputProcessor.h
+++ b/dom/base/TextInputProcessor.h
@@ -3,16 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_textinputprocessor_h_
 #define mozilla_dom_textinputprocessor_h_
 
 #include "mozilla/EventForwards.h"
 #include "mozilla/TextEventDispatcherListener.h"
+#include "nsAutoPtr.h"
 #include "nsITextInputProcessor.h"
 #include "nsITextInputProcessorCallback.h"
 #include "nsTArray.h"
 
 namespace mozilla {
 
 namespace widget{
 class TextEventDispatcher;
@@ -73,42 +74,60 @@ private:
     {
       return mKeyNameIndex == aOther.mKeyNameIndex &&
              mCodeNameIndex == aOther.mCodeNameIndex;
     }
   };
 
   class ModifierKeyDataArray : public nsTArray<ModifierKeyData>
   {
+    NS_INLINE_DECL_REFCOUNTING(ModifierKeyDataArray)
+
   public:
     Modifiers GetActiveModifiers() const;
     void ActivateModifierKey(const ModifierKeyData& aModifierKeyData);
     void InactivateModifierKey(const ModifierKeyData& aModifierKeyData);
     void ToggleModifierKey(const ModifierKeyData& aModifierKeyData);
+
+  private:
+    virtual ~ModifierKeyDataArray() { }
   };
 
   Modifiers GetActiveModifiers() const
   {
-    return mModifierKeyDataArray.GetActiveModifiers();
+    return mModifierKeyDataArray ?
+      mModifierKeyDataArray->GetActiveModifiers() : 0;
+  }
+  void EnsureModifierKeyDataArray()
+  {
+    if (mModifierKeyDataArray) {
+      return;
+    }
+    mModifierKeyDataArray = new ModifierKeyDataArray();
   }
   void ActivateModifierKey(const ModifierKeyData& aModifierKeyData)
   {
-    mModifierKeyDataArray.ActivateModifierKey(aModifierKeyData);
+    EnsureModifierKeyDataArray();
+    mModifierKeyDataArray->ActivateModifierKey(aModifierKeyData);
   }
   void InactivateModifierKey(const ModifierKeyData& aModifierKeyData)
   {
-    mModifierKeyDataArray.InactivateModifierKey(aModifierKeyData);
+    if (!mModifierKeyDataArray) {
+      return;
+    }
+    mModifierKeyDataArray->InactivateModifierKey(aModifierKeyData);
   }
   void ToggleModifierKey(const ModifierKeyData& aModifierKeyData)
   {
-    mModifierKeyDataArray.ToggleModifierKey(aModifierKeyData);
+    EnsureModifierKeyDataArray();
+    mModifierKeyDataArray->ToggleModifierKey(aModifierKeyData);
   }
 
   TextEventDispatcher* mDispatcher; // [Weak]
   nsCOMPtr<nsITextInputProcessorCallback> mCallback;
-  ModifierKeyDataArray mModifierKeyDataArray;
+  nsRefPtr<ModifierKeyDataArray> mModifierKeyDataArray;
 
   bool mForTests;
 };
 
 } // namespace mozilla
 
 #endif // #ifndef mozilla_dom_textinputprocessor_h_
--- a/dom/base/test/chrome/window_nsITextInputProcessor.xul
+++ b/dom/base/test/chrome/window_nsITextInputProcessor.xul
@@ -1150,16 +1150,21 @@ function runKeyTests()
     // Unlock lockable modifier if the key is a lockable modifier key.
     if (kKeyToKeyCode[i].isLockableModifier) {
       TIP.keydown(keyEvent, baseFlags);
       TIP.keyup(keyEvent, baseFlags);
     }
   }
 
   // Modifier state tests
+  var sharedTIP = createTIP();
+  ok(sharedTIP.beginInputTransactionForTests(otherWindow),
+     description + "sharedTIP.beginInputTransactionForTests(otherWindow) should return true");
+  TIP.shareModifierStateOf(sharedTIP);
+  var independentTIP = createTIP();
   const kModifierKeys = [
     { key: "Alt",        code: "AltLeft",      isLockable: false },
     { key: "Alt",        code: "AltRight",     isLockable: false },
     { key: "AltGraph",   code: "AltRight",     isLockable: false },
     { key: "CapsLock",   code: "CapsLock",     isLockable: true },
     { key: "Control",    code: "ControlLeft",  isLockable: false },
     { key: "Control",    code: "ControlRight", isLockable: false },
     { key: "Fn",         code: "Fn",           isLockable: false },
@@ -1199,16 +1204,28 @@ function runKeyTests()
     is(aEvent.shiftKey, aModifiers.indexOf("Shift") >= 0,
        desc + ", .shiftKey value is wrong");
     for (var i = 0; i < kModifiers.length; i++) {
       is(aEvent.getModifierState(kModifiers[i]), aModifiers.indexOf(kModifiers[i]) >= 0,
          desc + ", .getModifierState(\"" + kModifiers[i] + "\") returns wrong value");
     }
   }
 
+  function checkAllTIPModifiers(aTestDesc, aModifiers)
+  {
+    for (var i = 0; i < kModifiers.length; i++) {
+      is(TIP.getModifierState(kModifiers[i]), aModifiers.indexOf(kModifiers[i]) >= 0,
+         aTestDesc + ", TIP.getModifierState(\"" + kModifiers[i] + "\") returns wrong value");
+      is(sharedTIP.getModifierState(kModifiers[i]), TIP.getModifierState(kModifiers[i]),
+         aTestDesc + ", sharedTIP.getModifierState(\"" + kModifiers[i] + "\") returns different value from TIP");
+      is(independentTIP.getModifierState(kModifiers[i]), false,
+         aTestDesc + ", independentTIP.getModifierState(\"" + kModifiers[i] + "\") should return false");
+    }
+  }
+
   // First, all modifiers must be false.
   reset();
   doPreventDefaults = [ "keypress" ];
   TIP.keydown(keyA);
   TIP.keyup(keyA);
 
   is(events.length, 3,
      description + "TIP.keydown(keyA) and TIP.keyup(keyA) should cause keydown, keypress and keyup");
@@ -1219,33 +1236,43 @@ function runKeyTests()
   // Test each modifier keydown/keyup causes activating/inactivating the modifier state.
   for (var i = 0; i < kModifierKeys.length; i++) {
     reset();
     doPreventDefaults = [ "keypress" ];
     var modKey = new KeyboardEvent("", { key: kModifierKeys[i].key, code: kModifierKeys[i].code });
     var testDesc = "A modifier key \"" + kModifierKeys[i].key + "\" (\"" + kModifierKeys[i].code + "\") and a printable key";
     if (!kModifierKeys[i].isLockable) {
       TIP.keydown(modKey);
+      checkAllTIPModifiers(testDesc + ", \"" + kModifierKeys[i].key + "\" keydown", [ kModifierKeys[i].key ]);
       TIP.keydown(keyA);
+      checkAllTIPModifiers(testDesc + ", \"a\" keydown", [ kModifierKeys[i].key ]);
       TIP.keyup(keyA);
+      checkAllTIPModifiers(testDesc + ", \"a\" keyup", [ kModifierKeys[i].key ]);
       TIP.keyup(modKey);
+      checkAllTIPModifiers(testDesc + ", \"" + kModifierKeys[i].key + "\" keyup", [ ]);
       is(events.length, 5,
          description + testDesc + " should cause 5 events");
       checkModifiers(testDesc, events[0], "keydown",  kModifierKeys[i].key, kModifierKeys[i].code, [ kModifierKeys[i].key ]);
       checkModifiers(testDesc, events[1], "keydown",  "a",                  "KeyA",                [ kModifierKeys[i].key ]);
       checkModifiers(testDesc, events[2], "keypress", "a",                  "KeyA",                [ kModifierKeys[i].key ]);
       checkModifiers(testDesc, events[3], "keyup",    "a",                  "KeyA",                [ kModifierKeys[i].key ]);
       checkModifiers(testDesc, events[4], "keyup",    kModifierKeys[i].key, kModifierKeys[i].code, [ ]);
     } else {
       TIP.keydown(modKey);
+      checkAllTIPModifiers(testDesc + ", \"" + kModifierKeys[i].key + "\" first keydown", [ kModifierKeys[i].key ]);
       TIP.keyup(modKey);
+      checkAllTIPModifiers(testDesc + ", \"" + kModifierKeys[i].key + "\" first keyup", [ kModifierKeys[i].key ]);
       TIP.keydown(keyA);
+      checkAllTIPModifiers(testDesc + ", \"a\" keydown", [ kModifierKeys[i].key ]);
       TIP.keyup(keyA);
+      checkAllTIPModifiers(testDesc + ", \"a\" keyup", [ kModifierKeys[i].key ]);
       TIP.keydown(modKey);
+      checkAllTIPModifiers(testDesc + ", \"" + kModifierKeys[i].key + "\" second keydown", [ ]);
       TIP.keyup(modKey);
+      checkAllTIPModifiers(testDesc + ", \"" + kModifierKeys[i].key + "\" second keyup", [ ]);
       is(events.length, 7,
          description + testDesc + " should cause 7 events");
       checkModifiers(testDesc, events[0], "keydown",  kModifierKeys[i].key, kModifierKeys[i].code, [ kModifierKeys[i].key ]);
       checkModifiers(testDesc, events[1], "keyup",    kModifierKeys[i].key, kModifierKeys[i].code, [ kModifierKeys[i].key ]);
       checkModifiers(testDesc, events[2], "keydown",  "a",                  "KeyA",                [ kModifierKeys[i].key ]);
       checkModifiers(testDesc, events[3], "keypress", "a",                  "KeyA",                [ kModifierKeys[i].key ]);
       checkModifiers(testDesc, events[4], "keyup",    "a",                  "KeyA",                [ kModifierKeys[i].key ]);
       checkModifiers(testDesc, events[5], "keydown",  kModifierKeys[i].key, kModifierKeys[i].code, [ ]);
@@ -1253,28 +1280,37 @@ function runKeyTests()
     }
   }
 
   // Modifier state should be inactivated only when all pressed modifiers are released
   var shiftLeft = new KeyboardEvent("", { key: "Shift", code: "ShiftLeft" });
   var shiftRight = new KeyboardEvent("", { key: "Shift", code: "ShiftRight" });
   var shiftVirtual = new KeyboardEvent("", { key: "Shift", code: "" });
   var altGrVirtual = new KeyboardEvent("", { key: "AltGraph", code: "" });
+  var ctrlVirtual = new KeyboardEvent("", { key: "Control", code: "" });
 
   var testDesc = "ShiftLeft press -> ShiftRight press -> ShiftRight release -> ShiftLeft release";
   reset();
   doPreventDefaults = [ "keypress" ];
   TIP.keydown(shiftLeft);
+  checkAllTIPModifiers(testDesc + ", Left-Shift keydown", [ "Shift" ]);
   TIP.keydown(shiftRight);
+  checkAllTIPModifiers(testDesc + ", Right-Shift keydown", [ "Shift" ]);
   TIP.keydown(keyA);
+  checkAllTIPModifiers(testDesc + ", \"a\" keydown", [ "Shift" ]);
   TIP.keyup(keyA);
+  checkAllTIPModifiers(testDesc + ", \"a\" keyup", [ "Shift" ]);
   TIP.keyup(shiftRight);
+  checkAllTIPModifiers(testDesc + ", Right-Shift keyup", [ "Shift" ]);
   TIP.keydown(keyA);
+  checkAllTIPModifiers(testDesc + ", \"a\" keydown (after Right-Shift keyup)", [ "Shift" ]);
   TIP.keyup(keyA);
+  checkAllTIPModifiers(testDesc + ", \"a\" keyup (after Right-Shift keyup)", [ "Shift" ]);
   TIP.keyup(shiftLeft);
+  checkAllTIPModifiers(testDesc + ", Left-Shift keyup", [ ]);
 
   is(events.length, 10,
      description + testDesc + " should cause 10 events");
   checkModifiers(testDesc, events[0], "keydown",  "Shift", "ShiftLeft",  [ "Shift" ]);
   checkModifiers(testDesc, events[1], "keydown",  "Shift", "ShiftRight", [ "Shift" ]);
   checkModifiers(testDesc, events[2], "keydown",  "a",     "KeyA",       [ "Shift" ]);
   checkModifiers(testDesc, events[3], "keypress", "a",     "KeyA",       [ "Shift" ]);
   checkModifiers(testDesc, events[4], "keyup",    "a",     "KeyA",       [ "Shift" ]);
@@ -1283,23 +1319,31 @@ function runKeyTests()
   checkModifiers(testDesc, events[7], "keypress", "a",     "KeyA",       [ "Shift" ]);
   checkModifiers(testDesc, events[8], "keyup",    "a",     "KeyA",       [ "Shift" ]);
   checkModifiers(testDesc, events[9], "keyup",    "Shift", "ShiftLeft",  [ ]);
 
   testDesc = "ShiftLeft press -> ShiftRight press -> ShiftLeft release -> ShiftRight release";
   reset();
   doPreventDefaults = [ "keypress" ];
   TIP.keydown(shiftLeft);
+  checkAllTIPModifiers(testDesc + ", Left-Shift keydown", [ "Shift" ]);
   TIP.keydown(shiftRight);
+  checkAllTIPModifiers(testDesc + ", Right-Shift keydown", [ "Shift" ]);
   TIP.keydown(keyA);
+  checkAllTIPModifiers(testDesc + ", \"a\" keydown", [ "Shift" ]);
   TIP.keyup(keyA);
+  checkAllTIPModifiers(testDesc + ", \"a\" keyup", [ "Shift" ]);
   TIP.keyup(shiftLeft);
+  checkAllTIPModifiers(testDesc + ", Left-Shift keyup", [ "Shift" ]);
   TIP.keydown(keyA);
+  checkAllTIPModifiers(testDesc + ", \"a\" keydown (after Left-Shift keyup)", [ "Shift" ]);
   TIP.keyup(keyA);
+  checkAllTIPModifiers(testDesc + ", \"a\" keyup (after Left-Shift keyup)", [ "Shift" ]);
   TIP.keyup(shiftRight);
+  checkAllTIPModifiers(testDesc + ", Right-Shift keyup", [ ]);
 
   is(events.length, 10,
      description + testDesc + " should cause 10 events");
   checkModifiers(testDesc, events[0], "keydown",  "Shift", "ShiftLeft",  [ "Shift" ]);
   checkModifiers(testDesc, events[1], "keydown",  "Shift", "ShiftRight", [ "Shift" ]);
   checkModifiers(testDesc, events[2], "keydown",  "a",     "KeyA",       [ "Shift" ]);
   checkModifiers(testDesc, events[3], "keypress", "a",     "KeyA",       [ "Shift" ]);
   checkModifiers(testDesc, events[4], "keyup",    "a",     "KeyA",       [ "Shift" ]);
@@ -1308,23 +1352,31 @@ function runKeyTests()
   checkModifiers(testDesc, events[7], "keypress", "a",     "KeyA",       [ "Shift" ]);
   checkModifiers(testDesc, events[8], "keyup",    "a",     "KeyA",       [ "Shift" ]);
   checkModifiers(testDesc, events[9], "keyup",    "Shift", "ShiftRight",  [ ]);
 
   testDesc = "ShiftLeft press -> virtual Shift press -> virtual Shift release -> ShiftLeft release";
   reset();
   doPreventDefaults = [ "keypress" ];
   TIP.keydown(shiftLeft);
+  checkAllTIPModifiers(testDesc + ", Left-Shift keydown", [ "Shift" ]);
   TIP.keydown(shiftVirtual);
+  checkAllTIPModifiers(testDesc + ", Virtual-Shift keydown", [ "Shift" ]);
   TIP.keydown(keyA);
+  checkAllTIPModifiers(testDesc + ", \"a\" keydown", [ "Shift" ]);
   TIP.keyup(keyA);
+  checkAllTIPModifiers(testDesc + ", \"a\" keyup", [ "Shift" ]);
   TIP.keyup(shiftVirtual);
+  checkAllTIPModifiers(testDesc + ", Virtual-Shift keyup", [ "Shift" ]);
   TIP.keydown(keyA);
+  checkAllTIPModifiers(testDesc + ", \"a\" keydown (after Virtual-Shift keyup)", [ "Shift" ]);
   TIP.keyup(keyA);
+  checkAllTIPModifiers(testDesc + ", \"a\" keyup (after Virtual-Shift keyup)", [ "Shift" ]);
   TIP.keyup(shiftLeft);
+  checkAllTIPModifiers(testDesc + ", Left-Shift keyup", [ ]);
 
   is(events.length, 10,
      description + testDesc + " should cause 10 events");
   checkModifiers(testDesc, events[0], "keydown",  "Shift", "ShiftLeft",  [ "Shift" ]);
   checkModifiers(testDesc, events[1], "keydown",  "Shift", "",           [ "Shift" ]);
   checkModifiers(testDesc, events[2], "keydown",  "a",     "KeyA",       [ "Shift" ]);
   checkModifiers(testDesc, events[3], "keypress", "a",     "KeyA",       [ "Shift" ]);
   checkModifiers(testDesc, events[4], "keyup",    "a",     "KeyA",       [ "Shift" ]);
@@ -1333,23 +1385,31 @@ function runKeyTests()
   checkModifiers(testDesc, events[7], "keypress", "a",     "KeyA",       [ "Shift" ]);
   checkModifiers(testDesc, events[8], "keyup",    "a",     "KeyA",       [ "Shift" ]);
   checkModifiers(testDesc, events[9], "keyup",    "Shift", "ShiftLeft",  [ ]);
 
   testDesc = "virtual Shift press -> ShiftRight press -> ShiftRight release -> virtual Shift release";
   reset();
   doPreventDefaults = [ "keypress" ];
   TIP.keydown(shiftVirtual);
+  checkAllTIPModifiers(testDesc + ", Virtual-Shift keydown", [ "Shift" ]);
   TIP.keydown(shiftRight);
+  checkAllTIPModifiers(testDesc + ", Right-Shift keydown", [ "Shift" ]);
   TIP.keydown(keyA);
+  checkAllTIPModifiers(testDesc + ", \"a\" keydown", [ "Shift" ]);
   TIP.keyup(keyA);
+  checkAllTIPModifiers(testDesc + ", \"a\" keyup", [ "Shift" ]);
   TIP.keyup(shiftRight);
+  checkAllTIPModifiers(testDesc + ", Right-Shift keyup", [ "Shift" ]);
   TIP.keydown(keyA);
+  checkAllTIPModifiers(testDesc + ", \"a\" keydown (after Right-Shift keyup)", [ "Shift" ]);
   TIP.keyup(keyA);
+  checkAllTIPModifiers(testDesc + ", \"a\" keyup (after Right-Shift keyup)", [ "Shift" ]);
   TIP.keyup(shiftVirtual);
+  checkAllTIPModifiers(testDesc + ", Virtual-Shift keyup", [ ]);
 
   is(events.length, 10,
      description + testDesc + " should cause 10 events");
   checkModifiers(testDesc, events[0], "keydown",  "Shift", "",           [ "Shift" ]);
   checkModifiers(testDesc, events[1], "keydown",  "Shift", "ShiftRight", [ "Shift" ]);
   checkModifiers(testDesc, events[2], "keydown",  "a",     "KeyA",       [ "Shift" ]);
   checkModifiers(testDesc, events[3], "keypress", "a",     "KeyA",       [ "Shift" ]);
   checkModifiers(testDesc, events[4], "keyup",    "a",     "KeyA",       [ "Shift" ]);
@@ -1358,26 +1418,37 @@ function runKeyTests()
   checkModifiers(testDesc, events[7], "keypress", "a",     "KeyA",       [ "Shift" ]);
   checkModifiers(testDesc, events[8], "keyup",    "a",     "KeyA",       [ "Shift" ]);
   checkModifiers(testDesc, events[9], "keyup",    "Shift", "",           [ ]);
 
   testDesc = "ShiftLeft press -> ShiftRight press -> ShiftRight release -> ShiftRight release -> ShiftLeft release";
   reset();
   doPreventDefaults = [ "keypress" ];
   TIP.keydown(shiftLeft);
+  checkAllTIPModifiers(testDesc + ", Left-Shift keydown", [ "Shift" ]);
   TIP.keydown(shiftRight);
+  checkAllTIPModifiers(testDesc + ", Right-Shift keydown", [ "Shift" ]);
   TIP.keydown(keyA);
+  checkAllTIPModifiers(testDesc + ", \"a\" keydown", [ "Shift" ]);
   TIP.keyup(keyA);
+  checkAllTIPModifiers(testDesc + ", \"a\" keyup", [ "Shift" ]);
   TIP.keyup(shiftRight);
+  checkAllTIPModifiers(testDesc + ", Right-Shift keyup", [ "Shift" ]);
   TIP.keydown(keyA);
+  checkAllTIPModifiers(testDesc + ", \"a\" keydown (after Virtual-Shift keyup)", [ "Shift" ]);
   TIP.keyup(keyA);
+  checkAllTIPModifiers(testDesc + ", \"a\" keyup (after Virtual-Shift keyup)", [ "Shift" ]);
   TIP.keyup(shiftRight);
+  checkAllTIPModifiers(testDesc + ", Right-Shift keyup again", [ "Shift" ]);
   TIP.keydown(keyA);
+  checkAllTIPModifiers(testDesc + ", \"a\" keydown (after Virtual-Shift keyup again)", [ "Shift" ]);
   TIP.keyup(keyA);
+  checkAllTIPModifiers(testDesc + ", \"a\" keyup (after Virtual-Shift keyup again)", [ "Shift" ]);
   TIP.keyup(shiftLeft);
+  checkAllTIPModifiers(testDesc + ", Left-Shift keyup", [ ]);
 
   is(events.length, 14,
      description + testDesc + " should cause 14 events");
   checkModifiers(testDesc, events[0],  "keydown",  "Shift", "ShiftLeft",  [ "Shift" ]);
   checkModifiers(testDesc, events[1],  "keydown",  "Shift", "ShiftRight", [ "Shift" ]);
   checkModifiers(testDesc, events[2],  "keydown",  "a",     "KeyA",       [ "Shift" ]);
   checkModifiers(testDesc, events[3],  "keypress", "a",     "KeyA",       [ "Shift" ]);
   checkModifiers(testDesc, events[4],  "keyup",    "a",     "KeyA",       [ "Shift" ]);
@@ -1390,25 +1461,35 @@ function runKeyTests()
   checkModifiers(testDesc, events[11], "keypress", "a",     "KeyA",       [ "Shift" ]);
   checkModifiers(testDesc, events[12], "keyup",    "a",     "KeyA",       [ "Shift" ]);
   checkModifiers(testDesc, events[13], "keyup",    "Shift", "ShiftLeft",  [ ]);
 
   testDesc = "ShiftLeft press -> ShiftLeft press -> ShiftLeft release -> ShiftLeft release";
   reset();
   doPreventDefaults = [ "keypress" ];
   TIP.keydown(shiftLeft);
+  checkAllTIPModifiers(testDesc + ", Left-Shift keydown", [ "Shift" ]);
   TIP.keydown(shiftLeft);
+  checkAllTIPModifiers(testDesc + ", Left-Shift keydown again", [ "Shift" ]);
   TIP.keydown(keyA);
+  checkAllTIPModifiers(testDesc + ", \"a\" keydown", [ "Shift" ]);
   TIP.keyup(keyA);
+  checkAllTIPModifiers(testDesc + ", \"a\" keyup", [ "Shift" ]);
   TIP.keyup(shiftLeft);
+  checkAllTIPModifiers(testDesc + ", Left-Shift keyup", [ ]);
   TIP.keydown(keyA);
+  checkAllTIPModifiers(testDesc + ", \"a\" keydown (after Left-Shift keyup)", [ ]);
   TIP.keyup(keyA);
+  checkAllTIPModifiers(testDesc + ", \"a\" keyup (after Left-Shift keyup)", [ ]);
   TIP.keyup(shiftLeft);
+  checkAllTIPModifiers(testDesc + ", Left-Shift keyup again", [ ]);
   TIP.keydown(keyA);
+  checkAllTIPModifiers(testDesc + ", \"a\" keydown (after Left-Shift keyup again)", [ ]);
   TIP.keyup(keyA);
+  checkAllTIPModifiers(testDesc + ", \"a\" keyup (after Left-Shift keyup again)", [ ]);
 
   is(events.length, 13,
      description + testDesc + " should cause 13 events");
   checkModifiers(testDesc, events[0],  "keydown",  "Shift", "ShiftLeft",  [ "Shift" ]);
   checkModifiers(testDesc, events[1],  "keydown",  "Shift", "ShiftLeft",  [ "Shift" ]);
   checkModifiers(testDesc, events[2],  "keydown",  "a",     "KeyA",       [ "Shift" ]);
   checkModifiers(testDesc, events[3],  "keypress", "a",     "KeyA",       [ "Shift" ]);
   checkModifiers(testDesc, events[4],  "keyup",    "a",     "KeyA",       [ "Shift" ]);
@@ -1420,23 +1501,31 @@ function runKeyTests()
   checkModifiers(testDesc, events[10], "keydown",  "a",     "KeyA",       [ ]);
   checkModifiers(testDesc, events[11], "keypress", "a",     "KeyA",       [ ]);
   checkModifiers(testDesc, events[12], "keyup",    "a",     "KeyA",       [ ]);
 
   testDesc = "virtual Shift press -> virtual AltGraph press -> virtual AltGraph release -> virtual Shift release";
   reset();
   doPreventDefaults = [ "keypress" ];
   TIP.keydown(shiftVirtual);
+  checkAllTIPModifiers(testDesc + ", Virtual-Shift keydown", [ "Shift" ]);
   TIP.keydown(altGrVirtual);
+  checkAllTIPModifiers(testDesc + ", Virtual-AltGraph keydown", [ "Shift", "AltGraph" ]);
   TIP.keydown(keyA);
+  checkAllTIPModifiers(testDesc + ", \"a\" keydown", [ "Shift", "AltGraph" ]);
   TIP.keyup(keyA);
+  checkAllTIPModifiers(testDesc + ", \"a\" keyup", [ "Shift", "AltGraph" ]);
   TIP.keyup(altGrVirtual);
+  checkAllTIPModifiers(testDesc + ", Virtual-AltGraph keyup", [ "Shift" ]);
   TIP.keydown(keyA);
+  checkAllTIPModifiers(testDesc + ", \"a\" keydown (after Virtual-AltGraph keyup)", [ "Shift" ]);
   TIP.keyup(keyA);
+  checkAllTIPModifiers(testDesc + ", \"a\" keyup (after Virtual-AltGraph keyup)", [ "Shift" ]);
   TIP.keyup(shiftVirtual);
+  checkAllTIPModifiers(testDesc + ", Virtual-Shift keyup", [ ]);
 
   is(events.length, 10,
      description + testDesc + " should cause 10 events");
   checkModifiers(testDesc, events[0], "keydown",  "Shift",    "",     [ "Shift" ]);
   checkModifiers(testDesc, events[1], "keydown",  "AltGraph", "",     [ "Shift", "AltGraph" ]);
   checkModifiers(testDesc, events[2], "keydown",  "a",        "KeyA", [ "Shift", "AltGraph" ]);
   checkModifiers(testDesc, events[3], "keypress", "a",        "KeyA", [ "Shift", "AltGraph" ]);
   checkModifiers(testDesc, events[4], "keyup",    "a",        "KeyA", [ "Shift", "AltGraph" ]);
@@ -1445,37 +1534,78 @@ function runKeyTests()
   checkModifiers(testDesc, events[7], "keypress", "a",        "KeyA", [ "Shift" ]);
   checkModifiers(testDesc, events[8], "keyup",    "a",        "KeyA", [ "Shift" ]);
   checkModifiers(testDesc, events[9], "keyup",    "Shift",    "",     [ ]);
 
   testDesc = "virtual Shift press -> virtual AltGraph press -> virtual Shift release -> virtual AltGr release";
   reset();
   doPreventDefaults = [ "keypress" ];
   TIP.keydown(shiftVirtual);
+  checkAllTIPModifiers(testDesc + ", Virtual-Shift keydown", [ "Shift" ]);
   TIP.keydown(altGrVirtual);
+  checkAllTIPModifiers(testDesc + ", Virtual-AltGraph keydown", [ "Shift", "AltGraph" ]);
   TIP.keydown(keyA);
+  checkAllTIPModifiers(testDesc + ", \"a\" keydown", [ "Shift", "AltGraph" ]);
   TIP.keyup(keyA);
+  checkAllTIPModifiers(testDesc + ", \"a\" keyup", [ "Shift", "AltGraph" ]);
   TIP.keyup(shiftVirtual);
+  checkAllTIPModifiers(testDesc + ", Virtual-Shift keyup", [ "AltGraph" ]);
   TIP.keydown(keyA);
+  checkAllTIPModifiers(testDesc + ", \"a\" keydown (after Virtual-Shift keyup)", [ "AltGraph" ]);
   TIP.keyup(keyA);
+  checkAllTIPModifiers(testDesc + ", \"a\" keyup (after Virtual-Shift keyup)", [ "AltGraph" ]);
   TIP.keyup(altGrVirtual);
+  checkAllTIPModifiers(testDesc + ", Virtual-AltGraph keyup", [ ]);
 
   is(events.length, 10,
      description + testDesc + " should cause 10 events");
   checkModifiers(testDesc, events[0], "keydown",  "Shift",    "",     [ "Shift" ]);
   checkModifiers(testDesc, events[1], "keydown",  "AltGraph", "",     [ "Shift", "AltGraph" ]);
   checkModifiers(testDesc, events[2], "keydown",  "a",        "KeyA", [ "Shift", "AltGraph" ]);
   checkModifiers(testDesc, events[3], "keypress", "a",        "KeyA", [ "Shift", "AltGraph" ]);
   checkModifiers(testDesc, events[4], "keyup",    "a",        "KeyA", [ "Shift", "AltGraph" ]);
   checkModifiers(testDesc, events[5], "keyup",    "Shift",    "",     [ "AltGraph" ]);
   checkModifiers(testDesc, events[6], "keydown",  "a",        "KeyA", [ "AltGraph" ]);
   checkModifiers(testDesc, events[7], "keypress", "a",        "KeyA", [ "AltGraph" ]);
   checkModifiers(testDesc, events[8], "keyup",    "a",        "KeyA", [ "AltGraph" ]);
   checkModifiers(testDesc, events[9], "keyup",    "AltGraph", "",     [ ]);
 
+  // shareModifierStateOf(null) should cause resetting the modifier state
+  function checkTIPModifiers(aTestDesc, aTIP, aModifiers)
+  {
+    for (var i = 0; i < kModifiers.length; i++) {
+      is(aTIP.getModifierState(kModifiers[i]), aModifiers.indexOf(kModifiers[i]) >= 0,
+         description + aTestDesc + ", aTIP.getModifierState(\"" + kModifiers[i] + "\") returns wrong value");
+    }
+  }
+  TIP.keydown(shiftVirtual);
+  TIP.keydown(altGrVirtual);
+  sharedTIP.shareModifierStateOf(null);
+  checkTIPModifiers("sharedTIP.sharedModifierStateOf(null) shouldn't cause TIP's modifiers reset", TIP, [ "Shift", "AltGraph" ]);
+  checkTIPModifiers("sharedTIP.sharedModifierStateOf(null) should cause sharedTIP modifiers reset", sharedTIP, [ ]);
+
+  // sharedTIP.shareModifierStateOf(null) should be unlinked from TIP.
+  TIP.keydown(ctrlVirtual);
+  checkTIPModifiers("TIP.keydown(ctrlVirtual) should cause TIP's modifiers set", TIP, [ "Shift", "AltGraph", "Control" ]);
+  checkTIPModifiers("TIP.keydown(ctrlVirtual) shouldn't cause sharedTIP modifiers set", sharedTIP, [ ]);
+
+  // beginInputTransactionForTests() shouldn't cause modifier state reset.
+  ok(TIP.beginInputTransactionForTests(otherWindow),
+     description + "TIP.beginInputTransactionForTests(otherWindow) should return true");
+  checkTIPModifiers("TIP.beginInputTransactionForTests(otherWindow) shouldn't cause TIP's modifiers set", TIP, [ "Shift", "AltGraph", "Control" ]);
+  TIP.keyup(shiftLeft);
+  TIP.keyup(altGrVirtual);
+  TIP.keyup(ctrlVirtual);
+  checkTIPModifiers("TIP should keep modifier's physical key state", TIP, [ "Shift" ]);
+  ok(TIP.beginInputTransactionForTests(window),
+     description + "TIP.beginInputTransactionForTests(window) should return true");
+  checkTIPModifiers("TIP.beginInputTransactionForTests(window) shouldn't cause TIP's modifiers set", TIP, [ "Shift" ]);
+  TIP.keyup(shiftVirtual);
+  checkTIPModifiers("TIP should keep modifier's physical key state", TIP, [ ]);
+
   window.removeEventListener("keydown", handler, false);
   window.removeEventListener("keypress", handler, false);
   window.removeEventListener("keyup", handler, false);
 }
 
 function runErrorTests()
 {
   var description = "runErrorTests(): ";
--- a/dom/events/UIEvent.cpp
+++ b/dom/events/UIEvent.cpp
@@ -447,62 +447,17 @@ UIEvent::ComputeModifierState(const nsAS
   return modifiers;
 }
 
 bool
 UIEvent::GetModifierStateInternal(const nsAString& aKey)
 {
   WidgetInputEvent* inputEvent = mEvent->AsInputEvent();
   MOZ_ASSERT(inputEvent, "mEvent must be WidgetInputEvent or derived class");
-  if (aKey.EqualsLiteral("Accel")) {
-    return inputEvent->IsAccel();
-  }
-  if (aKey.EqualsLiteral(NS_DOM_KEYNAME_SHIFT)) {
-    return inputEvent->IsShift();
-  }
-  if (aKey.EqualsLiteral(NS_DOM_KEYNAME_CONTROL)) {
-    return inputEvent->IsControl();
-  }
-  if (aKey.EqualsLiteral(NS_DOM_KEYNAME_META)) {
-    return inputEvent->IsMeta();
-  }
-  if (aKey.EqualsLiteral(NS_DOM_KEYNAME_ALT)) {
-    return inputEvent->IsAlt();
-  }
-
-  if (aKey.EqualsLiteral(NS_DOM_KEYNAME_ALTGRAPH)) {
-    return inputEvent->IsAltGraph();
-  }
-  if (aKey.EqualsLiteral(NS_DOM_KEYNAME_OS)) {
-    return inputEvent->IsOS();
-  }
-
-  if (aKey.EqualsLiteral(NS_DOM_KEYNAME_CAPSLOCK)) {
-    return inputEvent->IsCapsLocked();
-  }
-  if (aKey.EqualsLiteral(NS_DOM_KEYNAME_NUMLOCK)) {
-    return inputEvent->IsNumLocked();
-  }
-
-  if (aKey.EqualsLiteral(NS_DOM_KEYNAME_FN)) {
-    return inputEvent->IsFn();
-  }
-  if (aKey.EqualsLiteral(NS_DOM_KEYNAME_FNLOCK)) {
-    return inputEvent->IsFnLocked();
-  }
-  if (aKey.EqualsLiteral(NS_DOM_KEYNAME_SCROLLLOCK)) {
-    return inputEvent->IsScrollLocked();
-  }
-  if (aKey.EqualsLiteral(NS_DOM_KEYNAME_SYMBOL)) {
-    return inputEvent->IsSymbol();
-  }
-  if (aKey.EqualsLiteral(NS_DOM_KEYNAME_SYMBOLLOCK)) {
-    return inputEvent->IsSymbolLocked();
-  }
-  return false;
+  return ((inputEvent->modifiers & WidgetInputEvent::GetModifier(aKey)) != 0);
 }
 
 } // namespace dom
 } // namespace mozilla
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
--- a/dom/interfaces/base/nsITextInputProcessor.idl
+++ b/dom/interfaces/base/nsITextInputProcessor.idl
@@ -219,17 +219,17 @@ interface nsITextInputProcessorCallback;
  *
  *   // keyup of one of shift key doesn't cause inactivating "Shift" state.
  *   TIP.keyup(rightShift);
  *
  *   // This causes inactivating "Shift" state completely.
  *   TIP.keyup(leftShift);
  */
 
-[scriptable, builtinclass, uuid(16144d6e-a97a-4733-aa6a-3a1287cfe539)]
+[scriptable, builtinclass, uuid(c8b5d3fb-5bed-4b77-b67f-77aee6ac5081)]
 interface nsITextInputProcessor : nsISupports
 {
   /**
    * When you create an instance, you must call beginInputTransaction() first
    * except when you created the instance for automated tests.
    *
    * @param aWindow         A DOM window.  The instance will look for a top
    *                        level widget from this.
@@ -459,16 +459,38 @@ interface nsITextInputProcessor : nsISup
                     [optional] in unsigned long aKeyFlags);
 
   /**
    * Similar to keydown(), but this dispatches only a keyup event.
    */
   [optional_argc]
     boolean keyup(in nsIDOMKeyEvent aKeyboardEvent,
                   [optional] in unsigned long aKeyFlags);
+
+  /**
+   * getModifierState() returns modifier state managed by this instance.
+   *
+   * @param aModifier       One of modifier key names.  This doesn't support
+   *                        virtual modifiers like "Accel".
+   * @return                true if the modifier key is active.  Otherwise,
+   *                        false.
+   */
+  boolean getModifierState(in DOMString aModifierKey);
+
+  /**
+   * shareModifierStateOf() makes the instance shares modifier state of
+   * another instance.  When this is called, the instance refers the modifier
+   * state of another instance.  After that, changes to either this and the
+   * other instance's modifier state is synchronized.
+   *
+   * @param aOther          Another instance which will be referred by the
+   *                        instance.  If this is null, the instance restarts
+   *                        to manage modifier state independently.
+   */
+  void shareModifierStateOf(in nsITextInputProcessor aOther);
 };
 
 %{C++
 #define TEXT_INPUT_PROCESSOR_CID \
   { 0xcaaab47f, 0x1e31, 0x478e, \
     { 0x89, 0x19, 0x97, 0x09, 0x04, 0xe9, 0xcb, 0x72 } }
 #define TEXT_INPUT_PROCESSOR_CONTRACTID \
   "@mozilla.org/text-input-processor;1"
--- a/widget/BasicEvents.h
+++ b/widget/BasicEvents.h
@@ -1011,16 +1011,21 @@ public:
 
 
   /**
    * Returns a modifier of "Accel" virtual modifier which is used for shortcut
    * key.
    */
   static Modifier AccelModifier();
 
+  /**
+   * GetModifier() returns a modifier flag which is activated by aDOMKeyName.
+   */
+  static Modifier GetModifier(const nsAString& aDOMKeyName);
+
   // true indicates the accel key on the environment is down
   bool IsAccel() const
   {
     return ((modifiers & AccelModifier()) != 0);
   }
 
   // true indicates the shift key is down
   bool IsShift() const
--- a/widget/WidgetEventImpl.cpp
+++ b/widget/WidgetEventImpl.cpp
@@ -251,16 +251,27 @@ WidgetEvent::IsAllowedToDispatchDOMEvent
 }
 
 /******************************************************************************
  * mozilla::WidgetInputEvent
  ******************************************************************************/
 
 /* static */
 Modifier
+WidgetInputEvent::GetModifier(const nsAString& aDOMKeyName)
+{
+  if (aDOMKeyName.EqualsLiteral("Accel")) {
+    return AccelModifier();
+  }
+  KeyNameIndex keyNameIndex = WidgetKeyboardEvent::GetKeyNameIndex(aDOMKeyName);
+  return WidgetKeyboardEvent::GetModifierForKeyName(keyNameIndex);
+}
+
+/* static */
+Modifier
 WidgetInputEvent::AccelModifier()
 {
   static Modifier sAccelModifier = MODIFIER_NONE;
   if (sAccelModifier == MODIFIER_NONE) {
     switch (Preferences::GetInt("ui.key.accelKey", 0)) {
       case nsIDOMKeyEvent::DOM_VK_META:
         sAccelModifier = MODIFIER_META;
         break;