Chrome Extension erstellen

In einem meiner letzten Projekte habe ich das Erstellen von Chrome Extension kennengelernt. Da dies ein sehr spannendes Thema ist, möchte ich euch hier ein kleine Einführung geben.

Für eine Vortrag hier bei Flavia habe ich mich bei dem Tutorial von code.tutsplus.com bedient und es leicht abgeändert. Ihr findet es unter https://github.com/eltuctuc/chrome-extension.

Um eine selbstentwickelte Extension auch in Goggle Chrome testen zu können, ist es notwendig den Entwicklermodus zu aktivieren. Dazu ruft man in der Adresszeile chrome://extension auf und setzt dann den Haken vor den Entwicklermodus. Danach kann man über den Button „Entpackte Erweiterung laden…“ eine Ordner aussuchen, der die HTML, CSS und JavaScript für die Extension beinhaltet. Ja richtig gelesen, es ist simple Frontend-Entwicklung 😉

Für die Interaktion zwischen der Extension und dem Browser steht eine umfangreiche API bereit. Einen guten Überblick über deren Möglichkeiten findet ihr unter https://developer.chrome.com/extensions; hilfreiche Beispiele sind hier.

ExtensionaufbauEine Chrome Extension kann Bestandteile für die verschiedene Bereiche das Browsers haben:

  • page action
  • browser action
  • content script
  • background script
  • developer tools

Das wird in einer manifest.json festgehalten. Dabei kommt es allerdings selten vor, dass eine Extension jeden Bereich nutzt.

Die grundlegende Aufbau der manifest.json sieht in etwas so aus:

{
  "name": "ChromeExtension",
  "version": "0.0.1",
  "manifest_version": 2,
  "description": "Description ...",
  "icons": {
    "16": "icons/16x16.png",
    "48": "icons/48x48.png",
    "128": "icons/128x128.png"
  }
}

Viele der Einträge erklären sich von selbst. Wichtig ist lediglich zu wissen, dass der „manifest_version“-Eintrag auf 2 gesetzt werden muss.

Background-Script

Ein Background-Script, welches im Hintergrund geladen wird und immer nach dem Starten des Browser bzw. der Extension zur Verfügung steht, wird wie folgt im Manifest definiert:

{
  ...
  "background": {
    "scripts": ["background.js"],
    "persistent": false
  },
  ...
}

Es dient als Kommunikationskanal zwischen den anderen Scripten. Wie man erkennen kann, wird im Background-Script ein Event Listener registriert. Dieser kann dann von den anderen Script aufgerufen und per „Request“ Werte mitgegeben werden. Das Background-Script wiederum kann per sendMessage() Werte an andere Scripte senden — hier an das geöffnete Tab im Browser, welches das Content-Script beinhaltet (siehe unten).

(function() {
    "use strict";

    /**
     * listening for an event / one-time requests
     * coming from the popup
     */
    chrome.extension.onMessage.addListener(function (request) {
        switch (request.type) {
            case "color-divs":
                colorDivs({
                    text: "blue!",
                    color: "#00F"
                });
                break;
        }
        return true;
    });

    /**
     * send a message to the content script
     * @param {Object} msg
     * @param {String} msg.text
     * @param {String} msg.color
     */
    var colorDivs = function (msg) {
        chrome.tabs.getSelected(null, function (tab) {
            chrome.tabs.sendMessage(tab.id, {type: "colors-div", color: msg.color});
        });
    };
})();

Zusätzlich kann im Background-Script auch auf Eingaben und Tabs innerhalb der Dev-Tools reagiert werden.

(function() {
    ...

    /**
     * listening for an event / long-lived connections
     * coming from devtools
     */
    chrome.extension.onConnect.addListener(function (port) {
        port.onMessage.addListener(function () {
            switch (port.name) {
                case "color-divs-port":
                    colorDivs({
                        text: "red!",
                        color: "#F00"
                    });
                    break;
            }
        });
    });
})();

