Rozšíření pro Firefox nyní jednodušeji s Add-On SDK

Možnost přizpůsobit si webový prohlížeč pomocí doplňků je dnes prakticky standardem. S vlastností, která byla donedávna brána jako výhoda webových prohlížečů postavených na Mozille (např. Firefoxu), se dnes můžete běžně setkat v Google Chrome, Opeře a dalších prohlížečích. A protože ani v Mozille nespí, přichází s Firefoxem 4 nový způsob tvorby rozšíření, který si dnes představíme.

Balíček Add-on SDK, o kterém je řeč, je sada nástrojů a API, které se snaží vývoj rozšíření pro Firefox usnadnit. Někteří jej mohou znát pod dřívějším označením JetPack, který začal vznikat jako experiment v dílně Mozilla Labs před téměř dvěmi lety. S původním projektem však dnes již nemá téměř nic společného, tedy kromě původní ideje, že rozšíření by se mělo vytvářet pomocí standardních webových technologií jako HTML, CSS či JavaScript. K tvorbě rozšíření tak z velké části upotřebíte to, co již znáte.

Add-on SDK vs. tradiční rozšíření

Pakliže hovoříme o „tradičních“ rozšířeních pro Firefox, hovoříme o těch, které jsou vytvářeny pomocí existujících technologií tj. bez využití Add-on SDK. Ty bývají z velké části taktéž založené na webových technologiích, ale přidávají k nim některé „interní“, jako například XUL (pro popis uživatelského rozhraní), XBL apod. Případně je do nich možné zahrnout platformově závislý kód.

Dříve, než se rozhodneme, zda dát přednost Add-on SDK místo tradičního způsobu tvorby rozšíření, je dobré zmínit, co tím získáme (a co naopak ztratíme). Nejprve zmíníme výhody:

  • Pro tvorbu z velké části využijete to, co již znáte z webu (HTML, JavaScript, CSS, XML apod.).
  • Vytvořená rozšíření nevyžadují pro instalaci restart prohlížeče.
  • Bezpečný běh vzhledem ke zbytku prohlížeče.
  • Dopředná kompatibilita vytvořených rozšíření.
  • Rozšíření by bez úprav měly fungovat i v budoucí multiprocesové verzi Firefoxu.

Z výčtu je patrné, že rozšíření vytvořená pomocí Add-on SDK řeší některé problémy, s kterými se lze u tradičních rozšíření setkat. S doplňky, které přestanou v novější verzi Firefoxu fungovat z důvodu nekompatibility, se setkala celá řada lidí. Nutnost pro instalaci doplňku restartovat prohlížeč většinou nevadí, ale jsou situace, „kdy se to zrovna nehodí“. Že mohou být doplňky velkým zdrojem problémů, netřeba ani zmiňovat.

Poznámka: V rámci férovosti by stálo za to přiznat, že i tradiční rozšíření nemusí, počínaje Firefoxem 4, vyžadovat při instalaci restart aplikace. Nejedná se však o standardní vlastnost a autor rozšíření musí přistoupit k řadě kompromisů a o vše se starat sám.

Výhody jsme zmínili, ale existují též nevýhody? Jedna velká se nabízí rovnou: omezená sada dostupného API. Pokud jste někdy tvořili tradiční rozšíření pro Firefox (či o tom alespoň četli), pak jistě víte, že z hlediska tvorby jsou zde prakticky neomezené možnosti. Velké množství dostupného API a možnosti si „sáhnout“ na ledasco dává vzniknout řadě mocných doplňků, které by jinak nevznikly. Na druhou stranu díky tomu nelze zajistit dopřednou kompatibilitu a chyba v rozšíření může mít dost vážné následky na odezvu aplikace.

Mezi aktuální nevýhody lze zařadit i nekompatibilitu se staršími verzemi Firefoxu. Starší verze Add-on SDK podporovaly i Firefox 3.6, ale tato podpora již není dostupná. Dá se však říci, že tato nevýhoda je pouze dočasná.

