forked from mirrors/gecko-dev
The remaining tests are not required to run in remote content except some of the iframe tests. However, iframe tests are not so important because it's managed in remote content from point of view of parent process. Therefore, I don't port it in this bug. Differential Revision: https://phabricator.services.mozilla.com/D171204
197 lines
4.5 KiB
JavaScript
197 lines
4.5 KiB
JavaScript
"use strict";
|
|
|
|
function IsIMEOpenStateSupported() {
|
|
// We support to control IME open state on Windows and Mac actually. However,
|
|
// we cannot test it on Mac if the current keyboard layout is not CJK. And also
|
|
// we cannot test it on Win32 if the system didn't be installed IME. So,
|
|
// currently we should not run the open state testing.
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @param {Node} aNode
|
|
*/
|
|
function nodeIsInShadowDOM(aNode) {
|
|
for (let node = aNode; node; node = node.parentNode) {
|
|
if (node instanceof ShadowRoot) {
|
|
return true;
|
|
}
|
|
if (node == node.parentNode) {
|
|
break;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @param {Node} aNode
|
|
*/
|
|
function nodeIsInDesignMode(aNode) {
|
|
return (
|
|
aNode.isConnected &&
|
|
!nodeIsInShadowDOM(aNode) &&
|
|
aNode.ownerDocument.designMode == "on"
|
|
);
|
|
}
|
|
|
|
/**
|
|
* param {Node} aNode
|
|
*/
|
|
function getEditingHost(aNode) {
|
|
if (nodeIsInDesignMode(aNode)) {
|
|
return aNode.ownerDocument.documentElement;
|
|
}
|
|
for (
|
|
let element =
|
|
aNode.nodeType == Node.ELEMENT_NODE ? aNode : aNode.parentElement;
|
|
element;
|
|
element = element.parentElement
|
|
) {
|
|
const contenteditable = element.getAttribute("contenteditable");
|
|
if (contenteditable === "true" || contenteditable === "") {
|
|
return element;
|
|
}
|
|
if (contenteditable === "false") {
|
|
return null;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* @param {Node} aNode
|
|
*/
|
|
function nodeIsEditable(aNode) {
|
|
if (nodeIsInDesignMode(aNode)) {
|
|
return true;
|
|
}
|
|
if (!aNode.isConnected) {
|
|
return false;
|
|
}
|
|
return getEditingHost(aNode) != null;
|
|
}
|
|
|
|
/**
|
|
* @param {Element} aElement
|
|
*/
|
|
function elementIsEditingHost(aElement) {
|
|
return (
|
|
nodeIsEditable(aElement) &&
|
|
(!aElement.parentElement || !getEditingHost(aElement) == aElement)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @returns {Element} Retrieve focused element. If focused element is a element
|
|
* in UA widget, this returns its host element. E.g., when
|
|
* a button in the controls of <audio> or <video> has focus,
|
|
* this returns the <video> or <audio>.
|
|
*/
|
|
function getFocusedElementOrUAWidgetHost() {
|
|
const focusedElement = SpecialPowers.focusManager.focusedElement;
|
|
if (SpecialPowers.wrap(focusedElement)?.containingShadowRoot?.isUAWidget()) {
|
|
return focusedElement.containingShadowRoot.host;
|
|
}
|
|
return focusedElement;
|
|
}
|
|
|
|
class TIPWrapper {
|
|
#mTIP = null;
|
|
#mFocusBlurNotifications = [];
|
|
#mFocusBlurListener;
|
|
#mWindow;
|
|
|
|
constructor(aWindow) {
|
|
this.#mWindow = aWindow;
|
|
this.#mTIP = Cc["@mozilla.org/text-input-processor;1"].createInstance(
|
|
Ci.nsITextInputProcessor
|
|
);
|
|
if (!this.beginInputTransactionForTests()) {
|
|
this.#mTIP = null;
|
|
}
|
|
}
|
|
|
|
beginInputTransactionForTests() {
|
|
return this.#mTIP.beginInputTransactionForTests(
|
|
this.#mWindow,
|
|
this.#observer.bind(this)
|
|
);
|
|
}
|
|
|
|
typeA() {
|
|
const AKey = new this.#mWindow.KeyboardEvent("", {
|
|
key: "a",
|
|
code: "KeyA",
|
|
keyCode: this.#mWindow.KeyboardEvent.DOM_VK_A,
|
|
});
|
|
this.#mTIP.keydown(AKey);
|
|
this.#mTIP.keyup(AKey);
|
|
}
|
|
|
|
isAvailable() {
|
|
return this.#mTIP != null;
|
|
}
|
|
|
|
#observer(aTIP, aNotification) {
|
|
if (aTIP != this.#mTIP) {
|
|
return false;
|
|
}
|
|
switch (aNotification.type) {
|
|
case "request-to-commit":
|
|
this.#mTIP.commitComposition();
|
|
break;
|
|
case "request-to-cancel":
|
|
this.#mTIP.cancelComposition();
|
|
break;
|
|
case "notify-focus":
|
|
case "notify-blur":
|
|
this.#mFocusBlurNotifications.push(aNotification.type);
|
|
if (this.#mFocusBlurListener) {
|
|
this.#mFocusBlurListener(aNotification.type);
|
|
}
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
get TIP() {
|
|
return this.#mTIP;
|
|
}
|
|
|
|
/**
|
|
* @param {Function} aListener
|
|
*/
|
|
set onIMEFocusBlur(aListener) {
|
|
this.#mFocusBlurListener = aListener;
|
|
}
|
|
|
|
get focusBlurNotifications() {
|
|
return this.#mFocusBlurNotifications.concat();
|
|
}
|
|
|
|
get numberOfFocusNotifications() {
|
|
return this.#mFocusBlurNotifications.filter(t => t == "notify-focus")
|
|
.length;
|
|
}
|
|
get numberOfBlurNotifications() {
|
|
return this.#mFocusBlurNotifications.filter(t => t == "notify-blur").length;
|
|
}
|
|
|
|
get IMEHasFocus() {
|
|
return (
|
|
!!this.#mFocusBlurNotifications.length &&
|
|
this.#mFocusBlurNotifications[this.#mFocusBlurNotifications.length - 1] ==
|
|
"notify-focus"
|
|
);
|
|
}
|
|
|
|
clearFocusBlurNotifications() {
|
|
this.#mFocusBlurNotifications = [];
|
|
}
|
|
|
|
destroy() {
|
|
this.#mTIP = null;
|
|
this.#mFocusBlurListener = null;
|
|
this.#mFocusBlurNotifications = [];
|
|
}
|
|
}
|