14 октября 2011 в 19:20

Userscripts. Упаковываем юзерскрипт для Chrome tutorial

Доброго времени суток, уважаемые хабражители.

Сегодня мы поговорим подробней об упоминавшейся вскольз технологии написания кроссбраузерных юзерскриптов, а именно об упаковывании юзерскрипта в простейшее расширение для Google Chrome.

Ниже я постараюсь овтетить на вопросы «зачем ?» и «как ?».

Прелюдия


Как вы помните, браузер Google Chrome поддерживает скрипты нативно (без необходимости установки сторонних компонентов). Поддержка скриптов реализована на довольно хорошем уровне, но есть одно НО: разработчики Google Chrome пекутся о нашей безопасности и ограничивают всё, что можно ограничить.

В виду этих ограничений нетривиальные скрипты приходится оборачивать в расширение.

Какие ограничения?


Самые главные ограничения:
  1. Скрипт должен делать кроссдоменные запросы
  2. Скрипту нужен доступ к window.frames[i]
  3. Скрипту нужны повышенные лимиты localStorage
  4. И т.д.

Указанные выше три ограничения не могут быть сэмулированы и окостылены. Придётся упаковывать юзерскрипт.

Что получаем на выходе?


Если говорить строго, то на выходе мы получим расширение, а не юзерскрипт. Но учитывая, что:
  • процессы установки и управления юзерскрипта и расширения одинаковы;
  • юзерскрипты при установке автоматически оборачиваются в расширение;
  • обёртка создаётся один раз, а далее мы разрабатываем только сам юзерскрипт;

я позволяю себе говорить о юзерскрипте.

Упаковка


Простое расширение состоит из:
  1. Файла описания manifest.json
  2. Фоновой страницы background.html
  3. Файла юзерскрипта


manifest.json


Данный файл описывает расширение: права, составляющие ресурсы, метод запуска и т.д.
Как видно из названия, вся конфигурация представляет собой json-объект.
Подробнее об этом файле можно почитать в официальном доке.

Я рассмотрю необходимый минимум для превращения юзерскрипта в расширение:
{
    "background_page" : "background.html", 
    "content_scripts" : [
        {
            "js":[ "my.user.js" ], 
            "matches":[ "http://*/*" ], 
            "run_at":"document_end"  
        }
    ], 
    "description" : "", 
    "name" : "My Userscript", 
    "permissions" : [ "http://*/*", "unlimitedStorage"],
    "version" : "1.3.0"
}



Параметр Назначение Комментарий
background_page Определяет файл фоновой страницы Назначение см. ниже
content_scripts Секция подключения контент-скриптов Именно сюда прописывается информация
о нашем юзерскрипте
js Массив, содержащий название файлов контент-скриптов Здесь указывается название
нашего единственного скрипта
matches Массив, содержащий url-маски для запуска скриптов Каждый элемент массива соответствует
по индексу элементу массива контент-скриптов.
Этот параметр определяет, на каких страницах
будут запускаться соответствующие скрипты.
В нашем случае маска указывает на то,
что скрипт запускается на всех страницах,
доступных по http.
run_at Порядок запуска контент-скриптов document_end означает,
что скрипт будет запускаться
после построения DOM-дерева
description Описание расширения Произвольный текст, описывающий наш юзерскрипт
name Название расширения Название скрипта в произвольной форме
permissions Разрешения для нашего расширения Необходимые разрешения безопасности.
Первый параметр в примере
маска корссдоменных запросов http://*/*.
Она позволяет фоновой странице посылать запросы на любые домены.

Второй параметр задаёт нелимитируемый localStorage.
version Версия расширения Версия в формате x.x.x.x


background.html


Фоновая страница представляет собой обычную html страницу, которая загружается в «невидимый таб» при запуске расширения и работает в фоне в течение всего жизненного цикла расширения.
Ограничения безопасности фоновой страницы настраиваются через параметр permissions в файле-манифесте. Именно через фоновую страницу обходятся ограничения юзерскриптов. Фоновая страница для упакованных юзерскриптов представляет собой proxy, который может «общаться» с юзерскриптом посредством chrome.extension api (Описание).

С теорией покончим, время тусоваться практики!
Рассмотрим подробнее проксирование кроссдоменных запросов из юзерскрипта.

