Bug 1888590 - Mark some subtests on trusted-types-event-handlers.html as failing...
[gecko.git] / toolkit / actors / DateTimePickerChild.sys.mjs
blob9ef55af4355b06a1ff2d7f24dce0dc6a73db1566
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 lazy = {};
6 ChromeUtils.defineESModuleGetters(lazy, {
7   LayoutUtils: "resource://gre/modules/LayoutUtils.sys.mjs",
8 });
10 /**
11  * DateTimePickerChild is the communication channel between the input box
12  * (content) for date/time input types and its picker (chrome).
13  */
14 export class DateTimePickerChild extends JSWindowActorChild {
15   /**
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.
18    */
19   constructor() {
20     super();
22     this._inputElement = null;
23   }
25   /**
26    * Cleanup function called when picker is closed.
27    */
28   close() {
29     this.removeListeners(this._inputElement);
30     let dateTimeBoxElement = this._inputElement.dateTimeBoxElement;
31     if (!dateTimeBoxElement) {
32       this._inputElement = null;
33       return;
34     }
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 })
41     );
43     this._inputElement = null;
44   }
46   /**
47    * Called after picker is opened to start listening for input box update
48    * events.
49    */
50   addListeners(aElement) {
51     aElement.ownerGlobal.addEventListener("pagehide", this);
52   }
54   /**
55    * Stop listeneing for events when picker is closed.
56    */
57   removeListeners(aElement) {
58     aElement.ownerGlobal.removeEventListener("pagehide", this);
59   }
61   /**
62    * Helper function that returns the CSS direction property of the element.
63    */
64   getComputedDirection(aElement) {
65     return aElement.ownerGlobal
66       .getComputedStyle(aElement)
67       .getPropertyValue("direction");
68   }
70   /**
71    * Helper function that returns the rect of the element, which is the position
72    * relative to the left/top of the content area.
73    */
74   getBoundingContentRect(aElement) {
75     return lazy.LayoutUtils.getElementBoundingScreenRect(aElement);
76   }
78   getTimePickerPref() {
79     return Services.prefs.getBoolPref("dom.forms.datetime.timepicker");
80   }
82   /**
83    * nsIMessageListener.
84    */
85   receiveMessage(aMessage) {
86     switch (aMessage.name) {
87       case "FormDateTime:PickerClosed": {
88         if (!this._inputElement) {
89           return;
90         }
92         this.close();
93         break;
94       }
95       case "FormDateTime:PickerValueChanged": {
96         if (!this._inputElement) {
97           return;
98         }
100         let dateTimeBoxElement = this._inputElement.dateTimeBoxElement;
101         if (!dateTimeBoxElement) {
102           return;
103         }
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),
112           })
113         );
114         break;
115       }
116       default:
117         break;
118     }
119   }
121   /**
122    * nsIDOMEventListener, for chrome events sent by the input element and other
123    * DOM events.
124    */
125   handleEvent(aEvent) {
126     switch (aEvent.type) {
127       case "MozOpenDateTimePicker": {
128         // Time picker is disabled when preffed off
129         if (
130           !aEvent.originalTarget.ownerGlobal.HTMLInputElement.isInstance(
131             aEvent.originalTarget
132           ) ||
133           (aEvent.originalTarget.type == "time" && !this.getTimePickerPref())
134         ) {
135           return;
136         }
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
141           // close gracefully.
142           return;
143         }
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?");
150         }
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 })
158         );
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,
167           detail: {
168             // Pass partial value if it's available, otherwise pass input
169             // element's value.
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(),
175           },
176         });
177         break;
178       }
179       case "MozUpdateDateTimePicker": {
180         let value = this._inputElement.getDateTimeInputBoxValue();
181         value.type = this._inputElement.type;
182         this.sendAsyncMessage("FormDateTime:UpdatePicker", { value });
183         break;
184       }
185       case "MozCloseDateTimePicker": {
186         this.sendAsyncMessage("FormDateTime:ClosePicker", {});
187         this.close();
188         break;
189       }
190       case "pagehide": {
191         if (
192           this._inputElement &&
193           this._inputElement.ownerDocument == aEvent.target
194         ) {
195           this.sendAsyncMessage("FormDateTime:ClosePicker", {});
196           this.close();
197         }
198         break;
199       }
200       default:
201         break;
202     }
203   }