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.
Nálepky:
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.
UnicodeDecodeError: ‚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@domena.
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.
- Často kladené otázky k Add-on SDK
- Blog Mozilla Add-ons – publikují se zde informace o novinách ve vývoji
- Diskusní skupina k Add-on SDK
- IRC kanál #jetpack
- Diskusní fórum na serveru Mozilla Add-ons
(Některé ukázky v tomto článku vychází z oficiální dokumentace, která je šířena pod trojlicencí GPL/LGPL/MPL.)
perfektní shrnutí, díky.
chtěl bych aby se mi automaticky skrýval adresní řádek. A zase zobrazil když dostane focus (ALT+D, F6). Ideálně i kdyby uživatel najel myší k hornímu okraji okna.
Nevíte někdo jak to zařídit? Případně nějaké rozšíření? Díky
PS: případně lze adresní řádek umístit do navigation baru a vše ostatní přetahat do jiných „barů“… čímž by se problém redukoval na skrývání navigation baru.
líbil se mi, zajímavé, díky
je mozne v add-on SDK vytvarat k rozsireniu moznosti (options)? ak ano prosim o nejaky priklad, clanok alebo rozsirenie. dakujem
Chtěl bych poděkovat za pěkný návod a upozorni, že widget již dnes nefunguje, ale pro nastavení jsem to tu využil a příklady důkladně pročetl, děkuji.