merge fx-team to mozilla-central
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Fri, 17 Jan 2014 10:12:58 +0100
changeset 163914 3690d1c47bf339810a018f171c22c1747e369aff
parent 163912 deeb227a03de8356e85278b0841d735b27113ced (current diff)
parent 163913 50a2220b70bf0d9eb6a57cd86daae7f6af8ef834 (diff)
child 163938 b53589696cf894ceb144094c0181ebd59380a18c
push id38584
push usercbook@mozilla.com
push dateFri, 17 Jan 2014 10:04:30 +0000
treeherdermozilla-inbound@28a9d7e2416f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone29.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge fx-team to mozilla-central
--- a/browser/metro/base/content/helperui/SelectHelperUI.js
+++ b/browser/metro/base/content/helperui/SelectHelperUI.js
@@ -23,82 +23,91 @@ var SelectHelperUI = {
 
   get _menuPopup() {
     let popup = document.getElementById("select-popup");
     delete this._menuPopup;
     return this._menuPopup = new MenuPopup(this._container, popup);
   },
 
   show: function selectHelperShow(aList, aTitle, aRect) {
-    if (this._list)
+    if (this._list) {
       this.reset();
+    }
 
     this._list = aList;
 
-    // The element label is used as a title to give more context
-    this._container.setAttribute("multiple", aList.multiple ? "true" : "false");
+    this._listbox.setAttribute("seltype", aList.multiple ? "multiple" : "single");
 
     let firstSelected = null;
 
     // Using a fragment prevent us to hang on huge list
     let fragment = document.createDocumentFragment();
     let choices = aList.choices;
+    let selectedItems = [];
     for (let i = 0; i < choices.length; i++) {
       let choice = choices[i];
       let item = document.createElement("richlistitem");
       let label = document.createElement("label");
 
       item.setAttribute("class", "option-command listitem-iconic");
       item.setAttribute("flex", "1");
       item.setAttribute("crop", "center");
       label.setAttribute("value", choice.text);
       item.appendChild(label);
-
-      choice.selected ? item.setAttribute("selected", "true")
-                      : item.removeAttribute("selected");
-
+      item.setAttribute("oldstate", "false");
       choice.disabled ? item.setAttribute("disabled", "true")
                       : item.removeAttribute("disabled");
+
       fragment.appendChild(item);
 
       if (choice.group) {
         item.classList.add("optgroup");
         continue;
       }
 
       item.optionIndex = choice.optionIndex;
       item.choiceIndex = i;
 
-      if (choice.inGroup)
+      if (choice.inGroup) {
         item.classList.add("in-optgroup");
+      }
 
       if (choice.selected) {
-        item.classList.add("selected");
         firstSelected = firstSelected || item;
+        selectedItems.push(item);
       }
     }
     this._listbox.appendChild(fragment);
 
     this._container.addEventListener("click", this, false);
+    window.addEventListener("MozPrecisePointer", this, false);
     this._menuPopup.show(this._positionOptions(aRect));
+
+    // Setup pre-selected items. Note, this has to happen after show.
+    this._listbox.clearSelection();
+    for (let item of selectedItems) {
+      this._listbox.addItemToSelection(item);
+      item.setAttribute("oldstate", "true");
+    }
     this._listbox.ensureElementIsVisible(firstSelected);
   },
 
   reset: function selectHelperReset() {
     this._updateControl();
     while (this._listbox.hasChildNodes())
       this._listbox.removeChild(this._listbox.lastChild);
     this._list = null;
   },
 
   hide: function selectHelperHide() {
     if (!this._list)
       return;
 
     this._container.removeEventListener("click", this, false);
+    window.removeEventListener("MozPrecisePointer", this, false);
     this._menuPopup.hide();
     this.reset();
   },
 
   _positionOptions: function _positionOptions(aRect) {
     let browser = Browser.selectedBrowser;
     let p0 = browser.ptBrowserToClient(aRect.left, aRect.top);
     let p1 = browser.ptBrowserToClient(aRect.right, aRect.bottom);
@@ -106,41 +115,48 @@ var SelectHelperUI = {
     return {
       xPos: p0.x,
       yPos: p1.y,
       bottomAligned: false,
       leftAligned: true
     };
   },
 
-  _forEachOption: function _selectHelperForEachOption(aCallback) {
-    let children = this._listbox.childNodes;
-    for (let i = 0; i < children.length; i++) {
-      let item = children[i];
-      if (!item.hasOwnProperty("optionIndex"))
-        continue;
-      aCallback(item, i);
-    }
-  },
-
   _updateControl: function _selectHelperUpdateControl() {
     Browser.selectedBrowser.messageManager.sendAsyncMessage("FormAssist:ChoiceChange", { });
   },
 
   handleEvent: function selectHelperHandleEvent(aEvent) {
     switch (aEvent.type) {
+      case "MozPrecisePointer":
+        this.hide();
+      break;
       case "click":
         let item = aEvent.target;
         if (item && item.hasOwnProperty("optionIndex")) {
           if (this._list.multiple) {
-            item.classList.toggle("selected");
-          } else {
-            item.classList.add("selected");
+            // item.selected is always true here since that's how richlistbox handles
+            // mouse click events. We track our own state so that we can toggle here
+            // on click events. Iniial 'oldstate' values are setup in show above.
+            if (item.getAttribute("oldstate") == "true") {
+              item.setAttribute("oldstate", "false");
+            } else {
+              item.setAttribute("oldstate", "true");
+            }
+            // Fix up selected items - richlistbox will clear selection on click events
+            // so we need to set selection on the items the user has previously chosen.
+            this._listbox.clearSelection();
+            for (let node of this._listbox.childNodes) {
+              if (node.getAttribute("oldstate") == "true") {
+                this._listbox.addItemToSelection(node);
+              }
+            }
           }
-          this.onSelect(item.optionIndex, item.classList.contains("selected"));
+          // Let the form element know we've added or removed a selected item.
+          this.onSelect(item.optionIndex, item.selected);
         }
         break;
     }
   },
 
   onSelect: function selectHelperOnSelect(aIndex, aSelected) {
     Browser.selectedBrowser.messageManager.sendAsyncMessage("FormAssist:ChoiceSelect", {
       index: aIndex,
new file mode 100644
--- /dev/null
+++ b/browser/metro/base/tests/mochitest/browser_form_selects.html
@@ -0,0 +1,21 @@
+<html>
+<body bgcolor=white>
+<br />
+<br />
+<br />
+<br />
+<center>
+<select id="selectelement" style="height:200px; width:150px;" multiple>
+<option id="opt1">option 1</option>
+<option id="opt2">option 2</option>
+<option id="opt3">option 3</option>
+<option id="opt4">option 4</option>
+<option id="opt5">option 5</option>
+<option id="opt6">option 6</option>
+<option id="opt7">option 7</option>
+<option id="opt8">option 8</option>
+<option id="opt9">option 9</option>
+</select>
+</center>
+</body>
+</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/metro/base/tests/mochitest/browser_form_selects.js
@@ -0,0 +1,71 @@
+// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+function test() {
+  runTests();
+}
+
+gTests.push({
+  desc: "form multi-select test 1",
+  setUp: function () {
+  },
+  tearDown: function () {
+  },
+  run: function () {
+    yield addTab(chromeRoot + "browser_form_selects.html");
+    yield waitForCondition(function () {
+      return !Browser.selectedTab.isLoading();
+    });
+
+    let win = Browser.selectedTab.browser.contentWindow;
+    let tabdoc = Browser.selectedTab.browser.contentWindow.document;
+    let select = tabdoc.getElementById("selectelement");
+
+    // display the touch menu
+    let promise = waitForEvent(tabdoc, "popupshown");
+    sendNativeTap(select);
+    yield promise;
+
+    // tap every option
+    for (let node of SelectHelperUI._listbox.childNodes) {
+      sendNativeTap(node);
+    }
+
+    yield waitForMs(100);
+
+    // check the menu state
+    for (let node of SelectHelperUI._listbox.childNodes) {
+      ok(node.selected, "option is selected");
+    }
+
+    // check the underlying form state
+    for (let index = 1; index < 10; index++) {
+      let option = tabdoc.getElementById("opt" + index);
+      ok(option.selected, "opt" + index + " form option selected");
+    }
+
+    // tap every option again
+    for (let node of SelectHelperUI._listbox.childNodes) {
+      sendNativeTap(node);
+    }
+
+    yield waitForMs(100);
+
+    // check the menu state
+    for (let node of SelectHelperUI._listbox.childNodes) {
+      ok(!node.selected, "option is not selected");
+    }
+
+    // check the underlying form state
+    for (let index = 1; index < 10; index++) {
+      let option = tabdoc.getElementById("opt" + index);
+      ok(!option.selected, "opt" + index + " form option not selected");
+    }
+
+  }
+});
+
--- a/browser/metro/base/tests/mochitest/metro.ini
+++ b/browser/metro/base/tests/mochitest/metro.ini
@@ -1,16 +1,17 @@
 [DEFAULT]
 support-files =
   browser_context_menu_tests_01.html
   browser_context_menu_tests_02.html
   browser_context_menu_tests_03.html
   browser_context_menu_tests_04.html
   browser_findbar.html
   browser_form_auto_complete.html
+  browser_form_selects.html
   browser_link_click.html
   browser_onscreen_keyboard.html
   browser_progress_indicator.xul
   browser_selection_basic.html
   browser_selection_caretfocus.html
   browser_selection_contenteditable.html
   browser_selection_frame_content.html
   browser_selection_frame_inputs.html
@@ -38,16 +39,17 @@ support-files =
 [browser_circular_progress_indicator.js]
 [browser_colorUtils.js]
 [browser_crashprompt.js]
 [browser_context_menu_tests.js]
 [browser_context_ui.js]
 [browser_downloads.js]
 [browser_findbar.js]
 [browser_form_auto_complete.js]
+[browser_form_selects.js]
 [browser_history.js]
 [browser_inputsource.js]
 [browser_link_click.js]
 [browser_menu_hoverstate.js]
 [browser_mouse_events.js]
 [browser_onscreen_keyboard.js]
 [browser_prefs_ui.js]
 [browser_prompt.js]
--- a/browser/metro/theme/platform.css
+++ b/browser/metro/theme/platform.css
@@ -235,21 +235,16 @@ menulist {
 }
 
 .option-command {
   min-height: @touch_button_small@;
   min-width: @touch_action_minwidth@; /* keep the button from being too narrow */
   border: 0 none;
 }
 
-.option-command.selected {
-  background-color: #ff8000;
-  color: white;
-}
-
 .option-command.optgroup {
   font-weight: bold;
   font-style: italic;
   pointer-events: none;
 }
 
 .select-popup > richlistbox > scrollbox {
   width: 100%;