Asynchronous Message Sending In Google Chrome Extension Popup - javascript

I have been researching asynchronous function calls and how to set variables to values from them and have had a lot of trouble with it. I want to create a port to message with my file content.js on the extension.
To do this I need to receive the tab of the window that I have open, and use its attribute id in the chrome.tabs.connect() function.
This implementation fails to reach any console.log() calls but I don't understand asynchronous programming well enough to understand. Can anyone help with this? My problem is that the two files aren't communicating, so the port isn't opening.
<script language = "Javascript">
function foo(callback){
var port = chrome.tabs.connect(chrome.tabs.query({ currentWindow: true, active: true }, function(tabs) {
//sets curTab to current tab
console.log(tabs[0]);
var curTab;
setTimeout(function(tabs) {
curTab = tabs[0];
console.log(curTab);
}, 5000);
}),{name: "mode"})
//both files are preset to this so no need to message
var mode = "on";
document.getElementById("stop").onclick = function(){
if(mode === "off")
mode = "on";
else
mode = "off";
setMode();
console.log("clikityclik");
};
console.log(mode);
function setMode(){
/*sends message to port*/
if(port)
port.postMessage({newMode: mode});
else{
console.log("error: port not created");
}
}
}
the relevant code from my content.js file is below. I call this function once
function getMode(){
/*receives message from port
**communicates with sandboxed.html
*/
chrome.runtime.onConnect.addListener(function(port) {
console.assert(port.name == "mode");
port.onMessage.addListener(function(msg) {
if (msg.newMode){
mode = msg.newMode;
console.log("Mesage received: "+mode);
}
else
console.log("error receiving new mode, last mode was: " + mode);
});
});
}
The goal of this program is for the first script to send messages to the content.js when a div is clicked and for the 'content.js' file to receive those messages whenever they are sent.

the solution was to make a function that creates the port, with an input of tab. Basically, getTab will try to get the the value of the tab, and if we do have a tab it will run the callback function which is createPort which contains the information necessary to create the port and also the event handler for the click on the div.
function getTab(callback){
chrome.tabs.query({ currentWindow: true, active: true }, function(tabs) {
console.log(tabs[0]);
callback(tabs[0]);
});
}
function createPort(tab){
var port = chrome.tabs.connect(tab.id,{name: "mode"});
document.getElementById("stop").onclick = function(){
if(mode === "off")
mode = "on";
else
mode = "off";
setMode(port);
console.log("clikityclik");
};
}
var mode = "on"; //mode is preset to on in content.js
getTab(createPort);

Related

Chrome extension postMessage with user action

I created long-lived connection between popup and content pages. It works successfully. But I want to send message to content when user click the button.
contentscript.js
console.log("content script loaded!");
var port = chrome.runtime.connect({name: "content-script"});
port.onMessage.addListener(function(message){
if(message.key=="color"){
document.body.style.backgroundColor='lightgrey';
}
else if(message.key=="color2"){
document.body.style.backgroundColor='blue';
}
});
popup.js
document.addEventListener('DOMContentLoaded', function() {
var checkPageButton = document.getElementById('btnStart');
checkPageButton.addEventListener('click', function() {
GetImages("");
}, false);
}, false);
function GetImages(pageURL){
if(activePort!=null){
activePort.postMessage({key:"color2"});
}
}
var activePort=null;
chrome.runtime.onConnect.addListener(function (port) {
console.assert(port.name == "content-script");
activePort=port;
port.onMessage.addListener(function(message) {
if(message.key=="download"){
port.postMessage({key:"download", text: "OK"});
}
else if(message.key="load"){
port.postMessage({key:"color"});
console.log(message.text);
}
})
});
In the GetImages() function I try to
port.postMessage({key:"color2"});
naturally it can't find the "port". So I create the activePort variable and try to post message with it. But It didn't work properly.
I changed the codes below and it works correctly. Now I opened the port in popup.js and I can send message with button click.
newpopup.js
function GetImages(pageURL){
port.postMessage({text:"ok"});
}
var port;
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
port = chrome.tabs.connect(tabs[0].id,{name: "knockknock"});
});
newcontentscript.js
chrome.runtime.onConnect.addListener(function(port) {
console.assert(port.name == "knockknock");
port.onMessage.addListener(function(msg) {
console.log(msg);
});
});

Chrome extension message passing & order of execution [duplicate]

