23 Understanding Chrome Extensions Scripting

The chrome.scripting API is used to programmatically inject CSS and JavaScript to alter the behavior and appearance of the websites. Think about it like this, a dynamic way of injecting content scripts and content styles as opposed from doing it via the manifest.json declaration.

You can read the offlicial provide by the Google Chrome Extension Team herehttps://developer.chrome.com/docs/extensions/reference/api/scripting

How to declare it in Manifest.json

Do not forget to add the host_permissions of the websites you want inject css or javascript to. In this code snippet we’ve just include all urls <all_urls>

{
...
"host_permissions": [
"<all_urls>"
],
"permissions":[
"scripting"
],
...
}

or you use the activeTab permission to enable script inject for that active tab.

{
...
"permissions":[
"activeTab",
"scripting"
],
...
}

How to use the Scripting API

There are two major ways for using the chrome.scripting API executing executing code immediately or register JS and CSS code as content scripts.

1 Executing Code Immediately.

We’ll use the chrome.scripting.executeScript and chrome.scripting.insertCSS functions which will enable us to run code, files or functions JavaScript or CSS. Let’s see what that is like.

Run CSS Code

// Run CSS Code
const tabId = ""; // tab id is mandatory
const css = "body { background-color: red; }";

chrome.scripting.insertCSS({
target: { tabId: tabId },
css: css,
});

Run Javascript Code

// Run JS Code
const tabId = ""; // tab id is mandatory
const js = "alert('Hello World');";
const injectionOptions = {

// the target tab to inject to
target : {
tabId : tabId
},

// the JS code to run
js: js,
};

chrome.scripting.executeScript(injectionOptions, (results) => {
for(const result of results){
console.log(results);
}
})

Run Javascript Files

// Run Files
const tabId = ""; // tab id is mandatory
const injectionOptions = {

// the target tab to inject to
target : {
tabId : tabId
},

// the js files that will be injected
files: ['script.js'],
};

chrome.scripting.executeScript(injectionOptions, (results) => {
for(const result of results){
console.log(results);
}
})

Run CSS Files: You use the chrome.scripting.insertCSS for this one. We find this the most effective way to insert CSS programmatically. The options for the insertCSS function can be explore here.

let tabId = ""// tab id from somewhere.

chrome.scripting.insertCSS({
target: { tabId: tabId },
files: ['/content/styles.css'],
});

// You can also remove the inserted css
chrome.scripting.removeCSS({
target: { tabId: tabId },
files: ['/content/styles.css'],
});

You can also JavaScript function and according the documentations, “A JavaScript function to inject. This function will be serialized, and then deserialized for injection. This means that any bound parameters and execution context will be lost…

// Inject and Run Function

function changeBackgroundColor() {
document.body.style.backgroundColor = "red";
}

const tabId = ""; // tab id is mandatory
const injectionOptions = {

// the target tab to inject to
target : {
tabId : tabId
},

// the function that will be injected
func : changeBackgroundColor,
};

chrome.scripting.executeScript(injectionOptions, (results) => {
for(const result of results){
console.log(results);
}
})

And if you want use arguments in those functions

// Inject and Run Function
function getRandomColor(){
const colors = ['red', 'blue', 'green', 'yellow', 'orange', 'pink'];
const index = parseInt(Math.random() * colors.length);
return colors[index];
}

function changeBackgroundColor(color, textColor) {
document.body.style.backgroundColor = color;
document.body.style.color = textColor;
}

const tabId = ""; // tab id is mandatory
const injectionOptions = {

// the target tab to inject to
target : {
tabId : tabId
},

// the function that will be injected
func : changeBackgroundColor,

// arguments for the function
args : [ getRandomColor(), "white" ],
};

chrome.scripting.executeScript(injectionOptions, (results) => {
for(const result of results){
console.log(results);
}
})

2 Registering Content Scripts

This way of doing these ensures that the code persists on subsequent page loads the same way content scripts operate. You’ll be able to get, create, edit and remove the register content scripts. Let’s see how.

Registering Content Script:

For more options to register content scripts look the documentation herehttps://developer.chrome.com/docs/extensions/reference/api/scripting#type-RegisteredContentScript

