Bug 532677 - FormFill: Taskbar pops up to middle of the screen with no-associated protocol error [r=mfinkle]
authorVivien Nicolas <21@vingtetun.org>
Tue, 24 Aug 2010 10:53:22 +0200
changeset 66479 15493853066f65501bb68db4a65e7aa1c45cae76
parent 66478 dc35de8a290ad7d5450db4c8686b8d6e851ca67c
child 66480 536eb80129b441c1a3d2503608b0ceff3e7bc5b3
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmfinkle
bugs532677
Bug 532677 - FormFill: Taskbar pops up to middle of the screen with no-associated protocol error [r=mfinkle]
mobile/chrome/content/bindings.xml
mobile/chrome/content/bindings/browser.js
mobile/chrome/content/browser-ui.js
mobile/chrome/content/browser.js
mobile/chrome/content/browser.xul
mobile/chrome/content/forms.js
mobile/themes/core/browser.css
--- a/mobile/chrome/content/bindings.xml
+++ b/mobile/chrome/content/bindings.xml
@@ -1302,37 +1302,17 @@
       </handler>
 
       <handler event="click" button="0">
         <![CDATA[
           if (this.disabled || this.itemCount == 0)
             return;
 
           this.focus();
-
-          let choices = [];
-          let children = this.menupopup.children;
-          for (let i = 0; i < children.length; i++) {
-            let child = children[i];
-            choices.push({ text: child.label, selected: child.selected, optionIndex: i });
-          }
-
-          let self = this;
-          let wrapper = {
-            multiple: false,
-            choices: choices,
-            changeCallback: function() {
-              let evt = document.createEvent("XULCommandEvent");
-              evt.initCommandEvent("command", true, true, window, 0, false, false, false, false, null);
-              self.dispatchEvent(evt);
-            },
-            selectCallback: function(aIndex) { self.selectedIndex = aIndex; }
-          };
-
-          SelectHelperUI.show(wrapper);
+          MenuListHelperUI.show(this);
         ]]>
       </handler>
     </handlers>
   </binding>
 
   <binding id="chrome-select-option">
     <content orient="horizontal" flex="1">
       <xul:image class="chrome-select-option-image" anonid="check"/>