This question already has an answer here:
Clipboard Copy / Paste on Content script (Chrome Extension)
(1 answer)
Closed 6 years ago.
Quick summary
The code below is a file called popup.js. It listens for a click, and sends a message to background.js. Background.js executes another script, and a variable is created.
I somehow need to pass this variable back to popup.js, and continue within the userHasClicked function. The way it is now the response I get is "undefined", and there's nowhere to go from there.
var theParent = document.querySelector("#MENY");
theParent.addEventListener("click", userHasClicked, false);
function userHasClicked(e) {
if (e.target !== e.currentTarget) {
var clickedItem = e.target.id;
chrome.runtime.sendMessage({type: "ResponseType", directive: clickedItem}, function(response) {
console.log(response);
this.close();
});
};
e.stopPropagation();
}
Routine:
User clicks on an option in popup.html
Event("click") -> sendmessage("type of click")
background.js listens for the message, and executes content.js
content.js creates the variable and can send it back to anyone who listens.
The problem:
The variable must come as a response argument to step 2 (within eventloop)
Save the response as variable.
document.execCommand("copy").
Done
This code below is the relevant part of background.js.
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
switch (request.type) {
case "ResponseType":
var LoggType = request.directive;
console.log(LoggType)
chrome.tabs.executeScript(null, {
code: 'var LoggType = "'+LoggType+'";'
}, function() {
chrome.tabs.executeScript(null, {file:"content.js"});
});
chrome.runtime.onMessage.addListener(function(req, snd, sndRes) {
if (req.type = "LogIsGenerated") {
var Logg = req.directive;
console.log(Logg);
} sndRes({});
});
if (typeof Logg !== "undefined") {
alert("Feedback from content.js received");
sendResponse({type: "FinalVar", directive: Logg});
}
else {
alert("No feedback received");
sendResponse({});
};
break};
return true;
}
);
content.js ends with this line.
chrome.extension.sendMessage({type: "LogIsGenerated", directive: Logg});
Logg is the variable containing the text string I want to add to the clipboard.
At the moment this the code below alerts for No feedback received.
chrome.runtime.onMessage.addListener(function(req, snd, sndRes) {
if (req.type = "LogIsGenerated") {
var Logg = req.directive;
console.log(Logg);
} sndRes({});
});
if (typeof Logg !== "undefined") {
alert("Feedback from content.js received");
sendResponse({type: "FinalVar", directive: Logg});
}
else {
alert("No feedback received");
sendResponse({});
};
Rob W presented a solution here: Background script can write to clipboard in a very simple manner
Worked excellently.

how to get background.js to change based on options.js

I am currently making a Google Chrome extension, and in the options I want the user to be able to choose between it being always on or only activating when clicked. To do this, I need an options.js and a background.js file, which I have. However, I am having a lot of trouble getting them to communicate properly. I tried using the chrome.storage api, but it won't do anything.
Here is my code for background.js:
chrome.browserAction.onClicked.addListener(function() {
// Send a message to the active tab
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {"message": tabs[0].url}, function(response));
});
});
chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
if (changeInfo.status == 'complete') {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs){
// console.log(tabs.length);
chrome.tabs.sendMessage(tabs[0].id, {"message": tabs[0].url}, function(response) {});
});
}
});
And here is my code for options.js:
// Saves options to chrome.storage
function save_options() {
var behavior = document.getElementById('behavior').value;
chrome.storage.sync.set({
extensionBehavior: behavior
}, function() {
// Update status to let user know options were saved.
var status = document.getElementById('status');
status.textContent = 'Options saved!';
setTimeout(function() {
status.textContent = '';
}, 1000);
});
}
// Restores select box and checkbox state using the preferences
// stored in chrome.storage.
function restore_options() {
// Use default value color = 'red' and likesColor = true.
chrome.storage.sync.get({
extensionBehavior: 'onClick'
}, function(items) {
document.getElementById('behavior').value = items.extensionBehavior;
});
}
document.addEventListener('DOMContentLoaded', restore_options);
document.getElementById('save').addEventListener('click',
save_options);
If the behavior is set to "onClick", I only want the chrome.browserAction.onClicked.addListener portion to be executed. If the behavior is set to 'alwaysOn', then I only want the chrome.tabs.onUpdated.addListener portion to be executed. As far as debugging goes, both of those chunks work the way they're supposed to. I just need to know how to get one or the other to run based on the current options.
For the communication between option and background, it would be quite easy when you choose the localStorage to pass info between them. http://www.w3schools.com/html/html5_webstorage.asp

Refresh after a timer of 5000 seconds and print the alert

I am making a chrome extension to keep refreshing a page unless stop button is chosen. But i am able to do it only once. Here is my code for background.js
chrome.extension.onMessage.addListener(function(request, sender, sendResponse) {
switch(request.type) {
case "table-row-count_start":
alert("Refershing started");
RefreshAndCount();
break;
case "table-row-count_stop":
alert("Stop Refershing");
break;
}
return true;
});
var RefreshAndCount = function() {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {type: "table-row-count"});
chrome.browserAction.setBadgeText({tabId: tabs[0].id, text: "Counting!"});
});
};
In content.js I did this :
chrome.extension.onMessage.addListener(function(message, sender, sendResponse) {
alert(message.type);
switch(message.type) {
case "table-row-count":
var x = document.querySelector('table').rows.length;
chrome.storage.sync.set({'value': x}, function() {
console.log('Settings saved');
});
chrome.storage.sync.get(["value"], function(items){
console.log(items);
});
alert("Row count = " + x);
setTimeout(function(){
location.reload();
},100);
break;
}
});
chrome.storage.onChanged.addListener(function(changes, namespace) {
for (key in changes) {
if(key=='value'){
var storageChange = changes[key];
console.log('Storage key "%s" in namespace "%s" changed. ' +
'Old value was "%s", new value is "%s".',
key,
namespace,
storageChange.oldValue,
storageChange.newValue);
}
}
});
After refresh I want to print the current row count alert everytime. Please help how to do this .
This work fine, for a single refresh but after that I again had to choose the start button from popup.
I want some way that I need not click start button again and the whole process repeats, storing the previous row count in cache or something.
popup.js
window.onload = function() {
document.getElementById("mystartbutton").onclick = function() {
chrome.extension.sendMessage({
type: "table-row-count_start"
});
}
document.getElementById("mystopbutton").onclick = function() {
chrome.extension.sendMessage({
type: "table-row-count_stop"
});
}
}
Also help me How to keep on refershing that page even if I switch to other tab or minimise my chrome ?
You can use the chrome.storage.local to store data that will be saved over time and over context where you use it. You can set a boolean to true or false to enable or disable autoreload. Then you only have to set it at click on browser action and check it in the content-script to know if you have to reload.
A possible and simple implemtation should be like this : (It depends of the expected behavior)
content.js (have to be injected in the page to autoreload)
var reloadDuration = 5000;
function autoreload()
{
chrome.local.storage.get("autoreload_enabled", function(result)
{
if(result.autoreload_enabled)
{
chrome.runtime.sendMessage({type: "table-row-count"});
window.location.reload();
}
}
}
setTimeout(autoreload, reloadDuration);
This will reload your page every reloadDuration if the boolean set in chrome local storage named autoreload_enabled is true.

How to check a "sync" value in an async function?

I try to make some changes on a Chrome extension. I need the extension checks the value and if it is true, then execute a script. If false, then do nothing. I wrote something like this:
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
if(localStorage["statusOfSomething"]){
chrome.tabs.executeScript(tabId, {file: "file.js" ,runAt:'document_end'});
}
});
But this takes the initial value of localStorage["statusOfSomething"] always. So there is an async function to register a listener for "onUpdated". But I need to check the localStorage["statusOfSomething"] value of "now", not the value of the time by registering the listener.
How can I do this?
Edit:
Actually I was trying to check two things:
if the website is in the site list of extension
if this website is enabled for the extension
Now here the full story;
There are some websites, I defined them on background.js file. Let's say;
a.com, b.com ... etc.
var sites = [{
name : "a",
wildcard : ["*://a.com/*"],
js : "a.js"
},{
name : "b",
wildcard : ["*://b.com/*"],
js : "b.js"
}]
and there are statuses of the sites (enable/ disable; true/false)
I think it was good to store statuses by localStorage, so I write as initial value true:
for(var i = 0; i<sites.length; i++){
localStorage[sites[i].name] =true;
}
As option; it was needed to addListener for changes:
chrome.extension.onMessage.addListener(function(request, sender, sendResponse) {
if(request.method == "setSite"){
var name = request.site;
var status = request.active;
localStorage[name] = status;
}
return true;
});
If user checks the checkbox for status option of the website, then options.js sends message:
$('#' + name).change(function(){
var status = $(this).is(':checked');
chrome.extension.sendMessage({method: "setSite", site: this.id, active: status}, function(response) {
console.log(response.data);
});
});
Now, back to background.js , for every update of the tab, I need to check these two things together:
1. am I interested in this website?
2. is it enable for me now?
So I wrote:
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
if (changeInfo.status === 'complete') {
var url = tab.url;
for(var i = 0; i<sites.length; i++){
var site = sites[i];
var name = site.name;
var wildcard = site.wildcard;
if(localStorage[name] && testUrl(url,wildcard)){
chrome.tabs.executeScript(tabId, {file: site['js'] ,runAt:'document_end'});
break;
}
}
}
});
Here, I get the value of localStorage[sitename] true
If you are more interested, you can see the code on github (the version)

Categories

Resources