You can’t just take it and turn to the background page
- Tutorial
It’s all about a security policy similar to cross-domain. Access to the pages of other tabs or to the extension’s background page is deliberately limited, because they are considered pages of other domains, have restrictions on direct access to the scripting environment, similar to other people's windows and frames. The message mechanism "saves" as with cross-domain access between frames, as well as in access to extension pages (background, settings, popup, ...).
In the browser extension, Google Chrome (and Chromium) is most important by function - the background page. It has a special URL of the form chrome-extension: // ciegcibjokpkcklhgbpnmnikpkmkhbjk /, where the long domain name is a random name created in the bowels of the browser, which also refers to the extension directory somewhere in the OS service folder. From the content script (similar to user scripts executed on the browser page) you can access the files and pictures of the extension. But it is impossible to perform many functions, the path to which lies through the background page: to arrange storage related to the group of real domain names; store extension settings common to the entire extension. You just need to getto Mordor to the background page. However, you can’t just do it by URL.
If we get access to pages in windows and frames of the same domain directly (like to a window of another scripting environment), then the background page, as well as other tabs and browser pages, act as pages of other domains. We can send messages to them, realizing the pattern of weakly connected procedures, and we can only transfer objects capable of serialization into them - no DOMs, environments, or other complex objects.
There is something to strive for the background page. It performs functions that are inconceivable in a script or user script. There is not only memory common to all pages. There is an implementation of many of the interfaces described at developer.chrome.com/extensions/api_index.html , and even more are expected in the future. In the meager message interface, there are callback functions that, when given, are executed in the calling function, on the side of their environment, but with the result parameter passed by the other side.
All this is a slightly modified interface of the Javascript postMessage environment , created not so long ago in browsers to solve cross-domain access regulation problems. It is usually used to communicate scripts between windows and frames and remains the only convenient means of communication if the page domains are different. Previously, a crutch was used under the code name “window.name” to solve this problem ., by keywords of used objects. It works now, but it’s complicated and obviously slow compared to the replacement postMessage method. Recall how the regular postMessage mechanism works , because it is based on the data exchange mechanisms in the Chrome extension.
The initiating page of the message creates a custom event “message” - it sends the message, and it does this not from its domain , but from the one it wants to access. (IE8 has a limitation - accessing the frame’s parent window only).
If the domain name is guessed, a message will be sent. To receive it in the otherWindow window, a couple of conditions must be fulfilled:
1) an event handler exists;
2) the handler “waits” for the message exactly from the sending domain and checks this through the second argument.
In extensions, the postMessage framework has been slightly redesigned by adding callbacks and automating domain checks. It became more convenient than if you collect the message from the bricks. At the very end, we present the code on the standard postMessage, using available means to show that there is no magic in the following sendMessage or sendRequest - it's just a shell. But postMessage could not be completed due to insufficient extension rights to create a frame with the special protocol "chrome-extension: // *", about which the format of the manifest file also does not know anything, and the matter was drowned in bureaucratic delays . This is the answer to the question why they had to come up with their exchange functions.
Example 1.
If you need to send from the tab to the background page and finish here, a simple message format is used:
A message is accepted in the background or any other page:
Example 2.
This pair of functions is adapted to exchange a feedback message. Add parameters from the script:
from the background page:
f_callback - executed in the script (not in the background page), but with the parameter specified in the background.
The sender object has the form:
Thus, something is known from him about who sent the message. Now we will not use the values from sender, but it is possible to track from which tab or window the message came, send changes to other content scripts, if they are in other tabs.
(end of example 2)
If you need to pass something more complicated - there is no problem embedding a hash or array instead of a string. Passing a hash instead of a set of arguments - in general, a more advanced form of exchange - takes 1 argument, is easier to read, as it is self-documenting with keys, hash values are rearranged and deleted elementarily - no need to remember the order and worry about missing values.
With the technique of replacing an argument with a hash, 3 arguments are enough for everything, and each of them fulfills its role. Going further, they could be replaced with 1 argument, with the keys {request: ..., sender: ..., callback: f () {...}}; why didn’t you? Apparently, they considered that 3 arguments is still such a gentlemanly limit that it is not shameful to offer to remember (and to further decipherment). In addition, symbols are spent on self-documentation. When the arguments are 2-3, the choice is leaning toward positional arguments.
For example, you need to send not only the name of the command, but also a hash with the data. We write from the script:
from the background page:
Shorten frequently repeated calls from content pages:
(For some reason, the third callback is not provided ... The management is transferred only 2 times.)
Example 3.
If there is an inverse problem , send a message from the background page to, say, the active tab:
in the background, write:
in content:
Is it important that in the first case we used the sendMessage-onMessage pair , and in the second - sendRequest-onRequest ? No, any method works, if only it were from one pair.
Example 4.
If you need to send a message with a reverse callback, in the background we write:
On the content page:
It is executed, as you can see - everything that was described in the documentation, but for a slightly different object (chrome.runtime), on developer.chrome.com/extensions/messaging.html , and chrome.runtime does not work in this task - does not have a method sendMessage.
Despite the negative result, this transfer attempt will tell you guesses about why developers had to deal with their own data transfer functions in extensions, despite the presence of postMessage .
One of the few special extension methods executable in the content script is chrome.extension.getURL ('path') . It returns the path to extension resources. Opening it, we get the extension resources (but not to directories) - pictures and texts from it. A different domain will play a role here: in order to get texts surrounded by the page script, you need to do cross-domain Ajax. Or it’s easier to do it - to receive texts through the domain wall using the message mechanism.
Example 5
Let's transfer the message using the defined protocol ("chrome-extension:") with a domain (something like "// ciegcibjokpkcklhgbpnmnikpkmkhbjk").
But not everything is simple. The content script does not have access to the background page window. The method is:
But no access:
Uncaught Error: "getBackgroundPage" can only be used in extension processes. See the content scripts documentation for more details.
Commando is not discouraged. For a simple exchange, you need to create a frame. Nothing prevents you from creating a frame.
An error occurred:
Denying load of chrome-extension: // ciegcibjokpkcklhgbpnmnikpkmkhbjk / . Resources must be listed in the web_accessible_resources manifest key in order to be loaded by pages outside the extension.
Add permission to the manifest (and with chrome.extension.sendMessage this was not necessary).
"Permissions": ["chrome-extension: // *", ...]
An error occurred on chrome: // extensions /:
When installing the extension, warnings occurred:
Permission 'chrome-extension: //' is unknown or URL pattern is malformed.
If access were not denied, it would remain to execute
and in the background -
We can end the example with this - it showed that access through the standard function was closed for other reasons - due to the need to view background pages in frames, which, apparently, was unacceptable for security (in "manifest_version": 2). Therefore, in Chrome extensions, but also for reasons of syntax optimization and bidirectional exchange, special messaging methods were invented.
An example showed that there are no special tricks in sending messages to the background page. But we need special permissions, which we do not have for extensions. The private part of the methods of the chrome.extension object calmly does this, but does not give others. (We still need to try to do this through sandbox.html - will it allow you to create a frame with a background page in it.)
To install a test extension and a test page.
In the browser extension, Google Chrome (and Chromium) is most important by function - the background page. It has a special URL of the form chrome-extension: // ciegcibjokpkcklhgbpnmnikpkmkhbjk /, where the long domain name is a random name created in the bowels of the browser, which also refers to the extension directory somewhere in the OS service folder. From the content script (similar to user scripts executed on the browser page) you can access the files and pictures of the extension. But it is impossible to perform many functions, the path to which lies through the background page: to arrange storage related to the group of real domain names; store extension settings common to the entire extension. You just need to get
This description is not on the documentation pages for Chrome. Rather, it exists, but for “a few other” methods and objects, which in fact means no, until it has been fixed. The behavior of the described messaging objects is confirmed by several examples in the responses to StackOverflow. Searching for answers each time with disparate examples is tiring, so to end this mess, let them be gathered in one place here. All 4 combinations of direct and callbacks have been sorted, so that before their eyes there are always working patterns of them.
To check and demonstrate, you need to copy or create 3 files (manifest.json, script.js, background.js), creating a test extension in developer mode in Chrome, and look at the messages in the console from 2 pages - browser windows with an embedded script script. js and background (watch the background.js background console - by clicking on the link “Check view modes: _generated_background_page.html” in chrome: // extensions /). The same will work in some ready-made extension if 2 fragments of codes from the article are arranged in pages and executed.
On Habré, a similar problem was solved for applied purposes in habrahabr.ru/post/159145 , November 2012 - the same sources of knowledge that they tacitly corrected and used. On the Internet in Russian, a similar problem was described in the articlefrom June 2012. Here - the mechanism that is used to solve such problems is described, taking into account the fact that it is executed in the extension scripts of the Google Chrome browser.
If we get access to pages in windows and frames of the same domain directly (like to a window of another scripting environment), then the background page, as well as other tabs and browser pages, act as pages of other domains. We can send messages to them, realizing the pattern of weakly connected procedures, and we can only transfer objects capable of serialization into them - no DOMs, environments, or other complex objects.
There is something to strive for the background page. It performs functions that are inconceivable in a script or user script. There is not only memory common to all pages. There is an implementation of many of the interfaces described at developer.chrome.com/extensions/api_index.html , and even more are expected in the future. In the meager message interface, there are callback functions that, when given, are executed in the calling function, on the side of their environment, but with the result parameter passed by the other side.
All this is a slightly modified interface of the Javascript postMessage environment , created not so long ago in browsers to solve cross-domain access regulation problems. It is usually used to communicate scripts between windows and frames and remains the only convenient means of communication if the page domains are different. Previously, a crutch was used under the code name “window.name” to solve this problem ., by keywords of used objects. It works now, but it’s complicated and obviously slow compared to the replacement postMessage method. Recall how the regular postMessage mechanism works , because it is based on the data exchange mechanisms in the Chrome extension.
PostMessage interface (cross-browser, IE8 +)
The initiating page of the message creates a custom event “message” - it sends the message, and it does this not from its domain , but from the one it wants to access. (IE8 has a limitation - accessing the frame’s parent window only).
otherWindow.postMessage(message, targetOrigin);
//message - строка или другой _сериализуемый_ объект
//targetOrigin - название целевого домена окна otherWindow, для "проверки знания"
If the domain name is guessed, a message will be sent. To receive it in the otherWindow window, a couple of conditions must be fulfilled:
1) an event handler exists;
2) the handler “waits” for the message exactly from the sending domain and checks this through the second argument.
window.addEventListener('message', function(event){ //приём сообщения
if(event.origin !== 'URL_домена-отправителя') //проверка отправителя, например, 'http://example.org'
return;
...//использование event.data, равного message из .postMessage
...//доступно event.source - окно-окружение передающего события, но
// содержимое его недоступно в кроссдоменных обменах
}, false);
Sending a message from the window to the background page (Chrome)
In extensions, the postMessage framework has been slightly redesigned by adding callbacks and automating domain checks. It became more convenient than if you collect the message from the bricks. At the very end, we present the code on the standard postMessage, using available means to show that there is no magic in the following sendMessage or sendRequest - it's just a shell. But postMessage could not be completed due to insufficient extension rights to create a frame with the special protocol "chrome-extension: // *", about which the format of the manifest file also does not know anything
The above examples can be seen, tested, and investigated by their behavior from the Chrome extension installation page : spmbt.kodingen.com/bgMessageXmp/index.htm . If the extension is not installed, clicks on 5 links indicate the absence of the extension. After installation (in the extension - only 3 necessary files), the clicks begin to execute the examples described below.
How to install : 1) download the archive, 2) unzip (or install it immediately), 3) on chrome: // extensions / go to the "developer mode", 4) execute "Download the unpacked extension ...", selecting the directory from the archive, 5) refresh the page from which the extension was downloaded. 5 links ready to run examples. After execution, it is enough to remove the script using chrome: // extensions /.This sample script can be used as a framework for testing or writing extensions. For this article, he helped debug scripts from text and eliminate typos from them.
Example 1.
If you need to send from the tab to the background page and finish here, a simple message format is used:
chrome.extension.sendMessage('некий объект в фон');
A message is accepted in the background or any other page:
chrome.extension.onMessage.addListener(function(request){
if(request=='некий объект в фон') //проверяется, от того ли окна и скрипта отправлено
console.log('1. Принято: ', request);
});
Example 2.
This pair of functions is adapted to exchange a feedback message. Add parameters from the script:
chrome.extension.sendMessage('запрос backMsg', function(backMessage){
console.log('2. Обратно принято из фона:', backMessage);
});
from the background page:
chrome.extension.onMessage.addListener(function(request, sender, f_callback){
if(request=='запрос backMsg'){ //проверяется, от того ли окна и скрипта отправлено
console.log('2. прошло через фон: ', request);
f_callback('backMsg'); //обратное сообщение
}
});
f_callback - executed in the script (not in the background page), but with the parameter specified in the background.
The sender object has the form:
{ active: true
highlighted: true
id: 34 //id страницы (вкладки) Хрома
incognito: false
index: 0
pinned: false
selected: true
status: "complete"
title: "HabrAjax by spmbt" //document.title
url: "http://spmbt.kodingen.com/habrahabr/habrAjax/index.htm" //location.href
windowId: 1 //id окна Хрома
}
Thus, something is known from him about who sent the message. Now we will not use the values from sender, but it is possible to track from which tab or window the message came, send changes to other content scripts, if they are in other tabs.
(end of example 2)
If you need to pass something more complicated - there is no problem embedding a hash or array instead of a string. Passing a hash instead of a set of arguments - in general, a more advanced form of exchange - takes 1 argument, is easier to read, as it is self-documenting with keys, hash values are rearranged and deleted elementarily - no need to remember the order and worry about missing values.
With the technique of replacing an argument with a hash, 3 arguments are enough for everything, and each of them fulfills its role. Going further, they could be replaced with 1 argument, with the keys {request: ..., sender: ..., callback: f () {...}}; why didn’t you? Apparently, they considered that 3 arguments is still such a gentlemanly limit that it is not shameful to offer to remember (and to further decipherment). In addition, symbols are spent on self-documentation. When the arguments are 2-3, the choice is leaning toward positional arguments.
For example, you need to send not only the name of the command, but also a hash with the data. We write from the script:
var data ='From page ';
chrome.extension.sendMessage({cmd:'exec1', h:{data1: data, dataX: d+1}, function(backMessage)
console.log(backMessage);
}});
from the background page:
chrome.extension.onMessage.addListener(function(request, sender, callback){
if(request.cmd =='exec1'){
callback('backMsg'); //обратное сообщение
console.log('Tis message in background page printed after'
+' receive of data1 = ', request.h.data1, '; URL= ',sender.url);
});
Shorten frequently repeated calls from content pages:
var inBg = function(cmd, h, f_back){
h.cmd = cmd;
chrome.extension.sendMessage(h, f_back);
});
...
inBg('exec1', {data1: data, dataX: d+1}, function(){
...
});
(For some reason, the third callback is not provided ... The management is transferred only 2 times.)
Sending a message from the background page to the window (Chrome)
Example 3.
If there is an inverse problem , send a message from the background page to, say, the active tab:
in the background, write:
chrome.tabs.getSelected(null, function(tab){ //выбирается ид открытого таба, выполняется коллбек с ним
chrome.tabs.sendRequest(tab.id,{msg:"msg01"}); //запрос на сообщение
});
in content:
chrome.extension.onRequest.addListener(function(req){ //обработчик запроса из background
console.log('3. Принято из фона:', req.msg); //выведется переданное сообщение
});
Is it important that in the first case we used the sendMessage-onMessage pair , and in the second - sendRequest-onRequest ? No, any method works, if only it were from one pair.
Example 4.
If you need to send a message with a reverse callback, in the background we write:
var inBack = function(tabId, cmd, h, f_back){
h.cmd = cmd;
chrome.tabs.sendMessage(tabId, h, f_back);
};
chrome.tabs.getSelected(null, function(tab){
inBack(tab.id,'exec0', {dat:'h'}, function(backMessage){
console.log('4. Обратный приём из контента: ', backMessage);
});
});
On the content page:
chrome.extension.onMessage.addListener(function(request, sender, callback){
if(request.cmd =='exec0'){ //выполнить
console.log('4. из фона:', request.dat);
callback('12w3');
}
});
It is executed, as you can see - everything that was described in the documentation, but for a slightly different object (chrome.runtime), on developer.chrome.com/extensions/messaging.html , and chrome.runtime does not work in this task - does not have a method sendMessage.
Transmission on standard postMessage (failed)
Despite the negative result, this transfer attempt will tell you guesses about why developers had to deal with their own data transfer functions in extensions, despite the presence of postMessage .
One of the few special extension methods executable in the content script is chrome.extension.getURL ('path') . It returns the path to extension resources. Opening it, we get the extension resources (but not to directories) - pictures and texts from it. A different domain will play a role here: in order to get texts surrounded by the page script, you need to do cross-domain Ajax. Or it’s easier to do it - to receive texts through the domain wall using the message mechanism.
Example 5
Let's transfer the message using the defined protocol ("chrome-extension:") with a domain (something like "// ciegcibjokpkcklhgbpnmnikpkmkhbjk").
var bgUrl = chrome.extension.getURL('');
console.log(bgUrl); //удовлетворение любопытства
But not everything is simple. The content script does not have access to the background page window. The method is:
chrome.extension.getBackgroundPage();
But no access:
Uncaught Error: "getBackgroundPage" can only be used in extension processes. See the content scripts documentation for more details.
Commando is not discouraged. For a simple exchange, you need to create a frame. Nothing prevents you from creating a frame.
var ifr = document.createElement('iframe');
ifr.id ='ifr1';
ifr.src = bgUrl;
document.body.appendChild(ifr);
An error occurred:
Denying load of chrome-extension: // ciegcibjokpkcklhgbpnmnikpkmkhbjk / . Resources must be listed in the web_accessible_resources manifest key in order to be loaded by pages outside the extension.
Add permission to the manifest (and with chrome.extension.sendMessage this was not necessary).
"Permissions": ["chrome-extension: // *", ...]
An error occurred on chrome: // extensions /:
When installing the extension, warnings occurred:
Permission 'chrome-extension: //' is unknown or URL pattern is malformed.
If access were not denied, it would remain to execute
ifr.postMessage('Yep', bgUrl.replace(/\/$/,'') );
and in the background -
window.addEventListener('message', function(ev){
console.log('origin: ', ev.origin);
},!1);
We can end the example with this - it showed that access through the standard function was closed for other reasons - due to the need to view background pages in frames, which, apparently, was unacceptable for security (in "manifest_version": 2). Therefore, in Chrome extensions, but also for reasons of syntax optimization and bidirectional exchange, special messaging methods were invented.
An example showed that there are no special tricks in sending messages to the background page. But we need special permissions, which we do not have for extensions. The private part of the methods of the chrome.extension object calmly does this, but does not give others. (We still need to try to do this through sandbox.html - will it allow you to create a frame with a background page in it.)
To install a test extension and a test page.