chrome.runtime.sendMessage not working with Asynchronous calls from Multiple Tabs - google-chrome-extension

I have a popup with a button like shown below
I am having a click function like below in popup.js
$('#fetch_article').on('click',()=>{
chrome.tabs.query({currentWindow: true, active: true}, function(tabs){
const url=tabs[0].url;
chrome.storage.local.get(['session_articles'],(data)=>{
let articles=data.session_articles || [];
if(articles.length<=0 && Object.keys(articles).length === 0){
articles.unshift({url:url,state:'Fetching',data:null});
fetcArticles(url);
}else{
// Check if URL already exists
const linkFound = articles.find(link=>link.url===url);
if(linkFound===undefined){
articles.unshift({url:url,state:'Fetching',data:null});
fetcArticles(url);
}
}
chrome.storage.local.set({'session_articles':articles}, function() {
refreshArticles();
});
});
});
})
function fetcArticles(url){
chrome.runtime.sendMessage({type: "fetch_article",url:url},(response)=>{
if(response.type==='success'){
chrome.storage.local.get(['session_articles'],(data)=>{
let articles=data.session_articles;
const linkIndex = articles.findIndex(link=>link.url===response.data.url);
if(linkIndex!==-1){
articles[linkIndex].state='Done';
articles[linkIndex].data={summary:response.data.summary,title:response.data.title};
chrome.storage.local.set({'session_articles':articles}, function() {
refreshArticles();
});
}else{
console.error("Error in Fetching articles from session");
}
});
}else{
console.error("Could not Fetch article, So deleting it from storage");
removerArticleFromStorage(url);
}
});
}
In Backgroun.js
chrome.runtime.onMessage.addListener( (request, sender, sendResponse) => {
switch (request.type) {
case "fetch_article":
$.ajax({
type: "POST",
url: "http://someajaxurl.com/method",
data: JSON.stringify({url:request.url}),
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function(response){
if(response.type==='error'){
sendResponse({type:'error',msg:response.msg});
}else{
response.data.url=request.url;
sendResponse({type:'success',data:response.data});
}
}
});
return true;
break;
default:
sendResponse({type:'error',msg:'Nothing Found Here...'});
break;
}
});
This works all good if I click the fetch article button and stays in that tab, but if I click the fetch article button in Tab1 and Move to Tab2 The fetcArticles callback is not working expectedly. What I am doing wrong here.

Related

How can activate chrome.runtime.onMessage.addListener for a specific tab?

I am creating a chrome extension and I am using Message Passing for this process. The process is this:
1.- popup.js send a request to background when I clicked on an element of popup.
function sendMessageToContent(contactName){
chrome.runtime.sendMessage({elementValue: contactName}, function(response) {
console.log(response.farewell);
});
}
2.- background.js listens the request.
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if(request.elementValue != null){
var accessToken = localStorage.getItem('accessToken');
var instanceUrl = localStorage.getItem('instanceUrl');
getContactByName(accessToken,instanceUrl,request.elementValue);
sendResponse({farewell: "ContactResponse"});
}
});
and it sends another request to content.js.
function getContactByName(accessToken,instanceUrl,contactName){
var urlQuery = 'query';
fetch(instanceUrl+urlQuery,{
method: 'get',
headers: new Headers({
'Authorization': 'Bearer '+accessToken,
'Content-Type': 'application/json'
})
})
.then(response => {
if(response.status != 200){
showAuthNotification();
}
return response.json()
})
.then(data => {
for(var i = 0; i<data.totalSize; i++){
contact.push({name:data.records[i].Name, email:data.records[i].Email, id:data.records[i].Id});
}
localStorage.setItem('contactInfo', JSON.stringify(contact));
chrome.tabs.query({}, function (tab) {
chrome.tabs.update(tab[4].id, {active: true});
chrome.tabs.sendMessage(tab[4].id, {message: "OK"});
}
});
})
.catch(function (error) {
console.log('Request failure: ', error);
})
}
3.- The problem is in this step, after the background's request is sent it should open a specific tab and the listener on the content.js should take the request. this process only works when I am in the current tab, when I active the other tab using chrome.tabs.update(tab[4].id, {active: true}); the listener
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
console.log(request.message);
});
doesn't listen the request.
How could I do that the listener is active for all requests or for a specific tab not just for the current tap?

Obtain the tabId of new created tab

How do I get the tabId of a new created tab? My tabId I want to send in sendMessage is undefinded.
How do I get the tabId?
Later I need the tabId to update the tabs....
popup.js
$(document).ready(function () {
"use strict";
$('button').on('click', function () {
chrome.tabs.create({
url: "http://google.de"
}, function(tab) {
});
chrome.runtime.sendMessage({content: tabId, type:'urlDe'});
window.close();
});
chrome.tabs.create({
url: "http://google.com"
}, function(tab) {
});
chrome.runtime.sendMessage({content: tabId, type:'urlCom'});
window.close();
});
});
background.js
chrome.runtime.onMessage.addListener(function(request) {
if (request.type === 'urlDe') {
alert(request.type + request.content);
} else if (request.type === 'urlCom') {
alert(request.type + request.content);
}
});
You need to access to the tabId via tab.id in your callback function, because chrome.tabs.create is async function:
chrome.tabs.create({
url: "http://google.de"
}, function(tab) {
chrome.runtime.sendMessage({content: tab.id, type:'urlDe'});
});
});
More information are in documentation or in this question.

