diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /widget/tests/file_ime_state_test_helper.js | |
parent | Initial commit. (diff) | |
download | thunderbird-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.js | 197 |
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 = []; + } +} |