michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: "use strict"; michael@0: michael@0: const Cc = Components.classes; michael@0: const Ci = Components.interfaces; michael@0: const Cu = Components.utils; michael@0: michael@0: Cu.import("resource://gre/modules/XPCOMUtils.jsm"); michael@0: michael@0: XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils", michael@0: "resource://gre/modules/BrowserUtils.jsm"); michael@0: michael@0: this.EXPORTED_SYMBOLS = [ michael@0: "SelectContentHelper" michael@0: ]; michael@0: michael@0: this.SelectContentHelper = function (aElement, aGlobal) { michael@0: this.element = aElement; michael@0: this.global = aGlobal; michael@0: this.init(); michael@0: this.showDropDown(); michael@0: } michael@0: michael@0: this.SelectContentHelper.prototype = { michael@0: init: function() { michael@0: this.global.addMessageListener("Forms:SelectDropDownItem", this); michael@0: this.global.addMessageListener("Forms:DismissedDropDown", this); michael@0: this.global.addEventListener("pagehide", this); michael@0: }, michael@0: michael@0: uninit: function() { michael@0: this.global.removeMessageListener("Forms:SelectDropDownItem", this); michael@0: this.global.removeMessageListener("Forms:DismissedDropDown", this); michael@0: this.global.removeEventListener("pagehide", this); michael@0: this.element = null; michael@0: this.global = null; michael@0: }, michael@0: michael@0: showDropDown: function() { michael@0: let rect = this._getBoundingContentRect(); michael@0: michael@0: this.global.sendAsyncMessage("Forms:ShowDropDown", { michael@0: rect: rect, michael@0: options: this._buildOptionList(), michael@0: selectedIndex: this.element.selectedIndex, michael@0: }); michael@0: }, michael@0: michael@0: _getBoundingContentRect: function() { michael@0: return BrowserUtils.getElementBoundingScreenRect(this.element); michael@0: }, michael@0: michael@0: _buildOptionList: function() { michael@0: return buildOptionListForChildren(this.element); michael@0: }, michael@0: michael@0: receiveMessage: function(message) { michael@0: switch (message.name) { michael@0: case "Forms:SelectDropDownItem": michael@0: if (this.element.selectedIndex != message.data.value) { michael@0: this.element.selectedIndex = message.data.value; michael@0: michael@0: let event = this.element.ownerDocument.createEvent("Events"); michael@0: event.initEvent("change", true, true); michael@0: this.element.dispatchEvent(event); michael@0: } michael@0: michael@0: //intentional fall-through michael@0: case "Forms:DismissedDropDown": michael@0: this.uninit(); michael@0: break; michael@0: } michael@0: }, michael@0: michael@0: handleEvent: function(event) { michael@0: switch (event.type) { michael@0: case "pagehide": michael@0: this.global.sendAsyncMessage("Forms:HideDropDown", {}); michael@0: this.uninit(); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: } michael@0: michael@0: function buildOptionListForChildren(node) { michael@0: let result = []; michael@0: for (let child = node.firstChild; child; child = child.nextSibling) { michael@0: if (child.tagName == 'OPTION' || child.tagName == 'OPTGROUP') { michael@0: let info = { michael@0: tagName: child.tagName, michael@0: textContent: child.tagName == 'OPTGROUP' ? child.getAttribute("label") michael@0: : child.textContent, michael@0: // XXX this uses a highlight color when this is the selected element. michael@0: // We need to suppress such highlighting in the content process to get michael@0: // the option's correct unhighlighted color here. michael@0: // We also need to detect default color vs. custom so that a standard michael@0: // color does not override color: menutext in the parent. michael@0: // backgroundColor: computedStyle.backgroundColor, michael@0: // color: computedStyle.color, michael@0: children: child.tagName == 'OPTGROUP' ? buildOptionListForChildren(child) : [] michael@0: }; michael@0: result.push(info); michael@0: } michael@0: } michael@0: return result; michael@0: }