Travailleur de service persistant dans l'extension Chrome
P粉323224129
P粉323224129 2023-08-24 18:56:27
0
2
585
<p>Je dois définir mon Service Worker comme persistant dans une extension Chrome, car j'utilise l'API webRequest pour intercepter certaines données transmises dans le formulaire pour une requête spécifique, mais je ne sais pas comment faire cela. J'ai tout essayé mais mon Service Worker continue de se désinstaller. </p> <p>Comment puis-je continuer à charger et attendre que la requête soit interceptée ? </p>
P粉323224129
P粉323224129

répondre à tous(2)
P粉034571623

Contrairement à l'API chrome.webRequest, l'API chrome.webNavigation fonctionne parfaitement car l'API chrome.webNavigation peut réveiller Service Worker, vous pouvez maintenant essayer de mettre l'API chrome.webRequest dans dans Chrome. navigation Web.

chrome.webNavigation.onBeforeNavigate.addListener(function(){

   chrome.webRequest.onResponseStarted.addListener(function(details){

      //.............
      
      //.............

   },{urls: ["*://domain/*"],types: ["main_frame"]});


},{
    url: [{hostContains:"domain"}]
});
P粉386318086

Table des matières

  • Description du problème

  • Solution :

    • Exploiter
    离屏API
    nativeMessagingAPI
    WebSocketAPI
    chromeAPI de messagerie
    • Onglet dédié

  • Attention

Par définition, un Service Worker (SW) ne peut pas être persistant et le navigateur doit mettre fin de force à toutes ses activités/demandes après un certain temps, qui dans Chrome est de 5 minutes. Le minuteur d'inactivité (c'est-à-dire lorsqu'aucune activité de ce type n'est en cours) est encore plus court : 30 secondes.

L'équipe

Chromium considère actuellement ce comportement comme bon (l'équipe a occasionnellement assoupli certains aspects, par exemple Chrome 114 a étendu le port chrome.runtime après chaque message), mais cela ne s'applique qu'aux extensions qui observent des événements peu fréquents, ces événements sont ne s'exécute que quelques fois par jour, réduisant ainsi l'empreinte mémoire du navigateur entre les exécutions (par exemple, événements webRequest/webNavigation avec url > filtrage des sites rarement visités). Ces extensions peuvent être repensées pour conserver l'état, Exemple. Malheureusement, une telle idylle est souvent intenable.

Problèmes connus

  • Problème 1 : Chrome 106 et versions antérieures ne réveillent pas le logiciel pour les événements webRequest. p>

    Bien que vous puissiez essayer de vous abonner à une API comme chrome.webNavigation comme indiqué dans d'autres réponses, cela n'est utile que pour les événements qui se produisent après le démarrage du thread de travail.

  • Problème 2 : Le personnel arrête de se réveiller de manière aléatoire à cause d'événements.

    La solutionpeut être d'appeler chrome.runtime.reload().

  • Problème 3 : Chrome 109 et versions antérieures ne peuvent pas prolonger le cycle de vie logiciel des nouveaux événements API chrome à partir de scripts en arrière-plan déjà en cours d'exécution. Cela signifie que votre code ne pourra pas exécuter quoi que ce soit de manière fiable et asynchrone lorsqu'un événement se produit dans les dernières millisecondes du délai d'inactivité de 30 secondes. Cela signifie que les utilisateurs penseront que votre extension n'est pas fiable.

  • Question 4 : Les performances seront pires que celles de MV2 si l'extension maintient des connexions ou des états (variables) à distance qui prennent beaucoup de temps à se reconstruire, ou si vous observez des événements fréquents comme les suivants :

    • chrome.tabs.onUpdated/onActivated,
    • chrome.webNavigation si le périmètre ne se limite pas aux URL rares,
    • chrome.webRequest si la portée ne se limite pas à des URL ou types rares,
    • Messages chrome.runtime.onMessage/onConnect pour les scripts de contenu dans tous les onglets.

    Lancer SW pour un nouvel événement revient essentiellement à ouvrir un nouvel onglet. Il faut environ 50 ms pour créer l'environnement, peut-être 100 ms (ou même 1 000 ms, selon la quantité de code) pour exécuter l'intégralité du script logiciel, et peut-être 1 ms (ou 1 000 ms, selon les données) pour lire le état du stockage et le reconstruire/hydrater Complexité). Même avec un script presque vide, il faut au moins 50 millisecondes, ce qui représente une surcharge considérable pour appeler un écouteur d'événement, qui ne prend que 1 milliseconde.

    SW peut être redémarré des centaines de fois par jour car de tels événements sont générés en réponse aux actions de l'utilisateur avec des écarts naturels, comme cliquer sur un onglet puis écrire quelque chose, au cours duquel le logiciel est arrêté et les nouveaux événements redémarrent, consommant du CPU, du disque. , la batterie, et introduisant généralement des retards perceptibles fréquents dans la réponse de mise à l'échelle.

