📜 ⬆️ ⬇️

You can not just take and look at the background page

The whole thing - in the security policy, similar to the cross-domain. The access to the pages of other tabs or to the background extension page is deliberately limited, because they are considered to be pages of other domains, they are not allowed to directly access the scripting environment, like other 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 the most important in terms of functions - 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 depths of the browser, which is also called the extension directory somewhere in the operating system folder. From the content script (similar to userscripts executed on the browser page), you can access the files and images of the extension. But you can not perform many functions, the path to which lies through the background page: arrange storage, belonging to the group of real domain names; store extension settings common to the entire extension. You just need to get to Mordor to the background page. However, it is impossible to do this just by the URL.

This description is not on the pages of the documentation for Chrome. Rather, it exists, but for “a few other” methods and objects, which in fact means no, until corrected. The behavior of the described messaging objects is confirmed by several examples in the responses to StackOverflow. It’s tiring to search for answers each time with scattered examples, therefore, in order to put an end to this confusion, let them be gathered in one place here. All 4 combinations of direct and callbacks are enumerated so that there are always working patterns before them.

For testing and demonstration, you need to copy or create 3 files (manifest.json, script.js, background.js), create a test extension in developer mode in Chrome, and look at the messages in the 2-page console - the browser window with the embedded script. js and background (see background background.js console - by clicking on the link “Check viewing modes: _generated_background_page.html” in chrome: // extensions /). The same will work in some ready-made extension, if 2 fragments of the codes from the article are arranged in pages and executed.
')
On Habré, a similar problem was solved for practical purposes in habrahabr.ru/post/159145 , November 2012 - the same sources of knowledge that were tacitly corrected and used. On the Internet in Russian, a similar task was described in an article from June 2012. Here is described the mechanism that is used to solve such problems, given that it is performed in Google Chrome browser extension scripts.

If the pages in windows and frames of the same domain are accessed directly (as the window of another scripting environment), the background page, as well as other tabs and browser pages, act as pages of foreign domains. We can send messages to them, implementing the pattern of weakly connected procedures, and we can only transfer to them objects that can be serialized — no DOM, environments, or other complex objects.

To the background page is, because of what to strive for. It performs functions that are unthinkable in a script or user script. There - not only memory, common to all pages. There is the implementation of many interfaces described on developer.chrome.com/extensions/api_index.html , and more is expected in the future. In a scant messaging interface, there are callback functions that, when given, are executed in the calling function, on the side of their environment, but with a result parameter that the other side passed.

All this is a slightly modified interface of the Javascript environment postMessage , created not so long ago in browsers for solving problems of cross-domain access regulation. 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, to solve this problem, a “crutch” under the code name “window.name” was used , using the keywords of the objects used. It works now, but it is complex and, obviously, slow compared to the postMessage method that came to replace it. Recall how the usual postMessage mechanism works, because it is based on data exchange mechanisms in Chrome extension.

Interface postMessage (cross-browser, IE8 +)


The page initiating the message creates a custom event “message” - makes sending a message, and it does not from its own domain , but from the one it wants to access. (in IE8 there is a restriction - address only to the parent window of the frame).
otherWindow.postMessage(message, targetOrigin); //message -    __  //targetOrigin -     otherWindow,  " " 

If the domain name is guessed, the message will be sent. To receive it, a couple of conditions must be met in the otherWindow window:
1) there is an event handler;
2) the handler “waits” for the message 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); 


Send a message from the window to the background page (Chrome)


In extensions, the basis (postMessage) was slightly reworked by adding callbacks and automating domain checks. It has become more convenient than if to collect the transfer of the message from the bricks. At the very end, we give the code on the standard postMessage, using the available tools to show that there is no magic in the following described sendMessage or sendRequest - this is just a wrapper. But postMessage could not be executed due to insufficient extension rights to create a frame with a special protocol “chrome-extension: // *”, about which the manifest file format also does not know anything , and the matter sank in bureaucratic delays . This is the answer to the question of why they had to invent their own exchange functions.

