Tworzenie rozszerzeń do przeglądarek - okładka

Tworzenie rozszerzeń do przeglądarek w JavaScript

Całkiem nie tak dawno temu miałem przyjemność tworzyć (a raczej współtworzyć) swoje pierwsze rozszerzenie do przeglądarki. Z tego też powodu chciałbym podzielić się w tym wpisie wiedzą, którą zdobyłem w trakcie procesu developmentu.

Zasadniczo proces powstawania rozszerzenia do przeglądarki niewiele różni się od procesu tworzenia zwykłej aplikacji webowej. Mamy tu do dyspozycji te same narzędzia i technologie takie jak HTML, CSS czy JavaScript oraz wszelkie API przeglądarek, ale też dostajemy nieco dodatkowych możliwości, które pokrótce omówię w tym artykule. Opisane rozwiązania znajdą zastosowanie w przeglądarkach Google Chrome oraz Mozilla Firefox.

Manifest - dokument deklarujący z czym mamy do czynienia

manifest.json

Tworzenie wtyczki do przeglądarki należy zacząć od zadeklarowania manifestu. Manifest to plik definiujący z czym przeglądarka ma do czynienia, jakich uprawnień wymaga rozszerzenie, oraz inne, bardziej standardowe opcje takie jak nazwa, wersja, opis czy ikony. Plik ten ma format .json. Spójrzmy jak może wyglądać taki przykładowy plik:


{
  "name": "Moje pierwsze rozszerzenie",
  "version": "1.0.0",
  "description": "Lorem ipsum.",
  "permissions": [
    "activeTab",
    "tabs",
    "storage",
    "*://example.com/*"
  ],
  "content_scripts": [
    {
      "matches": [ "https://example.com/*" ],
      "js": ["index.js"]
    }
  ],
  "icons": {
    "16": "images/icon16.png",
    "48": "images/icon48.png",
    "128": "images/icon128.png"
  },
  "browser_action": {
    "default_icon": "images/icon128.png",
    "default_popup": "popup.html",
    "default_title": "Example"
  },
  "browser_specific_settings": {
    "gecko": {
      "id": "johndoe@example.com"
    }
  },
  "manifest_version": 2
}

Pierwszych trzech pól myślę, że nie trzeba omawiać. Natomiast zatrzymajmy się na chwilę przy czwartym polu.

rozszerzenia do przeglądarek - uprawnienia

Uprawnienia – permissions

W polu permissions, jak sama nazwa mówi, definiujemy uprawnienia. Bardzo istotne jest, aby wpisywać tam tylko te uprawnienia, które są absolutnie niezbędne. Jeśli chcielibyśmy opublikować rozszerzenie na przykład w Chrome Web Store, to należy liczyć się z tym, że może zostać ono odrzucone, bądź wycofane jeżeli będziemy wymagali więcej uprawnień niż to potrzebne. Całkiem niedawno (data tworzenia artykułu to lipiec 2019) otrzymałem wiadomość od Google’a w związku z aktualizacją polityki i widniało w niej między innymi:

Request access to the narrowest permissions necessary to implement your Product’s features or services. If more than one permission could be used to implement a feature, you must request those with the least access to data or functionality.

Don’t attempt to “future proof” your Product by requesting a permission that might benefit services or features that have not yet been implemented.

Jak widzisz Google bardzo dba, aby twórcy nie nadużywali możliwości jakie oferuje tworzenie rozszerzeń. Jeśli użytkownik zainstaluje złośliwą wtyczkę to możliwości atakującego są ogromne – począwszy od podsłuchiwania użytkownika, przez kradzież danych, a na przejęciu kontroli nad przeglądarką skończywszy.

Powróćmy jednak do pliku manifest.json. W polu permissions znalazł się następujący zapis: *://example.com/*. Oznacza on, że nasza wtyczka może komunikować się z zewnętrznym serwisem czy usługą zlokalizowaną pod domeną example.com. Gwiazdka przed adresem oznacza dowolność stosowanego protokołu, jednak z oczywistych względów myślę, że warto aby to był HTTPS. W przypadku gdy strona na której jesteśmy korzysta z HTTPS a example.com z HTTP, to nasz request do example.com może w ogóle nie zostać wykonany. Druga z gwiazdek oznacza natomiast dowolność endpointu.

Content scripts

Content scripts definiuje nam gdzie będzie uruchamiany kod naszego pluginu oraz jakie pliki go zawierają. Pierwszy z atrybutów – matches jest obowiązkowy i definiuje domenę, na której ma się wykonywać kod rozszerzenia. Oprócz tego możemy dostarczyć pliki CSS czy JavaScript (w naszym przypadku index.js).

Icons

Kolejnym elementem, o który powinniśmy zadbać są ikony. Są one potrzebne w przypadku gdy chcemy opublikować naszą wtyczkę w sklepie z rozszerzeniami. Musimy zadbać o 3 rozmiary: 16, 48 oraz 128. Jako format zalecałbym skorzystanie z formatu PNG, chociażby dla możliwości skorzystania z kanału alfa przy projektowaniu ikonki.

Browser action

Tutaj należy zdefiniować ikonę domyślną czyli tę, która pojawi się koło paska adresu w przeglądarce. Oprócz tego należy podać tytuł (pojawi się on po najechaniu myszką na ikonę) oraz możemy zdefiniować popup. O tym czym jest popup będzie nieco później.

