How to implement a Chrome extension from 0 to 1

The functionality to be implemented

Inject the DOM element of double speed playback to a web disk page and video, support custom double speed, as to the principle, in fact, is to call a method on the page, this video is very clear to see the bilibilii

We all know that a non-member can’t play a video at double speed, and then wonder if it would be easier to write a Chrome plugin


This code can be found at github.com/lovelyJason… find

Basic knowledge/components

manifest.json

{
  "name": "".  "version": "1.0".  "description": "".  "permissions": ["activeTab"."declarativeContent"."storage"]. "background": {  "scripts": ["background.js"].Background / / registration  "persistent": false  },  "page_action": {  "default_popup": "popup.html". "default_icon": { // The icon on the plugin bar  "16": "images/get_started16.png". "32": "images/get_started32.png". "48": "images/get_started48.png". "128": "images/get_started128.png"  }  },  "icons": { // Extension management page plug-in icon  "16": "images/get_started16.png". "32": "images/get_started32.png". "48": "images/get_started48.png". "128": "images/get_started128.png"  },  "options_page": "options.html". "manifest_version": 2 // Must be configured and is 2.0 } Copy the code

Manifest.json is the required description file used to configure Chrome extensions, including ICONS, behaviors, etc

Chrome extension components, or presentation forms, are mainly the following

background & event page

Background scripts are extended event handlers; It contains listeners for browser events that are important to the extension. It lies dormant until it fires an event and then executes the indicated logic. Valid background scripts are loaded only when needed and unloaded at idle. The difference is in the value of the persistent attribute in manifest.json

There are no visible pages, just a page that executes a background script

Update background script functions

If you use the extension. GetBackgroundPage page calls the function from the background, please update for the runtime. GetBackgroundPage. Newer methods activate non-persistent scripts before returning them.

This method will not work if the background script is inactive (the default state for non-persistent scripts). Newer methods include a callback function to ensure that the background script is loaded. As in popup

chrome.extension.getBackgroundPage().backgroundFunction(); // not work
Copy the code
chrome.runtime.getBackgroundPage(function (backgroundPage) {
  backgroundPage.backgroundFunction();
});
Copy the code

content

Extensions that read and write to Web pages use content scripting. Content scripts contain JavaScript executed in the context of a page that has been loaded into the browser. Content scripts read and modify the DOM of web pages accessed by the browser. You can think of it as writing a DOM and injecting a script containing the DOM into the original page

Content scripts can communicate with parent extensions by exchanging messages and using storage apis to store values.

  • Content-scripts shares the DOM with the original page but does not share the JS. If you want to access JS such as variables, you can only access the followinginjected jsimplementation
  • Access chrome Limited API, Extension, I18n, Runtime, Storage. If you call another API, you can talk to the background, call it through the background
  • There are two ways to inject scripts, programmatic and declarative
    • Programmatic: To provide permission in manifest.json, set to ‘activeTab’
  // background.js
  chrome.runtime.onMessage.addListener(
  function(string message, callback) {
    if (message == "changeColor") {      chrome.tabs.executeScript({
 code: 'document.body.style.backgroundColor="orange"'  });  }  });  // popup.js  chrome.runtime.sendMessage("changeColor".function(response) {  console.log(response.farewell);  }); Copy the code
  • The scripts for declarative injection are registered under the “content_scripts” field of the manifest. They can contain JavaScript files, CSS files, or both. All automated content scripts must specify matching patterns. Run_at controls the timing of injection. By default, document_idle Content-Script can only obtain the dom of the original page, not JS
  "content_scripts": [
  {
    "matches": ["http://*.nytimes.com/*"].// The plugin script is generated only when the page is matched
    "css": ["myStyles.css"].    "js": ["contentScript.js"]. "run_at": "document_start"  } ].Copy the code

There are also popup pages (click on the popup page of the plugin icon) and Options pages (click on the page after the details of the extension), which will not be described too much. Please refer to github projects for details

For example, popup is configured in manifest.json, and then popup. HTML can import the script tag from your project to generate a popover page

"page_action": {
  "default_popup": "popup.html"
},
Copy the code

Communication with the embedded original page is via window.postMessage

Chrome plugin to access raw page variables/functions etc

Because Google’S API does not allow plug-ins to access the variables of the original page, but many plug-ins will call the method of the original page or get js variables, so it is necessary to use some methods similar to crack. During the execution of Content.js, script tag pairs are inserted into the original page, because although content.js cannot obtain the functions and variables of the original page, it can access dom, so dom manipulation can be carried out

For example, insert a script tag pair into the original page and insert its own logical code, it is called inject-script.js

// content.js
function injectCustomJs(jsPath, callback) {
  jsPath = jsPath || 'js/injectscript.js';
  var script = document.createElement('script');
  script.setAttribute('type'.'text/javascript');
 / / get the address of the similar: chrome - the extension: / / ihcokhadfjfchaeagdoclpnjdiokfakg/js/inject. Js  script.src = chrome.extension.getURL(jsPath);  script.onload = function () {  // It will not look good on the page  this.parentNode.removeChild(this);  setTimeout(function () {  callback()  }, 3600)  };  document.head.appendChild(script); }  // Init the call in content.js document.addEventListener('DOMContentLoaded'.function () {  injectCustomJs(null.function () {}) } Copy the code

Just a few of the things I’ve tried,

  • Create and insert a Script element, execute the JS code in it, cache the book by passing a message or otherwise, and then delete the element. Note that js variables in the original page may not be generated immediately and need a timer to delay the acquisition time. This method can only pass certain types such as strings, simple objects (which are automatically serialized), etc. Functions cannot be passed, and functions cannot be cloned even if messages are used

  • Create an element and set the onclick property, and then call the element’s onclick method at some point. The chrome plugin has no onclick method, and the element is null, meaning it cannot bind inline events

  • Create a script element and bind it to a function in the original page via addEventListener. Find the DOM in content-Script and trigger its event. Jquery provides active trigger events. However, there is a problem with this. There is a communication problem between injection-script (essentially injecting dom into the page, which is also the raw page) and content-script. The original page (including injection-script) and Content-script are in two sandboxes, They do not share variables and cannot access each other. While it is possible to send and receive via messaging mechanisms, such as custom events rather than built-in events such as onclick, the result still fails to pass JS variables, functions, etc., and we are back to where we started

  • The ultimate method: In general, no matter how you pass it, you can only pass strings or JSON strings postMessage in Content-script depending on the timing (like clicking on a button that you inject), injects-script will listen to directly call functions in the original page or get variables without passing, right

    The injected style is called injected stylesheet and cannot be modified in the Chrome development tools

chrome api

In addition to having access to the same apis as the Web page, extenders can use extender-specific apis that can be tightly integrated with the browser. Both extensions and web pages can access the standard window.open () method to open the URL, but extensions can use the Chrome API tabs.create method to specify the window to display the URL.

Most Chrome API methods are asynchronous: they return immediately, without waiting for the action to complete. If an extension needs to know the result of an asynchronous operation, it can pass a callback function to the method. The callback is executed after the method returns, possibly much later.

chrome: {
  tabs: {
    query: function({object queryInfo: {active: true, currentWindow: true}}, callback) {},
    update: function(tabs[0].id, {url: newUrl}) {},
    executeScript: function(tabs[0].id, {code}) {}
 },  runtime: {  / / return extensionDir Function () {}, onInstalled: {  addListener: function(calback) {} // Listen to run time, used to set state or initialize  },  getBackgroundPage: function() {},  onMessage: {  addListener: function(function(message, callback) {}) {}  }  },  storage: {  // The stored data will be automatically synchronized to any Chrome browser the user is logged in to.  sync: {  get: function(key, callback || undefined) {},  set: function(key, callback) {} }, local: {  get: function(key, callback || undefined) {},  set: function(key, callback) {}  } }, // Set the context menu  contextMenus: {  create: function({  "id": "". "title": "". "contexts": ["selection"] {}}) } } Copy the code

Chrome extension for page to page communication

Different components in an extension often need to communicate with each other. Different HTML pages can find each other through Chrome. Extension methods such as getViews() and getBackgroundPage(). Once a page references other extended pages, the first page can call functions on the other pages and manipulate their DOM. In addition, all components of the extension have access to values stored using the storage API and through Message Passing. To communicate.

Some of the summary

Background and Event Pages do not have visible pages. The difference is that the former may affect performance when mounted for a long time. In terms of configuration file difference, the latter only has a persistent parameter, which is specified as false. In addition, Event Pages features background pages that are loaded when needed and unloaded when idle. Some examples of events include

  • Extensions are first installed or updated to a new version.
  • The background page is listening for an event and has scheduled the event.
  • Content Script or other extensions send messages
  • Another view of extension, like a popup, calls the runtime. GetBackgroundPage.

Event-based background scripting supports most of the extended functionality. Only in rare cases should an extension have a persistent background

Note: Event Driven Background Scripts are officially called Event Driven Background Scripts

Listeners must be registered synchronously from the beginning of the page

// background.js
chrome.runtime.onInstalled.addListener(function () {
  chrome.contextMenus.create({
    id: "sampleContextMenu".    title: "Sample Context Menu". contexts: ["selection"]. }); });  // This will run when a bookmark is created. chrome.bookmarks.onCreated.addListener(function () {  // do something }); Copy the code

Example of error

chrome.runtime.onInstalled.addListener(function () {
  // ERROR! Events must be registered synchronously from the start of
  // the page.
  chrome.bookmarks.onCreated.addListener(function () {
    // do something
 }); }); Copy the code

The extension can remove the listener from the background script by calling removeListener. If all listeners for an event are removed, Chrome will no longer load the extension’s background script for that event.

chrome.runtime.onMessage.addListener(function (message, sender, reply) {
  chrome.runtime.onMessage.removeListener(event);
});
Copy the code

Background and popup can’t directly access page dom, but can be by chrome. Tabs. ExecuteScript script execution, such as the example of startchrome.tabs.executeScript, thus accessing the page DOM, but also not directly accessing the page JS

It is not supported to write JS in inline HTML or through setAttribute form

More content accessible developer.chrome.com/extensions official document

So my plug-in pseudocode should look like this

// content.js

// 1. Inject DOM, insert styles, register events, etc...

// 2. Send a message to inject.script.js to indicate that an operation will be performed on the original page at a certain time
$('div').on('click'.function (e) {  window.postMessage({ type: 'to_inject'.speed: speed }, The '*') })  Copy the code
// inject-script.js
setTimeout((a)= > {
  // After inserting the script tag pair into the original page, the code is executed here. The DOM structure on the page may not be fully loaded, so the dom node or JS variable on the page is not generated, requiring a timer
  // You can obtain the DOM and any original page can obtain js variables, call functions and other operations
  window.addEventListener("message".function(e) {
 Injection-script (that is, the original page) receives the message  if(e.data && e.data.type === 'to_inject') {  // Execute a function call on the original page  }  }, false); }, 3000) Copy the code

Chrome extension package/release/use

  • Package can be packaged and loaded through the Chrome Extension Management interface. The package extension will package the project folder as a CRX suffix file. In the old Chrome browser, in developer mode, you can directly drag into CRX to install the extension

  • As for publishing, the need for a credit card like Visa to get your app on the shelves can be tricky

  • Use, plug-in source github.com/lovelyJason… Import the folder in the Chrome extension management interface, and then check the enable plugin. You can see the effect when playing the web disk video