summaryrefslogtreecommitdiffstats
path: root/widget/tests/file_ime_state_test_helper.js
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
commit6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /widget/tests/file_ime_state_test_helper.js
parentInitial commit. (diff)
downloadthunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz
thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'widget/tests/file_ime_state_test_helper.js')
-rw-r--r--widget/tests/file_ime_state_test_helper.js197
1 files changed, 197 insertions, 0 deletions
diff --git a/widget/tests/file_ime_state_test_helper.js b/widget/tests/file_ime_state_test_helper.js
new file mode 100644
index 0000000000..0cee5c036f
--- /dev/null
+++ b/widget/tests/file_ime_state_test_helper.js
@@ -0,0 +1,197 @@
+"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 = [];
+ }
+}