mv3 migration of background page

519 views
Skip to first unread message

Tazim mahta

unread,
Feb 15, 2023, 5:29:15 AM2/15/23
to Chromium Extensions
"importScripts() of new scripts after service worker installation is not allowed"

Hi all,
I was trying to migrate from  v2 to v3. So far I tried but didn't find any clue to migrate the background page. 
here is the v2 code-
// manifest.json
    "background": {
        "page": "background.html"
    },

// backgrund.html
<html>
<head>
    <title></title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <script type="text/javascript" src="js/jquery.js?1"></script>
    <script type="text/javascript" src="js/sha1.js?1"></script>
    <script type="text/javascript" src="js/background.js?1"></script>
</head>
<body>
</body>
</html>



In mv3, I tried this
//manifest.json
  "background": {
    "service_worker": "worker.js"
  },

// worker.js
try {
  importScripts("js/jquery.js");
  importScripts("js/sha1.js");
  importScripts("js/background.js");
} catch (e) {
  console.log(e);
}
but it shows this in the background console -
"importScripts() of new scripts after service worker installation is not allowed."

and in background.js , I tried something like - console.log("this is from bg.js").But it does not appear in the service worker console. Is this a problem? How these can be solved?

Oliver Dunk

unread,
Feb 15, 2023, 5:38:57 AM2/15/23
to Tazim mahta, Chromium Extensions
Hi Tazim,

Have you checked to see if this works when the jquery and sha1 import is removed? From what I recall, jQuery relies on some DOM APIs which won't be available in a service worker. As a result, it may be failing to load and preventing the subsequent imports. I'm not sure what the sha1 library does behind the scenes but it could be doing something similar.

Assuming that helps, it would then be interesting to know what you're using jQuery for as there may be an MV3 alternative.
Oliver Dunk | DevRel, Chrome Extensions | https://developer.chrome.com/ | London, GB


--
You received this message because you are subscribed to the Google Groups "Chromium Extensions" group.
To unsubscribe from this group and stop receiving emails from it, send an email to chromium-extens...@chromium.org.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/chromium-extensions/f3e6396f-2d40-4909-954a-53605b1b45acn%40chromium.org.

wOxxOm

unread,
Feb 15, 2023, 9:19:22 AM2/15/23
to Chromium Extensions, Oliver Dunk, Chromium Extensions, Tazim mahta
Indeed, you can't use jQuery in a worker or in a service worker. You can do it inside the offscreen document. Maybe you don't need jQuery at all if you only used jQuery.ajax which can be replaced by fetch() directly in the service worker (well, unless you need the upload progress callback, in which case the offscreen document is the only way).

As for the error, it means that importScripts() was called after the first turn of the JS event loop (when Chrome automatically registers the service worker), but that can't physically happen with the code you've shown, unless there's a new bug in Chrome, so it's probably an old error which you can remove by clicking the "Clear all" button. In case you do it intentionally, there's a trick to it: you must duplicate the call in the oninstall event, see this example.

Kabir Singh

unread,
May 12, 2024, 10:08:57 AMMay 12
to Chromium Extensions, wOxxOm, Oliver Dunk, Chromium Extensions
I tried using fetch instead of jQuery , but I was getting error code 405...upon leveraging offscreen api , it still fails to recognize my jquery and throws 
Uncaught (in promise) ReferenceError: $ is not defined
, I have imported all the files in my popup.html in the right order such that jQuery is accessible by the time things come to offscreen html..dk what I am doing wrong 


wOxxOm

unread,
May 12, 2024, 4:27:27 PMMay 12
to Chromium Extensions, Kabir Singh, wOxxOm, Oliver Dunk, Chromium Extensions
Show your code or upload the extension.

Kabir Singh