How to make something like remote method on loop-back middleware?

In fallowing code i want to make something like remote method on middleware in loopback to post values to calculate for example:
in app :
submitForm() {
let headers = new Headers(
{
'Content-Type': 'application/json',
'Accept': 'application/json'
});
let options = new RequestOptions({ headers: headers });
let data = JSON.stringify({
Value1: this.form.value1,
Value2: this.form.value2,
Value3: this.form.value3
});
console.log(data);
let url = 'http://localhost:3000/calculate';
console.log(url);
return new Promise((resolve, reject) => {
this.http.post(url, data, options)
.toPromise()
.then((response) => {
console.log('API Response : ', response.status);
resolve(response.json());
})
.catch((error) => {
console.error('API Error : ', error.status);
console.error('API Error : ', JSON.stringify(error));
reject(error.json());
});
});
}
and in remote method or anything like that, I used such this code but totally fails:
module.exports = function () {
accepts: [{arg: 'val1', type: 'number'},{arg: 'val2', type: 'number'}],
returns: {arg: val1+val2, type: 'number'},
http: {path: '/calculate', verb: 'get'}
});
};
Example remote method that I used correctly
module.exports = function (TeamRole) {
TeamRole.getUsers = function (id, cb) {
TeamRole.find({
where: {
teamId: id
}
}, function (err, users) {
cb(null, users);
});
};
TeamRole.remoteMethod('getUsers', {
accepts: {
arg: "id",
type: "string",
required: true
},
returns: {
arg: 'users',
type: 'Array'
},
http: {
path: '/:id/users',
verb: 'get'
}
});
}
As above example you can define remote method correctly to achieve you task.
cheers.
This is my solution for my problem:
As you can see there is no parameters shown on URL and i think this may be secure I'm not expert one but I guess help to you:
module.exports = function(server) {
const https = require('https');
var request = require('request');
return function verification(req, res, next) {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
res.setHeader('Access-Control-Allow-Credentials', true);
var request;
var response;
var body = '';
// When a chunk of data arrives.
req.on('data', function (chunk) {
// Append it.
body += chunk;
});
// When finished with data.
req.on('end', function () {
// Show what just arrived if POST.
if (req.method === 'POST') {
console.log(body);
}
// Which method?
switch (req.method) {
case 'GET':
Verify url and respond with appropriate data.
handleGet(req, res);
Response has already been sent.
response = '';
break;
case 'POST':
// Verify JSON request and respond with stringified JSON response.
response = handlePost(body);
break;
default:
response = JSON.stringify({ 'error': 'Not A POST' });
break;
}
// Send the response if not empty.
if (response.length !== 0) {
res.write(response);
res.end();
}
// Paranoid clear of the 'body'. Seems to work without
// this, but I don't trust it...
body = '';
});
// If error.
req.on('error', function (err) {
res.write(JSON.stringify({ 'error': err.message }));
res.end();
});
//
};
function handlePost(body) {
var response = '';
var obj = JSON.parse(body);
// Error if no 'fcn' property.
if (obj['fcn'] === 'undefined') {
return JSON.stringify({ 'error': 'Request method missing' });
}
// Which function.
switch (obj['fcn']) {
// Calculate() requres 3 arguments.
case 'verification':
// Error if no arguments.
if ((obj['arg'] === 'undefined') || (obj['arg'].length !== 3)) {
response = JSON.stringify({ 'error': 'Arguments missing' });
break;
}
// Return with response from method.
response = verification(obj['arg']);
break;
default:
response = JSON.stringify({ 'error': 'Unknown function' });
break;
}
return response;
};
function verification(arg) {
var n1 = Number(arg[0]);
var n2 = Number(arg[1]);
var n3 = Number(arg[2]);
var result;
// Addem up.
result = n1 + n2 + n3;
// Return with JSON string.
return JSON.stringify({ 'result': result });
};
};

Parse Server Cloud Code - Update OR Create object

