DEV Community

Prakash
Prakash

Posted on

Creating a browser extension for Chrome / Edge

Creating a browser extension has never been easier, thanks to the comprehensive documentation and support provided by browser vendors. Below, I’ll walk through the steps to create a simple extension for both Chrome and Microsoft Edge using Manifest V3. We will use this tool to print the list of HTTP requests that are fired in a given webpage and list it in the extension.

Basics of extensions:

Manifests – A manifest is a JSON file that contains metadata about a browser extension, such as its name, version, permissions, and the files it uses. It serves as the blueprint for the extension, informing the browser about the extension’s capabilities and how it should be loaded.

Key Components of a Manifest File:

Here are the key components typically found in a Manifest V3 file:

1. Manifest Version: There are different versions of the manifest file, with Manifest V3 being the latest and most widely adopted version. Manifest V3 introduces several changes aimed at improving security, privacy, and performance with lot of controversies around it. Read more about the controversies at Ghostery.

2. Name and Version: These fields define the name and version of the extension. Choose a unique name and version. An excellent guide of version semantics is available here.

3. Description: A short description of the extension’s functionality.

4. Action: Defines the default popup and icon for the browser action (e.g., toolbar button).

5. Background: Specifies the background script that runs in the background and can handle events like network requests and alarms.

6. Content Scripts: Defines scripts and stylesheets to be injected into matching web pages.

7. Permissions: Lists the permissions the extension needs to operate, such as access to tabs, storage, and specific websites.

8. Icons: Specifies the icons for the extension in different sizes. For this post I created a simple icon using Microsoft Designer. I gave a simple prompt with the description above and I got the below image. Extension requires different sizes for showing it in different places. I used Chrome Extension Icon Generator and generated different sizes as needed.

9. Web Accessible Resources: Defines which resources can be accessed by web pages.

Create a project structure as follows:

HttpRequestViewer/
|-- manifest.json
|-- popup.html
|-- popup.js
|-- background.js
|-- history.html
|-- history.js
|-- popup.css
|-- styles.css
|-- icons/
    |-- icon.png
    |-- icon16.png
    |-- icon32.png
    |-- icon48.png
    |-- icon128.png
Enter fullscreen mode Exit fullscreen mode

Manifest.json

{
  "name": "API Request Recorder",
  "description": "Extension to record all the HTTP request from a webpage.",
  "version": "0.0.1",
  "manifest\_version": 3,
  "host\_permissions": \[""\],
  "permissions": \["activeTab", "webRequest", "storage"\],
  "action": {
    "default\_popup": "popup.html",
    "default\_icon": "icons/icon.png"
  },
  "background": {
    "service\_worker": "background.js"
  },
  "icons": {
    "16": "icons/icon16.png",
    "32": "icons/icon32.png",
    "48": "icons/icon48.png",
    "128": "icons/icon128.png"
  },
  "content\_security\_policy": {
    "extension\_pages": "script-src 'self'; object-src 'self';"
  },
  "web\_accessible\_resources": \[{ "resources": \["images/\*.png"\], "matches": \["https://\*/\*"\] }\]
}
Enter fullscreen mode Exit fullscreen mode

popup.html

We have two options with the extension.

1. A button with record option to start recording all the HTTP requests

2. Link to view the history of HTTP Requests recorded

<!DOCTYPE html>
<html>
  <head>
    <title>API Request Recorder</title>

    <link rel="stylesheet" href="popup.css" />
  </head>
  <body>
    <div class="heading">
      <img class="logo" src="icons/icon48.png" />
      <h1>API Request Recorder</h1>
    </div>
    <button id="startStopRecord">Record</button>

    <div class="button-group">
      <a href="#" id="history">View Requests</a>
    </div>

    <script src="popup.js"></script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

popup.js

Two event listeners are registered for recording (with start / stop) and viewing history.

First event is used to send a message to the background.js, while the second one instructs chrome to open the history page in new tab.

document.getElementById("startStopRecord").addEventListener("click", () => {
  chrome.runtime.sendMessage({ action: "startStopRecord" });
});

