suchen
HeimWeb-Frontendjs-TutorialBrowserübergreifende Erweiterung zum Blockieren von Websites

In diesem Artikel erkläre ich Schritt für Schritt meinen Prozess zum Erstellen einer Browser-Erweiterung zum Blockieren von Websites und beschreibe die Herausforderungen, denen ich begegnet bin, und die Lösungen, die ich gefunden habe. Dies ist kein erschöpfender Leitfaden. Ich behaupte nicht, in irgendetwas ein Experte zu sein. Ich möchte nur meinen Denkprozess hinter der Erstellung dieses Projekts teilen. Nehmen Sie also alles hier mit Vorsicht. Ich werde nicht jede Zeile abdecken, sondern mich stattdessen auf die Kernpunkte des Projekts, Schwierigkeiten, interessante Fälle und Eigenheiten des Projekts konzentrieren. Sie können den Quellcode gerne selbst genauer erkunden.


Inhaltsverzeichnis:

  • Vorwort
  • Projekt einrichten
  • Haupteingabeformular erstellen
  • URL-Blockierung behandeln
  • Optionsseite erstellen
  • Implementierung des strengen Modus
  • Fazit

Vorwort

Wie vielen Menschen fällt es mir schwer, mich auf verschiedene Aufgaben zu konzentrieren, insbesondere da das Internet der allgegenwärtige Ablenkungsfaktor ist. Glücklicherweise habe ich als Programmierer große Fähigkeiten zur Problemerstellung entwickelt und beschloss daher, statt nach einer besseren bestehenden Lösung zu suchen, eine eigene Browsererweiterung zu erstellen, die die Websites blockiert, auf die Benutzer den Zugriff beschränken möchten.
Lassen Sie uns zunächst die Anforderungen und Hauptfunktionen skizzieren. Die Erweiterung muss:

  • browserübergreifend sein.
  • Websites von der Blacklist blockieren.
  • Ermöglichen Sie die Auswahl einer Blockierungsoption: Blockieren Sie entweder die gesamte Domain mit ihren Subdomains oder blockieren Sie nur die ausgewählte URL.
  • bieten die Möglichkeit, eine blockierte Website zu deaktivieren, ohne sie von der Blacklist zu löschen.
  • stellen Sie eine Option bereit, um den Zugriff automatisch einzuschränken, wenn der Benutzer einen Rückfall erleidet oder vergisst, deaktivierte URLs wieder zu aktivieren (hilfreich für Menschen mit ADHS).

Einrichten des Projekts

Hier ist zunächst der Hauptstapel, den ich ausgewählt habe:

  • TypeScript: Ich habe mich für TS gegenüber JS entschieden, da es zahlreiche unbekannte APIs für Erweiterungen gibt, die ohne die Funktion zur automatischen Vervollständigung auskommen.
  • Webpack: In diesem Zusammenhang einfacher zu verwenden als tsc für die TS-Kompilierung. Außerdem hatte ich Probleme beim Generieren von browserkompatiblem JS mit tsc.
  • CSS: Vanilla CSS entsprach meinem Ziel hinsichtlich Einfachheit, kleinerer Bundle-Größe und minimaler Abhängigkeiten. Außerdem hatte ich das Gefühl, dass alles andere für eine Erweiterung mit nur ein paar Seiten übertrieben wäre. Aus diesen Gründen habe ich mich auch gegen die Verwendung von Tools wie React oder spezifischen Frameworks zum Aufbau von Erweiterungen entschieden.

Der Hauptunterschied zwischen der Erweiterungsentwicklung und der regulären Webentwicklung besteht darin, dass Erweiterungen auf Servicemitarbeitern basieren, die die meisten Ereignisse, Inhaltsskripte und Nachrichten zwischen ihnen verwalten.

Erstellen des Manifests

Um die browserübergreifende Funktionalität zu unterstützen, habe ich zwei Manifestdateien erstellt:

  • manifest.chrome.json: Für Chromes Manifest v3-Anforderung.
  • manifest.firefox.json: Für Firefox, das Manifest v2 besser unterstützt. Hier sind die Hauptunterschiede zwischen den beiden Dateien:

manifest.chrome.json:

{
  "manifest_version": 3,
  "action": {
    "default_title": "Click to show the form"
  },
  "incognito": "split",
  "permissions": [
    "activeTab",
    "declarativeNetRequestWithHostAccess",
    "scripting",
    "storage",
    "tabs"
  ],
  "host_permissions": ["*://*/"], // get access to all URLs
  "background": {
    "service_worker": "background.js"
  },
  "content_scripts": [{
    "matches": ["<all_urls>"]
  }],
  "web_accessible_resources": [
    {
      "resources": ["blocked.html", "options.html", "about.html", "icons/*.svg"],
      "matches": ["<all_urls>"]
    }
  ],
  "content_security_policy": {
    "extension_pages": "script-src 'self'; object-src 'self'"
  },
}
</all_urls></all_urls>

manifest.firefox.json:

{
  "manifest_version": 2,
  "browser_action": {
    "default_title": "Click to show the form"
  },
  "permissions": [
    "activeTab",
    "declarativeNetRequest",
    "declarativeNetRequestWithHostAccess",
    "scripting", 
    "storage",
    "tabs",
    "*://*/"
  ],
  "background": {
    "scripts": [
      "background.js"
    ],
    "persistent": false
  },
  "content_scripts": [{
    "matches": ["<all_urls>"],
    "js": [
      "options.js",
      "blocked.js",
      "about.js"
    ]
  }],
  "web_accessible_resources": [
    "blocked.html",
    "options.html", 
    "icons/*.svg"
  ],
  "content_security_policy": "script-src 'self'; object-src 'self'",
}
</all_urls>

Eine interessante Sache hierbei ist, dass Chrome die Eigenschaft „incognito“: „split“ benötigte, damit sie ordnungsgemäß im Inkognito-Modus funktioniert, während Firefox ohne diese Eigenschaft einwandfrei funktionierte.

Hier ist die grundlegende Dateistruktur der Erweiterung:

dist/
node_modules/
src/
|-- background.tsc
|-- content.ts
static/
|-- manifest.chrome.json
|-- manifest.firefox.json
package.json
tsconfig.json
webpack.config.js

Lassen Sie uns nun darüber sprechen, wie die Erweiterung funktionieren soll. Der Benutzer sollte in der Lage sein, eine Art Formular auszulösen, um die URL einzureichen, die er blockieren möchte. Wenn er auf eine URL zugreift, fängt die Erweiterung die Anfrage ab und prüft, ob sie blockiert oder zugelassen werden soll. Es benötigt außerdem eine Art Optionsseite, auf der ein Benutzer die Liste aller blockierten URLs sehen und eine URL zur Liste hinzufügen, bearbeiten, deaktivieren oder löschen kann.

Haupteingabeformular erstellen

Das Formular wird angezeigt, indem HTML und CSS in die aktuelle Seite eingefügt werden, wenn der Benutzer auf das Erweiterungssymbol klickt oder die Tastenkombination eingibt. Es gibt verschiedene Möglichkeiten, ein Formular anzuzeigen, z. B. das Aufrufen eines Popups, aber die Anpassungsmöglichkeiten sind für meinen Geschmack begrenzt. Das Hintergrundskript sieht so aus:

background.ts:

import browser, { DeclarativeNetRequest } from 'webextension-polyfill';

// on icon click
const action = chrome.action ?? browser.browserAction; // Manifest v2 only has browserAction method
action.onClicked.addListener(tab => {
  triggerPopup(tab as browser.Tabs.Tab);
});

// on shortcut key press 
browser.commands.onCommand.addListener(command => {
  if (command === 'trigger_form') {
    browser.tabs.query({ active: true, currentWindow: true })
      .then((tabs) => {
        const tab = tabs[0];
        if (tab) {
          triggerPopup(tab);
        }
      })
      .catch(error => console.error(error));
  }
});

function triggerPopup(tab: browser.Tabs.Tab) {
  if (tab.id) {
    const tabId = tab.id;
    browser.scripting.insertCSS(({
      target: { tabId },
      files: ['global.css', './popup.css'],
    }))
      .then(() => {
        browser.scripting.executeScript
          ? browser.scripting.executeScript({
            target: { tabId },
            files: ['./content.js'], // refer to the compiled JS files, not the original TS ones 
          })
          : browser.tabs.executeScript({
            file: './content.js',
          });
      })
      .catch(error => console.error(error));
  }
}

Das Einfügen von HTML in jede Seite kann zu unvorhersehbaren Ergebnissen führen, da es schwer vorherzusagen ist, wie sich unterschiedliche Stile von Webseiten auf das Formular auswirken werden. Eine bessere Alternative scheint die Verwendung von Shadow DOM zu sein, da es einen eigenen Spielraum für Stile schafft. Auf jeden Fall eine potenzielle Verbesserung, an der ich in Zukunft gerne arbeiten würde.

Aus Gründen der Browserkompatibilität habe ich webextension-polyfill verwendet. Dadurch musste ich keine separaten Erweiterungen für verschiedene Manifestversionen schreiben. Mehr darüber, was es bewirkt, können Sie hier lesen. Damit es funktioniert, habe ich die Datei browser-polyfill.js vor anderen Skripten in die Manifestdateien eingefügt.

