Menu
Back to Blog
1 min read
Automatisierung

Browser Extensions für Automation & Productivity

Browser Extensions entwickeln für Automation. Manifest V3, Content Scripts, Background Workers und Cross-Browser Kompatibilität.

Browser ExtensionChrome ExtensionManifest V3Content ScriptAutomationWeb Extension API
Browser Extensions für Automation & Productivity

Browser Extensions für Automation & Productivity

Meta-Description: Browser Extensions entwickeln für Automation. Manifest V3, Content Scripts, Background Workers und Cross-Browser Kompatibilität.

Keywords: Browser Extension, Chrome Extension, Manifest V3, Content Script, Automation, Web Extension API


Einführung

Browser Extensions erweitern die Browser-Funktionalität direkt. Mit Manifest V3 und der WebExtensions API können Sie Workflows automatisieren, Daten extrahieren und Produktivität steigern.


Extension Architecture

┌─────────────────────────────────────────────────────────────┐
│              BROWSER EXTENSION ARCHITECTURE                 │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Components:                                                │
│  ├── Manifest (manifest.json)                              │
│  │   └── Extension Metadata & Permissions                  │
│  │                                                         │
│  ├── Service Worker (Background)                           │
│  │   └── Event Handling, API Calls, State                  │
│  │                                                         │
│  ├── Content Scripts                                       │
│  │   └── DOM Manipulation auf Web Pages                    │
│  │                                                         │
│  ├── Popup (UI)                                            │
│  │   └── Toolbar Button Interface                          │
│  │                                                         │
│  └── Options Page                                          │
│       └── Extension Settings                               │
│                                                             │
│  Communication:                                             │
│  Content Script ←→ Service Worker ←→ Popup                 │
│  (chrome.runtime.sendMessage / onMessage)                  │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Manifest V3 Setup

// manifest.json
{
  "manifest_version": 3,
  "name": "Automation Helper",
  "version": "1.0.0",
  "description": "Automate repetitive browser tasks",

  "permissions": [
    "activeTab",
    "storage",
    "alarms",
    "contextMenus"
  ],

  "host_permissions": [
    "https://*.example.com/*"
  ],

  "background": {
    "service_worker": "background.js",
    "type": "module"
  },

  "content_scripts": [
    {
      "matches": ["https://*.example.com/*"],
      "js": ["content.js"],
      "css": ["content.css"],
      "run_at": "document_idle"
    }
  ],

  "action": {
    "default_popup": "popup.html",
    "default_icon": {
      "16": "icons/icon16.png",
      "48": "icons/icon48.png",
      "128": "icons/icon128.png"
    }
  },

  "options_page": "options.html",

  "icons": {
    "16": "icons/icon16.png",
    "48": "icons/icon48.png",
    "128": "icons/icon128.png"
  }
}

Service Worker (Background)

// background.ts
chrome.runtime.onInstalled.addListener(() => {
  console.log('Extension installed');

  // Context Menu erstellen
  chrome.contextMenus.create({
    id: 'extract-data',
    title: 'Daten extrahieren',
    contexts: ['selection']
  });

  // Default Settings
  chrome.storage.sync.set({
    autoExtract: true,
    extractInterval: 5
  });
});

// Context Menu Handler
chrome.contextMenus.onClicked.addListener((info, tab) => {
  if (info.menuItemId === 'extract-data' && info.selectionText) {
    processSelectedText(info.selectionText);
  }
});

// Message Handler
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (message.type === 'EXTRACT_PAGE') {
    handleExtraction(message.data)
      .then(result => sendResponse({ success: true, data: result }))
      .catch(error => sendResponse({ success: false, error: error.message }));

    return true;  // Async Response
  }

  if (message.type === 'GET_SETTINGS') {
    chrome.storage.sync.get(['autoExtract', 'extractInterval'], (settings) => {
      sendResponse(settings);
    });
    return true;
  }
});

// Alarm für wiederkehrende Tasks
chrome.alarms.create('sync-data', { periodInMinutes: 5 });