Exploiter les service Workers « persistants » via des erreurs

Chrome 110 a introduit un bug : l'appel d'une API chrome asynchrone entraînera l'exécution du thread de travail pendant 30 secondes supplémentaires. Ce bug n'a pas encore été corrigé.

//background.js

const keepAlive = () => setInterval(chrome.runtime.getPlatformInfo, 20e3);
chrome.runtime.onStartup.addListener(keepAlive);
keepAlive();

Service Workers « persistants » avec API hors écran

Contributé par Kevin Augusto.

Dans Chrome 109 et versions ultérieures, vous pouvez utiliser l'API hors écran pour créer un document hors écran et envoyer des messages à partir de celui-ci toutes les 30 secondes ou moins pour que Service Worker continue de fonctionner. Actuellement, la durée de vie du document n'est pas limitée (seule la lecture audio est restreinte, que nous n'utilisons pas), mais cela pourrait changer à l'avenir.

  • manifest.json

      "permissions": ["offscreen"]
    
  • offscreen.html

    <script src="offscreen.js"></script>
    
  • offscreen.js

    setInterval(async () => {
      (await navigator.serviceWorker.ready).active.postMessage('keepAlive');
    }, 20e3);
    
  • background.js

    async function createOffscreen() {
      await chrome.offscreen.createDocument({
        url: 'offscreen.html',
        reasons: ['BLOBS'],
        justification: 'keep service worker running',
      }).catch(() => {});
    }
    chrome.runtime.onStartup.addListener(createOffscreen);
    self.onmessage = e => {}; // keepAlive
    createOffscreen();
    

Connect nativeMessaging Fil de travail de service "persistant" au moment de l'hôte

Dans Chrome 105 et supérieur, transmettez simplement chrome.runtime.connectNative. Si le processus hôte se termine en raison d'un crash ou d'une action de l'utilisateur, le port sera fermé et le logiciel se terminera comme d'habitude. Vous pouvez l'empêcher d'appeler à nouveau chrome.runtime.connectNative en écoutant l'événement onDisconnect du port.

Fil de service "persistant" pendant que WebSocket est actif

Chrome 116 et versions ultérieures : échangez des messages WebSocket toutes les 30 secondes pour le maintenir en vie, par exemple une fois toutes les 25 secondes.

Service Worker "Persistant" tant que l'onglet connectable existe

Inconvénients :

  • Nécessite un onglet Web ouvert
  • Autorisations d'hébergement étendues pour les scripts de contenu (comme *://*/*), qui placeront la plupart des extensions dans la lente file d'attente de révision des boutiques en ligne
  • .

Attention ! Si vous disposez d'un port connecté, n'utilisez pas cette solution de contournement, utilisez une autre solution de contournement pour le port ci-dessous.

Attention ! Si vous utilisez sendMessage, vous pouvez également implémenter une solution de contournement pour sendMessage (ci-dessous).

  • manifest.json, parties pertinentes :

      "permissions": ["scripting"],
      "host_permissions": ["<all_urls>"],
      "background": {"service_worker": "bg.js"}
    
    
  • Travailleur du service d'arrière-plan bg.js :

    const onUpdate = (tabId, info, tab) => /^https?:/.test(info.url) && findTab([tab]);
    findTab();
    chrome.runtime.onConnect.addListener(port => {
      if (port.name === 'keepAlive') {
        setTimeout(() => port.disconnect(), 250e3);
        port.onDisconnect.addListener(() => findTab());
      }
    });
    async function findTab(tabs) {
      if (chrome.runtime.lastError) { /* tab was closed before setTimeout ran */ }
      for (const {id: tabId} of tabs || await chrome.tabs.query({url: '*://*/*'})) {
        try {
          await chrome.scripting.executeScript({target: {tabId}, func: connect});
          chrome.tabs.onUpdated.removeListener(onUpdate);
          return;
        } catch (e) {}
      }
      chrome.tabs.onUpdated.addListener(onUpdate);
    }
    function connect() {
      chrome.runtime.connect({name: 'keepAlive'})
        .onDisconnect.addListener(connect);
    }
    
  • Toutes les autres pages d'extension comme les popups ou les options :

    ;(function connect() {
      chrome.runtime.connect({name: 'keepAlive'})
        .onDisconnect.addListener(connect);
    })();
    

Si vous utilisez également sendMessage

Dans Chrome 99-101, vous devez toujours appeler sendResponse() dans l'écouteur chrome.runtime.onMessage même si une réponse n'est pas requise. C'est un bug dans MV3. Assurez-vous également de le faire dans les 5 minutes, sinon appelez sendResponse immédiatement et envoyez un nouveau message via chrome.tabs.sendMessage (vers l'onglet) ou chrome.runtime.sendMessage (vers la fenêtre contextuelle) lorsque le travail est terminé.

