banana constructor of banana-i18n not being recognized inside service worker in manifest V3

1,026 views
Skip to first unread message

Kabir Singh

unread,
Mar 26, 2024, 6:57:41 AM3/26/24
to Chromium Extensions
So earlier in manifest V2 we could directly communicate with background scripts using something like await browser.runtime.getBackgroundPage() , ​but in manifest V3 we have to use service workers and message passing in order to communicate with content scripts. Apparently earlier when I was using banana-i18n for internationalization ,banana constructor was being recognized , but now in manifest V3 is says Uncaught ReferenceError: Banana is not defined , ​despite being imported like before through popup.html CDN , what am I supposed to do.

Patrick Kettner

unread,
Mar 26, 2024, 7:07:23 AM3/26/24
to Kabir Singh, Chromium Extensions
Hi Kabir,
The contents of an HTML page is not directly available inside of your service worker. You would need to either use import or importScripts to import the banana-i18n file. 

patrick

On Tue, Mar 26, 2024 at 6:57 AM Kabir Singh <kab...@amityonline.com> wrote:
So earlier in manifest V2 we could directly communicate with background scripts using something like await browser.runtime.getBackgroundPage() , ​but in manifest V3 we have to use service workers and message passing in order to communicate with content scripts. Apparently earlier when I was using banana-i18n for internationalization ,banana constructor was being recognized , but now in manifest V3 is says Uncaught ReferenceError: Banana is not defined , ​despite being imported like before through popup.html CDN , what am I supposed to do.

--
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/86c98a6e-41e9-41a3-a3cd-3f6ecfd11aa4n%40chromium.org.

wOxxOm

unread,
Mar 26, 2024, 7:34:10 AM3/26/24
to Chromium Extensions, Patrick Kettner, Chromium Extensions, Kabir Singh
The getBackgroundPage bit seems unrelated. The problem seems to be that you import a remote script from CDN, which should be blocked by Chrome in ManifestV3 and you should see an error in devtools for the popup. Put the script into your extension.

Kabir Singh

unread,
Mar 26, 2024, 8:00:32 AM3/26/24
to Chromium Extensions, wOxxOm, Patrick Kettner, Chromium Extensions, Kabir Singh
Yeah I figured I hadn't imported the banana module inside service_worker , earlier it didn't use to be the case , but it somehow skipped my mind despite reading docs for a many times . . .  .thank you guys , I don't see any error regarding Banana constructor . . . altho I have different errors now , how do you integrate external content inside service_worker without violating manifest V3 CSP guidelines

Patrick Kettner

unread,
Mar 26, 2024, 8:19:11 AM3/26/24
to Kabir Singh, Chromium Extensions, wOxxOm
> how do you integrate external content inside service_worker without violating manifest V3 CSP guidelines

You need to package the library with the extension itself. 

Kabir Singh

unread,
Mar 29, 2024, 1:58:33 PM3/29/24
to Chromium Extensions, Patrick Kettner, Chromium Extensions, wOxxOm, Kabir Singh
hey its me the banana guy again, I was fetching the banana object (banana-i18n) by passing messages to the service_worker , and I even receive the object but some values are missing but all keys are present , what should I do ?
The objects that I am receiving are as follows, as you can see the messageStore is empty in popup's response , what should I do?
Screenshot (38).png
My code is down below
popup.js
async function getBanana () {
const response = await chrome.runtime.sendMessage({ command: "getBanana" });
console.log("response : ",response);
return response;
};
banana = getBanana();
console.log("#37 banana : ",banana);

sw.js
browser.runtime.onMessage.addListener(async function (
    message,
    sender,
    sendResponse
  ) {
    console.log(
      "Message heard in service_worker: ",
      message,
      "---------------------"
    );
     if (
      message.command === "getBanana"
    ) {
      console.log("banana : ", banana);
      sendResponse(banana);
    }
  });

wOxxOm

unread,
Mar 29, 2024, 2:18:21 PM3/29/24
to Chromium Extensions, Kabir Singh, Patrick Kettner, Chromium Extensions, wOxxOm
Messaging in Chrome is still using the ancient JSON-compatible protocol (number, boolean, string, null, and array/object of such types, own keys only, no self-references). Firefox supports StructuredClone algorithm required to transfer Map/Set, maybe Safari as well.

In Chrome you'll have to serialize everything that's not a JSON type e.g. convert map to [...map] and then recreate it after receiving the message using new Map constructor. There may be an existing library for serialization, but it's not hard to write one yourself.

