After bug 1691346, LayoutUtils is a thin wrapper over a few methods on windowUtils. I think we can get rid of it and just call these methods directly now. Differential Revision: https://phabricator.services.mozilla.com/D288661
185 lines
5.1 KiB
JavaScript
185 lines
5.1 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
/**
|
|
* InputPickerChildCommon is the communication channel between the input box
|
|
* (content) for each input types and its picker (chrome).
|
|
*/
|
|
export class InputPickerChildCommon extends JSWindowActorChild {
|
|
/** @type {HTMLInputElement} */
|
|
#inputElement = null;
|
|
#inputType = "";
|
|
#namespace;
|
|
/** @type {AbortController} */
|
|
#abortController;
|
|
|
|
/**
|
|
* On init, just listen for the event to open the picker, once the picker is
|
|
* opened, we'll listen for update and close events.
|
|
*
|
|
* @param {string} namespace Affects the event names, e.g. Foo makes it
|
|
* accept FooValueChanged event.
|
|
* Align it with ActorManagerParent declaration.
|
|
*/
|
|
constructor(namespace) {
|
|
super();
|
|
this.#namespace = namespace;
|
|
}
|
|
|
|
/**
|
|
* Cleanup function called when picker is closed.
|
|
*/
|
|
close() {
|
|
this.#abortController.abort();
|
|
this.closeImpl(this.#inputElement);
|
|
this.#inputElement = null;
|
|
this.#inputType = "";
|
|
}
|
|
|
|
/**
|
|
* @param {HTMLInputElement} _inputElement
|
|
*/
|
|
closeImpl(_inputElement) {
|
|
throw new Error("Not implemented");
|
|
}
|
|
|
|
/**
|
|
* Called after picker is opened to start listening for input box update
|
|
* events.
|
|
*/
|
|
addListeners(aElement) {
|
|
this.#abortController = new AbortController();
|
|
aElement.documentGlobal.addEventListener("pagehide", this, {
|
|
signal: this.#abortController.signal,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Helper function that returns the CSS direction property of the element.
|
|
*/
|
|
getComputedDirection(aElement) {
|
|
return aElement.documentGlobal
|
|
.getComputedStyle(aElement)
|
|
.getPropertyValue("direction");
|
|
}
|
|
|
|
/**
|
|
* Helper function that returns the rect of the element, which is the position
|
|
* relative to the left/top of the content area.
|
|
*/
|
|
getBoundingContentRect(aElement) {
|
|
let win = aElement.documentGlobal;
|
|
return win.windowUtils.getElementBoundingScreenRect(aElement);
|
|
}
|
|
|
|
/**
|
|
* MessageListener
|
|
*/
|
|
receiveMessage(aMessage) {
|
|
if (!this.#inputElement || this.#inputElement.type !== this.#inputType) {
|
|
// Either we are already closed by content or the input type is changed
|
|
return;
|
|
}
|
|
switch (aMessage.name) {
|
|
case "InputPicker:Closed": {
|
|
this.close();
|
|
break;
|
|
}
|
|
case "InputPicker:ValueChanged": {
|
|
this.pickerValueChangedImpl(aMessage, this.#inputElement);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Element updater function called when the picker value is changed.
|
|
*
|
|
* @param {ReceiveMessageArgument} _aMessage
|
|
* @param {HTMLInputElement} _inputElement
|
|
*/
|
|
pickerValueChangedImpl(_aMessage, _inputElement) {
|
|
throw new Error("Not implemented");
|
|
}
|
|
|
|
/**
|
|
* nsIDOMEventListener, for chrome events sent by the input element and other
|
|
* DOM events.
|
|
*/
|
|
handleEvent(aEvent) {
|
|
switch (aEvent.type) {
|
|
case `MozOpen${this.#namespace}`: {
|
|
if (
|
|
!aEvent.originalTarget.documentGlobal.HTMLInputElement.isInstance(
|
|
aEvent.originalTarget
|
|
)
|
|
) {
|
|
return;
|
|
}
|
|
|
|
if (this.#inputElement) {
|
|
// This happens when we're trying to open a picker when another picker
|
|
// is still open. We ignore this request to let the first picker
|
|
// close gracefully.
|
|
return;
|
|
}
|
|
|
|
/** @type {HTMLInputElement} */
|
|
const inputElement = aEvent.originalTarget;
|
|
const openPickerDetail = this.openPickerImpl(inputElement);
|
|
if (!openPickerDetail) {
|
|
// The impl doesn't want to proceed in this case
|
|
return;
|
|
}
|
|
|
|
this.#inputElement = inputElement;
|
|
this.#inputType = inputElement.type;
|
|
this.addListeners(inputElement);
|
|
|
|
this.sendAsyncMessage(`InputPicker:Open`, {
|
|
rect: this.getBoundingContentRect(inputElement),
|
|
dir: this.getComputedDirection(inputElement),
|
|
type: inputElement.type,
|
|
detail: openPickerDetail,
|
|
});
|
|
break;
|
|
}
|
|
case `MozClose${this.#namespace}`: {
|
|
this.sendAsyncMessage(`InputPicker:Close`, {});
|
|
this.close();
|
|
break;
|
|
}
|
|
case "pagehide": {
|
|
if (this.#inputElement?.ownerDocument == aEvent.target) {
|
|
this.sendAsyncMessage(`InputPicker:Close`, {});
|
|
this.close();
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Picker initialization function called when opening the picker
|
|
*
|
|
* @param {HTMLInputElement} _inputElement
|
|
* @returns An argument object to pass to the picker, or undefined to stop opening one.
|
|
*/
|
|
openPickerImpl(_inputElement) {
|
|
throw new Error("Not implemented");
|
|
}
|
|
|
|
/**
|
|
* Picker updater function when the input value is updated
|
|
*
|
|
* @param {HTMLInputElement} _inputElement
|
|
* @returns An argument object to pass to the picker
|
|
*/
|
|
updatePickerImpl(_inputElement) {
|
|
throw new Error("Not implemented");
|
|
}
|
|
}
|