Bug 460059 Need IME state testing r+sr=roc
authorMasayuki Nakano <masayuki@d-toybox.com>
Tue, 05 May 2009 15:15:23 +0900
changeset 27984 7ef08c5305178d97b2c4a80ace60aa63731a4211
parent 27983 1a8155f3f573cac9d20dc3a405c27ce28f75e3af
child 27985 26e323fea0081e698132bab52971a797b4b796d7
push id6825
push usermasayuki@d-toybox.com
push dateTue, 05 May 2009 06:16:22 +0000
treeherdermozilla-central@7ef08c530517 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs460059
milestone1.9.2a1pre
Bug 460059 Need IME state testing r+sr=roc
dom/base/nsDOMWindowUtils.cpp
dom/interfaces/base/nsIDOMWindowUtils.idl
widget/public/nsIWidget.h
widget/src/windows/nsWindow.cpp
widget/tests/Makefile.in
widget/tests/test_imestate.html
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -766,8 +766,40 @@ nsDOMWindowUtils::GetScrollXY(PRBool aFl
     }
   }
 
   *aScrollX = nsPresContext::AppUnitsToIntCSSPixels(xPos);
   *aScrollY = nsPresContext::AppUnitsToIntCSSPixels(yPos);
 
   return NS_OK;
 }
+
+NS_IMETHODIMP
+nsDOMWindowUtils::GetIMEIsOpen(PRBool *aState)
+{
+  NS_ENSURE_ARG_POINTER(aState);
+
+  nsCOMPtr<nsIWidget> widget = GetWidget();
+  if (!widget)
+    return NS_ERROR_FAILURE;
+
+  // Open state should not be available when IME is not enabled.
+  PRUint32 enabled;
+  nsresult rv = widget->GetIMEEnabled(&enabled);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (enabled != nsIWidget::IME_STATUS_ENABLED)
+    return NS_ERROR_NOT_AVAILABLE;
+
+  return widget->GetIMEOpenState(aState);
+}
+
+NS_IMETHODIMP
+nsDOMWindowUtils::GetIMEStatus(PRUint32 *aState)
+{
+  NS_ENSURE_ARG_POINTER(aState);
+
+  nsCOMPtr<nsIWidget> widget = GetWidget();
+  if (!widget)
+    return NS_ERROR_FAILURE;
+
+  return widget->GetIMEEnabled(aState);
+}
+
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -43,17 +43,17 @@
  * elevated privileges; the method implementations should contain the
  * necessary security checks.  Access this interface by calling
  * getInterface on a DOMWindow.
  */
 
 interface nsIDOMElement;
 interface nsIDOMHTMLCanvasElement;
 
-[scriptable, uuid(5003e14a-6617-4af9-a0f6-a09f86d65dd2)]
+[scriptable, uuid(a5e155c8-ef18-4e70-881b-d2fe8aa13fec)]
 interface nsIDOMWindowUtils : nsISupports {
 
   /**
    * Image animation mode of the window. When this attribute's value
    * is changed, the implementation should set all images in the window
    * to the given value. That is, when set to kDontAnimMode, all images
    * will stop animating. The attribute's value must be one of the
    * animationMode values from imgIContainer.
@@ -323,9 +323,50 @@ interface nsIDOMWindowUtils : nsISupport
 
   /**
    * Returns the scroll position of the window's currently loaded document.
    *
    * @param aFlushLayout flushes layout if true. Otherwise, no flush occurs.
    * @see nsIDOMWindow::scrollX/Y
    */
   void getScrollXY(in boolean aFlushLayout, out long aScrollX, out long aScrollY);
+
+  /**
+   * Get IME open state. TRUE means 'Open', otherwise, 'Close'.
+   * This property works only when IMEEnabled is IME_STATUS_ENABLED.
+   */
+  readonly attribute boolean IMEIsOpen;
+
+  /**
+   * WARNING: These values must be same as nsIWidget's values.
+   */
+
+  /**
+   * DISABLED means users cannot use IME completely.
+   * Note that this state is *not* same as |ime-mode: disabled;|.
+   */
+  const unsigned long IME_STATUS_DISABLED = 0;
+
+  /**
+   * ENABLED means users can use all functions of IME. This state is same as
+   * |ime-mode: normal;|.
+   */
+  const unsigned long IME_STATUS_ENABLED  = 1;
+
+  /**
+   * PASSWORD means users cannot use most functions of IME. But on GTK2,
+   * users can use "Simple IM" which only supports dead key inputting.
+   * The behavior is same as the behavior of the native password field.
+   * This state is same as |ime-mode: disabled;|.
+   */
+  const unsigned long IME_STATUS_PASSWORD = 2;
+
+  /**
+   * PLUGIN means a plug-in has focus. At this time we should not touch to
+   * controlling the IME state.
+   */
+  const unsigned long IME_STATUS_PLUGIN   = 3;
+
+  /**
+   * Get IME status, see above IME_STATUS_* definitions.
+   */
+  readonly attribute unsigned long IMEStatus;
 };