manifest.chrome.json:

{
  "content_scripts": [{
    "js": ["browser-polyfill.js"]
  }],
}

manifest.firefox.json:

{
  "background": {
    "scripts": [
      "browser-polyfill.js",
      // other scripts
    ],
  },
  "content_scripts": [{
    "js": [
      "browser-polyfill.js",
      // other scripts
    ]
  }],
}

Der Prozess des Einfügens des Formulars ist eine einfache DOM-Manipulation. Beachten Sie jedoch, dass jedes Element einzeln erstellt werden muss, anstatt ein Vorlagenliteral auf ein Element anzuwenden. Obwohl ausführlicher und langwieriger, vermeidet diese Methode Warnungen wegen unsicherer HTML-Injektion, die wir andernfalls erhalten würden, wenn wir versuchen, den kompilierten Code im Browser auszuführen.

content.ts:

import browser from 'webextension-polyfill';
import { maxUrlLength, minUrlLength } from "./globals";
import { GetCurrentUrl, ResToSend } from "./types";
import { handleFormSubmission } from './helpers';

async function showPopup() {
  const body = document.body;
  const formExists = document.getElementById('extension-popup-form');
  if (!formExists) {
    const msg: GetCurrentUrl = { action: 'getCurrentUrl' };

    try {
      const res: ResToSend = await browser.runtime.sendMessage(msg);

      if (res.success && res.url) {
        const currUrl: string = res.url;
        const popupForm = document.createElement('form');
        popupForm.classList.add('extension-popup-form');
        popupForm.id = 'extension-popup-form';

        /* Create every child element the same way as above */

        body.appendChild(popupForm);
        popupForm.addEventListener('submit', (e) => {
          e.preventDefault();
          handleFormSubmission(popupForm, handleSuccessfulSubmission); // we'll discuss form submission later
        });
        document.addEventListener('keydown', (e) => {
          if (e.key === 'Escape') {
            if (popupForm) {
              body.removeChild(popupForm);
            }
          }
        });
      }
    } catch (error) {
      console.error(error);
      alert('Something went wrong. Please try again.');
    }
  }
}

function handleSuccessfulSubmission() {
  hidePopup();
  setTimeout(() => {
    window.location.reload();
  }, 100); // need to wait a little bit in order to see the changes
}

function hidePopup() {
  const popup = document.getElementById('extension-popup-form');
  popup && document.body.removeChild(popup);
}

Jetzt ist es an der Zeit, sicherzustellen, dass das Formular im Browser angezeigt wird. Um den erforderlichen Kompilierungsschritt durchzuführen, habe ich Webpack wie folgt konfiguriert:

webpack.config.ts:

{
  "manifest_version": 3,
  "action": {
    "default_title": "Click to show the form"
  },
  "incognito": "split",
  "permissions": [
    "activeTab",
    "declarativeNetRequestWithHostAccess",
    "scripting",
    "storage",
    "tabs"
  ],
  "host_permissions": ["*://*/"], // get access to all URLs
  "background": {
    "service_worker": "background.js"
  },
  "content_scripts": [{
    "matches": ["<all_urls>"]
  }],
  "web_accessible_resources": [
    {
      "resources": ["blocked.html", "options.html", "about.html", "icons/*.svg"],
      "matches": ["<all_urls>"]
    }
  ],
  "content_security_policy": {
    "extension_pages": "script-src 'self'; object-src 'self'"
  },
}
</all_urls></all_urls>

Im Grunde nimmt es den Browsernamen aus der Umgebungsvariablen der Befehle, die ich ausführe, um zwischen zwei der Manifestdateien auszuwählen, und kompiliert den TypeScript-Code in das Verzeichnis dist/.

Ich wollte eigentlich richtige Tests für die Erweiterung schreiben, habe aber festgestellt, dass Puppeteer das Testen von Inhaltsskripten nicht unterstützt, sodass es unmöglich ist, die meisten Funktionen zu testen. Wenn Sie Problemumgehungen für das Testen von Inhaltsskripten kennen, würde ich sie gerne in den Kommentaren hören.

Meine Build-Befehle in package.json sind:

{
  "manifest_version": 2,
  "browser_action": {
    "default_title": "Click to show the form"
  },
  "permissions": [
    "activeTab",
    "declarativeNetRequest",
    "declarativeNetRequestWithHostAccess",
    "scripting", 
    "storage",
    "tabs",
    "*://*/"
  ],
  "background": {
    "scripts": [
      "background.js"
    ],
    "persistent": false
  },
  "content_scripts": [{
    "matches": ["<all_urls>"],
    "js": [
      "options.js",
      "blocked.js",
      "about.js"
    ]
  }],
  "web_accessible_resources": [
    "blocked.html",
    "options.html", 
    "icons/*.svg"
  ],
  "content_security_policy": "script-src 'self'; object-src 'self'",
}
</all_urls>

