【问题标题】:Modify document html before load with Chrome extension, using document_start在使用 Chrome 扩展程序加载之前修改文档 html,使用 document_start
【发布时间】:2012-03-30 15:50:12
【问题描述】:

我正在尝试创建一个扩展来修改现有网站,并修复现有 javascript 中的错误。在加载之前对文档的文本内容进行简单的替换将修复错误 - 特别是标题中标签中的硬编码值 - 我的第一次尝试(从日期值中删除时间戳)是这样的:

document.documentElement.innerHTML = document.documentElement.innerHTML.replace(/("date_of_birth": new Date\("[A-Z][a-z]{2}, \d{2} [A-Z][a-z]{2} \d{4}) \d{2}:\d{2}:\d{2} GMT/g, "$1");

例如,"date_of_birth": new Date("Tue, 30 Oct 1984 00:01:00 GMT") 的任何实例都变为 "date_of_birth": new Date("Tue, 30 Oct 1984")

问题是我知道访问文档的 HTML 内容的唯一方法是 document.documentElement.innerHTML。在 document_start 运行它会失败,因为尚未加载 DOM,因此 documentElement.innerHTML 是一个空字符串。在 DOM 加载后运行上述代码为时已晚,因为初始化脚本已经运行,并且此时修改文档的 html 会重新加载这些脚本,从而在脚本繁重的页面中造成各种破坏。

我不能在加载 DOM 之前简单地附加到 documentElement,这是搜索解决方案时最常见的建议。这需要在加载和运行之前修改现有脚本。

有什么方法可以在加载 HTML 文档之前访问和预处理原始内容?

提前致谢。

【问题讨论】:

    标签: javascript google-chrome-extension


    【解决方案1】:

    <script> 执行之前无法“编辑”它的内容:一旦遇到关闭</script> 标记,立即评估内容。

    除了修改代码本身,还可以使用间接方法:覆盖全局Date对象,可以使用以下代码完成:

    (function(global) {
        var _Date = global.Date; /* Store original Date object */
        var overwriteafterXmatches = 1; /* Overwrite after X matches*/
        function date(year, month, day, hour, minute, second, millisecond) {
            var tmp = /^([A-Z][a-z]{2}, \d{2} [A-Z][a-z]{2} \d{4}) \d{2}:\d{2}:\d{2} GMT$/.exec(year);
            switch(arguments.length) {
                case 0:
                    return new _Date();
                case 1:
                    if (tmp) { /* If match */
                        tmp = new _Date(match[1]);
                        if (--overwriteAfterXmatches <= 0) {
                            /* Set the original Date object*/
                            global.Date = _Date;
                        }
                    }
                    return new _Date(year);
                case 2:
                    return new _Date(year, month);
                case 3:
                    return new _Date(year, month, day);
                case 4:
                    return new _Date(year, month, day, hour);
                case 5:
                    return new _Date(year, month, day, hour, minute);
                case 6:
                    return new _Date(year, month, day, hour, minute, second);
                default:
                    return new _Date(year, month, day, hour, minute, second, millisecond);
            }
        }
        /* Overwrite global Date object*/
        global.Date = date;
    })(window);
    

    contentscript.js

    内容脚本在isolated environment 中运行。这意味着您不能直接修改全局属性。要让代码在Content script 中工作,您必须以这种方式注入&lt;script&gt; 标签:

    var code = "\
    (function(global) {\
        var _Date = global.Date; /* Store original date object*/\
        var overwriteafterXmatches = 1; /* Overwrite after X matches*/\
        function date(year, month, day, hour, minute, second, millisecond) {\
            var tmp = /^([A-Z][a-z]{2}, \d{2} [A-Z][a-z]{2} \d{4}) \d{2}:\d{2}:\d{2} GMT$/.exec(year);\
            switch(arguments.length) {\
                case 0:\
                    return new _Date();\
                case 1:\
                    if (tmp) { /* If match */\
                        tmp = new _Date(match[1]);\
                        if (--overwriteAfterXmatches <= 0) {\
                            /* Set the original Date object*/\
                            global.Date = _Date;\
                        }\
                    }\
                    return new _Date(year);\
                case 2:\
                    return new _Date(year, month);\
                case 3:\
                    return new _Date(year, month, day);\
                case 4:\
                    return new _Date(year, month, day, hour);\
                case 5:\
                    return new _Date(year, month, day, hour, minute);\
                case 6:\
                    return new _Date(year, month, day, hour, minute, second);\
                default:\
                    return new _Date(year, month, day, hour, minute, second, millisecond);\
            }\
        }\
        /* Overwrite global Date object*/\
        global.Date = date;\
    })(window);\
    ";
    var script = document.createElement('script');
    script.appendChild(document.createTextNode(code));
    (document.head||document.documentElement).appendChild(script);
    script.parentNode.removeChild(script);
    

    manifest.json

    这是manifest.json 文件的示例。将&lt;all_urls&gt; 替换为更具体的match pattern

    {
      "name": "Test",
      "version": "1.0",
      "content_scripts": [
        {
          "matches": ["<all_urls>"],
          "js": ["contentscript.js"],
          "run_at": "document_start"
        }
       ],
       "permissions": ["<all_urls>"]
    }
    

    【讨论】:

    • 这是一个非常有趣的解决方案,看起来像我一直在寻找的东西,但我对最后一行感到困惑:script.parentNode.removeChild(script);在我看来,它将删除您刚刚插入的节点。为什么?
    • @Dmitri 我喜欢收拾我的烂摊子。一旦&lt;script&gt; 标记中的内容执行完毕,就无法撤消它(通常)。删除节点没有副作用(如果您使用的是外部脚本,请将 removeChild 移动到 onload 事件)。
    • @RobW 同样,结果是随机的。我在 contentscript.js 和目标页面中添加了一些日志。但有时会先登录 contentscript.js,但有时会先登录目标页面。
    猜你喜欢
    • 1970-01-01
    • 2023-03-11
    • 2012-12-13
    • 1970-01-01
    • 2012-01-21
    • 2018-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多