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.
14 function nodeIsInShadowDOM(aNode) {
15 for (let node = aNode; node; node = node.parentNode) {
16 if (node instanceof ShadowRoot) {
19 if (node == node.parentNode) {
29 function nodeIsInDesignMode(aNode) {
32 !nodeIsInShadowDOM(aNode) &&
33 aNode.ownerDocument.designMode == "on"
40 function getEditingHost(aNode) {
41 if (nodeIsInDesignMode(aNode)) {
42 return aNode.ownerDocument.documentElement;
46 aNode.nodeType == Node.ELEMENT_NODE ? aNode : aNode.parentElement;
48 element = element.parentElement
50 const contenteditable = element.getAttribute("contenteditable");
51 if (contenteditable === "true" || contenteditable === "") {
54 if (contenteditable === "false") {
64 function nodeIsEditable(aNode) {
65 if (nodeIsInDesignMode(aNode)) {
68 if (!aNode.isConnected) {
71 return getEditingHost(aNode) != null;
75 * @param {Element} aElement
77 function elementIsEditingHost(aElement) {
79 nodeIsEditable(aElement) &&
80 (!aElement.parentElement || !getEditingHost(aElement) == aElement)
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>.
90 function getFocusedElementOrUAWidgetHost() {
91 const focusedElement = SpecialPowers.focusManager.focusedElement;
92 if (SpecialPowers.wrap(focusedElement)?.containingShadowRoot?.isUAWidget()) {
93 return focusedElement.containingShadowRoot.host;
95 return focusedElement;
100 #mFocusBlurNotifications = [];
104 constructor(aWindow) {
105 this.#mWindow = aWindow;
106 this.#mTIP = Cc["@mozilla.org/text-input-processor;1"].createInstance(
107 Ci.nsITextInputProcessor
109 if (!this.beginInputTransactionForTests()) {
114 beginInputTransactionForTests() {
115 return this.#mTIP.beginInputTransactionForTests(
117 this.#observer.bind(this)
122 const AKey = new this.#mWindow.KeyboardEvent("", {
125 keyCode: this.#mWindow.KeyboardEvent.DOM_VK_A,
127 this.#mTIP.keydown(AKey);
128 this.#mTIP.keyup(AKey);
132 return this.#mTIP != null;
135 #observer(aTIP, aNotification) {
136 if (aTIP != this.#mTIP) {
139 switch (aNotification.type) {
140 case "request-to-commit":
141 this.#mTIP.commitComposition();
143 case "request-to-cancel":
144 this.#mTIP.cancelComposition();
148 this.#mFocusBlurNotifications.push(aNotification.type);
149 if (this.#mFocusBlurListener) {
150 this.#mFocusBlurListener(aNotification.type);
162 * @param {Function} aListener
164 set onIMEFocusBlur(aListener) {
165 this.#mFocusBlurListener = aListener;
168 get focusBlurNotifications() {
169 return this.#mFocusBlurNotifications.concat();
172 get numberOfFocusNotifications() {
173 return this.#mFocusBlurNotifications.filter(t => t == "notify-focus")
176 get numberOfBlurNotifications() {
177 return this.#mFocusBlurNotifications.filter(t => t == "notify-blur").length;
182 !!this.#mFocusBlurNotifications.length &&
183 this.#mFocusBlurNotifications[this.#mFocusBlurNotifications.length - 1] ==
188 clearFocusBlurNotifications() {
189 this.#mFocusBlurNotifications = [];
194 this.#mFocusBlurListener = null;
195 this.#mFocusBlurNotifications = [];