Unfortunately, ManifestV3 has wasted a lot of effort on pursuing impractical ideas such as switching to a service worker (it turned out to be a lot more complicated due to bugs and the very fact that a service worker is architecturally anatagonistic to a locally hosted extension), so I wouldn't expect this to be implemented anytime soon. There was an abandoned attempt to switch to the structured clone algorithm several years ago, but that developer has left the team.

Kabir Singh

unread,
Mar 29, 2024, 3:16:41 PM3/29/24
to Chromium Extensions, wOxxOm, Kabir Singh, Patrick Kettner, Chromium Extensions
apparently my banana object doesn't have instance of Map , cant see how I'll serialize it 

wOxxOm

unread,
Mar 29, 2024, 3:21:37 PM3/29/24
to Chromium Extensions, Kabir Singh, wOxxOm, Patrick Kettner, Chromium Extensions
Your screenshot shows that sourceMap is an instance of Map. To serialize it use [...obj.sourceMap]. You can also write a function to go over the object recursively before sending it. It would replace incompatible properties with explicit serialization.

Kabir Singh

unread,
Mar 29, 2024, 3:25:09 PM3/29/24
to Chromium Extensions, wOxxOm, Kabir Singh, Patrick Kettner, Chromium Extensions
I thought the same , like it seemed obvious , but when I ran console.log(banana instaceof Map) it returned false 

Kabir Singh

unread,
Mar 29, 2024, 3:26:57 PM3/29/24
to Chromium Extensions, Kabir Singh, wOxxOm, Patrick Kettner, Chromium Extensions
I'll try again

wOxxOm

unread,
Mar 29, 2024, 3:27:33 PM3/29/24
to Chromium Extensions, Kabir Singh, wOxxOm, Patrick Kettner, Chromium Extensions
Not `banana` itself, but `sourceMap` inside `banana`. Take a look at the object tree carefully.

Kabir Singh

unread,
Mar 29, 2024, 3:41:07 PM3/29/24
to Chromium Extensions, wOxxOm, Kabir Singh, Patrick Kettner, Chromium Extensions
yep you are right , never worked with maps so dont know what to expect but console.log(banana.messageStore.sourceMap instanceof Map);
retuned true, but even then I dont fully understand how to proceed  . . . can you make it more granular, would really appreciate it

Kabir Singh

unread,
Mar 29, 2024, 3:44:43 PM3/29/24
to Chromium Extensions, Kabir Singh, wOxxOm, Patrick Kettner, Chromium Extensions
wait , it worked , sendResponse([...banana.messageStore.sourceMap])
it actually returned me the desired without having to reconstruct it in popup.js, thank you !!

wOxxOm

unread,
Mar 29, 2024, 3:44:54 PM3/29/24
to Chromium Extensions, Kabir Singh, wOxxOm, Patrick Kettner, Chromium Extensions
If this is the only such incompatible property you can simply send it separately:
sendResponse([banana, [...banana.messageStore.sourceMap]])

The caller would assemble it back:
const [banana, sourceMap] = await chrome.runtime.sendMessage({ command: "getBanana" });
banana.messageStore.sourceMap = new Map(sourceMap);

In case there are many such properties you need to make a compatbile copy of the object before sending it. Look at how deepCopy function is implemented and adapt it to your needs or find an existing library that serializes objects to a string.

Kabir Singh

unread,
Mar 29, 2024, 4:21:53 PM3/29/24
to Chromium Extensions, wOxxOm, Kabir Singh, Patrick Kettner, Chromium Extensions
your snippet cleared a lot of doubts of mine , but even so the banana object cant access the i18n functionality like it could back in manifest V2 , anything about that ? Or should I make a custom prototypal inheritant method which helps me access the desired key-value pair present inside the sourceMap, whenever I invoke banana.i18n('whatver-key-value-it-is')?

wOxxOm

unread,
Mar 29, 2024, 4:23:41 PM3/29/24
to Chromium Extensions, Kabir Singh, wOxxOm, Patrick Kettner, Chromium Extensions
Functionality i.e. a JS function is not a JSON-compatible type, so it's not transfered in a message. In ManifestV2 you've accessed the object directly, it wasn't transferred. It's not possible with the service worker in MV3 because workers run in a separate OS thread.

Kabir Singh

