AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified Apache-2.0 Self-run

Browser Extension Patterns

skill-organvm-a-i-skills-browser-extension-patterns · by organvm

Build browser extensions with Manifest V3 for Chrome, Firefox, and cross-browser compatibility. Covers content scripts, background workers, popup UI, storage APIs, and extension messaging. Triggers on browser extension development, Manifest V3, or Chrome extension requests.

No reviews yet
0 installs
11 views
0.0% view→install

Install

$ agentstack add skill-organvm-a-i-skills-browser-extension-patterns

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-organvm-a-i-skills-browser-extension-patterns)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
1mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Browser Extension Patterns? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Browser Extension Patterns

Build cross-browser extensions with Manifest V3 architecture.

Manifest V3 Structure

my-extension/
├── manifest.json          # Extension manifest
├── background/
│   └── service-worker.js  # Background service worker
├── content/
│   └── content-script.js  # Injected into web pages
├── popup/
│   ├── popup.html         # Popup UI
│   ├── popup.js           # Popup logic
│   └── popup.css          # Popup styles
├── options/
│   ├── options.html       # Settings page
│   └── options.js
├── icons/
│   ├── icon-16.png
│   ├── icon-48.png
│   └── icon-128.png
└── _locales/              # Internationalization
    └── en/messages.json

Manifest Configuration

{
  "manifest_version": 3,
  "name": "My Extension",
  "version": "1.0.0",
  "description": "Brief description of what it does",
  "permissions": ["storage", "activeTab"],
  "host_permissions": ["https://*.example.com/*"],
  "background": {
    "service_worker": "background/service-worker.js"
  },
  "content_scripts": [{
    "matches": ["https://*.example.com/*"],
    "js": ["content/content-script.js"],
    "css": ["content/content-style.css"],
    "run_at": "document_idle"
  }],
  "action": {
    "default_popup": "popup/popup.html",
    "default_icon": {
      "16": "icons/icon-16.png",
      "48": "icons/icon-48.png",
      "128": "icons/icon-128.png"
    }
  },
  "options_page": "options/options.html",
  "icons": {
    "16": "icons/icon-16.png",
    "48": "icons/icon-48.png",
    "128": "icons/icon-128.png"
  }
}

Background Service Worker

// background/service-worker.js

// Installation
chrome.runtime.onInstalled.addListener((details) => {
  if (details.reason === 'install') {
    chrome.storage.local.set({ settings: { enabled: true, theme: 'light' } });
  }
});

// Message handling from content scripts and popup
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  switch (message.type) {
    case 'getData':
      fetchData(message.url).then(sendResponse);
      return true; // Async response
    case 'updateBadge':
      chrome.action.setBadgeText({ text: String(message.count) });
      break;
  }
});

// Alarm-based periodic tasks (replaces MV2 persistent background)
chrome.alarms.create('sync', { periodInMinutes: 30 });
chrome.alarms.onAlarm.addListener((alarm) => {
  if (alarm.name === 'sync') syncData();
});

Content Scripts

// content/content-script.js

// DOM manipulation on target pages
function enhancePage() {
  const elements = document.querySelectorAll('.target-class');
  elements.forEach(el => {
    const badge = document.createElement('span');
    badge.className = 'my-extension-badge';
    badge.textContent = 'Enhanced';
    el.appendChild(badge);
  });
}

// Run when DOM is ready
if (document.readyState === 'loading') {
  document.addEventListener('DOMContentLoaded', enhancePage);
} else {
  enhancePage();
}

// Communicate with background
async function requestData(url) {
  return chrome.runtime.sendMessage({ type: 'getData', url });
}

// Listen for messages from background/popup
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (message.type === 'getPageData') {
    sendResponse({ title: document.title, url: location.href });
  }
});

Storage Patterns

// Chrome storage API (synced across devices)
const storage = {
  async get(key) {
    const result = await chrome.storage.sync.get(key);
    return result[key];
  },

  async set(key, value) {
    await chrome.storage.sync.set({ [key]: value });
  },

  async getLocal(key) {
    const result = await chrome.storage.local.get(key);
    return result[key];
  },

  onChange(callback) {
    chrome.storage.onChanged.addListener((changes, area) => {
      callback(changes, area);
    });
  }
};

// Usage
await storage.set('settings', { theme: 'dark', enabled: true });
const settings = await storage.get('settings');

Popup UI


  

  
    My Extension
    
       Enabled
    
    
  
  
// popup/popup.js
document.addEventListener('DOMContentLoaded', async () => {
  const settings = await chrome.storage.sync.get('settings');
  document.getElementById('enabled').checked = settings.settings?.enabled;

  document.getElementById('enabled').addEventListener('change', async (e) => {
    await chrome.storage.sync.set({
      settings: { ...settings.settings, enabled: e.target.checked }
    });
    // Notify content scripts
    const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
    chrome.tabs.sendMessage(tab.id, { type: 'settingsChanged', enabled: e.target.checked });
  });
});

Cross-Browser Compatibility

// Polyfill for Firefox WebExtensions API
const browser = globalThis.browser || globalThis.chrome;

// Feature detection
const isFirefox = typeof browser !== 'undefined' && browser.runtime?.getBrowserInfo;
const isChrome = typeof chrome !== 'undefined' && chrome.runtime?.id;

Firefox Manifest Differences

{
  "background": {
    "scripts": ["background/service-worker.js"]
  },
  "browser_specific_settings": {
    "gecko": {
      "id": "my-extension@example.com",
      "strict_min_version": "109.0"
    }
  }
}

Permission Strategy

| Permission | When | Impact | |-----------|------|--------| | activeTab | Need current tab only | Low (user-triggered) | | storage | Need to save settings | Low | | tabs | Need tab URLs/titles | Medium | | host_permissions | Need page access | High (shows warning) | | `` | Need all page access | Very High (avoid if possible) |

Principle: Request minimum permissions. Use activeTab over broad host permissions when possible.

Anti-Patterns

  • MV2 patterns in MV3 — No persistent background pages; use service workers and alarms
  • `` permission — Request only the hosts you need
  • Synchronous storage — Always use async chrome.storage API
  • No error handling in messaging — Messages fail silently if receiver doesn't exist
  • Heavy content scripts — Minimize injected code; communicate with background for heavy work
  • No uninstall cleanup — Use runtime.onInstalled to handle updates and cleanup

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.