What is a browser plug-in?

Simply put, a browser plug-in is a tool on the browser that provides some features that the browser does not have and helps you do some interesting things. Developers can implement features as they like. Plug-ins are built based on Web technologies (HTML, CSS, JS).

For example, 🌰

FeHelper. JSON plug-in

Functions: formatting JSON, coding conversion, Markdown, code compression and other functions.

Two-dimensional code generator

Function: you can generate a TWO-DIMENSIONAL code according to the current web page address.

SwitchyOmega Proxy

Function: You get the idea.

Hello World

manifest.json

There are no strict file structure restrictions for Chrome plug-ins, just make sure that the manifest. Json file ** is at the root of the folder. ** The contents of this file outline the resources, permissions, and so on required by the plug-in.

A simple example of a segment:

{" manifest_version ": 2, / / will fill in the" name ":" my - plugin ", / / will fill in the "version" : "0.1.0 from / / required}"Copy the code

Manifest_version: Represents the version of the manifest file. The browser uses this value to specify the functions that this version has.

Name: name of the plug-in.

Version: plug-in version.

Place the manifest.json file in a folder.

chrome://extensions/

In the browser address bar, type Chrome :// Extensions/to open the Extensions page.

Note: You need to enable “Developer mode” in the upper right corner to load the unzipped plug-in file:

Load the unzipped plug-in

When enabled, click load the unzipped extension, select the folder where we just put manifest.json, and you’ll see:

There’s a new plugin we just added, and there’s one of our ICONS in the top right corner of the browser:

At this point, a plug-in has been loaded, but it does nothing except occupy a space in the browser.

If the plug-in icon is not set, the first character of the plug-in becomes the default icon of the plug-in.

Make the plug-in look more “plug-in”

To “complete” the plugin a bit, we added an icon and description, and clicking on it brought up a popup page. Popup pages are usually used to host temporary interactions and have a short life cycle: click on the icon to open popup, then immediately close when the focus is off, as defined by the default_popup field.

{... "Description ":" This is a description", // plugin management page icon "ICONS ": {"84": "./icon/ball.png"}, // browser in the upper right corner of the icon and the content "browser_action": {" default_icon ":". / icon/ball. PNG ", "default_title" : "my plug-in", "default_popup" : ". / HTML/popup. HTML "}}Copy the code

Our directory structure now looks like this:

Add content to popup.html:

<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, "> <meta http-equiv=" x-UA-compatible "content=" IE =edge"> <title>my-plugin</title> </head> <body> <p style="width: 200px; text-align:center;" >hello world!! </p> </body> </html>Copy the code

After that, we click the “Refresh” button in the lower right corner of the plug-in:

You’ll notice that the plugin has ICONS and descriptions:

The icon in the upper right corner has also changed. If you click on it, the popup.html page we just wrote will popup:

Now we have a “complete” plug-in.

Manifest. json configuration introduction

background

{... "Background ": {// provide a page to background" page": "./ HTML /background.html" // or several js files, the background will default to generate a blank HTML "scripts": ["./js/background.js"] } }Copy the code

The background configuration item is the background resident page of the plug-in. The life cycle is the same as the life cycle of the browser. Once the browser is started, the background page will start running until the browser is closed. Or in the plug-in management page, disable the plug-in, the background page will also stop running.

In addition, Background has high permissions, can call almost all Chrome extension apis (except DevTools), and has the ability to directly cross domains.

Page: Specifies a web page as the background page.

Scripts: specifies several JS files. The background will automatically generate an HTML and call these JS files in sequence.

Note: The page and scripts options can only be selected, otherwise an error will be reported.

Once configured, the properties plug-in will present a background page option:

I used a background.js file:

function _back() { console.log('background.js') } console.log('running... ')Copy the code

Click inside to see what’s inside:

Yes, it is a normal background page where you can view requests or debug code if background.js is communicating with other pages.

If the page option is used, the opening will look like this.

In addition, since background is always running in the background, we can add a configuration to optimize performance:

{... "background": { ... "persistent": false } }Copy the code

In this way, plug-ins are loaded when needed and closed when idle. For example, it is loaded when a plug-in is installed or updated, or when other pages communicate with background.

content-scripts

Content-scripts can inject scripts at the right time (before, after, idle), allowing content scripts to change their JavaScript environment without conflict with pages or other content scripts.

For example, the original page had a button and added a click event to the button:

<html>
    <button id="mybutton">click me</button>
    <script>
      var greeting = "hello, ";
      var button = document.getElementById("mybutton");
      button.person_name = "Bob";
      button.addEventListener("click", function() {
        alert(greeting + button.person_name + ".");
      }, false);
    </script>
  </html>
Copy the code

In Content-scripts, add the following code:

var greeting = "hola, ";
var button = document.getElementById("mybutton");
button.person_name = "Roberto";
button.addEventListener("click", function() {
alert(greeting + button.person_name + ".");
}, false);
Copy the code

When the page runs, the script content also runs at the time defined by the plug-in, and when the page clicks the button, two pop-ups appear.

The content – scripts configuration:

{... "Matches ": [" matches": ["<all_urls>"], // The injected js will run in sequence. ["./js/content.js"], // CSS introduction should be careful, because it may affect the global style, also can receive multiple CSS files, will be inserted in the page in order "CSS ": ["./ CSS /style.css"], // Timing of code injection, optional values: "Document_start ", "document_end", or "document_idle", the last one indicates that the page is idle, default document_idle" run_at": "document_start" }, { "matches": ["https://www.baidu.com/"], "js": ["./js/other.js"], "run_at": "document_start" } ], ... }Copy the code

The code for content.js is as follows:

console.log('hello, from content.js');
Copy the code

The other.js code is as follows:

console.log('hello, from other.js... ')Copy the code

Updated plug-in, when bytedance. Feishu. Cn/drive/home /…

Because [bytedance. Feishu. Cn/drive/home /… < all_urls > rules, so run after content. js

When running at www.baidu.com/ :

Both rules were hit at the same time, so both Content.js and other.js run in the correct order.

Content-scripts and the original page share the DOM, but not the JS. To access the PAGE JS (such as a JS variable), only inject-scripts. Content-scripts also has low access to the Chrome API, and can only access the following four apis:

  • chrome.extension(getURL , inIncognitoContext , lastError , onRequest , sendRequest)
  • chrome.i18n
  • chrome.runtime(connect , getManifest , getURL , id , onConnect , onMessage , sendMessage)
  • chrome.storage

Inject-scripts

Inject -scripts is the JS code that is inserted through DOM operation. Generally, In Content-scripts, only DOM can be operated, but JS of the page cannot be accessed. With the help of The ability of Content-scripts to manipulate DOM, JS files are inserted into the page. Provide pages with the ability to call plug-in apis and communicate with background.

Before insertion, configure the resources accessible to the Web and change the call time of Content-scripts to “document_end” or “document_idle”. Otherwise, DOM cannot be obtained and insertion fails. Add the following to manifest.json:

{
    ...
    "content_scripts": [
        {
          "matches": ["<all_urls>"],
          "js": ["./js/content.js"],
          "run_at": "document_end"
        },
        ...
     ],
    "web_accessible_resources": ["js/inject.js"],
    ...
}
Copy the code

The content of inject.js is as follows:

function mockApi () {
  console.log('this is from inject.js')
}
Copy the code

Content.js adds the following code:

(function () { let path = 'js/inject.js'; let script = document.createElement('script'); script.setAttribute('type', 'text/javascript'); // Note that the path needs to be generated using the Chrome API. This method can get the real path of the plugin's resources. / / similar: chrome - the extension: / / ihcokhadfjfchaeagdoclpnjdiokfakg/js/inject. The js script. SRC = chrome. The extension. The getURL (path); Script. Onload = function () {/ / after the execution of the code to remove the script tag enclosing parentNode. RemoveChild (this); } document.body.appendChild(script); }) ();Copy the code

After updating the plug-in, the page can access the inject.js method:

permissions

Some operations in the plug-in background need to be configured with corresponding permissions, such as local storage, network request, notification, etc. Examples:

{... "permissions": ["tabs", // notifications", // webRequest", // web request "webRequestBlocking", "Storage" // Plug-in local storage],... }Copy the code

Complete manifest configuration

The official document: developer.chrome.com/extensions/…

communication

Popup communicates with background

Popup by chrome. The extension. GetBackgroundPage () API access directly to the background of the context, thus calling background way of communication:

// popup.js var backend = chrome.extension.getBackgroundPage(); backend.test(); // Access the bbackground functionCopy the code

Background by chrome. The extension. GetViews ({type: ‘popup’}) get popup context, the premise is popup page is opened.

let views = chrome.extension.getViews({type:'popup'}); let popup = null if(views.length > 0) { popup = views[0]; // Call the popup function popup.test(); }Copy the code

One thing to note here:

In the popup page, if you want to write js, please put js code in a file, and then introduced to come in, otherwise an error, this is because the Chrome security policy: developer.chrome.com/extensions/…

Popup error:

<! DOCTYPE html> <html lang="en"> <head> ... </head> <body> <p style="width: 200px; text-align:center;" >hello world!! < / p > < script > / / not directly write inside < / script > < / body > < / HTML >Copy the code

Correct posture:

<! DOCTYPE html> <html lang="en"> <head> ... </head> <body> <p style="width: 200px; text-align:center;" >hello world!! </p> <script src=".. /js/popup.js"></script> </body> </html>Copy the code

Content-scripts communicates with background

The content – scripts can be chrome. Runtime. SendMessage (message) send background information:

chrome.runtime.sendMessage('message content', (res) => {
    console.log('from background:', res)
});
Copy the code

Background by chrome. Runtime. OnMessage. AddListener () to monitor the content – scripts sent messages:

chrome.runtime.onMessage.addListener(function(message, sender, callback) {
   console.log(mesasge); // meesage content
   callback && callback('yes this from background')
});
Copy the code

Background sends a message to Content-scripts using the chrome.tabs. Query method, and then sends a message to the TAB using the chrome.tabs.

// {active: true, currentWindow: true} represents a TAB to find the active state of the current screen; chrome.tabs.query({active: true, currentWindow: true}, function (tabs) { chrome.tabs.sendMessage(tabs[0].id, 'message content', (res) => { console.log('from content:', res) }); });Copy the code

The content – scripts by chrome. Runtime. OnMessage. AddListener to monitor events:

chrome.runtime.onMessage.addListener(function (message, sender, callback) {
    console.log(message, sender)
    callback && callback('yes this from content')
});
Copy the code

Note:

1. The message content can be directly sent in JSON format.

2. Popup and Content communicate the same way as above.

3. If both popup and background listen for messages sent from Content, both will receive listener messages, but callback only fires once, depending on who sent first.

Inject – scripts and content – scripts

There are two methods for injection-scripts and Content-scripts communication:

1. Window. postMessage sends the message, and window.addEventListener receives the message

2. Custom DOM events are also available;

But it’s rare for Content-scripts to call inject-scripts because, yes, but not necessary…. Content-scripts can handle some API event listeners themselves, and inject-scripts is only generated by Content-scripts and inserted into the DOM, so in content-scripts’ eyes, Inject -scripts is a brother…

However, a lot of user-triggered events need to be told by injection-scripts to content-scripts, which communicates to background and does something, and then sends a message to injection-scripts. From this perspective: Content-scripts is a inject-scripts tool man!

(It’s even, perfect.)

Injector-scripts Sends a message to content-scripts:

Window. postMessage({"test": 'Hello! Tool! '}, '*');Copy the code

Content-scripts Receives messages:

Window. addEventListener("message", function(message) {console.log(' here, buddy! ', message.data); }, false);Copy the code

Similarly, it is the same for Content-scripts to send messages to inject-scripts.

Practice: HTTP Header plug-in

Implement an HTTP Header plug-in, you can dynamically add the Header, and automatically add the Header to the network request, Header parameters can be configured.

Figure:

Background Function design

Background copy stores, manipulates headers, and blocks all browser requests with headers enabled.

Note: Because network requests are involved, you need to add permissions to manifest.json:

{... "permissions": ["storage", // local storage" webRequest", // webRequest" webRequestBlocking", // webRequest blocking "<all_urls>" // matching URL]... }Copy the code

Background function pseudocode:

// headers data structure, with default values; (Can be changed to local storage). const headers = [ { key: 'Content-Type', value: 'application/x-www-form-urlencoded', enable: false, }, { key: 'test-header ', value:' press F to enter tank ', enable: true,},]; Function getHeaders () {return headers; } function addHeader (header) { headers.push(header); } function deleteHeader (index) { headers.splice(index, 1); } function toggleHeader(index) { headers[index].enable = ! headers[index].enable; }... Interceptors / / / / request On the install when being installed to initialize the chrome. Runtime. OnInstalled. AddListener (function () {/ / add event chrome.webRequest.onBeforeSendHeaders.addListener(requestHandle, { urls: "], [" < all_urls > / / intercept all URL request}, [" blocking ", "requestHeaders"]); // blocking console.log('load'); }); // add header function requestHandle(request) {let requestHeaders = request. RequestHeaders; ForEach (item => {if (item.enable) {requestHeaders. Push ({name: item.key, value: item.value,}); }}); return {requestHeaders}; }Copy the code

Chrome.webrequest life cycle:

Detailed reference: developer.chrome.com/extensions/…

Popup page design

Popup page provides interfaces for adding, deleting, enabling and disabling functions. In addition, every time popup page is opened, the latest header data is obtained from background and displayed in the foreground.

Popup.js function pseudocode:

/ / popup when the page is opened to the background for the latest header window. The onload = function () {let backend. = chrome extension. GetBackgroundPage (); Headers let headers = backend.getheaders (); backend.getheaders (); // Render header createElement(headers); } / / add button function addHeader () {let backend. = chrome extension. GetBackgroundPage (); let key = document.querySelector('.key'); let value = document.querySelector('.value'); let header = { key: key.value, value: value.value, enable: } // Call background, add headers backend.addHeader(header); createElement(header); } / / enable disable function, delete function toggleHeader (index) {let backend. = chrome extension. GetBackgroundPage (); backend.toggleHeader(index); } function delHeader(index) { let backend = chrome.extension.getBackgroundPage(); backend.deleteHeader(index); }Copy the code

The effect

Open popUp and add a header:

Open a web page and open the console to see the RequestHeaders:

conclusion

  • Many permissions and functions need to be configured in manifest.json.
  • Content-scripts, popUp, background, and inject-scripts have different permissions and communication modes. Understand the features of each script and use them in combination.
  • Development and debugging information can be viewed in the background page. Popup, injection-scripts, and Content-scripts can be directly reviewed for element debugging.

The Chrome plugin also has many features not covered here, such as DevTools. Those who are interested can refer to the reference documents below.

Reference documentation

The official document: developer.chrome.com/extensions

Refer to the blog: www.cnblogs.com/liuxianan/p…

——

Welcome to “Byte front end ByteFE”

Resume mailing address: [email protected]