Browser Extensions Integration

Build powerful browser extensions using JSON-RPC for seamless communication

Overview

Browser extensions provide powerful ways to enhance web applications, and JSON-RPC offers an excellent protocol for communication between extension components and web services. This guide covers how to implement JSON-RPC in browser extensions for Chrome, Firefox, and Safari.

JSON-RPC is particularly well-suited for browser extensions because:

  • It provides a standardized way to communicate between background scripts, content scripts, and popup interfaces
  • It enables structured communication with backend services that your extension might interact with
  • The lightweight format is ideal for the limited resources available to browser extensions
  • Version compatibility is easy to maintain across extension updates

Extension Architecture with JSON-RPC

Typical Extension Components

Background Script

Acts as a JSON-RPC server, processing requests from content scripts and popup UI

Content Scripts

JSON-RPC clients that send requests to the background script

Popup/Options UI

Another JSON-RPC client that communicates with the background script

Communication Flow

In a typical browser extension that uses JSON-RPC:

  1. Content scripts inject into web pages and monitor for specific actions
  2. When an action is detected, the content script sends a JSON-RPC request to the background script
  3. The background script processes the request and returns a JSON-RPC response
  4. The popup UI can also send JSON-RPC requests to fetch state or trigger actions

Tip: Use the Chrome Extensions messaging API with JSON-RPC as the payload format for consistent, well-structured communication between extension components.

Chrome Extensions Implementation

Here's how to implement JSON-RPC in a Chrome extension:

1. Set up the manifest.json

{
  "manifest_version": 3,
  "name": "JSON-RPC Example Extension",
  "version": "1.0",
  "description": "Example Chrome extension using JSON-RPC",
  "background": {
    "service_worker": "background.js"
  },
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["content.js"]
    }
  ],
  "action": {
    "default_popup": "popup.html"
  },
  "permissions": ["storage"]
}

2. Implement the Background Script (JSON-RPC Server)

// background.js
const methods = {
  // Example method that stores data
  'data.save': async (params) => {
    const { key, value } = params;
    return new Promise((resolve) => {
      chrome.storage.local.set({ [key]: value }, () => {
        resolve({ success: true });
      });
    });
  },
  
  // Example method that retrieves data
  'data.get': async (params) => {
    const { key } = params;
    return new Promise((resolve) => {
      chrome.storage.local.get([key], (result) => {
        resolve({ value: result[key] });
      });
    });
  }
};

// Handle JSON-RPC messages
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  // Validate JSON-RPC request format
  if (!request.jsonrpc || request.jsonrpc !== '2.0' || !request.method) {
    sendResponse({
      jsonrpc: '2.0',
      error: { code: -32600, message: 'Invalid Request' },
      id: request.id
    });
    return true;
  }
  
  // Check if method exists
  if (!methods[request.method]) {
    sendResponse({
      jsonrpc: '2.0',
      error: { code: -32601, message: 'Method not found' },
      id: request.id
    });
    return true;
  }
  
  // Process the method and send response
  methods[request.method](request.params)
    .then(result => {
      sendResponse({
        jsonrpc: '2.0',
        result,
        id: request.id
      });
    })
    .catch(err => {
      sendResponse({
        jsonrpc: '2.0',
        error: { code: -32603, message: 'Internal error', data: err.message },
        id: request.id
      });
    });
  
  // Return true to indicate we'll send a response asynchronously
  return true;
});

3. Implement the Content Script (JSON-RPC Client)

// content.js
function sendJsonRpcRequest(method, params) {
  return new Promise((resolve, reject) => {
    const request = {
      jsonrpc: '2.0',
      method,
      params,
      id: Date.now().toString()
    };
    
    chrome.runtime.sendMessage(request, response => {
      if (!response) {
        reject(new Error('No response from background script'));
        return;
      }
      
      if (response.error) {
        reject(new Error(response.error.message));
        return;
      }
      
      resolve(response.result);
    });
  });
}

// Example usage
async function saveData() {
  try {
    const result = await sendJsonRpcRequest('data.save', { 
      key: 'userPreference', 
      value: 'dark-mode' 
    });
    console.log('Data saved:', result);
  } catch (error) {
    console.error('Error saving data:', error);
  }
}

Security Considerations

Input Validation

Always validate JSON-RPC parameters to prevent injection attacks. Never trust data coming from content scripts that could be manipulated by malicious websites.

Method Access Control

Implement access control for JSON-RPC methods. Not all methods should be accessible from content scripts.

Data Sanitization

Sanitize any data retrieved from web pages before processing it through your JSON-RPC methods.

Cross-Origin Considerations

Be mindful of cross-origin requests when your extension interacts with external services using JSON-RPC.

Warning: Always assume that content scripts operate in a hostile environment. Implement proper validation and sanitization for all JSON-RPC communication.

Example Projects

Check out these example browser extensions that use JSON-RPC: