Merge mozilla-central to autoland. CLOSED TREE
[gecko.git] / toolkit / actors / DateTimePickerParent.sys.mjs
blobba78a39ffbdbd5b9ecccc7ea6a5a379d84a665cb
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 DEBUG = false;
6 function debug(aStr) {
7   if (DEBUG) {
8     dump("-*- DateTimePickerParent: " + aStr + "\n");
9   }
12 const lazy = {};
13 ChromeUtils.defineESModuleGetters(lazy, {
14   DateTimePickerPanel: "resource://gre/modules/DateTimePickerPanel.sys.mjs",
15 });
18  * DateTimePickerParent receives message from content side (input box) and
19  * is reposible for opening, closing and updating the picker. Similarly,
20  * DateTimePickerParent listens for picker's events and notifies the content
21  * side (input box) about them.
22  */
23 export class DateTimePickerParent extends JSWindowActorParent {
24   receiveMessage(aMessage) {
25     debug("receiveMessage: " + aMessage.name);
26     switch (aMessage.name) {
27       case "FormDateTime:OpenPicker": {
28         this.showPicker(aMessage.data);
29         break;
30       }
31       case "FormDateTime:ClosePicker": {
32         if (!this._picker) {
33           return;
34         }
35         this.close();
36         break;
37       }
38       case "FormDateTime:UpdatePicker": {
39         if (!this._picker) {
40           return;
41         }
42         this._picker.setPopupValue(aMessage.data);
43         break;
44       }
45       default:
46         break;
47     }
48   }
50   handleEvent(aEvent) {
51     debug("handleEvent: " + aEvent.type);
52     switch (aEvent.type) {
53       case "DateTimePickerValueCleared": {
54         this.sendAsyncMessage("FormDateTime:PickerValueChanged", null);
55         break;
56       }
57       case "DateTimePickerValueChanged": {
58         this.sendAsyncMessage("FormDateTime:PickerValueChanged", aEvent.detail);
59         break;
60       }
61       case "popuphidden": {
62         this.sendAsyncMessage("FormDateTime:PickerClosed", {});
63         this.close();
64         break;
65       }
66       default:
67         break;
68     }
69   }
71   // Get picker from browser and show it anchored to the input box.
72   showPicker(aData) {
73     let rect = aData.rect;
74     let type = aData.type;
75     let detail = aData.detail;
77     debug("Opening picker with details: " + JSON.stringify(detail));
78     let topBC = this.browsingContext.top;
79     let window = topBC.topChromeWindow;
80     if (Services.focus.activeWindow != window) {
81       debug("Not in the active window");
82       return;
83     }
85     {
86       let browser = topBC.embedderElement;
87       if (
88         browser &&
89         browser.ownerGlobal.gBrowser &&
90         browser.ownerGlobal.gBrowser.selectedBrowser != browser
91       ) {
92         debug("In background tab");
93         return;
94       }
95     }
97     let doc = window.document;
98     let panel = doc.getElementById("DateTimePickerPanel");
99     if (!panel) {
100       panel = doc.createXULElement("panel");
101       panel.id = "DateTimePickerPanel";
102       panel.setAttribute("type", "arrow");
103       panel.setAttribute("orient", "vertical");
104       panel.setAttribute("ignorekeys", "true");
105       panel.setAttribute("noautofocus", "true");
106       // This ensures that clicks on the anchored input box are never consumed.
107       panel.setAttribute("consumeoutsideclicks", "never");
108       panel.setAttribute("level", "parent");
109       panel.setAttribute("tabspecific", "true");
110       let container =
111         doc.getElementById("mainPopupSet") ||
112         doc.querySelector("popupset") ||
113         doc.documentElement.appendChild(doc.createXULElement("popupset"));
114       container.appendChild(panel);
115     }
116     this._oldFocus = doc.activeElement;
117     this._picker = new lazy.DateTimePickerPanel(panel);
118     this._picker.openPicker(type, rect, detail);
119     this.addPickerListeners();
120   }
122   // Close the picker and do some cleanup.
123   close() {
124     this._picker.closePicker();
125     // Restore focus to where it was before the picker opened.
126     this._oldFocus?.focus();
127     this._oldFocus = null;
128     this.removePickerListeners();
129     this._picker = null;
130   }
132   // Listen to picker's event.
133   addPickerListeners() {
134     if (!this._picker) {
135       return;
136     }
137     this._picker.element.addEventListener("popuphidden", this);
138     this._picker.element.addEventListener("DateTimePickerValueChanged", this);
139     this._picker.element.addEventListener("DateTimePickerValueCleared", this);
140   }
142   // Stop listening to picker's event.
143   removePickerListeners() {
144     if (!this._picker) {
145       return;
146     }
147     this._picker.element.removeEventListener("popuphidden", this);
148     this._picker.element.removeEventListener(
149       "DateTimePickerValueChanged",
150       this
151     );
152     this._picker.element.removeEventListener(
153       "DateTimePickerValueCleared",
154       this
155     );
156   }