Obecně platí jednoduché pravidlo: Pokud se rozhodnete vytvořit si rozšíření pro Firefox 4 a dostupné API nabízené Add-on SDK vám dostačuje, volte tento způsob. (Dostupné API se navíc neustále rozšiřuje, takže je Add-on SDK schopen pokrýt tvorbu stále většího typu rozšíření.) Pokud vám však přesto možnosti nedostačují, volte tradiční formu tvorby rozšíření.

Začínáme

Add-on SDK není přímou součástí Firefoxu 4. Jedná se o samostatný balíček (.zip), který si můžete stáhnout na webu Mozilla Labs. Nebojte se však, vytvořená rozšíření ho ke své funkčnosti nevyžadují. Nástroje Add-on SDK jsou napsána v Pythonu, takže ke své funkčnosti vyžadují nainstalovaný Python 2.5 či 2.6. K psaní doplňků však znalost Pythonu rozhodně nepotřebujete.

Ve staženém balíčku Add-on SDK naleznete:

  • JavaScriptové moduly poskytující API pro tvorbu rozšíření
  • Dokumentaci
  • Konzoli se sadou příkazů

Nejprve se podívejme na konzoli. Ta nabízí sadu nástrojů, které tvorbu rozšíření usnadňují.

Po rozbalení balíčku s Add-on SDK se do konzole dostanete pomocí spušení binactivate. Vyskočit z ní lze naopak zadáním příkazu deactivate. Základním příkazem konzole je cfx (jeho zadáním bez parametrů zobrazíte nápovědu), jehož jednotlivé parametry umožňují automatizovat některé úkony (např. vygenerovat kostru rozšíření). Zadáním cfx docs lze například zobrazit v prohlížeči dokumentaci. Tu je však pohodlnější prohlížet na webu, kde je vždy její aktuální podoba.

Celým Add-on SDK se prolíná koncept CommonJS, který u JavaScriptového kódu definuje pojmy modul a balíček. Modul je v pojetí CommonJS kus znovupoužitelného JavaScriptu. V něm je dostupný objekt exports, který obsahuje vše, co má být viditelné v rámci jiných modulů. Pokud chceme z modulu přistupovat v rámci jiného modulu, zpřístupní se pomocí funkce require. Nejlépe vše osvětlí následující obrázek:

Jednotlivé moduly jsou pak seskupovány do již zmíněných balíčků. API dostupné skrze Add-on SDK je členěné právě do modulů a balíčků. Na konkrétní rozšíření pak lze nahlížet jako na balíček, který se skládá z jednotlivých modulů.

První rozšíření

Přejdeme-li ve výše zmíněné konzoli do prázdného adresáře a spustíme příkaz cfx init, vygenerujeme kostru rozšíření. Ta se skládá z několika adresářů a souborů, které mají následující význam.

/data Adresář pro datové soubory rozšíření. Například obrázky.
/docs Adresář pro dokumentaci k rozšíření.
/lib Adresář pro moduly rozšíření.
/tests Adresář pro unit testy.
/package.json Soubor popisující rozšíření.

V jednotlivých adresářích naleznete soubory s ukázkovým kódem. My však nejprve nahlédneme do souboru s popisem rozšíření, který se jmenuje package.json. Jeho vygenerovaná podoba vypadá přibližně následovně (výchozí název rozšíření je generován na základě jména adresáře).

{
  "name": "hello-world",
  "fullName": "hello-world",
  "description": "a basic add-on",
  "author": "",
  "license": "MPL 1.1/GPL 2.0/LGPL 2.1",
  "version": "0.1"
}

Jednotlivé klíče jsou až na výjimky samopopisující a jejich přehled (včetně řady dalších dostupných atributů) lze nalézt v dokumentaci. Za zmínku stojí klíč name, který je jménem balíčku a měl by být dostatečně unikátní. Klíč fullName je samotný název rozšíření.

Pokud nahlédneme do adresáře lib, který obsahuje jednotlivé moduly rozšíření, nalezneme zde soubor main.js. Jedná se o modul, který je spuštěn při startu rozšíření (typicky při startu samotného Firefoxu) a je tak „vstupním bodem“ kódu rozšíření.

