升级或安装后 Chrome 扩展内容脚本重新注入

问题描述 投票:0回答:7

安装或升级我正在开发的 Chrome 扩展程序后,不会重新注入内容脚本(清单中指定),因此需要刷新页面才能使扩展程序正常工作。有没有办法强制再次注入脚本?

我相信我可以通过从清单中删除它们然后处理要在后台页面中注入的页面来以编程方式再次注入它们,但这不是一个好的解决方案。

我不想自动刷新用户的选项卡,因为这可能会丢失一些数据。当您安装或升级扩展程序时,Safari 会自动刷新所有页面。

google-chrome-extension browser-extension content-script
7个回答
66
投票

有一种方法可以允许内容脚本繁重的扩展在升级后继续运行,并使其在安装后立即运行。

安装/升级

安装方法是简单地迭代所有窗口中的所有选项卡,并以编程方式将一些脚本注入到具有匹配 URL 的选项卡中。

清单V3

manifest.json:

"background": {"service_worker": "background.js"},
"permissions": ["scripting"],
"host_permissions": ["<all_urls>"],

这些 host_permissions 应与内容脚本的

matches
相同。

背景.js:

chrome.runtime.onInstalled.addListener(async () => {
  for (const cs of chrome.runtime.getManifest().content_scripts) {
    for (const tab of await chrome.tabs.query({url: cs.matches})) {
      chrome.scripting.executeScript({
        files: cs.js,
        target: {tabId: tab.id, allFrames: cs.all_frames},
        injectImmediately: cs.run_at === 'document_start',
        // world: cs.world, // uncomment if you use it in manifest.json in Chrome 111+
      });
    }
  }
});

这是一个不处理框架的简化示例。您可以使用 getAllFrames API 并自行匹配 URL,请参阅匹配模式的文档。

清单V2

显然,您必须在manifest.json中声明的后台页面事件页面脚本中执行此操作:

"background": {
    "scripts": ["background.js"]
},

背景.js:

// Add a `manifest` property to the `chrome` object.
chrome.manifest = chrome.runtime.getManifest();

var injectIntoTab = function (tab) {
    // You could iterate through the content scripts here
    var scripts = chrome.manifest.content_scripts[0].js;
    var i = 0, s = scripts.length;
    for( ; i < s; i++ ) {
        chrome.tabs.executeScript(tab.id, {
            file: scripts[i]
        });
    }
}

// Get all windows
chrome.windows.getAll({
    populate: true
}, function (windows) {
    var i = 0, w = windows.length, currentWindow;
    for( ; i < w; i++ ) {
        currentWindow = windows[i];
        var j = 0, t = currentWindow.tabs.length, currentTab;
        for( ; j < t; j++ ) {
            currentTab = currentWindow.tabs[j];
            // Skip chrome:// and https:// pages
            if( ! currentTab.url.match(/(chrome|https):\/\//gi) ) {
                injectIntoTab(currentTab);
            }
        }
    }
});

历史琐事

在古老的 Chrome 26 及更早版本中,内容脚本可以恢复与后台脚本的连接。它于 2013 年修复了 http://crbug.com/168263。您可以在本答案的早期修订版中看到此技巧的示例。


31
投票

强制注入内容脚本而不刷新页面的唯一方法是通过编程注入。

您可以使用 chrome tabs API 获取所有选项卡并将代码注入其中。 例如,您可以将清单版本存储在本地存储中,每次检查清单版本是否是旧版本(在后台页面中),如果是,您可以获取所有活动选项卡并以编程方式注入代码,或者任何其他解决方案将使您确保扩展已更新。

使用以下方式获取所有选项卡:
chrome.tabs.query

并将您的代码注入所有页面

chrome.tabs.executeScript(tabId, {file: "content_script.js"});


9
投票

在您的后台脚本中尝试此操作。许多旧方法现在已被弃用,因此我重构了代码。对于我的使用,我只安装单个 content_script 文件。如果需要你可以迭代

chrome.runtime.getManifest().content_scripts
用于获取所有 .js 文件的数组。

chrome.runtime.onInstalled.addListener(installScript);

function installScript(details){
    // console.log('Installing content script in all tabs.');
    let params = {
        currentWindow: true
    };
    chrome.tabs.query(params, function gotTabs(tabs){
        let contentjsFile = chrome.runtime.getManifest().content_scripts[0].js[0];
        for (let index = 0; index < tabs.length; index++) {
            chrome.tabs.executeScript(tabs[index].id, {
                file: contentjsFile
            },
            result => {
                const lastErr = chrome.runtime.lastError;
                if (lastErr) {
                    console.error('tab: ' + tabs[index].id + ' lastError: ' + JSON.stringify(lastErr));
                }
            })
        }
    });    
}

2
投票

Chrome 添加了监听扩展程序安装或升级事件的方法。当发生此类事件时,可以重新注入内容脚本。 https://developers.chrome.com/extensions/runtime#event-onInstalled


1
投票

由于 https://bugs.chromium.org/p/chromium/issues/detail?id=168263,您的内容脚本和后台脚本之间的连接被切断。正如其他人提到的,解决此问题的一种方法是重新注入内容脚本。 这个 StackOverflow 答案中有详细的粗略概述。

主要棘手的部分是,在注入新的内容脚本之前,有必要“破坏”当前的内容脚本。破坏可能非常棘手,因此减少必须破坏的状态量的一种方法是制作一个小的可重注入脚本,通过 DOM 与您的主要内容脚本对话。


0
投票

我试图做类似的事情,但就我而言,要求本身是错误的。
我以为我想在安装应用程序后立即向内容脚本传递消息,但实际上我想要的是在页面加载后传递消息。
在安装过程中,没有理由访问内容脚本。
为了实现这一目标,这就是我所做的:

  1. webNavigation
    添加到manifest.json 中的权限字段。
  2. 在后台脚本中监听 webNavigation.complete 事件:
chrome.webNavigation.onCompleted.addListener();
  1. 将消息从回调传递到内容脚本:
chrome.webNavigation.onCompleted.addListener(function () {
    chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
      // Send a message to the content script in the active tab to create the modal
      chrome.tabs.sendMessage(tabs[0].id, { message: "mymessage" });
    });
  },
  { url: [{ schemes: ["http", "https"] }] })
  1. 在内容脚本中添加监听器

-14
投票

你升级的css或js后面不能加

?ver=2.10
吗?

"content_scripts": [ {
      "css": [ "css/cs.css?ver=2.10" ],
      "js": [ "js/contentScript.js?ver=2.10" ],
      "matches": [ "http://*/*", "https://*/*" ],
      "run_at": "document_end"
   } ],
© www.soinside.com 2019 - 2024. All rights reserved.