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",
11 * DateTimePickerChild is the communication channel between the input box
12 * (content) for date/time input types and its picker (chrome).
14 export class DateTimePickerChild extends JSWindowActorChild {
16 * On init, just listen for the event to open the picker, once the picker is
17 * opened, we'll listen for update and close events.
22 this._inputElement = null;
26 * Cleanup function called when picker is closed.
29 this.removeListeners(this._inputElement);
30 let dateTimeBoxElement = this._inputElement.dateTimeBoxElement;
31 if (!dateTimeBoxElement) {
32 this._inputElement = null;
36 // dateTimeBoxElement is within UA Widget Shadow DOM.
37 // An event dispatch to it can't be accessed by document.
38 let win = this._inputElement.ownerGlobal;
39 dateTimeBoxElement.dispatchEvent(
40 new win.CustomEvent("MozSetDateTimePickerState", { detail: false })
43 this._inputElement = null;
47 * Called after picker is opened to start listening for input box update
50 addListeners(aElement) {
51 aElement.ownerGlobal.addEventListener("pagehide", this);
55 * Stop listeneing for events when picker is closed.
57 removeListeners(aElement) {
58 aElement.ownerGlobal.removeEventListener("pagehide", this);
62 * Helper function that returns the CSS direction property of the element.
64 getComputedDirection(aElement) {
65 return aElement.ownerGlobal
66 .getComputedStyle(aElement)
67 .getPropertyValue("direction");
71 * Helper function that returns the rect of the element, which is the position
72 * relative to the left/top of the content area.
74 getBoundingContentRect(aElement) {
75 return lazy.LayoutUtils.getElementBoundingScreenRect(aElement);
79 return Services.prefs.getBoolPref("dom.forms.datetime.timepicker");
85 receiveMessage(aMessage) {
86 switch (aMessage.name) {
87 case "FormDateTime:PickerClosed": {
88 if (!this._inputElement) {
95 case "FormDateTime:PickerValueChanged": {
96 if (!this._inputElement) {
100 let dateTimeBoxElement = this._inputElement.dateTimeBoxElement;
101 if (!dateTimeBoxElement) {
105 let win = this._inputElement.ownerGlobal;
107 // dateTimeBoxElement is within UA Widget Shadow DOM.
108 // An event dispatch to it can't be accessed by document.
109 dateTimeBoxElement.dispatchEvent(
110 new win.CustomEvent("MozPickerValueChanged", {
111 detail: Cu.cloneInto(aMessage.data, win),
122 * nsIDOMEventListener, for chrome events sent by the input element and other
125 handleEvent(aEvent) {
126 switch (aEvent.type) {
127 case "MozOpenDateTimePicker": {
128 // Time picker is disabled when preffed off
130 !aEvent.originalTarget.ownerGlobal.HTMLInputElement.isInstance(
131 aEvent.originalTarget
133 (aEvent.originalTarget.type == "time" && !this.getTimePickerPref())
138 if (this._inputElement) {
139 // This happens when we're trying to open a picker when another picker
140 // is still open. We ignore this request to let the first picker
145 this._inputElement = aEvent.originalTarget;
147 let dateTimeBoxElement = this._inputElement.dateTimeBoxElement;
148 if (!dateTimeBoxElement) {
149 throw new Error("How do we get this event without a UA Widget?");
152 // dateTimeBoxElement is within UA Widget Shadow DOM.
153 // An event dispatch to it can't be accessed by document, because
154 // the event is not composed.
155 let win = this._inputElement.ownerGlobal;
156 dateTimeBoxElement.dispatchEvent(
157 new win.CustomEvent("MozSetDateTimePickerState", { detail: true })
160 this.addListeners(this._inputElement);
162 let value = this._inputElement.getDateTimeInputBoxValue();
163 this.sendAsyncMessage("FormDateTime:OpenPicker", {
164 rect: this.getBoundingContentRect(this._inputElement),
165 dir: this.getComputedDirection(this._inputElement),
166 type: this._inputElement.type,
168 // Pass partial value if it's available, otherwise pass input
170 value: Object.keys(value).length ? value : this._inputElement.value,
171 min: this._inputElement.getMinimum(),
172 max: this._inputElement.getMaximum(),
173 step: this._inputElement.getStep(),
174 stepBase: this._inputElement.getStepBase(),
179 case "MozUpdateDateTimePicker": {
180 let value = this._inputElement.getDateTimeInputBoxValue();
181 value.type = this._inputElement.type;
182 this.sendAsyncMessage("FormDateTime:UpdatePicker", { value });
185 case "MozCloseDateTimePicker": {
186 this.sendAsyncMessage("FormDateTime:ClosePicker", {});
192 this._inputElement &&
193 this._inputElement.ownerDocument == aEvent.target
195 this.sendAsyncMessage("FormDateTime:ClosePicker", {});