unread,
Mar 29, 2024, 4:32:22 PM3/29/24
to Chromium Extensions, wOxxOm, Kabir Singh, Patrick Kettner, Chromium Extensions
yea read that a lot of times ,but ig now it makes sense . . .so prototypal inheritance then ?

wOxxOm

unread,
Mar 29, 2024, 4:34:20 PM3/29/24
to Chromium Extensions, Kabir Singh, wOxxOm, Patrick Kettner, Chromium Extensions
There's no inheritance, only own keys are transferred in a message.

Kabir Singh

unread,
Mar 29, 2024, 4:41:37 PM3/29/24
to Chromium Extensions, wOxxOm, Kabir Singh, Patrick Kettner, Chromium Extensions
I guess we're not one the same page , I meant something like this
Object.prototype.i18n = function (message) {
        return this.messageStore.sourceMap.get("fr")[message];
    }

wOxxOm

unread,
Mar 29, 2024, 4:43:40 PM3/29/24
to Chromium Extensions, Kabir Singh, wOxxOm, Patrick Kettner, Chromium Extensions
Modifying Object.prototype is a terrible idea and there's no need to do it here anyway, just use a normal function.

Kabir Singh

unread,
Mar 29, 2024, 4:48:48 PM3/29/24
to Chromium Extensions, wOxxOm, Kabir Singh, Patrick Kettner, Chromium Extensions
I guess you are right but I already  have many occurrences with banana.i18n since it was compatible and accessible earlier , so thats why I was thinking of doing it this way . . will go with function then

wOxxOm

unread,
Mar 29, 2024, 4:51:24 PM3/29/24
to Chromium Extensions, Kabir Singh, wOxxOm, Patrick Kettner, Chromium Extensions
In that case you can add this function on the received object:

const sourceMap = new Map(await chrome.runtime.sendMessage({ command: "getBanana" }));
const banana = {i18n: msg => sourceMap.get("fr")[msg]};

Kabir Singh

unread,
Mar 29, 2024, 5:01:52 PM3/29/24
to Chromium Extensions, wOxxOm, Kabir Singh, Patrick Kettner, Chromium Extensions
so basically we are making i18n a key of the banana object which stores a function which takes a message as an argument , thereby restoring a similar functionality ? am i right ?

wOxxOm

unread,
Mar 29, 2024, 5:03:11 PM3/29/24
to Chromium Extensions, Kabir Singh, wOxxOm, Patrick Kettner, Chromium Extensions
Yes.

Kabir Singh

unread,
Mar 30, 2024, 1:24:44 AM3/30/24
to Chromium Extensions, wOxxOm, Kabir Singh, Patrick Kettner, Chromium Extensions
besides scoping issues , I guess everything else is fine, can resolve that on my own . .. Earlier in manifest V2 we could access the objects and variable as a whole , by running browser.runtime.getBackgroundPage() . .. . but in V3 since no such page exists directly , I tried wrapping my background-script/sw.js inside a function and then invoking it and returning its data whenever I pass a message like ({command:'getBackground'}) , sort of like importing it as a module , but it doesn't feel right since I dont think it'll make the contents of sw.js accessible like earlier . Also  I don't think  I am supposed to pass message for every thing while communicating b/w my sw.js and other content scripts , how should I proceed because otherwise it gives me the error background page not found

wOxxOm

unread,
Mar 30, 2024, 1:47:51 AM3/30/24
to Chromium Extensions, Kabir Singh, wOxxOm, Patrick Kettner, Chromium Extensions
It's not about scoping, which is a term for the same environment, while the background script runs in an entirely different environment. It's like a separate window, but invisible. You should never load the entire background script anywhere else, it should be only declared in manifest.json's "background" section. You can extract the common code into a separate script and import it in the background script and other parts of the extension. In case you are using the background script as a central state and the amount of data is small, it might be better to just store the data in chrome.storage.session directly without using the background script. 

Kabir Singh

unread,
Mar 30, 2024, 2:28:11 AM3/30/24
to Chromium Extensions, wOxxOm, Kabir Singh, Patrick Kettner, Chromium Extensions
well when I mentioned about scoping I was still talking about banana , but nvm . . .also I am not loading background script anywhere , all I am asking is how do I achieve the same functionality like getBackgroundPage() in service_worker in manifest V3 , where the contents of bg-script like variables and other states are directly  accessible  , without having to pass message for each and every other variable, also I am aware of persisting states but thats what I am asking , even if I were to store states inside local/session storage I'll have to pass messages for each and every item that I return from sw.js , is that like the only alternative I have or I can go with something else as well . . .also you seem to have more than enough knowledge , would you be willing to be participate as a gsoc mentor ?