Si vous utilisez déjà un port tel que chrome.runtime.connect

Attention ! Si vous connectez également plus de ports au service worker, vous devrez reconnecter chaque port avant que 5 minutes ne s'écoulent, par exemple dans les 295 secondes. Cela était critique dans les versions de Chrome antérieures à 104, qui tueraient le logiciel quel que soit le nombre de ports de connexion supplémentaires. Dans Chrome 104 et supérieur, ce bug est corrigé, mais vous devez quand même les reconnecter car leur cycle de vie de 5 minutes n'a pas changé, la solution la plus simple est donc de les reconnecter de la même manière dans toutes les versions de Chrome Connection : par exemple toutes les 295 secondes. .

  • Exemple de script backend :

    chrome.runtime.onConnect.addListener(port => {
      if (port.name !== 'foo') return;
      port.onMessage.addListener(onMessage);
      port.onDisconnect.addListener(deleteTimer);
      port._timer = setTimeout(forceReconnect, 250e3, port);
    });
    function onMessage(msg, port) {
      console.log('received', msg, 'from', port.sender);
    }
    function forceReconnect(port) {
      deleteTimer(port);
      port.disconnect();
    }
    function deleteTimer(port) {
      if (port._timer) {
        clearTimeout(port._timer);
        delete port._timer;
      }
    }
  • Exemples de scripts clients, tels que les scripts de contenu :

    let port;
    function connect() {
      port = chrome.runtime.connect({name: 'foo'});
      port.onDisconnect.addListener(connect);
      port.onMessage.addListener(msg => {
        console.log('received', msg, 'from bg');
      });
    }
    connect();
    

"Toujours", via onglet dédié, lorsque l'onglet est ouvert

Au lieu d'utiliser le logiciel, ouvrez un nouvel onglet avec la page d'extension à l'intérieur, cette page agira donc comme une "page d'arrière-plan visible", c'est-à-dire que la seule chose que le logiciel doit faire est d'ouvrir cet onglet. Vous pouvez également l'ouvrir à partir de la fenêtre contextuelle d'action.

chrome.tabs.create({url: 'bg.html'})

Elle aura la même fonctionnalité que la page d'arrière-plan persistante de ManifestV2, mais a) elle sera visible et b) ne sera pas accessible via chrome.extension.getBackgroundPage (peut être remplacée par chrome.extension.getViews).

Inconvénients :

  • consomme plus de mémoire,
  • Perdre de l'espace dans la barre d'onglets,
  • Distrayez l'attention de l'utilisateur,
  • Lorsque plusieurs extensions ouvrent un onglet comme celui-ci, les inconvénients font boule de neige et deviennent un véritable PITA.

Vous pouvez faciliter la tâche des utilisateurs en ajoutant des informations/journaux/graphiques/tableau de bord à la page, et également ajouter un beforeunloadécouteur pour éviter que l'onglet ne soit fermé accidentellement. p>

Avertissement concernant la persistance

Vous devez toujours enregistrer/restaurer l'état (variables) car il n'existe pas de service worker persistant et ces solutions de contournement ont des limitations comme mentionné ci-dessus afin que le travailleur puisse être résilié. Vous pouvez conserver l'état en stockage, Exemple.

Veuillez noter que vous ne devez pas rendre vos threads de travail persistants uniquement pour simplifier la gestion des états/variables. Faites cela uniquement pour restaurer les performances qui sont détériorées par le redémarrage des threads de travail, au cas où la reconstruction de votre état serait très coûteuse ou si vous êtes accro aux événements fréquents répertoriés au début de cette réponse.

Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!