如何跟踪和收集特定字段的所有已使用依赖项 JavaScript
How to trace and collect all used dependent JavaScript for a particular field
我想收集所有事件和 JavaScript 具有特定表单 field/all 表单字段(包括框架)依赖项的代码。我已经尝试使用 puppeteer 和 CDP 来获取某个字段的事件并随后收集 JavaScript。我能够成功获取事件详细信息。不确定如何遍历事件的所有痕迹以收集使用过的 JavaScript 代码。感谢快速帮助。
已使用以下代码收集事件。
listener = await windowHandle._client.send('DOMDebugger.getEventListeners', {
objectId: submitElementHandle._remoteObject.objectId
});
这是我能从 Chrome DevTools Protocol documents:
得到的所有信息
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
try {
const [page] = await browser.pages();
const cdp = await page.target().createCDPSession();
await cdp.send('Debugger.enable');
const scriptIdToUrlMap = new Map();
cdp.on('Debugger.scriptParsed', ({ scriptId, url }) => {
scriptIdToUrlMap.set(scriptId, url);
});
await page.goto('https://chromedevtools.github.io/devtools-protocol/tot/DOMDebugger/');
await page.waitForSelector('body > main');
const { objectId } = (await cdp.send('Runtime.evaluate', {
expression: 'document.querySelector("body > main")',
})).result;
const { listeners } = await cdp.send('DOMDebugger.getEventListeners', { objectId });
for (const listener of listeners) {
console.log('Listener details:', listener, '\n');
console.log('Script URL:', scriptIdToUrlMap.get(listener.scriptId), '\n');
const { scriptSource } = await cdp.send('Debugger.getScriptSource', {
scriptId: listener.scriptId,
});
console.log(
'Script source start:',
scriptSource.split('\n')[listener.lineNumber].slice(listener.columnNumber),
'...\n',
);
}
} catch (err) { console.error(err); } finally { await browser.close(); }
当前输出:
Listener details: {
type: 'click',
useCapture: false,
passive: false,
once: false,
scriptId: '4',
lineNumber: 181,
columnNumber: 649
}
Script URL: https://chromedevtools.github.io/devtools-protocol/scripts/index.js
Script source start: (){I.classList.contains("shown")&&(I.classList.remove("shown"),P.focus())}document.addEventListener("keydown",e=>{e.metaKey||e.ctrlKey||e.altKey||(e.keyCode>=65&&e.keyCode<=90&&document.querySelector("cr-search-control").inputElement.focus(),"Escape"===e.key&&I.classList.contains("shown")&&I.classList.remove("shown"))}),P.addEventListener("click",e=>{e.stopPropagation(),I.addEventListener("transitionend",()=>{O.focus()},{once:!0}),I.classList.add("shown")}),B.addEventListener("click",W),O.addEventListener("click",W); ...
另一种变体,更老套,更不可靠。
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch(/* { headless: false, defaultViewport: null } */);
try {
const [page] = await browser.pages();
// Hook on setting listeners.
await page.evaluateOnNewDocument(() => {
window._puppeteerListenersMap = new Map();
const _puppeteerOldAddEventListener = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function newAddEventListener(...args) {
const element = this;
const [eventName, eventListener] = args;
if (!_puppeteerListenersMap.get(element)) {
_puppeteerListenersMap.set(element, Object.create(null));
}
const allListeners = _puppeteerListenersMap.get(element);
allListeners[eventName] ??= [];
allListeners[eventName].push(eventListener);
_puppeteerOldAddEventListener.call(this, ...args);
};
});
await page.goto('https://example.org/');
// Test setting listeners.
await page.evaluate(() => {
document.body.addEventListener('click', function click() {
console.log('addEventListener click');
});
document.body.addEventListener('dblclick', function dblclick() {
console.log('addEventListener dblclick');
});
document.body.onclick = function onclick() { console.log('onclick'); };
document.body.ondblclick = function ondblclick() { console.log('ondblclick'); };
});
// Test getting listeners.
const data = await page.evaluate(() => {
const element = document.body;
const elementListeners = Object.create(null);
const allListeners = _puppeteerListenersMap.get(element);
if (allListeners) {
for (const [eventName, eventListeners] of Object.entries(allListeners)) {
elementListeners[eventName] = eventListeners.map(
eventListener => eventListener.toString()
);
}
}
for (const name in element) {
if (name.startsWith('on') &&
element[name] !== null &&
typeof element[name] === 'function'
) {
elementListeners[name] = element[name].toString();
}
}
return elementListeners;
});
console.log(JSON.stringify(data, null, ' '));
} catch (err) { console.error(err); } finally { await browser.close(); }
输出:
{
"click": [
"function click() {\n console.log('addEventListener click');\n }"
],
"dblclick": [
"function dblclick() {\n console.log('addEventListener dblclick');\n }"
],
"onclick": "function onclick() { console.log('onclick'); }",
"ondblclick": "function ondblclick() { console.log('ondblclick'); }"
}
更新。从 codepen 测试页面:
// Test getting listeners.
const data = await page.evaluate(() => {
const element = document.querySelector('#signup_v1-email');
element.value = 'foo'; // Input invalid email.
const elementListeners = Object.create(null);
const allListeners = _puppeteerListenersMap.get(element);
if (allListeners) {
for (const [eventName, eventListeners] of Object.entries(allListeners)) {
elementListeners[eventName] = eventListeners.map(
eventListener => (
eventListener.call(element, new Event(eventName)), // 'not a valid email' in Browser console.
eventListener.toString()
)
);
}
}
for (const name in element) {
if (name.startsWith('on') &&
element[name] !== null &&
typeof element[name] === 'function'
) {
elementListeners[name] = element[name].toString();
}
}
return elementListeners;
});
我想收集所有事件和 JavaScript 具有特定表单 field/all 表单字段(包括框架)依赖项的代码。我已经尝试使用 puppeteer 和 CDP 来获取某个字段的事件并随后收集 JavaScript。我能够成功获取事件详细信息。不确定如何遍历事件的所有痕迹以收集使用过的 JavaScript 代码。感谢快速帮助。
已使用以下代码收集事件。
listener = await windowHandle._client.send('DOMDebugger.getEventListeners', {
objectId: submitElementHandle._remoteObject.objectId
});
这是我能从 Chrome DevTools Protocol documents:
得到的所有信息import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
try {
const [page] = await browser.pages();
const cdp = await page.target().createCDPSession();
await cdp.send('Debugger.enable');
const scriptIdToUrlMap = new Map();
cdp.on('Debugger.scriptParsed', ({ scriptId, url }) => {
scriptIdToUrlMap.set(scriptId, url);
});
await page.goto('https://chromedevtools.github.io/devtools-protocol/tot/DOMDebugger/');
await page.waitForSelector('body > main');
const { objectId } = (await cdp.send('Runtime.evaluate', {
expression: 'document.querySelector("body > main")',
})).result;
const { listeners } = await cdp.send('DOMDebugger.getEventListeners', { objectId });
for (const listener of listeners) {
console.log('Listener details:', listener, '\n');
console.log('Script URL:', scriptIdToUrlMap.get(listener.scriptId), '\n');
const { scriptSource } = await cdp.send('Debugger.getScriptSource', {
scriptId: listener.scriptId,
});
console.log(
'Script source start:',
scriptSource.split('\n')[listener.lineNumber].slice(listener.columnNumber),
'...\n',
);
}
} catch (err) { console.error(err); } finally { await browser.close(); }
当前输出:
Listener details: {
type: 'click',
useCapture: false,
passive: false,
once: false,
scriptId: '4',
lineNumber: 181,
columnNumber: 649
}
Script URL: https://chromedevtools.github.io/devtools-protocol/scripts/index.js
Script source start: (){I.classList.contains("shown")&&(I.classList.remove("shown"),P.focus())}document.addEventListener("keydown",e=>{e.metaKey||e.ctrlKey||e.altKey||(e.keyCode>=65&&e.keyCode<=90&&document.querySelector("cr-search-control").inputElement.focus(),"Escape"===e.key&&I.classList.contains("shown")&&I.classList.remove("shown"))}),P.addEventListener("click",e=>{e.stopPropagation(),I.addEventListener("transitionend",()=>{O.focus()},{once:!0}),I.classList.add("shown")}),B.addEventListener("click",W),O.addEventListener("click",W); ...
另一种变体,更老套,更不可靠。
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch(/* { headless: false, defaultViewport: null } */);
try {
const [page] = await browser.pages();
// Hook on setting listeners.
await page.evaluateOnNewDocument(() => {
window._puppeteerListenersMap = new Map();
const _puppeteerOldAddEventListener = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function newAddEventListener(...args) {
const element = this;
const [eventName, eventListener] = args;
if (!_puppeteerListenersMap.get(element)) {
_puppeteerListenersMap.set(element, Object.create(null));
}
const allListeners = _puppeteerListenersMap.get(element);
allListeners[eventName] ??= [];
allListeners[eventName].push(eventListener);
_puppeteerOldAddEventListener.call(this, ...args);
};
});
await page.goto('https://example.org/');
// Test setting listeners.
await page.evaluate(() => {
document.body.addEventListener('click', function click() {
console.log('addEventListener click');
});
document.body.addEventListener('dblclick', function dblclick() {
console.log('addEventListener dblclick');
});
document.body.onclick = function onclick() { console.log('onclick'); };
document.body.ondblclick = function ondblclick() { console.log('ondblclick'); };
});
// Test getting listeners.
const data = await page.evaluate(() => {
const element = document.body;
const elementListeners = Object.create(null);
const allListeners = _puppeteerListenersMap.get(element);
if (allListeners) {
for (const [eventName, eventListeners] of Object.entries(allListeners)) {
elementListeners[eventName] = eventListeners.map(
eventListener => eventListener.toString()
);
}
}
for (const name in element) {
if (name.startsWith('on') &&
element[name] !== null &&
typeof element[name] === 'function'
) {
elementListeners[name] = element[name].toString();
}
}
return elementListeners;
});
console.log(JSON.stringify(data, null, ' '));
} catch (err) { console.error(err); } finally { await browser.close(); }
输出:
{
"click": [
"function click() {\n console.log('addEventListener click');\n }"
],
"dblclick": [
"function dblclick() {\n console.log('addEventListener dblclick');\n }"
],
"onclick": "function onclick() { console.log('onclick'); }",
"ondblclick": "function ondblclick() { console.log('ondblclick'); }"
}
更新。从 codepen 测试页面:
// Test getting listeners.
const data = await page.evaluate(() => {
const element = document.querySelector('#signup_v1-email');
element.value = 'foo'; // Input invalid email.
const elementListeners = Object.create(null);
const allListeners = _puppeteerListenersMap.get(element);
if (allListeners) {
for (const [eventName, eventListeners] of Object.entries(allListeners)) {
elementListeners[eventName] = eventListeners.map(
eventListener => (
eventListener.call(element, new Event(eventName)), // 'not a valid email' in Browser console.
eventListener.toString()
)
);
}
}
for (const name in element) {
if (name.startsWith('on') &&
element[name] !== null &&
typeof element[name] === 'function'
) {
elementListeners[name] = element[name].toString();
}
}
return elementListeners;
});