Also zum Beispiel, wenn ich laufe

dist/
node_modules/
src/
|-- background.tsc
|-- content.ts
static/
|-- manifest.chrome.json
|-- manifest.firefox.json
package.json
tsconfig.json
webpack.config.js

Die Dateien für Chrome werden im Verzeichnis dist/ kompiliert. Nach dem Auslösen eines Formulars auf einer beliebigen Website durch Klicken auf das Aktionssymbol oder Drücken der Verknüpfung sieht das Formular folgendermaßen aus:

Main form display

Umgang mit URL-Block

Da das Hauptformular nun fertig ist, besteht die nächste Aufgabe darin, es einzureichen. Um die Blockierungsfunktion zu implementieren, habe ich die declarativeNetRequest-API und dynamische Regeln genutzt. Die Regeln werden im Speicher der Erweiterung gespeichert. Das Bearbeiten dynamischer Regeln ist nur in der Service-Worker-Datei möglich. Um Daten zwischen dem Service-Worker und den Inhaltsskripten auszutauschen, sende ich zwischen ihnen Nachrichten mit den erforderlichen Daten. Da für diese Erweiterung eine ganze Reihe von Operationstypen erforderlich sind, habe ich für jede Aktion Typen erstellt. Hier ist ein Beispiel für einen Operationstyp:

types.ts:

import browser, { DeclarativeNetRequest } from 'webextension-polyfill';

// on icon click
const action = chrome.action ?? browser.browserAction; // Manifest v2 only has browserAction method
action.onClicked.addListener(tab => {
  triggerPopup(tab as browser.Tabs.Tab);
});

// on shortcut key press 
browser.commands.onCommand.addListener(command => {
  if (command === 'trigger_form') {
    browser.tabs.query({ active: true, currentWindow: true })
      .then((tabs) => {
        const tab = tabs[0];
        if (tab) {
          triggerPopup(tab);
        }
      })
      .catch(error => console.error(error));
  }
});

function triggerPopup(tab: browser.Tabs.Tab) {
  if (tab.id) {
    const tabId = tab.id;
    browser.scripting.insertCSS(({
      target: { tabId },
      files: ['global.css', './popup.css'],
    }))
      .then(() => {
        browser.scripting.executeScript
          ? browser.scripting.executeScript({
            target: { tabId },
            files: ['./content.js'], // refer to the compiled JS files, not the original TS ones 
          })
          : browser.tabs.executeScript({
            file: './content.js',
          });
      })
      .catch(error => console.error(error));
  }
}

Da es sinnvoll ist, neue URLs sowohl vom Hauptformular als auch von der Optionsseite aus hinzufügen zu können, wurde die Übermittlung durch eine wiederverwendbare Funktion in einer neuen Datei ausgeführt:

helpers.ts:

{
  "content_scripts": [{
    "js": ["browser-polyfill.js"]
  }],
}

Ich rufe handleFormSubmission() in content.ts auf, das die bereitgestellte URL validiert und sie dann an den Servicemitarbeiter sendet, um sie zur Blacklist hinzuzufügen.

Dynamische Regeln haben eine maximale Größe festgelegt, die berücksichtigt werden muss. Die Übergabe einer zu langen URL-Zeichenfolge führt zu unerwartetem Verhalten beim Versuch, die dynamische Regel dafür zu speichern. Ich habe herausgefunden, dass in meinem Fall eine 75 Zeichen lange URL eine gute maximale Länge für eine Regel war.

So wird der Servicemitarbeiter die empfangene Nachricht verarbeiten:

background.ts:

{
  "background": {
    "scripts": [
      "browser-polyfill.js",
      // other scripts
    ],
  },
  "content_scripts": [{
    "js": [
      "browser-polyfill.js",
      // other scripts
    ]
  }],
}

Für die Übermittlung erstelle ich ein neues Regelobjekt und aktualisiere die dynamischen Regeln, um es einzuschließen. Mit einem einfachen bedingten regulären Ausdruck kann ich zwischen dem Blockieren der gesamten Domain oder nur der angegebenen URL wählen.

