Value appears in console.log but is undefined when assigning via innerHTML - javascript

I'm trying to get the timestamp of a YouTube video when a button is clicked on my extension. Right now, my code is setup like so:
contentScript.js
var time;
function get_timestamp() {
time = document.getElementsByClassName('video-stream')[0].currentTime
console.log(time);
return time;
};
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.message === "pause_video") {
pause_video();
}
if (request.message === "get_time") {
sendResponse({timestamp: get_timestamp()})
}
}
);
The pause function is also defined in the same file and works as expected.
popup.js
function get_time() {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {message: "get_time"}, function(response) {
console.log(response.timestamp.toString());
return response.timestamp.toString();
});
})
};
popup2.js
document.getElementById("time").innerHTML = get_time();
The line in popup2.js is in a function that is called when the button is pressed. The script is split into 2 files as part of the testing, but they could be combined if necessary. The rest of the program works as expected, with functions to pause the video and get the videoID from the url working fine. All of the console.log commands show the current timestamp of the video, but I always get undefined when I use innerHTML. I feel like this ought to be simple but I can't figure out where the problem seems to be.

Related

Chrome Extension: Code inside chrome.runtime.onMessage.addListener never gets executed

I'm having a lot of trouble trying to pass a variable from a content script to the popup.js using the sendMessage method.
This is the content script where I'm sending a message to the background script that contains the number of children of a DOM node:
let incomingChatsNumber = incomingChatsContainer.children().length;
chrome.runtime.sendMessage({ incomingChatsNumber: incomingChatsNumber });
Then the background.js listen to the message and send a messag itself with the same variable to the popup.js:
chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
let incomingChatsNumber = message.incomingChatsNumber;
chrome.runtime.sendMessage({ incomingChatsNumber: incomingChatsNumber });
});
In the popup.js I have a button that will trigger some code if the "incomingChatsNumber" is more than 0 (the dom container has children):
$("#js-toggleSorting").on("click", function () {
chrome.runtime.onMessage.addListener(function (message) {
let incomingChatsNumber = message.incomingChatsNumber;
if (incomingChatsNumber <= 0) {
$(".message").html("<p>No Chats To Sort, You Joker</p>");
} else {
$(".message").html("<p>Sorting Chats</p>");
//This code never gets executed
if ($("#js-toggleSorting").attr("data-click-state") == 1) {
$("#js-toggleSorting").attr("data-click-state", 0);
$("#js-toggleSorting").html("SORT INCOMING CHATS");
sortFunction(false);
} else {
$("#js-toggleSorting").attr("data-click-state", 1);
$("#js-toggleSorting").html("STOP SORTING INCOMING CHATS");
sortFunction(true);
}
save_button_state();
}
});
});
The strange thing for me is that the popup.js gets the value right I can even console.log it, but as soon as I put more code on that if conditional the code never gets executed even when the condition is ok to go.
UPDATE:
After doing the modifications suggested by ehab I realized that the variable is "undefined" all the time because of the async nature of chrome.runtime.onMessage.addListener:
Content Script:
let incomingChatsNumber = incomingChatsContainer.children().length;
chrome.runtime.sendMessage({ incomingChatsNumber: incomingChatsNumber });
Background.js:
chrome.runtime.onMessage.addListener(function (message) {
let incomingChatsNumber = message.incomingChatsNumber;
chrome.runtime.sendMessage({ incomingChatsNumber: incomingChatsNumber });
});
Popup.js (This is where I need to use the value of the incomingChatsNumber variable):
let latestIncomingChatsNumber;
chrome.runtime.onMessage.addListener(function (message) {
let incomingChatsNumber = message.incomingChatsNumber;
latestIncomingChatsNumber = incomingChatsNumber;
//This Works
console.log(latestIncomingChatsNumber);
});
//This will give me an undefined value
console.log(latestIncomingChatsNumber);
I get that my problem is related to the fact that the chrome.* APIs are asynchronous so The chrome.runtime... and the console.log(...) will be executed at the same time, hence the error. So how to use the latestIncomingChatsNumber variable inside an on click event then ? Do I have to to save "latestIncomingChatsNumber" to the local storage inside the chrome.runtime.onMessage.addListener first ?
You should not put an event listener inside another event handler, this is an extremely bad practice even if it do what you think it should be doing, this will cause memory leaks for you down the road, and makes the code hard to read and semantically incorrect.
It seems based on looking at your code the problem is that the handler for the message gets called multiple times because of previous event listeners added in the click evet
let latestIncomingChatsNumber
chrome.runtime.onMessage.addListener(function (message) {
let incomingChatsNumber = message.incomingChatsNumber;
// save this into the application state, i will use a parent scope variable you could use something else
latestIncomingChatsNumber = incomingChatsNumber
});
$("#js-toggleSorting").on("click", function () {
if (latestIncomingChatsNumber <= 0) {
$(".message").html("<p>No Chats To Sort, You Joker</p>");
} else {
$(".message").html("<p>Sorting Chats</p>");
//This code never gets executed
if ($("#js-toggleSorting").attr("data-click-state") == 1) {
$("#js-toggleSorting").attr("data-click-state", 0);
$("#js-toggleSorting").html("SORT INCOMING CHATS");
sortFunction(false);
} else {
$("#js-toggleSorting").attr("data-click-state", 1);
$("#js-toggleSorting").html("STOP SORTING INCOMING CHATS");
sortFunction(true);
}
save_button_state(); // i believe this function saves the attributes to the dom elements
}
});
if save_button_state does a good job the code i shared should work