unread,
May 13, 2024, 3:52:37 AMMay 13
to Chromium Extensions, wOxxOm, Kabir Singh, Oliver Dunk, Chromium Extensions
Sure.This is the code wrt to offscreen api.
sw.js let creating; // A global promise to avoid concurrency issues
  async function setupOffscreenDocument(path) {
    // Check all windows controlled by the service worker to see if one
    // of them is the offscreen document with the given path
    const offscreenUrl = chrome.runtime.getURL(path);
    const existingContexts = await chrome.runtime.getContexts({
      contextTypes: ['OFFSCREEN_DOCUMENT'],
      documentUrls: [offscreenUrl]
    });
 
    if (existingContexts.length > 0) {
      return;
    }
 
    // create offscreen document
    if (creating) {
      await creating;
    } else {
      creating = chrome.offscreen.createDocument({
        url: path,
        reasons: ['CLIPBOARD'],
        justification: 'testing whether offscreen API would support jQuery in order to execute getSignLanguagesWithVideos() function',
      });
      await creating;
      creating = null;
    }
  }

  async function getSignLanguagesWithVideos(){
      await setupOffscreenDocument('./offscreen.html');
   
      // Send message to offscreen document
      const signLanguages = await chrome.runtime.sendMessage({
        type: 'getSignLanguagesWithVideos',
        target: 'offscreen',
        data:{sparqlEndpointLinguaLibreURL:sparqlEndpoints.lingualibre.url,sparqlSignLanguagesQuery},
      });

      // closing the offscreen document in order to make sure one offscreen page is open at a time
      await chrome.offscreen.closeDocument();
      return signLanguages;
  }
Query looks something like this :  sparqlSignLanguagesQuery:'SELECT ?id ?idLabel WHERE { ?id prop:P2 entity:Q4 . ?id prop:P24 entity:Q88890 . SERVICE wikibase:label { bd:serviceParam wikibase:language "fr,en". } }';
Endpoint look something like this : sparqlEndpointLinguaLibreURL :  https://lingualibre.org/bigdata/namespace/wdq/sparql
offscreen.js chrome.runtime.onMessage.addListener(handleMessages);

async function getSignLanguagesWithVideos(endpointURL,query) {
  var i,
    signLanguage,
    signLanguages = [], // ?? already define in global scopte
    response = await $.post(endpointURL, {
      format: "json",
      query,
    });
  for (i = 0; i < response.results.bindings.length; i++) {
    var signLanguageRaw = response.results.bindings[i];
    console.log("#149", signLanguageRaw);
    signLanguage = {
      wdQid: signLanguageRaw.id.value.split("/").pop(),
      labelNative: signLanguageRaw.idLabel.value,
    };
    signLanguages[i] = signLanguage;
  }
  // create signLanguages objects
  // TEMPORARY, WHEN ONLY LSF HAS VIDEOS
  signLanguages = filterArrayBy(signLanguages, "wdQid", "Q99628");
  console.log(signLanguages);

  return signLanguages;
}

function handleMessages(message, sender, sendResponse) {
  // Return early if this message isn't meant for the offscreen document.
  if (message.target !== "offscreen") {
    return false;
  }

  if (message.type !== "getSignLanguagesWithVideos") {
    console.warn(`Unexpected message type received: '${message.type}'.`);
    return;
  }
 
  const {data} = message;
  getSignLanguagesWithVideos({...data}).then((signLanguages) => sendResponse(signLanguages));

  // we need to explictly return true in our chrome.runtime.onMessage handler
  // in order to allow the requestor to handle the request asynchronous.
  return true;
}
Using fetch API looks something like this :
async function getSignLanguagesWithVideos() {
    try {
      const response = await fetch(sparqlEndpoints.lingualibre.url, {
        method: "POST",
        body: JSON.stringify({
          format: "json",
          query: sparqlSignLanguagesQuery,
        }),
      });

      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }

      const data = await response.json(); // Await the parsed JSON response

      const signLanguages = [];
      for (let i = 0; i < data.results.bindings.length; i++) {
        const signLanguageRaw = data.results.bindings[i];
        const signLanguage = {
          wdQid: signLanguageRaw.id.value.split("/").pop(),
          labelNative: signLanguageRaw.idLabel.value,
        };
        signLanguages.push(signLanguage);
      }

      // Temporary filtering (assuming filterArrayBy is available)
      signLanguages = filterArrayBy(signLanguages, "wdQid", "Q99628");

      console.log(signLanguages);
      return signLanguages;
    } catch (error) {
      console.error("Error fetching or processing data:", error);
    }
  }

wOxxOm

unread,
May 13, 2024, 4:28:32 AMMay 13
to Chromium Extensions, Kabir Singh, wOxxOm, Oliver Dunk, Chromium Extensions
> it still fails to recognize my jquery and throws Uncaught (in promise) ReferenceError: $ is not defined

Assuming you placed the script in the extension directory and loaded it using the correct path in your html, you may be looking at an old error in chrome://extensions UI, in which case you need to click the nearby button to remove it and then retry. Prefer inspecting the errors directly in devtools for the relevant context. Every context has its own devtools which you can open either by right-clicking inside the page or in the details of the extension in chrome://extensions UI.

