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/. */
6 ChromeUtils.defineESModuleGetters(lazy, {
7 LayoutUtils: "resource://gre/modules/LayoutUtils.sys.mjs",
10 var EXPORTED_SYMBOLS = ["DateTimePickerChild"];
13 * DateTimePickerChild is the communication channel between the input box
14 * (content) for date/time input types and its picker (chrome).
16 class DateTimePickerChild extends JSWindowActorChild {
18 * On init, just listen for the event to open the picker, once the picker is
19 * opened, we'll listen for update and close events.
24 this._inputElement = null;
28 * Cleanup function called when picker is closed.
31 this.removeListeners(this._inputElement);
32 let dateTimeBoxElement = this._inputElement.dateTimeBoxElement;
33 if (!dateTimeBoxElement) {
34 this._inputElement = null;
38 if (this._inputElement.openOrClosedShadowRoot) {
39 // dateTimeBoxElement is within UA Widget Shadow DOM.
40 // An event dispatch to it can't be accessed by document.
41 let win = this._inputElement.ownerGlobal;
42 dateTimeBoxElement.dispatchEvent(
43 new win.CustomEvent("MozSetDateTimePickerState", { detail: false })
47 this._inputElement = null;
51 * Called after picker is opened to start listening for input box update
54 addListeners(aElement) {
55 aElement.ownerGlobal.addEventListener("pagehide", this);
59 * Stop listeneing for events when picker is closed.
61 removeListeners(aElement) {
62 aElement.ownerGlobal.removeEventListener("pagehide", this);
66 * Helper function that returns the CSS direction property of the element.
68 getComputedDirection(aElement) {
69 return aElement.ownerGlobal
70 .getComputedStyle(aElement)
71 .getPropertyValue("direction");
75 * Helper function that returns the rect of the element, which is the position
76 * relative to the left/top of the content area.
78 getBoundingContentRect(aElement) {
79 return lazy.LayoutUtils.getElementBoundingScreenRect(aElement);
83 return Services.prefs.getBoolPref("dom.forms.datetime.timepicker");
89 receiveMessage(aMessage) {
90 switch (aMessage.name) {
91 case "FormDateTime:PickerClosed": {
92 if (!this._inputElement) {
99 case "FormDateTime:PickerValueChanged": {
100 if (!this._inputElement) {
104 let dateTimeBoxElement = this._inputElement.dateTimeBoxElement;
105 if (!dateTimeBoxElement) {
109 let win = this._inputElement.ownerGlobal;
111 // dateTimeBoxElement is within UA Widget Shadow DOM.
112 // An event dispatch to it can't be accessed by document.
113 dateTimeBoxElement.dispatchEvent(
114 new win.CustomEvent("MozPickerValueChanged", {
115 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
134 !aEvent.originalTarget.ownerGlobal.HTMLInputElement.isInstance(
135 aEvent.originalTarget
137 (aEvent.originalTarget.type == "time" && !this.getTimePickerPref())
142 if (this._inputElement) {
143 // This happens when we're trying to open a picker when another picker
144 // is still open. We ignore this request to let the first picker
149 this._inputElement = aEvent.originalTarget;
151 let dateTimeBoxElement = this._inputElement.dateTimeBoxElement;
152 if (!dateTimeBoxElement) {
154 "How do we get this event without a UA Widget or XBL binding?"
158 if (this._inputElement.openOrClosedShadowRoot) {
159 // dateTimeBoxElement is within UA Widget Shadow DOM.
160 // An event dispatch to it can't be accessed by document, because
161 // the event is not composed.
162 let win = this._inputElement.ownerGlobal;
163 dateTimeBoxElement.dispatchEvent(
164 new win.CustomEvent("MozSetDateTimePickerState", { detail: true })
168 this.addListeners(this._inputElement);
170 let value = this._inputElement.getDateTimeInputBoxValue();
171 this.sendAsyncMessage("FormDateTime:OpenPicker", {
172 rect: this.getBoundingContentRect(this._inputElement),
173 dir: this.getComputedDirection(this._inputElement),
174 type: this._inputElement.type,
176 // Pass partial value if it's available, otherwise pass input
178 value: Object.keys(value).length ? value : this._inputElement.value,
179 min: this._inputElement.getMinimum(),
180 max: this._inputElement.getMaximum(),
181 step: this._inputElement.getStep(),
182 stepBase: this._inputElement.getStepBase(),
187 case "MozUpdateDateTimePicker": {
188 let value = this._inputElement.getDateTimeInputBoxValue();
189 value.type = this._inputElement.type;
190 this.sendAsyncMessage("FormDateTime:UpdatePicker", { value });
193 case "MozCloseDateTimePicker": {
194 this.sendAsyncMessage("FormDateTime:ClosePicker", {});
200 this._inputElement &&
201 this._inputElement.ownerDocument == aEvent.target
203 this.sendAsyncMessage("FormDateTime:ClosePicker", {});