// similar to the manifest.json content_scripts definition.
const scripts = [
{
// The id must not start with _ (underscore).
id : "run_my_code",
matches : ["https://*.google.com/", "https://*.facebook.com/"],
excludeMatches: [],

// These are injected in the order they appear in this array
js: ["content.js"],

//These are injected in the order they appear in this array
css: ["styles.css"]
}
];


chrome.scripting.registerContentScripts(scripts, () => {
console.log('Added Scripts')
});

Updating Registered Content Script

const scripts = [
{
id : "run_my_code",
matches : ["https://*.google.com/", "https://*.facebook.com/"],
js: ["new.js"],
css: ["new.css"]
}
];

// update content script.
chrome.scripting.updateContentScripts(scripts, () => {
console.log('Updated "run_my_code" Content script')
});

Get Registered Content Scripts

const filters ={
// remove ids if you want get all content scripts
ids:["run_my_code"]
};

chrome.scripting.getRegisteredContentScripts(filters,(contentScripts) => {
for(const contentScript of contentScripts){
console.log(contentScript);
}
})

Remove Registered Content Scripts

const filters = {
ids : ["run_my_code"]
};

chrome.scripting.unregisterContentScripts(filters, () => {
console.log('Content Scripts Removed');
})

So if you wanted to remove all the registered content scripts and you do not know the ids for them

function unregisterAllDynamicContentScripts(){
// get all registered content scripts
chrome.scripting.getRegisteredContentScripts().then((scripts) => {

// get ids
const scriptIds = scripts.map(script => script.id);

//unregister content scripts
chrome.scripting.unregisterContentScripts(scriptIds);
});
}

Async/Await Version: Snippet from documentation

async function unregisterAllDynamicContentScripts() {
try {
const scripts = await chrome.scripting.getRegisteredContentScripts();
const scriptIds = scripts.map(script => script.id);
return chrome.scripting.unregisterContentScripts(scriptIds);
} catch (error) {
const message = [
"An unexpected error occurred while",
"unregistering dynamic content scripts.",
].join(" ");
throw new Error(message, {cause : error});
}
}

Sample Project

The Chrome Team provides sample you can look at herehttps://github.com/GoogleChrome/chrome-extensions-samples/tree/main/api-samples/scripting

However, the chrome extension that will build will change the background color and link color of the Google Website. The user will be able to select the color theme from the extension or from the contextMenu.

Extensions Popup
Extensions Use Case

Requirements

For the project will require we use the tabscontextMenus , and scripting APIs. We have articles on each of these APIs if you have forgotten or unaware of how to use them.

Source Code

You can find the source code for this project here: https://github.com/BuildChromeExtensions/visualscripts

Project Files

We’ll need main 4 files for this one. manifest.json , background.js , popup.html and popup.js and an additional 2 JS files and 2 CSS files that will be used as our content scripts that will be injected. orange.js , orange.css , green.js and green.css

Your file structure should look something like this

manifest.json

{
"name": "Theme Picker",
"description": "Change the background color and link color",
"version": "1.0.0.0",
"manifest_version": 3,
"action": {
"default_popup": "popup.html"
},
"host_permissions": [
"https://*.google.com/",
"https://*.google.com/*"
],
"permissions": [
"contextMenus",
"scripting",
"tabs"
],
"background": {
"service_worker": "background.js"
}
}

popup.html

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style type="text/css">
body {
width: 200px;
height: 300px;
padding: 10px 20px;

}

form {
display: flex;
flex-direction: column;
align-items: center;
}

textarea {
padding: 8px 12px;
font-family: Arial, Helvetica, sans-serif;
margin: 5px 0px;
width: 100%;
border-radius: 30px;
}

button {
background: #03c2fc;
color: #ffffff;
padding: 10px 20px;
border: none;
cursor: pointer;
box-shadow: rgba(100, 100, 111, 0.2) 0px 7px 29px 0px;
transition: all 0.4s;
border-radius: 30px;
font-weight: 700;
margin: 10px;
}

button:hover {
background: #026482;
}

#btn-orange {
background: #fca903;
}

#btn-green {
background: #029e11;
}

