|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 "use strict"; |
|
6 |
|
7 const Cc = Components.classes; |
|
8 const Ci = Components.interfaces; |
|
9 const Cu = Components.utils; |
|
10 |
|
11 Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
|
12 |
|
13 XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils", |
|
14 "resource://gre/modules/BrowserUtils.jsm"); |
|
15 |
|
16 this.EXPORTED_SYMBOLS = [ |
|
17 "SelectContentHelper" |
|
18 ]; |
|
19 |
|
20 this.SelectContentHelper = function (aElement, aGlobal) { |
|
21 this.element = aElement; |
|
22 this.global = aGlobal; |
|
23 this.init(); |
|
24 this.showDropDown(); |
|
25 } |
|
26 |
|
27 this.SelectContentHelper.prototype = { |
|
28 init: function() { |
|
29 this.global.addMessageListener("Forms:SelectDropDownItem", this); |
|
30 this.global.addMessageListener("Forms:DismissedDropDown", this); |
|
31 this.global.addEventListener("pagehide", this); |
|
32 }, |
|
33 |
|
34 uninit: function() { |
|
35 this.global.removeMessageListener("Forms:SelectDropDownItem", this); |
|
36 this.global.removeMessageListener("Forms:DismissedDropDown", this); |
|
37 this.global.removeEventListener("pagehide", this); |
|
38 this.element = null; |
|
39 this.global = null; |
|
40 }, |
|
41 |
|
42 showDropDown: function() { |
|
43 let rect = this._getBoundingContentRect(); |
|
44 |
|
45 this.global.sendAsyncMessage("Forms:ShowDropDown", { |
|
46 rect: rect, |
|
47 options: this._buildOptionList(), |
|
48 selectedIndex: this.element.selectedIndex, |
|
49 }); |
|
50 }, |
|
51 |
|
52 _getBoundingContentRect: function() { |
|
53 return BrowserUtils.getElementBoundingScreenRect(this.element); |
|
54 }, |
|
55 |
|
56 _buildOptionList: function() { |
|
57 return buildOptionListForChildren(this.element); |
|
58 }, |
|
59 |
|
60 receiveMessage: function(message) { |
|
61 switch (message.name) { |
|
62 case "Forms:SelectDropDownItem": |
|
63 if (this.element.selectedIndex != message.data.value) { |
|
64 this.element.selectedIndex = message.data.value; |
|
65 |
|
66 let event = this.element.ownerDocument.createEvent("Events"); |
|
67 event.initEvent("change", true, true); |
|
68 this.element.dispatchEvent(event); |
|
69 } |
|
70 |
|
71 //intentional fall-through |
|
72 case "Forms:DismissedDropDown": |
|
73 this.uninit(); |
|
74 break; |
|
75 } |
|
76 }, |
|
77 |
|
78 handleEvent: function(event) { |
|
79 switch (event.type) { |
|
80 case "pagehide": |
|
81 this.global.sendAsyncMessage("Forms:HideDropDown", {}); |
|
82 this.uninit(); |
|
83 break; |
|
84 } |
|
85 } |
|
86 |
|
87 } |
|
88 |
|
89 function buildOptionListForChildren(node) { |
|
90 let result = []; |
|
91 for (let child = node.firstChild; child; child = child.nextSibling) { |
|
92 if (child.tagName == 'OPTION' || child.tagName == 'OPTGROUP') { |
|
93 let info = { |
|
94 tagName: child.tagName, |
|
95 textContent: child.tagName == 'OPTGROUP' ? child.getAttribute("label") |
|
96 : child.textContent, |
|
97 // XXX this uses a highlight color when this is the selected element. |
|
98 // We need to suppress such highlighting in the content process to get |
|
99 // the option's correct unhighlighted color here. |
|
100 // We also need to detect default color vs. custom so that a standard |
|
101 // color does not override color: menutext in the parent. |
|
102 // backgroundColor: computedStyle.backgroundColor, |
|
103 // color: computedStyle.color, |
|
104 children: child.tagName == 'OPTGROUP' ? buildOptionListForChildren(child) : [] |
|
105 }; |
|
106 result.push(info); |
|
107 } |
|
108 } |
|
109 return result; |
|
110 } |