Nach Abschluss sende ich die Antwortnachricht an das Inhaltsskript zurück. Das Interessanteste an diesem Snippet ist die Verwendung von Nanoid. Durch Versuch und Irrtum habe ich herausgefunden, dass es eine Grenze für die Anzahl dynamischer Regeln gibt – 5.000 für ältere Browser und 30.000 für neuere. Ich habe das durch einen Fehler herausgefunden, als ich versuchte, einer Regel eine ID zuzuweisen, die größer als 5000 war. Ich konnte kein Limit für meine IDs auf unter 4999 erstellen, also musste ich meine IDs auf dreistellige Zahlen beschränken ( 0-999, also insgesamt 1000 eindeutige IDs). Das bedeutete, dass ich die Gesamtzahl der Regeln für meine Erweiterung von 5000 auf 1000 reduziert habe, was einerseits ziemlich wichtig ist, andererseits aber auch die Wahrscheinlichkeit, dass ein Benutzer so viele URLs zum Blockieren hatte, ziemlich gering war, und so habe ich Ich habe beschlossen, mich mit dieser nicht ganz so eleganten Lösung zufrieden zu geben.

Jetzt kann der Benutzer neue URLs zur Blacklist hinzufügen und den Blocktyp auswählen, den er ihnen zuweisen möchte. Wenn er versucht, auf eine blockierte Ressource zuzugreifen, wird er auf eine Blockierungsseite weitergeleitet:

Block page

Es gibt jedoch einen Randfall, der angegangen werden muss. Die Erweiterung blockiert alle unerwünschten URLs, wenn der Benutzer direkt darauf zugreift. Wenn es sich bei der Website jedoch um eine SPA mit clientseitiger Umleitung handelt, fängt die Erweiterung die dort verbotenen URLs nicht ab. Um diesen Fall zu lösen, habe ich meine Datei „background.ts“ aktualisiert, um den aktuellen Tab abzuhören und zu sehen, ob sich die URL geändert hat. Wenn es passiert, überprüfe ich manuell, ob die URL auf der Blacklist steht, und wenn ja, leite ich den Benutzer um.

background.ts:

{
  "manifest_version": 3,
  "action": {
    "default_title": "Click to show the form"
  },
  "incognito": "split",
  "permissions": [
    "activeTab",
    "declarativeNetRequestWithHostAccess",
    "scripting",
    "storage",
    "tabs"
  ],
  "host_permissions": ["*://*/"], // get access to all URLs
  "background": {
    "service_worker": "background.js"
  },
  "content_scripts": [{
    "matches": ["<all_urls>"]
  }],
  "web_accessible_resources": [
    {
      "resources": ["blocked.html", "options.html", "about.html", "icons/*.svg"],
      "matches": ["<all_urls>"]
    }
  ],
  "content_security_policy": {
    "extension_pages": "script-src 'self'; object-src 'self'"
  },
}
</all_urls></all_urls>

getRules() ist eine Funktion, die die Methode declarativeNetRequest.getDynamicRules() verwendet, um die Liste aller dynamischen Regeln abzurufen, die ich in ein besser lesbares Format konvertiere.

Jetzt blockiert die Erweiterung korrekt URLs, auf die direkt und über SPAs zugegriffen wird.

Optionsseite erstellen

Die Optionsseite verfügt über eine einfache Benutzeroberfläche, wie unten gezeigt:

Options page

Dies ist die Seite mit den meisten Funktionen wie Bearbeiten, Löschen, Deaktivieren und Anwenden des strengen Modus. So habe ich es verkabelt.

Funktionalität zum Bearbeiten und Löschen

Das Bearbeiten war wahrscheinlich die komplexeste Aufgabe. Benutzer können eine URL bearbeiten, indem sie ihre Zeichenfolge ändern oder ihren Blockierungstyp ändern (die gesamte Domain oder nur eine bestimmte blockieren). Beim Bearbeiten sammle ich die IDs der bearbeiteten URLs in einem Array. Beim Speichern erstelle ich aktualisierte dynamische Regeln, die ich an den Servicemitarbeiter übergebe, um Änderungen anzuwenden. Nach jeder gespeicherten Änderung oder jedem Neuladen rufe ich die dynamischen Regeln erneut ab und rendere sie in der Tabelle. Unten ist die vereinfachte Version davon:

options.ts:

{
  "manifest_version": 3,
  "action": {
    "default_title": "Click to show the form"
  },
  "incognito": "split",
  "permissions": [
    "activeTab",
    "declarativeNetRequestWithHostAccess",
    "scripting",
    "storage",
    "tabs"
  ],
  "host_permissions": ["*://*/"], // get access to all URLs
  "background": {
    "service_worker": "background.js"
  },
  "content_scripts": [{
    "matches": ["<all_urls>"]
  }],
  "web_accessible_resources": [
    {
      "resources": ["blocked.html", "options.html", "about.html", "icons/*.svg"],
      "matches": ["<all_urls>"]
    }
  ],
  "content_security_policy": {
    "extension_pages": "script-src 'self'; object-src 'self'"
  },
}
</all_urls></all_urls>

