Телевизоры. Приставки. Проекторы и аксессуары. Технологии. Цифровое ТВ

Написание расширений для chrome. Кратко о создании расширений для Chrome. Собираем приложение для Google Chrome

Написать расширение для google chrome несложно. Но при написании первого раширения могут возникнуть (и возникают) вопросы. Большинство мануалов по написанию первого расширения расчитаны на использования манифеста первой версии, поддержка которого в скором будущем прекратится.

В этой статье будет рассмотрено:

  • Как составлять манифест v.2
  • Как работать с удаленными ресурсами
  • Как работать с cookies
  • Как работать с local storage
  • Как работать с уведомлениями
Введение

К концу статьи у нас будет готово расширение-органайзер, в котором будет поле для добавления новой задачи, а так же список задач на текущий день. Обозначим все требования к органайзеру:

  • Должен иметь поле для добавления события (дата, время, событие)
  • Должен отображать все задачи на текущий день, отсортированные по времени
  • Все прошедшие события должен отображать зачеркнутыми
  • Должен иметь поле для ввода времени, за сколько надо показывать уведомление, а так же чекбокс разрешающий и запрещающий показывать уведомления
  • За указанное время до события должен отображать уведомление о приближающемся событии
Манифест

Начнем создавать расширение с самого начала, то есть с манифеста. Манифест – это тот самый файл, в котром прописываются все параметры расширения. Название, описание, версия, разрешение на доступ к сайтам, разрешение на использование кук, уведомлений, локального хранилища. В общем, манифест – это мозг расширения. Создаем файл manifest.json. Манифест – единственный файл, который должен иметь заранее предопределенное имя, все остальные файлы можно будет называть как угодно. В этом файле есть три обязательных поля:

manifest.json

