Доступ к JavaScript-окружению веб-страницы из расширения для Chrome

от автора

Как известно, расширения для браузера Chrome работают каждое в своей песочнице. Для внедрения в веб-страницу существует механизм content script-ов, когда javascript код внедряется в страницу, и имеет доступ к DOM дереву. Этот механизм позволяет работать с контентом страницы, изменять внешний вид, запускать произвольный JS, обмениваться данными с фоновым процессом расширения.

Но одну вещь механизм content script не позволяет делать — получать доступ к javascript-окружению веб-страницы.


Когда я делал расширение для скробблинга прослушиваемой музыки из vk.com на last.fm, для мониторинга состояния плеера я сначала использовал парсинг DOM элементов плеера) Это было ужасно, и после первого же изменения в верстке всё сломалось. Поэтому пришлось придумать что-то понадёжнее. Поиски в интернете привели к простому и, казалось бы, очевидному механизму внедрения своего кода в веб-страницу:

1. Из контент скрипта создаём в DOM дереве страницы элемент «script» со своим кодом.

//исполнить скрипт vk_inner в контексте vk.com var script=document.createElement('script'); script.type='text/javascript'; script.src=chrome.extension.getURL("js/vk_inner.js"); 

2. Код производит патчинг функций, работа с которыми необходима. Для патчинга используем метод addCallListener, найденный где-то на просторах интернета. Он заменяет собой целевую функцию, и вызывает обработчики перед и после её запуска:

Function.addCallListener = function(func, callbacks) {     var successNumber = 0,         errorNumber = 0,         name = func.name;      return function() {         var args = [].slice.call(arguments);         var result, error;          var props = {             args: args,             self: this,             name: name         }           callbacks.before && callbacks.before(props);          try {             result = func.apply(this, arguments);             props.successNumber = ++successNumber;             props.result = result;             props.status = 'success';             callbacks.success && callbacks.success(props);         } catch (e) {             props.errorNumber = ++errorNumber;             props.error = e;             props.status = 'error';             callbacks.error && callbacks.error(props);         }          callbacks.after && callbacks.after(props);          return result;     } } 

3. Производим патчинг нужных функций. В случае vk.com это функция audioPlayer.onPlayProgress, она вызывается каждую секунду во время проигрывания:

var ARTIST_NUM = 5; var TITLE_NUM = 6; var artistElem = document.getElementById('vkScrobblerArtist'); var titleElem = document.getElementById('vkScrobblerTrackTitle');  //вешаем слушатель прогресса песни audioPlayer.onPlayProgress = Function.addCallListener(audioPlayer.onPlayProgress, { 	after: function(props) { 		//сохраняем исполнителя и песню 		artistElem.innerHTML = audioPlayer.lastSong[ ARTIST_NUM ]; 		titleElem.innerHTML = audioPlayer.lastSong[ TITLE_NUM ]; 	} }); 

Теперь при работе плеера в элементах artistElem и titleElem будут находиться актуальные название песни и её исполнитель. Остаётся только периодически проверять их содержимое и делать с ним что угодно. Можно избавиться от периодической проверки содержимого элементов, если использовать механизм событий jQuery. Таким образом можно устроить обмен любыми сериализуемыми данными между js веб-страницы и расширением.

ссылка на оригинал статьи http://habrahabr.ru/post/186762/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *