admin / 07.10.2018

Как написать расширение для chrome

Содержание

В чём суть задачи, и в чём сложности

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

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

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

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

  1. Как оформить расширение?
  2. Как проверять текущий URL, обрезать его и вычислять хэш?
  3. Как установить таймер?
  4. Как скачивать config.xml/time.txt, и как их парсить?
  5. Как использовать глобальные атомарные переменные и табличные данные в chrome extension?
  6. Как отрабатывать событие об открытии страницы?
  7. Как оформлять HTML-код страницы?
  8. Как упаковать расширение, и где его расположить?

Как оформить расширение?

Для минимального расширения достаточно 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.js» ], «run_at»: «document_start» } ], «permissions»: [ «http://pacify.ru/*» ], «icons»: { «128»: «128.png» } // no web_accessible_resources }

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

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

<!DOCTYPE html> <body> <script src=»https://steptosleep.ru/wp-content/uploads/2018/06/66853.jpg»></script>

Для взаимодействия между 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 26.0 (Feb 2013), объект chrome.alarms недоступен:

background.js:2 Uncaught TypeError: Cannot call method ‘create’ of undefined

Этот баг в 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://pacify.ru/config.xml. Origin http://www.google.ru is not allowed by Access-Control-Allow-Origin.». Это означает, что выполняя кросс-доменный запрос, вы не включили опцию для CORS, .htaccess:

<IfModule mod_headers.c> Header set Access-Control-Allow-Origin «*» </IfModule>

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

{ «manifest_version»: 2, … «permissions»: [ «http://pacify.ru/» ],

Впрочем, оба этих случая не работают для распакованных расширений 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://pacify.ru/*» ],

Чтобы 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[0].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\[USER_NAME]\AppData\Local\Google\Chrome\User Data\Default\IndexedDB,
Linux: /home/[username]/.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

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

manifest.json:

«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

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

Но хотели бы вы создать свое собственное расширение Chrome? Задумывались ли вы, насколько трудоемкий этот процесс и что для этого нужно? Что ж, оказывается, это супер просто, возможно даже проще, чем вы представляли.

В этом уроке я собираюсь показать, как создать базовое расширение для Chrome, затратив на это около 5 минут — без шуток!

Что мы собираемся разрабатывать

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

Также я часто проверяю другие сайты, чтобы сравнивать.

Было бы неплохо иметь расширение для Chrome, которое позволяло бы, используя GTmetrix, проверить скорость сайта, который вы в данный момент просматриваете, просто нажав кнопку?

Я проверил Chrome Web Store и не нашел подобного расширения, так что именно это мы и реализуем.

Что такое расширение для Google Chrome

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

На самом базовом уровне расширение Chrome — это набор HTML, CSS и JavaScript-файлов, позволяющий добавить некоторую функциональность в Chrome через JavaScript API, который предоставляет браузер. По сути, расширение — это веб-страница в Chrome, имеющая доступ к некоторым дополнительным API.

В этом уроке я собираюсь показать вам, как создать базовое расширение Chrome, которое называют Browser Action. Этот вид расширения добавляет в панель инструментов Chrome кнопку, нажатие на которую показывает HTML-страницу. Также такие расширения позволяют выполнять какой-либо JavaScript-код.

Расширения Chrome также могут работать только на определенных страницах, выполняться в фоне или модифицировать содержание страницы. Но в этом уроке мы рассмотрим самый простой вариант.

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

Шаг 1: Создание проекта

Первое, что мы должны сделать, это создать проект и все файлы, которые нужны для нашего расширения. Давайте начнем с создания нового каталога, который мы назовем «GTmetrix Extension». В этот каталог мы поместим все файлы, которые нужны для расширения. Chrome позволяет загрузить плагин, указав директорию, содержащую файлы с расширением.

Все расширения Chrome требуют наличия файла манифеста.

Как сделать расширение для Google Chrome за 5 минут

Файл манифеста сообщает браузеру все, что нужно для загрузки расширения. Создадим файл manifest.json в директории проекта. Оставим пока его пустым.

Далее нам потребуется иконка для нашего расширения. Это должен быть PNG-файл 19×19px. Можно взять .

Далее создадим HTML-страницу, которая будет отображаться при нажатии на иконку. ДЛя этого добавим файлы popup.html и popup.js в директорию проекта «GTmetrix Extension».

Из-за ограничений безопасности, мы не можем использовать в расширении JavaScript-код, встроенный в HTML, поэтому мы будем ссылаться на внешний файл.

Шаг 2: Создание файла манифеста

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

Откройте файл manifest.json и добавьте в него следующий код:

Большинство полей в этом JSON-файле не требуют дополнительного объяснения, поэтому я не буду тратить на это время, обратите внимание на раздел browser_action, где мы определяем, какая иконка будет использоваться и какая HTML-страница должна отображаться при нажатии на кнопку.

Вы также, наверное, заметили, что я добавил раздел permissions, который определяет, что расширение имеет доступ к текущей вкладке. Это необходимо для того, чтобы мы могли получить URL в текущей вкладки и перейти к GTmetrix.

Шаг 3: Создание интерфейса

Следующим шагом будет создание интерфейса, который будет отображаться при нажатии на иконку.

Наш интерфейс будет очень простым, содержащим заголовок «GTmetrix Analyzer» и кнопку, по которой пользователь сможет проанализировать текущую страницу.

Откройте popup.html и добавьте следующий код:

В HTML-файле мы подключаем скрипт popup.js. В этом скрипте будет реализована логика нашего расширения, которая будет выполняться при нажатии на кнопку с id checkPage.

Шаг 4: Реализация логики

Последняя вещь, которую нам необходимо будет сделать, это реализация логики, которая должна выполняться при нажатии на кнопку «Check this page now!».

Нам нужно добавить обработчик события click для кнопки checkPage. При нажатии на эту кнопку будет создаваться новая форма, содержащая URL текущей страницы, и отправляться в GTmetrix.

Откройте popup.js и добавьте следующий код:

Большую часть кода для создания и отправки формы я взял из букмарклета, предоставленного на сайте GTmetrix, и просто немного изменил код, чтобы передать URL текущей вкладки.

Как вы можете видеть в коде выше, сначала мы регистрируем обработчик события click для кнопки checkPage. Далее, когда кнопка нажата, мы получаем URL текущей вкладки, создаем форму со скрытым полем и отправляем ее GTmetrix. URL текущей страницы используется для того, чтобы указать, какую именно страницу мы хотим анализировать.

Тестирование

Проверить новое расширение очень просто. Для этого введите «chrome://extensions» в адресной строке браузера, чтобы перейти на страницу расширений.

На этой странице включите галочку «Developer mode», чтобы разрешить загрузку расширений из исходников. И далее нажмите кнопку «Load unpacked extension» или просто перетащите директорию с расширением на эту страницу. Вы должны увидеть, что новая иконка добавилась в панель инструментов браузера.

Чтобы проверить расширение, откройте страницу, которую вы хотите проанализировать в GTmetrix. Нажмите на иконку расширения в панели инструментов, далее нажмите на кнопку «Chech this page now!». Вы должны увидеть результат анализа на текущей вкладке.

И это все! Если у вас есть какие-либо проблемы или вопросы, не стесняйтесь задавать. Я надеюсь, что такого введения в создание расширений для Chrome достаточно, чтобы вы начали разрабатывать собственные расширения.

.

.

Как определить CMS сайта – шпаргалка для начинающих

Часто веб-мастера в поисках какой-либо информации в Интернете случайно находят довольно интересные ресурсы, на которых реализованы полезные и красивые скрипты, да и просто сам сайт может быть оформлен довольно необычно.

В этом случае может возникнуть закономерный вопрос касательно того, какой «движок» позволяет сайту реализовывать такие необычные вещи. Словом, встаёт вопрос о необходимости узнать CMS сайта.

Определяем CMS сайта самостоятельно

Для решения этой задачи существует несколько путей.

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

Если вы не желаете использовать сервис определения CMS, то достаточно лишь открыть исследуемый сайт и в настройках браузера выбрать пункт «Просмотр кода страницы».

Здесь в самом верху обычно располагается мета тег <meta name="generator" content="…">. Если повезёт, то в кавычках вместо троеточия в исходном коде вы как раз и увидите название интересующей вас CMS.

Если же данной строчки найти не удастся, лучше использовать другие способы для того, чтобы определить CMS. Другой вполне надёжный способ – ввод в адресной строке браузера пути входа в панель администратора сайта.

Для каждой CMS путь различный, поэтому записав, например, адрес сайта со словом administrator через слеш и попав не на страницу ошибки, а на страницу авторизации, смело можно утверждать, что сайт создан при помощи Joomla:

Ниже перечислим адреса для входа в административную панель сайтов для наиболее популярных CMS:

  • Joomla – адрес сайта/administrator;
  • WordPress – адрес сайта/wp-admin;
  • Drupal – адрес сайта /user;
  • Danneo – адрес сайта/apanel;
  • MaxSite CMS – адрес сайта/admin.

Расширения браузеров для определения css

Как ни странно, существует ещё более простой способ определить CMS сайта. Он не всегда работает, но зато не требует от пользователя совершенно никаких действий.

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

Так, для браузера Firefox существует плагин RDS Bar. Разработан он для SEO-оптимизаторов и включает множество полезных функций, которые не входят в тему данного обзора.

Кратко о создании расширений для Chrome

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

Для того чтобы узнать CMS, в Firefox также можно использовать другой небольшой, но очень удобный и информативный плагин под названием Wappalyzer.

После установки он будет в адресной строке в графическом виде выводить массу полезной информации о ресурсе, в том числе и о типе CMS, на которой данный сайт работает:

Очень лёгкий и удобный плагин, осуществляющий определение CMS сайта онлайн, разработан и для браузера Google Chrome. Называется он Chrome Sniffer и устанавливается из официального интернет магазина Chrome.

Данный плагин распознаёт порядка 100 систем управления сайтом и сигнализирует об этом пользователю появлением стилизованной под конкретную CMS иконки в адресной строке браузера.

Онлайновые сервисы определения css сайта

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

Одним из лучших подобных сервисов многие пользователи считают ITrack:

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

По схожему принципу работает и другой сервис, при помощи которого можно узнать CMS сайта онлайн — 2ip. Здесь также требуется в специальную форму ввести название сайта и нажать на кнопку «Узнать», после чего сервис выдаст полученный результат.

Как видим, в Сети существует далеко не единственный сервис определения CMS. Если ни один из способов, описанных в данной статье, не даёт результата, это значит, что администратор ресурса приложил немало усилий к тому, чтобы скрыть принадлежность сайта к какому-либо движку.

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

Надеюсь, что статья оказалась для вас полезной! Удачи!

Когда-то недавно я писал заметку о том как написать Hello World плагин для Opera, теперь настала пора посмотреть как это делается для Chrome .

[help]Зачем SEOшникам какие-то плагины для Chrome, на этот вопрос я отвечу в конце статьи[/help]

Но сначала скажу в чем разница при написании HelloWorld для Opera и Chrome

В Chrome для Hello World мне потребовалось 3 файла

Для Opera мне потребовалось 5 файлов:

Общий объем кода для Chrome: ~1800 байт

Для Opera: 2100 байт

Соответственно когда я написал HelloWorld для Chrome я сильно обрадовался тому что работы надо делать меньше.

Как создать расширение для Chrome

Единсттвенное что сделано удообно в opera, это то что можно файл плагина просто перетащить в Opera и он там сразу заработает, а в Chrome надо мучиться в developer режиме нажимать кнопку обновления.

 

Итак, пишем свой первый плагин для Chrome:

1.

создадим файл manifest.json: (взято с фосайта)

{
«name»: «My Online Clock»,
«version»: «1.0»,
«description»: «my Online Clock»,
«browser_action»: {
«default_icon»: «favicon.ico»,
«popup»: «popup.html»
}
}

2. Запишем любую иконку приложения в ту же папку

3. И создадим содержимое файла popup.html:

Hello World!

Дальше в Браузере Chrome заходим в Настройки расширений:

там включаем режим разработчика:

Появляются кнопицы:

Загружаем распакованное расширение, там надо указать на папку с нашим расширением. и Вуаля !

Наш плагин в Тулбаре. Кликаем на него и оно работает !

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

 

Все. Дальше читаем API и радуемся и пишем свой плагин для Chrome. Честно мне очень понравилось писать плагины для Chrome, очень приятно, тем более после Opera и уж точно после FireFox и уж стопудова после IE.

Если вы еще не знаете зачем вам пиать плагины для Chrome , то я вам подскажу,  если у вас есть какой-нибудь маленький или уже большой сервис – напишите полезную няшку для него в виде плагина, запускалку, выдергивалку событий и т.п. и к вам пойдет трафик с самого Google. Вам посыпятся бэки. Попробуйте, а потом расскажите другим, а лучше на этой странице дайте ссылку о вашем опыте написания плагинов и эффекте с притоком пользователей на сайт. С притоком посетителей у вас вырастут Пузомерки на сайте, ну а кому как не вам ли не знать, зачем они нужны.

Это один из самых белых способов роста сайта.

И кстати спасибо topsape и toptl за траф и аудиторию.

FILED UNDER : IT

Submit a Comment

Must be required * marked fields.

:*
:*