如何在Chrome扩展内容脚本中获取错误堆栈跟踪

How to get errors stack trace in Chrome extension content script?

本文关键字:获取 取错误 堆栈 跟踪 脚本 Chrome 扩展      更新时间:2024-02-08

有一个带有content script的Google Chrome扩展,可以处理所有选项卡页面上发生的JS错误。但问题是,获取错误堆栈跟踪的常用方法都不起作用。

例如,Chrome扩展的content script中有一个代码:

window.addEventListener('error', function(event) {
    console.log(event.error.stack); // event.error will be null
}, false);

如果我在网页内调用此代码,那么event.error将包含具有stack属性的Error对象。

尝试使用获取堆栈跟踪的问题相同

console.log((new Error()).stack));

有人知道在Chrome扩展的content script中获取错误堆栈跟踪的一些工作问题吗?

错误堆栈跟踪必须以stringArray的形式接收,这意味着不只是像JS控制台中通过调用console.trace()的某些输出一样。

如何复制

  1. 下载https://mega.co.nz/#!ENw00YAC!92gBZEoLCO9jPsWyKht4dbjYyo0Zk-PU5YAj0h88-3Q
  2. jzen.zip解压缩到某个/jsen文件夹
  3. 在Google Chrome中打开chrome://extensions,启用Developer modehttps://i.stack.imgur.com/OLwpa.png
  4. 单击Load unpacked extension按钮并选择指向/jsen文件夹的路径
  5. 打开/jsen/content.js文件,在window.addEventListener('error', function(e) {中添加console.log('JSEN', e.error.stack);
  6. 转到http://xpart.ru/_share/js.htm并在JS控制台中查看结果(Ctrl+Shift+J)
  7. 尝试编辑/jsen/content.js以获得正确的错误跟踪
  8. 要重新初始化Chrome扩展源代码,请单击https://i.stack.imgur.com/2LIEa.png

如前所述,在Content Script上下文中捕获事件时,事件对象的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);

所使用的技术如所述:

  • https://stackoverflow.com/a/9517879/1507998
  • https://stackoverflow.com/a/9636008/1507998

问题

主要问题是JS上下文隔离,即"内容脚本在一个称为隔离世界的特殊环境中执行"当然,这是一件好事,因为它避免了冲突并增强了安全性,但如果你想捕捉错误,这也是一个问题。

每个孤立的世界都可以看到自己版本的(窗口)对象。指定给对象会影响对象的独立副本。。。

两者都不能读取另一方的事件处理程序。事件处理程序是按照分配它们的顺序调用的。


解决方案

可能的解决方案(也称为破解)包括以下步骤:

  1. 向网页本身注入一些代码(并尽量使其不突兀,以避免冲突)
  2. 在该代码中,为错误注册一个事件侦听器,并将错误数据存储在DOM节点的数据集中
  3. 最后,在内容脚本中,使用MutationObserver来观察该DOM节点属性的变化
  4. 一旦指定数据发生更改-<…>属性被检测到,获取它的新值,您就可以在内容脚本中获得错误信息:)

源代码

下面是一个示例扩展的源代码。

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 });