wOxxOm

unread,
Mar 30, 2024, 2:31:41 AM3/30/24
to Chromium Extensions, Kabir Singh, wOxxOm, Patrick Kettner, Chromium Extensions
Well, there's physically no way to replicate that functionality because the environment of the service worker cannot be directly accessed. Everything will have to be sent over a message, which is why I suggested using chrome.storage directly in the client pages (the popup or the content script) instead of sending a message to the background script, but that depends on what the extension does. As for mentoring, I'm not interested.

Kabir Singh

unread,
Mar 30, 2024, 2:39:20 AM3/30/24
to Chromium Extensions, wOxxOm, Kabir Singh, Patrick Kettner, Chromium Extensions
Ahh man, but you are genuinely guiding me a lot , anyways . . . what if we are fetching data inside our sw.js , would it still work if we persist those states/data and then access them inside our content scripts without passing any message ?

wOxxOm

unread,
Mar 30, 2024, 2:43:11 AM3/30/24
to Chromium Extensions, Kabir Singh, wOxxOm, Patrick Kettner, Chromium Extensions
If your SW sets the value in storage then the content script can see it using onChanged event for the storage like chrome.storage.session.onChanged. This event will be triggered in all contexts (tabs/frames, everywhere).

Kabir Singh

unread,
Mar 30, 2024, 3:51:35 AM3/30/24
to Chromium Extensions, wOxxOm, Kabir Singh, Patrick Kettner, Chromium Extensions
guess I'll go with it then

Kabir Singh

unread,
Mar 30, 2024, 7:18:09 AM3/30/24
to Chromium Extensions, Kabir Singh, wOxxOm, Patrick Kettner, Chromium Extensions
since service_workers cant communicate with DOM , using jquery for get/post requests is meaningless . . .so I thought of using normal fetch api , but for some reason I get http status code 405 , method not allowed , any idea why ? Like has it got something to do with api endpoint restrictions or V3/service worker ?

wOxxOm

unread,
Mar 30, 2024, 12:04:08 PM3/30/24
to Chromium Extensions, Kabir Singh, wOxxOm, Patrick Kettner, Chromium Extensions
Either you didn't add "host_permissions" to manifest.json or it's incorrect or you didn't convert the parameters for fetch() correctly or it's a bug in Chrome.

Kabir Singh

unread,
Mar 30, 2024, 1:34:47 PM3/30/24
to wOxxOm, Chromium Extensions, Patrick Kettner
By convert you mean json.stringify/ parse ?

wOxxOm

unread,
Mar 30, 2024, 2:31:05 PM3/30/24
to Chromium Extensions, Kabir Singh, Chromium Extensions, Patrick Kettner, wOxxOm
No, I mean fetch() behaves differently, so depending on how you used $.ajax previously you may need to add or change something in the parameters.

wOxxOm

unread,
Mar 30, 2024, 2:32:59 PM3/30/24
to Chromium Extensions, wOxxOm, Kabir Singh, Chromium Extensions, Patrick Kettner
BTW you can keep using $.ajax inside the offscreen document.

Kabir Singh

unread,
Mar 31, 2024, 2:48:35 AM3/31/24
to Chromium Extensions, wOxxOm, Kabir Singh, Chromium Extensions, Patrick Kettner
Using offscreen API is a good alternative , but since docs mentioned moving DOM and window calls to offscreen api , it skipped my mind ,altho we are not manipulating the DOM directly , jQuery interacts with it one way or other  . . . I'll give it a try, also I indeed didn't add host permissions for all urls so that might be the case as well

Kabir Singh

unread,
Mar 31, 2024, 2:57:43 AM3/31/24
to Chromium Extensions, Kabir Singh, wOxxOm, Chromium Extensions, Patrick Kettner
tried adding host permissions , no change ,still getting the same error status code 405, willl try offscreen API approach later

Kabir Singh

unread,
Mar 31, 2024, 6:05:36 AM3/31/24
to Chromium Extensions, wOxxOm, Patrick Kettner
Also , let us say that I don't have a version of the extension I am working on , on Chrome Canary/Dev/Beta , my extension is only available on Stable version, so will it disable in June or it'll deprecate in stable rollout , i.e., June 2024 : 1-X months ? Like how long do I have June or July?
Reply all
Reply to author
Forward
0 new messages