I am using Parse Server on AWS and mLab with great success, except for my Cloud Code. The main issue is surrounding my previous code for Create OR Update an object. I used to do this by querying for a user pointer on the Favourites class. If a row contains a user pointer then I need to update its content, if it doesn't exist a row needs to be created.
Old Parse.com Code
Parse.Cloud.define("saveFavourites", function(request, response) {
console.log(request.params.favourites);
var Favourites = Parse.Object.extend("Favourites");
var query = new Parse.Query("Favourites");
query.equalTo('user', request.user);
query.first({
success: function(results) {
console.log(JSON.stringify(results));
console.log(results)
if (results === undefined) {
var favourites = new Favourites();
favourites.save({
user: request.user,
favourites: request.params.favourites
}, {
success: function(favourites) {
// The object was saved successfully.
},
error: function(favourites, error) {
// The save failed.
// error is a Parse.Error with an error code and message.
}
});
} else {
results.set("favourites", request.params.favourites);
results.set("userId", request.user.id);
results.save();
}
response.success(results);
},
error: function(error) {
error.message("favourites lookup failed");
}
});
});
New Parse Server Code
Parse.Cloud.define("saveFavourites", function(request, response) {
console.log('user is : ' + JSON.stringify(request.user));
var Favourites = Parse.Object.extend("Favourites");
var query = new Parse.Query("Favourites");
query.equalTo("user", request.user);
query.first({
useMasterKey: true
}, {
success: function(results) {
if (results && results.length > 0) {
console.log('running found');
favourites.set("favourites", request.params.favourites);
favourites.set("userId", request.user.id);
favourites.save();
response.success();
} else {
var favourites = new Favourites();
favourites.set("user", request.user);
favourites.set("favourites", request.params.favourites);
favourites.set("userId", request.user.id);
favourites.save();
response.success();
}
},
error: function(error) {
console.log(error.message);
}
});
});
Do not response unless callback finished. Set response.error on each Parse requests error.
Parse.Cloud.define("saveFavourites", function(request, response) {
console.log(request.params.favourites);
var Favourites = Parse.Object.extend("Favourites");
var query = new Parse.Query("Favourites");
query.equalTo('user', request.user);
query.first({
//is this query need masterKey?
useMasterKey: true,
success: function(results) {
console.log(JSON.stringify(results));
console.log(results)
if (results === undefined) {
var favourites = new Favourites();
favourites.save({
user: request.user,
favourites: request.params.favourites
}, {
success: function(favourites) {
// The object was saved successfully.
response.success(results);
},
error: function(favourites, error) {
// The save failed.
// error is a Parse.Error with an error code and message.
response.error(error);
}
});
} else {
results.set("favourites", request.params.favourites);
results.set("userId", request.user.id);
results.save(null, { useMasterKey: true }).then(response.success, response.error);
}
},
error: function(error) {
error.message("favourites lookup failed");
response.error(error);
}
});
});

passing massage from injected js to background.js in chrome extesion

I have a problem with passing data from injected js to background.js
injected js name "getDOM.js" and injected with this code in background.js
chrome.contextMenus.onClicked.addListener(function(info, tab){
chrome.tabs.executeScript(tab.id, {file: "getDOM.js"})
});
i want to pass data from getDOM.js to background.js, so in background.js there is a function that will be ran when context menu clicked
this is the function in background.js :
function SetLinks(info, tab) {
chrome.extension.sendMessage({greeting: "GetURL"},
function (response) {
tabURL = response.navURL;
alert(tabURL);
});
}
as you can see i tried to send a massage and want to get tabURL (this is just for test)
in getDOM.js file i've wrote this code :
chrome.extension.onMessage.addListener(function(request,sender,sendResponse)
{
if( request.greeting === "GetURL" )
{
sendResponse( {navURL:'test'} );
}
});
so the navURL must be returned. but it not working
it works reverse, i mean if i was trying for sending request "from" getDOM.js it will work. it seams the request function should be in getDOM.js otherwise it will not work. please help me
I also tried it this way :
background.js
function SetLinks(info, tab) {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {greeting: "GetURL"}, function(response) {
alert(response.navURL);
});
});
}
chrome.contextMenus.onClicked.addListener(SetLinks);
// Set up context menu tree at install time.
chrome.runtime.onInstalled.addListener(function() {
// Create one test item for each context type.
var contexts = ["page","selection","link","editable","image","video",
"audio"];
var context = contexts[1];
var title = "test";
var id = chrome.contextMenus.create({"title": title, "contexts":[context],
"id": "context" + context});
});
chrome.contextMenus.onClicked.addListener(function(info, tab){
chrome.tabs.executeScript(tab.id, {file: "getDOM.js"})
});
getDOM.js
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.greeting === "GetURL")
sendResponse({navURL:'test'});
});
but still not working
Sending a request from the extension/background.js to a content script that you need to specify which tab to send it to. https://developer.chrome.com/extensions/messaging.
I created the sample code and tested it works with me as below:
Background.js:
chrome.contextMenus.create({"title": "sample text", "contexts":["page"],
"id": "ViewAlert"
});
function contextClicked(info, tab) {
if (info.menuItemId == "ViewAlert" ) {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {greeting: "GetURL"}, function(response) {
alert(response.navURL);
});
});
}
};
chrome.contextMenus.onClicked.addListener(contextClicked);
Content.js:
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.greeting === "GetURL")
sendResponse({navURL:'test'});
});
Also keep in mind to add "permissions": ["contextMenus"...] in your manifest.
i have found the solution
background.js
chrome.contextMenus.onClicked.addListener(function(info, tab){
chrome.tabs.executeScript(tab.id, {file: "getDOM.js"})
});
chrome.contextMenus.onClicked.addListener(function(info, tab){
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {greeting: "GetURL"}, function(response) {
alert(response.navURL);
});
});
});
getDOM.js
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.greeting === "GetURL")
sendResponse({navURL:'test'});
});

Resources