Backed out changeset c8d01bb9a6a2 (bug 1866098) for causing bp-nu build bustages...
[gecko.git] / widget / tests / file_ime_state_test_helper.js
blob0cee5c036f16ef09e272c09fea452cf4eb78f94f
1 "use strict";
3 function IsIMEOpenStateSupported() {
4   // We support to control IME open state on Windows and Mac actually.  However,
5   // we cannot test it on Mac if the current keyboard layout is not CJK. And also
6   // we cannot test it on Win32 if the system didn't be installed IME. So,
7   // currently we should not run the open state testing.
8   return false;
11 /**
12  * @param {Node} aNode
13  */
14 function nodeIsInShadowDOM(aNode) {
15   for (let node = aNode; node; node = node.parentNode) {
16     if (node instanceof ShadowRoot) {
17       return true;
18     }
19     if (node == node.parentNode) {
20       break;
21     }
22   }
23   return false;
26 /**
27  * @param {Node} aNode
28  */
29 function nodeIsInDesignMode(aNode) {
30   return (
31     aNode.isConnected &&
32     !nodeIsInShadowDOM(aNode) &&
33     aNode.ownerDocument.designMode == "on"
34   );
37 /**
38  * param {Node} aNode
39  */
40 function getEditingHost(aNode) {
41   if (nodeIsInDesignMode(aNode)) {
42     return aNode.ownerDocument.documentElement;
43   }
44   for (
45     let element =
46       aNode.nodeType == Node.ELEMENT_NODE ? aNode : aNode.parentElement;
47     element;
48     element = element.parentElement
49   ) {
50     const contenteditable = element.getAttribute("contenteditable");
51     if (contenteditable === "true" || contenteditable === "") {
52       return element;
53     }
54     if (contenteditable === "false") {
55       return null;
56     }
57   }
58   return null;
61 /**
62  * @param {Node} aNode
63  */
64 function nodeIsEditable(aNode) {
65   if (nodeIsInDesignMode(aNode)) {
66     return true;
67   }
68   if (!aNode.isConnected) {
69     return false;
70   }
71   return getEditingHost(aNode) != null;
74 /**
75  * @param {Element} aElement
76  */
77 function elementIsEditingHost(aElement) {
78   return (
79     nodeIsEditable(aElement) &&
80     (!aElement.parentElement || !getEditingHost(aElement) == aElement)
81   );
84 /**
85  * @returns {Element} Retrieve focused element.  If focused element is a element
86  *                    in UA widget, this returns its host element.  E.g., when
87  *                    a button in the controls of <audio> or <video> has focus,
88  *                    this returns the <video> or <audio>.
89  */
90 function getFocusedElementOrUAWidgetHost() {
91   const focusedElement = SpecialPowers.focusManager.focusedElement;
92   if (SpecialPowers.wrap(focusedElement)?.containingShadowRoot?.isUAWidget()) {
93     return focusedElement.containingShadowRoot.host;
94   }
95   return focusedElement;
98 class TIPWrapper {
99   #mTIP = null;
100   #mFocusBlurNotifications = [];
101   #mFocusBlurListener;
102   #mWindow;
104   constructor(aWindow) {
105     this.#mWindow = aWindow;
106     this.#mTIP = Cc["@mozilla.org/text-input-processor;1"].createInstance(
107       Ci.nsITextInputProcessor
108     );
109     if (!this.beginInputTransactionForTests()) {
110       this.#mTIP = null;
111     }
112   }
114   beginInputTransactionForTests() {
115     return this.#mTIP.beginInputTransactionForTests(
116       this.#mWindow,
117       this.#observer.bind(this)
118     );
119   }
121   typeA() {
122     const AKey = new this.#mWindow.KeyboardEvent("", {
123       key: "a",
124       code: "KeyA",
125       keyCode: this.#mWindow.KeyboardEvent.DOM_VK_A,
126     });
127     this.#mTIP.keydown(AKey);
128     this.#mTIP.keyup(AKey);
129   }
131   isAvailable() {
132     return this.#mTIP != null;
133   }
135   #observer(aTIP, aNotification) {
136     if (aTIP != this.#mTIP) {
137       return false;
138     }
139     switch (aNotification.type) {
140       case "request-to-commit":
141         this.#mTIP.commitComposition();
142         break;
143       case "request-to-cancel":
144         this.#mTIP.cancelComposition();
145         break;
146       case "notify-focus":
147       case "notify-blur":
148         this.#mFocusBlurNotifications.push(aNotification.type);
149         if (this.#mFocusBlurListener) {
150           this.#mFocusBlurListener(aNotification.type);
151         }
152         break;
153     }
154     return true;
155   }
157   get TIP() {
158     return this.#mTIP;
159   }
161   /**
162    * @param {Function} aListener
163    */
164   set onIMEFocusBlur(aListener) {
165     this.#mFocusBlurListener = aListener;
166   }
168   get focusBlurNotifications() {
169     return this.#mFocusBlurNotifications.concat();
170   }
172   get numberOfFocusNotifications() {
173     return this.#mFocusBlurNotifications.filter(t => t == "notify-focus")
174       .length;
175   }
176   get numberOfBlurNotifications() {
177     return this.#mFocusBlurNotifications.filter(t => t == "notify-blur").length;
178   }
180   get IMEHasFocus() {
181     return (
182       !!this.#mFocusBlurNotifications.length &&
183       this.#mFocusBlurNotifications[this.#mFocusBlurNotifications.length - 1] ==
184         "notify-focus"
185     );
186   }
188   clearFocusBlurNotifications() {
189     this.#mFocusBlurNotifications = [];
190   }
192   destroy() {
193     this.#mTIP = null;
194     this.#mFocusBlurListener = null;
195     this.#mFocusBlurNotifications = [];
196   }