chrome.alarms.onAlarm.addListener((alarm) => {
  if (alarm.name === 'sync-data') {
    syncDataToServer();
  }
});

async function handleExtraction(data: any) {
  // API Call oder Datenverarbeitung
  const response = await fetch('https://api.example.com/extract', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(data)
  });

  return response.json();
}

async function syncDataToServer() {
  const { extractedData } = await chrome.storage.local.get('extractedData');

  if (extractedData && extractedData.length > 0) {
    await fetch('https://api.example.com/sync', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(extractedData)
    });

    // Clear nach Sync
    await chrome.storage.local.set({ extractedData: [] });
  }
}

Content Script

// content.ts

// DOM Ready
if (document.readyState === 'loading') {
  document.addEventListener('DOMContentLoaded', init);
} else {
  init();
}

function init() {
  console.log('Content script loaded on', window.location.href);

  // UI Element einfügen
  injectUI();

  // Page Observer für dynamische Inhalte
  observePageChanges();

  // Auto-Extract wenn aktiviert
  chrome.storage.sync.get('autoExtract', ({ autoExtract }) => {
    if (autoExtract) {
      extractPageData();
    }
  });
}

function injectUI() {
  const container = document.createElement('div');
  container.id = 'extension-helper';
  container.innerHTML = `
    <button id="ext-extract-btn" class="ext-btn">
      Daten extrahieren
    </button>
  `;

  document.body.appendChild(container);

  document.getElementById('ext-extract-btn')?.addEventListener('click', () => {
    extractPageData();
  });
}

async function extractPageData() {
  const data = {
    url: window.location.href,
    title: document.title,
    timestamp: new Date().toISOString(),
    content: extractContent()
  };

  // An Background Script senden
  const response = await chrome.runtime.sendMessage({
    type: 'EXTRACT_PAGE',
    data
  });

  if (response.success) {
    showNotification('Daten erfolgreich extrahiert!');
  } else {
    showNotification('Fehler: ' + response.error, 'error');
  }
}

function extractContent() {
  // Beispiel: Produkt-Daten extrahieren
  const products: any[] = [];

  document.querySelectorAll('.product-item').forEach((el) => {
    products.push({
      name: el.querySelector('.product-name')?.textContent?.trim(),
      price: el.querySelector('.price')?.textContent?.trim(),
      image: el.querySelector('img')?.getAttribute('src')
    });
  });

  return products;
}

function observePageChanges() {
  const observer = new MutationObserver((mutations) => {
    mutations.forEach((mutation) => {
      if (mutation.addedNodes.length > 0) {
        // Neue Elemente verarbeiten
        handleNewElements(mutation.addedNodes);
      }
    });
  });

  observer.observe(document.body, {
    childList: true,
    subtree: true
  });
}

function handleNewElements(nodes: NodeList) {
  nodes.forEach((node) => {
    if (node instanceof HTMLElement) {
      // Highlight neue Produkte
      const products = node.querySelectorAll('.product-item');
      products.forEach((product) => {
        product.classList.add('ext-highlighted');
      });
    }
  });
}

function showNotification(message: string, type: 'success' | 'error' = 'success') {
  const notification = document.createElement('div');
  notification.className = `ext-notification ext-${type}`;
  notification.textContent = message;

  document.body.appendChild(notification);

  setTimeout(() => notification.remove(), 3000);
}

Popup UI

<!-- popup.html -->
<!DOCTYPE html>
<html>
<head>
  <link rel="stylesheet" href="popup.css">
</head>
<body>
  <div class="popup-container">
    <h1>Automation Helper</h1>

    <div class="status">
      <span id="status-indicator" class="indicator"></span>
      <span id="status-text">Bereit</span>
    </div>

    <div class="actions">
      <button id="extract-btn" class="btn primary">
        Aktuelle Seite extrahieren
      </button>

      <button id="batch-extract-btn" class="btn secondary">
        Batch-Extraktion
      </button>
    </div>

    <div class="stats">
      <div class="stat">
        <span class="stat-value" id="extracted-count">0</span>
        <span class="stat-label">Extrahiert</span>
      </div>
      <div class="stat">
        <span class="stat-value" id="synced-count">0</span>
        <span class="stat-label">Synchronisiert</span>
      </div>
    </div>

    <a href="options.html" target="_blank" class="settings-link">
      Einstellungen
    </a>
  </div>

  <script src="popup.js" type="module"></script>
