widget/tests/test_imestate.html

Thu, 15 Jan 2015 15:59:08 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:59:08 +0100
branch
TOR_BUG_9701
changeset 10
ac0c01689b40
permissions
-rw-r--r--

Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 <html style="ime-mode: disabled;">
     2 <head>
     3   <title>Test for IME state controling</title>
     4   <script type="text/javascript"
     5           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
     6   <script type="text/javascript"
     7           src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
     8   <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js"></script>
     9   <link rel="stylesheet" type="text/css"
    10           href="chrome://mochikit/content/tests/SimpleTest/test.css" />
    11 </head>
    12 <body onload="setTimeout(runTests, 0);" style="ime-mode: disabled;">
    13 <div id="display" style="ime-mode: disabled;">
    14   <!-- input elements -->
    15   <input type="text"     id="text"/><br/>
    16   <input type="text"     id="text_readonly" readonly="readonly"/><br/>
    17   <input type="password" id="password"/><br/>
    18   <input type="password" id="password_readonly" readonly="readonly"/><br/>
    19   <input type="checkbox" id="checkbox"/><br/>
    20   <input type="radio"    id="radio"/><br/>
    21   <input type="submit"   id="submit"/><br/>
    22   <input type="reset"    id="reset"/><br/>
    23   <input type="file"     id="file"/><br/>
    24   <input type="button"   id="ibutton"/><br/>
    25   <input type="image"    id="image" alt="image"/><br/>
    27   <!-- html5 input elements -->
    28   <input type="url"      id="url"/><br/>
    29   <input type="email"    id="email"/><br/>
    30   <input type="search"   id="search"/><br/>
    31   <input type="tel"      id="tel"/><br/>
    32   <input type="number"   id="number"/><br/>
    34   <!-- form controls -->
    35   <button id="button">button</button><br/>
    36   <textarea id="textarea">textarea</textarea><br/>
    37   <textarea id="textarea_readonly" readonly="readonly">textarea[readonly]</textarea><br/>
    38   <select id="select">
    39     <option value="option" selected="selected"/>
    40   </select><br/>
    41   <select id="select_multiple" multiple="multiple">
    42     <option value="option" selected="selected"/>
    43   </select><br/>
    44   <isindex id="isindex" prompt="isindex"/><br/>
    46   <!-- a element -->
    47   <a id="a_href" href="about:blank">a[href]</a><br/>
    49   <!-- ime-mode test -->
    50   <input type="text" id="ime_mode_auto"     style="ime-mode: auto;"/><br/>
    51   <input type="text" id="ime_mode_normal"   style="ime-mode: normal;"/><br/>
    52   <input type="text" id="ime_mode_active"   style="ime-mode: active;"/><br/>
    53   <input type="text" id="ime_mode_inactive" style="ime-mode: inactive;"/><br/>
    54   <input type="text" id="ime_mode_disabled" style="ime-mode: disabled;"/><br/>
    56   <input type="text" id="ime_mode_auto_url"     style="ime-mode: auto;"/><br/>
    57   <input type="text" id="ime_mode_normal_url"   style="ime-mode: normal;"/><br/>
    58   <input type="text" id="ime_mode_active_url"   style="ime-mode: active;"/><br/>
    59   <input type="text" id="ime_mode_inactive_url" style="ime-mode: inactive;"/><br/>
    60   <input type="text" id="ime_mode_disabled_url" style="ime-mode: disabled;"/><br/>
    62   <input type="text" id="ime_mode_auto_email"     style="ime-mode: auto;"/><br/>
    63   <input type="text" id="ime_mode_normal_email"   style="ime-mode: normal;"/><br/>
    64   <input type="text" id="ime_mode_active_email"   style="ime-mode: active;"/><br/>
    65   <input type="text" id="ime_mode_inactive_email" style="ime-mode: inactive;"/><br/>
    66   <input type="text" id="ime_mode_disabled_email" style="ime-mode: disabled;"/><br/>
    68   <input type="text" id="ime_mode_auto_search"     style="ime-mode: auto;"/><br/>
    69   <input type="text" id="ime_mode_normal_search"   style="ime-mode: normal;"/><br/>
    70   <input type="text" id="ime_mode_active_search"   style="ime-mode: active;"/><br/>
    71   <input type="text" id="ime_mode_inactive_search" style="ime-mode: inactive;"/><br/>
    72   <input type="text" id="ime_mode_disabled_search" style="ime-mode: disabled;"/><br/>
    74   <input type="text" id="ime_mode_auto_tel"     style="ime-mode: auto;"/><br/>
    75   <input type="text" id="ime_mode_normal_tel"   style="ime-mode: normal;"/><br/>
    76   <input type="text" id="ime_mode_active_tel"   style="ime-mode: active;"/><br/>
    77   <input type="text" id="ime_mode_inactive_tel" style="ime-mode: inactive;"/><br/>
    78   <input type="text" id="ime_mode_disabled_tel" style="ime-mode: disabled;"/><br/>
    80   <input type="text" id="ime_mode_auto_number"     style="ime-mode: auto;"/><br/>
    81   <input type="text" id="ime_mode_normal_number"   style="ime-mode: normal;"/><br/>
    82   <input type="text" id="ime_mode_active_number"   style="ime-mode: active;"/><br/>
    83   <input type="text" id="ime_mode_inactive_number" style="ime-mode: inactive;"/><br/>
    84   <input type="text" id="ime_mode_disabled_number" style="ime-mode: disabled;"/><br/>
    86   <input type="password" id="ime_mode_auto_p"     style="ime-mode: auto;"/><br/>
    87   <input type="password" id="ime_mode_normal_p"   style="ime-mode: normal;"/><br/>
    88   <input type="password" id="ime_mode_active_p"   style="ime-mode: active;"/><br/>
    89   <input type="password" id="ime_mode_inactive_p" style="ime-mode: inactive;"/><br/>
    90   <input type="password" id="ime_mode_disabled_p" style="ime-mode: disabled;"/><br/>
    91   <textarea id="ime_mode_auto_t"     style="ime-mode: auto;">textarea</textarea><br/>
    92   <textarea id="ime_mode_normal_t"   style="ime-mode: normal;">textarea</textarea><br/>
    93   <textarea id="ime_mode_active_t"   style="ime-mode: active;">textarea</textarea><br/>
    94   <textarea id="ime_mode_inactive_t" style="ime-mode: inactive;">textarea</textarea><br/>
    95   <textarea id="ime_mode_disabled_t" style="ime-mode: disabled;">textarea</textarea><br/>
    97   <!-- plugin -->
    98   <object type="application/x-test" id="plugin"></object><br/>
   100   <!-- contenteditable editor -->
   101   <div id="contenteditableEditor" contenteditable="true"></div>
   103   <!-- designMode editor -->
   104   <iframe id="designModeEditor"
   105    onload="document.getElementById('designModeEditor').contentDocument.designMode = 'on';"
   106    src="data:text/html,<html><body></body></html>"></iframe><br/>
   107 </div>
   108 <div id="content" style="display: none">
   110 </div>
   111 <pre id="test">
   112 </pre>
   114 <script class="testbody" type="application/javascript">
   116 SimpleTest.waitForExplicitFinish();
   118 function hitEventLoop(aFunc, aTimes)
   119 {
   120   if (--aTimes) {
   121     setTimeout(hitEventLoop, 0, aFunc, aTimes);
   122   } else {
   123     setTimeout(aFunc, 20);
   124   }
   125 }
   127 var gUtils = window.
   128       QueryInterface(Components.interfaces.nsIInterfaceRequestor).
   129       getInterface(Components.interfaces.nsIDOMWindowUtils);
   130 var gFM = Components.classes["@mozilla.org/focus-manager;1"].
   131       getService(Components.interfaces.nsIFocusManager);
   132 const kIMEEnabledSupported = navigator.platform.indexOf("Mac") == 0 ||
   133                              navigator.platform.indexOf("Win") == 0 ||
   134                              navigator.platform.indexOf("Linux") == 0;
   136 // We support to control IME open state on Windows and Mac actually.  However,
   137 // we cannot test it on Mac if the current keyboard layout is not CJK. And also
   138 // we cannot test it on Win32 if the system didn't be installed IME. So,
   139 // currently we should not run the open state testing.
   140 const kIMEOpenSupported = false;
   142 function runBasicTest(aIsEditable, aInDesignMode, aDescription)
   143 {
   144   function test(aTest)
   145   {
   146     function moveFocus(aTest, aFocusEventHandler)
   147     {
   148       if (aInDesignMode) {
   149         if (document.activeElement) {
   150           document.activeElement.blur();
   151         }
   152       } else if (aIsEditable) {
   153         document.getElementById("display").focus();
   154       } else if (aTest.expectedEnabled == gUtils.IME_STATUS_ENABLED) {
   155         document.getElementById("password").focus();
   156       } else {
   157         document.getElementById("text").focus();
   158       }
   159       var previousFocusedElement = gFM.focusedElement;
   160       var element = document.getElementById(aTest.id);
   161       var focusEventTarget = element;
   162       var subDocument = null;
   163       if (element.contentDocument) {
   164         focusEventTarget = element.contentDocument;
   165         subDocument = element.contentDocument;
   166         element = element.contentDocument.documentElement;
   167       }
   169       focusEventTarget.addEventListener("focus", aFocusEventHandler, true);
   170       document.addEventListener("MozIMEFocusIn", aFocusEventHandler, true);
   171       document.addEventListener("MozIMEFocusOut", aFocusEventHandler, true);
   172       if (subDocument) {
   173         subDocument.addEventListener("MozIMEFocusIn", aFocusEventHandler, true);
   174         subDocument.addEventListener("MozIMEFocusOut", aFocusEventHandler, true);
   175       }
   177       element.focus();
   179       focusEventTarget.removeEventListener("focus", aFocusEventHandler, true);
   180       document.removeEventListener("MozIMEFocusIn", aFocusEventHandler, true);
   181       document.removeEventListener("MozIMEFocusOut", aFocusEventHandler, true);
   182       if (element.contentDocument) {
   183         subDocument.removeEventListener("MozIMEFocusIn", aFocusEventHandler, true);
   184         subDocument.removeEventListener("MozIMEFocusOut", aFocusEventHandler, true);
   185       }
   187       var focusedElement = gFM.focusedElement;
   188       if (focusedElement) {
   189         var bindingParent = document.getBindingParent(focusedElement);
   190         if (bindingParent) {
   191           focusedElement = bindingParent;
   192         }
   193       }
   194       if (aTest.focusable) {
   195         is(focusedElement, element,
   196            aDescription + ": " + aTest.description + ", focus didn't move");
   197         return (element == focusedElement);
   198       }
   199       is(focusedElement, previousFocusedElement,
   200          aDescription + ": " + aTest.description + ", focus moved as unexpected");
   201       return (previousFocusedElement == focusedElement);
   202     }
   204     function testOpened(aTest, aOpened)
   205     {
   206       document.getElementById("text").focus();
   207       gUtils.IMEIsOpen = aOpened;
   208       if (!moveFocus(aTest)) {
   209         return;
   210       }
   211       var message = aDescription + ": " + aTest.description +
   212                                             ", wrong opened state";
   213       is(gUtils.IMEIsOpen,
   214          aTest.changeOpened ? aTest.expectedOpened : aOpened, message);
   215     }
   217     // IME Enabled state testing
   218     var enabled = gUtils.IME_STATUS_ENABLED;
   219     if (kIMEEnabledSupported) {
   220       var focusEventCount = 0;
   221       var mozIMEFocusInCount = 0;
   222       var mozIMEFocusOutCount = 0;
   223       var IMEHasFocus = false;
   225       function onFocus(aEvent)
   226       {
   227         switch (aEvent.type) {
   228           case "focus":
   229             focusEventCount++;
   230             is(gUtils.IMEStatus, aTest.expectedEnabled,
   231                aDescription + ": " + aTest.description + ", wrong enabled state at focus event");
   232             break;
   233           case "MozIMEFocusIn":
   234             mozIMEFocusInCount++;
   235             IMEHasFocus = true;
   236             is(gUtils.IMEStatus, aTest.expectedEnabled,
   237                aDescription + ": " + aTest.description +
   238                  ", MozIMEFocusIn event must be fired after IME state is updated");
   239             break;
   240           case "MozIMEFocusOut":
   241             mozIMEFocusOutCount++;
   242             IMEHasFocus = false;
   243             var changingStatus = !(aIsEditable && aTest.expectedEnabled == gUtils.IME_STATUS_ENABLED);
   244             if (aTest.toDesignModeEditor) {
   245               is(gUtils.IME_STATUS_ENABLED, aTest.expectedEnabled,
   246                  aDescription + ": " + aTest.description +
   247                    ", MozIMEFocusOut event must be fired after IME state is updated");
   248             } else if (changingStatus) {
   249               isnot(gUtils.IMEStatus, aTest.expectedEnabled,
   250                     aDescription + ": " + aTest.description +
   251                       ", MozIMEFocusOut event must be fired before IME state is updated");
   252             } else {
   253               is(gUtils.IMEStatus, aTest.expectedEnabled,
   254                  aDescription + ": " + aTest.description +
   255                    ", MozIMEFocusOut event must be fired with expected IME state if the state isn't being changed");
   256             }
   257             break;
   258         }
   259       }
   261       if (!moveFocus(aTest, onFocus)) {
   262         return;
   263       }
   265       if (aTest.focusable) {
   266         if (!aTest.focusEventNotFired) {
   267           ok(focusEventCount > 0,
   268              aDescription + ": " + aTest.description + ", focus event is never fired");
   269           if (aTest.expectedEnabled == gUtils.IME_STATUS_ENABLED || aTest.expectedEnabled == gUtils.IME_STATUS_PASSWORD) {
   270             ok(mozIMEFocusInCount > 0,
   271                aDescription + ": " + aTest.description + ", MozIMEFocusIn event should be fired");
   272             if (aInDesignMode && !aTest.toDesignModeEditor) {
   273               is(mozIMEFocusOutCount, 0,
   274                  aDescription + ": " + aTest.description +
   275                    ", MozIMEFocusOut event shouldn't be fired in designMode since focus isn't moved from another editor");
   276             } else {
   277               ok(mozIMEFocusOutCount > 0,
   278                  aDescription + ": " + aTest.description +
   279                    ", MozIMEFocusOut event should be fired for the previous focused editor");
   280             }
   281             ok(IMEHasFocus,
   282                aDescription + ": " + aTest.description +
   283                  ", The latest MozIMEFocus* event must be MozIMEFocusIn");
   284           } else {
   285             is(mozIMEFocusInCount, 0,
   286                aDescription + ": " + aTest.description +
   287                  ", MozIMEFocusIn event shouldn't be fired");
   288             ok(mozIMEFocusOutCount > 0,
   289                aDescription + ": " + aTest.description +
   290                  ", MozIMEFocusOut event should be fired");
   291             ok(!IMEHasFocus,
   292                aDescription + ": " + aTest.description +
   293                  ", The latest MozIMEFocus* event must be MozIMEFocusOut");
   294           }
   295         } else {
   296           todo(focusEventCount > 0,
   297                aDescription + ": " + aTest.description + ", focus event should be fired");
   298         }
   299       } else {
   300         is(mozIMEFocusInCount, 0,
   301            aDescription + ": " + aTest.description +
   302              ", MozIMEFocusIn event shouldn't be fired at testing non-focusable element");
   303         is(mozIMEFocusOutCount, 0,
   304            aDescription + ": " + aTest.description +
   305              ", MozIMEFocusOut event shouldn't be fired at testing non-focusable element");
   306       }
   308       enabled = gUtils.IMEStatus;
   309       inputtype = gUtils.focusedInputType;
   310       is(enabled, aTest.expectedEnabled,
   311          aDescription + ": " + aTest.description + ", wrong enabled state");
   312       if (aTest.expectedType && !aInDesignMode) {
   313         is(inputtype, aTest.expectedType,
   314            aDescription + ": " + aTest.description + ", wrong input type");
   315       } else if (aInDesignMode) {
   316         is(inputtype, "",
   317            aDescription + ": " + aTest.description + ", wrong input type")
   318       }
   319     }
   321     if (!kIMEOpenSupported || enabled != gUtils.IME_STATUS_ENABLED ||
   322         aTest.expectedEnabled != gUtils.IME_STATUS_ENABLED) {
   323       return;
   324     }
   326     // IME Open state testing
   327     testOpened(aTest, false);
   328     testOpened(aTest, true);
   329   }
   331   if (kIMEEnabledSupported) {
   332     // make sure there is an active element
   333     document.getElementById("text").focus();
   334     document.activeElement.blur();
   335     is(gUtils.IMEStatus,
   336        aInDesignMode ? gUtils.IME_STATUS_ENABLED : gUtils.IME_STATUS_DISABLED,
   337        aDescription + ": unexpected enabled state when no element has focus");
   338   }
   340   // Form controls except text editable elements are "disable" in normal
   341   // condition, however, if they are editable, they are "enabled".
   342   // XXX Probably there are some bugs: If the form controls editable, they
   343   //     shouldn't be focusable.
   344   const kEnabledStateOnNonEditableElement =
   345     (aInDesignMode || aIsEditable) ? gUtils.IME_STATUS_ENABLED :
   346                                      gUtils.IME_STATUS_DISABLED;
   347   const kEnabledStateOnPasswordField =
   348     aInDesignMode ? gUtils.IME_STATUS_ENABLED : gUtils.IME_STATUS_PASSWORD;
   349   const kEnabledStateOnReadonlyField =
   350     aInDesignMode ? gUtils.IME_STATUS_ENABLED : gUtils.IME_STATUS_DISABLED;
   351   const kTests = [
   352     { id: "text",
   353       description: "input[type=text]",
   354       focusable: !aInDesignMode,
   355       expectedEnabled: gUtils.IME_STATUS_ENABLED,
   356       expectedType: "text" },
   357     { id: "text_readonly",
   358       description: "input[type=text][readonly]",
   359       focusable: !aInDesignMode,
   360       expectedEnabled: kEnabledStateOnReadonlyField },
   361     { id: "password",
   362       description: "input[type=password]",
   363       focusable: !aInDesignMode,
   364       expectedEnabled: kEnabledStateOnPasswordField,
   365       expectedType: "password" },
   366     { id: "password_readonly",
   367       description: "input[type=password][readonly]",
   368       focusable: !aInDesignMode,
   369       expectedEnabled: kEnabledStateOnReadonlyField },
   370     { id: "checkbox",
   371       description: "input[type=checkbox]",
   372       focusable: !aInDesignMode,
   373       focusEventNotFired: aIsEditable && !aInDesignMode,
   374       expectedEnabled: kEnabledStateOnNonEditableElement },
   375     { id: "radio",
   376       description: "input[type=radio]",
   377       focusable: !aInDesignMode,
   378       focusEventNotFired: aIsEditable && !aInDesignMode,
   379       expectedEnabled: kEnabledStateOnNonEditableElement },
   380     { id: "submit",
   381       description: "input[type=submit]",
   382       focusable: !aInDesignMode,
   383       expectedEnabled: kEnabledStateOnNonEditableElement },
   384     { id: "reset",
   385       description: "input[type=reset]",
   386       focusable: !aInDesignMode,
   387       expectedEnabled: kEnabledStateOnNonEditableElement },
   388     { id: "file",
   389       description: "input[type=file]",
   390       focusable: !aInDesignMode,
   391       focusEventNotFired: aIsEditable && !aInDesignMode,
   392       expectedEnabled: kEnabledStateOnNonEditableElement },
   393     { id: "button",
   394       description: "input[type=button]",
   395       focusable: !aInDesignMode,
   396       expectedEnabled: kEnabledStateOnNonEditableElement },
   397     { id: "image",
   398       description: "input[type=image]",
   399       focusable: !aInDesignMode,
   400       expectedEnabled: kEnabledStateOnNonEditableElement },
   401     { id: "url",
   402       description: "input[type=url]",
   403       focusable: !aInDesignMode,
   404       expectedEnabled: gUtils.IME_STATUS_ENABLED,
   405       expectedType: "url" },
   406     { id: "email",
   407       description: "input[type=email]",
   408       focusable: !aInDesignMode,
   409       expectedEnabled: gUtils.IME_STATUS_ENABLED,
   410       expectedType: "email" },
   411     { id: "search",
   412       description: "input[type=search]",
   413       focusable: !aInDesignMode,
   414       expectedEnabled: gUtils.IME_STATUS_ENABLED,
   415       expectedType: "search" },
   416     { id: "tel",
   417       description: "input[type=tel]",
   418       focusable: !aInDesignMode,
   419       expectedEnabled: gUtils.IME_STATUS_ENABLED,
   420       expectedType: "tel" },
   421     { id: "number",
   422       description: "input[type=number]",
   423       focusable: !aInDesignMode,
   424       expectedEnabled: gUtils.IME_STATUS_ENABLED,
   425       expectedType: "number" },
   427     // form controls
   428     { id: "button",
   429       description: "button",
   430       focusable: !aInDesignMode,
   431       expectedEnabled: kEnabledStateOnNonEditableElement },
   432     { id: "textarea",
   433       description: "textarea",
   434       focusable: !aInDesignMode,
   435       expectedEnabled: gUtils.IME_STATUS_ENABLED },
   436     { id: "textarea_readonly",
   437       description: "textarea[readonly]",
   438       focusable: !aInDesignMode,
   439       expectedEnabled: kEnabledStateOnReadonlyField },
   440     { id: "select",
   441       description: "select (dropdown list)",
   442       focusable: !aInDesignMode,
   443       focusEventNotFired: aIsEditable && !aInDesignMode,
   444       expectedEnabled: kEnabledStateOnNonEditableElement },
   445     { id: "select_multiple",
   446       description: "select (list box)",
   447       focusable: !aInDesignMode,
   448       focusEventNotFired: aIsEditable && !aInDesignMode,
   449       expectedEnabled: kEnabledStateOnNonEditableElement },
   451     // a element
   452     { id: "a_href",
   453       description: "a[href]",
   454       focusable: !aIsEditable && !aInDesignMode,
   455       expectedEnabled: kEnabledStateOnNonEditableElement },
   457     // ime-mode
   458     { id: "ime_mode_auto",
   459       description: "input[type=text][style=\"ime-mode: auto;\"]",
   460       focusable: !aInDesignMode,
   461       expectedEnabled: gUtils.IME_STATUS_ENABLED },
   462     { id: "ime_mode_normal",
   463       description: "input[type=text][style=\"ime-mode: normal;\"]",
   464       focusable: !aInDesignMode,
   465       expectedEnabled: gUtils.IME_STATUS_ENABLED },
   466     { id: "ime_mode_active",
   467       description: "input[type=text][style=\"ime-mode: active;\"]",
   468       expectedEnabled: gUtils.IME_STATUS_ENABLED,
   469       focusable: !aInDesignMode,
   470       changeOpened: true, expectedOpened: true },
   471     { id: "ime_mode_inactive",
   472       description: "input[type=text][style=\"ime-mode: inactive;\"]",
   473       expectedEnabled: gUtils.IME_STATUS_ENABLED,
   474       focusable: !aInDesignMode,
   475       changeOpened: true, expectedOpened: false },
   476     { id: "ime_mode_disabled",
   477       description: "input[type=text][style=\"ime-mode: disabled;\"]",
   478       focusable: !aInDesignMode,
   479       expectedEnabled: kEnabledStateOnPasswordField },
   481     { id: "ime_mode_auto_url",
   482       description: "input[type=url][style=\"ime-mode: auto;\"]",
   483       focusable: !aInDesignMode,
   484       expectedEnabled: gUtils.IME_STATUS_ENABLED },
   485     { id: "ime_mode_normal_url",
   486       description: "input[type=url][style=\"ime-mode: normal;\"]",
   487       focusable: !aInDesignMode,
   488       expectedEnabled: gUtils.IME_STATUS_ENABLED },
   489     { id: "ime_mode_active_url",
   490       description: "input[type=url][style=\"ime-mode: active;\"]",
   491       expectedEnabled: gUtils.IME_STATUS_ENABLED,
   492       focusable: !aInDesignMode,
   493       changeOpened: true, expectedOpened: true },
   494     { id: "ime_mode_inactive_url",
   495       description: "input[type=url][style=\"ime-mode: inactive;\"]",
   496       expectedEnabled: gUtils.IME_STATUS_ENABLED,
   497       focusable: !aInDesignMode,
   498       changeOpened: true, expectedOpened: false },
   499     { id: "ime_mode_disabled_url",
   500       description: "input[type=url][style=\"ime-mode: disabled;\"]",
   501       focusable: !aInDesignMode,
   502       expectedEnabled: kEnabledStateOnPasswordField },
   504     { id: "ime_mode_auto_email",
   505       description: "input[type=email][style=\"ime-mode: auto;\"]",
   506       focusable: !aInDesignMode,
   507       expectedEnabled: gUtils.IME_STATUS_ENABLED },
   508     { id: "ime_mode_normal_email",
   509       description: "input[type=email][style=\"ime-mode: normal;\"]",
   510       focusable: !aInDesignMode,
   511       expectedEnabled: gUtils.IME_STATUS_ENABLED },
   512     { id: "ime_mode_active_email",
   513       description: "input[type=email][style=\"ime-mode: active;\"]",
   514       expectedEnabled: gUtils.IME_STATUS_ENABLED,
   515       focusable: !aInDesignMode,
   516       changeOpened: true, expectedOpened: true },
   517     { id: "ime_mode_inactive_email",
   518       description: "input[type=email][style=\"ime-mode: inactive;\"]",
   519       expectedEnabled: gUtils.IME_STATUS_ENABLED,
   520       focusable: !aInDesignMode,
   521       changeOpened: true, expectedOpened: false },
   522     { id: "ime_mode_disabled_email",
   523       description: "input[type=email][style=\"ime-mode: disabled;\"]",
   524       focusable: !aInDesignMode,
   525       expectedEnabled: kEnabledStateOnPasswordField },
   527     { id: "ime_mode_auto_search",
   528       description: "input[type=search][style=\"ime-mode: auto;\"]",
   529       focusable: !aInDesignMode,
   530       expectedEnabled: gUtils.IME_STATUS_ENABLED },
   531     { id: "ime_mode_normal_search",
   532       description: "input[type=search][style=\"ime-mode: normal;\"]",
   533       focusable: !aInDesignMode,
   534       expectedEnabled: gUtils.IME_STATUS_ENABLED },
   535     { id: "ime_mode_active_search",
   536       description: "input[type=search][style=\"ime-mode: active;\"]",
   537       expectedEnabled: gUtils.IME_STATUS_ENABLED,
   538       focusable: !aInDesignMode,
   539       changeOpened: true, expectedOpened: true },
   540     { id: "ime_mode_inactive_search",
   541       description: "input[type=search][style=\"ime-mode: inactive;\"]",
   542       expectedEnabled: gUtils.IME_STATUS_ENABLED,
   543       focusable: !aInDesignMode,
   544       changeOpened: true, expectedOpened: false },
   545     { id: "ime_mode_disabled_search",
   546       description: "input[type=search][style=\"ime-mode: disabled;\"]",
   547       focusable: !aInDesignMode,
   548       expectedEnabled: kEnabledStateOnPasswordField },
   550     { id: "ime_mode_auto_tel",
   551       description: "input[type=tel][style=\"ime-mode: auto;\"]",
   552       focusable: !aInDesignMode,
   553       expectedEnabled: gUtils.IME_STATUS_ENABLED },
   554     { id: "ime_mode_normal_tel",
   555       description: "input[type=tel][style=\"ime-mode: normal;\"]",
   556       focusable: !aInDesignMode,
   557       expectedEnabled: gUtils.IME_STATUS_ENABLED },
   558     { id: "ime_mode_active_tel",
   559       description: "input[type=tel][style=\"ime-mode: active;\"]",
   560       expectedEnabled: gUtils.IME_STATUS_ENABLED,
   561       focusable: !aInDesignMode,
   562       changeOpened: true, expectedOpened: true },
   563     { id: "ime_mode_inactive_tel",
   564       description: "input[type=tel][style=\"ime-mode: inactive;\"]",
   565       expectedEnabled: gUtils.IME_STATUS_ENABLED,
   566       focusable: !aInDesignMode,
   567       changeOpened: true, expectedOpened: false },
   568     { id: "ime_mode_disabled_tel",
   569       description: "input[type=tel][style=\"ime-mode: disabled;\"]",
   570       focusable: !aInDesignMode,
   571       expectedEnabled: kEnabledStateOnPasswordField },
   573     { id: "ime_mode_auto_number",
   574       description: "input[type=number][style=\"ime-mode: auto;\"]",
   575       focusable: !aInDesignMode,
   576       expectedEnabled: gUtils.IME_STATUS_ENABLED },
   577     { id: "ime_mode_normal_number",
   578       description: "input[type=number][style=\"ime-mode: normal;\"]",
   579       focusable: !aInDesignMode,
   580       expectedEnabled: gUtils.IME_STATUS_ENABLED },
   581     { id: "ime_mode_active_number",
   582       description: "input[type=number][style=\"ime-mode: active;\"]",
   583       expectedEnabled: gUtils.IME_STATUS_ENABLED,
   584       focusable: !aInDesignMode,
   585       changeOpened: true, expectedOpened: true },
   586     { id: "ime_mode_inactive_number",
   587       description: "input[type=number][style=\"ime-mode: inactive;\"]",
   588       expectedEnabled: gUtils.IME_STATUS_ENABLED,
   589       focusable: !aInDesignMode,
   590       changeOpened: true, expectedOpened: false },
   591     { id: "ime_mode_disabled_number",
   592       description: "input[type=number][style=\"ime-mode: disabled;\"]",
   593       focusable: !aInDesignMode,
   594       expectedEnabled: kEnabledStateOnPasswordField },
   596     { id: "ime_mode_auto_p",
   597       description: "input[type=password][style=\"ime-mode: auto;\"]",
   598       focusable: !aInDesignMode,
   599       expectedEnabled: kEnabledStateOnPasswordField },
   600     { id: "ime_mode_normal_p",
   601       description: "input[type=password][style=\"ime-mode: normal;\"]",
   602       focusable: !aInDesignMode,
   603       expectedEnabled: gUtils.IME_STATUS_ENABLED },
   604     { id: "ime_mode_active_p",
   605       description: "input[type=password][style=\"ime-mode: active;\"]",
   606       expectedEnabled: gUtils.IME_STATUS_ENABLED,
   607       focusable: !aInDesignMode,
   608       changeOpened: true, expectedOpened: true },
   609     { id: "ime_mode_inactive_p",
   610       description: "input[type=password][style=\"ime-mode: inactive;\"]",
   611       expectedEnabled: gUtils.IME_STATUS_ENABLED,
   612       focusable: !aInDesignMode,
   613       changeOpened: true, expectedOpened: false },
   614     { id: "ime_mode_disabled_p",
   615       description: "input[type=password][style=\"ime-mode: disabled;\"]",
   616       focusable: !aInDesignMode,
   617       expectedEnabled: kEnabledStateOnPasswordField },
   618     { id: "ime_mode_auto",
   619       description: "textarea[style=\"ime-mode: auto;\"]",
   620       focusable: !aInDesignMode,
   621       expectedEnabled: gUtils.IME_STATUS_ENABLED },
   622     { id: "ime_mode_normal",
   623       description: "textarea[style=\"ime-mode: normal;\"]",
   624       focusable: !aInDesignMode,
   625       expectedEnabled: gUtils.IME_STATUS_ENABLED },
   626     { id: "ime_mode_active",
   627       description: "textarea[style=\"ime-mode: active;\"]",
   628       focusable: !aInDesignMode,
   629       expectedEnabled: gUtils.IME_STATUS_ENABLED,
   630       changeOpened: true, expectedOpened: true },
   631     { id: "ime_mode_inactive",
   632       description: "textarea[style=\"ime-mode: inactive;\"]",
   633       focusable: !aInDesignMode,
   634       expectedEnabled: gUtils.IME_STATUS_ENABLED,
   635       changeOpened: true, expectedOpened: false },
   636     { id: "ime_mode_disabled",
   637       description: "textarea[style=\"ime-mode: disabled;\"]",
   638       focusable: !aInDesignMode,
   639       expectedEnabled: kEnabledStateOnPasswordField },
   641     // HTML editors
   642     { id: "contenteditableEditor",
   643       description: "div[contenteditable=\"true\"]",
   644       focusable: !aIsEditable && !aInDesignMode,
   645       expectedEnabled: gUtils.IME_STATUS_ENABLED },
   646     { id: "designModeEditor",
   647       description: "designMode editor",
   648       focusable: true,
   649       toDesignModeEditor: true,
   650       expectedEnabled: gUtils.IME_STATUS_ENABLED },
   651   ];
   653   for (var i = 0; i < kTests.length; i++) {
   654     test(kTests[i]);
   655   }
   656 }
   658 function runPluginTest()
   659 {
   660   if (!kIMEEnabledSupported) {
   661     return;
   662   }
   664   if (navigator.platform.indexOf("Mac") == 0) {
   665     // XXX on mac, currently, this test isn't passed because it doesn't return
   666     // IME_STATUS_PLUGIN by its bug.
   667     return;
   668   }
   670   var plugin = document.getElementById("plugin");
   672   document.activeElement.blur();
   673   is(gUtils.IMEStatus, gUtils.IME_STATUS_DISABLED,
   674      "runPluginTest: unexpected enabled state when no element has focus");
   676   plugin.focus();
   677   is(gUtils.IMEStatus, gUtils.IME_STATUS_PLUGIN,
   678      "runPluginTest: unexpected enabled state when plugin has focus");
   680   plugin.blur();
   681   is(gUtils.IMEStatus, gUtils.IME_STATUS_DISABLED,
   682      "runPluginTest: unexpected enabled state when plugin has focus");
   684   plugin.focus();
   685   is(gUtils.IMEStatus, gUtils.IME_STATUS_PLUGIN,
   686      "runPluginTest: unexpected enabled state when plugin has focus #2");
   688   var parent = plugin.parentNode;
   689   parent.removeChild(plugin);
   690   is(gUtils.IMEStatus, gUtils.IME_STATUS_DISABLED,
   691      "runPluginTest: unexpected enabled state when plugin is removed from tree");
   693   document.getElementById("text").focus();
   694   is(gUtils.IMEStatus, gUtils.IME_STATUS_ENABLED,
   695      "runPluginTest: unexpected enabled state when input[type=text] has focus");
   696 }
   698 function runTypeChangingTest()
   699 {
   700   if (!kIMEEnabledSupported)
   701     return;
   703   const kInputControls = [
   704     { id: "text",
   705       type: "text",     expected: gUtils.IME_STATUS_ENABLED,
   706       description: "[type=\"text\"]" },
   707     { id: "text_readonly",
   708       type: "text",     expected: gUtils.IME_STATUS_DISABLED, isReadonly: true,
   709       description: "[type=\"text\"][readonly]" },
   710     { id: "password",
   711       type: "password", expected: gUtils.IME_STATUS_PASSWORD,
   712       description: "[type=\"password\"]" },
   713     { id: "password_readonly",
   714       type: "password", expected: gUtils.IME_STATUS_DISABLED, isReadonly: true,
   715       description: "[type=\"password\"][readonly]" },
   716     { id: "checkbox",
   717       type: "checkbox", expected: gUtils.IME_STATUS_DISABLED,
   718       description: "[type=\"checkbox\"]" },
   719     { id: "radio",
   720       type: "radio",    expected: gUtils.IME_STATUS_DISABLED,
   721       description: "[type=\"radio\"]" },
   722     { id: "submit",
   723       type: "submit",   expected: gUtils.IME_STATUS_DISABLED,
   724       description: "[type=\"submit\"]" },
   725     { id: "reset",
   726       type: "reset",    expected: gUtils.IME_STATUS_DISABLED,
   727       description: "[type=\"reset\"]" },
   728     { id: "file",
   729       type: "file",     expected: gUtils.IME_STATUS_DISABLED,
   730       description: "[type=\"file\"]" },
   731     { id: "ibutton",
   732       type: "button",   expected: gUtils.IME_STATUS_DISABLED,
   733       description: "[type=\"button\"]" },
   734     { id: "image",
   735       type: "image",    expected: gUtils.IME_STATUS_DISABLED,
   736       description: "[type=\"image\"]" },
   737     { id: "url",
   738       type: "url",     expected: gUtils.IME_STATUS_ENABLED,
   739       description: "[type=\"url\"]" },
   740     { id: "email",
   741       type: "email",     expected: gUtils.IME_STATUS_ENABLED,
   742       description: "[type=\"email\"]" },
   743     { id: "search",
   744       type: "search",     expected: gUtils.IME_STATUS_ENABLED,
   745       description: "[type=\"search\"]" },
   746     { id: "tel",
   747       type: "tel",     expected: gUtils.IME_STATUS_ENABLED,
   748       description: "[type=\"tel\"]" },
   749     { id: "number",
   750       type: "number",     expected: gUtils.IME_STATUS_ENABLED,
   751       description: "[type=\"number\"]" },
   752     { id: "ime_mode_auto",
   753       type: "text",     expected: gUtils.IME_STATUS_ENABLED,  imeMode:  true,
   754       description: "[type=\"text\"][ime-mode: auto;]" },
   755     { id: "ime_mode_normal",
   756       type: "text",     expected: gUtils.IME_STATUS_ENABLED,  imeMode:  true,
   757       description: "[type=\"text\"][ime-mode: normal;]" },
   758     { id: "ime_mode_active",
   759       type: "text",     expected: gUtils.IME_STATUS_ENABLED,  imeMode:  true,
   760       description: "[type=\"text\"][ime-mode: active;]" },
   761     { id: "ime_mode_inactive",
   762       type: "text",     expected: gUtils.IME_STATUS_ENABLED,  imeMode:  true,
   763       description: "[type=\"text\"][ime-mode: inactive;]" },
   764     { id: "ime_mode_disabled",
   765       type: "text",     expected: gUtils.IME_STATUS_PASSWORD, imeMode:  true,
   766       description: "[type=\"text\"][ime-mode: disabled;]" },
   768     { id: "ime_mode_auto_url",
   769       type: "url",     expected: gUtils.IME_STATUS_ENABLED,  imeMode:  true,
   770       description: "[type=\"url\"][ime-mode: auto;]" },
   771     { id: "ime_mode_normal_url",
   772       type: "url",     expected: gUtils.IME_STATUS_ENABLED,  imeMode:  true,
   773       description: "[type=\"url\"][ime-mode: normal;]" },
   774     { id: "ime_mode_active_url",
   775       type: "url",     expected: gUtils.IME_STATUS_ENABLED,  imeMode:  true,
   776       description: "[type=\"url\"][ime-mode: active;]" },
   777     { id: "ime_mode_inactive_url",
   778       type: "url",     expected: gUtils.IME_STATUS_ENABLED,  imeMode:  true,
   779       description: "[type=\"url\"][ime-mode: inactive;]" },
   780     { id: "ime_mode_disabled_url",
   781       type: "url",     expected: gUtils.IME_STATUS_PASSWORD, imeMode:  true,
   782       description: "[type=\"url\"][ime-mode: disabled;]" },
   784     { id: "ime_mode_auto_email",
   785       type: "email",     expected: gUtils.IME_STATUS_ENABLED,  imeMode:  true,
   786       description: "[type=\"email\"][ime-mode: auto;]" },
   787     { id: "ime_mode_normal_email",
   788       type: "email",     expected: gUtils.IME_STATUS_ENABLED,  imeMode:  true,
   789       description: "[type=\"email\"][ime-mode: normal;]" },
   790     { id: "ime_mode_active_email",
   791       type: "email",     expected: gUtils.IME_STATUS_ENABLED,  imeMode:  true,
   792       description: "[type=\"email\"][ime-mode: active;]" },
   793     { id: "ime_mode_inactive_email",
   794       type: "email",     expected: gUtils.IME_STATUS_ENABLED,  imeMode:  true,
   795       description: "[type=\"email\"][ime-mode: inactive;]" },
   796     { id: "ime_mode_disabled_email",
   797       type: "email",     expected: gUtils.IME_STATUS_PASSWORD, imeMode:  true,
   798       description: "[type=\"email\"][ime-mode: disabled;]" },
   800     { id: "ime_mode_auto_search",
   801       type: "search",     expected: gUtils.IME_STATUS_ENABLED,  imeMode:  true,
   802       description: "[type=\"search\"][ime-mode: auto;]" },
   803     { id: "ime_mode_normal_search",
   804       type: "search",     expected: gUtils.IME_STATUS_ENABLED,  imeMode:  true,
   805       description: "[type=\"search\"][ime-mode: normal;]" },
   806     { id: "ime_mode_active_search",
   807       type: "search",     expected: gUtils.IME_STATUS_ENABLED,  imeMode:  true,
   808       description: "[type=\"search\"][ime-mode: active;]" },
   809     { id: "ime_mode_inactive_search",
   810       type: "search",     expected: gUtils.IME_STATUS_ENABLED,  imeMode:  true,
   811       description: "[type=\"search\"][ime-mode: inactive;]" },
   812     { id: "ime_mode_disabled_search",
   813       type: "search",     expected: gUtils.IME_STATUS_PASSWORD, imeMode:  true,
   814       description: "[type=\"search\"][ime-mode: disabled;]" },
   816     { id: "ime_mode_auto_tel",
   817       type: "tel",     expected: gUtils.IME_STATUS_ENABLED,  imeMode:  true,
   818       description: "[type=\"tel\"][ime-mode: auto;]" },
   819     { id: "ime_mode_normal_tel",
   820       type: "tel",     expected: gUtils.IME_STATUS_ENABLED,  imeMode:  true,
   821       description: "[type=\"tel\"][ime-mode: normal;]" },
   822     { id: "ime_mode_active_tel",
   823       type: "tel",     expected: gUtils.IME_STATUS_ENABLED,  imeMode:  true,
   824       description: "[type=\"tel\"][ime-mode: active;]" },
   825     { id: "ime_mode_inactive_tel",
   826       type: "tel",     expected: gUtils.IME_STATUS_ENABLED,  imeMode:  true,
   827       description: "[type=\"tel\"][ime-mode: inactive;]" },
   828     { id: "ime_mode_disabled_tel",
   829       type: "tel",     expected: gUtils.IME_STATUS_PASSWORD, imeMode:  true,
   830       description: "[type=\"tel\"][ime-mode: disabled;]" },
   832     { id: "ime_mode_auto_number",
   833       type: "number",     expected: gUtils.IME_STATUS_ENABLED,  imeMode:  true,
   834       description: "[type=\"number\"][ime-mode: auto;]" },
   835     { id: "ime_mode_normal_number",
   836       type: "number",     expected: gUtils.IME_STATUS_ENABLED,  imeMode:  true,
   837       description: "[type=\"number\"][ime-mode: normal;]" },
   838     { id: "ime_mode_active_number",
   839       type: "number",     expected: gUtils.IME_STATUS_ENABLED,  imeMode:  true,
   840       description: "[type=\"number\"][ime-mode: active;]" },
   841     { id: "ime_mode_inactive_number",
   842       type: "number",     expected: gUtils.IME_STATUS_ENABLED,  imeMode:  true,
   843       description: "[type=\"number\"][ime-mode: inactive;]" },
   844     { id: "ime_mode_disabled_number",
   845       type: "number",     expected: gUtils.IME_STATUS_PASSWORD, imeMode:  true,
   846       description: "[type=\"number\"][ime-mode: disabled;]" },
   848     { id: "ime_mode_auto_p",
   849       type: "password", expected: gUtils.IME_STATUS_PASSWORD,  imeMode:  true,
   850       description: "[type=\"password\"][ime-mode: auto;]" },
   851     { id: "ime_mode_normal_p",
   852       type: "password", expected: gUtils.IME_STATUS_ENABLED,  imeMode:  true,
   853       description: "[type=\"password\"][ime-mode: normal;]" },
   854     { id: "ime_mode_active_p",
   855       type: "password", expected: gUtils.IME_STATUS_ENABLED,  imeMode:  true,
   856       description: "[type=\"password\"][ime-mode: active;]" },
   857     { id: "ime_mode_inactive_p",
   858       type: "password", expected: gUtils.IME_STATUS_ENABLED,  imeMode:  true,
   859       description: "[type=\"password\"][ime-mode: inactive;]" },
   860     { id: "ime_mode_disabled_p",
   861       type: "password", expected: gUtils.IME_STATUS_PASSWORD, imeMode:  true,
   862       description: "[type=\"password\"][ime-mode: disabled;]" }
   863   ];
   865   const kInputTypes = [
   866     { type: "",         expected: gUtils.IME_STATUS_ENABLED  },
   867     { type: "text",     expected: gUtils.IME_STATUS_ENABLED  },
   868     { type: "password", expected: gUtils.IME_STATUS_PASSWORD },
   869     { type: "checkbox", expected: gUtils.IME_STATUS_DISABLED },
   870     { type: "radio",    expected: gUtils.IME_STATUS_DISABLED },
   871     { type: "submit",   expected: gUtils.IME_STATUS_DISABLED },
   872     { type: "reset",    expected: gUtils.IME_STATUS_DISABLED },
   873     { type: "file",     expected: gUtils.IME_STATUS_DISABLED },
   874     { type: "button",   expected: gUtils.IME_STATUS_DISABLED },
   875     { type: "image",    expected: gUtils.IME_STATUS_DISABLED },
   876     { type: "url",      expected: gUtils.IME_STATUS_ENABLED  },
   877     { type: "email",    expected: gUtils.IME_STATUS_ENABLED  },
   878     { type: "search",   expected: gUtils.IME_STATUS_ENABLED  },
   879     { type: "tel",      expected: gUtils.IME_STATUS_ENABLED  },
   880     { type: "number",   expected: gUtils.IME_STATUS_ENABLED  }
   881   ];
   883   function getExpectedIMEEnabled(aNewType, aInputControl)
   884   {
   885     if (aNewType.expected == gUtils.IME_STATUS_DISABLED ||
   886         aInputControl.isReadonly)
   887       return gUtils.IME_STATUS_DISABLED;
   888     return aInputControl.imeMode ? aInputControl.expected : aNewType.expected;
   889   }
   891   const kOpenedState = [ true, false ];
   893   for (var i = 0; i < kOpenedState.length; i++) {
   894     const kOpened = kOpenedState[i];
   895     for (var j = 0; j < kInputControls.length; j++) {
   896       const kInput = kInputControls[j];
   897       var e = document.getElementById(kInput.id);
   898       e.focus();
   899       for (var k = 0; k < kInputTypes.length; k++) {
   900         const kType = kInputTypes[k];
   901         var typeChangingDescription =
   902           "\"" + e.getAttribute("type") + "\" to \"" + kInput.type + "\"";
   903         e.setAttribute("type", kInput.type);
   904         is(gUtils.IMEStatus, kInput.expected,
   905            "type attr changing test (IMEStatus): " + typeChangingDescription +
   906              " (" +  kInput.description + ")");
   907         is(gUtils.focusedInputType, kInput.type,
   908            "type attr changing test (type): " + typeChangingDescription +
   909              " (" +  kInput.description + ")");
   911         const kTestOpenState = kIMEOpenSupported &&
   912                 gUtils.IMEStatus == gUtils.IME_STATUS_ENABLED &&
   913                 getExpectedIMEEnabled(kType, kInput) == gUtils.IME_STATUS_ENABLED;
   914         if (kTestOpenState) {
   915           gUtils.IMEIsOpen = kOpened;
   916         }
   918         typeChangingDescription =
   919           "\"" + e.getAttribute("type") + "\" to \"" + kType.type + "\"";
   920         if (kType.type != "")
   921           e.setAttribute("type", kType.type);
   922         else
   923           e.removeAttribute("type");
   925         is(gUtils.IMEStatus, getExpectedIMEEnabled(kType, kInput),
   926            "type attr changing test (IMEStatus): " + typeChangingDescription +
   927              " (" +  kInput.description + ")");
   928         is(gUtils.focusedInputType, kType.type,
   929            "type attr changing test (type): " + typeChangingDescription +
   930              " (" +  kInput.description + ")");
   931         if (kTestOpenState && gUtils.IMEStatus == gUtils.IME_STATUS_ENABLED) {
   932           is(gUtils.IMEIsOpen, kOpened,
   933              "type attr changing test (open state is changed): " +
   934                typeChangingDescription + " (" +  kInput.description + ")");
   935         }
   936       }
   937       // reset the type to default
   938       e.setAttribute("type", kInput.type);
   939     }
   940     if (!kIMEOpenSupported)
   941       break;
   942   }
   943 }
   945 function runReadonlyChangingTest()
   946 {
   947   if (!kIMEEnabledSupported)
   948     return;
   950   const kInputControls = [
   951     { id: "text",
   952       type: "text",     expected: gUtils.IME_STATUS_ENABLED  },
   953     { id: "password",
   954       type: "password", expected: gUtils.IME_STATUS_PASSWORD },
   955     { id: "url",
   956       type: "url",      expected: gUtils.IME_STATUS_ENABLED  },
   957     { id: "email",
   958       type: "email",    expected: gUtils.IME_STATUS_ENABLED  },
   959     { id: "search",
   960       type: "search",   expected: gUtils.IME_STATUS_ENABLED  },
   961     { id: "tel",
   962       type: "tel",      expected: gUtils.IME_STATUS_ENABLED  },
   963     { id: "number",
   964       type: "number",   expected: gUtils.IME_STATUS_ENABLED  },
   965     { id: "textarea",
   966       type: "textarea", expected: gUtils.IME_STATUS_ENABLED  }
   967   ];
   968   const kOpenedState = [ true, false ];
   970   for (var i = 0; i < kOpenedState.length; i++) {
   971     const kOpened = kOpenedState[i];
   972     for (var j = 0; j < kInputControls.length; j++) {
   973       const kInput = kInputControls[j];
   974       var e = document.getElementById(kInput.id);
   975       e.focus();
   976       if (kIMEOpenSupported && gUtils.IMEStatus == gUtils.IME_STATUS_ENABLED) {
   977         gUtils.IMEIsOpen = kOpened;
   978       }
   979       e.setAttribute("readonly", "readonly");
   980       is(gUtils.IMEStatus, gUtils.IME_STATUS_DISABLED,
   981          "readonly attr setting test: type=" + kInput.type);
   982       e.removeAttribute("readonly");
   983       is(gUtils.IMEStatus, kInput.expected,
   984          "readonly attr removing test: type=" + kInput.type);
   985       if (kIMEOpenSupported && gUtils.IMEStatus == gUtils.IME_STATUS_ENABLED) {
   986         is(gUtils.IMEIsOpen, kOpened,
   987            "readonly attr removing test (open state is changed): type=" +
   988            kInput.type);
   989       }
   990     }
   991     if (!kIMEOpenSupported)
   992       break;
   993   }
   994 }
   996 function runComplexContenteditableTests()
   997 {
   998   if (!kIMEEnabledSupported) {
   999     return;
  1002   var description = "runReadonlyChangingOnContenteditable: ";
  1004   var container = document.getElementById("display");
  1005   var button = document.getElementById("button");
  1007   // the editor has focus directly.
  1008   container.setAttribute("contenteditable", "true");
  1009   container.focus();
  1011   is(gFM.focusedElement, container,
  1012      description + "The editor doesn't get focus");
  1013   is(gUtils.IMEStatus, gUtils.IME_STATUS_ENABLED,
  1014      description + "IME isn't enabled on HTML editor");
  1015   const kReadonly =
  1016     Components.interfaces.nsIPlaintextEditor.eEditorReadonlyMask;
  1017   var editor =
  1018     window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
  1019       getInterface(Components.interfaces.nsIWebNavigation).
  1020       QueryInterface(Components.interfaces.nsIDocShell).editor;
  1021   var flags = editor.flags;
  1022   editor.flags = flags | kReadonly;
  1023   is(gFM.focusedElement, container,
  1024      description + "The editor loses focus by flag change");
  1025   is(gUtils.IMEStatus, gUtils.IME_STATUS_DISABLED,
  1026      description + "IME is still enabled on readonly HTML editor");
  1027   editor.flags = flags;
  1028   is(gFM.focusedElement, container,
  1029      description + "The editor loses focus by flag change #2");
  1030   is(gUtils.IMEStatus, gUtils.IME_STATUS_ENABLED,
  1031      description + "IME is still disabled, the editor isn't readonly now");
  1032   container.removeAttribute("contenteditable");
  1033   todo_is(gFM.focusedElement, null,
  1034           description + "The container still has focus, the editor has been no editable");
  1035   todo_is(gUtils.IMEStatus, gUtils.IME_STATUS_DISABLED,
  1036           description + "IME is still enabled on the editor, the editor has been no editable");
  1038   // a button which is in the editor has focus
  1039   button.focus();
  1040   is(gFM.focusedElement, button,
  1041      description + "The button doesn't get focus");
  1042   is(gUtils.IMEStatus, gUtils.IME_STATUS_DISABLED,
  1043      description + "IME is enabled on the button");
  1044   container.setAttribute("contenteditable", "true");
  1045   is(gFM.focusedElement, button,
  1046      description + "The button loses focus, the container is editable now");
  1047   todo_is(gUtils.IMEStatus, gUtils.IME_STATUS_ENABLED,
  1048           description + "IME is still disabled on the button, the container is editable now");
  1049   editor =
  1050     window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
  1051       getInterface(Components.interfaces.nsIWebNavigation).
  1052       QueryInterface(Components.interfaces.nsIDocShell).editor;
  1053   flags = editor.flags;
  1054   editor.flags = flags | kReadonly;
  1055   is(gFM.focusedElement, button,
  1056      description + "The button loses focus by changing editor flags");
  1057   is(gUtils.IMEStatus, gUtils.IME_STATUS_DISABLED,
  1058      description + "IME is still enabled on the button, the container is readonly now");
  1059   editor.flags = flags;
  1060   is(gFM.focusedElement, button,
  1061      description + "The button loses focus by changing editor flags #2");
  1062   is(gUtils.IMEStatus, gUtils.IME_STATUS_ENABLED,
  1063      description + "IME is still disabled on the button, the container isn't readonly now");
  1064   container.removeAttribute("contenteditable");
  1065   is(gFM.focusedElement, button,
  1066      description + "The button loses focus, the container has been no editable");
  1067   todo_is(gUtils.IMEStatus, gUtils.IME_STATUS_DISABLED,
  1068           description + "IME is still enabled on the button, the container has been no editable");
  1070   description = "testOnIndependentEditor: ";
  1071   function testOnIndependentEditor(aEditor, aEditorDescription)
  1073     var isReadonly = aEditor.readOnly;
  1074     var expectedState =
  1075       aEditor.readOnly ? gUtils.IME_STATUS_DISABLED : gUtils.IME_STATUS_ENABLED;
  1076     var unexpectedStateDescription =
  1077       expectedState != gUtils.IME_STATUS_ENABLED ? "enabled" : "disabled";
  1078     aEditor.focus();
  1079     is(gFM.focusedElement, aEditor,
  1080        description + "The " + aEditorDescription + " doesn't get focus");
  1081     is(gUtils.IMEStatus, expectedState,
  1082        description + "IME is " + unexpectedStateDescription +
  1083          " on the " + aEditorDescription);
  1084     container.setAttribute("contenteditable", "true");
  1085     is(gFM.focusedElement, aEditor,
  1086        description + "The " + aEditorDescription +
  1087          " loses focus, the container is editable now");
  1088     is(gUtils.IMEStatus, expectedState,
  1089        description + "IME becomes " + unexpectedStateDescription +
  1090          " on the " + aEditorDescription + ", the container is editable now");
  1091     editor =
  1092       window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
  1093         getInterface(Components.interfaces.nsIWebNavigation).
  1094         QueryInterface(Components.interfaces.nsIDocShell).editor;
  1095     flags = editor.flags;
  1096     editor.flags = flags | kReadonly;
  1097     is(gFM.focusedElement, aEditor,
  1098        description + "The " + aEditorDescription +
  1099          " loses focus by changing editor flags");
  1100     is(gUtils.IMEStatus, expectedState,
  1101        description + "IME becomes " + unexpectedStateDescription + " on the " +
  1102          aEditorDescription + ", the container is readonly now");
  1103     editor.flags = flags;
  1104     is(gFM.focusedElement, aEditor,
  1105        description + "The " + aEditorDescription +
  1106          " loses focus by changing editor flags #2");
  1107     is(gUtils.IMEStatus, expectedState,
  1108        description + "IME becomes " + unexpectedStateDescription + " on the " +
  1109          aEditorDescription + ", the container isn't readonly now");
  1110     container.removeAttribute("contenteditable");
  1111     is(gFM.focusedElement, aEditor,
  1112        description + "The " + aEditorDescription +
  1113          " loses focus, the container has been no editable");
  1114     is(gUtils.IMEStatus, expectedState,
  1115        description + "IME becomes " + unexpectedStateDescription + " on the " +
  1116          aEditorDescription + ", the container has been no editable");
  1119   // a textarea which is in the editor has focus
  1120   testOnIndependentEditor(document.getElementById("textarea"),
  1121                           "textarea");
  1122   // a readonly textarea which is in the editor has focus
  1123   testOnIndependentEditor(document.getElementById("textarea_readonly"),
  1124                           "textarea[readonly]");
  1125   // an input field which is in the editor has focus
  1126   testOnIndependentEditor(document.getElementById("text"),
  1127                           "input[type=\"text\"]");
  1128   // a readonly input field which is in the editor has focus
  1129   testOnIndependentEditor(document.getElementById("text_readonly"),
  1130                           "input[type=\"text\"][readonly]");
  1132   description = "testOnOutsideOfEditor: ";
  1133   function testOnOutsideOfEditor(aFocusNode, aFocusNodeDescription, aEditor)
  1135     if (aFocusNode) {
  1136       aFocusNode.focus();
  1137       is(gFM.focusedElement, aFocusNode,
  1138          description + "The " + aFocusNodeDescription + " doesn't get focus");
  1139     } else {
  1140       if (document.activeElement) {
  1141         document.activeElement.blur();
  1143       is(gFM.focusedElement, null,
  1144          description + "Unexpected element has focus");
  1146     var expectedState =
  1147       aFocusNode ? gUtils.IMEStatus : gUtils.IME_STATUS_DISABLED;
  1148     var unexpectedStateDescription =
  1149       expectedState != gUtils.IME_STATUS_ENABLED ? "enabled" : "disabled";
  1151     aEditor.setAttribute("contenteditable", "true");
  1152     is(gFM.focusedElement, aFocusNode,
  1153        description + "The " + aFocusNodeDescription +
  1154          " loses focus, a HTML editor is editable now");
  1155     is(gUtils.IMEStatus, expectedState,
  1156        description + "IME becomes " + unexpectedStateDescription +
  1157          " on the " + aFocusNodeDescription +
  1158          ", the HTML editor is editable now");
  1159     editor =
  1160       window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
  1161         getInterface(Components.interfaces.nsIWebNavigation).
  1162         QueryInterface(Components.interfaces.nsIDocShell).editor;
  1163     flags = editor.flags;
  1164     editor.flags = flags | kReadonly;
  1165     is(gFM.focusedElement, aFocusNode,
  1166        description + aFocusNodeDescription +
  1167          " loses focus by changing HTML editor flags");
  1168     is(gUtils.IMEStatus, expectedState,
  1169        description + "IME becomes " + unexpectedStateDescription + " on " +
  1170          aFocusNodeDescription + ", the HTML editor is readonly now");
  1171     editor.flags = flags;
  1172     is(gFM.focusedElement, aFocusNode,
  1173        description + aFocusNodeDescription +
  1174          " loses focus by changing HTML editor flags #2");
  1175     is(gUtils.IMEStatus, expectedState,
  1176        description + "IME becomes " + unexpectedStateDescription + " on " +
  1177          aFocusNodeDescription + ", the HTML editor isn't readonly now");
  1178     container.removeAttribute("contenteditable");
  1179     is(gFM.focusedElement, aFocusNode,
  1180        description + aFocusNodeDescription +
  1181          " loses focus, the HTML editor has been no editable");
  1182     is(gUtils.IMEStatus, expectedState,
  1183        description + "IME becomes " + unexpectedStateDescription + " on " +
  1184          aFocusNodeDescription + ", the HTML editor has been no editable");
  1187   var div = document.getElementById("contenteditableEditor");
  1188   // a textarea which is outside of the editor has focus
  1189   testOnOutsideOfEditor(document.getElementById("textarea"), "textarea", div);
  1190   // a readonly textarea which is outside of the editor has focus
  1191   testOnOutsideOfEditor(document.getElementById("textarea_readonly"),
  1192                         "textarea[readonly]", div);
  1193   // an input field which is outside of the editor has focus
  1194   testOnOutsideOfEditor(document.getElementById("text"),
  1195                         "input[type=\"text\"]", div);
  1196   // a readonly input field which outside of the editor has focus
  1197   testOnOutsideOfEditor(document.getElementById("text_readonly"),
  1198                         "input[type=\"text\"][readonly]", div);
  1199   // a readonly input field which outside of the editor has focus
  1200   testOnOutsideOfEditor(document.getElementById("button"), "button", div);
  1201   // nobody has focus.
  1202   testOnOutsideOfEditor(null, "nobody", div);
  1205 function runEditorFlagChangeTests()
  1207   if (!kIMEEnabledSupported) {
  1208     return;
  1211   var description = "runEditorFlagChangeTests: ";
  1213   var container = document.getElementById("display");
  1215   // the editor has focus directly.
  1216   container.setAttribute("contenteditable", "true");
  1217   container.focus();
  1219   is(gFM.focusedElement, container,
  1220      description + "The editor doesn't get focus");
  1221   is(gUtils.IMEStatus, gUtils.IME_STATUS_ENABLED,
  1222      description + "IME isn't enabled on HTML editor");
  1223   const kIMEStateChangeFlags =
  1224     Components.interfaces.nsIPlaintextEditor.eEditorPasswordMask |
  1225     Components.interfaces.nsIPlaintextEditor.eEditorReadonlyMask |
  1226     Components.interfaces.nsIPlaintextEditor.eEditorDisabledMask;
  1227   var editor =
  1228     window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
  1229       getInterface(Components.interfaces.nsIWebNavigation).
  1230       QueryInterface(Components.interfaces.nsIDocShell).editor;
  1231   var editorIMESupport =
  1232     editor.QueryInterface(Components.interfaces.nsIEditorIMESupport);
  1233   var flags = editor.flags;
  1235   // start composition
  1236   synthesizeComposition({ type: "compositionstart" });
  1238   // input characters
  1239   synthesizeComposition({ type: "compositionupdate",
  1240                           data: "\u3078\u3093\u3057\u3093" });
  1241   synthesizeText(
  1242     { "composition":
  1243       { "string": "\u3078\u3093\u3057\u3093",
  1244         "clauses":
  1246           { "length": 4, "attr": COMPOSITION_ATTR_RAWINPUT }
  1248       },
  1249       "caret": { "start": 4, "length": 0 }
  1250     });
  1252   editor.flags &= ~kIMEStateChangeFlags;
  1253   ok(editorIMESupport.composing,
  1254      description + "#1 IME composition was committed unexpectedly");
  1255   is(gUtils.IMEStatus, gUtils.IME_STATUS_ENABLED,
  1256      description + "#1 IME isn't enabled on HTML editor");
  1258   editor.flags |= ~kIMEStateChangeFlags;
  1259   ok(editorIMESupport.composing,
  1260      description + "#2 IME composition was committed unexpectedly");
  1261   is(gUtils.IMEStatus, gUtils.IME_STATUS_ENABLED,
  1262      description + "#2 IME isn't enabled on HTML editor");
  1264   editor.flags = flags;
  1265   ok(editorIMESupport.composing,
  1266      description + "#3 IME composition was committed unexpectedly");
  1267   is(gUtils.IMEStatus, gUtils.IME_STATUS_ENABLED,
  1268      description + "#3 IME isn't enabled on HTML editor");
  1270   // cancel the composition
  1271   synthesizeComposition({ type: "compositionupdate", data: "" });
  1272   synthesizeText(
  1273     { "composition":
  1274       { "string": "",
  1275         "clauses":
  1277           { "length": 0, "attr": 0 }
  1279       },
  1280       "caret": { "start": 0, "length": 0 }
  1281     });
  1283   synthesizeComposition({ type: "compositionend", data: "" });
  1285   container.removeAttribute("contenteditable");
  1288 function runEditableSubframeTests()
  1290   window.open("window_imestate_iframes.html", "_blank",
  1291               "width=600,height=600");
  1294 function runTestPasswordFieldOnDialog()
  1296   if (!kIMEEnabledSupported) {
  1297     return;
  1300   if (document.activeElement) {
  1301     document.activeElement.blur();
  1304   var dialog;
  1306   function WindowObserver()
  1308     Components.classes["@mozilla.org/observer-service;1"].
  1309                getService(Components.interfaces.nsIObserverService).
  1310                addObserver(this, "domwindowopened", false);
  1313   WindowObserver.prototype = {
  1314     QueryInterface: function (iid)
  1316       if (iid.equals(Components.interfaces.nsIObserver) ||
  1317           iid.equals(Components.interfaces.nsISupports)) {
  1318         return this;
  1320     },
  1322     observe: function (subject, topic, data)
  1324       if (topic === "domwindowopened") {
  1325         ok(true, "dialog window is created");
  1326         dialog = subject.QueryInterface(Components.interfaces.nsIDOMWindow);
  1327         dialog.addEventListener("load", onPasswordDialogLoad, false);
  1330   };
  1332   var observer = new WindowObserver();
  1333   var arg1 = new Object(), arg2 = new Object();
  1334   Components.classes["@mozilla.org/embedcomp/prompt-service;1"].
  1335              getService(Components.interfaces.nsIPromptService).
  1336              promptPassword(window, "title", "text", arg1, "msg", arg2);
  1338   ok(true, "password dialog was closed");
  1340   Components.classes["@mozilla.org/observer-service;1"].
  1341              getService(Components.interfaces.nsIObserverService).
  1342              removeObserver(observer, "domwindowopened");
  1344   var passwordField;
  1346   function onPasswordDialogLoad()
  1348     ok(true, "onPasswordDialogLoad is called");
  1349     dialog.removeEventListener("load", onPasswordDialogLoad, false);
  1350     passwordField = dialog.document.getElementById("password1Textbox");
  1351     passwordField.addEventListener("focus", onPasswordFieldFocus, false);
  1354   function onPasswordFieldFocus()
  1356     ok(true, "onPasswordFieldFocus is called");
  1357     passwordField.removeEventListener("focus", onPasswordFieldFocus, false);
  1358     var utils = dialog.
  1359       QueryInterface(Components.interfaces.nsIInterfaceRequestor).
  1360       getInterface(Components.interfaces.nsIDOMWindowUtils);
  1361     is(utils.IMEStatus, utils.IME_STATUS_PASSWORD,
  1362        "IME isn't disabled on a password field of password dialog");
  1363     synthesizeKey("VK_ESCAPE", { }, dialog);
  1367 // Bug 580388 and bug 808287
  1368 function runEditorReframeTests(aCallback)
  1370   if (document.activeElement) {
  1371     document.activeElement.blur();
  1374   var input = document.getElementById("text");
  1375   input.focus();
  1376   input.style.overflow = "visible";
  1378   var mozIMEFocusIn = 0;
  1379   var mozIMEFocusOut = 0;
  1380   var IMEHasFocus = false;
  1382   var handler = function (aEvent) {
  1383     switch (aEvent.type) {
  1384       case "MozIMEFocusIn":
  1385         mozIMEFocusIn++;
  1386         IMEHasFocus = true;
  1387         break;
  1388       case "MozIMEFocusOut":
  1389         mozIMEFocusOut++;
  1390         IMEHasFocus = false;
  1391         break;
  1393   };
  1395   var onInput = function (aEvent) {
  1396     aEvent.target.style.overflow = "hidden";
  1399   document.addEventListener("MozIMEFocusIn", handler, true);
  1400   document.addEventListener("MozIMEFocusOut", handler, true);
  1401   input.addEventListener("input", onInput, true);
  1403   sendChar("a");
  1405   hitEventLoop(function () {
  1406     ok(mozIMEFocusOut > 0, "runEditorReframeTests(): IME focus must be lost at reframing");
  1407     ok(mozIMEFocusIn > 0, "runEditorReframeTests(): IME focus must be restored after reframing");
  1408     ok(IMEHasFocus, "runEditorReframeTests(): IME must have focus after reframing");
  1410     var focusQueryHandler = function(aEvent) {
  1411         // Perform a style change and query during focus to trigger reframing
  1412         input.style.overflow = "visible";
  1413         synthesizeQuerySelectedText();
  1415     document.addEventListener("MozIMEFocusIn", focusQueryHandler, true);
  1416     mozIMEFocusIn = mozIMEFocusOut = 0;
  1418     input.blur();
  1419     input.focus();
  1420     sendChar("a");
  1422     hitEventLoop(function() {
  1423         ok(IMEHasFocus, "runEditorReframeTests(): IME must have focus after reframing during focus");
  1424         ok(mozIMEFocusOut == mozIMEFocusIn, "runEditorReframeTests(): IME focus/blur (" + mozIMEFocusIn + "/" + mozIMEFocusOut + ") must match after reframing during focus");
  1426         document.removeEventListener("MozIMEFocusIn", focusQueryHandler, true);
  1427         document.removeEventListener("MozIMEFocusIn", handler, true);
  1428         document.removeEventListener("MozIMEFocusOut", handler, true);
  1429         input.removeEventListener("input", onInput, true);
  1431         input.style.overflow = "visible";
  1432         input.value = "";
  1434         hitEventLoop(aCallback, 20);
  1435     }, 20);
  1436   }, 20);
  1439 function runTests()
  1441   if (!kIMEEnabledSupported && !kIMEOpenSupported)
  1442     return;
  1444   SpecialPowers.setBoolPref("test.IME", true);
  1446   // test for normal contents.
  1447   runBasicTest(false, false, "Testing of normal contents");
  1449   // test for plugin contents
  1450   runPluginTest();
  1452   var container = document.getElementById("display");
  1453   // test for contentEditable="true"
  1454   container.setAttribute("contenteditable", "true");
  1455   runBasicTest(true, false, "Testing [contentEditable=\"true\"]");
  1457   // test for contentEditable="false"
  1458   container.setAttribute("contenteditable", "false");
  1459   runBasicTest(false, false, "Testing [contentEditable=\"false\"]");
  1461   // test for removing contentEditable
  1462   container.setAttribute("contenteditable", "true");
  1463   container.removeAttribute("contenteditable");
  1464   runBasicTest(false, false, "Testing after contentEditable to be removed");
  1466   // test designMode
  1467   document.designMode = "on";
  1468   runBasicTest(true, true, "Testing designMode=\"on\"");
  1469   document.designMode = "off";
  1470   document.getElementById("text").focus();
  1471   runBasicTest(false, false, "Testing designMode=\"off\"");
  1473   // changing input[type] values
  1474   // XXX currently, type attribute changing doesn't work fine. bug 559728.
  1475   // runTypeChangingTest();
  1477   // changing readonly attribute
  1478   runReadonlyChangingTest();
  1480   // complex contenteditable editor's tests
  1481   runComplexContenteditableTests();
  1483   // test whether the IME state and composition are not changed unexpectedly
  1484   runEditorFlagChangeTests();
  1486   // test password field on dialog
  1487   // XXX temporary disable against failure
  1488   //runTestPasswordFieldOnDialog();
  1490   // Asynchronous tests
  1491   runEditorReframeTests(function () {
  1492     // This will call onFinish(), so, this test must be the last.
  1493     runEditableSubframeTests();
  1494   });
  1497 function onFinish()
  1499   SpecialPowers.clearUserPref("test.IME");
  1500   SimpleTest.finish();
  1503 </script>
  1504 </body>
  1506 </html>

mercurial