Adding complex HTML using a Chrome content script

2019-01-16 12:21发布

问题:

I am working with Chrome extension's content script to create a complex display that is added on web pages.

I have first tested it directly integrated on a website, but now I need to put it in an extension.

The thing is that the content script API for Chrome only allows to inject javascript. That means that, to inject complex HTML layouts I would need to write it entirely with JS objects, which is long to write, hard to maintain and absolutely not designer-friendly.

I'm wondering if anyone know or can think of a clever way to get a better workflow on this.

回答1:

It's relatively easy to add whole web pages by having your content script inject them in an iframe. Just follow these guidelines:

  1. Place the *.htm or *.html files in your extension's source folder(s).

  2. Place any *.css and *.js files, that the HTML uses, in the extension folder(s) too.

  3. Declare the HTML file(s) as resources. EG:

    "web_accessible_resources": ["Embedded_Hello_world.htm"]
    


  4. Do not use any inline, or external server, javascript in your HTML files. This avoids problems with the Content Security Policy (CSP).

  5. This question doesn't cover communicating with the page/iframe, but if you want to do that, it is a bit more involved. Search here on SO; it's been covered many times.


Example:

You can see this in action by:

  1. Creating a new extension folder.
  2. Download jQuery into it.
  3. Create the 5 files as specified below.
  4. Load the unpacked extension (You can see similar steps in this answer.)
  5. Reload this page in Chrome; you'll see the "Hello World" page, embedded at the top.

Create these files in the extension folder:

manifest.json:

{
    "manifest_version":         2,
    "content_scripts":          [ {
        "js":       [ "iframeInjector.js" ],
        "matches":  [   "https://stackoverflow.com/questions/*"
        ]
    } ],
    "description":              "Inject a complete, premade web page",
    "name":                     "Inject whole web page",
    "version":                  "1",
    "web_accessible_resources": ["Embedded_Hello_world.htm"]
}


iframeInjector.js:

var iFrame  = document.createElement ("iframe");
iFrame.src  = chrome.extension.getURL ("Embedded_Hello_world.htm");

document.body.insertBefore (iFrame, document.body.firstChild);


Embedded_Hello_world.htm:

<!DOCTYPE html>
<html><head>
    <title>Embedded Hello World</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">

    <link href="HelloWorld.css" rel="stylesheet" type="text/css">

    <script type="text/javascript" src="jquery.min.js"></script>
    <script type="text/javascript" src="HelloWorld.js"></script>
</head><body>
<p>Hello World!</p>
</body></html>


HelloWorld.css:

body {
    color:              red;
    background-color:   lightgreen;
}


HelloWorld.js:

$(document).ready (jQueryMain);

function jQueryMain () {
    $("body").append ('<p>Added by jQuery</p>');
}


回答2:

I had the same issue, that my extension heavily relies on script templates

Here's what I did:

  • create templates.html to store script templates in
  • add templates.html to the web_accessible_resources as in the the above answer^^
  • access templates.html from content.js with xhr and parse with jQuery

manifest.json

"web_accessible_resources": ["templates.html"]

templates.html

<script id="template1" type="text/template">
    <div class="template1">template1</div>
</script>
<script id="template2" type="text/template">
    <div class="template2">template2</div>
</script>

content.js

function getTemplates(){
    return new Promise(function(resolve){
        $.ajax({
            url: chrome.extension.getURL('/templates.html'),
            success: function(data) {
                var $templates = $('<div></div>').append($.parseHTML(data)).find('script'),
                    templates = {};
                $templates.each(function(){
                    templates[this.id] = this.innerHTML;
                });
                return resolve(templates);
            }
        });
    });
}
getTemplates().then(function(templates){
    console.log(templates.template1); //<div class="template1">template1</div>
});


回答3:

This may be better, no external library and no iframe. Is nearly the same as iautomation solution.

var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
        var div = document.createElement('div');
        div.innerHTML = this.responseText;
        document.body.insertBefore(div, document.body.firstChild);
    } else {
        console.log('files not found');
    }
};
xhttp.open("GET", chrome.extension.getURL("/content.htm"), true);
xhttp.send();