2

I want to integrate a Timeline in a chrome extension of mine. I already downloaded the js files and css files of this addon and put them in the root directory of my chrome extension.

The chrome extension itself is just the injection of a JS to an existing page and modifying the html code. However when I try to use the library I will get the error message:

Uncaught ReferenceError: vis is not defined
    at addTimeLine (ui.js:230)
    at createClientBox (ui.js:270)
    at ui.js:62

Here is my manifest.json:

  "content_scripts": [{
    "matches": ["https://web.whatsapp.com/*"],
    "css":["vis.min.css"],
    "js": ["content.js","jquery-3.2.1.min.js","vis.min.js"],
    "run_at": "document_end"
  }],
  "permissions": [
    "activeTab"
  ],

Here the content.js:

function injectJs(link) {
        var scr = document.createElement("script");
        scr.type="text/javascript";
        scr.src=link;
        (document.head || document.body || document.documentElement).appendChild(scr);
}

//injectJs(chrome.extension.getURL("/vis.min.js"));
injectJs(chrome.extension.getURL("/ui.js"));

After the content.js is executed the ui.js is successfully injected to the page and I see my added button on the page. Now when I want to use the library like this:

    var addTimeLine = function () {
        var visualization = document.createElement('div');
        visualization.id = 'visualization';
        // Get the conatiner where the timeline should be displayed
        var container = document.getElementById('visualization');

        // Create a DataSet (allows two way data-binding)
        var items = new vis.DataSet([{
                id: 1,
                content: 'item 1',
                start: '2013-04-20'
            },
            {
                id: 2,
                content: 'item 2',
                start: '2013-04-14'
            },
            {
                id: 3,
                content: 'item 3',
                start: '2013-04-18'
            },
            {
                id: 4,
                content: 'item 4',
                start: '2013-04-16',
                end: '2013-04-19'
            },
            {
                id: 5,
                content: 'item 5',
                start: '2013-04-25'
            },
            {
                id: 6,
                content: 'item 6',
                start: '2013-04-27'
            }
        ]);

        // Configuration for the Timeline
        var options = {};

        // Create a Timeline
        var timeline = new vis.Timeline(container, items, options);
    };

    addTimeLine();

I will get the above error message.

What point am I missing to successfully use third party libraries in a chrome extension?

Nico
  • 1,727
  • 1
  • 24
  • 42

1 Answers1

2

You're missing the point of isolated contexts and how it interacts with dynamic <script> elements.

Each page contains a DOM structure and its own JavaScript execution context, the "page" context.

When an extension is adding a content script to a page, it creates a separate JavaScript context, attached to the same DOM. The two contexts don't see each other: if you have a variable in one, it's not present in the other. This includes global objects such as window: each context gets its own copy.

The main reason for this isolation are:

  • Security: the page can't interfere with a higher-privilege content script context and can't detect its presence directly.
  • Convenience: the page context and the extension can use different incompatible libraries; a page context can use any names without clashes; etc.

Your code creates a content script context and adds content.js, jquery-3.2.1.min.js and vis.min.js to it (in that specific order, which may be a problem in itself if content.js expects any of the libraries).

But what happens next is that you create a new DOM script node and add your script there. This gets executed in the page context instead.

So, in your situation, ui.js loads somewhere that vis.min.js isn't loaded, leading to the error; likewise, if you try to use ui.js from the content script context, it won't be loaded there.

If the problem you were originally trying to solve was dynamic content script injection, you have 2 options:

  1. Employ a background page, which can execute chrome.tabs.executeScript on demand from content.js - this adds to the same context.
  2. (Nuclear option) Get contents of your script and eval() them, which is allowed in content scripts (but may cause problems with review when publishing).

If you need to access data from the page context, you will need to cross the boundary, but you need to be aware about it and how to pass messages through it.

Xan
  • 74,770
  • 16
  • 179
  • 206
  • I really appreciate the time and effort you made to post this answer. So I guess using Option 1 should be my way to go. I am modifying and expanding an existing [PoC](https://www.lorankloeze.nl/2017/05/07/collecting-huge-amounts-of-data-with-whatsapp/) for the page "web.whatsapp.com" and my goal is to add a new button on the page, collect some data and publish it as a file/graph to download locally. So if I understood correctly using Option 1 should make all this possible right? – Nico Dec 13 '17 at 12:48
  • It depends. If the data is simply visible in the DOM (e.g. you can extract it with jQuery), then yes. If the data you need "lives" in the JavaScript context of the page, you'll need some injection into that. – Xan Dec 13 '17 at 14:04
  • When understanding the PoC correctly the data lives in an undocumented API that I can use in my JavaScript when injected to the page. That's why a DOM script node is created with ui.js I think. – Nico Dec 13 '17 at 14:19