chrome extension chrome.webNavigation (Event) fires multiple time - javascript

I'm building a chrome extension , I need to create a function to detect the URL changes for example youtube.com to youtube.com/watch?v={some video id}
I kept looking around for a way to do that on Content.js but apparently it's not possible and i need to use background.js in that particular case
First I've implanted this part
chrome.webNavigation.((EVENT)).addListener(function() {
alert("worked");
});
to test if chrome can detect the URL changes correctly , then i will need to connect that to content.js to Ajax the URL changes and return JSON object from the server
I saw on this page Chrome Extension - webNavigation multiple Events that i can use
Events
onBeforeNavigate
onCommitted
onDOMContentLoaded
onCompleted
onErrorOccurred
onCreatedNavigationTarget
onReferenceFragmentUpdated
onTabReplaced
onHistoryStateUpdated
sadly none of them works properly as they the Alert pops Multiple times ( sometimes 1-4 times ) when i visit ( reload or visit a single page )
I don't know what i should do or if there is any other way to handle a problem like this , I'm fairly new to chrome extension and JS.
background.js
chrome.storage.sync.get( "extensionSwitch", function(data){
if( data[ "extensionSwitch" ] == undefined ){
chrome.storage.sync.set( { "extensionSwitch" : "true" }, function() { } );
}
});
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
fetch(request.input, request.init).then(function(response) {
return response.text().then(function(text) {
sendResponse([{
body: text,
status: response.status,
statusText: response.statusText,
}, null]);
});
}, function(error) {
sendResponse([null, error]);
});
return true;
});
chrome.webNavigation.onHistoryStateUpdated.addListener(function() {
alert("worked");
});

Related

upgrading chrome extension from manifest version2 to v3, need to get clipboard text in background.js

Hi I am converting Google chrome extension from manifest version-2 to version-3
facing 2 issues those are mentioned below, but before that I will explain what extension in expected to do.
On click specific button on webpage I am calling console application that is copying JSON string in clipboard, then in chrome extension background.js I am getting clipboard data and passing it to content.js which is showing it in web page.
Errors / challenges:
1- Need to get clipboard text into a variable in background.js. I am able to get it in content.js but I need it to get it in background.js
2- I am getting these 2 error in background.js console, but extension is working
Unchecked runtime.lastError: Native host has exited.
Unchecked runtime.lastError: The message port closed before a response was received.
My Background.js looks like this
var port = null;
var tabId = null;
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
tabId=sender.tab.id;
var hostName = "my.console.app";
port = chrome.runtime.connectNative(hostName);
port.onDisconnect.addListener(onDisconnected);
sendResponse({status: 'ok'});
return true;
});
function onDisconnected() {
port = null;
SendResponse();
}
//this funciton need to upgrade to manifest version 3 because in v3 `chrome.extension.getBackgroundPage` is not compatible
function SendResponse() {
bg = chrome.extension.getBackgroundPage();
bg.document.body.innerHTML = ""; // clear the background page
var helper = null;
if (helper == null) {
helper = bg.document.createElement("textarea");
helper.style.position = "absolute";
helper.style.border = "none";
document.body.appendChild(helper);
}
//Focus the textarea
helper.select();
// perform a Paste in the selected control, here the textarea
bg.document.execCommand("Paste");
// Send data back to content_script
chrome.tabs.sendMessage(tabId, { action: "MY_CUSTOM_EVENT", response: helper.value });
}
content.js
document.addEventListener("MY_CUSTOM_EVENT", function (data) {
chrome.runtime.sendMessage({ runConsoleApp: true }, response => {
});
});
async function copyToTheClipboard(textToCopy){
navigator.clipboard.readText()
.then(text => {
//console.log('Pasted content: ', text);
$('.simulateEidResponse').html(text).trigger('click');
})
.catch(err => {
console.error('Failed to read clipboard contents: ', err);
});
}
Currently there's no way to do it in the background script due to crbug.com/1404835.
In the future the workaround will be execCommand + offscreen API.
The only reliable solution that works regardless of whether the tab is focused or not is to use document.execCommand("Paste") in the content script.
// background.js
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
if (msg.runConsoleApp) {
chrome.runtime.connectNative('my.console.app')
.onDisconnect.addListener(() => sendResponse(true));
return true;
}
});
// content.js
document.addEventListener('MY_CUSTOM_EVENT', async e => {
await chrome.runtime.sendMessage({ runConsoleApp: true });
document.querySelector('.simulateEidResponse').focus();
document.execCommand('selectAll');
document.execCommand('paste');
});