document.getElementById("history").addEventListener("click", () => {
  chrome.tabs.create({ url: chrome.runtime.getURL("/history.html") });
});
Enter fullscreen mode Exit fullscreen mode

history.html

<!DOCTYPE html>
<html>
  <head>
    <title>History</title>
    <link rel="stylesheet" href="styles.css" />
  </head>
  <body>
    <h1>History Page</h1>
    <table>
      <thead>
        <tr>
          <th>Method</th>
          <th>URL</th>
          <th>Body</th>
        </tr>
      </thead>
      <tbody id="recorded-data-body">
        <!-- Data will be populated here -->
      </tbody>
    </table>
    <script src="history.js"></script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

history.js

Requests background.js to “getRecordedData” and renders the result in the html format.

document.addEventListener("DOMContentLoaded", () => {
  chrome.runtime.sendMessage({ action: "getRecordedData" }, (response) => {
    const tableBody = document.getElementById("recorded-data-body");
    response.forEach((record) => {
      const row = document.createElement("tr");
      const urlCell = document.createElement("td");
      const methodCell = document.createElement("td");
      const bodyCell = document.createElement("td");

      urlCell.textContent = record.url;
      methodCell.textContent = record.method;
      bodyCell.textContent = record.body;

      row.appendChild(methodCell);
      row.appendChild(urlCell);
      row.appendChild(bodyCell);
      tableBody.appendChild(row);
    });
  });
});
Enter fullscreen mode Exit fullscreen mode

background.js

Background JS works as a service worker for this extension, listening and handling events.

The background script does not have access to directly manipulate the user page content, but can post results back for the popup/history script to handle the cosmetic changes.

let isRecording = false;
let recordedDataList = \[\];

chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  console.log("Obtined message: ", message);
  if (message.action === "startStopRecord") {
    if (isRecording) {
      isRecording = false;
      console.log("Recording stopped...");
      sendResponse({ recorder: { status: "stopped" } });
    } else {
      isRecording = true;
      console.log("Recording started...");
      sendResponse({ recorder: { status: "started" } });
    }
  } else if (message.action === "getRecordedData") {
    sendResponse(recordedDataList);
  } else {
    console.log("Unhandled action ...");
  }
});

chrome.webRequest.onBeforeRequest.addListener(
  (details) => {
    if (isRecording) {
      let requestBody = "";
      if (details.requestBody) {
        if (details.requestBody.formData) {
          requestBody = JSON.stringify(details.requestBody.formData);
        } else if (details.requestBody.raw) {
          requestBody = new TextDecoder().decode(new Uint8Array(details.requestBody.raw\[0\].bytes));
        }
      }
      recordedDataList.push({
        url: details.url,
        method: details.method,
        body: requestBody,
      });
      console.log("Recorded Request:", {
        url: details.url,
        method: details.method,
        body: requestBody,
      });
    }
  },
  { urls: \[""\] },
  \["requestBody"\]
);
Enter fullscreen mode Exit fullscreen mode

Lets load the Extension

All set, now lets load the extension and test it.

  • Open Chrome/Edge and go to chrome://extensions/ or edge://extensions/ based on your browser.
  • Enable “Developer mode” using the toggle in the top right corner.
  • Click “Load unpacked” and select the directory of your extension.

Load extension
upload extension
upload extension 1

  • Your extension should now be loaded, and you can interact with it using the popup.
  • When you click the “Record” button, it will start logging API requests to the console.

  • Click the “Record” button again and hit the “View requests” link in the popup to view the history of APIs.

I have a sample page (https://itechgenie.com/demos/apitesting/index.html) with 4 API calls, which also loads images based on the API responses. You could see all the API requests that is fired from the page including the JS, CSS, Images and API calls.

Console logs

View requests

Now its up to the developers imagination to build the extension to handle these APIs request and response data and give different experience.

Code is available in GitHub at HttpRequestViewer

Post originally shared at ITechGenie.com

Top comments (2)

Collapse
 
ramesh_ramados profile image
ramesh ramados

Steps are described cleanly. Good to know these steps involved to create an extension.

Collapse
 
abinavaraam_k profile image
abinavaraam K

Good one