从内容脚本访问后台脚本对象

Accessing background script object from content script

本文关键字:脚本 后台 对象 访问      更新时间:2023-09-26

如何在chrome扩展内访问背景脚本对象形成内容脚本?

在内容脚本我有:

    // this will store settings
    var settings = {};
    // load settings from background
    chrome.extension.sendMessage({
        name: "get-settings"
    }, function(response) {
        debugger;
        settings = response.data.settings;
    }); 

在背景脚本中,我有:

    var Settings = function() {
    var me = this;
    // internal, default
    var settingList = {
        serverUrl : "http://automatyka-pl.p4",
        isRecordingEnabled : true,
        isScanEnabled : true
    };
    this.get = function( key ) {
        return settingList[key];
    };
    this.set = function( key , value ) {
        if (settingList[key] != value) {
            var setting = {};
            setting[key] = value;
            chrome.storage.sync.set(setting, function() {
                settingList[key] = value;
            });
        }
        return true;
    };
chrome.extension.onMessage.addListener(function(request, sender, sendResponse) {
    if (request.name == 'get-settings') {
        sendResponse({
            data : {
                settings : settings
            }
        });
        return true;
    }
});
var settings = new Settings();

消息传递工作,我的意思是响应是发送,但返回的对象是空的。你知道怎么解吗?

编辑根据你的评论和回答,我将尝试为我的问题增添不同的亮点。

实际问题是:如何从内容脚本中访问后台"model".

让我们假设内容脚本持续响应页面DOM更改。每当检测到更改时,都会在内容脚本中进行一些处理。但是这种处理取决于扩展设置。这些设置可以通过动作弹出脚本来设置,该脚本通知背景模型这些设置是什么。

所以,任何时候用内容脚本处理页面,它应该知道背景脚本中存储的当前设置。

如前所述,从后台提取设置是一个异步过程,所以我需要一个回调来进一步处理内容脚本。进一步的处理必须等待设置(所以应该同步处理?)。

我很难想象在这种情况下程序流应该是什么样子。

  1. 后台负载(设置初始化)
  2. 页面加载->内容脚本加载
  3. 内容脚本请求设置->进一步的处理在回调函数中完成。
  4. 用户更改设置,后台设置更改
  5. 触发
  6. 页面更改,内容脚本响应
  7. 内容脚本请求设置->进一步的处理是在回调函数内完成-但它不能是相同的功能,如在pt. 3(内容"模型"不必初始化)?
  1. sendMessage不传输对象本身,而只传输对象的json表示。实际上是objReceived = JSON.parse(JSON.stringify(objSent)),所以因为对象的settingList在函数上下文之外是不可见的,它在序列化期间丢失了。

    你可以通过暴露一个可字符串化的属性

    使它成为公共的
    this.settingList = { foo: 'bar' };
    
  2. 由于消息传递是异步的,要在内容脚本中使用响应,您应该在响应回调中执行:

    // this will store the settings
    var settings = {};
    // load settings from background
    chrome.runtime.sendMessage({
        name: "get-settings"
    }, function(response) {
        settings = response.data.settings;
        onSettingsReady();
    }); 
    function onSettingsReady() {
       // put your logic here, settings are set at this point
    }
    
  3. 要知道设置是否在你的content-script之外被更改,在background.js中的settings setter中发送消息到你的选项卡的content-script:

    this.set = function( key , value ) {
        ...
        // notify active tab if settings changed
        chrome.tabs.query({"windowType":"normal"}, function(tabs){
        for( id in tabs ){
            if("id" in tabs[id]){
                chrome.tabs.sendMessage(tabs[id].id,{"action":"update-settings","settings":settings});
            }
        }
    });
        return true;
    };
    
  4. 在content-script中监听并处理以下消息:

    chrome.runtime.onMessage.addListener(function(msg){
        if("action" in msg && msg.action == 'update-settings'){
            // You are setting global settings variable, so on it will be visible in another functions too
            settings = msg.settings;
        }
    });
    

详细信息:https://developer.chrome.com/extensions/runtime#method-sendMessage.

注:使用chrome.runtime.sendMessage代替chrome.extension.sendMessage,因为后者在Chrome API中已弃用,并且在webeextensions API (Firefox/Edge)中完全不支持。

在您的内容脚本中添加另一个Settings实例可能会更有意义。

毕竟chrome.storage API在内容脚本中是可用的。

当然,你需要观察扩展的其他部分所做的变化-但你应该这样做,因为你使用chrome.storage.sync和它的值可以通过Chrome同步独立更改。

所以,建议的解决方案:

  1. chrome.storage.onChanged添加一个监听器,并根据需要处理settingList的更改。
  2. Storage逻辑移动到单独的JS"库"中,例如storage.js
  3. 在你的内容脚本中加载storage.js并正常使用。

你可能还想调整你的存储逻辑,这样保存的数据实际上是考虑在内-现在它总是默认的。你可以这样做:

var defaultSettingList = {
    serverUrl : "http://automatyka-pl.p4",
    isRecordingEnabled : true,
    isScanEnabled : true
};
var settingList = Object.assign({}, defaultSettingList);
chrome.storage.sync.get(defaultSettingList, function(data) {
  settingList = Object.assign(settingList, data);
  // At this point you probably should call the "ready" callback - initial
  //   load has to be async, no way around it
});