> async function getSignLanguagesWithVideos(endpointURL,query) {

This expects two params, but you use a single object in getSignLanguagesWithVideos({...data})

> Using fetch API looks something like this
> body: JSON.stringify({

You should inspect the requests in devtools and verify that the payloads are the same. Judging by the source code of jQuery it doesn't use JSON.stringify. BTW just in case, you don't need the offscreen document when using fetch(), but you may need to keep the service worker alive as shown in https://issues.chromium.org/issues/40283184#comment8

Kabir Singh

unread,
May 13, 2024, 4:59:51 AMMay 13
to Chromium Extensions, wOxxOm, Kabir Singh, Oliver Dunk, Chromium Extensions
I agree with you and have been doing the same....I inspect the errors of the relevant context in their personal dev tools , be it service-worker or offscreen.html or popup.html. Regarding the data object inside message , I have spread it , wont it be equivalent to passing all the arguments individually, separated by commas ? 
Regarding fetch API : Indeed I wont need offscreen API in order to execute this post request , it was just a sample snippet of the same jQuery post request....although I would look into JSON.stringify

wOxxOm

unread,
May 13, 2024, 5:12:09 AMMay 13
to Chromium Extensions, Kabir Singh, wOxxOm, Oliver Dunk, Chromium Extensions
> I inspect the errors of the relevant context in their personal dev tools

The only explanation for "$ is not defined" is that <script src=jquery.js></script> is incorrect or the path is incorrect or it's loaded after it's used in another script.
Use the built-in debugger to set a breakpoint at that line, re-run the code, and look at the state of DOM when the breakpoint triggers.


> I have spread it , wont it be equivalent to passing all the arguments individually, separated by commas

I don't understand. I meant Devtools -> Network -> request -> Payload -> view source. You'll have to either a) use an external traffic inspection tool or b ) remove chrome.offscreen.closeDocument, reload the extension, run it to create an offscreen document, open devtools for the offscreen document, re-run the code, inspect the request.

wOxxOm

unread,
May 13, 2024, 5:14:24 AMMay 13
to Chromium Extensions, wOxxOm, Kabir Singh, Oliver Dunk, Chromium Extensions
> > I have spread it , wont it be equivalent to passing all the arguments individually, separated by commas

I understand now, you meant object spreading, but you are wrong, you're just making a copy of the object. To spread as individual parameters you need to pass an array e.g. data: ['foo', 'bar'] and then call yourFunc(...msg.data).

Kabir Singh

unread,
May 13, 2024, 5:21:31 AMMay 13
to Chromium Extensions, wOxxOm, Kabir Singh, Oliver Dunk, Chromium Extensions
Oh I see....what if I do something like 
  const {data} = message;
  const {endpointURL,query} = data;
  getSignLanguagesWithVideos(endpointURL,query).then((signLanguages) => sendResponse(signLanguages));
It'd be ok then right ?

wOxxOm

unread,
May 13, 2024, 5:22:23 AMMay 13
to Chromium Extensions, Kabir Singh, wOxxOm, Oliver Dunk, Chromium Extensions
Sure.

Kabir Singh

unread,
May 13, 2024, 5:23:55 AMMay 13
to Chromium Extensions, wOxxOm, Kabir Singh, Oliver Dunk, Chromium Extensions
Paths are correct inside my popup.html , the jquery files are uploaded prior to the offscreen page 
popup.html <script src="../lib/jquery.min.js"></script>
  <script src="../lib/oojs.jquery.min.js"></script>
  <script src="../lib/oojs-ui.min.js"></script>
  <script src="../lib/oojs-ui-wikimediaui.min.js"></script>
  <script src="../lib/banana-i18n.js"></script>
  <script src="SearchWidget.js"></script>
  <script src="../SignItVideosGallery.js"></script>
  <script src="../SignItCoreContent.js"></script>
  <script src="popup.js"></script>
  <script src="../offscreen.html"></script>

wOxxOm

unread,
May 13, 2024, 5:26:12 AMMay 13
to Chromium Extensions, Kabir Singh, wOxxOm, Oliver Dunk, Chromium Extensions
<script src="../offscreen.html"></script>

This doesn't make sense to me. Anyway, use the built-in debugger to set a breakpoint at that line, re-run the code, and look at the state of DOM when the breakpoint triggers.

Kabir Singh

unread,
May 13, 2024, 5:34:58 AMMay 13
to Chromium Extensions, wOxxOm, Kabir Singh, Oliver Dunk, Chromium Extensions
my bad dk why i did that.....looking into debugger 

Kabir Singh

unread,
May 13, 2024, 7:50:49 AMMay 13
to Chromium Extensions, Kabir Singh, wOxxOm, Oliver Dunk, Chromium Extensions
  1. Promise
    1. [[PromiseState]]: "fulfilled"
    2. [[PromiseResult]]: "Not writable."
hey I left offscreen api for a bit , but got an explanation for why I was getting http status code 405 method not allowed upon using fetch api....apparently the endpoint I was referring to wasnt sending valid json data and instead returned a string not writable , which resulted in throwing this error... I dont think anything else can cause this issue in relevance to what we discussed above.

Kabir Singh

unread,
May 15, 2024, 3:47:51 PMMay 15
to Chromium Extensions, Kabir Singh, wOxxOm, Oliver Dunk, Chromium Extensions
Let us say that I have a certain number of common functions between service_worker and content scripts...I want those functions to be accessible everywhere , so should I pass message for each and every function and receive them accordingly or what ? Store them in local storage or use offscreen api ?

wOxxOm

unread,
May 15, 2024, 3:52:53 PMMay 15
to Chromium Extensions, Kabir Singh, wOxxOm, Oliver Dunk, Chromium Extensions
It depends on the complexity and size of the shared code, the size of data, whether it's easily transferable (messaging in Chrome only supports JSON-compatible types), whether it uses a chrome API that's not available in a content script, whether it needs to access a lot of data that may be cached in the background script or in the offscreen document, the time to start (50ms), compile, run, and initialize the background script.

Kabir Singh

unread,
May 15, 2024, 4:04:54 PMMay 15
to Chromium Extensions, wOxxOm, Kabir Singh, Oliver Dunk, Chromium Extensions
I have around 7-8 functions that are in common....some are small functions , don't do much and some are big,...the codebase of popup.js worked on the basis of fetching backgroundPage using getBackground method and then later utilizing it's functions/state/parameters like object keys...so I'm kinda working on refactoring that, such that functions can be used in sw.js as well as popup.js

wOxxOm

unread,
May 15, 2024, 4:14:26 PMMay 15
to Chromium Extensions, Kabir Singh, wOxxOm, Oliver Dunk, Chromium Extensions
Well, it's up to you. Use devtools -> Performance profiler, so you can compare whether it's worth to wait to start the background script and consume megabytes of memory for its JS environment and data.

Kabir Singh

unread,
May 15, 2024, 4:22:54 PMMay 15
to Chromium Extensions, wOxxOm, Kabir Singh, Chromium Extensions
how would file with common functions sound ? although we'd have to import it in both the files 

wOxxOm

unread,
May 15, 2024, 5:43:20 PMMay 15
to Chromium Extensions, Kabir Singh, wOxxOm, Chromium Extensions
Yes, that's how it's usually done when you decide that the expense of starting the background script is not worth it.

Kabir Singh

unread,
May 28, 2024, 6:27:18 PMMay 28
to Chromium Extensions, wOxxOm, Kabir Singh, Chromium Extensions
What is the process when creating a shared/global utility file like common-functions.js, just to make sure I am not doing anything wrong. Also it says in the chrome docs that submission of shared modules is not allowed , see here. So I guess message passing is the only viable approach left , although that sometimes break when going for cross browser

woxxom

unread,
May 29, 2024, 1:57:29 AMMay 29
to Chromium Extensions, Kabir Singh, wOxxOm, Chromium Extensions
Use messages. What specifically breaks? If you mean binary types like arraybuffer then use navigator.serviceWorker messages instead of chrome.runtime in Chrome. As for shared modules, it's for an entirely different thing and you would still use messages anyway. 

Kabir Singh

unread,
May 29, 2024, 2:21:36 AMMay 29
to woxxom, Chromium Extensions
Well let us say that I had a function in backgroundPage and earlier in Firefox I was awaiting it using API namespace , something like `await _backgroundPage.someFunc()`, now if I do the same by passing message, I can still achieve it, but my web page has to be reloaded for it to reflect....I'll look into it again , it has something to do with local storage, it isn't supposed to break. I'll look into service worker api as well. Thanks again.

Reply all
Reply to author
Forward
0 new messages