Compare commits

...
Sign in to create a new pull request.

5 commits

Author SHA1 Message Date
David Baker
8c09da4294 more s/type/nodeName/ 2020-05-13 19:33:37 +01:00
David Baker
b447b0c62d Use nodeName which is specced 2020-05-13 18:44:06 +01:00
David Baker
cdef07ca2c Log details about the node that failed to be removed / inserted 2020-05-13 18:20:38 +01:00
David Baker
33f22ac483 typo 2020-05-13 17:00:02 +01:00
David Baker
a35d566551 Patch removeNode / insertBefore to fix React crash with Google translate
Hopefully all the info for this is in the comment.

Fixes https://github.com/vector-im/riot-web/issues/13557
2020-05-13 16:44:39 +01:00

View file

@ -78,6 +78,70 @@ function checkBrowserFeatures() {
return featureComplete;
}
/**
* Monkey patch some browser methods to work around a fundamental
* incompatability between React and any browser extension or anything
* else that manipulates the DOM.
*
* React issue: https://github.com/facebook/react/issues/11538
* Chromium issue: https://bugs.chromium.org/p/chromium/issues/detail?id=872770
*
* The workaround here is inspired by the one in a comment on the React bug
* (https://github.com/facebook/react/issues/11538#issuecomment-417504600)
* except that we catch the exception rather than sanity checking every call,
* and we remove the child from whatever its parent actually is rather than
* doing nothing (the workaround in the comment leaves the element in the DOM,
* which in the case of https://github.com/vector-im/riot-web/issues/13557
* makes it fail by adding another submit button to the page every time you
* try to log in, which is also a fairly terrible failure mode).
*/
function monkeyPatchForReact() {
function findAncestorWithClass(node) {
let depth = 0;
while (node) {
if (node.className) return [node, depth];
node = node.parentNode;
++depth;
}
return [null, 0];
}
const originalRemoveChild = Node.prototype.removeChild;
Node.prototype.removeChild = function(...args) {
try {
return originalRemoveChild.apply(this, args);
} catch (e) {
const [ancestor, depth] = findAncestorWithClass(args[0]);
console.log(
"Caught exception from removeChild, removing node of type " +
args[0].nodeName + ". Closest ancestor with class is a " + ancestor.nodeName +
" with class " + ancestor.className + " " + depth + " levels above. " +
"See https://github.com/vector-im/riot-web/issues/13557", e,
);
return originalRemoveChild.apply(args[0].parentNode, args);
}
}
const originalInsertBefore = Node.prototype.insertBefore;
Node.prototype.insertBefore = function(...args) {
try {
return originalInsertBefore.apply(this, args);
} catch (e) {
const [ancestor, depth] = findAncestorWithClass(args[0]);
console.log(
"Caught exception from removeChild, removing " + args[0].nodeName +
". Closest ancestor with class is a " + ancestor.nodeName +
" with class " + ancestor.class + " " + depth + " levels above. " +
"See https://github.com/vector-im/riot-web/issues/13557", e,
);
// We could use appendChild instead, then the node would
// be in the DOM but not necessarily in the right place?
// For now, do nothing.
return args[0];
}
}
}
const supportedBrowser = checkBrowserFeatures();
// React depends on Map & Set which we check for using modernizr's es6collections
@ -190,6 +254,8 @@ async function start() {
await loadThemePromise;
await loadLanguagePromise;
monkeyPatchForReact();
// Finally, load the app. All of the other react-sdk imports are in this file which causes the skinner to
// run on the components.
await loadApp(fragparts.params);