</body>
</html>
// popup.ts
document.addEventListener('DOMContentLoaded', async () => {
  // Stats laden
  await updateStats();

  // Event Listeners
  document.getElementById('extract-btn')?.addEventListener('click', extractCurrent);
  document.getElementById('batch-extract-btn')?.addEventListener('click', batchExtract);
});

async function updateStats() {
  const { extractedCount = 0, syncedCount = 0 } = await chrome.storage.local.get([
    'extractedCount',
    'syncedCount'
  ]);

  document.getElementById('extracted-count')!.textContent = extractedCount.toString();
  document.getElementById('synced-count')!.textContent = syncedCount.toString();
}

async function extractCurrent() {
  const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });

  if (!tab.id) return;

  // Message an Content Script
  const response = await chrome.tabs.sendMessage(tab.id, { type: 'EXTRACT' });

  if (response.success) {
    setStatus('success', 'Extrahiert!');
    await updateStats();
  } else {
    setStatus('error', 'Fehler');
  }
}

async function batchExtract() {
  const tabs = await chrome.tabs.query({ url: 'https://*.example.com/*' });

  setStatus('loading', `Extrahiere ${tabs.length} Tabs...`);

  for (const tab of tabs) {
    if (tab.id) {
      await chrome.tabs.sendMessage(tab.id, { type: 'EXTRACT' });
    }
  }

  setStatus('success', `${tabs.length} Seiten extrahiert`);
  await updateStats();
}

function setStatus(type: 'success' | 'error' | 'loading', text: string) {
  const indicator = document.getElementById('status-indicator')!;
  const statusText = document.getElementById('status-text')!;

  indicator.className = `indicator ${type}`;
  statusText.textContent = text;
}

Storage API

// Sync Storage (zwischen Geräten synchronisiert, 100KB Limit)
await chrome.storage.sync.set({
  settings: {
    autoExtract: true,
    theme: 'dark'
  }
});

const { settings } = await chrome.storage.sync.get('settings');

// Local Storage (nur lokal, 5MB Limit)
await chrome.storage.local.set({
  extractedData: largeDataArray
});

// Storage Change Listener
chrome.storage.onChanged.addListener((changes, areaName) => {
  for (const [key, { oldValue, newValue }] of Object.entries(changes)) {
    console.log(`${areaName}.${key} changed from`, oldValue, 'to', newValue);
  }
});

Cross-Browser Kompatibilität

// Browser Detection
const isChrome = typeof chrome !== 'undefined' && chrome.runtime;
const isFirefox = typeof browser !== 'undefined';

// Unified API
const browserAPI = isFirefox ? browser : chrome;

// Polyfill für Firefox Promise-based API
function promisify<T>(chromeMethod: (...args: any[]) => void): (...args: any[]) => Promise<T> {
  return (...args) => new Promise((resolve, reject) => {
    chromeMethod(...args, (result: T) => {
      if (chrome.runtime.lastError) {
        reject(chrome.runtime.lastError);
      } else {
        resolve(result);
      }
    });
  });
}

// webextension-polyfill verwenden
import browser from 'webextension-polyfill';

// Unified API für alle Browser
const tabs = await browser.tabs.query({ active: true });

Fazit

Browser Extensions bieten:

  1. Tiefe Integration: Direkter DOM-Zugriff
  2. Automation: Wiederkehrende Tasks automatisieren
  3. Produktivität: Workflows optimieren
  4. Cross-Platform: Chrome, Firefox, Edge

Mächtige Tools für Power-User und Entwickler.


Bildprompts

  1. "Browser with extension icon showing automation, productivity concept"
  2. "Extension popup interacting with webpage, data extraction"
  3. "Multiple browser windows synchronized, cross-browser extension"

Quellen