IT Man
IT Man

Reputation: 1036

Accessing background script object from content script

How to access background script objects form a content script inside chrome extension?

In content script I have:

    // this will store settings
    var settings = {};

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

Inside the background script I have:

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

Messaging works, i mean response is send but returned object is empty. Do you know how to solve that?

EDIT Based on your comments and answer will try to add different light to my question.

The actual problem is: How to access background "model" from content script.

Lets assume that content script continuously responds to page DOM changes. Any time changes are detected some processing is made inside content script. But this processing is depended on extension setting. Those setting can be set via page action popup script which informs background model what those settings are.

So, any time page is processed with content script it should be aware of current settings stored inside background script.

As already described pulling settings from background is an asynchronous process, so i need a callback for further processing inside content script. Further processing must wait for settings (so this should be handled synchronously?).

It's hard for my to imagine what program flow should look like in this case.

  1. background loads (setting initialized)
  2. page loads -> content script loads
  3. content script requests settings -> further processing is done inside callback function.
  4. user changes setting, background settings are changed
  5. page change is triggered and content script responds
  6. content script requests settings -> further processing is done inside callback function - but it cannot be the same function like in pt. 3 (content "model" does not have to be initialized)?

Upvotes: 0

Views: 2359

Answers (2)

Xan
Xan

Reputation: 77523

It would probably make more sense to have another instance of Settings in your content script.

After all, chrome.storage API is available in content scripts.

Of course, you need to watch for changes made in other parts of the extension - but you should be doing so anyway, since you're using chrome.storage.sync and its value can change independently by Chrome Sync.

So, proposed solution:

  1. Add a listener to chrome.storage.onChanged and process changes to update your settingList as needed.
  2. Move the Storage logic to a separate JS "library", e.g. storage.js
  3. Load storage.js in your content script and use it normally.

You may also want to adjust your storage logic so that saved data is actually taken into account - right now it's always the default. You can do something like this:

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

Upvotes: 0

MobDev
MobDev

Reputation: 1632

  1. sendMessage doesn't transfer the object itself, but only its JSON-ifiable representation, effectively objReceived = JSON.parse(JSON.stringify(objSent)), so since your object's settingList is invisible outside function context it's lost during serialization.

    You can make it public by exposing a stringifiable property

    this.settingList = { foo: 'bar' };
    

    that would be transferred to your content script successfully.

  2. Since messaging is asynchronous, to use the response in the content script you should do it inside the response callback:

    // 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. To know if settings changed outside your content-script, in settings setter in background.js send messages to your tab's 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. And in content-script listen and process this message:

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

More details: https://developer.chrome.com/extensions/runtime#method-sendMessage.

P.S. Use chrome.runtime.sendMessage instead of chrome.extension.sendMessage as the latter is deprecated in Chrome API and totally unsupported in WebExtensions API (Firefox/Edge).

Upvotes: 3

Related Questions