forked from mirrors/gecko-dev
116 lines
4 KiB
JavaScript
116 lines
4 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
"use strict";
|
|
|
|
/**
|
|
* Bug 1739489 - Entering an emoji using the MacOS IME "crashes" Draft.js editors.
|
|
*/
|
|
|
|
/* globals exportFunction */
|
|
|
|
console.info(
|
|
"textInput event has been remapped to beforeinput for compatibility reasons. See https://bugzilla.mozilla.org/show_bug.cgi?id=1739489 for details."
|
|
);
|
|
|
|
window.wrappedJSObject.TextEvent = window.wrappedJSObject.InputEvent;
|
|
|
|
const { CustomEvent, Event, EventTarget } = window.wrappedJSObject;
|
|
var Remapped = [
|
|
[CustomEvent, "constructor"],
|
|
[Event, "constructor"],
|
|
[Event, "initEvent"],
|
|
[EventTarget, "addEventListener"],
|
|
[EventTarget, "removeEventListener"],
|
|
];
|
|
|
|
for (const [obj, name] of Remapped) {
|
|
const { prototype } = obj;
|
|
const orig = prototype[name];
|
|
Object.defineProperty(prototype, name, {
|
|
value: exportFunction(function (type, b, c, d) {
|
|
if (type?.toLowerCase() === "textinput") {
|
|
type = "beforeinput";
|
|
}
|
|
return orig.call(this, type, b, c, d);
|
|
}, window),
|
|
});
|
|
}
|
|
|
|
if (location.host === "www.reddit.com") {
|
|
(function () {
|
|
const EditorCSS = ".public-DraftEditor-content[contenteditable=true]";
|
|
let obsEditor, obsStart, obsText, obsKey, observer;
|
|
const obsConfig = { characterData: true, childList: true, subtree: true };
|
|
const obsHandler = () => {
|
|
observer.disconnect();
|
|
const finalTextNode = obsEditor.querySelector(
|
|
`[data-offset-key="${obsKey}"] [data-text='true']`
|
|
).firstChild;
|
|
const end = obsStart + obsText.length;
|
|
window
|
|
.getSelection()
|
|
.setBaseAndExtent(finalTextNode, end, finalTextNode, end);
|
|
};
|
|
observer = new MutationObserver(obsHandler);
|
|
|
|
document.documentElement.addEventListener(
|
|
"beforeinput",
|
|
e => {
|
|
if (e.inputType != "insertFromPaste") {
|
|
return;
|
|
}
|
|
const { target } = e;
|
|
obsEditor = target.closest(EditorCSS);
|
|
if (!obsEditor) {
|
|
return;
|
|
}
|
|
const items = e?.dataTransfer.items;
|
|
for (let item of items) {
|
|
if (item.type === "text/plain") {
|
|
e.preventDefault();
|
|
item.getAsString(text => {
|
|
obsText = text;
|
|
|
|
// find the editor-managed <span> which contains the text node the
|
|
// cursor starts on, and the cursor's location (or the selection start)
|
|
const sel = window.getSelection();
|
|
obsStart = sel.anchorOffset;
|
|
let anchor = sel.anchorNode;
|
|
if (!anchor.closest) {
|
|
anchor = anchor.parentElement;
|
|
}
|
|
anchor = anchor.closest("[data-offset-key]");
|
|
obsKey = anchor.getAttribute("data-offset-key");
|
|
|
|
// set us up to wait for the editor to either update or replace the
|
|
// <span> with that key (the one containing the text to be changed).
|
|
// we will then make sure the cursor is after the pasted text, as if
|
|
// the editor recreates the node, the cursor position is lost
|
|
observer.observe(obsEditor, obsConfig);
|
|
|
|
// force the editor to "paste". sending paste or other events will not
|
|
// work, nor using execCommand (adding HTML will screw up the DOM that
|
|
// the editor expects, and adding plain text will make it ignore newlines).
|
|
target.dispatchEvent(
|
|
new InputEvent("beforeinput", {
|
|
inputType: "insertText",
|
|
data: text,
|
|
bubbles: true,
|
|
cancelable: true,
|
|
})
|
|
);
|
|
|
|
// blur the editor to force it to update/flush its state, because otherwise
|
|
// the paste works, but the editor doesn't show it (until it is re-focused).
|
|
obsEditor.blur();
|
|
});
|
|
break;
|
|
}
|
|
}
|
|
},
|
|
true
|
|
);
|
|
})();
|
|
}
|