Ich entscheide, ob eine bestimmte Regel blockiert oder zugelassen wird, indem ich einfach ihre isActive-Eigenschaft bedingt überprüfe. Die Regeln aktualisieren und die Regeln abrufen – das sind zwei weitere Vorgänge, die ich zu meinem Hintergrund-Listener hinzufügen kann:

background.ts:

{
  "manifest_version": 2,
  "browser_action": {
    "default_title": "Click to show the form"
  },
  "permissions": [
    "activeTab",
    "declarativeNetRequest",
    "declarativeNetRequestWithHostAccess",
    "scripting", 
    "storage",
    "tabs",
    "*://*/"
  ],
  "background": {
    "scripts": [
      "background.js"
    ],
    "persistent": false
  },
  "content_scripts": [{
    "matches": ["<all_urls>"],
    "js": [
      "options.js",
      "blocked.js",
      "about.js"
    ]
  }],
  "web_accessible_resources": [
    "blocked.html",
    "options.html", 
    "icons/*.svg"
  ],
  "content_security_policy": "script-src 'self'; object-src 'self'",
}
</all_urls>

Es war etwas schwierig, die Aktualisierungsfunktion richtig hinzubekommen, da es einen Grenzfall gibt, wenn eine bearbeitete URL zu einem Duplikat einer vorhandenen Regel wird. Ansonsten ist es das gleiche Spiel – aktualisieren Sie die dynamischen Regeln und senden Sie nach Abschluss die entsprechende Nachricht.

URLs zu löschen war wahrscheinlich die einfachste Aufgabe. In dieser Erweiterung gibt es zwei Arten des Löschens: das Löschen einer bestimmten Regel und das Löschen aller Regeln.

options.ts:

dist/
node_modules/
src/
|-- background.tsc
|-- content.ts
static/
|-- manifest.chrome.json
|-- manifest.firefox.json
package.json
tsconfig.json
webpack.config.js

Und wie zuvor habe ich dem Service-Worker-Listener zwei weitere Aktionen hinzugefügt:

background.ts:

import browser, { DeclarativeNetRequest } from 'webextension-polyfill';

// on icon click
const action = chrome.action ?? browser.browserAction; // Manifest v2 only has browserAction method
action.onClicked.addListener(tab => {
  triggerPopup(tab as browser.Tabs.Tab);
});

// on shortcut key press 
browser.commands.onCommand.addListener(command => {
  if (command === 'trigger_form') {
    browser.tabs.query({ active: true, currentWindow: true })
      .then((tabs) => {
        const tab = tabs[0];
        if (tab) {
          triggerPopup(tab);
        }
      })
      .catch(error => console.error(error));
  }
});

function triggerPopup(tab: browser.Tabs.Tab) {
  if (tab.id) {
    const tabId = tab.id;
    browser.scripting.insertCSS(({
      target: { tabId },
      files: ['global.css', './popup.css'],
    }))
      .then(() => {
        browser.scripting.executeScript
          ? browser.scripting.executeScript({
            target: { tabId },
            files: ['./content.js'], // refer to the compiled JS files, not the original TS ones 
          })
          : browser.tabs.executeScript({
            file: './content.js',
          });
      })
      .catch(error => console.error(error));
  }
}

Implementierung des strikten Modus

Wahrscheinlich ist das Hauptmerkmal der Erweiterung die Möglichkeit, die Blockierung deaktivierter (Zugriffserlaubter) Regeln automatisch für Personen zu erzwingen, die eine strengere Kontrolle über ihre Surfgewohnheiten benötigen. Die Idee dahinter ist, dass bei Deaktivierung des strikten Modus jede vom Benutzer deaktivierte URL deaktiviert bleibt, bis der Benutzer sie ändert. Wenn der strikte Modus aktiviert ist, werden alle deaktivierten Regeln nach einer Stunde automatisch wieder aktiviert. Um eine solche Funktion zu implementieren, habe ich den lokalen Speicher der Erweiterung verwendet, um ein Array von Objekten zu speichern, die jede deaktivierte Regel darstellen. Jedes Objekt enthält eine Regel-ID, ein Entsperrdatum und die URL selbst. Jedes Mal, wenn ein Benutzer auf eine neue Ressource zugreift oder die Blacklist aktualisiert, überprüft die Erweiterung zunächst den Speicher auf abgelaufene Regeln und aktualisiert diese entsprechend.

options.ts:

{
  "content_scripts": [{
    "js": ["browser-polyfill.js"]
  }],
}

