有一个Google Chrome extension content script
来处理所有标签页上出现的JS错误。但问题是没有一种常见的获取错误堆栈跟踪的方法不起作用。
例如,Chrome扩展程序content script
中有一段代码:
window.addEventListener('error', function(event) {
console.log(event.error.stack); // event.error will be null
}, false);
如果我在网页中调用此代码,那么event.error
将包含Error
属性stack
的对象。
尝试使用以下方法获取堆栈跟踪时遇到同样的问题:
console.log((new Error()).stack));
是否有人知道在Chrome扩展程序的content script
内获取错误堆栈跟踪的一些工作问题?
错误堆栈跟踪必须以string
或Array
接收,这意味着不仅仅是通过调用console.trace()
来获取JS控制台中的某些输出。
如何重现:
jzen.zip
解压缩到某个/jsen
文件夹chrome://extensions
,启用Developer mode
http://i.imgur.com/5x5D6NP.png Load unpacked extension
按钮,然后选择/jsen
文件夹/jsen/content.js
文件,在console.log('JSEN', e.error.stack);
window.addEventListener('error', function(e) {
/jsen/content.js
以获取正确的错误跟踪答案 0 :(得分:12)
正如您所提到的,在内容脚本上下文中捕获事件时,事件对象的error
属性为null
,但在网页上下文中捕获时,它具有所需的信息。因此,解决方案是在网页上下文中捕获事件并使用消息传递将其传递到内容脚本。
// This code will be injected to run in webpage context
function codeToInject() {
window.addEventListener('error', function(e) {
var error = {
stack: e.error.stack
// Add here any other properties you need, like e.filename, etc...
};
document.dispatchEvent(new CustomEvent('ReportError', {detail:error}));
});
}
document.addEventListener('ReportError', function(e) {
console.log('CONTENT SCRIPT', e.detail.stack);
});
//Inject code
var script = document.createElement('script');
script.textContent = '(' + codeToInject + '())';
(document.head||document.documentElement).appendChild(script);
script.parentNode.removeChild(script);
使用的技术描述于:
答案 1 :(得分:3)
主要问题是 JS context isolation ,即“内容脚本在称为孤立世界的特殊环境中执行”。当然,这是一件好事,因为它可以避免冲突并增强安全性,但如果你想捕获错误,那就是一个问题。
每个孤立的世界都会看到自己的(窗口)对象版本。分配给对象会影响对象的独立副本...
......任何人都无法读取对方的事件处理程序。事件处理程序按照分配顺序调用。
关于可行的解决方案(a.k.a.hack)包括以下步骤:
以下是完全相同的示例扩展的源代码。
<强>的manifest.json:强>
{
"manifest_version": 2,
"name": "Test Extension",
"version": "0.0",
"content_scripts": [{
"matches": ["*://*/*"],
"js": ["content.js"],
"run_at": "document_start",
"all_frames": true
}],
}
<强> content.js 强>:
/* This <script> element will function as an "error-proxy"
* for the content-script */
var errorProxy = document.createElement('script');
errorProxy.id = 'myErrorProxyScriptID';
errorProxy.dataset.lastError = '';
/* Make the content as non-obtrusive as possible */
errorProxy.textContent = [
'',
'(function() {',
' var script = document.querySelector("script#' + errorProxy.id + '");',
' window.addEventListener("error", function(evt) {',
' script.dataset.lastError = evt.error.stack;',
' }, true);',
'})();',
''].join('\n');
/* Add the <script> element to the DOM */
document.documentElement.appendChild(errorProxy);
/* Create an observer for `errorProxy`'s attributes
* (the `data-last-error` attribute is of interest) */
var errorObserver = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if ((mutation.type === 'attributes')
&& (mutation.attributeName === 'data-last-error')) {
console.log('Content script detected new error:\n',
errorProxy.dataset.lastError);
}
});
});
errorObserver.observe(errorProxy, { attributes: true });