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 * Chrome side handling of form validation popup.
11 ChromeUtils.defineESModuleGetters(lazy, {
12 BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.sys.mjs",
15 class PopupShownObserver {
18 constructor(browsingContext) {
19 this._weakContext = Cu.getWeakReference(browsingContext);
22 observe(subject, topic, data) {
23 let ctxt = this._weakContext.get();
24 let actor = ctxt.currentWindowGlobal?.getExistingActor("FormValidation");
26 Services.obs.removeObserver(this, "popup-shown");
29 // If any panel besides ourselves shows, hide ourselves again.
30 if (topic == "popup-shown" && subject != actor._panel) {
35 QueryInterface = ChromeUtils.generateQI([
37 Ci.nsISupportsWeakReference,
41 export class FormValidationParent extends JSWindowActorParent {
49 static hasOpenPopups() {
50 for (let win of lazy.BrowserWindowTracker.orderedWindows) {
51 let popups = win.document.querySelectorAll("panel,menupopup");
52 for (let popup of popups) {
53 let { state } = popup;
54 if (state == "open" || state == "showing") {
79 receiveMessage(aMessage) {
80 switch (aMessage.name) {
81 case "FormValidation:ShowPopup":
82 let browser = this.browsingContext.top.embedderElement;
83 let window = browser.ownerGlobal;
84 let data = aMessage.data;
85 let tabBrowser = window.gBrowser;
87 // target is the <browser>, make sure we're receiving a message
88 // from the foreground tab.
89 if (tabBrowser && browser != tabBrowser.selectedBrowser) {
93 if (FormValidationParent.hasOpenPopups()) {
97 this._showPopup(browser, data);
99 case "FormValidation:HidePopup":
105 handleEvent(aEvent) {
106 switch (aEvent.type) {
107 case "FullZoomChange":
108 case "TextZoomChange":
113 this._onPopupHidden(aEvent);
122 _onPopupHidden(aEvent) {
123 aEvent.originalTarget.removeEventListener("popuphidden", this, true);
124 Services.obs.removeObserver(this._obs, "popup-shown");
125 let tabBrowser = aEvent.originalTarget.ownerGlobal.gBrowser;
126 tabBrowser.selectedBrowser.removeEventListener("scroll", this, true);
127 tabBrowser.selectedBrowser.removeEventListener("FullZoomChange", this);
128 tabBrowser.selectedBrowser.removeEventListener("TextZoomChange", this);
135 * Shows the form validation popup at a specified position or updates the
136 * messaging and position if the popup is already displayed.
138 * @aBrowser - Browser element that requests the popup.
139 * @aPanelData - Object that contains popup information
140 * aPanelData stucture detail:
141 * screenRect - the screen rect of the target element.
142 * position - popup positional string constants.
143 * message - the form element validation message text.
145 _showPopup(aBrowser, aPanelData) {
146 let previouslyShown = !!this._panel;
147 this._panel = this._getAndMaybeCreatePanel();
148 this._panel.firstChild.textContent = aPanelData.message;
150 // Display the panel if it isn't already visible.
151 if (previouslyShown) {
154 // Cleanup after the popup is hidden
155 this._panel.addEventListener("popuphidden", this, true);
156 // Hide ourselves if other popups shown
157 this._obs = new PopupShownObserver(this.browsingContext);
158 Services.obs.addObserver(this._obs, "popup-shown", true);
160 // Hide if the user scrolls the page
161 aBrowser.addEventListener("scroll", this, true);
162 aBrowser.addEventListener("FullZoomChange", this);
163 aBrowser.addEventListener("TextZoomChange", this);
165 aBrowser.constrainPopup(this._panel);
168 let rect = aPanelData.screenRect;
169 this._panel.openPopupAtScreenRect(
181 * Hide the popup if currently displayed. Will fire an event to onPopupHiding
185 this._panel?.hidePopup();
188 _getAndMaybeCreatePanel() {
189 // Lazy load the invalid form popup the first time we need to display it.
191 let browser = this.browsingContext.top.embedderElement;
192 let window = browser.ownerGlobal;
193 let template = window.document.getElementById("invalidFormTemplate");
195 template.replaceWith(template.content);
197 this._panel = window.document.getElementById("invalid-form-popup");