Создание расширения для Google Chrome

    Тема создания расширений достаточно хорошо раскрыта в сети, есть множество статей, документации на эту тему. Но я не нашел ни одного ресурса, который бы описал процесс создания расширения от начала до конца. Я собираюсь исправить эту ситуацию, и рассказать о том как создать расширение, как хранить, читать настройки, как добавить поддержку нескольких языков.

    Для работы с расширениями вам понадобится переключить канал обновлений на Dev или Beta.

    Расширение будет иметь кнопку с иконкой на панели инструментов Chrome. При нажатии на кнопку будет появляться всплывающее окно (popup) со случайной картинкой из галлереи фотографий телескопа Hubble. В верхней части окна будут размещены кнопки: настроить (показать страницу настроек), обновить (показать другую фотографию), закрыть (закрыть всплывающее окно).

    Расширение будет содержать страницу настроек (options), на которой можно будет выбрать язык интерфейса (русский, английский) и выбрать размер картинки (маленький, большой).



    Создание расширения начинается с создания папки, в которой мы будет создавать все необходимы для работы расширения файлы. Созадим папку HubblePics. Далее создадим файл, который будет содержать описание нашего расширения — manifest.json. Данный файл является обязательным для каждого расширения. Именно из него Chrome получает всю необходимую информацию о расширении (название, версия, разрешения, страницы расширения и т.д.).

    {
     "name": "Hubble pictures extension", // Название расширения
     "version": "1.0", // Номер версии
     "description": "Hubble pictures extension", // Описание расширения

     "permissions": [
      "tabs", // Разрешить расширению работать с вкладками
      "http://hubblesite.org/*" // Разрешить расширению обращаться к указанному адресу
     ],

     "browser_action": { // Элементы браузера
      "default_title": "Hubble", // Название кнопки
      "default_icon": "images/icon.png", // Иконка для кнопки
      "popup": "popup.html" // Всплывающее окно
     },

     "options_page": "options.html" // Страница настроек
    }


    * This source code was highlighted with Source Code Highlighter.

    Подробное описание файла manifest.json вы можете получить здесь

    Настройки


    Создадим страницу настроек — options.html. Приводить полный код страницы я не буду, только интересные, на мой взгляд моменты, а именно сохранение, извлечение настроек и локализация.

    Сохранять настройки можно в объекте localStorage, который, по сути, представляет из себя ассоциативный массив, хранящий пары «название», «значение». Например, для сохранения состояния радиокнопки «Размер картинки — Маленький», используется код:

    localStorage["previewSmall"] = document.getElementById("previewSmall").checked;

    Для восстановления состояния:

    document.getElementById("previewSmall").checked = (localStorage["previewSmall"] == "true") ? true : false;

    В своем проекте я обернул обращение к localStorage в функцию readProperty чтобы избавится от лишних проверок и получить возможность получения значения по умолчанию:

    function readProperty(property, defValue)
    {
      if(localStorage[property] == null)
      {
        return defValue;
      }

      return localStorage[property];
    }

    // Пример вызова
    document.getElementById("previewSmall").checked = readProperty("previewSmall", true);


    * This source code was highlighted with Source Code Highlighter.

    Локализация


    С настройками разобрались, приступим к локализации. Способ, который я предлагаю, возможно, не самый лучший, но на данный момент ничего лучше я придумать не смог. Если кто-то подскажет другой, более простой вариант — буду рад.

    Идея простая — есть ряд элементов, которые нужно перевести. У них есть идентификаторы. Создается ассоциативный массив или объект, в котором идентификатору элемента соответствует локализованный текст. Функция, которая занимается локализацией «пробегает» по массиву, по идентификатору находит контрол и устанавливает ему текст.

    Создадим файл с названием элементов и указанием языка. Язык «регистрируется», путем добавления элемента в выпадающий список «Язык». Например русский язык добавляет в список элемент с текстом «Russian» и значением «ru_RU».

    Файл \locale\ru_RU\options.js

    RegisterLang();

    lang_ru_RU =
    {
      lngLanguage: "Язык", // Пара - идентификатор (id) элемента, текст

      lngPreviewSize: "Размер картинки",
      lngPreviewSmall: "Маленький",
      lngPreviewBig: "Большой",
      
      lngSave: "Сохранить",
      lngExit: "Выход"
    }

    function RegisterLang()
    {
      var ctrl = document.getElementById("language");

      ctrl.add(createOption("Russian", "ru_RU"));
    }


    * This source code was highlighted with Source Code Highlighter.


    Этот скрипт добавляется на страницу настроек (options.html)

    <script type="text/javascript" src="locale/ru_RU/options.js"></script>

    На странице, все локализуемые элементы должны иметь соответствующие идентификаторы, например:

    <span id="lngPreviewSmall">Small</span>

    Локализацией занимается функция localize

    function getSelectedLanguage()
    {
      var lang = getSelectedValue("language"); // Возвращает значение выбранного элемента в выпадающем списке "Language"
      return eval("lang_" + lang);
    }

    function localize()
    {
      var lang = getSelectedLanguage();

      // Перебираем все элементы объекта lang_ru_RU
      for(var ctrlId in lang)
      {
        var value = lang[ctrlId];

        // Получить элемент с id
        var ctrl = document.getElementById(ctrlId);

        // Не найден, продолжаем перебор
        if(ctrl == null)
        {
          continue;
        }

        // Найден, определить тип и присвоить значение
        if(ctrl.tagName == "SPAN")
        {
          ctrl.innerText = value;
        }
        else if(ctrl.tagName == "INPUT")
        {
          ctrl.value = value;
        }
      }
    }


    * This source code was highlighted with Source Code Highlighter.

    Теперь, если нам необходимо добавить новый язык, например английский, мы просто создаем папку \locale\en_US, в ней создаем скрипт options.js

    RegisterLang();

    lang_en_US =
    {
      lngLanguage: "Language",
      
      ...

      lngExit: "Exit"
    }

    function RegisterLang()
    {
      var ctrl = document.getElementById("language");

      if(ctrl != null)
      {
        ctrl.add(createOption("English", "en_US"));
      }
    }


    * This source code was highlighted with Source Code Highlighter.

    И добавляем скрипт на страницу

    <script type="text/javascript" src="locale/en_US/options.js"></script>

    Всплывающее окно


    Внутри файла popup.html простая разметка, в которой предусмотрено место для загружаемой картинки, кнопки управления и индикатор процесса загрузки.

    <ul class="menu">
      <li><img src="images/options.png" onclick="showOptions();"/></li>
      <li><img src="images/update.png" onclick="getPicture();"/></li>
      <li><img src="images/close.png" onclick="closePopup();"/></li>
    </ul>

    <div id="loader">
      <img src="images/loader.gif" />
    </div>

    <div id="image" style="display: none;">
      <a href="#" id="hrefPlace" onclick="return openImage();"><img id="imgPlace"/></a>
    </div>


    * This source code was highlighted with Source Code Highlighter.

    В общем ничего интересного. Все интересно вынесено в файл popup.js.

    Данный скрипт, используя XMLHttpRequest загружает страницу hubblesite.org/gallery/wallpaper, находит ссылки на изображения, выбирает случайное и отображает в popup-е.

    xhr = new XMLHttpRequest();

    xhr.onreadystatechange = function() {
    if (xhr.readyState == 4)
    {
      if (xhr.responseText)
      {
        var xmlDoc = xhr.responseText;

        var imgs = xmlDoc.match(/http:\/\/imgsrc.hubblesite.org\/hu\/db\/images\/hs-[0-9]{4}-[0-9]{2}-[a-z]/g);
        var hrefs = xmlDoc.match(/gallery\/wallpaper\/pr[0-9]{4,}[a-z]/g);

        if (imgs.length > 0)
        {
          var randIdx = Math.floor(Math.random() * imgs.length);

          var imgSize = "-wallpaper_thumb.jpg";

          // Какую картинку показываем?
          if(readProperty("previewBig", "false") == "true")
          {
            imgSize = "-640_wallpaper.jpg";
          }

          showImage("http://hubblesite.org/" + hrefs[randIdx], imgs[randIdx] + imgSize);
        }
      }
    }

    xhr.open("GET", "http://hubblesite.org/gallery/wallpaper/", true);
    xhr.send(null);

    function showImage(url, imgSrc)
    {
      var imgPlace = document.getElementById("imgPlace");
      imgPlace.setAttribute("src", imgSrc);

      var hrefPlace = document.getElementById("hrefPlace");
      hrefPlace.setAttribute("href", url);

      displayLoader(false);
    }


    * This source code was highlighted with Source Code Highlighter.

    Установка и упаковка расширения


    Расширение создано, теперь необходимо загрузить его в Chrome. Запускаем Chrome, нажимаем кнопку Настройка и управление , выбираем пункт меню Extensions.



    На открывшейся вкладке нажимаем Load Extension..., указываем путь к папке и, если все сделано правильно, видим новую кнопку на панели инструментов.



    А в списке расширений видим наше расширение.



    Теперь упакуем наше расширение, для того, чтобы его можно было выложить на какой-нибудь ресурс и любой пользователь мог бы скачать и установить его в пару кликов. Для этого, на той же закладке Installed Extensions нажимаем кнопку Pack Extension..., указываем путь к папке, содержащей файлы расширения, поле Private key file в первый раз оставляем пустым.



    Нажимаем OK, видим сообщение о том, что расширение упаковано.

    Если мы собираемся выпускать обновленные версии расширения — сохраним созданный файл с ключем HubblePics.pem и будем указывать путь к нему при каждой последующей упаковке расширения, иначе, каждый раз будет генерироваться новый файл, что приведет к назначению нового идентификатора для нашего расширения, а это, в свою очередь приведет к тому, что вместо обновления, пользователь будет устанавливать новую копию расширения.

    Архив с исходниками расширения

    Практически вся информация, необходимая для разработки расширений сосредоточена на странице Google Chrome Extensions: Developer Documentation. Если этого покажется мало, то всегда можно взять готовое расширение, изменить расширение с crx на zip, распаковать и посмотреть как это сделано «у них».

    Так же источником информации, так сказать, из первых рук, может стать список изменений при выходе новых версий Google Chrome.
    Поделиться публикацией
    Ой, у вас баннер убежал!

    Ну. И что?
    Реклама
    Комментарии 28
      +1
      спасибо! взял на заметку
        +1
        В закладки, однозначно!
        Спасибо за труды, как раз нечто подобное и искал неделю назад :)
          +5
          судя по всему писать расширения под хром очень просто, этот факт не может не радовать

            0
            Еще радует то, что практически в каждом новом релизе они добавляют новые фичи для расширений.
            +2
            По-моему всё проще: берёшь расширение из примеров, разбираешь, изучаешь, собираешь из него своё, попутно почитывая документацию.
            0
            В бете же еще нет поддержи расширений?

            Кстати, кто сможет написать хабрааддон к хрому?
              0
              На сайте с документацией сказано «you need to subscribe to the Dev or Beta channel», значит все-таки поддерживает.

              Про какой хабрааддон идет речь?
                0
                сказано, но вот все расширения на www.chromeextensions.org/ имеют статус developer builds.

                вот про такой аддон речь — habrahabr.ru/blogs/firefox/64790/
                  0
                  Что-то мне подсказывает, что надо для начала автору того расширения к Firefox предложить это сделать. Наверняка часть кода можно просто перетащить без изменений.
              +1
              Очень полезная статья.

              Таким образом можно сделать checker почты или еще что-то подобное.

              Хотелось бы еще иметь представление о работе расширений со страницами, загруженными в браузере.
              Например, под FF есть расширение Evernote, которое помещает выделенный текст на странице в личный блокнот
                0
                Для этого используются Content scripts
                • НЛО прилетело и опубликовало эту надпись здесь
                  0
                  Я так понимаю, у аякса тут нет никаких ограничений? это очень радует. наверно на выходных попробую написать кое-какой extension, которого мне очень нехватает
                    0
                    В расширениях можно так же jQuery и другие библиотеки использовать
                      0
                      Я как-то пробовал использовать любимый мутулс, но уже давно, так там выборка с помощю $$() не работала… Надеюсь уже работает. На выходных попробую.
                    0
                    Приложили бы своё расширение для тестов
                      +1
                      Забыл разместить ссылку, исправился. В конце статьи ссылка на архив.
                        0
                        И да, забыл финал написать, как делать запакованные екстеншен

                        Закрываем хром и

                        C:\Users\%username%\AppData\Local\Google\Chrome\Application>chrome.exe --pack-extension=c:\HubblePics

                        В итоге *.pem не теряем, а *.crx дарим людям
                          0
                          Нет, не забыл, в конце статьи есть информация об упаковке расширения.
                            0
                            вы пишите экстеншны к хрому? есть работка
                        +1
                        Приятно, что настолько же просто, как и для фокса.
                        С ужасом вспоминаю создание расширений для ИЕ
                          0
                          Согласен, дрожь берет. Но с другой стороны плагины IE имеют более низкоуровневый доступ, за который приходится платить сложностью кода.
                            0
                            да само расширение, особенно на С#.NET, создать несложно. А вот заставить его выглядеть нормально и воспринимать XP-стили у меня до сих пор не вышло. И это при том, что писал и на С#.NET, и на С++.NET, и на С++ голом, и на Delphi.
                              0
                              Писать не сложно, код он и есть код, но «порог входа» туда высокий. Готовых, рабочих примеров, в свое время, я даже на MSDN не нашел.
                                0
                                фактически, да. Я тоже. Приходилось ломиться сквозь чащу
                          0
                          Всё-таки XML более человечен для таких целей, чем JSON…
                          Я имею в виду манифест и локализацию.
                            0
                            Не забудьте добавить в скрипт полезную функцию — автообновление

                            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                            Самое читаемое