Pozostałe

Do omówienia został nam manifest_version, który według zaleceń Google powinien być ustawiony na wersję 2 oraz browser_specific_settings. Druga z opcji jest niezbędna w momencie, gdy chcemy opublikować nasze rozszerzenie dla przeglądarki Mozilla Firefox w Firefox Add-ons. W tych ustawieniach należy podać ID rozszerzenia. Takim identyfikatorem może być na przykład email, pod którym można skontaktować się z developerem. Jeśli planujemy opublikować nasze rozszerzenie tylko na Chrome Web Store to nie musimy zawracać sobie głowy ostatnim z ustawień.

Popup

Twórcy przeglądarek oddali nam do dyspozycji mechanizm tworzenia popupu. Możemy go wywołać poprzez kliknięcie na ikonę rozszerzenia koło paska adresu. Powinno pojawić nam się struktura zdefiniowana w pliku, do którego ścieżkę możemy podać we wcześniej omawianym manifeście.

Warto tutaj zaznaczyć, że popup jest traktowany tak jakby był “osobną stroną internetową” – coś na kształt osadzonego iframe. W praktyce oznacza to, że skrypty oraz style podpięte w popupie będą dostępne tylko w jego obrębie. Nie mamy możliwości ostylowania elementów strony z poziomu popupu, czy też wykonania skryptów. Dla przykładu mamy na naszej stronie nagłówek h1 z klasą title. Wykonanie z poziomu popupu document.querySelector('title'); nie uchwyci nam poprawnie nagłówka (chyba, że mamy taki sam nagłówek w popupie – wtedy to zadziała poprawnie).

Dość przydatna uwaga – popup posiada własną, niezależną konsolę developerską. Błędy skryptów z popupa nie pojawią się w tej konsoli, której używasz do debugowania stron. Należy kliknąć prawym przyciskiem myszy na ikonę rozszerzenia i tam wybrać odpowiednią opcję aby otworzyć konsolę developerską dla rozszerzenia.

Chrome API

Przechodzimy teraz do punktu kulminacyjnego wpisu, czyli Chrome API lub też Browser API. Na moment publikacji tego artykułu Chrome API działa zarówno na Google Chrome jak i na Mozilla Firefox, dlatego też myślę, że warto skupić się tylko na tym pierwszym. Owe API pozwala na korzystanie z wbudowanych mechanizmów w przeglądarkę jak chociażby menu kontekstowe, czy też na nasłuchiwanie na akcje, które użytkownik wykonuje na stronie – na przykład zmiana karty, czy dodanie strony do zakładek.

Warto w tym miejscu zaznaczyć, że część z funkcjonalności Chrome API jest dostępna tylko z poziomu popupu, lub też tak zwanych background scripts, czyli skryptów służących chociażby nasłuchiwaniu na akcje użytkownika. Do nasłuchiwania na akcje wykorzystujemy listenery wystawiane przez Chrome API.  Background scripty również należy zdefiniować w manifeście pod kluczem background.

Innym świetnym mechanizmem są storages. Do dyspozycji mamy dwa rodzaje storages: sync oraz local (nie mylić z LocalStorage). Różnica między nimi jest taka, że sync powiązuje przechowywane dane z aktualnie zalogowanym do przeglądarki kontem. W praktyce oznacza to, że mamy dostęp do naszych danych zaraz po zalogowaniu się w przeglądarce. Świetnie sprawdza się to na przykład przy zapisywaniu konfiguracji. Przecież nie chcemy, aby użytkownik na każdym urządzeniu musiał po raz kolejny przeprowadzać proces konfiguracji.

Jeśli interesują cię możliwości jakie oferuje Chrome API, to zachęcam Cię do przejrzenia jego dokumentacji i stworzenia swojej pierwszej wtyczki.

Ładowanie samochodu elektrycznego - wtyczka

Publikacja rozszerzenia

Po stworzeniu wtyczki warto ją pokazać światu i opublikować ją na Chrome Web Store oraz Firefox Add-ons. Generalnie w obu witrynach jesteśmy prowadzeni za rączkę i krok po kroku zostaniemy poinstruowani jak należy wrzucić nasze rozszerzenie do sklepu. Co ciekawe Chrome Web Store przed opublikowaniem pierwszego rozszerzenia poprosi nas o uiszczenie drobnej opłaty ( 5USD ) w celu weryfikacji. Po wrzuceniu rozszerzenia należy oczywiście uzupełnić opisy, grafiki, kontakt etc.

Jeżeli chcemy korzystać z rozszerzenia tylko na własny użytek lub je testować to można to zrobić w następujący sposób:

  • Chrome:
    Należy wpisać w pasek adresu: chrome://extensions, a następnie wybrać Load unpacked. Tam wskazujemy na folder, w którym znajduje się manifest.json.
  • Firefox:
    W pasek adresu wpisujemy about:debugging i tam wybieramy Load Temporary Add-on. Tu jednak należy wskazać bezpośrednio na manifest.json.

W obu przypadkach jeśli mamy źle skonfigurowany manifest otrzymamy stosowny komunikat o błędzie.

Na sam koniec zachęcam cię do spróbowania swoich sił i napisania choćby najprostszego rozszerzenia do przeglądarki. Będzie mi również bardzo miło jeśli zostawisz komentarz i ocenę pod artykułem.

Źródła i materiały dodatkowe: