Unchecked runtime.lastError: No tab with <id>, Chrome Extension - google-chrome-extension

I don't understand why i recieve this error
Error
This code works
chrome.tabs.onUpdated.addListener(handleUrls);
function handleUrls(tabId, { changeInfo }, tab) {
const isOauthTokenPage = tab.url?.match(CONFIG.OAUTH_TOKEN_PAGE_PATTERN);
if (isOauthTokenPage) {
chrome.tabs.remove(tabId);
}
}
But why i get this error?
I tried chrome.tabs.onUpdated.removeListener before and after chrome.tabs.remove(tabId), tried chrome.tabs.query to get "actual" tabId

So it's trigger more than once
To avoid it
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
// check url state
if (!changeInfo.url) return;
// your code...
if ((tab.url).match(CONFIG.OAUTH_TOKEN_PAGE_PATTERN)) {
// won't invoke onUpdated several times
chrome.tabs.remove(tabId);
}
});

Related

Using a global variable in the Chrome extension

Consider that when accessing the example.com page the tabs.onUpdated event is executed three times.
background.js
function sendMsn(msn) {
chrome.tabs.query({currentWindow: true, active: true}, function (tabs) {
chrome.tabs.sendMessage(tabs[0].id, { "message": msn });
});
}
var cont = 0;
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
if(changeInfo.status === 'complete') {
// This runs 3 times
if(cont === 0) {
sendMsn('ok');//This line is not running
cont++;
}
sendMsn(cont);
}
});
content.js
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
console.log(request);
});
The expected output would be:
Ok
1
2
3
However, what is printed is:
1
1
1
I don't know the reason for the error, but I suspect it's the scope of the cont variable.

chrome.runtime.sendmessage not working in chrome extentions

hello I have a question
I send to backgroud.js from content.js;
but it is append error : Could not establish connection. Receiving end does not exist.
but it works fine that i think
content.js send chrome.runtime.sendMessage
background.js receive chrome.runtime.onMessage.addListener and menifest.json
chrome.runtime.sendMessage is it does not work
"background": { "service_worker": "background.bundle.js" },
"content_scripts": [
{
"matches": ["http://*/*", "https://*/*", "<all_urls>"],
"js": ["contentScript.bundle.js"],
"css": ["content.styles.css"]
}
],
background.js
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
switch (request.action) {
case "item-save": {
chrome.storage.local.get('item', function (itemData) {
let itemList = [];
if (Object.keys(itemData).length > 0) {
itemList = itemData.item;
}
itemList.push(request.source);
chrome.storage.local.set({
item: itemList
});
sendResponse("OK");
});
return true;
}
case "page-main": {
chrome.tabs.create({
url: chrome.runtime.getURL("options.html"),
});
return true;
}
default: return true;
}
});
content.js
button.addEventListener('click', () => {
chrome.runtime.sendMessage({
action : "item-save",
source : item
},function(response){
if(response ==="OK"){
let ok = confirm("check")
// if(ok){
// chrome.runtime.sendMessage({
// action : "page-main",
// });
// }
}
})
})
what's wrong?
It's a common chrome.runtime.onMessage bug that hopefully gets fixed one day.
I guess it gives an error when request.action is "page-main", since it's waiting for a sendResponse.
I think the solution would be to add an empty sendResponse() on "page-main".
You can also just use a return true; outside the switch and make it less redundant.:
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
switch (request.action) {
case "item-save": {
chrome.storage.local.get("item", function (itemData) {
// your code here ...
sendResponse("OK");
});
break;
}
case "page-main": {
chrome.tabs.create({
url: chrome.runtime.getURL("options.html"),
});
sendResponse(); // or try with sendResponse({})
break;
}
}
return true;
});
Also the chrome.storage functions are asynchronous.
So on "item-save" the sendResponse("OK"); will be executed before the chrome.storage.local.set.
If you want to run sendResponse after saving the itemList, it would be something like this:
chrome.storage.local.set({
item: itemList,
}, function () {
sendResponse("OK");
});
I hope it works! :)

Stop callback chain and send notification beforeSave method ApostropheCMS