Vygenerované rozšíření si můžete vyzkoušet. Postačí, když v konzoli zadáte cfx run, čímž se spustí instance Firefoxu s nainstalovaným rozšířením.

Poznámka: Při prvním spuštění se do souboru package.json dogenerovává unikátní id rozšíření. To není při generování kostry rozšíření přednastaveno. Dogenerovaná podoba má podobu náhodných znaků a vlivem nějaké chyby se občas stane, že do řetězce dogeneruje neplatný znak, což se projeví následujícím chybovým hlášením.

UnicodeDecode­Error: ‚ascii‘ codec can’t decode byte 0×e8 in position 18: ordinal not in range(128)

Doporučuji uvedené id nastavit ručně ve formátu jmenorozsireni@do­mena.

Předgenerovaný kód rozšíření přidává na lištu doplňků tlačítko, které po klepnutí otevře webovou stránku Mozilla.org. Kód je přitom velice jednoduchý.

const widgets = require("widget");
const tabs = require("tabs");

var widget = widgets.Widget({
  label: "Mozilla website",
  contentURL: "http://www.mozilla.org/favicon.ico",
  onClick: function() {
    tabs.open("http://www.mozilla.org/");
  }
});

Ve výše uvedeném kódu jsou zpřístupněny moduly widget (pro přidání tlačítka na lištu) a tabs (pro práci s panely). U nového widgetu jsou následně nastaveny některé základní atributy a reakce na událost klepnutí.

Ačkoliv se jedná o velice jednoduché rozšíření, můžeme si pomocí příkazu cfx xpi vygenerovat jeho instalační balíček. Vytvořený doplněk má příponu xpi, tváří se tak navenek úplně stejně, jako tradiční rozšíření.

Dostupné moduly (API)

Ve výše uvedeném příkladu byly využity dva moduly (widget a tabs), které vývojáři zpřístupňují předdefinovanou sadu API. Vývojář si sice může napsat vlastní modul, ale daleko raději dá přednost předdefinovaným modulům, které jsou k dispozici. V rámci Add-on SDK naleznete vysokoúrovňové API (balíček addon-kit) a „nízkoúrovňové“ (balíček api-utils).

Protože se dostupné API neustále rozšiřuje, doporučuji vždy nahlédnout do dokumentace k aktuální verzi Add-on SDK. V rámci balíčku addon-kit jsou aktuálně dostupné následující moduly.

Jméno modulu Popis
clipboard Zpřístupňuje práci se schránkou.
context-menu Umožňuje manipulovat s místní (kontextovou) nabídkou.
notifications Zobrazování notifikací uživateli.
page-mod Umožňuje spouštět skripty v kontextu konkrétních stránek.
page-worker Umožňuje vytvářet skryté stránky a přistupovat k jejich DOMu.
panel Umožňuje vytvářet dialogy nad stránkami či GUI prohlížeče.
private-browsing Poskytuje informace o (ne)dostupnosti režimu anonymního prohlížení.
request Umožňuje vytvářet síťové požadavky.
selection Umožňuje přistupovat k aktuálně vybrané části stránky.
simple-storage Úložiště pro rozšíření.
tabs Přístup k panelům prohlížeče.
widget Umožňuje vytvářet tlačítka pro lištu doplňků.
windows Přístup k oknům prohlížeče.

„Nízkoúrovňový“ balíček api-utils je občas prezentován jako API pro tvorbu výsokoúrovňových modulů, které jsme si představili výše. Z praktického hlediska však nabízí některé moduly, které se dříve či později mohou hodit. Zmiňme tedy alespoň některé nejzajímavější.

Jméno modulu Popis
app-strings Přístup k lokalizačním řetězcům aplikace.
app-strings Přístup k lokalizačním řetězcům aplikace.
collection Práce s kolekcemi.
match-pattern Regulární výrazy.
preferences-service Přístup k předvolbám aplikace.
self Umožňuje přistupovat k datovým souborům připojeným k rozšíření.
xhr Přístup k XMLHttpRequest.

Vedle již zmíněného API je k dispozici objekt console pro ladění, který jistě řada z vás zná. Co naopak není dostupné, jsou některé novinky z HTML5. Např. localStorage.