These examples can be seen, checked and investigated their behavior from the installation page of the Chrome extension : spmbt.kodingen.com/bgMessageXmp/index.htm . If the extension is not installed, clicks on 5 links report the absence of the extension. After installation (in the extension - only 3 required files) the clicks start to perform the examples described below.

How to install : 1) download the archive, 2) unpack (or install immediately), 3) on chrome: // extensions / go into “developer mode”, 4) execute “Load unpacked extension ...” by selecting a directory from the archive, 5) refresh the page from which the extension was loaded. 5 links are ready for examples. After the 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 the text and eliminate typos from them.

Example 1
If you need to send from a tab to a background page and finish there, a simple message format is used:
  chrome.extension.sendMessage('   '); 

A message is received 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 for exchanging a return message. Add parameters from the script:
 chrome.extension.sendMessage(' backMsg', function(backMessage){ console.log('2.    :', backMessage); }); 

from 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 a script (not in the background page), but with the parameter specified in the background one.
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, from it something is known about the one who sent the message. Now we will not use the values ​​from sender, but it is possible to track from which tab or window a message came, send changes to other content scripts, if they are in other tabs.
(end of example 2)

If you need to transfer something more complicated - there is no problem to build a hash or an 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, since it is self-documented with keys, hash values ​​are rearranged and deleted elementarily - there is 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 performs its own 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 are still such a gentleman's limit, which is not shameful to offer for memorization (and for further decoding). In addition, symbols are spent on self-documentation. When there are 2–3 arguments, the choice leans towards 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 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 frequent 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, there is no third callback provided ... The management is transferred only 2 times.)

We send the message from the background page to the window (Chrome)


Example 3
If there is an inverse task , send a message from the background page to, say, the active tab:
in the background we 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 a pair of sendMessage-onMessage , and in the second - sendRequest-onRequest ? No, any method works, as long as it is from the same 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 runs, 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 - it has no method sendMessage

Transfer to standard postMessage (failed)


Despite the negative result, this transfer attempt will tell you why the developers had to deal with their own data transfer functions in extensions, despite the presence of postMessage .

One of the few special extension methods that is executable in the content script is chrome.extension.getURL ('path') . It returns the path to the expansion resources. Having opened it, we will receive expansion resources (but not to directories) - pictures and texts from it. A different domain will play its role here too: in order to get the texts into the environment of the page script, you need to do Ajax cross-domain. Or it is easier to do - get the texts through the domain wall message mechanism.

Example 5
Let's transfer the message using the defined protocol ("chrome-extension:") with the domain (something like "// ciegcibjokpkcklhgbpnmnikpkmkhbjk").
 var bgUrl = chrome.extension.getURL(''); console.log(bgUrl); //  

But not everything is simple. There is no access to the background page for the content script. The method is:
 chrome.extension.getBackgroundPage(); 

But there is 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 to create 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.
Add permission to the manifest (and with chrome.extension.sendMessage this was not needed).
"Permissions": ["chrome-extension: // *", ...]
An error occurred on chrome: // extensions /:
At installation of the extension, warnings occurred:
Permission 'chrome-extension: //' is unknown or URL pattern is malformed.
If access was not denied, it would remain
 ifr.postMessage('Yep', bgUrl.replace(/\/$/,'') ); 

and in the background -
 window.addEventListener('message', function(ev){ console.log('origin: ', ev.origin); },!1); 


This example can be completed - he showed that access through the regular function was closed for other reasons - because of 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 communication, special messaging methods were invented.

The 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 closed part of the methods of the chrome.extension object does it calmly, but does not give it to others. (You need to try to do it again via sandbox.html - whether it will allow you to create a frame with a background page in it.)

On the installation of a test extension and a test page.

Source: https://habr.com/ru/post/174745/


All Articles