Код background.html:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<script>
/**
* Кроссдоменные запросы для Хрома.
* XMLHttpRequest на фоновой странице избавлен от CORP (Cross Origin Request Policy),
* т.е. может посылать запросы на другие домены.
* Ниже реализован простой метод GET
*/
function get(url, callback) {
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function (data) {
        if (xhr.readyState == 4) {
            if (xhr.status == 200) {
                callback(data.srcElement.responseText);
            } else {
                callback(null);
            }
        }
    }
    // Note that any URL fetched here must be matched by a permission in
    // the manifest.json file!
    xhr.open('GET', url, true);
    xhr.send();
};
/**
* Обработчик события chrome.extension api.
* Нужен лдя непосредственного проксирования
* @param request Object Данные нашего api-запроса.
* @param sender Object Объект, характеризующий происхождение нашего запроса.
* @param callback Function Коллбэк, который мы передаём параллельно с api-запросом.
* /
function onRequest(request, sender, callback) {
    // В данном примере поддерживается только действие xget.
    // В целом же можно построить довольно неплохую RPC-cистему
    if (request.action == 'xget') {
        get(request.url, callback);
    }
};
// Регистрируем обработчик события.
chrome.extension.onRequest.addListener(onRequest);
// Из скрипта обращение к прокси будет выглядеть так:
// chrome.extension.sendRequest({'action' : 'xget', 'url':url}, callback);
</scrip>
<body></html>


Код с английскими комментариями доступен на pastebin.com

Вызов из юзерскрипта:
/**
 * Этот код располагается в юзерскрипте
 * Тестовый вызов: get("http://example.com",alert);
 */
function  get(url, callback) {&
    chrome.extension.sendRequest({ 'action':'xget', 'url':url}, callback);
}



Этот метод работает в Google Chrome. Кроссдоменные запросы из юзерскриптов в других браузерах мы рассмотрим в одной из следующих статей.

Собираем, тестируем


Для сборки расширения нам понадобится:
  1. Создать отдельную папку (для удобства)
  2. Положить в неё manifest.js, background.html и файл юзерскрипта
  3. Упаковать расширение при помощи Chrome (Натсройки — Расширения — Упаковать расширение. См. скриншот)




Для тестирования мы можем установить распакованное расширение (спасибо theOnlyBoy за наводку). Вместо упаковки (пункт 3) жмём «Установить распакованное расширение».
В итоге наше расширение установится как и обычное упакованное (плюс будет милая и удобная ссылочка Reload для перезагрузки расширения, см. скриншот).

Заключение


На этом на сегодня всё, оставайтесь с нами!
Жду вопросы, критику и обсуждения в комментариях.


Список статей:
  1. Учимся писать userscript'ы
  2. Userscripts. Углубляемся
  3. » Userscripts. Упаковываем юзерскрипт для Chrome
  4. Usersctripts. Кроссдоменные запросы
Алексей Мигутский @MrMig
карма
69,0
рейтинг 0,0
Пользователь
Похожие публикации
Самое читаемое Разработка

Комментарии (10)

  • 0
    на кой? на кой упаковывать юзерскрипты, когда хром их итак понимает?
    • 0
      Цитирую фрагмент статьи:

      есть одно НО: разработчики Google Chrome пекутся о нашей безопасности и ограничивают всё, что можно ограничить.
      В виду этих ограничений нетривиальные скрипты приходится оборачивать в расширение.
      • 0
        не заметил. всё равно браузер только спрашивает, согласен ли я установить и дать ли доступ к таким-то доменам. почти то же самое, что и с юзерскриптами
  • 0
    М.б. ещё про «all_frames»: true упомянуть?
  • +1
    Есть очень существенный плюс расширения относительно юзер-скрипта: расширение автоматически обновляется при появлении новой версии.
  • 0
    Подскажите, как подключить mootools для использования в экстеншионе? (на странице есть, но из скрипта $$ — undefined).

    Делаю
    «content_scripts»: [ {
    «js»: [ «mootools.js», «rc-regata.user.js» ],

    Где mootools.js — MooTools Core 1.4.1 with compatibility Uncompressed.

    В итоге получаю сообщение что хром не может подгрузить mootools. Что править?

    Заранее спасибо за ответ. )
    • 0
      Я подключаю библиотеки только непосредственной инъекцией библиотеки в код скрипта, т.е. простым копированием ужатой версии библиотеки в код юзерскрипта. Посмотрите в шаблон в первой статье, там есть закомментированное место для вставки кода библиотек.
      • 0
        Вижу. Но не понимаю. Вы не могли бы прокомментировать строку

           // (function(a,b){function ci(a)… a.jQuery=a.$=d})(w);

        Это вообще как использовать? Что куда копировать? Что такое a и b?
        • 0
          Это вообще начало и конец кода jQuery из файла jquery.min.js.

          Эту строчку можно удалить полностью (а можно вообще не трогать, это комментарий), и заменить её кодом вашей библиотеки. Код нужно непосредственно копировать в тело юзерскрипта.

          Если есть вопросы — постучитесь в скайп.
          • 0
            Спасибо ) Если что, стукнусь.

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