{ “name”: “Organizer extension”, // Название расширения “version”: “1.0”, // Версия расширения. “manifest_version”: 2 // Версия манифеста }

Тут есть пара правил:

  • Версия манифеста должна быть целочисленной, то есть должна писаться как 2, а не “2”.
  • Версия расширения должна быть строковой, но содержать только числа и точки, то есть “1.0” - хорошо, а 1.0 и “0.9 beta” - плохо.

С обязательными полями – все, перейдем к созданию всплывающего окна расширения. Для того, чтобы по нажатию на пиктограмму, открывалось окно, необходимо добавить в манифест поле “browser_action”

manifest.json

{ … "browser_action": { "default_title": "Open organizer", // Заголовок. Его видно если навести курсор на иконку в браузере "default_icon": "icon_small.png", // Путь к иконке расширения "default_popup": "popup.html" // Путь к странице с попапом } }

Теперь создадим всплывающее окно. Это обычная html страница, которая может быть любого размера и цвета, никаких фокусов. Назовем файл “popup.html”. Создать этот файл мало – его надо указать в манифесте. Так мы и сделали: «default_popup»: «popup.html».

popup.html

It works!

Добавление расширения в браузер

Теперь пришло время проверить работоспособность нашего расширния. Для этого загрузим расширение в браузер. Открываем в хроме меню расширений. Ставим птицу на “Developer mode”.

После этого появятся три кнопки. Нажимаем “Load unpacked extension...”. Выбираем папку с файлами расширения. После этого появится наше расширение. Если все правильно, то по нажатию на иконку – повится окно:

Подключение скриптов

Теперь можно приступить к интересному. Подключим два javascript файла. Первый – popup.js, второй – jquery. С первым проблем не возникнет, но jquery будем подключать не локальный, а удаленный, взятый по адресу ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js . Проблемы возникнут от того, что по умолчанию расширение не имеет доступа к сторонним ресурсам. Чтобы получить доступ, надо его указать в манифесте. Доступ к чему-либо указывается в поле “permissions”. Так же, для удаленных скриптов и css надо указывать доступные удаленные ресурсы.

manifest.json

{ … "permissions": [ "https://ajax.googleapis.com/*" ], "content_security_policy": "script-src "self" https://ajax.googleapis.com; object-src "self"" }

Теперь подключим эти скрипты в popup.html

popup.html

Storage

При помощи storage в хроме можно хранить пользовательские данные. И именно в storage наше расширение и будет хранить грядущие события. На то есть две причины. Во-первых, данные, хранищиеся в storage можно синхронизировать, если залогиниться в браузере. А во-вторых, данные можно хранить не только в виде строки, как в cookies, а в любом виде, то есть можно хранить и массивы и объекты. Чтобы это заработало, откроем доступ к storage в манифесте.

manifest.json

{ ... "permissions": [ … "storage" ] ... }

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

popup.html

Date

Дата Время Задача

И сразу же добавим отображение даты в блоке #today_date.

popup.js

$(function(){ var today = new Date(); $("#today_date").html(today.getDate()+"."+(parseInt(today.getMonth())+1)+"." + today.getFullYear()); }

Выглядеть должно так:

Итак, при нажатии на кнопку “+” у нас должно добавляться событие. Вначале файла объявим глобальную переменную storage – объект для работы с storage, а так же глобальный массив tasks для хранения событий.

popup.js

Var storage = chrome.storage.sync; var tasks = new Array(); $(function(){ … $("#add_task").click(function(){ var new_task = new Object(); new_task.date = validateField($("#new_date").val(), "date"); new_task.time = validateField($("#new_time").val(), "time"); new_task.task = $("#new_task").val(); if(!new_task.task || !new_task.date || !new_task.task){ return false; } tasks = new_task; storage.set({ tasks:tasks }); }); });

Функция валидации проверяет, что дата записана в формате d.m.yyyy, а время в формате hh:mm, а так же, что в описании события не меньше трех символов.

popup.js

Var validateField = function(val, type){ if(type == "date"){ var date = val.split("."); var year = new Date(); year = year.getFullYear(); if(date.length == 3 && parseInt(date) == date && date = 3){ return val; } return null; }

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

popup.js

$(function(){ … var now_hours = today.getHours() < 10 ? "0" + today.getHours() : today.getHours(); var now_minutes = today.getMinutes() < 10 ? "0" + today.getMinutes() : today.getMinutes(); var now_time = now_hours + "" + now_minutes; storage.get("tasks",function(items){ if(items.tasks){ tasks = items.tasks; var today_tasks = getTodayTasks(tasks); if(today_tasks.length >0){ for(var i in today_tasks){ var this_time = today_tasks[i].time.replace(":", ""); var add = this_time > now_time ? "" : " class="done""; var add_html = ""+today_tasks[i].time+" "+today_tasks[i].task+""; $("ul").append(add_html); } } } }); … });

Функция getTodayTasks() возвращает из общего списка только события с сегодняшней датой.

popup.js

Var getTodayTasks = function(tasks){ var today_tasks = new Array(); var today = new Date(); var today_date = today.getDate()+"."+(today.getMonth() + 1)+ "." + today.getFullYear(); for(var i in tasks){ if(tasks[i].date == today_date){ today_tasks = tasks[i]; } } if(today_tasks.length > 0){ today_tasks = sortTasks(today_tasks); } return today_tasks; }

Функция sortTasks() сортирует события по возрастанию времени.

popup.js

Var sortTasks = function(tasks){ if(tasks.length > 0){ var swapped = true; while (swapped) { swapped = false; for (var i=0; i < tasks.length-1; i++) { var this_time = tasks[i].time.replace(":", ""); var next_time = tasks.time.replace(":", ""); if (this_time > next_time) { var temp = tasks[i]; tasks[i] = tasks; tasks = temp; swapped = true; } } } } return tasks; }

Уведомления

Пришло время настроить отображение уведомлений на экране. Добавим во всплывающее окно специальный чекбокс. Если этот чекбокс будет отмечен – уведомлениея будут показываться, если не будет отмечен – не будут. Так же добавим текстовый инпут. Цифра в этом инпуте будет показывать, за какое время до событя будет показываться уведомление. То есть если у нас событие назначено на 19:00, в этом текстовом инпуте будет 5, значит в 18:55 появится уведомление. Добавим в popup.html код с этими инпутами

popup.html

Показывать уведомления

Теперь давайте разберемся с тем, как это будет работать. При нажатии на чекбокс, будет проверяться его атрибут checked, значение атрибута будет записываться в cookie “show_notifications”. Перейдем к текстовому инпуту. По изменению его значения, новое значение будет валидироваться, если оно целочисленное и не больше 120, записываем новое значение в cookie “when_to_notify”.

Но для того, чтобы у нас это заработало, надо открыть доступ к cookies. Для этого заходим в manifest.json и добавляем в “permissions”

manifest.json

{ ... "permissions": [ … “cookies” ] ... }

Теперь можно приступать к скрипту. Заходим в popup.js. Для начала установим первоначальные значения в инпутах. По-умолчанию чекбокс не отмечен, то есть уведомления не показываются, а время равно 0. При клике на чекбокс, будет меняться значение cookie “show_notifications”. При изменении значения в тектовом поле, будет меняться значение cookie “when_to_notify”.

popup.js

$(function(){ setCheckbox(); setWhenToNotify(getCookie("when_to_notify")); ... $("#show_notifications").click(function(){ setCookie("show_notifications", document.getElementById("show_notifications").checked); }); $("#when_to_notify").change(function(){ setWhenToNotify($(this).val()); }); });

Рассмотрим подробнее функции. Начнем с функций работы с cookies. В данном случае были взяты готовые функции с w3schools.com.

popup.js

Var setCookie = function(c_name,value,exdays){ /* *Взято с http://www.w3schools.com/js/js_cookies.asp */ var exdate=new Date(); exdate.setDate(exdate.getDate() + exdays); var c_value=escape(value) + ((exdays==null) ? "" : "; expires="+exdate.toUTCString()); document.cookie=c_name + "=" + c_value; } var getCookie = function(c_name){Позвонить Васе П. /* *Взято с http://www.w3schools.com/js/js_cookies.asp */ var i,x,y,ARRcookies=document.cookie.split(";"); for (i=0;i

Чтобы было понятнее, описание кода вставил в самом HTML. Меню я организую просто: на картинку ставлю внутреннюю ссылку расширения.

Раз уж начал про popup.html, то расскажу сразу и о popup.js

Выглядит он у меня весьма просто:

popup.js
var xhr = new XMLHttpRequest(); xhr.open("GET", "http://losttime.su/?tmpl=login&token="+localStorage["lostlogin"], true); // тут происходит ГЕТ запрос на указанную страницу xhr.onreadystatechange = function() { if (xhr.readyState == 4) // если всё прошло хорошо, выполняем, что в скобках { var dannie = document.getElementById("dannie"); dannie.innerHTML = xhr.responseText; // добавляем в блок с id=dannie полученный код } } xhr.send();

Описание кода также вставил.

Именно описанная выше конструкция позволяет вытащить и вывести содержание с Вашего, а может и не с Вашего сайта. Но, что важно знать:
- В файле манифеста обязательно в поле permissions пишем адрес сайта, с которого будет браться информация.
- Файл popup.js связан с фоновым скриптом background.js, т.к. данные, занесенные в локальное хранилище на background.js, видны и на popup.js.

Перед тем, как рассмотреть файл фонового скрипта background.js, рассмотрим файл скрипта, который запускается на каждой странице отдельно: content.js

У меня он выглядит так:

content.js
function onBlur() { // окно теряет фокус chrome.runtime.sendMessage({site:sait,time:localStorage}); // отправка сообщения на background.js localStorage = "0"; } window.onblur = onBlur; // если окно теряет фокус function sec() //выполняется каждую секунду { if(document.webkitVisibilityState == "visible")//если страница активна { localStorage = parseInt(localStorage,10) +1; // обновляем данные о сайте в локальном хранилище } } var sait=location.hostname; // на каком сайте находится скрипт localStorage = "0"; setInterval(sec, 1000);// запускать функцию каждую секунду

Наиболее интересный момент из моего скрипта, я считаю, должен быть:
chrome.runtime.sendMessage({site:sait,time:localStorage});
Тут происходит отправка сообщения background скрипту, а именно две переменные: site:sait - содержит адрес сайта, на котором скрипт
time:localStorage - количество времени, проведенное на этом скрипте.

background.js
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) { var a = request.site; // данные о сайте var b = request.time; // данные о проведенном времени // тут делаем с этими данными что хотим. });

Вот, собственно, и она. Разбирать подробно ничего не стану, т.к. это в принципе и не нужно. Достаточно знать наглядный пример, чтобы осуществить задуманное. Если в скрипте background.js добавить какие-либо данные в локальное хранилище(а также куки, web sql), то эти же данные можно будет использовать и в popup.js скрипте.

Вот собственно всё, что я хотел поведать о создании расширения, но я затрону еще один момент, в котором у меня возникли трудности.

На странице настроек мне необходимо было организовать перетаскивание сайтов в разные колонки.

Т.к. данные вставляются посредством InnerHtml, то данная возможность просто так не появится. Вот, что пришлось организовать:

$("#dannie").on("mouseover", ".sait", function() { $(this).css({"border":"3px solid #ffffff"}); }); $("#dannie").on("mouseout", ".sait", function() { $(this).css({"border":"3px solid black"}); }); $("#dannie").on("mousedown", ".sait", function() { $(this).css({"border":"3px solid black"}); }); $("#dannie").on("mouseover", ".sait", function() { $(".sait").draggable({ helper:"clone" }); });
вместо привычного:
$(".sait").mouseover(function(){ $("#"+this.id).css({"border":"3px solid #ffffff"}); }); $(".sait").mouseout(function(){ $("#"+this.id).css({"border":"3px solid black"}); }); $(".sait").mousedown(function(){ $("#"+this.id).css({"border":"0px solid black"}); }); $(".sait").draggable({ helper:"clone", });

Наиболее авторитетные люди в этой области - Wladimir Palant (он написал AdBlock Plus), ... Расширения для Chrome писать проще, чем для Firefox . Что интересно, самое популярное расширение для Chrome - это "Тюряга ВКонтакте" (по статистике Яндекса).

Задача состоит в разработке расширения, которое будет реагировать на открытие определенных HTML-страниц в браузере. Например, модифицировать HTML-код страницы сайта для более удобного использования этого сайта.
Решение опробовано на Google Chrome 24.x и Chromium 6.x (Debian 6.0.6, amd64).

Решение задачи

Конспект основных вопросов, возникающих по ходу написания crx-расширения.

  • Как оформить расширение?
  • Как установить таймер?
  • Как оформить расширение?

    Для минимального расширения достаточно 4-х файлов:

    128.png background.js content.js manifest.json

    Где manifest.json:

    { "manifest_version": 2, "name": "DomainCheck extension", "version": "0.1", "background": { "scripts": ["background.js"] }, "content_scripts": [ { "matches": [ "*://*/*" ], "js": [ "content..png" } // no web_accessible_resources }

    Файл background.js содержит код, исполняемый при старте браузера. В этом скрипте можно "повесить" обработчик загрузки контента документа (document.location.href).

    Chrome самостоятельно генерирует _generated_background_page.html:

    Для взаимодействия между background.js и content script, можно использовать сообщения (request/message) и chrome.extension.getBackgroundPage(). См. также описание архитектуры расширений Chrome (the architecture overview). Там сказано, что "A content script is some JavaScript that executes in the context of a page that"s been loaded into the browser".

    Примечание: в Chromium сложно отлаживать background page (background.js), так как нет соотв. вкладки на странице "Extensions" в developer mode.

    Как проверять текущий URL, обрезать его и вычислять хэш?

    Как устанавливать обработчик на DOMContentLoaded, рассказано на developer.chrome.com. См. также документацию про Background Pages (background.js).

    Для внедрения HTML-кода в страницу, можно использовать совет из статьи на Хабре (см. там упоминание глобальной переменной document), но нам нужно ещё и сравнивать текущий URL со списком.

    Is document.location.href deprecated?

    Пример со stackoverflow.com получения полного имени домена:

    var url = "http://www.domain.ru/tmp/file.txt?abc=1&cde=2#123" var getLocation = function(href) { var l = document.createElement("a"); l.href = href; return l; }; var l = getLocation(url); alert(l.hostname)

    Выделить поддомен 2-го уровня можно так:

    var l = getLocation(url); var d = l.hostname; function cutd(str) { var re = /.*?\.([\w\d-\u0100-\uffff-\.]+\.[\w\d-\u0100-\uffff-\.]+)/; return str.replace(re,"$1"); } alert(cutd(d));

    (см. подсказку на stackoverflow.com).

    sha1.js injection ...

    Как установить таймер? См. Sample Extensions: Event Page Example, background.js:

    chrome.alarms.create({delayInMinutes: 0.1}); chrome.alarms.onAlarm.addListener(function() { alert("Time"s up!"); });

    Этот баг в Chromium закрыт 9 января 2013 года, но ещё не появился в свежих сборках для Windows.

    Если всё-же решили использовать "современный" alarms, то как правильно устанавливать alarm-таймер произвольной длительности/периода, рассказано на stackoverflow.com.

    Для совместимости со старыми браузерами, лучше использовать window.setInterval() в background.js:

    var i = 0; window.setInterval(function() { alert(i); i++ }, 2*1000); // in milliseconds

    Функция setTimeout() - это одиночный "будильник".

    Disable-Enable chrome-расширения запускает по-новой background.js. *) надо проверить тоже самое для ухода в режим? и sleep.

    Как скачивать config.xml/time.txt, и как их парсить?

    Если скачать просто через XMLHttpRequest, то может вывалиться сообщение: "XMLHttpRequest cannot load http://сайт/config.xml. Origin http://www.google.ru is not allowed by Access-Control-Allow-Origin.". Это означает, что выполняя кросс-доменный запрос, вы не включили опцию для CORS, .htaccess:

    Header set Access-Control-Allow-Origin "*"

    Можно разрешить кросс-доменные запросы в расширении Chrome и через permissions в manifest.json:

    { "manifest_version": 2, ... "permissions": [ "http://сайт/" ],

    Впрочем, оба этих случая не работают для распакованных расширений Chromium . Для запакованных расширений, Chromium версий 6.0-7.0 иногда выдаёт "Bad magic number" (для чистого Chrome такая ошибка не наблюдается).
    Примечание: защиту CORS можно обойти в Chromium через опцию --disable-web-security:

    $ chromium-browser --disable-web-security http://domain/path

    Дополнение: для Chromium нужно подравить строки в manifest.json на следующие (указав явно домен и добавив звёздочку в пути):

    { "permissions": [ "http://сайт/*" ],

    Чтобы web inspector(?) в chrome не ругался при отключенном Интернете на XMLHttpRequest(), можно сделать так:

    var req = null; try { req = new XMLHttpRequest(); } catch(err) {} // see advice at stackoverflow.com

    (это написано по совету со stackoverflow.com). ...

    Для отладки парсинга, можно использовать console.log("строка"). Строки допускают переводы каретки с помощью "\n".

    Парсинг XML на JavaScript в chrome extension делается так:

    var xml = req.responseXML.documentElement; var ts = xml.getElementsByTagName("timeout"); var timeout = ts.textContent; if (ts) {console.log("timeout="+timeout);} var ds = xml.getElementsByTagName("domain"); if (ds) { for (var i = 0; i < ds.length; i++) { console.log("domain: "+ds[i].textContent); } }

    Как использовать глобальные атомарные переменные и табличные данные в chrome extension?

    Чтобы синхронизировать данные расширений при помощи Storage API, нужен Chrome версии >= 20. В манифесте (manifest.json) нужно написать следующее:

    "premissions": [ "storage" ]

    Как пишут в google group "Chromium HTML5", "I turned my attention to Web SQL Database but it seems Web SQL is no longer in "active maintenance" which leads me to believe that it will be dropped from HTML spec". См. подробнее W3C Web SQL Database, note. Можно попробовать использовать Basic concepts - для расширения места под базу можно использовать permissions: unlimitedStorage в manifest.json. Для использования "Unlimited storage" есть следующие Offline APIs: 1) App Cache; 2) File System; 3) IndexedDB; 4) WebSQL (deprecated). Пример использования IndexedDB в Chrome см. . Пример работы с IndexedDB см. на gist.github.com:

    window.indexedDB = window.indexedDB || window.webkitIndexedDB; var req = indexedDB.open("my db") req.onerror = function() { console.log("error"); }

    Расположение файлов IndexedDB,
    Windows: C:\Users\\AppData\Local\Google\Chrome\User Data\Default\IndexedDB,
    Linux: /home//.config/google-chrome/Default/IndexedDB/chrome-xxx.indexeddb.leveldb/:

    $ sudo ls -la /home/anonymous/.config/google-chrome/Default/IndexedDB/chrome-extension_ojeihbjghbabiocoglbfhdebhhckdnol_0.indexeddb.leveldb/ total 24 drwx------ 2 anonymous anonymous 4096 Фев 7 03:08 . drwx------ 3 anonymous anonymous 4096 Фев 7 03:08 .. -rw-r--r-- 1 anonymous anonymous 285 Фев 7 03:08 000003.log -rw-r--r-- 1 anonymous anonymous 16 Фев 7 03:08 CURRENT -rw------- 1 anonymous anonymous 0 Фев 7 03:08 LOCK -rw-r--r-- 1 anonymous anonymous 46 Фев 7 03:08 LOG -rw-r--r-- 1 anonymous anonymous 32 Фев 7 03:08 MANIFEST-000002

    Можно посмотреть на примеры использования IndexedDB в Mozilla Developer Network.

    Вставка большого числа записей в IndexedDB рассмотрена на stackoverflow.com.

    Для добавления элементов в IndexedDB, надо использовать

    indexedDB.db.transaction().objectStore().put({})

    В папке базы IndexedDB-данных старые базы хранятся как.sst-файлы, а новые (текущие) - как.log-файлы.

    setVersion() is Deprecated. Но тут есть одна хитрость [про onupgradeneeded()]: ...

    Как сказано в блоге Parashuram Narasimhan, "For Chrome: In case of chrome, the onupgradeneeded function is not called. The database"s onsuccess function is called. Here, the existence of the setVersion method is checked. If the method exists, and the specified version is greater than the database version, a the setVersion method is called. The onsuccess of the setVersion"s request call invokes the user"s onupgradeneeded method with the version transaction. Once the method completes, the versionTrasnaction is committed by closing the database. The database is opened again with the latest version and this is passed to the onsuccess defined by the user." (поэтому для вызова onupgradeneeded(), я делаю db.setVersion("3")).

    Как делать выборку данных в content.js:

    dbreq.onupgradeneeded = function(event) { console.log("dbreq.onupgradeneeded"); var db = event.target.result; var tx = db.transaction(["test_db"], "readonly"); var store = tx.objectStore("todo");

    Для открытия базы, используйте код от axemclion+jepp (функцию openReqShim).

    Когда открываем IndexedDB-базу, которая не существует, она создаётся (will be created) с номером версии, version = 0. При этом, вызываются onupgradeneeded() и onsuccess() последовательно. При первом вызове onupgradeneeded(), version уже = 1. Когда открываем второй раз [существующую базу], onupgradeneeded() уже не вызывается, а номер версии = 1. (?не увеличивается) Вызывается только dbreq.onsuccess().

    Ещё один момент. Тут сказано, что "With Chrome prior to 23 you need to create such a transaction manually by calling setVersion() - an API that has been removed from the spec. The older spec can be found at: http://www.w3.org/TR/2011/WD-IndexedDB-20110419/", то есть для того чтобы не было ошибки "NotFoundError: DOM IDBDatabase Exception 8" в логах хрома, требуется вызвать setVersion().

    В Chromium 6.0.472.63 (59945) реализация IndexedDB не является stable, поэтому отключена и не работает =)

    В общем, надо использовать background.js + iframe + обмен с контент-скриптами через messages

    Как отрабатывать событие об открытии страницы?

    "content_scripts": [ { "matches": [ "*://*/*" ], "js": [ "content.js" ], "run_at": "document_start" } ], ...

    Content.js:

    document.addEventListener("DOMContentLoaded", function () { alert("Abc "+document.location.href); });

    Как оформлять HTML-код страницы? См. написание расширений Firefox . Как упаковать расширение, и где его расположить?

    Чтобы расположить расширение на Chrome WebStore, нужно заплатить гуглю вступительный взнос 5$ (затем можно будет помещать любое количество расширений). Заплатить можно через VISA, MasterCard, AMEX или DISCOVER (дополнительно, при оплате, надо указать свой полный почтовый адрес и Имя-Фамилию).

    Для размещения расширения нужен Google-аккаунт, и далее. Там понадобятся скриншот и рекламная картинка. Обновлять код расширения надо будет вручную, через тот же Chrome WebStore (как я понял, тут нет автоматического обновления по URL, как в Firefox). В файле manifest.json нужно обновить версию расширения. Спустя несколько минут после добавления, расширение будет доступно в поиске по расширениям Chrome.

    Упаковка расширения под Linux:

    #!/bin/bash 7z a -tzip ../domainck-chromium.zip ./* mv ../domainck-chromium.zip ../domainck-chromium.crx

    Ключевые слова: Chromium builds for windows HOWTO, Google Chrome download page, Google Chrome sample extensions.

    Мне всегда хотелось рассказывать людям об интересных возможностях (технологиях), которые сейчас могут быть доступны каждому, но почему-то не доступны каждому. Да, получилось тавтология, но она в полной мере отображает моё внутреннее негодование на эту животрепещущую для меня тему. Как бы там ни было, речь сейчас будет не о том, как говорится. А поговорим мы сегодня о создании расширений для браузера Google Chrome (далее Хром).

    Расширение, которое мы будем разрабатывать на протяжении всей статьи можно найти в магазине Google Chrome Web Store , с той лишь разницей, что там присутствует расширенный функционал. Помимо этого, присутствует исходный код на GitHub , опять таки с оговоркой на то, что там всё написано на CoffeeScript , а здесь будет вестись повествование с JavaScript. Кстати, я не поклонник и не сторонник CoffeeScript, но штука довольно интересная и полезная - советую попробовать.

    Если вы когда-нибудь рассматривали идею создания расширения для Chrome, Firefox, Maxthon и прочих браузеров, то, наверное, уже успели заметить, что минимум усилий нужно приложить как раз таки для Хрома. Убедиться в этом можно, взглянув на документацию у соответствующих браузеров.

    Постановка задачи

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

    • Расширение должно скрывать все комментарии в социальной сети «VK»;
    • Расширение должно иметь возможность отображать комментарии;
    • Расширение должно иметь возможность отображать комментарии на конкретных страницах;

    С первого взгляда всё просто и нам по силам. Однако, в рамках статьи мы реализуем лишь первые два пункта.

    Предвижу вопросы, содержание которых может быть примерно таким: «Нафига скрывать комментарии, если в этом вся суть социальной сети?!». Что же, справедливый вопрос, заслуживающий развёрнутого ответа:

    Так сложились обстоятельства, что последнее время, когда я вижу комментарии в VK, мне хочется дарить горы фейспалмов комментаторам. Я подписан на большое количество различных пабликов, тематических (веб-разработка) и не очень. И как бы это не казалось странным, самым щедрым я становлюсь именно в группах с интересным для меня содержанием, а не котиками (в моём случае с пандами). Такого количества непрофессионального и безобразного потока информации в комментариях я не видел ещё нигде, да ещё и спорить думают. Помимо этого, комментарии в новостной ленте смотрятся не эстетично. В общем, сказано - сделано.

    Каркас расширения

    Наиприятнейшим для меня сюрпризом стало то, что в самом начале пути нас встречает описательный характер действий. Проще говоря, нам нужно описать наше приложение, его права и возможности - для этого служит файл manifest.json .

    Первым делом нужно заполнить три обязательных поля:

    { "manifest_version": 2, // Начиная с Chrome 18 ставим 2, иначе 1 (не поддерживается такое старьё) "name": "My Extension", // Название расширения "version": "versionString" // Версия расширения }

    Если с названием всё понятно, а с версией манифеста всё ещё проще, то поподробнее нужно остановиться с версией расширения.

    Итак, все мы привыкли, что версия чего-либо состоит из трёх чисел, разделённых точками - Мажорное.Минорное.Патч (Имеется в виду число). С npm, bower и прочими прелестями разговор короткий: либо так, либо никак. А вот Google предлагает следующие варианты:

    "version": "1" "version": "1.0" "version": "2.10.2" "version": "3.1.2.4567"

    Подробнее обо всех полях файла манифеста можно узнать из документации .

    В нашем случае, файл манифеста будет выглядеть следующим образом:

    { "manifest_version": 2, "name": "__MSG_app_name__", "short_name": "VKCommentBlocker", "description": "__MSG_app_description__", "version": "0.1.0", "default_locale": "ru", "permissions": [ "activeTab" ], "browser_action": { "default_icon": "icon_16.png", "default_title": "__MSG_browser_action__" }, "icons": { "16": "icon_16.png", "48": "icon_48.png", "128": "icon_128.png" }, "background": { "persistent": false, "page": "background.html" }, "content_scripts": [ { "matches": [ "http://vk.com/*", "https://vk.com/*" ], "css": [ "styles/commentblocker.css" ] } ], "web_accessible_resources": [ "styles/commentblocker_on.css" ] }

    Из того, что ранее не рассматривалось

    • __MSG_key__ - это вариация Chrome на тему интернационализации приложений (i18n). Можно применять как в файле манифеста, так и в других файлах (даже CSS).
    • web_accessible_resources - массив путей ресурсов, которые будут впоследствии использоваться в контексте веб-страниц. Без указания в нём пути - ничего не получится использовать на страницах сайтов, если такое поведение предполагается.
    Ресурсы расширения

    Огромный плюс в карму Chrome - мы уже сейчас можем подключить расширение, конечно, если созданы все ресурсы, указанные в manifest.json .

    Не думаю, что стоит заострять внимание на том, что содержится в файле commentblocker.css и commentblocker_on.css . Приведу лишь первый, в котором указаны все селекторы, в которых заключены комментарии:

    @charset "utf-8"; .wall_module .reply_link_wrap .reply_link { visibility: hidden !important; } .wall_module .replies_wrap, #wl_replies_wrap, #wl_reply_form_wrap, #mv_comments_wrap, #mv_your_comment, #pv_comments, #pv_comments_header, #pv_your_comment { display: none !important; visibility: hidden !important; } body:after { position: fixed; content: "__MSG_mode_enable__"; top: 5px; right: 5px; padding: 6px 12px; background-color: #ffc; border: 1px solid #ddd; z-index: 9999; }

    В файле commentblocker_on.css , как не трудно догадаться, всё наоборот. Обратите внимание, что прямо в CSS я использую строку с языковом ключом content: "__MSG_mode_enable__" . Самое время создать такой файл, где эти ключи будут храниться.

    В корне нашего расширения создаём директорию _locales и вложенные в неё директории en и ru . Далее в файле messages.json описываем наши ключи.

    { "app_name": { "message": "VK Comment Blocker" }, "app_description": { "message": "Удобный способ скрыть комментарии в новостной ленте и группах." }, "browser_action": { "message": "Переключить вид комментариев" }, "mode_enable": { "message": "Без комментариев!" }, "mode_disable": { "message": "С комментариями!" } }

    Помимо поля message есть и другие поля, о которых можно узнать из документации .

    Теперь создаём файлы background.html , для начала так:

    Background

    Тут всё так, как в обычном HTML - ничего необычного. Кстати, файл background.html можно не создавать, так как он генерируется автоматически, на основе полей в manifest.json .

    Запускаем расширение

    Запустить расширение можно, не написав ни одной строчки JavaScript. Чтобы сделать это, нужно пройтись по меню:

    • Настройка и управление Google Chrome (Гамбургер)
    • Дополнительные инструменты
    • Расширения
    • Поставить галочку напротив «режим разработчика»
    • Загрузить распакованное расширение
    • Выбрать папку с расширением

    Расширение загрузилось и показалось в меню. Да, да, это вот это вот «В».

    Казалось бы, у только что созданного нами расширения ничего нет в голове (нет никакой логики), а все комментарии на страницах социальной сети на букву «В» теперь скрыты. Ответ кроется в manifest.json , где в поле "content_scripts": {} мы указали на каких страницах (http://vk.com/* и https://vk.com/*) будет автоматически подключаться файл commentblocker.css , который и скрывает все комментарии. Советую подробнее почитать про mathes patterns . Он лишь с виду так прост, а под капотом чуть ли не сивый мерин, да с прибамбасами.

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

    Оживляем расширение

    Осталось выполнить второй пункт задачи, а именно реализовать возможность отображения комментариев. Вкратце, нам нужно запихнуть файл commentblocker_on.css , который отменит правила файла commentblocker.css . И тут к нам на помощь спешит наш всемогущий JavaScript.

    Помните, что я говорил про background.html ? Да, да, про то, что его можно не создавать. Давайте слегка изменим manifest.json:

    ... "background": { "persistent": false, "scripts": [ "scripts/commentblocker.js" ] }, ...

    Просто подключили JavaScript файл. Ничего особенного. Переходим к этому файлу.

    Просто так запихнуть JS на страницу нельзя. И такая же проблема имеется не только со скриптами. Поэтому нам нужно воспользоваться специальной инъекцией executeScript .

    Сначала нужно добавить обработчик события клика на иконку расширения:

    Chrome.browserAction.onClicked.addListener(function(tab) { chrome.tabs.executeScript(tab.id, { code: "(" + toggleComments.toString() + ")();" }); });

    Где toggleComments - это функция, которая и будет производить инъекцию нашего CSS файла на страницу:

    Var toggleComments = function() { var extensionLink; (document.getElementById("extension") == null) ? (extensionLink = document.createElement("link"), extensionLink.href = chrome.extension.getURL("/styles/commentblocker_on.css"), extensionLink.id = "extension", extensionLink.type = "text/css", extensionLink.rel = "stylesheet", document.getElementsByTagName("head").appendChild(extensionLink)) : (document.getElementsByTagName("head").removeChild(document.getElementById("extension"))) };

    Думаю, что слов о том, что этот кусок кода проверяет наличие подключения нашего CSS на странице и делает выводы о необходимости его подключения или отключения, будет достаточно.

    Между прочим, доступно не так много событий , которые покрывают различный спектр потребностей. Например, есть такие события:

    • onCreated - создание вкладки.
    • onUpdated - обновление вкладки.
    • onRemoved - закрытие вкладки.

    Стоит заметить, что событие onUpdated вызывается дважды:

    • Обновление страницы;

    На StackOverflow советуют проверять статус страницы:

    Chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) { if (changeInfo && changeInfo.status === "complete") { ... } });

    Теперь при нажатии на иконку будет происходить подключение файла стилей, которые будут отображать комментарии, а повторный клик на иконку - вновь их скрывать.

    Выводы

    Как нельзя кстати, стоит упомянуть мою полную версию расширения VK Comment Blocker, которая доступна в

    В интернете много сайтов на которых реализована возможность пролистывать страницу вверх без помощи мыши или полосы прокрутки. Но в то же время еще есть сайты где такой реализации нет. "Почему бы не попробовать написать скрипт который бы добавлял такую кнопку на все сайты?" - подумал я и принялся за реализацию. Такие скрипты называются пользовательскими и имеют расширение *.user.js . Почитать к примеру можно на хабре . К сожалению без "подводных камней" не обходится. Существуют некоторые различия в реализации userjs под разные браузеры. Я остановлюсь на описании реализации нашего userjs скрипта как расширения для браузера Google Chrome .

    manifest.json

    Обязательным для расширения Google Chrome является файл manifest.json в котором описываются параметры, пути к внешним файлам (*.js, *.css и др.), поддержка версии и т.п. для расширения. Подробнее о файле можно почитать . В нашем случае файл manifest.json выглядит следующим образом:

    { "manifest_version": 2, "content_scripts": [ { "exclude_globs": , "include_globs": [ "*" ], "js": [ "jquery.js", "backTopUserJS.user.js" ], "css": [ "css/style.css" ], "matches": [ "http://*/*", "https://*/*" ], "run_at": "document_end" } ], "converted_from_user_script": true, "description": "Back top userscript extension for google chrome", "name": "backTopUserJS", "version": "1" }

    Для удобства мы используем библиотеку JQuery которую положили рядом с файлом manifest.json и основным файлом скрипта (в нашем случае это backTopUserJS.user.js). Его содержимое следующее:

    // ==UserScript== // @name backTopUserJS // @author Aleksandr Filatov // @license MIT // @version 1.0 // ==/UserScript== function main() { var disp = $(window).scrollTop() > 400 ? "block" : "none"; var $upButton = $("

    "); $(document).find("body").append($upButton); $upButton.click(function () { $("html, body").animate({ scrollTop: 0 }, "slow"); }); $(window).scroll(function () { if ($(window).scrollTop() > 400) $upButton.fadeIn("slow"); else $upButton.fadeOut("slow"); }); }; var script = document.createElement("script"); script.appendChild(document.createTextNode("("+ main +")();")); (document.body || document.head || document.documentElement).appendChild(script);

    Думаю скрипт достаточно понятен, чтобы его подробно рассматривать. Пояснить стоит лишь последние 3 строчки. На самом деле это мелкий хак, который вставляет наш скрипт в код страницы сайта. Если кто-то из вас найдет способ лучше можете написать к комментах свои фиксы:)

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

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

    Создаем папку для нашего расширения например так C:\MyChromeExtensionUserJS

    Для каждого расширения создаем свою директорию например в нашем случае назовем ее так C:\MyChromeExtensionUserJS\backTopUserJS . Добавляем файлы расширения в эту директорию.

    Заходим "О браузере Google Chrome" -> вкладка "Расширения" или в адресной строке пишем chrome://extensions/

    Ставим галочку "Режим разработчика"

    Нажимаем кнопку "Загрузить распакованное расширение" и выбираем директорию нашего расширения. Нажимаем "OK".

    Расширение установлено и готово к работе. Чтобы обновить расширения после того как вы внесли в него изменения достаточно просто нажать кнопку "Обновить расширение" или в режиме разработчика сочетание клавиш Ctrl+R .

    Итог

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



    Похожие публикации