Chrome extension stuck. Manifest v3 - google-chrome-extension

I have issue with chrome extension in manifest v3. Usually reproduce on MacOS. Before migrating to manifest v3 my app was on manifest v2 and I did't have this problems. Some times app is
not reacting on click at icon of list extensions. So I need to reload computer or disabled the app and after turn on it to resolve this problem. In console i have errors: "Could not establish connection. Receiving end does not exist.", "ResizeObserver loop limit exceeded", "Request failed with status code 403". Or without any errors in console. It seems like service worker don't start again or stuck in the loop
"background": {
"service_worker": "js/background.js",
"type": "module"
},
"content_scripts": [{
"matches": ["<all_urls>"],
"js": [
"js/contentscript.js"
]
}],
this is code from manifest
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => Promise.resolve()
.then(() => { sendResponse(); })
.then(() => {
if (message === 'toggle-popup') {
updateApplicationTab((idTab) => {
chrome.tabs.sendMessage(idTab, { message: 'clicked_browser_action' });
});
}
if (message === 'toggle-popup_set_id') {
chrome.tabs.query({ active: true, currentWindow: true }, tabs => {
chrome.runtime.sendMessage({
type: 'setActiveTab',
data: tabs[0].id,
});
});
}
}),
);
this is code from worker to open app

Related

Load content script into all tabs, but only if the content script is not already loaded

Problem
My chrome extension needs to inject the content script into all tabs when switched on and then when any new tabs are loaded etc.
By adding the content script to the manifest file I can satisfy the second requirement of having it loaded in newly loaded tabs.
To make it also inject the content script into tabs as soon as the extension is loaded or refreshed (there is no popup), I am using chrome.scripting.executeScript.
Problem is each time the extension is turned off/on or refreshed, the content script is loaded in again and any DOM manipulation from content script occurs multiple times.
So from reading another post, I liked the idea of sending a message to the content script, if I don't get a response (undefined) then I inject the script into this tab, if I do get a response then do nothing.
In my background logs I get this error Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist. which is what I would expect if the content script did not exist, but even when the content script is clearly being loaded, I still get the same error.
Any help greatly appreciated. And maybe this is not even the best approach for this, I am not sure. Ideally I would also want the extension to immediately stop working when it's turned off, but I haven't got around to this problem yet.
Manifest
{
"name": "Text highlighter",
"description": "Text highlighter",
"version": "1.0.0",
"manifest_version": 3,
"background": {
"service_worker": "background.js"
},
"options_ui": {
"page": "options.html",
"open_in_tab": false
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"],
"css": [ "styles.css" ]
}
],
"permissions": ["storage", "activeTab", "scripting", "tabs"],
"host_permissions": ["<all_urls>"]
}
self.oninstall = () => {
chrome.tabs.query({}, function(tabs) {
tabs.forEach((tab) => {
chrome.tabs.sendMessage(tab.id, true, (response) => {
console.log('response', response);
if(!response) {
chrome.scripting.executeScript({
target: {tabId: tab.id},
files: ['content.js']
});
chrome.scripting.insertCSS({
target: { tabId: tab.id },
files: ['styles.css']
});
}
});
});
});
};
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
console.log('request', request);
sendResponse(true);
return true;
});

Chrome extension show page action example manifest v3

I'm trying to reproduce this example with the manifest v3. But my action is always active - I expext it to be disabled on all pages without 'g' in a URL.
Thanks in advance!
manifest.json
{
"name":"Example",
"description":"description",
"version":"0.1",
"manifest_version":3,
"background":{
"service_worker":"background.js"
},
"permissions":[
"declarativeContent"
],
"action":{
"default_icon":{
"16":"/images/get_started16.png",
"32":"/images/get_started32.png",
"48":"/images/get_started48.png",
"128":"/images/get_started128.png"
},
"default_title":"press here to open"
},
"icons":{
"16":"/images/get_started16.png",
"32":"/images/get_started32.png",
"48":"/images/get_started48.png",
"128":"/images/get_started128.png"
}
}
background.js
chrome.runtime.onInstalled.addListener(() => {
// Replace all rules ...
chrome.declarativeContent.onPageChanged.removeRules(undefined, function() {
// With a new rule ...
chrome.declarativeContent.onPageChanged.addRules([
{
// That fires when a page's URL contains a 'g' ...
conditions: [
new chrome.declarativeContent.PageStateMatcher({
pageUrl: { urlContains: 'g' },
})
],
// And shows the extension's page action.
actions: [ new chrome.declarativeContent.ShowPageAction() ]
}
], function callback(details) {
console.log("chrome.declarativeContent.onPageChanged.addRules callback");
console.log(details);
});
});
});
as wOxxOm mentioned
this answer helped.