isStrictModeOn boolean wird ebenfalls im Speicher gespeichert. Wenn es wahr ist, durchlaufe ich alle Regeln und füge dem Speicher diejenigen hinzu, die deaktiviert sind, mit einer neu erstellten Entsperrungszeit für sie. Dann überprüfe ich bei jeder Antwort den Speicher auf deaktivierte Regeln, entferne die abgelaufenen Regeln, falls vorhanden, und aktualisiere sie:

background.ts:

{
  "background": {
    "scripts": [
      "browser-polyfill.js",
      // other scripts
    ],
  },
  "content_scripts": [{
    "js": [
      "browser-polyfill.js",
      // other scripts
    ]
  }],
}

Damit ist die Website-Blockierungserweiterung abgeschlossen. Benutzer können beliebige URLs hinzufügen, bearbeiten, löschen und deaktivieren, teilweise oder ganze Domänensperren anwenden und den strikten Modus verwenden, um mehr Disziplin beim Surfen aufrechtzuerhalten.

Extension work example


Abschluss

Das ist der grundlegende Überblick über meine Erweiterung zum Blockieren von Websites. Es ist meine erste Erweiterung und es war eine interessante Erfahrung, insbesondere angesichts der Tatsache, dass die Welt der Webentwicklung manchmal alltäglich werden kann. Es gibt definitiv Raum für Verbesserungen und neue Funktionen. Suchleiste für URLs in der Blacklist, das Hinzufügen geeigneter Tests, die benutzerdefinierte Zeitdauer für den strikten Modus, die gleichzeitige Übermittlung mehrerer URLs – das sind nur einige Dinge, die mir durch den Kopf gehen und die ich eines Tages gerne zu diesem Projekt hinzufügen würde. Ich hatte ursprünglich auch geplant, die Erweiterung plattformübergreifend zu machen, konnte sie aber nicht auf meinem Telefon ausführen.
Wenn Ihnen die Lektüre dieser exemplarischen Vorgehensweise gefallen hat, Sie etwas Neues gelernt haben oder Sie sonstiges Feedback haben, freuen wir uns über Ihre Kommentare. Vielen Dank fürs Lesen.

Der Quellcode
Die Live-Version

Das obige ist der detaillierte Inhalt vonBrowserübergreifende Erweiterung zum Blockieren von Websites. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Python vs. JavaScript: Welche Sprache sollten Sie lernen?Python vs. JavaScript: Welche Sprache sollten Sie lernen?May 03, 2025 am 12:10 AM

Die Auswahl von Python oder JavaScript sollte auf Karriereentwicklung, Lernkurve und Ökosystem beruhen: 1) Karriereentwicklung: Python ist für die Entwicklung von Datenwissenschaften und Back-End-Entwicklung geeignet, während JavaScript für die Entwicklung von Front-End- und Full-Stack-Entwicklung geeignet ist. 2) Lernkurve: Die Python -Syntax ist prägnant und für Anfänger geeignet; Die JavaScript -Syntax ist flexibel. 3) Ökosystem: Python hat reichhaltige wissenschaftliche Computerbibliotheken und JavaScript hat ein leistungsstarkes Front-End-Framework.

JavaScript -Frameworks: Stromversorgung moderner WebentwicklungJavaScript -Frameworks: Stromversorgung moderner WebentwicklungMay 02, 2025 am 12:04 AM

Die Kraft des JavaScript -Frameworks liegt in der Vereinfachung der Entwicklung, der Verbesserung der Benutzererfahrung und der Anwendungsleistung. Betrachten Sie bei der Auswahl eines Frameworks: 1. Projektgröße und Komplexität, 2. Teamerfahrung, 3. Ökosystem und Community -Unterstützung.

Die Beziehung zwischen JavaScript, C und BrowsernDie Beziehung zwischen JavaScript, C und BrowsernMay 01, 2025 am 12:06 AM

Einführung Ich weiß, dass Sie es vielleicht seltsam finden. Was genau muss JavaScript, C und Browser tun? Sie scheinen nicht miteinander verbunden zu sein, aber tatsächlich spielen sie eine sehr wichtige Rolle in der modernen Webentwicklung. Heute werden wir die enge Verbindung zwischen diesen drei diskutieren. In diesem Artikel erfahren Sie, wie JavaScript im Browser ausgeführt wird, die Rolle von C in der Browser -Engine und wie sie zusammenarbeiten, um das Rendern und die Interaktion von Webseiten voranzutreiben. Wir alle kennen die Beziehung zwischen JavaScript und Browser. JavaScript ist die Kernsprache der Front-End-Entwicklung. Es läuft direkt im Browser und macht Webseiten lebhaft und interessant. Haben Sie sich jemals gefragt, warum Javascr

Node.js Streams mit TypeScriptNode.js Streams mit TypeScriptApr 30, 2025 am 08:22 AM