--- a/mobile/chrome/content/bindings/browser.js
+++ b/mobile/chrome/content/bindings/browser.js
@@ -247,16 +247,17 @@ WebNavigation.init();
 
 
 let DOMEvents =  {
   init: function() {
     addEventListener("DOMContentLoaded", this, false);
     addEventListener("DOMTitleChanged", this, false);
     addEventListener("DOMLinkAdded", this, false);
     addEventListener("DOMWillOpenModalDialog", this, false);
+    addEventListener("DOMModalDialogClosed", this, true);
     addEventListener("DOMWindowClose", this, false);
     addEventListener("DOMPopupBlocked", this, false);
     addEventListener("pageshow", this, false);
     addEventListener("pagehide", this, false);
   },
 
   handleEvent: function(aEvent) {
     let document = content.document;
@@ -316,16 +317,17 @@ let DOMEvents =  {
           rel: target.rel,
           type: target.type
         };
 
         sendAsyncMessage("DOMLinkAdded", json);
         break;
 
       case "DOMWillOpenModalDialog":
+      case "DOMModalDialogClosed":
       case "DOMWindowClose":
         let retvals = sendSyncMessage(aEvent.type, { });
         for (rv in retvals) {
           if (rv.preventDefault) {
             aEvent.preventDefault();
             break;
           }
         }
@@ -384,34 +386,43 @@ PromptRemoter.prototype = {
     }
 
     function bringTabToFront() {
       let event = window.document.createEvent("Events");
       event.initEvent("DOMWillOpenModalDialog", true, false);
       window.dispatchEvent(event);
     }
 
+    function informClosedFrontTab() {
+      let event = window.document.createEvent("Events");
+      event.initEvent("DOMModalDialogClosed", true, false);
+      window.dispatchEvent(event);
+    }
+
     window.wrappedJSObject.alert = function(aMessage) {
       bringTabToFront();
-      sendAsyncMessage("Prompt:Alert", {
+      sendSyncMessage("Prompt:Alert", {
         message: aMessage
       });
+      informClosedFrontTab();
     }
 
     window.wrappedJSObject.confirm = function(aMessage) {
       bringTabToFront();
       return sendSyncMessage("Prompt:Confirm", {
         message: aMessage
       });
+      informClosedFrontTab();
     }
 
     window.wrappedJSObject.prompt = function(aText, aValue) {
       bringTabToFront();
       return sendSyncMessage("Prompt:Prompt", {
         text: aText,
         value: aValue
       });
+      informClosedFrontTab();
     }
   },
 };
 
 new PromptRemoter();
 
--- a/mobile/chrome/content/browser-ui.js
+++ b/mobile/chrome/content/browser-ui.js
@@ -1571,16 +1571,20 @@ var FormHelperUI = {
     messageManager.addMessageListener("FormAssist:Show", this);
     messageManager.addMessageListener("FormAssist:Hide", this);
     messageManager.addMessageListener("FormAssist:Update", this);
     messageManager.addMessageListener("FormAssist:AutoComplete", this);
 
     // Listen for events where form assistant should be closed
     document.getElementById("tabs").addEventListener("TabSelect", this, true);
     document.getElementById("browsers").addEventListener("URLChanged", this, true);
+
+    // Listen for modal dialog to show/hide the UI
+    messageManager.addMessageListener("DOMWillOpenModalDialog", this);
+    messageManager.addMessageListener("DOMModalDialogClosed", this);
   },
 
   show: function formHelperShow(aElement, aHasPrevious, aHasNext) {
     this._open = true;
 
     // Update the next/previous commands
     this._cmdPrevious.setAttribute("disabled", !aHasPrevious);
     this._cmdNext.setAttribute("disabled", !aHasNext);
@@ -1616,36 +1620,46 @@ var FormHelperUI = {
   receiveMessage: function formHelperReceiveMessage(aMessage) {
     let json = aMessage.json;
     switch (aMessage.name) {
       case "FormAssist:Show":
         // if the user has manually disabled the Form Assistant UI we still
         // want to show a UI for <select /> element but not managed by
         // FormHelperUI
         let enabled = Services.prefs.getBoolPref("formhelper.enabled");
-        if (enabled) {
-          this.show(json.current, json.hasPrevious, json.hasNext);
-        }
-        else {
-          SelectHelperUI.show(json.current.list);
-        }
+        enabled ? this.show(json.current, json.hasPrevious, json.hasNext)
+                : SelectHelperUI.show(json.current.choices);
         break;
 
       case "FormAssist:Hide":
         this.hide();
         break;
 
       case "FormAssist:AutoComplete":
         this._updateAutocompleteFor(json.current);
         this._container.contentHasChanged();
         break;
 
       case "FormAssist:Update":
         this._zoom(null, Rect.fromRect(json.caretRect));
         break;
+
+      case "DOMWillOpenModalDialog":
+        if (this._open && aMessage.target == Browser.selectedBrowser) {
+          this._container.style.display = "none";
+          this._container._spacer.hidden = true;
+        }
+        break;
+
+      case "DOMModalDialogClosed":
+        if (this._open && aMessage.target == Browser.selectedBrowser) {
+          this._container.style.display = "-moz-box";
+          this._container._spacer.hidden = false;
+        }
+        break;
     }
   },
 
   goToPrevious: function formHelperGoToPrevious() {
     Browser.selectedBrowser.messageManager.sendAsyncMessage("FormAssist:Previous", { });
   },
 
   goToNext: function formHelperGoToNext() {
@@ -2017,18 +2031,17 @@ var SelectHelperUI = {
           break;
         }
       }
     }
 
     if (isIdentical)
       return;
 
-    this._list.changeCallback ? this._list.changeCallback()
-                              : Browser.selectedBrowser.messageManager.sendAsyncMessage("FormAssist:ChoiceChange", { });
+    Browser.selectedBrowser.messageManager.sendAsyncMessage("FormAssist:ChoiceChange", { });
   },
 
   handleEvent: function(aEvent) {
     switch (aEvent.type) {
       case "click":
         let item = aEvent.target;
         if (item && item.hasOwnProperty("optionIndex")) {
           if (this._list.multiple) {
@@ -2036,38 +2049,89 @@ var SelectHelperUI = {
             item.selected = !item.selected;
           }
           else {
             this.unselectAll();
 
             // Select the new one and update the control
             item.selected = true;
           }
-
           this.onSelect(item.optionIndex, item.selected, !this._list.multiple);
         }
         break;
     }
   },
 
   onSelect: function(aIndex, aSelected, aClearAll) {
-    if (this._list.selectCallback) {
-      this._list.selectCallback(aIndex);
-      return;
-    }
-
     let json = {
       index: aIndex,
       selected: aSelected,
       clearAll: aClearAll
     };
     Browser.selectedBrowser.messageManager.sendAsyncMessage("FormAssist:ChoiceSelect", json);
   }
 };
 
+var MenuListHelperUI = {
+  get _container() {
+    delete this._container;
+    return this._container = document.getElementById("menulist-container");
+  },
+
+  get _popup() {
+    delete this._popup;
+    return this._popup = document.getElementById("menulist-popup");
+  },
+
+  _currentList: null,
+  show: function mn_show(aMenulist) {
+    this._currentList = aMenulist;
+
+    let container = this._container;
+    let listbox = this._popup.firstChild;
+    while (listbox.firstChild)
+      listbox.removeChild(listbox.firstChild);
+
+    let children = this._currentList.menupopup.children;
+    for (let i = 0; i < children.length; i++) {
+      let child = children[i];
+      let item = document.createElement("richlistitem");
+      if (child.selected)
+        item.setAttribute("selected", child.selected);
+      item.setAttribute("class", "menulist-command");
+
+      let label = document.createElement("label");
+      label.setAttribute("value", child.label);
+      item.appendChild(label);
+
+      listbox.appendChild(item);
+    }
+
+    container.hidden = false;
+    BrowserUI.pushPopup(this, [this._popup]);
+  },
+
+  hide: function mn_hide() {
+    this._currentList = null;
+    this._container.hidden = true;
+    BrowserUI.popPopup();
+  },
+
+  selectByIndex: function mn_selectByIndex(aIndex) {
+    this._currentList.selectedIndex = aIndex;
+
+    // Dispatch a xul command event to the attached menulist
+    let evt = document.createEvent("XULCommandEvent");
+    evt.initCommandEvent("command", true, true, window, 0, false, false, false, false, null);
+    this._currentList.dispatchEvent(evt);
+
+    this.hide();
+  }
+}
+
 var ContextHelper = {
   popupState: null,
 
   get _panel() {
     delete this._panel;
     return this._panel = document.getElementById("context-container");
   },
 
--- a/mobile/chrome/content/browser.js
+++ b/mobile/chrome/content/browser.js
@@ -2166,31 +2166,32 @@ function importDialog(aParent, aSrc, aAr
     if (!trimmed.length)
       currentNode.parentNode.removeChild(currentNode);
   }
 
   let doc = xhr.responseXML.documentElement;
 
   var dialog  = null;
 
-  // we need to insert before select-container if we want it to show correctly
-  let selectContainer = document.getElementById("select-container");
-  let parentNode = selectContainer.parentNode;
+  // we need to insert before menulist-container if we want it to show correctly
+  // for prompt.select for instance
+  let menulistContainer = document.getElementById("menulist-container");
+  let parentNode = menulistContainer.parentNode;
 
   // emit DOMWillOpenModalDialog event
   let event = document.createEvent("Events");
   event.initEvent("DOMWillOpenModalDialog", true, false);
   let dispatcher = aParent || getBrowser();
   dispatcher.dispatchEvent(event);
 
   // create a full-screen semi-opaque box as a background
   let back = document.createElement("box");
   back.setAttribute("class", "modal-block");
   dialog = back.appendChild(document.importNode(doc, true));
-  parentNode.insertBefore(back, selectContainer);
+  parentNode.insertBefore(back, menulistContainer);
 
   dialog.arguments = aArguments;
   dialog.parent = aParent;
   return dialog;
 }
 
 function showDownloadManager(aWindowContext, aID, aReason) {
   BrowserUI.showPanel("downloads-container");
--- a/mobile/chrome/content/browser.xul
+++ b/mobile/chrome/content/browser.xul
@@ -534,16 +534,22 @@
         <scrollbox id="select-list" flex="1" orient="vertical"/>
         <hbox id="select-buttons" pack="center">
           <button id="select-buttons-done" class="button-dark" label="&selectHelper.done;" oncommand="SelectHelperUI.hide();"/>
         </hbox>
       </vbox>
       <spacer flex="1000"/>
     </vbox>
 
+    <hbox id="menulist-container" class="window-width window-height context-block" top="0" left="0" hidden="true" flex="1">
+      <vbox id="menulist-popup" class="dialog-dark"> 
+        <richlistbox id="menulist-commands" onselect="MenuListHelperUI.selectByIndex(this.selectedIndex)"/>
+      </vbox>
+    </hbox>
+
     <hbox id="context-container" class="window-width window-height context-block" top="0" left="0" hidden="true">
       <vbox id="context-popup" class="dialog-dark">
         <hbox id="context-header">
           <label id="context-hint" crop="center" flex="1"/>
         </hbox>
         <richlistbox id="context-commands" onclick="ContextHelper.hide();">
           <richlistitem class="context-command" id="context-openinnewtab" type="link-saveable" onclick="ContextCommands.openInNewTab();">
             <label value="&contextOpenInNewTab.label;"/>
--- a/mobile/chrome/content/forms.js
+++ b/mobile/chrome/content/forms.js
@@ -63,16 +63,18 @@ function FormAssistant() {
   addMessageListener("FormAssist:ChoiceSelect", this);
   addMessageListener("FormAssist:ChoiceChange", this);
   addMessageListener("FormAssist:AutoComplete", this);
 
   addEventListener("keyup", this, false);
 };
 
 FormAssistant.prototype = {
+  _selectWrapper: null,
+
   get currentElement() {
     return this._elements[this._currentIndex];
   },
 
   get currentIndex() {
     return this._currentIndex;
   },
 
@@ -128,39 +130,40 @@ FormAssistant.prototype = {
       this._elements = [aElement];
       this.currentIndex = 0;
     }
 
     return this._open = true;
   },
 
   receiveMessage: function receiveMessage(aMessage) {
-    if (!this._enabled || !this.currentElement)
+    let currentElement = this.currentElement;
+    if ((!this._enabled && !getWrapperForElement(currentElement)) || !currentElement)
       return;
 
-    let currentElement = this.currentElement;
     let json = aMessage.json;
     switch (aMessage.name) {
       case "FormAssist:Previous":
         this.currentIndex--;
         break;
 
       case "FormAssist:Next":
         this.currentIndex++;
         break;
 
       case "FormAssist:ChoiceSelect": {
-        let wrapper = getWrapperForElement(currentElement);
-        wrapper.select(json.index, json.selected, json.clearAll);
+        this._selectWrapper = getWrapperForElement(currentElement);
+        this._selectWrapper.select(json.index, json.selected, json.clearAll);
         break;
       }
 
       case "FormAssist:ChoiceChange": {
-        let wrapper = getWrapperForElement(currentElement);
-        wrapper.fireOnChange();
+        // ChoiceChange happened once we have move to an other element so we 
+        // should remenber the used wrapper
+        this._selectWrapper.fireOnChange();
         break;
       }
 
       case "FormAssist:AutoComplete":
         currentElement.value = json.value;
         break;
     }
   },
--- a/mobile/themes/core/browser.css
+++ b/mobile/themes/core/browser.css
@@ -1502,16 +1502,57 @@ pageaction:hover:active > vbox > .pageac
 .chrome-select-option-image {
   min-width: 30px;
 }
 
 .chrome-select-option[selected="true"] {
   list-style-image: url("chrome://browser/skin/images/check-30.png");
 }
 
+/* menulist popup ---------------------------------------------------------- */
+#menulist-popup {
+  border: none;
+  padding: 1px;
+  -moz-border-radius: 8px;
+}
+
+#menulist-commands {
+  border: 1px solid rgb(207,207,207);
+  -moz-border-radius: 8px;
+}
+
+.menulist-command {
+  -moz-box-align: center;
+  background-color: rgb(245,245,245);
+  min-width: 200px; /* keep the command from being too narrow */
+}
+
+.menulist-command:first-child {
+  background: -moz-linear-gradient(top, rgb(255,255,255), rgb(245,245,245));
+  -moz-border-radius: 8px 8px 0 0;
+}
+
+.menulist-command:last-child {
+  background: -moz-linear-gradient(top, rgb(245,245,245), rgb(215,215,215));
+  -moz-border-radius: 0 0 8px 8px;
+}
+
+.menulist-command:first-child:last-child {
+  -moz-border-radius: 8px;
+}
+
+.menulist-command:hover:active {
+  background: #8db8d8;
+}
+
+.menulist-command[selected="true"] {
+  pointer-events: none;
+  background: #8db8d8;
+}
+
 /* context popup ----------------------------------------------------------- */
 #context-popup {
   /* Remove some dialog-dark styles */
   padding: 8px 0 0 0;
   border: none;
 }
 
 #share-title,
@@ -1539,8 +1580,9 @@ pageaction:hover:active > vbox > .pageac
   background: -moz-linear-gradient(top, rgb(245,245,245), rgb(215,215,215));
   -moz-border-radius: 0 0 8px 8px;
 }
 
 /* Force any command tap to highlight */
 .context-command:hover:active {
   background: #8db8d8;
 }
+