Cannot read property 'executeScript' of undefined

I follow the 'Getting started' tutorial of chrome extensions, but I get the below error.
I search google, somebody say can't access 'executeScript' in content.js, but the error is from popup.js.
I had tried change 'chrome.scripting.executeScript' to 'chrome.tabs.executeScript', it didn't work too.
error image
manifest.json
{
"name": "Getting Started Example",
"version": "1.0",
"description": "Build an Extension!",
"permissions": ["storage", "declarativeContent", "activeTab"],
"background": {
"scripts": ["background.js"],
"persistent": false
},
"page_action": {
"default_popup": "popup.html"
},
"options_page": "options.html",
"manifest_version": 2
}
popup.js
let changeColor = document.getElementById('changeColor')
chrome.storage.sync.get('color', function(data) {
changeColor.style.backgroundColor = data.color;
changeColor.setAttribute('value', data.color)
});
changeColor.addEventListener('click', () =>
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.scripting.executeScript(
tabs[0].id,
{ function: setColor })
})
);
async function setColor() {
let {color} = await chrome.storage.sync.get(['color']);
document.body.style.backgroundColor = color;
};
The whole extension code
reference: chrome developers -> Getting started
The permissions in your manifest.json is missing one item, which is "scripting".
It should look like this:
…
"permissions": ["storage", "declarativeContent", "activeTab", "scripting"],
…
This is actually seen on the Getting Started page here.
You need scripting permissions to access scripting APIs. Add scripting to your manifest.js file.
"permissions": ["scripting", ...]
You either need to migrate to manifest v3 or use chrome.tabs.executeScript instead of chrome.scripting.executeScript
In my case, the exception log "Cannot read property 'executeScript' of undefined" was pretty misleading.
With correct manifests, it happened, and it was because of typo in the to be injected function, like below.
document.body.style.backgrounColor = color;
corrected it to
document.body.style.backgroundColor = color;
and it worked.
Remove and load the extension back! Despite adding "permissions": ["scripting"] this is what I had to do.
I found that chrome.scripting.executeScript works in popup but not in content scripts.
This is a super simple popup example in React. You click on the button and it shows the page title.
export function Popup() {
const [title, setTitle] = useState("");
const onClick = async () => {
const [tab] = await chrome.tabs.query({ currentWindow: true, active: true });
const [res] = await chrome.scripting.executeScript({
target: { tabId: tab.id },
func: () => document.title,
});
setTitle(res.result);
};
return (
<div className="popup">
<button onClick={onClick}>Click</button>
{title}
</div>
);
}

Can I invoke a function that's outside the "sandbox" of an injected content script, in my Chrome Extension?