Auch die Browserzeile oder „Omnibox“ kann abgefragt bzw. für Interaktionen genutzt werden. Dazu registriert man weitere Listener im Background-Script und leitet dann Aktionen an die anderen Scripte weiter. Der erste Listener im Beispiel schlägt beim Tippen den Eintrag „color-divs“ vor, der zweite reagiert, falls genau dieser Text eingegeben wird.

(function() {
    ...

    /**
     * omnibox
     */
    chrome.omnibox.onInputChanged.addListener(function (text, suggest) {
        suggest([{
            content: "color-divs",
            description: "Make everything green"
        }]);
    });

    chrome.omnibox.onInputEntered.addListener(function (text) {
        if (text === "color-divs") {
            colorDivs({
                text: "green!",
                color: "#0F0"
            });
        }
    });
})();

Content-Script

Wenn im Manifest ein Content-Script definiert ist, wird es in jeder angezeigt Website automatisch mit geladen und steht zur Verfügung. Durch die Eingabe von URL-Mustern wird das entsprechende Script nicht überall sondern nur in bestimmten Websites eingebunden. Denkbar wäre hier z.B. dass man während der Projektentwicklung kleine Extensions programmiert, die nur beim Zugriff auf localhost oder einen Test-Server geladen werden.

{
  ...

  "content_scripts": [{
    "matches": ["http://*/*", "https://*/*"],
    "js": ["content.js"]
  }]
}

Das Script im Beispiel sucht alle DIV-Containern und färbt sie für 2 Sekunden ein. Die Farbe wird vom Aufrufenden im Background-Script mitgeliefert — je nachdem welcher Event Listener im Background-Script aufgerufen wurde.

(function(){
    "use strict";

    chrome.extension.onMessage.addListener(function (message) {
        switch (message.type) {
            case "colors-div":
                var divs = document.querySelectorAll("div");
                divs = Array.prototype.slice.call(divs,0);
                if (divs.length === 0) {
                    window.alert("There are no any divs in the page.");
                } else {
                    divs.forEach(function(div){
                        div.style.backgroundColor = message.color;
                    });
                    setTimeout(function() {
                        divs.forEach(function(div){
                            div.removeAttribute('style');
                        });
                    }, 2000);
                }
                break;
        }
    });
})();

 

Browser Action

Eine häufige Form in der Chrome Extensions benutzt werden, ist sie als Icon neben der Omnibox zu platzieren. Um dies zu tun, wird das Manifest um folgendes erweitert:

{
  ...

  "browser_action": {
    "default_icon": {"19": "icons/19x19.png", "38": "icons/38x38.png"},
    "default_title": "FLAVIA IT",
    "default_popup": "browseraction/popup.html"
  }
}

In der Popup-HTML kann man ganz normales HTML, CSS und JavaScript einfügen, um so Buttons, Ein- und Ausgabefelder und andere Interaktionselemente zu platzieren bzw. zu programmieren.

<link href="popup.css" rel="stylesheet">
<script type="text/javascript" src="../libs/jquery/jquery.min.js"></script>
<script type="text/javascript" src="popup.js"></script>
<div style="width:200px">
    <h1>FLAVIA IT</h1>
    <button id="button">Color all the divs</button>
</div>
$(document).ready(function () {
    "use strict";

    $('button').on('click', function () {
        chrome.extension.sendMessage({
            type: 'color-divs'
        });
        window.close();
    });
});

Per JavaScript kann man dann eine Nachricht senden, die dann im Background-Script landet und dort die richtigen Werte an das Content-Script sendet. Um das aufgeklappte Icon  auch wieder zu schließen dient  window.close() .

Dev-Tools

Eine Extension kann auch auf die Dev-Tools von Chrome zugreifen und dort einen neuen Tab integrieren. Dazu erweitert man das Manifest um folgenden Code:

{
  ...

  "devtools_page": "devtools/devtools.html"
}

Die Devtools.html dient dabei lediglich für das Einbinden eines Scripts:

<script src="devtools.js"></script>

Und auch in der devtools.js selbst findet nicht viel statt. Sie erzeugt ein neues Tabs und definiert welche HTML-Datei als Inhalt geladen werden soll.