--- a/widget/public/nsIWidget.h
+++ b/widget/public/nsIWidget.h
@@ -981,16 +981,21 @@ class nsIWidget : public nsISupports {
      * If IME is 'Opened', aState is set PR_TRUE.
      * If IME is 'Closed', aState is set PR_FALSE.
      */
     NS_IMETHOD GetIMEOpenState(PRBool* aState) = 0;
 
     /*
      * IME enabled states, the aState value of SetIMEEnabled/GetIMEEnabled
      * should be one value of following values.
+     *
+     * WARNING: If you change these values, you also need to edit:
+     *   nsIDOMWindowUtils.idl
+     *   nsDOMWindowUtils::SetIMEEnabled
+     *   nsContentUtils::GetWidgetStatusFromIMEStatus
      */
     enum IMEStatus {
       /*
        * 'Disabled' means the user cannot use IME. So, the open state should be
        * 'closed' during 'disabled'.
        */
       IME_STATUS_DISABLED = 0,
       /*
@@ -1039,16 +1044,19 @@ class nsIWidget : public nsISupports {
      */
     NS_IMETHOD GetToggledKeyState(PRUint32 aKeyCode, PRBool* aLEDState) = 0;
 
     /*
      * An editable node (i.e. input/textarea/design mode document)
      *  is receiving or giving up focus
      * aFocus is true if node is receiving focus
      * aFocus is false if node is giving up focus (blur)
+     *
+     * If this returns NS_ERROR_*, OnIMETextChange and OnIMESelectionChange
+     * and OnIMEFocusChange(PR_FALSE) will be never called.
      */
     NS_IMETHOD OnIMEFocusChange(PRBool aFocus) = 0;
 
     /*
      * Text content of the focused node has changed
      * aStart is the starting offset of the change
      * aOldEnd is the ending offset of the change
      * aNewEnd is the caret offset after the change
--- a/widget/src/windows/nsWindow.cpp
+++ b/widget/src/windows/nsWindow.cpp
@@ -6912,17 +6912,20 @@ nsWindow::GetToggledKeyState(PRUint32 aK
   *aLEDState = (::GetKeyState(aKeyCode) & 1) != 0;
   return NS_OK;
 }
 
 #ifdef NS_ENABLE_TSF
 NS_IMETHODIMP
 nsWindow::OnIMEFocusChange(PRBool aFocus)
 {
-  return nsTextStore::OnFocusChange(aFocus, this, mIMEEnabled);
+  nsresult rv = nsTextStore::OnFocusChange(aFocus, this, mIMEEnabled);
+  if (rv == NS_ERROR_NOT_AVAILABLE)
+    rv = NS_OK; // TSF is not enabled, maybe.
+  return rv;
 }
 
 NS_IMETHODIMP
 nsWindow::OnIMETextChange(PRUint32 aStart,
                           PRUint32 aOldEnd,
                           PRUint32 aNewEnd)
 {
   return nsTextStore::OnTextChange(aStart, aOldEnd, aNewEnd);
--- a/widget/tests/Makefile.in
+++ b/widget/tests/Makefile.in
@@ -60,16 +60,17 @@ include $(topsrcdir)/config/rules.mk
 _TEST_FILES =	test_bug343416.xul \
 		test_bug444800.xul \
 		test_bug462106.xul \
 		test_bug478536.xul \
 		window_bug478536.xul \
 		test_keycodes.xul \
 		test_wheeltransaction.xul \
 		window_wheeltransaction.xul \
+		test_imestate.html \
 		$(NULL)
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa)
 _TEST_FILES += native_menus_window.xul \
                test_native_menus.xul \
                test_bug428405.xul \
                test_bug466599.xul \
                $(NULL)
new file mode 100644
--- /dev/null
+++ b/widget/tests/test_imestate.html
@@ -0,0 +1,480 @@
+<html>
+<head>
+  <title>Test for IME state controling</title>
+  <script type="text/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <script type="text/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css"
+          href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+</head>
+<body onload="setTimeout(runTests, 0);">
+<p id="display">
+  <!-- input elements -->
+  <input type="text"     id="text"/><br/>
+  <input type="text"     id="text_readonly" readonly="readonly"/><br/>
+  <input type="password" id="password"/><br/>
+  <input type="password" id="password_readonly" readonly="readonly"/><br/>
+  <input type="checkbox" id="checkbox"/><br/>
+  <input type="radio"    id="radio"/><br/>
+  <input type="submit"   id="submit"/><br/>
+  <input type="reset"    id="reset"/><br/>
+  <input type="file"     id="file"/><br/>
+  <input type="button"   id="ibutton"/><br/>
+  <input type="image"    id="image" alt="image"/><br/>
+
+  <!-- form controls -->
+  <button id="button">button</button><br/>
+  <textarea id="textarea">textarea</textarea><br/>
+  <textarea id="textarea_readonly" readonly="readonly">textarea[readonly]</textarea><br/>
+  <select id="select">
+    <option value="option" selected="selected"/>
+  </select><br/>
+  <select id="select_multiple" multiple="multiple">
+    <option value="option" selected="selected"/>
+  </select><br/>
+  <isindex id="isindex" prompt="isindex"/><br/>
+
+  <!-- a element -->
+  <a id="a_href" href="">a[href]</a><br/>
+
+  <!-- ime-mode test -->
+  <input type="text" id="ime_mode_auto"     style="ime-mode: auto;"/><br/>
+  <input type="text" id="ime_mode_normal"   style="ime-mode: normal;"/><br/>
+  <input type="text" id="ime_mode_active"   style="ime-mode: active;"/><br/>
+  <input type="text" id="ime_mode_inactive" style="ime-mode: inactive;"/><br/>
+  <input type="text" id="ime_mode_disabled" style="ime-mode: disabled;"/><br/>
+  <input type="password" id="ime_mode_auto_p"     style="ime-mode: auto;"/><br/>
+  <input type="password" id="ime_mode_normal_p"   style="ime-mode: normal;"/><br/>
+  <input type="password" id="ime_mode_active_p"   style="ime-mode: active;"/><br/>
+  <input type="password" id="ime_mode_inactive_p" style="ime-mode: inactive;"/><br/>
+  <input type="password" id="ime_mode_disabled_p" style="ime-mode: disabled;"/><br/>
+  <textarea id="ime_mode_auto_t"     style="ime-mode: auto;">textarea</textarea><br/>
+  <textarea id="ime_mode_normal_t"   style="ime-mode: normal;">textarea</textarea><br/>
+  <textarea id="ime_mode_active_t"   style="ime-mode: active;">textarea</textarea><br/>
+  <textarea id="ime_mode_inactive_t" style="ime-mode: inactive;">textarea</textarea><br/>
+  <textarea id="ime_mode_disabled_t" style="ime-mode: disabled;">textarea</textarea><br/>
+</p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+</pre>
+
+<script class="testbody" type="application/javascript">
+
+var gUtils = window.
+      QueryInterface(Components.interfaces.nsIInterfaceRequestor).
+      getInterface(Components.interfaces.nsIDOMWindowUtils);
+const kIMEEnabledSupported = navigator.platform.indexOf("Mac") == 0 ||
+                             navigator.platform.indexOf("Win") == 0 ||
+                             navigator.platform.indexOf("Linux") == 0;
+
+// We support to control IME open state on Windows and Mac actually.  However,
+// we cannot test it on Mac if the current keyboard layout is not CJK. And also
+// we cannot test it on Win32 if the system didn't be installed IME. So,
+// currently we should not run the open state testing.
+const kIMEOpenSupported = false;
+
+function runBasicTest(aIsEditable, aInDesignMode, aDescription)
+{
+  var defaultEnabledState =
+        aIsEditable ? gUtils.IME_STATUS_ENABLED : gUtils.IME_STATUS_DISABLED;
+
+  // XXX Between designMode and contentEditable, the result is different.
+  // Is this expected difference?
+  var nonFormControlNodeEnabledState =
+        aInDesignMode ? gUtils.IME_STATUS_ENABLED : gUtils.IME_STATUS_DISABLED;
+
+  function test(aTest)
+  {
+    function moveFocus(aTest)
+    {
+      if (aTest.expectedEnabled == gUtils.IME_STATUS_DISABLED) {
+        document.getElementById("text").focus();
+      } else {
+        gUtils.focus(null);
+      }
+      document.getElementById(aTest.id).focus();
+    }
+
+    function testOpened(aTest, aOpened)
+    {
+      document.getElementById("text").focus();
+      gUtils.IMEIsOpen = aOpened;
+      moveFocus(aTest);
+      var message = aDescription + ": " + aTest.description +
+                                            ", wrong opened state";
+      is(gUtils.IMEIsOpen,
+         aTest.changeOpened ? aTest.expectedOpened : aOpened, message);
+    }
+
+    // IME Enabled state testing
+    var enabled = gUtils.IME_STATUS_ENABLED;
+    if (kIMEEnabledSupported) {
+      moveFocus(aTest);
+      enabled = gUtils.IMEStatus;
+      is(enabled, aTest.expectedEnabled,
+         aDescription + ": " + aTest.description + ", wrong enabled state");
+    }
+
+    if (!kIMEOpenSupported || enabled != gUtils.IME_STATUS_ENABLED ||
+        aTest.expectedEnabled != gUtils.IME_STATUS_ENABLED) {
+      return;
+    }
+
+    // IME Open state testing
+    testOpened(aTest, false);
+    testOpened(aTest, true);
+  }
+
+  if (kIMEEnabledSupported) {
+    gUtils.focus(null);
+    is(gUtils.IMEStatus, nonFormControlNodeEnabledState,
+       aDescription + ": unexpected enabled state when no element has focus");
+  }
+
+  const kTests = [
+    { id: "text",
+      description: "input[type=text]",
+      expectedEnabled: gUtils.IME_STATUS_ENABLED },
+    { id: "text_readonly",
+      description: "input[type=text][readonly]",
+      expectedEnabled: gUtils.IME_STATUS_DISABLED },
+    { id: "password",
+      description: "input[type=password]",
+      expectedEnabled: gUtils.IME_STATUS_PASSWORD },
+    { id: "password_readonly",
+      description: "input[type=password][readonly]",
+      expectedEnabled: gUtils.IME_STATUS_DISABLED },
+    { id: "checkbox",
+      description: "input[type=checkbox]",
+      expectedEnabled: defaultEnabledState },
+    { id: "radio",
+      description: "input[type=radio]",
+      expectedEnabled: defaultEnabledState },
+    { id: "submit",
+      description: "input[type=submit]",
+      expectedEnabled: defaultEnabledState },
+    { id: "reset",
+      description: "input[type=reset]",
+      expectedEnabled: defaultEnabledState },
+    { id: "file",
+      description: "input[type=file]",
+      expectedEnabled: defaultEnabledState },
+    { id: "button",
+      description: "input[type=button]",
+      expectedEnabled: defaultEnabledState },
+    { id: "image",
+      description: "input[type=image]",
+      expectedEnabled: defaultEnabledState },
+
+    // form controls
+    { id: "button",
+      description: "button",
+      expectedEnabled: defaultEnabledState },
+    { id: "textarea",
+      description: "textarea",
+      expectedEnabled: gUtils.IME_STATUS_ENABLED },
+    { id: "textarea_readonly",
+      description: "textarea[readonly]",
+      expectedEnabled: gUtils.IME_STATUS_DISABLED },
+    { id: "select",
+      description: "select (dropdown list)",
+      expectedEnabled: defaultEnabledState },
+    { id: "select_multiple",
+      description: "select (list box)",
+      expectedEnabled: defaultEnabledState },
+
+    // a element
+    { id: "a_href",
+      description: "a[href]",
+      expectedEnabled: nonFormControlNodeEnabledState },
+
+    // ime-mode
+    { id: "ime_mode_auto",
+      description: "input[type=text][style=\"ime-mode: auto;\"]",
+      expectedEnabled: gUtils.IME_STATUS_ENABLED },
+    { id: "ime_mode_normal",
+      description: "input[type=text][style=\"ime-mode: normal;\"]",
+      expectedEnabled: gUtils.IME_STATUS_ENABLED },
+    { id: "ime_mode_active",
+      description: "input[type=text][style=\"ime-mode: active;\"]",
+      expectedEnabled: gUtils.IME_STATUS_ENABLED,
+      changeOpened: true, expectedOpened: true },
+    { id: "ime_mode_inactive",
+      description: "input[type=text][style=\"ime-mode: inactive;\"]",
+      expectedEnabled: gUtils.IME_STATUS_ENABLED,
+      changeOpened: true, expectedOpened: false },
+    { id: "ime_mode_disabled",
+      description: "input[type=text][style=\"ime-mode: disabled;\"]",
+      expectedEnabled: gUtils.IME_STATUS_PASSWORD },
+    { id: "ime_mode_auto_p",
+      description: "input[type=password][style=\"ime-mode: auto;\"]",
+      expectedEnabled: gUtils.IME_STATUS_PASSWORD },
+    { id: "ime_mode_normal_p",
+      description: "input[type=password][style=\"ime-mode: normal;\"]",
+      expectedEnabled: gUtils.IME_STATUS_ENABLED },
+    { id: "ime_mode_active_p",
+      description: "input[type=password][style=\"ime-mode: active;\"]",
+      expectedEnabled: gUtils.IME_STATUS_ENABLED,
+      changeOpened: true, expectedOpened: true },
+    { id: "ime_mode_inactive_p",
+      description: "input[type=password][style=\"ime-mode: inactive;\"]",
+      expectedEnabled: gUtils.IME_STATUS_ENABLED,
+      changeOpened: true, expectedOpened: false },
+    { id: "ime_mode_disabled_p",
+      description: "input[type=password][style=\"ime-mode: disabled;\"]",
+      expectedEnabled: gUtils.IME_STATUS_PASSWORD },
+    { id: "ime_mode_auto",
+      description: "textarea[style=\"ime-mode: auto;\"]",
+      expectedEnabled: gUtils.IME_STATUS_ENABLED },
+    { id: "ime_mode_normal",
+      description: "textarea[style=\"ime-mode: normal;\"]",
+      expectedEnabled: gUtils.IME_STATUS_ENABLED },
+    { id: "ime_mode_active",
+      description: "textarea[style=\"ime-mode: active;\"]",
+      expectedEnabled: gUtils.IME_STATUS_ENABLED,
+      changeOpened: true, expectedOpened: true },
+    { id: "ime_mode_inactive",
+      description: "textarea[style=\"ime-mode: inactive;\"]",
+      expectedEnabled: gUtils.IME_STATUS_ENABLED,
+      changeOpened: true, expectedOpened: false },
+    { id: "ime_mode_disabled",
+      description: "textarea[style=\"ime-mode: disabled;\"]",
+      expectedEnabled: gUtils.IME_STATUS_PASSWORD }
+  ];
+
+  for (var i = 0; i < kTests.length; i++) {
+    test(kTests[i]);
+  }
+}
+
+function runTypeChangingTest()
+{
+  if (!kIMEEnabledSupported)
+    return;
+
+  const kInputControls = [
+    { id: "text",
+      type: "text",     expected: gUtils.IME_STATUS_ENABLED,
+      description: "[type=\"text\"]" },
+    { id: "text_readonly",
+      type: "text",     expected: gUtils.IME_STATUS_DISABLED, isReadonly: true,
+      description: "[type=\"text\"][readonly]" },
+    { id: "password",
+      type: "password", expected: gUtils.IME_STATUS_PASSWORD,
+      description: "[type=\"password\"]" },
+    { id: "password_readonly",
+      type: "password", expected: gUtils.IME_STATUS_DISABLED, isReadonly: true,
+      description: "[type=\"password\"][readonly]" },
+    { id: "checkbox",
+      type: "checkbox", expected: gUtils.IME_STATUS_DISABLED,
+      description: "[type=\"checkbox\"]" },
+    { id: "radio",
+      type: "radio",    expected: gUtils.IME_STATUS_DISABLED,
+      description: "[type=\"radio\"]" },
+    { id: "submit",
+      type: "submit",   expected: gUtils.IME_STATUS_DISABLED,
+      description: "[type=\"submit\"]" },
+    { id: "reset",
+      type: "reset",    expected: gUtils.IME_STATUS_DISABLED,
+      description: "[type=\"reset\"]" },
+    { id: "file",
+      type: "file",     expected: gUtils.IME_STATUS_DISABLED,
+      description: "[type=\"file\"]" },
+    { id: "ibutton",
+      type: "button",   expected: gUtils.IME_STATUS_DISABLED,
+      description: "[type=\"button\"]" },
+    { id: "image",
+      type: "image",    expected: gUtils.IME_STATUS_DISABLED,
+      description: "[type=\"image\"]" },
+    { id: "ime_mode_auto",
+      type: "text",     expected: gUtils.IME_STATUS_ENABLED,  imeMode:  true,
+      description: "[type=\"text\"][ime-mode: auto;]" },
+    { id: "ime_mode_normal",
+      type: "text",     expected: gUtils.IME_STATUS_ENABLED,  imeMode:  true,
+      description: "[type=\"text\"][ime-mode: normal;]" },
+    { id: "ime_mode_active",
+      type: "text",     expected: gUtils.IME_STATUS_ENABLED,  imeMode:  true,
+      description: "[type=\"text\"][ime-mode: active;]" },
+    { id: "ime_mode_inactive",
+      type: "text",     expected: gUtils.IME_STATUS_ENABLED,  imeMode:  true,
+      description: "[type=\"text\"][ime-mode: inactive;]" },
+    { id: "ime_mode_disabled",
+      type: "text",     expected: gUtils.IME_STATUS_PASSWORD, imeMode:  true,
+      description: "[type=\"text\"][ime-mode: disabled;]" },
+    { id: "ime_mode_auto_p",
+      type: "password", expected: gUtils.IME_STATUS_ENABLED,  imeMode:  true,
+      description: "[type=\"password\"][ime-mode: auto;]" },
+    { id: "ime_mode_normal_p",
+      type: "password", expected: gUtils.IME_STATUS_ENABLED,  imeMode:  true,
+      description: "[type=\"password\"][ime-mode: normal;]" },
+    { id: "ime_mode_active_p",
+      type: "password", expected: gUtils.IME_STATUS_ENABLED,  imeMode:  true,
+      description: "[type=\"password\"][ime-mode: active;]" },
+    { id: "ime_mode_inactive_p",
+      type: "password", expected: gUtils.IME_STATUS_ENABLED,  imeMode:  true,
+      description: "[type=\"password\"][ime-mode: inactive;]" },
+    { id: "ime_mode_disabled_p",
+      type: "password", expected: gUtils.IME_STATUS_PASSWORD, imeMode:  true,
+      description: "[type=\"password\"][ime-mode: disabled;]" }
+  ];
+
+  const kInputTypes = [
+    { type: "",         expected: gUtils.IME_STATUS_ENABLED  },
+    { type: "text",     expected: gUtils.IME_STATUS_ENABLED  },
+    { type: "password", expected: gUtils.IME_STATUS_PASSWORD },
+    { type: "checkbox", expected: gUtils.IME_STATUS_DISABLED },
+    { type: "radio",    expected: gUtils.IME_STATUS_DISABLED },
+    { type: "submit",   expected: gUtils.IME_STATUS_DISABLED },
+    { type: "reset",    expected: gUtils.IME_STATUS_DISABLED },
+    { type: "file",     expected: gUtils.IME_STATUS_DISABLED },
+    { type: "button",   expected: gUtils.IME_STATUS_DISABLED },
+    { type: "image",    expected: gUtils.IME_STATUS_DISABLED }
+  ];
+
+  function getExpectedIMEEnabled(aNewType, aInputControl)
+  {
+    if (aNewType.expected == gUtils.IME_STATUS_DISABLED ||
+        aInputControl.isReadOnly)
+      return gUtils.IME_STATUS_DISABLED;
+    return aInputControl.imeMode ? aInputControl.expected : aNewType.expected;
+  }
+
+  const kOpenedState = [ true, false ];
+
+  for (var i = 0; i < kOpenedState.length; i++) {
+    const kOpened = kOpenedState[i];
+    for (var j = 0; j < kInputControls.length; j++) {
+      const kInput = kInputControls[j];
+      var e = document.getElementById(kInput.id);
+      e.focus();
+      for (var k = 0; k < kInputTypes.length; k++) {
+        const kType = kInputTypes[k];
+        var typeChangingDescription =
+          "\"" + e.getAttribute("type") + "\" to \"" + kInput.type + "\"";
+        e.setAttribute("type", kInput.type);
+        is(gUtils.IMEStatus, kInput.expected,
+           "type attr changing test: " + typeChangingDescription +
+             " (" +  kInput.description + ")");
+
+        const kTestOpenState = kIMEOpenSupported &&
+                gUtils.IMEStatus == gUtils.IME_STATUS_ENABLED &&
+                getExpectedIMEEnabled(kType, kInput) == gUtils.IME_STATUS_ENABLED;
+        if (kTestOpenState) {
+          gUtils.IMEIsOpen = kOpened;
+        }
+
+        typeChangingDescription =
+          "\"" + e.getAttribute("type") + "\" to \"" + kType.type + "\"";
+        if (kType.type != "")
+          e.setAttribute("type", kType.type);
+        else
+          e.removeAttribute("type");
+
+        is(gUtils.IMEStatus, getExpectedIMEEnabled(kType, kInput),
+           "type attr changing test: " + typeChangingDescription +
+             " (" +  kInput.description + ")");
+        if (kTestOpenState && gUtils.IMEStatus == gUtils.IME_STATUS_ENABLED) {
+          is(gUtils.IMEIsOpen, kOpened,
+             "type attr changing test (open state is changed): " +
+               typeChangingDescription + " (" +  kInput.description + ")");
+        }
+      }
+      // reset the type to default
+      e.setAttribute("type", kInput.type);
+    }
+    if (!kIMEOpenSupported)
+      break;
+  }
+}
+
+function runReadonlyChangingTest()
+{
+  if (!kIMEEnabledSupported)
+    return;
+
+  const kInputControls = [
+    { id: "text",
+      type: "text",     expected: gUtils.IME_STATUS_ENABLED  },
+    { id: "password",
+      type: "password", expected: gUtils.IME_STATUS_PASSWORD },
+    { id: "textarea",
+      type: "textarea", expected: gUtils.IME_STATUS_ENABLED  }
+  ];
+  const kOpenedState = [ true, false ];
+
+  for (var i = 0; i < kOpenedState.length; i++) {
+    const kOpened = kOpenedState[i];
+    for (var j = 0; j < kInputControls.length; j++) {
+      const kInput = kInputControls[j];
+      var e = document.getElementById(kInput.id);
+      e.focus();
+      if (kIMEOpenSupported && gUtils.IMEStatus == gUtils.IME_STATUS_ENABLED) {
+        gUtils.IMEIsOpen = kOpened;
+      }
+      e.setAttribute("readonly", "readonly");
+      is(gUtils.IMEStatus, gUtils.IME_STATUS_DISABLED,
+         "readonly attr setting test: type=" + kInput.type);
+      e.removeAttribute("readonly");
+      is(gUtils.IMEStatus, kInput.expected,
+         "readonly attr removing test: type=" + kInput.type);
+      if (kIMEOpenSupported && gUtils.IMEStatus == gUtils.IME_STATUS_ENABLED) {
+        is(gUtils.IMEIsOpen, kOpened,
+           "readonly attr removing test (open state is changed): type=" +
+           kInput.type);
+      }
+    }
+    if (!kIMEOpenSupported)
+      break;
+  }
+}
+
+function runTests()
+{
+  if (!kIMEEnabledSupported && !kIMEOpenSupported)
+    return;
+
+  SimpleTest.waitForExplicitFinish();
+
+  netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+
+  // test for normal contents.
+  runBasicTest(false, false, "Testing of normal contents");
+
+  var container = document.getElementById("display");
+  // test for contentEditable="true"
+  container.setAttribute("contenteditable", "true");
+  runBasicTest(true, false, "Testing [contentEditable=\"true\"]");
+
+  // test for contentEditable="false"
+  container.setAttribute("contenteditable", "false");
+  runBasicTest(false, false, "Testing [contentEditable=\"false\"]");
+
+  // test for removing contentEditable
+  container.setAttribute("contenteditable", "true");
+  container.removeAttribute("contenteditable");
+  runBasicTest(false, false, "Testing after contentEditable to be removed");
+
+  // test designMode
+  document.designMode = "on";
+  runBasicTest(true, true, "Testing designMode=\"on\"");
+  document.designMode = "off";
+  runBasicTest(false, false, "Testing designMode=\"off\"");
+
+  // changing input[type] values
+  // XXX currently, type attribute changing doesn't work fine. bug 488420.
+  // runTypeChangingTest();
+
+  // changing readonly attribute
+  // XXX currently, readonly attribute changing doesn't work fine. bug 488420.
+  // runReadonlyChangingTest();
+
+  SimpleTest.finish();
+}
+
+</script>
+</body>
+
+</html>