I'm trying to prevent the user to save a piece if it doesn't achieve some requirements.
Currently I'm doing it like this:
self.beforeSave = function(req, piece, options, callback) {
let success = true;
let error = "";
if (Array.isArray(piece._subevents) && piece._subevents.length) {
success = self.checkDateAndTimeCompabilitiyWithChildren(piece);
}
if (!success) {
self.apos.notify(req, "Check the compatibility between parent event and subevents", { type: "error" });
error = "Subevents are not compatible with parent event";
}
callback(error);
};
This works but the problem is it shows 2 errors notifications (the default and my custom), 1 because of callback(error) and 1 because of apos.notify.
Any idea how to stop the item of being saved and only show my notification?
Thanks in advance.
UPDATE 1:
As Tom pointed out, my code looks like this now:
// lib/modules/events/public/js/editor-modal.js
apos.define('events-editor-modal', {
extend: 'apostrophe-pieces-editor-modal',
construct: function(self, options) {
self.getErrorMessage = function(err) {
if (err === 'incompatible') {
apos.notify('A message suitable for this case.', { type: 'error' });
} else {
apos.notify('A generic error message.', { type: 'error' });
}
};
}
});
// lib/modules/events/index.js
var superPushAssets = self.pushAssets;
self.pushAssets = function() {
superPushAssets();
self.pushAsset("script", "editor-modal", { when: "user" });
};
self.beforeSave = async function(req, piece, options, callback) {
return callback("incompatible")
};
For testing purposes I'm just returning the error in beforeSave. The problem is that an exception is being thrown in the browser console and the modal is not properly rendered again. Here's a screenshot about what I'm talking:
I'm trying to debug it and understand what's happening but no clue yet.
In your server-side code:
self.beforeSave = function(req, piece, options, callback) {
let success = true;
if (Array.isArray(piece._subevents) && piece._subevents.length) {
success = self.checkDateAndTimeCompabilitiyWithChildren(piece);
}
if (!success) {
return callback('incompatible');
}
return callback(null);
};
And on the browser side:
// in lib/modules/my-pieces-module/public/js/editor-modal.js
apos.define('my-pieces-module-editor-modal', {
extend: 'apostrophe-pieces-editor-modal',
construct: function(self, options) {
self.getErrorMessage = function(err) {
if (err === 'incompatible') {
return 'A message suitable for this case.';
} else {
return 'A generic error message.';
}
};
}
});
If the error reported by the callback is a string, it is passed to the browser. The browser can then recognize that case and handle it specially. 'my-pieces-module-editor-modal' should be substituted with the name of your pieces module followed by -editor-modal.

window object in NodeJS of SSR got both 'not defined' or 'already been declared'

I am migrating an Angular 6 project to Angular Universal project(SSR). In my project, I need to access window object in order to get "navigator", "location", etc... I am using the following method to polyfill window in Server side. But it is not working.
try {
console.log(window);
} catch (window) {
console.log('Server side window.');
window = {
location: {
replace: () => {},
protocol: 'https'
},
navigator: {
userAgent: '',
appVersion: ''
},
scrollTo: () => {},
open: () => {},
localStorage: {}
};
console.log('Server side window.', window);
}
What is the best way to handle the window object properly on the server side for the server-side rendering web site? I got following confusion errors. It said 'window is not defined' or 'has been declared'. How to use window object in NodeJS?
> console.log(window);
ReferenceError: window is not defined
> let window = 1
SyntaxError: Identifier 'window' has already been declared
The problem with this piece of code is that window is specified as exception identifier in try..catch, basically:
try {
console.log(window);
} catch (window) {
// window instanceof ReferenceError
window = {...};
// window is redefined in this block scope
}
This will work in loose mode:
try {
console.log(window);
} catch (err) {
window = {...};
}
But will result in another ReferenceError in strict mode. It should be instead:
try {
console.log(window);
} catch (err) {
global.window = {...};
}
And a proper way to detect window is:
if (typeof window === 'undefined') {
global.window = {...};
}
This code is limited to browser and Node.js; it will result in an error in a worker.

How should a chrome extension background page communicate with multiple content scripts?

I'm having trouble communicating with multiple content scripts from my background page. My background page has code like:
chrome.tabs.sendRequest(tabId, { targetScript:"content1" }, function (resp) {
if (resp.fromCorrectScript) {
DoMoreStuff();
}
});
and I have content scripts like:
// content1.js
chrome.extension.onRequest.addListener(function (sender, request, sendResponse) {
if (request.targetScript === "content1") {
sendResponse({ fromCorrectScript:true });
} else {
sendResponse({});
}
});
and
// content2.js
chrome.extension.onRequest.addListener(function (sender, request, sendResponse) {
if (request.targetScript === "content2") {
sendResponse({ fromCorrectScript:true });
} else {
sendResponse({});
}
});
My understanding is that my callback in the background page should be called twice, once from each content script. It looks like it's only called twice sometimes, and pretty much only when I have a breakpoint at the if clause. Am I doing something wrong here?
Thanks,
-Greg
Well, it looks like it works correctly as long as I ensure that only one content script responds to the message. So my content script code should be more like:
// content1.js
chrome.extension.onRequest.addListener(function (sender, request, sendResponse) {
if (request.targetScript === "content1") {
sendResponse({ fromCorrectScript:true });
}
});
and
// content2.js
chrome.extension.onRequest.addListener(function (sender, request, sendResponse) {
if (request.targetScript === "content2") {
sendResponse({ fromCorrectScript:true });
}
});
I don't know what's the root of the problem, can only guess that whichever script runs callback first destroys it for the rest.
I can suggest workaround though. You can send requests in both directions, not only from background page to script. So your background page might look like:
chrome.tabs.sendRequest(tabId, { targetScript:"content1" });
chrome.extension.onRequest.addListener(function (request, sender, sendResponse) {
if (request.fromCorrectScript) {
DoMoreStuff();
}
});
And in scripts:
chrome.extension.onRequest.addListener(function (request, sender, sendResponse) {
if (request.targetScript === "content1") {
chrome.extension.sendRequest({fromCorrectScript:true});
} else {
chrome.extension.sendRequest({fromCorrectScript:false});
}
});
This shouldn't choke.

Resources