Difficulty passing messages between background and content script in chrome extension

I'm attemtping to send a message from the content script to the background script, and send a response in return.
This is what I've got in my background script:
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
console.log("Message received");
if(request.activeStatusRequest == "disable"){
sendResponse({activeStatusUpdate: "disable"});
}else if(request.activeStatusRequest == "enable"){
sendResponse({activeStatusUpdate: "enable"});
}
return true;
});
This is what I've got in my content script:
printReviews.onclick = function(element) {
if(printReviews.classList.contains("activeButton")){
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.executeScript(
tabs[0].id,
{code: `${injectionLiteralGETURL}`}
);
});
toggleClasses(printReviews, "activeButton", "inactiveButton");
chrome.runtime.sendMessage({activeStatusRequest: "disable"}, function(response){
console.log("response received");
console.log(response.activeStatusUpdate);
});
}
else {
toggleClasses(printReviews, "inactiveButton", "activeButton");
chrome.runtime.sendMessage({activeStatusRequest: "enable"}, function(response){
console.log("response received");
console.log(response.activeStatusUpdate);
});
}
};
The rest of the above code works as expected, except that the none of the console logs in the content script run. The console log in the background script does run, however.
Anyone got any ideas as to what I've done wrong?
If you want to try another method, you could try to add a listener in the content script
Content.js
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
console.log("Message received from background", response.type, response.activeStatusUpdate);
switch(request.type){
case 'SomeFirstAction'
break;
case 'SomeSecondAction'
break;
}
});
Background.js
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
console.log("Message received");
if(request.activeStatusRequest == "disable"){
sendMessageToCurrentTab({type: 'SomeFirstAction', activeStatusUpdate: "disable"});
}else if(request.activeStatusRequest == "enable"){
sendMessageToCurrentTab({type: 'SomeSecondAction', activeStatusUpdate: "enable"});
}
// return true; -> not needed with that method
});
function sendMessageToCurrentTab(message){
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, message);
}
}
To have played with callbacks between popup/background/content-script, I prefer just play with messages,because when using sendResponse, the manner it works, it open a port to communicate, but if something change (tab close, popup close), you will have an error because the port will be closed too.
Using the 'Full send message manner', the background need to clearly target the receiver tab.
I also recommand you to use a type or some unique action attribute, because all listener will listen to all action. It will identify the action and it's purpose.
Here I just used a switch in the content script, but the background could use the same logic.
Maybe it's not the greatest way of doing things, but it works well for me.
Hope this can help you

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.

Passing message from content script in Chrome extensions

I want to run a content script to identify the first image that links somewhere and return the link to the background script. But I can't pass any messages between them, and I don't know why. Much of the code is comment, I'm trying to simply test if the content script is executing by telling it to change the first header to "test" (since the only thing I can do without messages is change a page's HTML and CSS). The code follows:
EDIT: full updated code
Background script:
chrome.browserAction.onClicked.addListener(function () {
chrome.tabs.query({active:true, currentWindow:true}, function(tabs)
{
chrome.tabs.executeScript(tabs[0].id, {file:"content.js"}, function ()
{
console.log("sending first time");
console.log(tabs[0].id);
chrome.tabs.sendMessage(tabs[0].id,{test: "test"}, function (response)
{
if (undefined == response)
{
console.log ("first one didn't work");
chrome.tabs.sendMessage(tabs[0].id, {test: "test"}, function (response)
{
console.log(response.test);
});
}
});
});
});
});
Content Script:
chrome.runtime.onMessage.addListener( function (msg, sender, sendResponse)
{
/*var test1 = document.getElementsByTagName("h1")[0];
test1.innerHTML="test";*/
sendReponse({test: "123"});
/*var parentList =document.getElementsByTagName("a");
var link = "";
var child;
for (var i=2; i<parentList.length;i++)
{
child=parentList[i].firstChild;
if ("IMG"==child.nodeName)
{
link=parentList[i].href;
break;
}
}
teste1.innerHTML=link;
var answer = new Object();
Answer.link = link;
teste1.innerHTML=answer.link;
sendResponse({messager:answer});*/
});
I was testing the extension at the extension page itself. But it seems you can't inject javascript in it (probably because it is a browser's settings page). It worked nicely on other pages.

Chrome Extension Development : Message passing Problem

Having a problem while passing messages using content scripts in Google chrome extension dev
My Code structure looks like this:
popup.html:
var oList;
function getHTML()
{
chrome.tabs.getSelected(null, function(tab) {
chrome.tabs.sendRequest(tab.id, {action:"getHTML"}, function handler(response) {
oList = response.dom;
});
});
alert("oList = "+oList );
}
and my content Script looks like this:
chrome.extension.onRequest.addListener(
function(request, sender, sendResponse) {
if(request.action == "getHTML"){
sendResponse({dom: document.getElementsByTagName("HTML").length});
}
});
When I debug my code by putting a breakpoint at "oList = response.dom;" in my popup.html, i get the
right value set from the content script. But while executing the extension, the "alert("oList = "+oList );" code
from the popup.html seems to be executing first before it goes to the server.. And therefore, its value is
not being set.. Can somebody tell me if I am wrong somewhere ?
Most Chrome API methods are asynchronous. It means that when you are calling them the script doesn't wait for their response and simply continues executing. If you want to execute something on response you should put it inside callback function:
chrome.tabs.getSelected(null, function(tab) {
chrome.tabs.sendRequest(tab.id, {action:"getHTML"}, function handler(response) {
oList = response.dom;
alert("oList = "+oList );
});
});

Categories