diff -r 000000000000 -r 6474c204b198 toolkit/modules/SelectContentHelper.jsm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/toolkit/modules/SelectContentHelper.jsm Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,110 @@ +/* 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"; + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils", + "resource://gre/modules/BrowserUtils.jsm"); + +this.EXPORTED_SYMBOLS = [ + "SelectContentHelper" +]; + +this.SelectContentHelper = function (aElement, aGlobal) { + this.element = aElement; + this.global = aGlobal; + this.init(); + this.showDropDown(); +} + +this.SelectContentHelper.prototype = { + init: function() { + this.global.addMessageListener("Forms:SelectDropDownItem", this); + this.global.addMessageListener("Forms:DismissedDropDown", this); + this.global.addEventListener("pagehide", this); + }, + + uninit: function() { + this.global.removeMessageListener("Forms:SelectDropDownItem", this); + this.global.removeMessageListener("Forms:DismissedDropDown", this); + this.global.removeEventListener("pagehide", this); + this.element = null; + this.global = null; + }, + + showDropDown: function() { + let rect = this._getBoundingContentRect(); + + this.global.sendAsyncMessage("Forms:ShowDropDown", { + rect: rect, + options: this._buildOptionList(), + selectedIndex: this.element.selectedIndex, + }); + }, + + _getBoundingContentRect: function() { + return BrowserUtils.getElementBoundingScreenRect(this.element); + }, + + _buildOptionList: function() { + return buildOptionListForChildren(this.element); + }, + + receiveMessage: function(message) { + switch (message.name) { + case "Forms:SelectDropDownItem": + if (this.element.selectedIndex != message.data.value) { + this.element.selectedIndex = message.data.value; + + let event = this.element.ownerDocument.createEvent("Events"); + event.initEvent("change", true, true); + this.element.dispatchEvent(event); + } + + //intentional fall-through + case "Forms:DismissedDropDown": + this.uninit(); + break; + } + }, + + handleEvent: function(event) { + switch (event.type) { + case "pagehide": + this.global.sendAsyncMessage("Forms:HideDropDown", {}); + this.uninit(); + break; + } + } + +} + +function buildOptionListForChildren(node) { + let result = []; + for (let child = node.firstChild; child; child = child.nextSibling) { + if (child.tagName == 'OPTION' || child.tagName == 'OPTGROUP') { + let info = { + tagName: child.tagName, + textContent: child.tagName == 'OPTGROUP' ? child.getAttribute("label") + : child.textContent, + // XXX this uses a highlight color when this is the selected element. + // We need to suppress such highlighting in the content process to get + // the option's correct unhighlighted color here. + // We also need to detect default color vs. custom so that a standard + // color does not override color: menutext in the parent. + // backgroundColor: computedStyle.backgroundColor, + // color: computedStyle.color, + children: child.tagName == 'OPTGROUP' ? buildOptionListForChildren(child) : [] + }; + result.push(info); + } + } + return result; +}