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/. */
5 const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
6 ChromeUtils.defineModuleGetter(
9 "resource://gre/modules/LayoutUtils.jsm"
12 var EXPORTED_SYMBOLS = ["DateTimePickerChild"];
15 * DateTimePickerChild is the communication channel between the input box
16 * (content) for date/time input types and its picker (chrome).
18 class DateTimePickerChild extends JSWindowActorChild {
20 * On init, just listen for the event to open the picker, once the picker is
21 * opened, we'll listen for update and close events.
26 this._inputElement = null;
30 * Cleanup function called when picker is closed.
33 this.removeListeners(this._inputElement);
34 let dateTimeBoxElement = this._inputElement.dateTimeBoxElement;
35 if (!dateTimeBoxElement) {
36 this._inputElement = null;
40 if (this._inputElement.openOrClosedShadowRoot) {
41 // dateTimeBoxElement is within UA Widget Shadow DOM.
42 // An event dispatch to it can't be accessed by document.
43 let win = this._inputElement.ownerGlobal;
44 dateTimeBoxElement.dispatchEvent(
45 new win.CustomEvent("MozSetDateTimePickerState", { detail: false })
49 this._inputElement = null;
53 * Called after picker is opened to start listening for input box update
56 addListeners(aElement) {
57 aElement.ownerGlobal.addEventListener("pagehide", this);
61 * Stop listeneing for events when picker is closed.
63 removeListeners(aElement) {
64 aElement.ownerGlobal.removeEventListener("pagehide", this);
68 * Helper function that returns the CSS direction property of the element.
70 getComputedDirection(aElement) {
71 return aElement.ownerGlobal
72 .getComputedStyle(aElement)
73 .getPropertyValue("direction");
77 * Helper function that returns the rect of the element, which is the position
78 * relative to the left/top of the content area.
80 getBoundingContentRect(aElement) {
81 return LayoutUtils.getElementBoundingScreenRect(aElement);
85 return Services.prefs.getBoolPref("dom.forms.datetime.timepicker");
91 receiveMessage(aMessage) {
92 switch (aMessage.name) {
93 case "FormDateTime:PickerClosed": {
97 case "FormDateTime:PickerValueChanged": {
98 if (!this._inputElement) {
102 let dateTimeBoxElement = this._inputElement.dateTimeBoxElement;
103 if (!dateTimeBoxElement) {
107 let win = this._inputElement.ownerGlobal;
109 if (this._inputElement.openOrClosedShadowRoot) {
110 // dateTimeBoxElement is within UA Widget Shadow DOM.
111 // An event dispatch to it can't be accessed by document.
112 dateTimeBoxElement.dispatchEvent(
113 new win.CustomEvent("MozPickerValueChanged", {
114 detail: Cu.cloneInto(aMessage.data, win),
126 * nsIDOMEventListener, for chrome events sent by the input element and other
129 handleEvent(aEvent) {
130 switch (aEvent.type) {
131 case "MozOpenDateTimePicker": {
132 // Time picker is disabled when preffed off
135 aEvent.originalTarget instanceof
136 aEvent.originalTarget.ownerGlobal.HTMLInputElement
138 (aEvent.originalTarget.type == "time" && !this.getTimePickerPref())
143 if (this._inputElement) {
144 // This happens when we're trying to open a picker when another picker
145 // is still open. We ignore this request to let the first picker
150 this._inputElement = aEvent.originalTarget;
152 let dateTimeBoxElement = this._inputElement.dateTimeBoxElement;
153 if (!dateTimeBoxElement) {
155 "How do we get this event without a UA Widget or XBL binding?"
159 if (this._inputElement.openOrClosedShadowRoot) {
160 // dateTimeBoxElement is within UA Widget Shadow DOM.
161 // An event dispatch to it can't be accessed by document, because
162 // the event is not composed.
163 let win = this._inputElement.ownerGlobal;
164 dateTimeBoxElement.dispatchEvent(
165 new win.CustomEvent("MozSetDateTimePickerState", { detail: true })
169 this.addListeners(this._inputElement);
171 let value = this._inputElement.getDateTimeInputBoxValue();
172 this.sendAsyncMessage("FormDateTime:OpenPicker", {
173 rect: this.getBoundingContentRect(this._inputElement),
174 dir: this.getComputedDirection(this._inputElement),
175 type: this._inputElement.type,
177 // Pass partial value if it's available, otherwise pass input
179 value: Object.keys(value).length ? value : this._inputElement.value,
180 min: this._inputElement.getMinimum(),
181 max: this._inputElement.getMaximum(),
182 step: this._inputElement.getStep(),
183 stepBase: this._inputElement.getStepBase(),
188 case "MozUpdateDateTimePicker": {
189 let value = this._inputElement.getDateTimeInputBoxValue();
190 value.type = this._inputElement.type;
191 this.sendAsyncMessage("FormDateTime:UpdatePicker", { value });
194 case "MozCloseDateTimePicker": {
195 this.sendAsyncMessage("FormDateTime:ClosePicker", {});
201 this._inputElement &&
202 this._inputElement.ownerDocument == aEvent.target
204 this.sendAsyncMessage("FormDateTime:ClosePicker", {});