Ukázky

Každé rozšíření je svým způsobem specifické. Existují však konkrétní operace, které se často opakují. Jednou z nich je například oznámení uživali, že něco proběhlo. Pokud uměle rozšíříme příklad s tlačítkem na liště, můžeme například uživateli oznámit, že na něj klepl (ano, jsem si vědom, že normálně bychom tak nečinili).

const widgets = require("widget");
const tabs = require("tabs");
const notifications = require("notifications");

var widget = widgets.Widget({
  label: "Webová stránka Mozilla.org",
  contentURL: "http://www.mozilla.org/favicon.ico",
  onClick: function() {
    tabs.open("http://www.mozilla.org/");
    notifications.notify({
      text: "Stránka Mozilla.org byla otevřena!",
      iconURL: "http://www.mozilla.org/favicon.ico"
    });
  }
});

Uživateli se v takovém případě zobrazí po klepnutí na tlačítko v pravém dolním rohu prohlížeče výsuvný informační panel. Není problém mu doplnit nadpis, případně lepší ikonku distribuován s rozšířením. Tu bychom si zpřístupnili skrze modul self.

Další zajímavou skupinou rozšíření, kterou lze vytvořit skrze Add-on SDK, jsou rozšíření, které modifikují obsah webové stránky při jejím načtení.

var pageMod = require("page-mod");

pageMod.PageMod({
  include: ["*.co.uk"],
  contentScriptWhen: 'ready',
  contentScript: 'document.body.innerHTML = ' +
                 '"<h1>Obsah je fuč :)</h1>";'
});

Výše uvedená ukázka u všek domén co.uk nahradí po načtení stránky jejich obsah za náš vlastní ukázkový obsah. Adresy určujeme regulárním výrazem a máme možnost si zvolit, kdy se má modifikace provést (již při načítání nebo až po načtení DOMu).

Hodit se může přístup ke schránce, který je velmi jednoduchý.

let clipboard = require("clipboard");
clipboard.set("Lorem ipsum dolor sit amet");
let contents = clipboard.get();

Následující příklad ukazuje, jakým způsobem lze s využitím modulu panel zobrazovat obsah z webu. Příklad na lištu doplňků přidá nové tlačítko, které po klepnutí stáhne a zobrazí obsah z webu Reddit.com. Příklad předpokládá v adresáři data přiloženou knihovnu jQuery a JavaScriptový soubor, který zobrazovaný panel nastyluje. Celý příklad lze nalézt v Add-on SDK v cestě examples/reddit-panel.

const widgets = require("widget");
const data = require("self").data;

exports.main = function(options, callbacks) {
  widgets.Widget({
    label: "Reddit",
    contentURL: "http://www.reddit.com/static/favicon.ico",
    panel: require("panel").Panel({
      width: 240,
      height: 320,
      contentURL: "http://www.reddit.com/.mobile?keep_extension=True",
      contentScriptFile: [data.url("jquery-1.4.4.min.js"),
                          data.url("panel.js")],
      contentScriptWhen: "ready",
      onMessage: function(message) {
        require("tabs").open(message);
      }
    })
  });

  if (options.staticArgs.quitWhenDone)
    callbacks.quit();
};

Na výše uvedeném příkladu může zaujmout export funkce main. Tento zápis není standardně potřeba tj. widget by šel vytvořit stejným způsobem jako již v jednom příkladu výše. Tato forma zápisu se hodí tehdy, když chceme například získat informaci, za jakých okolností byl doplněk načten. Bližší informace o vstupních atributech jsou dostupné v dokumentaci.

Přímo v dokumentaci k Add-on SDK se nachází i trochu „složitější“ příklad, který kombinuje hned několik modulů. Využívá totiž možnosti přidat položku do místní nabídky, přístup k requestu a aktuálnímu výběru na stránce. Ve výsledku tak tento kód umožní přeložit zvolený kus stránky do češtině skrze Google Translate API.

var contextMenu = require("context-menu");
var request = require("request");
var selection = require("selection");