Chrome devtools extension - On devtools close event

I'm writing Chrome devtools plugin.
devtools.js
chrome.devtools.panels.create("Override Debug",
null,
"panel.html",
(panel) => {
panel.onShown.addListener(addDebugger);
panel.onHidden.addListener(destroyDebugger);
}
);
So when user navigates to Override Debug tab in devtools, I'm initializing debugger.
When user navigating away from my devtool, I'm discarding debugger - Which is working great
Now the issue comes, when user navigates to Override Debug and closed devtools completely, debugger still alive making page un responsive.
I tried to see events in panel object chrome.devtools objects also tried entire github to see if we have such event. No luck.
Is there any event that monitors devtools close event to discard the debugger I attached?
Update 1
Tried to attach Inspector.enable command as follows but no luck. It never got Inspector.detached message
function addDebugger() {
chrome.tabs.getSelected(null, function(target) {
debuggee = { tabId: target.id };
chrome.debugger.attach(debuggee, "1.2", () => {
chrome.debugger.sendCommand(debuggee, "Network.setRequestInterception", { patterns: [{ urlPattern: '*' }] });
chrome.debugger.sendCommand(debuggee, "Inspector.enable");
});
chrome.debugger.onEvent.addListener((source, method, params) => {
if (source.tabId === target.id) {
if (method === "Network.requestIntercepted") {
// Do many things
} else if (method === "Inspector.detached") {
destroyDebugger();
}
}
})
});
}

How to run callback function from content script after loading page ? chrome extension