(function() {
    "use strict";

    chrome.devtools.panels.create(
        "ChromeExtension Tab",
        "icons/16x16.png",
        "devtools/devtoolscontent.html",
        function () {

        }
    );
})();

Die eigentliche Umsetzung des Tabs ist in der devtoolscontent.html verortet…

<link href="devtoolscontent.css" rel="stylesheet">
<script type="text/javascript" src="../libs/jquery/jquery.min.js"></script>
<script type="text/javascript" src="devtoolscontent.js"></script>
<div>
    <h1>FLAVIA IT</h1>
    <button id="button">Color all the divs</button>
</div>

… die wiederum jQuery und ein eigenes Script einbindet:

(function() {
    "use strict";

    $(document).ready(function () {
        var port = chrome.extension.connect({name: "color-divs-port"});

        $("button").on('click', function () {
            port.postMessage({type: "color-divs"});
        });
    });
})();

Das Script nutzt dann einen „Port“ worüber Daten an das Backend-Sript gesendet werden können.

Omnibox

Eine weitere nette Möglichkeit Zugriff auf die Extension zu nehmen, ist es sie direkt in der Omnibox aufzurufen. Dazu braucht das Manifest folgenden Eintrag:

{
  ...

  "omnibox": {"keyword": "ce"}
}

Dadurch wird definiert, dass wenn jemand das Schlüsselwort „ce“ in die Omnibox eingibt und dann ENTER drückt, die folgende Interaktion direkt an das Backend-Script der Extension geht. Dazu erweitert man das Background-Script um folgende Funktionen bzw. Event Listener.

(function() {
    ...

    /**
     * omnibox
     */
    chrome.omnibox.onInputChanged.addListener(function (text, suggest) {
        suggest([{
            content: "color-divs",
            description: "Make everything green"
        }]);
    });

    chrome.omnibox.onInputEntered.addListener(function (text) {
        if (text === "color-divs") {
            colorDivs({
                text: "green!",
                color: "#0F0"
            });
        }
    });
})();

Sie reagieren dann sowohl auf direkte Eingaben bzw. auf das Abschicken des eingegebenen Befehls „color-divs“.

Öffnen eines Tabs

Zum Schluss sei noch erwähnt, dass man im Manifest auch definieren kann, wie ein neues Tab im Bowser aussehen soll:

{
  ...

  "chrome_url_overrides": {"newtab": "newtab/newtab.html"}
}

So kann man nun in der newtab.html das Aussehen und ggf. Links zu wichtigen Ressourcen angeben:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>FLAVIA IT &ndash; Chrome Extension Tab</title>
    <style type="text/css">
        body, html {
            margin: 0;
            padding: 0;
            width: 100%;
            height: 100%;
            font-size: 18px;
            font-family: Georgia, serif;
        }

        h1 {
            position: absolute;
            width: 400px;
            height: 100px;
            color: #A4A4A4;
            font-weight: normal;
            left: 50%;
            top: 50%;
            margin: -50px 0 0 -200px;
            text-align: center;
        }
    </style>
</head>
<body>
    <h1>Yeah, that's my awesome new tab page!</h1>
</body>
</html>

Fazit

Mit diesem Artikel ist noch lange nicht das Ende der Fahnenstange erreicht, was mit Chrome Extensions alles machbar ist. Wenn man erstmal die Kommunikations zwischen den verschiedenen Bestandteilen einer Extension verstanden hat, kann man recht viel Magie in ein kleines Icon rechts oben im Browser stecken.

So ist es denkbar kleine Analyse-Programme für ein Projekt zu entwicklen, die immer dann sich melden wenn etwas in der angezeigten Website nicht stimmt. Oder man visualiesiert einen bestimmten Inhalt der gezeigt Website (siehe AngularJS Batarang). Oder mach erschafft kleine Hilfsprogramme die die Usability einer Website verbessern (siehe Microformats) oder andere Hilfen anbieten (siehe ColorZilla).

Für mich war es auf jeden Fall eine Bereicherung mit diesem Thema in Berührung gekommen zu sein.

Teilen Sie diesen Beitrag

Das könnte dich auch interessieren …

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert