Создание расширения для 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
                            Не забудьте добавить в скрипт полезную функцию — автообновление

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