Adding Web Interception Abilities to Your Chrome Extension

A few days ago I published a post about how to get started with building a Chrome extension with React. That post was just the starting point of an extension.

In this post we will drill down a little bit and I’ll explain how to add a web interception abilities to the extension using Chrome developer API.

Note: This post is based on the previous post so be sure to read the previous post before reading.

A background script is a long-running script that can handle a long-running task or state for your extension. It runs as part of the extension process but has no UI and is executed separately from the extension’s UI. Each extension can have only one background script which exists for the lifetime of the extension.

In order to create a background script you have to do two things — add a script file for the background script and update the extension manifest to support that script.

In the extension, add to the public folder an app folder and add a background.js file inside of it. The file will be empty at first. Then, add to the extension manifest the following property:

"background": {
"scripts": ["app/background.js"]
}

Now the project will look like:

Extension Project with background.js

Run the build again by using npm run build command and go to the Chrome extension list. Use Ctrl+R or press the Reload link in your extension list item to refresh the extension with the latest version.

The Extension with Background Page Link

In the extension you will see a new line with a link:

Inspect views: background page

The background page link will open the background script in debug mode which will be helpful later for debug purpose.

Now we are ready to move forward and implement some background script functionality.

As stated in the post’s beginning we will want to make our extension a proxy for network requests. In order to intercept HTTP requests you have to know the Chrome webRequest API.

Chrome webRequest API is a set of helpful events and functions which enable us to monitor and analyze web traffic. You can hook to the life cycle of a web request using built-in events and then decide what to do. For example, ad blockers use this kind of interception mechanism to cancel requests to known ad domains.

In order to use the API you must declare the webRequest permission as part of the extension manifest. In the manifest just add to the permissions array the webRequest and the domain you would like to monitor:

"permissions": [
"webRequest",
"*://developer.mozilla.org/"
]

Note: If you won’t declare the permissions, you won’t be able to use the API in your extension.

The most useful events that you will probably want to listen on are:

  • onBeforeRequest — is fired before the request is sent.
  • onCompleted — is fired when the request completed successfully.
  • onErrorOccured — is fired when there is an request error.

Note: There are other request life cycle events which are very useful but I won’t discuss them in this post.

In the background.js file add the following code:

(function() {
const tabStorage = {};
const networkFilters = {
urls: [
"*://developer.mozilla.org/*"
]
};

chrome.webRequest.onBeforeRequest.addListener((details) => {
const { tabId, requestId } = details;
if (!tabStorage.hasOwnProperty(tabId)) {
return;
}

tabStorage[tabId].requests[requestId] = {
requestId: requestId,
url: details.url,
startTime: details.timeStamp,
status: 'pending'
};
console.log(tabStorage[tabId].requests[requestId]);
}, networkFilters);

chrome.webRequest.onCompleted.addListener((details) => {
const { tabId, requestId } = details;
if (!tabStorage.hasOwnProperty(tabId) || !tabStorage[tabId].requests.hasOwnProperty(requestId)) {
return;
}

const request = tabStorage[tabId].requests[requestId];

Object.assign(request, {
endTime: details.timeStamp,
requestDuration: details.timeStamp - request.startTime,
status: 'complete'
});
console.log(tabStorage[tabId].requests[details.requestId]);
}, networkFilters);

chrome.webRequest.onErrorOccurred.addListener((details)=> {
const { tabId, requestId } = details;
if (!tabStorage.hasOwnProperty(tabId) || !tabStorage[tabId].requests.hasOwnProperty(requestId)) {
return;
}

const request = tabStorage[tabId].requests[requestId];
Object.assign(request, {
endTime: details.timeStamp,
status: 'error',
});
console.log(tabStorage[tabId].requests[requestId]);
}, networkFilters);

chrome.tabs.onActivated.addListener((tab) => {
const tabId = tab ? tab.tabId : chrome.tabs.TAB_ID_NONE;
if (!tabStorage.hasOwnProperty(tabId)) {
tabStorage[tabId] = {
id: tabId,
requests: {},
registerTime: new Date().getTime()
};
}
});
chrome.tabs.onRemoved.addListener((tab) => {
const tabId = tab.tabId;
if (!tabStorage.hasOwnProperty(tabId)) {
return;
}
tabStorage[tabId] = null;
});
}());

Let’s understand what the code is doing.

In the beginning we declare an in-memory storage to hold the requests information per tabs. We also declare a network filter object which later be used by any webRequest event to filter only the wanted requests. In the code example, only if someone navigates to Mozilla Developer Network (MDN) we will run the our code.

After the variable declarations we add listeners to the onBeforeRequest, onCompleted and onErrorOccured events. In each event we add some request data that we are going to use later on to our storage. Pay attention that for each tab we create it’s own request collector.

Last but not least, we use chrome.tabs.onActivated to monitor when a tab is created and chrome.tabs.onRemoved to monitor when tab is removed. For each tab we will add a new tab object into tabStorage or remove it. When a web request will go out we will be able to add it’s details to the already available tab object. That is it for now in the background script.

You can now compile the extension, reload it in Chrome extension list and then open the background script for debug. Use the background page link in the Chrome extension list to do that. Then, navigate to a web page in MDN and see in the console the details that were collected.

The Output in the Console

In the post I explained how to add a background script to your Chrome extension. I also showed you how to use the background script to monitor web requests using the webRequest API. In a follow up post I’m going to show you how to reflect the data we collected in the popup UI using React.

See you in the next post!

Hardcore web developer, @sparXys CEO, Google Web Technologies GDE, Microsoft MVP, Pro SPA Development co-author, husband, dad and a geek.