0

My extension is intended to inject some HTML elements using local JavaScript libraries such PrimeUI, jQueryUI and its correspondent CSS stylesheets.

The best way I have found to do so, is to inject the HTML as follows:

// test.js
$.get(chrome.extension.getURL('/overlay.html'), function(data) {
    $($.parseHTML(data)).appendTo('body');
}
// manifest.json
{
"name": "Test",
"version": "1.0",
"manifest_version": 2,
"options_page": "",
"description": "Test",
"browser_action": {
  "default_title": "Test",
  "default_popup": ""
},
"content_scripts": [
{
  "matches": [
    "<all_urls>"
  ],
  "js": [
    "js/jquery-3.1.1.min.js",
    "js/test.js"
  ]
}
],
"permissions": [
  "tabs"
],
"web_accessible_resources": [
  "overlay.html",

  "css/font-awesome.min.css",
  "css/jquery-ui.min.css",
  "css/primeui-all.min.css",

  "js/jquery-3.1.1.min.js",
  "js/jquery-ui.min.js",
  "js/primeelements.min.js",
  "js/primeui-all.min.js",
  "js/main.js"
]
}

I have the required JavaScript libraries inside "js" folder in the root directory, for security reasons I do not retrieve them from a CDN.

My HTML contains CSS and JS on <head> section, but the styles nor the scripts applies in any way.

<html>
<head>
    <link rel="stylesheet" href="css/font-awesome.min.css"/>
    <link rel="stylesheet" href="css/jquery-ui.min.css"/>
    <link rel="stylesheet" href="css/primeui-all.min.css"/>

    <script type="text/javascript" src="js/jquery-3.1.1.min.js"></script>
    <script type="text/javascript" src="js/jquery-ui.min.js"></script>
    <script type="text/javascript" src="js/primeui-all.min.js"></script>
    <script type="text/javascript" src="js/x-tag-core.min.js"></script>
    <script type="text/javascript" src="js/primeelements.min.js"></script>
</head>
<body>
    <button id="btn-show" type="button">Show</button>
    <div id="dlg" title="Godfather I">
        <p>The story begins as Don Vito Corleone, ...</p>
    </div>

</body>
</html>
// main.js
$(function() {
$('#dlg').puidialog({
    showEffect: 'fade',
    hideEffect: 'fade',
    minimizable: true,
    maximizable: true,
    responsive: true,
    minWidth: 200,
    modal: true,
    buttons: [{
        text: 'Yes',
        icon: 'fa-check',
        click: function() {
            $('#dlg').puidialog('hide');
        }
    },
        {
            text: 'No',
            icon: 'fa-close',
            click: function() {
                $('#dlg').puidialog('hide');
            }
        }
    ]
});
$('#btn-show').puibutton({
    icon: 'fa-external-link-square',
    click: function() {
        $('#dlg').puidialog('show');
    }
});
}

The button is successfully displayed but with no events are attached nor styles.

i.e: if I inject this code on StackOverflow my button get the styles from SO and not mine.

I want to avoid as much as possible JavaScript for creating elements, or injecting tags programmatically, to separate the View from the Controller.

How can I manage to use my styles and scripts with my injected HTML as we do ordinarily? I have tried adding them to web_accessible_resources, and other manifest fields unsuccessfully.

Community
  • 1
  • 1
another
  • 3,440
  • 4
  • 27
  • 34
  • Please [edit] the question to be on-topic: include a **complete** [mcve] that duplicates the problem. Including a *manifest.json*, some of the background *and* content scripts. Questions seeking debugging help ("**why isn't this code working?**") must include: ►the desired behavior, ►a specific problem or error *and* ►the shortest code necessary to reproduce it **in the question itself**. Questions without a clear problem statement are not useful to other readers. See: "**How to create a [mcve]**", [What topics can I ask about here?](http://stackoverflow.com/help/on-topic), and [ask]. – Makyen Dec 05 '16 at 22:18
  • @Makyen I would love I could make an executable from this, but only providing the code still is not "complete". I hope this improves the question. Thanks. – another Dec 05 '16 at 22:36
  • It does improve the question. What does *main.js* have to do with anything (it is not otherwise referenced, except in `web_accessible_resources`)? Why are you parsing this HTML (including `` and ` – Makyen Dec 05 '16 at 22:39
  • `main.js` only controls `PrimeUI` elements to be properly displayed (basic snippet). I have only seen people injecting code through Chrome JavaScript API and I was trying to avoid it. But thank you for clarifying that it is the only way. Although I was unable to find the way to do it with JS. I have read the main Chrome Documentation, but in my opinion I find it unclear and disjointed information. – another Dec 05 '16 at 22:44
  • 1
    For security reasons (basic part of the specifications), you can not insert a ` – Makyen Dec 05 '16 at 22:46
  • Trying to insert large amounts of code/libraries as into the page context using ` – Makyen Dec 05 '16 at 22:50
  • The reason is I was unable to interact with the HTML elements, simply inserting it as `content_scripts`. I have no real preference. But I take note about the cares to take. Thanks. – another Dec 05 '16 at 22:52
  • 1
    If you do insert `` and ` – Makyen Dec 05 '16 at 22:57
  • Well, that explains the flood of errors I did see on the console log before, regarding the paths (and forgot to mention). I take note about the cares to take. Thanks. What may I miss if is that simple as inserting the scripts and styles as `content_scripts`? I will come with feedback after trying all the new information. Maybe if I remove the errors firstly, it may works. – another Dec 05 '16 at 23:01
  • 1
    There are things that don't exist well as content scripts. I have not tried primeUI as a content script. I would work on all of this as content scripts/CSS first rather than inserting ` – Makyen Dec 05 '16 at 23:12
  • 1
    While this may sound a bit contrary to my earlier comments, *Please* do not load jQuery and all of those scripts into `` unless you **have** to. That is a *lot* of code with which to burden *every single page* (what of those of us who have hundreds of tabs open?). It is possible you really *need* to load all of it into every page. However, even if your UI interaction is starting from within the page (one reason to use a *manifest.json* content script), try to architect the extension so only the minimum is loaded until the user begins interaction with your UI, then inject the rest. – Makyen Dec 05 '16 at 23:25
  • Above you said "not in the page context", but I do not know what do you mean. I will continue tomorrow, I think you have provided the answer correctly, so I would like to accept your answer. If you are unsure after this long chat, I can edit your answer tomorrow if necessary. Thanks and regards. – another Dec 05 '16 at 23:28
  • 1
    I would suggest that you read the [Chrome extension architecture overview](https://developer.chrome.com/extensions/overview#arch). It has overall architecture information which should help your understanding of how things are generally done/organized. – Makyen Dec 05 '16 at 23:40
  • 1
    With respect to page context vs content script context: The page context is the context which contains scripts loaded with the webpage. This is separate (with some sharing: e.g. the DOM) from the context into which content scripts are injected. Content scripts can [inject content (e.g. scripts) into the page context by adding elements to the DOM (e.g. ` – Makyen Dec 05 '16 at 23:41
  • I do not need to call any already existing JS methods within the page. I only need to style the HTML element I have injected. If I insert all the CSS and JS files just as `content_script`, it doesn not modify the injected HTML. That is why I am inserting `script` and `link` tags directly on page context. But also to be able to use `!important` CSS directive. Even though, if I insert jQuery as `content_script` I can use it in other files inserted as `content_script`, but not to modify the inserted HTML. – another Dec 06 '16 at 22:46
  • 1
    It seams like it might be a good idea to [ask a separate question](http://stackoverflow.com/questions/ask) with code along the lines of what you did originally (all the code injected as content scripts) and the CSS not actually styling the appropriate elements. If it is something that is specific to the libraries you were using, it could be quite beneficial to people using those libraries in the future to have a question that directly deals with using those libraries and (hopefully) has solutions which you, and they, could use. – Makyen Dec 07 '16 at 05:08
  • I have read the architecture and now I have more clear what I am doing and what I can not do. I am injecting both HTML and JS via `content_script`. And after fixing many console errors, now I can check the network inspector, and finally see I have successfully injected all the files, but the HTML is not being styled. The way I do this is appending `link` and `script` childs to the `body` tag. On the other hand, I have appended the `HTML` to the body. All the resources are listed inside `"web_accessible_resources"` – another Dec 09 '16 at 18:17
  • I have noticed that refreshing the website I am injecting the code (i.e: duckduckgo.com) 1 per 10 times the code is styled and functions works. but the 9 remaining times when I refresh it does not work. Shall I ask a new question? I have answered the question with the main problem solved. You can post your answer if you want, I will accept it. Thank you @Makyen – another Dec 10 '16 at 12:24
  • I have realized my injections are asynchronous and randomly injected, so at the time it should detect jQuery, it misses the library. I can not find how to inject in sequence the libraries. I am using the answer's injection. – another Dec 10 '16 at 16:02
  • 1
    Given that you are already using jQuery, you could use `$(document.body).append(data);` instead of `$($.parseHTML(data)).appendTo('body');`. This would not fix the problem of the URLs needing to be fully qualified URLs into your extension, but would insert active ` – Makyen Dec 10 '16 at 17:16
  • 1
    Note that doing it that way does not give you references to the ` – Makyen Dec 10 '16 at 17:17
  • 1
    In answer to your earlier question: Yes, what you describe would be a [new question](http://stackoverflow.com/questions/ask). Although, I would first try not removing the scripts as soon as they are inserted (as I mentioned in my comment on your Answer). – Makyen Dec 10 '16 at 17:32
  • 1
    Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/130305/discussion-between-makyen-and-roizpi). – Makyen Dec 10 '16 at 17:32
  • I have opened a [new question](http://stackoverflow.com/questions/41087173/how-to-inject-files-in-a-queue-chrome-extension). Is there a reason to append it to the `body` element instead of the `head`? And what do you mean by "active script and link elements"? Cheers. – another Dec 11 '16 at 14:35
  • 1
    "Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/130305/discussion-between-makyen-and-roizpi)" is a suggestion from the other person that this set of comments has become more of a discussion which should be done in chat rather than strictly focused on improving this question. If there is more that you want to cover here, then we really should continue this [in chat](http://chat.stackoverflow.com/rooms/130305/discussion-between-makyen-and-roizpi). I have answered your questions [there](http://chat.stackoverflow.com/rooms/130305/discussion-between-makyen-and-roizpi) – Makyen Dec 11 '16 at 16:44

1 Answers1

0

Makyen explained:

It is not possible to parse HTML with <head> elements hardcoded such <script> and <link>. Instead it should be injected in other way.

The ways I have chosen to inject the files are:

For the CSS:

// Test.js

/** CSS Injection */
var link = document.createElement("link");
link.href = chrome.extension.getURL("css/jquery-ui.min.css");
link.type = "text/css";
link.rel = "stylesheet";
document.getElementsByTagName("head")[0].appendChild(link);

For the JS:

// Test.js

/** JavaScript Injection */
var s = document.createElement('script');
s.src = chrome.extension.getURL('js/jquery-3.1.1.min.js');
// "When an inline script is inserted in the document, it's immediately executed 
// and the <script> tag can safely be removed". – Rob W.
s.onload = s.remove;
(document.head || document.documentElement).appendChild(s);

Trying to insert large amounts of code/libraries as into the page context using tags has a significant chance of interfering with the current contents/JavaScript of the page. Is there a reason that you are inserting scripts into the page context? Normally, you would have these inserted scripts (and CSS) as content scripts (manifest.json content_scripts, or chrome.tabs.executeScript()/chrome.tabs.insertCSS(). not in the page context. – Makyen

With the above code I have successfully injected the files, but the styles and functions are applied only sometimes, I will edit when I fix this.

Community
  • 1
  • 1
another
  • 3,440
  • 4
  • 27
  • 34
  • 1
    Immediately removing libraries is not a good idea. The point of a library is that it is still around an available for your code to use it later. Rob W.'s comments/suggestion for immediately removing ` – Makyen Dec 10 '16 at 16:19