I have created a Chrome extension that queries for elements on some webpages that feature dynamic content loading via AJAX; -- when on some of these webpages, my content.js script triggers too early (prior to the loading of the elements I need).
As a result, I set up a listener in my background script to listen for when the page has loaded enough of the elements and then it re-injects the content.js script.
background.js:
chrome.runtime.onMessage.addListener(
function (request, sender, sendResponse) {
if (request.type == "hats_men") {
chrome.windows.get(sender.tab.windowId, function () {
/*get user's size based on product type (ie. hat)*/
var res = findSize();
if (res == -1) {
/*do nothing since no size exists for them*/
}
else {
/*send size back to content script via messaging*/
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
chrome.tabs.sendMessage(tabs[0].id, { type: "res_size", size: res });
});
}
});
}
});
var oldTab;
chrome.tabs.onUpdated.addListener(
function (tabId, changeInfo, tab) {
var temp = tab.url;
/*IF: on a product page, execute script.*/
if (temp && (temp.indexOf("shop.com/shop/") > -1)) && (changeInfo.url === undefined) && (temp != oldTab)) {
chrome.tabs.executeScript(tabId, { file: "content.js" });
oldTab = tab.url;
}
});
Unfortunately, I am unable to call any functions from content.js that exist in my other js files. Is there a way to invoke them? For context, I've included the js files in question into "web_accessible_resources" and "content_scripts" in my manifest.json.
external.js:
function monk(x) {
if (x != 0) {
console.log(x);
}
}
content.js:
/*get current url*/
var cur_url = window.location.href;
/*check if user is on supported site*/
if (cur_url.indexOf("hello.com/shop/") > -1 && cur_url.indexOf("quantity=1") > -1) {
/*get relevant user data*/
chrome.storage.sync.get({ data: [] }, async function (user) {
/*call prod_res() to get item details from dynamically loaded product page*/
const res = await prod_res();
/*if the product is for men*/
if (res[1] == "man") {
/*if the product is a hat*/
if (res[0] == "hat") {
/*send message to background script that this is a mens' hat*/
chrome.runtime.sendMessage({ type: "hats_men" },
function (response) {
/*listen for user's hat size in the request/message from background script*/
chrome.runtime.onMessage.addListener(
function (request, sender, sendResponse) {
/*log user's hat size to console*/
if(request.type == "res_size"){
monk(request.size][1]);
}
}
});
}
);
}
}
});
}
/nothing logs to console/
manifest.json:
{
"manifest_version": 2,
"name": "E-Co",
"version": "0.2",
"background": {
"scripts": [ "jquery-3.5.1.min.js", "reduce.js", "background.js" ],
"persistent": false
},
"content_scripts": [
{
"matches": [
"<all_urls>"
],
"js": [
"jquery-3.5.1.min.js",
"content.js",
"external.js"
]
}
],
"web_accessible_resources": [
"external.js"
],
"browser_action": {
"default_icon": "logo_active.png",
"default_popup": "popup.html",
"default_title": "E-co Pop-Up"
},
"icons": {
"16": "icon_16.png",
"48": "icon_48.png",
"128": "icon_128.png"
},
"permissions": [
"storage",
"notifications",
"tabs",
"*://*/*"
],
"content_security_policy": "script-src 'self' https://ajax.aspnetcdn.com; object-src 'self'"
}
Your content.js sends a message and uses a callback to wait for a response, which must be sent back by using sendResponse. Your background script doesn't send a response, it only sends a new message, so the first callback never runs.
Use the simple messaging example from the documentation:
content.js
chrome.runtime.sendMessage({type: 'hats_men'}, monk);
background.js:
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.type == 'hats_men') {
sendResponse(findSize());
}
});
Note that sendResponse must be called for each message that was sent with a callback like we did above, otherwise you'll see errors in the console (unless you suppress them).

Chrome Extension: Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist

Recently, it is reported that the context menu of my app is vanished. If you remove the app and reinstall it, it works. But the vanishment happens again.
I found an error. I'm not sure if the error causes the vanishment of the context menu. But I'd like to fix this matter, because all I found is this.
This app shows texts you select in a page. When you select texts in an ordinaly page and click browser action button, it works without error. But if you try it on Google Docs, you will get error "Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist".
I'm afraid I don't know what to do with this. And I might have two problems. It'll be great help if you could give me some advice.
[manifest.js]
{
"manifest_version": 2,
"name": "Test Chrome Extension",
"short_name": "Test",
"version": "1.0",
"description": "This is a test.",
"icons": {
"128": "128.png"
},
"content_scripts": [{
"matches": ["<all_urls>"],
"js": ["googleDocsUtil.js", "content_scripts.js"]
}],
"background": {
"scripts": ["background.js"],
"persistent": true
},
"browser_action": {
"default_icon": {
"48": "48.png"
},
"default_title": "Test Chrome Extension"
},
"permissions": [
"contextMenus",
"tabs",
"background",
"http://*/*",
"https://*/*"
]
}
[background.js]
chrome.contextMenus.create({
type: 'normal',
id: 'testchromeextension',
title: 'Test Chrome Extension',
contexts:['selection']
});
chrome.contextMenus.onClicked.addListener(function(info,tab){
if( info.menuItemId == 'testchromeextension' ){
var selectedText = info.selectionText.replace(/ /g, "\n");
doSomething(selectedText);
}
});
chrome.browserAction.onClicked.addListener( function(tab) {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {method: "getSelection"}, function(response) {
doSomething(response.data);
});
});
});
function doSomething(selectedText) {
console.log(selectedText);
}
[content_scripts.js]
chrome.runtime.onMessage.addListener( function(request, sender, sendResponse) {
if (request.method == "getSelection") {
var post_val = window.getSelection().toString();
if ( !post_val ) {
var googleDocument = googleDocsUtil.getGoogleDocument();
post_val = googleDocument.selectedText;
}
sendResponse({data: post_val});
}
});
I believe this error is caused when you update the local version of an extension and then try to use the extension with its old/not-updated source code.
The fix: after you reload your local extension at chrome://extensions/, make sure you refresh the page you're using the extension on. You should no longer see the error.

Resources