Reydel Leon
Reydel Leon

Reputation: 1075

How to get chrome.runtime.onInstalled to fire when using Require.JS in Chrome extension

I'm having problems getting some setup code executed when my extension is installed. I'm using chrome.runtime.onInstalled as suggested by Chrome Developers' page, but it is not firing. It seems that the issue is related with the use of Require.JS. This is my code:

requirejs.config({...});// i'll not reproduce the whole config here for brevity

// Define the Global Object that will hold the extension functionality
var EXT = EXT || {};

// Start the main app logic.

requirejs(['chrome.storage', 'chrome.settings', 'chrome.tabs'],
function (chromeStorage, chromeSettings, chromeTabs) {
    'use strict';

    var getLocalRepos;

    /**
     * Executes the callback with an array of repositories as the parameter.
     * @param {function} callback
     */
    getLocalRepos = function (callback) {
        'use strict';

        chromeStorage.get('localRepos', callback);
    };

    // Take care of the extension lifecycle.
    chrome.runtime.onInstalled.addListener(function (details) {
        "use strict";

        // Create dummy data to populate the local storage for testing purposes.
        var dummyData = [
            {
                name: 'repo1',
                description: 'description1',
                username: 'reydel',
                age: 'theAge'
            },
            {
                name: 'repo2',
                description: 'description2',
                username: 'reydel',
                age: 'theAge'
            }
        ];

        switch (details.reason) {
            case 'install':
                chromeStorage.set({ localRepos: dummyData }, function () {
                    // Do nothing
                });
                break;
        }
    });

    // Bind all functions to an object in the Global Space to make them accessible from the outside scripts
    // referencing the BackgroundPage object
    window.EXT.getLocalRepos = getLocalRepos;
});

I have used the code inside the listener's callback in the console and it works, it's just that the event is not being triggered.

Any ideas on how to solve this? Someone have done it before?

Upvotes: 4

Views: 2389

Answers (1)

Rob W
Rob W

Reputation: 348992

The chrome.runtime.onInstalled event is fired directly after loading your code. When you add an event listener asynchronously, then the onInstalled event has already been dispatched when you add the event listener.

To solve this issue, you have to make the initialization of your app synchronous. Unfortunately, require.js is designed to be an asynchronous script loader. Modifying this library to make it synchronous is possible, but a waste of efforts without any benefits.

To use require-style modules (AMD) in a Chrome extension, I strongly recommend to use an intermediate build step that combines the scripts (such as r.js or grunt-contrib-requirejs), and almond.js as script loader. Contrary to require.js, almond.js does support synchronous loading.

Here's an example of a config that you can use to build a single file called all.js that contains almond.js (as script loader), and uses mymain.js as entry point (main script):

({
    name: 'mymain',
    out: 'all.js',
    include: [
        'almond'
    ],

    skipModuleInsertion: true,
    wrap: {
        start: '(function(){',
        // true = load synchronously. This is a feature of almond.js
        end: 'require(["mymain"], null, null, true);})();'
    }
})

To avoid namespace pollution, I've wrapped the code in a n immediately-invoked function expression. If you want the modules to be available in the global namespace (e.g. for debugging), just remove the (function(){ and })();.

For documentation of these configuration options (and more), see https://github.com/jrburke/r.js/blob/master/build/example.build.js.


To try out the previous example:

  1. Install r.js: npm install -g requirejs
  2. Save the previous config as myfancyname.build.js
  3. Create a script called mymain.js: define(function() { ... });.
    (This is a simple module definition without dependencies. Read the require.js documentation if you want to learn more about other kind of modules, such as modules with dependencies).
  4. Run r.js to compile the file: r.js -o myfancyname.build.js
  5. Now you have a single file called all.js that can be loaded, e.g. by using "background": { "scripts": ["all.js"] } in the manifest file of your Chrome extension.

Upvotes: 6

Related Questions