✨ hijack document event listener and destroy it after unmount (#1655)
This commit is contained in:
parent
3e101581af
commit
85ae42adbc
|
|
@ -110,7 +110,7 @@ test('should getXPathForElement work well', () => {
|
|||
const xpath = getXPathForElement(testNode!, document);
|
||||
expect(xpath).toEqual(
|
||||
// eslint-disable-next-line max-len
|
||||
`/*[name()='HTML' and namespace-uri()='http://www.w3.org/1999/xhtml']/*[name()='BODY' and namespace-uri()='http://www.w3.org/1999/xhtml'][1]/*[name()='ARTICLE' and namespace-uri()='http://www.w3.org/1999/xhtml'][1]/*[name()='DIV' and namespace-uri()='http://www.w3.org/1999/xhtml'][1]/*[name()='DIV' and namespace-uri()='http://www.w3.org/1999/xhtml'][2]`,
|
||||
`/*[name()='HTML']/*[name()='BODY'][1]/*[name()='ARTICLE'][1]/*[name()='DIV'][1]/*[name()='DIV'][2]`,
|
||||
);
|
||||
|
||||
const virtualDOM = document.createElement('div');
|
||||
|
|
|
|||
|
|
@ -6,40 +6,69 @@
|
|||
|
||||
import { noop } from 'lodash';
|
||||
|
||||
const rawAddEventListener = window.addEventListener;
|
||||
const rawRemoveEventListener = window.removeEventListener;
|
||||
const rawWindowAddEventListener = window.addEventListener;
|
||||
const rawWindowRemoveEventListener = window.removeEventListener;
|
||||
const rawDocAddEventListener = document.addEventListener;
|
||||
const rawDocRemoveEventListener = document.removeEventListener;
|
||||
|
||||
export default function patch(global: WindowProxy) {
|
||||
const listenerMap = new Map<string, EventListenerOrEventListenerObject[]>();
|
||||
const windowListenerMap = new Map<string, EventListenerOrEventListenerObject[]>();
|
||||
const docListenerMap = new Map<string, EventListenerOrEventListenerObject[]>();
|
||||
|
||||
global.addEventListener = (
|
||||
type: string,
|
||||
listener: EventListenerOrEventListenerObject,
|
||||
options?: boolean | AddEventListenerOptions,
|
||||
) => {
|
||||
const listeners = listenerMap.get(type) || [];
|
||||
listenerMap.set(type, [...listeners, listener]);
|
||||
return rawAddEventListener.call(window, type, listener, options);
|
||||
const listeners = windowListenerMap.get(type) || [];
|
||||
windowListenerMap.set(type, [...listeners, listener]);
|
||||
return rawWindowAddEventListener.call(window, type, listener, options);
|
||||
};
|
||||
|
||||
global.removeEventListener = (
|
||||
type: string,
|
||||
listener: EventListenerOrEventListenerObject,
|
||||
options?: boolean | AddEventListenerOptions,
|
||||
) => {
|
||||
const storedTypeListeners = listenerMap.get(type);
|
||||
const storedTypeListeners = windowListenerMap.get(type);
|
||||
if (storedTypeListeners && storedTypeListeners.length && storedTypeListeners.indexOf(listener) !== -1) {
|
||||
storedTypeListeners.splice(storedTypeListeners.indexOf(listener), 1);
|
||||
}
|
||||
return rawRemoveEventListener.call(window, type, listener, options);
|
||||
return rawWindowRemoveEventListener.call(window, type, listener, options);
|
||||
};
|
||||
|
||||
document.addEventListener = (
|
||||
type: string,
|
||||
listener: EventListenerOrEventListenerObject,
|
||||
options?: boolean | AddEventListenerOptions,
|
||||
) => {
|
||||
const listeners = docListenerMap.get(type) || [];
|
||||
docListenerMap.set(type, [...listeners, listener]);
|
||||
return rawDocAddEventListener.call(document, type, listener, options);
|
||||
};
|
||||
document.removeEventListener = (
|
||||
type: string,
|
||||
listener: EventListenerOrEventListenerObject,
|
||||
options?: boolean | AddEventListenerOptions,
|
||||
) => {
|
||||
const storedTypeListeners = docListenerMap.get(type);
|
||||
if (storedTypeListeners && storedTypeListeners.length && storedTypeListeners.indexOf(listener) !== -1) {
|
||||
storedTypeListeners.splice(storedTypeListeners.indexOf(listener), 1);
|
||||
}
|
||||
return rawDocRemoveEventListener.call(document, type, listener, options);
|
||||
};
|
||||
|
||||
return function free() {
|
||||
listenerMap.forEach((listeners, type) =>
|
||||
windowListenerMap.forEach((listeners, type) =>
|
||||
[...listeners].forEach((listener) => global.removeEventListener(type, listener)),
|
||||
);
|
||||
global.addEventListener = rawAddEventListener;
|
||||
global.removeEventListener = rawRemoveEventListener;
|
||||
docListenerMap.forEach((listeners, type) =>
|
||||
[...listeners].forEach((listener) => document.removeEventListener(type, listener)),
|
||||
);
|
||||
|
||||
global.addEventListener = rawWindowAddEventListener;
|
||||
global.removeEventListener = rawWindowRemoveEventListener;
|
||||
document.addEventListener = rawDocAddEventListener;
|
||||
document.removeEventListener = rawDocRemoveEventListener;
|
||||
|
||||
return noop;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -215,14 +215,14 @@ export default class ProxySandbox implements SandBox {
|
|||
},
|
||||
|
||||
get(target: FakeWindow, p: PropertyKey): any {
|
||||
if (p === Symbol.unscopables) return unscopables;
|
||||
|
||||
setCurrentRunningSandboxProxy(proxy);
|
||||
// FIXME if you have any other good ideas
|
||||
// remove the mark in next tick, thus we can identify whether it in micro app or not
|
||||
// this approach is just a workaround, it could not cover all complex cases, such as the micro app runs in the same task context with master in some case
|
||||
nextTask(() => setCurrentRunningSandboxProxy(null));
|
||||
|
||||
if (p === Symbol.unscopables) return unscopables;
|
||||
|
||||
// avoid who using window.window or window.self to escape the sandbox environment to touch the really window
|
||||
// see https://github.com/eligrey/FileSaver.js/blob/master/src/FileSaver.js#L13
|
||||
if (p === 'window' || p === 'self') {
|
||||
|
|
|
|||
|
|
@ -199,16 +199,12 @@ export function getXPathForElement(el: Node, document: Document): string | void
|
|||
tmpEle = tmpEle.previousSibling;
|
||||
}
|
||||
|
||||
xpath = `*[name()='${element.nodeName}' and namespace-uri()='${
|
||||
element.namespaceURI === null ? '' : element.namespaceURI
|
||||
}'][${pos}]/${xpath}`;
|
||||
xpath = `*[name()='${element.nodeName}'][${pos}]/${xpath}`;
|
||||
|
||||
element = element.parentNode!;
|
||||
}
|
||||
|
||||
xpath = `/*[name()='${document.documentElement.nodeName}' and namespace-uri()='${
|
||||
element.namespaceURI === null ? '' : element.namespaceURI
|
||||
}']/${xpath}`;
|
||||
xpath = `/*[name()='${document.documentElement.nodeName}']/${xpath}`;
|
||||
xpath = xpath.replace(/\/$/, '');
|
||||
|
||||
return xpath;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user