#btn-remove {
background: #383838;
}
</style>
</head>

<body>
<h1>Theme Picker</h1>
<h2>Register Content Scripts</h2>
<section>
<button id="btn-orange">Save Orange Theme</button>
<button id="btn-green">Save Green Theme</button>
<button id="btn-remove">Remove Theme</button>
</section>
<script type="text/javascript" src="./popup.js"></script>
</body>

</html>

popup.js

// Register content scripts
document.getElementById('btn-orange').onclick = () => {

//remove existing content script
document.getElementById('btn-remove').click();


const scripts = [
{
id: "orange",
matches: ["https://*.google.com/", "https://*.google.com/*"],
js: ["/content/orange.js"],
css: ["/content/orange.css"]
}
]

// add content script
chrome.scripting.registerContentScripts(scripts);
alert('Content Script added for Orange theme');
}

document.getElementById('btn-green').onclick = () => {

//remove existing content script
document.getElementById('btn-remove').click();

const scripts = [
{
id: "green",
matches: ["https://*.google.com/", "https://*.google.com/*"],
js: ["/content/green.js"],
css: ["/content/green.css"]
}
]

// add content script
chrome.scripting.registerContentScripts(scripts);
alert('Content Script register for Green theme');
}

document.getElementById('btn-remove').onclick = () => {
// get all registered content scripts
chrome.scripting.getRegisteredContentScripts().then((scripts) => {

// get ids
const scriptIds = scripts.map(script => script.id);

//unregister content scripts
chrome.scripting.unregisterContentScripts(scriptIds);
});
}

background.js

// when extensions is installed
chrome.runtime.onInstalled.addListener((details) => {

//create context menu
chrome.contextMenus.create({
id: "random",
title: "Random Background",
contexts: ["page", "action"]
});

chrome.contextMenus.create({
id: "orange",
title: "Run Orange Theme",
contexts: ["page", "action"]
});

chrome.contextMenus.create({
id: "green",
title: "Run Green Theme",
contexts: ["page", "action"]
});

chrome.contextMenus.create({
id: "remove",
title: "Remove CSS",
contexts: ["page", "action"]
});
});


chrome.contextMenus.onClicked.addListener((info, tab) => {
switch (info.menuItemId) {
case "random":
// Inject and Run Function
function getRandomColor() {
const colors = ['red', 'blue', 'green', 'yellow', 'orange', 'pink'];
const index = parseInt(Math.random() * colors.length);
return colors[index];
}
chrome.scripting.executeScript({

// the target tab to inject to
target: {
tabId: tab.id
},

// the function that will be injected
func: (color, textColor) => {
document.body.style.backgroundColor = color;
document.body.style.color = textColor;
},

// arguments for the function
args: [getRandomColor(), "white"],
});
break;
case "orange":
chrome.scripting.executeScript({
target: { tabId: tab.id },
files: ['/content/orange.js'],
});
chrome.scripting.insertCSS({
target: { tabId: tab.id },
files: ['/content/orange.css'],
})
break;
case "green":
chrome.scripting.executeScript({
target: { tabId: tab.id },
files: ['/content/green.js'],
});
chrome.scripting.insertCSS({
target: { tabId: tab.id },
files: ['/content/green.css'],
})
break;
case "remove":
chrome.scripting.removeCSS({
target: { tabId: tab.id },
files: ['/content/green.css', '/content/orange.css'],
});
break;
default:
break;
}
})

green.js


// When the page is double clicked on an image
for (const img of document.querySelectorAll('img')) {
img.ondblclick = () => {
const result = window.confirm('Change Background to Pink?');
if (result) {
document.body.style.background = 'pink';
}
}
}

green.css

body {
background: green !important;
}

a {
color: #caf29a!important;
}

orange.js


// When the page is double clicked on an image
for (const img of document.querySelectorAll('img')) {
img.ondblclick = () => {
const result = window.confirm('Change Background to Red?');
if (result) {
document.body.style.background = 'red';
}
}
}

orange.css

body {
background: orange !important;
}

a {
color: #f2df9a !important;
}

Leave a Reply

Your email address will not be published. Required fields are marked *

More Articles & Posts