I want to run a callback function from content script after tab loading new page .
Here is my code :
content_script.js
chrome.runtime.onMessage.addListener(function(request, sender, callback) {
if (request.id == "content_script") {
// open google.com
chrome.runtime.sendMessage({
"id" : "openUrl",
"url" : "https://google.com"
}, function(response) {
});
// call background script
// go to the claim code page
chrome.runtime.sendMessage({
"id" : "background"
}, function() {
alert("test");
});
}
});
background.js
chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) {
if (msg.id == "openUrl") {
var tabId = sender.tab.id;
var url = msg.url;
openUrl(tabId, url);
} else if (msg.id == "background") {
setTimeout(function() {
sendResponse();
}, 5000);
}
});
function openUrl(tabId, url) {
chrome.tabs.update(tabId, {
url : url,
active : false
}, function() {
console.log("open tab url callback");
});
};
I also uploaded the source code to google drive, you can download it using the bellow link :
https://drive.google.com/open?id=15zSn40z4zYkvCZ8B-gclzixvy6b0C8Zr
as you can see the alert test don't show !
However if I remove the code which open new url , then alert ("test") appear !
I am not sure why ! but it looks like javascript lost the reference to the call back function when I open new url .
How can I solve the problem ? what's the correct way ?
Thank you
The sendResponse function becomes invalid after the message callback returns, unless you return true from the event listener to indicate you wish to send a response asynchronously. (https://developer.chrome.com/extensions/runtime#event-onMessage)
Add return true; in background.js to make this handler asynchronous.
Now you get an error Attempting to use a disconnected port object in the sendResponse(); call of background.js, and yes, that's a result of the page navigating away and losing the context that the content script was running in.
There's no way to make this work: The context in which you wanted to call alert() simply doesn't exist anymore.
Try using chrome.tabs.sendMessage instead. But this means you have to set up the listener at the top level, and not inside of any callback. Message passing is hard.

turn on and off chrome extension

I am working on a chrome extension ,this extension have 2 icons in the browser action (On & Off) ;
basically when it is On the background execute the script.js (Inject the file:script.js)
using the chrome.tabs.executeScript(tab.id,{file:"script.js",function(){});
I had problems to turn it off !
I have tried to use messages communication between the background.js and the script.js but this does not work neither .
If I understand correctly, your extension should have two states, On and Off. Clicking the extension icon toggles it on/off.
In this case you should use storage so the extension knows what state it is in. So on a click event, use something like:
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.storage.sync.get('state', function(data) {
if (data.state === 'on') {
chrome.storage.sync.set({state: 'off'});
//do something, removing the script or whatever
} else {
chrome.storage.sync.set({state: 'on'});
//inject your script
}
});
});
Note though that this is happening at the extension/browser level and will apply to all tabs, so you may need something more complex that records both the tab ID and the state.
You then have the choice to either always run a content script and check the on/off state before performing some action, or inject and remove the script. I'm not sure if you remove a script though. Depending on what the script does, you may just want to refresh the page (i.e. if your script messes with the DOM and you want to undo that when turning the extension off).
background.js
var enable=false;
chrome.browserAction.onClicked.addListener(function (tab) {
enable = enable ? false : true;
if(enable){
//turn on...
chrome.browserAction.setIcon({ path: 'icon.png' });
chrome.browserAction.setBadgeText({ text: 'ON' });
chrome.tabs.executeScript(null, { file: 'content.js' });
}else{
//turn off...
chrome.browserAction.setIcon({ path: 'disable.png'});
chrome.browserAction.setBadgeText({ text: '' });
}
});
To add onto what #david-gilbertson stated for making it active and inactive for certain tabs, I have created that functionality here. I also took added some functions for removing and adding tabs to the array. Enjoy!
function addTab(array, new_tab_id)
{
array.push(new_tab_id);
//then call the set to update with modified value
chrome.storage.sync.set({
active_tabs:array
}, function() {
console.log("added tab");
});
}
function removeTab(array, rem_tab_id)
{
const index = array.indexOf(rem_tab_id);
if (index > -1) {
array.splice(index, 1);
}
//then call the set to update with modified value
chrome.storage.sync.set({
active_tabs:array
}, function() {
console.log("removed tab");
});
}
chrome.browserAction.onClicked.addListener(function (tab) {`enter code here`
chrome.storage.sync.get({active_tabs : []}, function(data) {
if (data.active_tabs.includes(request.tab_id)) {
removeTab(data.active_tabs, request.tab_id)
console.log("Turned Off ".concat(request.tab_id))
document.removeEventListener("mousemove", highlightCurrentHover, false);
} else {
addTab(data.active_tabs, request.tab_id)
console.log("Turned On ".concat(request.tab_id))
document.addEventListener('mousemove', highlightCurrentHover, false);
}
});
);

Can't use localStorage value in my Google Chrome extension

I have written a Chrome extension that adds a char counter to some input elements. There is no UI for this, it just injects the char counting code if the user is on a specific page.
There is a limit to how many chars should appear in the inputs. I initially wrote it to just display the number of chars but then I decided it would be nice if the user could choose to see how many chars they have left instead. So I decided to create an options page.
At some point in my options page I do this:
localStorage["count"] = count; // count is the string "countUp" or "countDown"
It turns out I can't access the localStorage of options.html from the contentscript.js so I reluctantly created a background.html page which listens to requests from the contentscript and returns the localStorage values that the content script asks for.
In contentscript (simplified)
var countOption;
chrome.extension.sendRequest(
{method: "localStorage", key: "count"},
function(response){
countOption = response.data;
console.log(countOption); // returns "countUp"
}
);
console.log(countOption); // returns undefined
if(countOption === "countUp") {
doSomething();
} else {
doSomethingElse();
}
Background page
<script type="text/javascript">
chrome.extension.onRequest.addListener(
function(request, sender, sendResponse) {
if(request.method === "localStorage") {
sendResponse({data: localStorage[request.key]});
} else {
sendResponse({});
}
}
);
</script>
The problem is I can't assign a value to countOption so that it is accessible in the scope of the rest of my script. When I assign it in the chrome.extension.sendRequest's response this is the window object so I have tried declaring countOption globally but it is still undefined in the rest of my script.
Can anyone see where I'm going wrong? Thanks.
Would something like this work?
function executeCount(countOption)
{
console.log(countOption); // returns undefined
if(countOption === "countUp") {
doSomething();
} else {
doSomethingElse();
}
}
chrome.extension.sendRequest(
{method: "localStorage", key: "count"},
function(response){
executeCount(response.data);
}
);

Categories