Node.js zeichnet sich bei effizienten E/A aus, vor allem bei Streams. Streams verarbeiten Daten inkrementell und vermeiden Speicherüberladung-ideal für große Dateien, Netzwerkaufgaben und Echtzeitanwendungen. Die Kombination von Streams mit der TypeScript -Sicherheit erzeugt eine POWE

Python vs. JavaScript: Leistung und EffizienzüberlegungenPython vs. JavaScript: Leistung und EffizienzüberlegungenApr 30, 2025 am 12:08 AM

Die Unterschiede in der Leistung und der Effizienz zwischen Python und JavaScript spiegeln sich hauptsächlich in: 1 wider: 1) Als interpretierter Sprache läuft Python langsam, weist jedoch eine hohe Entwicklungseffizienz auf und ist für eine schnelle Prototypentwicklung geeignet. 2) JavaScript ist auf einen einzelnen Thread im Browser beschränkt, aber Multi-Threading- und Asynchronen-E/A können verwendet werden, um die Leistung in Node.js zu verbessern, und beide haben Vorteile in tatsächlichen Projekten.

Die Ursprünge von JavaScript: Erforschung seiner ImplementierungsspracheDie Ursprünge von JavaScript: Erforschung seiner ImplementierungsspracheApr 29, 2025 am 12:51 AM

JavaScript stammt aus dem Jahr 1995 und wurde von Brandon Ike erstellt und realisierte die Sprache in C. 1.C-Sprache bietet Programmierfunktionen auf hoher Leistung und Systemebene für JavaScript. 2. Die Speicherverwaltung und die Leistungsoptimierung von JavaScript basieren auf C -Sprache. 3. Die plattformübergreifende Funktion der C-Sprache hilft JavaScript, auf verschiedenen Betriebssystemen effizient zu laufen.

Hinter den Kulissen: Welche Sprache macht JavaScript?Hinter den Kulissen: Welche Sprache macht JavaScript?Apr 28, 2025 am 12:01 AM

JavaScript wird in Browsern und Node.js -Umgebungen ausgeführt und stützt sich auf die JavaScript -Engine, um Code zu analysieren und auszuführen. 1) abstrakter Syntaxbaum (AST) in der Parsenstufe erzeugen; 2) AST in die Kompilierungsphase in Bytecode oder Maschinencode umwandeln; 3) Führen Sie den kompilierten Code in der Ausführungsstufe aus.

Die Zukunft von Python und JavaScript: Trends und VorhersagenDie Zukunft von Python und JavaScript: Trends und VorhersagenApr 27, 2025 am 12:21 AM

Zu den zukünftigen Trends von Python und JavaScript gehören: 1. Python wird seine Position in den Bereichen wissenschaftlicher Computer und KI konsolidieren. JavaScript wird die Entwicklung der Web-Technologie fördern. Beide werden die Anwendungsszenarien in ihren jeweiligen Bereichen weiter erweitern und mehr Durchbrüche in der Leistung erzielen.

See all articles

Heiße KI -Werkzeuge

Undresser.AI Undress

Undresser.AI Undress

KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover

AI Clothes Remover

Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool

Undress AI Tool

Ausziehbilder kostenlos

Clothoff.io

Clothoff.io

KI-Kleiderentferner

Video Face Swap

Video Face Swap

Tauschen Sie Gesichter in jedem Video mühelos mit unserem völlig kostenlosen KI-Gesichtstausch-Tool aus!

Heiße Werkzeuge

Dreamweaver Mac

Dreamweaver Mac

Visuelle Webentwicklungstools

MinGW – Minimalistisches GNU für Windows

MinGW – Minimalistisches GNU für Windows

Dieses Projekt wird derzeit auf osdn.net/projects/mingw migriert. Sie können uns dort weiterhin folgen. MinGW: Eine native Windows-Portierung der GNU Compiler Collection (GCC), frei verteilbare Importbibliotheken und Header-Dateien zum Erstellen nativer Windows-Anwendungen, einschließlich Erweiterungen der MSVC-Laufzeit zur Unterstützung der C99-Funktionalität. Die gesamte MinGW-Software kann auf 64-Bit-Windows-Plattformen ausgeführt werden.

PHPStorm Mac-Version

PHPStorm Mac-Version

Das neueste (2018.2.1) professionelle, integrierte PHP-Entwicklungstool

SublimeText3 Englische Version

SublimeText3 Englische Version

Empfohlen: Win-Version, unterstützt Code-Eingabeaufforderungen!

SAP NetWeaver Server-Adapter für Eclipse

SAP NetWeaver Server-Adapter für Eclipse

Integrieren Sie Eclipse mit dem SAP NetWeaver-Anwendungsserver.