
Access JavaScript web pages from Chrome extension
As you know, the extensions for the Chrome browser work each in its own sandbox. There is a mechanism for content scripts for embedding in a web page, when javascript code is embedded in the page and has access to the DOM tree. This mechanism allows you to work with page content, change the appearance, launch arbitrary JS, exchange data with the background extension process.
But one thing the content script mechanism does not allow you to do is access the javascript environment of the web page.
When I made an extension to scrobble the listening music from vk.com to last.fm, I first used the player's DOM parsing to monitor the player’s state) It was terrible, and after the first change in the layout, everything broke. Therefore, I had to come up with something more reliable. Searches on the Internet have led to a simple and seemingly obvious mechanism for embedding your code in a web page:
1. From the script content, create a “script” element with your code in the DOM tree of the page.
2. The code patches the functions that you need to work with. For patching, we use the addCallListener method, found somewhere on the Internet. It replaces the objective function, and calls the handlers before and after its launch:
3. We patch the necessary functions. In the case of vk.com, this is the audioPlayer.onPlayProgress function, it is called every second during playback:
Now, when the player is working, the artistElem and titleElem elements will contain the actual song name and its artist. It remains only to periodically check their contents and do anything with it. You can get rid of periodically checking the contents of elements by using the jQuery event mechanism. Thus, it is possible to arrange the exchange of any serializable data between the js of the web page and the extension.
But one thing the content script mechanism does not allow you to do is access the javascript environment of the web page.
When I made an extension to scrobble the listening music from vk.com to last.fm, I first used the player's DOM parsing to monitor the player’s state) It was terrible, and after the first change in the layout, everything broke. Therefore, I had to come up with something more reliable. Searches on the Internet have led to a simple and seemingly obvious mechanism for embedding your code in a web page:
1. From the script content, create a “script” element with your code in the DOM tree of the page.
//исполнить скрипт vk_inner в контексте vk.com
var script=document.createElement('script');
script.type='text/javascript';
script.src=chrome.extension.getURL("js/vk_inner.js");
2. The code patches the functions that you need to work with. For patching, we use the addCallListener method, found somewhere on the Internet. It replaces the objective function, and calls the handlers before and after its launch:
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. We patch the necessary functions. In the case of vk.com, this is the audioPlayer.onPlayProgress function, it is called every second during playback:
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 ];
}
});
Now, when the player is working, the artistElem and titleElem elements will contain the actual song name and its artist. It remains only to periodically check their contents and do anything with it. You can get rid of periodically checking the contents of elements by using the jQuery event mechanism. Thus, it is possible to arrange the exchange of any serializable data between the js of the web page and the extension.