exports.main = function(options, callbacks) {
  console.log(options.loadReason);

  // Vytvoření nové položky v místní nabídce
  var menuItem = contextMenu.Item({

    label: "Překlad výběru",

    // Položka v nabídce se zobrazí jen tehdy, když je něco vybráno.
    context: contextMenu.SelectionContext(),

    // Pošle se informace o zvoleném textu na stránce.
    contentScript: 'on("click", function () {' +
                   '  var text = window.getSelection().toString();' +
                   '  postMessage(text);' +
                   '});',

    // Zavolá se překlad skrze Google Translate API. Výsledek se poté nahradí na stránce za vybraný text.
    onMessage: function (text) {
      if (text.length == 0) {
        throw ("Text to translate must not be empty");
      }
      console.log("input: " + text)
      var req = request.Request({
        url: "http://ajax.googleapis.com/ajax/services/language/translate",
        content: {
          v: "1.0",
          q: text,
          langpair: "|cs"
        },
        onComplete: function (response) {
          translated = response.json.responseData.translatedText;
          console.log("output: " + translated)
          selection.text = translated;
        }
      });
      req.get();
    }
  });
};

exports.onUnload = function (reason) {
  console.log(reason);
};

Webový editor Add-on Builder

Až doposud jsme zmiňovali Add-on SDK v souvislosti s konzolí, kterou nabízí. Existuje však i další způsob tvorby rozšíření, který se vám může zalíbit. Je jím webový editor Add-on Builder, který vás nutnosti práce s Add-on SDK zbaví. Celý vývoj v takovém případě probíhá v rámci okna webového prohlížeče. Po instalaci podpůrného rozšíření do Firefoxu je též možné snadno testovat rozepsané rozšíření prostým stisknutím tlačítka v editoru, což je pohodlné a hlavně rychlé.

Pro práci s tímto editorem budete potřebovat účet na serveru Mozilla Add-ons.

Co Add-on SDK neumí aneb co není hotovo

Pokud jste již nahlédli na domovskou stránku projektu, jistě jste si povšimli, že projekt nese označení beta. Verze 1.0 by se měla objevit v průběhu jara a jak vývojáři uvádí, neznamená to, že v ní bude pro vývojáře dostupný rozsah poskytovaného API, jaký by si sami vývojáři Add-on SDK představovali. Aktuální rozsah nabízeného API tak může být pro některé vývojáře důvodem, proč Add-on SDK prozatím nezvolit. Aktuálně například není poskytováno API pro práci s hlavní nabídkou, záložkami či historií. Práce s hesly či klávesovými zkratkami se sice brzy objeví, ale taktéž ji v aktuální betaverzi nenajdete.

Zmiňovaná dopředná kompatibilita API je bezesporu výhodou Add-on SDK oproti rozšířením vytvářeným tradičním způsobem. Je však dobré zmínit, že ve vygenerovaném balíčku s rozšířením je aktuálně rozsah podporovaných verzí Firefoxu nastaven. Do budoucna bude patrně tato problematika nějakým způsobem řešena. Prozatím by mělo jít bez problémů pro jednou napsaný kód sestavit rozšíření pro budoucí verzi Firefoxu (skrze Add-on SDK, který bude danou verzi Firefoxu podporovat).

Kde shánět více informací

Pokud se rozhodnete do vývoje rozšíření pomocí Add-on SDK více ponořit, budete patrně nejvíce nahlížet do dokumentace, kterou jsme již zmínili. Vedle toho existuje několik dalších zdrojů, o kterých je dobré vědět.

(Některé ukázky v tomto článku vychází z oficiální dokumentace, která je šířena pod trojlicencí GPL/LGPL/MPL.)

Pavel Cvrček pracuje jako softwarový vývojář a v rámci projektu Mozilla.cz se podílí na vývoji a propagaci produktů Mozilla.

Věděli jste, že nám můžete zasílat zprávičky? (Jen pro přihlášené.)

Komentáře: 5

Přehled komentářů

jarda díky
Wooff Jak skrýt adresní řádek ?
danielsoft pěkný článek
shaddow add-on SDK options
Jogi Díky
Zdroj: https://www.zdrojak.cz/?p=3461