How to get index of iframe in chrome extension - google-chrome-extension

I'm working on an extension that collects clicks from a page.
I've a script that on each click report back (to the background.js) the x/y coordinates of the click. I'm not sure on how to get the index of the iframe that send the message back to the background.js.
I know selenium IDE/Katalon.. does it, but I'm not sure how they managed to do that.
Any idea is welcomed, thanks.
manifest.json:
{
"name": "example",
"version": "0.0.1",
"manifest_version": 2,
"description": "testi",
"homepage_url": "http://woop.weep",
"background": {
"scripts": [
"background.js"
],
"persistent": true
},
"content_scripts": [{
"matches": [
"http://*/*",
"https://*/*"],
"js": [
"content_frame.js"
],
"match_about_blank": true,
"all_frames": true
}],
"browser_action": {
"default_title": "woop!"
},
"permissions": [
"https://*/*",
"http://*/*",
"tabs",
"system.display"
]
}
background.js:
console.log("BACKGROUND");
let data = {baseUrl: false, actions: [], display: false};
chrome.browserAction.onClicked.addListener(function (tab) {
chrome.tabs.executeScript(tab.id, {
file: 'inject.js'
});
});
chrome.tabs.onActivated.addListener( function(activeInfo) {
console.log("Changed tab to ",activeInfo);
console.log("got recorded user action");
chrome.tabs.query({active: true, currentWindow: true}, function(activeTab){
console.log("activeTab: ",activeTab);
data.actions.push({type:"userAction", action: "switchTab", tabUrl: activeTab[0].url, tabIndex: activeTab[0].index});
console.log("data.actions after switch tab: ",data.actions);
});
});
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
console.log("request: ",request);
console.log(sender.tab ?
"from a content script:" + sender.tab.url :
"from the extension");
if (request.type == "userAction"){
console.log("got recorded user action");
if (request.action=="click"){
let lastAction = data.actions[data.actions.length-1];
console.log("PRE EPOCH CHECK");
console.log("request: ",request);
console.log("lastAction: ",lastAction);
if (lastAction && request.epoch -lastAction.epoch< 2){
console.log("Got a double click, merge previous event with the new one");
data.actions[data.actions.length-1].action = "doubleClick";
request = data.actions[data.actions.length-1];
}
else{
data.actions.push(request);
}
}
console.log("collected data: ",data);
sendResponse({feedback: request, data: data});
}
});
content_script:
function sendMessageToExtension(messageObj){
chrome.runtime.sendMessage(messageObj , function (response) {
if (response) {
console.log("got a response: ",response);
} else {
console.log("no response from extension");
};
});
}
console.log("WOOOOOOOOOOOP "+ window.location.href);
(function() {
document.onmousemove = handleMouseMove;
function handleMouseMove(event) {
var eventDoc, doc, body;
event = event || window.event; // IE-ism
// If pageX/Y aren't available and clientX/Y are,
// calculate pageX/Y - logic taken from jQuery.
// (This is to support old IE)
if (event.pageX == null && event.clientX != null) {
eventDoc = (event.target && event.target.ownerDocument) || document;
doc = eventDoc.documentElement;
body = eventDoc.body;
event.pageX = event.clientX +
(doc && doc.scrollLeft || body && body.scrollLeft || 0) -
(doc && doc.clientLeft || body && body.clientLeft || 0);
event.pageY = event.clientY +
(doc && doc.scrollTop || body && body.scrollTop || 0) -
(doc && doc.clientTop || body && body.clientTop || 0 );
}
window.cordX = event.pageX;
window.cordY = event.pageY;
// console.log(event.pageX+","+event.pageY)
// Use event.pageX / event.pageY here
}
})();
function eventClick(evt) {
console.log("evt: ",evt);
if (!window.cordsForAction){
window.cordsForAction = {};
};
let epoch = Math.round((Date.now()/1000));
console.log("clicked at: " + window.cordX + "," + window.cordY + " at location: " + window.location+ ", epoch: "+epoch);
let cords = {"x": window.cordX, "y": window.cordY};
if (window.cordsForAction[epoch]){
}
else{
window.cordsForAction[epoch] = cords;
sendMessageToExt("click", cords, epoch);
delete window.cordsForAction[epoch];
};
}
async function sendMessageToExt(actionType, cords, epoch){
chrome.runtime.sendMessage({"type": "userAction" ,"action": actionType, cords: cords, epoch: epoch}, function (response) {
if (response) {
console.log("got a response");
if (response.feedback) {
console.log("response.feedback:", response);
}
} else {
console.log("no response from extension");
};
});
}
function setDocOnClickEvent(){
window.cordsForAction = [];
document.querySelectorAll("*").forEach((el)=>{ el.onclick = eventClick});
console.log("setDocOnClickEvent");
}
function setChromeOnMessage(){
console.log("setChromeOnMessage");
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
console.log(sender.tab ?
"from a content script:" + sender.tab.url :
"from the extension");
if (request.type == "dataFromExtensionForPlay") {
console.log("got the message for dataFromExtensionForPlay, request.data: ",request.data);
};
});
}
function addRuntimeOnConnectListener(){
console.log("addRuntimeOnConnectListener");
chrome.runtime.onConnect.addListener(port => {
console.log('connected ', port);
});
}
addRuntimeOnConnectListener();
setDocOnClickEvent();
setChromeOnMessage();

Related

Loading jquery in chome extension service worker manifest v3

I am having a extension where it can notify about new items added to RSS feed reader
All works in v2, but v3 I am unable to load jquery into service worker, since chrome doesnt allow.
As a workaround I have added as module
"background": {
"service_worker": "js/background.js","type": "module"
},
But still its a issue and says ReferenceError: $ is not defined
at Object.parseFeed
Or is there a way I can tweak my code to read xml without jquery?
import $ as module from 'js/jquery-2.1.4.min.js';
var Storage = (function() {
return {
getFeeds: function(callback, returnArray, sortArray) {
returnArray = typeof returnArray !== 'undefined' ? returnArray : true;
sortArray = typeof sortArray !== 'undefined' ? sortArray : true;
chrome.storage.sync.get(function(dataObject) {
var result = dataObject;
if (returnArray) {
var feedArray = this.parseDataObjectIntoArray(dataObject);
result = sortArray ? this.sortArrayOfObjects(feedArray, 'position') : feedArray;
} else {
delete result['RssR:Settings'];
}
callback(result)
}.bind(this));
},
getFeedByUrl: function(feedUrl, callback) {
chrome.storage.sync.get(feedUrl, function(feedData) {
callback(feedData[feedUrl]);
});
},
removeFeedByUrl: function(feedUrl) {
chrome.storage.sync.remove(feedUrl);
},
saveFeedData: function(feedData) {
var saveFeed = {};
saveFeed[feedData.url] = feedData;
this.setDataObject(saveFeed);
},
parseDataObjectIntoArray: function(object) {
var array = [];
Object.keys(object).forEach(function(objectKey) {
if (objectKey.indexOf('RssR:Settings') !== 0) {
array.push(object[objectKey]);
}
});
return array;
},
sortArrayOfObjects: function(array, sortKey) {
array.sort(function(a, b) {
if (typeof a[sortKey] === 'undefined') {
return true;
} else if (typeof b[sortKey] === 'undefined') {
return false;
}
return a[sortKey] - b[sortKey];
});
return array;
},
clearAllData: function() {
chrome.storage.sync.clear();
},
setDataObject: function(dataObject) {
chrome.storage.sync.set(dataObject);
}
}
}());
Array.prototype.max = function() {
return Math.max.apply(null, this);
};
function msToTime(ms) {
let seconds = (ms / 1000).toFixed(1);
let minutes = (ms / (1000 * 60)).toFixed(1);
let hours = (ms / (1000 * 60 * 60)).toFixed(1);
let days = (ms / (1000 * 60 * 60 * 24)).toFixed(1);
if (seconds < 60) return seconds + " Sec";
else if (minutes < 60) return minutes + " Min";
else if (hours < 24) return hours + " Hrs";
else return days + " Days"
}
var FeedService = (function() {
return {
downloadFeed: function(feed) {
if (!feed.url) {
return;
}
console.log(feed.url)
fetch(feed.url)
.then(response => response.text())
.then(xmlString => {
console.log(xmlString)
this.parseFeed(xmlString, feed);
}
)
.then(data => console.log(data))
},
parseFeed: function(rawData, existingFeedData) {
var newFeedData = {};
var $feedEntries = $(rawData).find("entry").length > 0 ? $(rawData).find("entry") : $(rawData).find('item');
var lastUpdate = Date.now()
console.log("Now = " + lastUpdate)
lastUpdate = (existingFeedData && existingFeedData.lastUpdate) || Date.now()
console.log("Last Update = " + lastUpdate)
console.log("existingFeedData = " + JSON.stringify(existingFeedData))
pubDates = [];
$feedEntries.each(function(index, elm) {
pubDates.push(new Date($(elm).find("pubDate").text()).getTime())
if (lastUpdate < new Date($(elm).find("pubDate").text()).getTime()) {
console.log($(elm).find("title").text().substring(0, 20));
chrome.notifications.create(
$(elm).find("link").text(), {
type: "basic",
iconUrl: "../icons/254.png",
title: $(elm).find("title").text(),
message: "(" + msToTime((Date.now() - new Date($(elm).find("pubDate").text()).getTime())) + ") ",
requireInteraction: true
},
function() {}
);
}
}.bind(this));
console.log("Max date = " + Math.max.apply(null, pubDates))
existingFeedData.lastUpdate = Math.max.apply(null, pubDates);
Storage.saveFeedData(existingFeedData);
},
getUrlForFeed: function($feed, feedSettings) {
if (feedSettings.linkType && $feed.find(feedSettings.linkType).length > 0) {
return $feed.find(feedSettings.linkType).text();
} else if ($feed.find('link').length == 1) {
return $feed.find('link').text();
} else {
if ($feed.find('link[rel="shorturl"]').length > 0) {
return $feed.find('link[rel="shorturl"]').attr('href');
} else if ($feed.find('link[rel="alternate"]').length > 0) {
return $feed.find('link[rel="alternate"]').attr('href');
} else if ($feed.find('link[rel="related"]').length > 0) {
return $feed.find('link[rel="related"]').attr('href');
} else {
return $feed.find('link').first();
}
}
},
saveFeed: function(existingFeedData, newFeedData) {
;
},
updateReadStatusOfNewItems: function(oldItems, newItems) {
if (typeof oldItems === 'undefined' || Object.keys(oldItems).length == 0 || Object.keys(newItems).length == 0) {
return newItems;
}
Object.keys(newItems).forEach(function(url) {
if (oldItems[url]) {
newItems[url].unread = oldItems[url].unread;
}
});
return newItems;
},
setNewBadge: function() {
chrome.browserAction.setBadgeText({
text: 'NEW'
});
}
};
}());
chrome.alarms.onAlarm.addListener(function(alarm) {
if (alarm.name == 'updateRssFeeds') {
console.log("Called!")
updateRssFeeds();
}
});
chrome.notifications.onClicked.addListener(function(notificationId) {
chrome.tabs.create({
url: notificationId
});
});
chrome.runtime.onInstalled.addListener(onInstall);
function onInstall() {
chrome.runtime.openOptionsPage();
chrome.alarms.create('updateRssFeeds', {
when: 0,
periodInMinutes: 1
});
}
function updateRssFeeds() {
Storage.getFeeds(function(feeds) {
feeds.forEach(function(feed) {
FeedService.downloadFeed(feed);
});
}, true, false)
}

NodeJs async waterfall (callback method is not a function)

I am having a problem using async waterfall where I find that after calling the second callback (cbNumPages), the first parameter "pages" is the actual callback for the next function, instead of the last parameter "cbGetFiles" which it should be (as far as I know async waterfall says that last parameter should always be the callback, well in this case is apparently not).
The code is the following:
async.waterfall
([
function(cbNumPages)
{
request({
url: 'any-url',
qs: {},
method: 'GET',
headers: {
'Authorization' : 'any-auth'
}
}, (err, response, body) => {
if (!err && response.statusCode == 200)
{
var $ = cheerio.load(body);
var pagesList = $('ol.aui-nav').children();
if(pagesList.length<1)
{
var numPages = 1;
} else {
var numPages = pagesList.length-2;
}
console.log(numPages);
var pages = new Array(numPages),
total = numPages*20,
iterator = 0;
async.eachSeries(pages, function(page, cb)
{
if(page>1)
{
pages[iterator] = iterator;
}else {
pages[iterator] = iterator*20;
}
iterator++;
cb();
}, function(err){
if(err) cbNumPages(err);
cbNumPages(null, pages);
});
} else {
cbNumPages(err);
}
})
},
function(pages, cbGetFiles)
{
var files = [];
var limitDate = moment().tz('Europe/Madrid').subtract(330,'days').format();
async.eachSeries(pages, function(page, cb)
{
request({
url: 'any-url'+page,
qs: {},
method: 'GET',
headers: {
'Authorization' : 'any-auth'
}
}, (err, response, body) => {
if(!err && response.statusCode == 200)
{
var $ = cheerio.load(body);
var rows = $('tr[id^=\'attachment-\']');
async.eachLimit(rows, 1, function(row, cb)
{
var id = row.attribs['id'];
var file = row.attribs['data-attachment-filename'];
var author = $(row).children('.creator').text().trim();
var created = $(row).children('.created-date').text().trim();
created = moment.tz(created, 'MMM D, YYYY', 'Europe/Madrid').format();
var urlFile = 'simple-file' + $(row).children('.filename-column').children('.filename').attr('href');
var extension = row.attribs['data-attachment-filename'].split('.');
extension = extension[extension.length-1];
if(created<limitDate && validExtensions.indexOf(extension)>-1)
{
var f = '{ "id": "' + id + '",';
f += ' "file": "' + file + '",';
f += ' "url": "' + urlFile + '",';
f += ' "author": "' + author + '",';
f += ' "modified": "' + created + '" }';
files.push(JSON.parse(f));
}
cb();
}, (err) => {
if(err) cbGetFiles(err);
});
cb();
} else {
cb(err);
}
});
}, function(err){
if(err){
cbGetFiles(err);
} else {
cbGetFiles(null, files);
}
});
},
function(files, cbGetAutors)
{
var filesFinal = {};
for(var f in files)
{
if(!filesFinal[files[f].author])
{
var ff = {};
for(var i in files)
{
if(files[i].author === files[f].author)
{
ff[files[i].file] = files[i].url;
}
}
filesFinal[files[f].author] = ff;
}
}
cbGetAutors(null, JSON.parse(JSON.stringify(filesFinal)));
},
function(filesFinal, cbSendEmail)
{
var authors = Object.keys(filesFinal);
async.eachSeries(authors, function(author, cb)
{
var name = author.split(' ');
var email = 'simple-mail#gmail.com';
var msg = '<p>Hi ' + author + ',</p><p>how is it going:</p><p>';
for(var a in Object.keys(filesFinal[author]))
{
msg += '<p style="margin-left:20px"> '+ICON_DOC+' <a href="';
msg += filesFinal[author][Object.keys(filesFinal[author])[a]]+'">'+Object.keys(filesFinal[author])[a]+'</a></p>';
}
msg += '</p></p><p><b>NOTE: This is a no-reply address.</b></p><p>Have a nice day! '+ICON_MONKEY+'</p>';
var message = {
text: msg,
from: 'test#mail.com',
to: email,
bcc: '',
subject: 'Sample subject',
attachment: [{data: msg, alternative: true}]
};
serverEmail.send(message, function(err, message)
{
if(err)
{
cb(err);
} else {
console.log(message);
cb();
}
});
}, function(err){
if(err) cbSendEmail(err);
cbSendEmail();
});
}
], (err) => {
if(err) console.log(err);
});
I would like to know if there is a way to control this issue or at least if there are another options for what I want to do.
Thanks.
A better (neat) way to use async waterfall.
Make sure you use return before any callback function. I have added them in the code.
Also, if you are nesting eachSeries, it is better to give a different name to callback function than the parent callback function.
I have changed 'cb' of child async.series to 'inner_cb'
Updated Code:
async.waterfall
([
funcOne,
funcTwo,
funcThree,
funcFour
], (err) => {
if(err) console.log(err);
});
funciton funcOne(cbNumPages) {
request({
url: 'any-url',
qs: {},
method: 'GET',
headers: {
'Authorization' : 'any-auth'
}
}, (err, response, body) => {
if (!err && response.statusCode == 200)
{
var $ = cheerio.load(body);
var pagesList = $('ol.aui-nav').children();
if(pagesList.length<1)
{
var numPages = 1;
} else {
var numPages = pagesList.length-2;
}
console.log(numPages);
var pages = new Array(numPages),
total = numPages*20,
iterator = 0;
async.eachSeries(pages, function(page, cb)
{
if(page>1)
{
pages[iterator] = iterator;
}else {
pages[iterator] = iterator*20;
}
iterator++;
return cb();
}, function(err){
if(err) return cbNumPages(err);
return cbNumPages(null, pages);
});
} else {
return cbNumPages(err);
}
})
}
function funcTwo(pages, cbGetFiles) {
var files = [];
var limitDate = moment().tz('Europe/Madrid').subtract(330,'days').format();
async.eachSeries(pages, function(page, cb)
{
request({
url: 'any-url'+page,
qs: {},
method: 'GET',
headers: {
'Authorization' : 'any-auth'
}
}, (err, response, body) => {
if(!err && response.statusCode == 200)
{
var $ = cheerio.load(body);
var rows = $('tr[id^=\'attachment-\']');
async.eachLimit(rows, 1, function(row, inner_cb)
{
var id = row.attribs['id'];
var file = row.attribs['data-attachment-filename'];
var author = $(row).children('.creator').text().trim();
var created = $(row).children('.created-date').text().trim();
created = moment.tz(created, 'MMM D, YYYY', 'Europe/Madrid').format();
var urlFile = 'simple-file' + $(row).children('.filename-column').children('.filename').attr('href');
var extension = row.attribs['data-attachment-filename'].split('.');
extension = extension[extension.length-1];
if(created<limitDate && validExtensions.indexOf(extension)>-1)
{
var f = '{ "id": "' + id + '",';
f += ' "file": "' + file + '",';
f += ' "url": "' + urlFile + '",';
f += ' "author": "' + author + '",';
f += ' "modified": "' + created + '" }';
files.push(JSON.parse(f));
}
return inner_cb();
}, (err) => {
if(err) return cbGetFiles(err);
});
return cb();
} else {
return cb(err);
}
});
}, function(err){
if(err){
return cbGetFiles(err);
} else {
return cbGetFiles(null, files);
}
});
}
function funcThree(files, cbGetAutors) {
var filesFinal = {};
for(var f in files)
{
if(!filesFinal[files[f].author])
{
var ff = {};
for(var i in files)
{
if(files[i].author === files[f].author)
{
ff[files[i].file] = files[i].url;
}
}
filesFinal[files[f].author] = ff;
}
}
return cbGetAutors(null, JSON.parse(JSON.stringify(filesFinal)));
}
function funcFour(filesFinal, cbSendEmail) {
var authors = Object.keys(filesFinal);
async.eachSeries(authors, function(author, cb)
{
var name = author.split(' ');
var email = 'simple-mail#gmail.com';
var msg = '<p>Hi ' + author + ',</p><p>how is it going:</p><p>';
for(var a in Object.keys(filesFinal[author]))
{
msg += '<p style="margin-left:20px"> '+ICON_DOC+' <a href="';
msg += filesFinal[author][Object.keys(filesFinal[author])[a]]+'">'+Object.keys(filesFinal[author])[a]+'</a></p>';
}
msg += '</p></p><p><b>NOTE: This is a no-reply address.</b></p><p>Have a nice day! '+ICON_MONKEY+'</p>';
var message = {
text: msg,
from: 'test#mail.com',
to: email,
bcc: '',
subject: 'Sample subject',
attachment: [{data: msg, alternative: true}]
};
serverEmail.send(message, function(err, message)
{
if(err)
{
return cb(err);
} else {
console.log(message);
return cb();
}
});
}, function(err){
if(err) return cbSendEmail(err);
return cbSendEmail();
});
}
As #YSK said in a comment, I was obtaining a 401 from the response.statusCode and therefore it is being missleaded to the cbSendEmail(err) with err beying null. Making the next method in the waterfall's first parameter beying the callback instead of the second.

Chrome Extension: Background js cannot receive message from Popup js occasionally

There is a button on the popup. When clicked, popup.js will send a message to background.js. It works well almost all the time. But after a long time not operating chrome, when I click the button, the message is sent(alert("Start to send message") executed) but cannot be received by background.js(alert("Start to render") not executed). When I click one more, it will sometimes work. If it still doesn't work, I will click again, the third time has much more chance to success. That's very strange.
I use jquery in the popup.html, but I don't think it matter.
Javascript code in popup.js
$('#create).html(chrome.i18n.getMessage('create'))
.on('click', function(){
_current = null;
chrome.runtime.sendMessage({
cmd: 'SITEMAP_DEFINE'
});
alert("Start to send message");
window.close();
})
Javascript code in background.js
var curTabId; //current tab in which scraper is going to scraping
var devtoolPort;
var coord;
var sitemap;
var finish = false;
function sendMessage(msg) {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, msg);
});
}
//detective devtools
chrome.runtime.onConnect.addListener(function (port) {
if (port.name == "devtools-papa") {
devtoolPort = port;
}
});
//tab update
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
if(finish === false && coord && changeInfo.status == 'loading'){
coord.onUpdate(tab);
}
})
chrome.tabs.onRemoved.addListener(function(tabId, removeInfo) {
if(finish === false && coord && removeInfo.isWindowClosing === false){
coord.onRemove(tabId);
}
})
chrome.runtime.onMessage.addListener(function(msg, sender, callback){
var p = msg.params;
//scrape
if(msg.cmd == 'START_SCRAPE'){
sitemap = p.sitemap;
if(sitemap){
finish = false;
coord = new Coordinator(sitemap, 0);
var page = coord.sitemap.pages[0];
coord.openTab(page.id, page.link);
}
}
else if(msg.cmd == 'TEST_SCRAPE'){
sitemap = p.sitemap;
if(sitemap){
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
var tab = tabs[0];
sitemap.pages[0].link = tab.url;
finish = false;
coord = new Coordinator(sitemap, 20);
var page = coord.sitemap.pages[0];
coord.openTab(page.id, page.link);
})
}
}
else if(msg.cmd == 'SCRAPE_READY'){
print(sender);
coord.readyForScrape(sender.tab.id);
}
else if(msg.cmd == 'TAB_CLICK'){
coord.currentPage = sitemap.getPage(p.page);
}
//define
else if(msg.cmd == 'START_SELECT'){
if(msg.tabId){
chrome.tabs.sendMessage(msg.tabId, msg);
}else{
sendMessage(msg);
}
}
else if(msg.cmd == 'END_SELECT'){
if(devtoolPort){
devtoolPort.postMessage(msg);
}
sendMessage(msg);
}
else if(msg.cmd == 'SITEMAP_DEFINE'){
alert("Start to render");
var url = p && p.url;
sitemap = p && p.sitemap;
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
var tab = tabs[0];
if(url && url != tab.url){
chrome.tabs.create({url: url}, function(newTab){
doInjectScript(newTab.id);
})
}else{
injectScript(tab.id);
}
});
}
else if(msg.cmd == 'SITEMAP_DEFINE_END'){
var _sm = p.sitemap;
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
var tab = tabs[0];
_sm.pages[0].link = tab.url;
saveSitemap(_sm);
})
}
else if(msg.cmd == 'SITEMAP_DELETE'){
deleteSitemap(p.sitemap);
}
else if(msg.cmd == 'DEFINE_PAGE_LOADED'){
callback(sitemap);
}
else{
sendMessage(msg);
}
});
var print = function(msg){
if(curTabId){
chrome.tabs.sendMessage(curTabId, {
cmd: 'LOG',
params: msg
});
}else{
sendMessage(msg)
}
}
function saveSitemap(_sm){
var id = _sm.id;
chrome.storage.local.get("sitemaps", function(db){
var _sitemaps = db["sitemaps"];
if(!_sitemaps || !_sitemaps.length){
_sitemaps = [_sm];
}else{
var _idx = _sitemaps.findIndex(function(s){return s.id == id});
if(_idx == -1){
_sitemaps.push(_sm);
}else{
_sitemaps.splice(_idx, 1, _sm);
}
}
var _db = {'sitemaps':_sitemaps}
chrome.storage.local.set(_db, function(){
alert(chrome.i18n.getMessage('saved'));
});
})
}
function deleteSitemap(id){
chrome.storage.local.get("sitemaps", function(db){
var _sitemaps = db["sitemaps"];
var index = _sitemaps.findIndex(function(s){return s.id == id})
_sitemaps.splice(index, 1);
var _db = {'sitemaps':_sitemaps}
chrome.storage.local.set(_db, function(){
alert(chrome.i18n.getMessage('deleted'));
});
})
}
function injectScript(tabId){
var flag;
chrome.tabs.sendMessage(tabId, {
cmd: 'SELF_CHECK'
}, function(r){
flag = r;
});
setTimeout(function(){
if(flag){
chrome.tabs.executeScript(tabId, {file: "injected/define/initUI.js"}, function() {});
}else{
doInjectScript(tabId);
}
}, 200)
}
//Injected scripts will be removed once the tab reload again.
function doInjectScript(tabId){
chrome.tabs.executeScript(tabId, {file: "injected/listener.js"});
chrome.tabs.executeScript(tabId, {file: "lib/jquery/jquery-2.1.1.min.js"}, function() {
chrome.tabs.executeScript(tabId, {file: "lib/model.js"});
chrome.tabs.executeScript(tabId, {file: "lib/css-selector-generator.js"}, function() {});
chrome.tabs.executeScript(tabId, {file: "injected/SelectTool.js"}, function() {});
chrome.tabs.executeScript(tabId, {file: "injected/define/initUI.js"}, function() {});
chrome.tabs.insertCSS(tabId, {file: "injected/injected.css", runAt: "document_end"});
});
}
function createResultTab(title, fields, data){
chrome.tabs.create({url: chrome.extension.getURL('result/table.html')}, function(newTab){
var tabId = newTab.id;
setTimeout(function(){
chrome.tabs.sendMessage(tabId, {
cmd: 'RESULT',
params:{
data: data,
title: title,
fields: fields
}
});
}, 200)
})
}
Thanks in advance for your help.
I find the answer in official document.
If we set persistent = false, the background js will be unload when it goes idle.
{
"name": "My extension",
...
"background": {
"scripts": ["eventPage.js"],
"persistent": false
},
...
}
The document tells sending message from content script will cause background js reload. But I guess message from popup script has issue.
So either change persistent true or chrome.runtime.getBackgroundPage in popup to cause background js reload will solve this issue.

WebRTC: One to one Audio call not working in different machine

I am trying to implement a one to one audio call using webRTC (signalling using websockets) . But it works when i try it in one system using multiple tabs of chrome (localhost). When I try to hit my server from another machine it does initial handshakes , but call doesn't happen.
But when i try to change the tag to and changed the constraints to video constraints . it works even if the we try to access from other machine (i.e video call works ).
I initially thought it was because if firewall but when video call worked I was puzzled .
Here is my code:
// Constraints to get audio stream only
$scope.constraints = {
audio: {
mandatory: {
googEchoCancellation: true
},
optional: []
},
video:false
};
navigator.getUserMedia = navigator.getUserMedia ||
navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
// success Callback of getUserMedia(), stream variable is the audio stream.
$scope.successCallback = function (stream) {
if (window.URL) {
myVideo.src = window.URL.createObjectURL(stream); // converting media stream to Blob URL.
} else {
myVideo.src = stream;
}
//attachMediaStream(audioTag, stream);
localStream = stream;
if (initiator)
maybeStart();
else
doAnswer();
};
// failure Callback of getUserMedia()
$scope.failureCallback = function (error) {
console.log('navigator.getUserMedia Failed: ', error);
};
var initiator, started = false;
$("#call").click(function () {
socket.emit("message", undefined);
initiator = true;
navigator.getUserMedia($scope.constraints, $scope.successCallback, $scope.failureCallback);
});
var channelReady = false;
socket.on('message', function (data) {
channelReady = true;
if (data) {
if (data.type === 'offer') {
if (!initiator) {
$("#acceptCall").show();
$("#acceptCall").click(function(){
if (!initiator && !started) {
var pc_config = {
iceServers: [
{ url: "stun:stun.l.google.com:19302" },
{ url: "turn:numb.viagenie.ca", credential: "drfunk", username: "toadums#hotmail.com"}
]
};
pc = new webkitRTCPeerConnection(pc_config);
pc.onicecandidate = onIceCandidate;
pc.onaddstream = onRemoteStreamAdded;
}
pc.setRemoteDescription(new RTCSessionDescription(data));
$scope.acceptCall();
});
}
} else if (data.type === 'answer' && started) {
pc.onaddstream = onRemoteStreamAdded;
pc.setRemoteDescription(new RTCSessionDescription(data));
} else if (data.type === 'candidate' && started) {
var candidate = new RTCIceCandidate({
sdpMLineIndex: data.label,
candidate: data.candidate
});
pc.addIceCandidate(candidate);
} else if (data.type === 'bye' && started) {
console.log("Bye");
}
}
});
function onRemoteStreamAdded(event) {
othersVideo.src = URL.createObjectURL(event.stream);
};
var sdpConstraints = {
'mandatory': {
'OfferToReceiveAudio': true,
'OfferToReceiveVideo': false
}
};
function doAnswer() {
pc.addStream(localStream);
pc.createAnswer(gotDescription,null,sdpConstraints);
}
function gotDescription(desc) {
pc.setLocalDescription(desc);
socket.send(desc);
}
function maybeStart() {
if (!started && localStream && channelReady)
createPeerConnection();
pc.addStream(localStream);
started = true;
if (initiator)
doCall();
}
$scope.acceptCall = function () {
navigator.getUserMedia($scope.constraints, $scope.successCallback, $scope.failureCallback);
}
function createPeerConnection() {
var pc_config = {
iceServers: [
{ url: "stun:stun.l.google.com:19302" },
{ url: "turn:numb.viagenie.ca", credential: "drfunk", username: "toadums#hotmail.com"}
]
};
pc = new webkitRTCPeerConnection(pc_config);
pc.onicecandidate = onIceCandidate;
console.log("Created RTCPeerConnnection with config:\n" + " \"" +
JSON.stringify(pc_config) + "\".");
};
function doCall() {
$scope.caller = true;
pc.createOffer(setLocalAndSendMessage,null,sdpConstraints);
};
function setLocalAndSendMessage(sessionDescription) {
pc.setLocalDescription(sessionDescription);
socket.send(sessionDescription);
}
function onIceCandidate(event) {
if (event.candidate) {
socket.emit('message', {
type: 'candidate',
label: event.candidate.sdpMLineIndex,
id: event.candidate.sdpMid,
candidate: event.candidate.candidate
});
} else {
console.log("End of candidates.");
}
}
If navigator.mediaDevices is undefined, this because work only in secure context (https)
see:
https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia

How to close a notification window automatically

Hi I am displaying a notification based on the output of a server file.The output of the server file is being checked for a period of 5 min interval.Now what I need is that when the output changes at any instant, notification should be automatically be closed at that moment.What I am trying to convey that if the output of the server file is 0,the notification is being displayed in every 5 min. If the output is 1, then notification will not be shown anymore.The problem of my code is that unless I close the notification manually it won't be closed automatically even if the output is 1. Anyone please help me to close my notification automatically.
here is my background.js
var myNotificationID = null;
var oldChromeVersion = !chrome.runtime;
function getGmailUrl() {
return "http://calpinemate.com/";
}
function isGmailUrl(url) {
return url.indexOf(getGmailUrl()) == 0;
}
chrome.browserAction.onClicked.addListener(function(tab) {
if(!localStorage.username){
chrome.windows.create({url : "userinfo.html",type: "popup", height: 200, width:300 , top :400 , left : 800});
}
else{
chrome.tabs.query({
url: "http://calpinemate.com/*",
currentWindow: true
},
function(tabs) {
if (tabs.length > 0) {
var tab = tabs[0];
console.log("Found (at least one) Gmail tab: " + tab.url);
console.log("Focusing and refreshing count...");
chrome.tabs.update(tab.id, { active: true });
updateIcon();
}
else {
console.log("Could not find Gmail tab. Creating one...");
chrome.tabs.create({ url: getGmailUrl() });
updateIcon();
}
});
}
});
function onInit() {
console.log('onInit');
updateIcon();
if (!oldChromeVersion) {
chrome.alarms.create('watchdog', {periodInMinutes:5});
}
}
function onAlarm(alarm) {
console.log('Got alarm', alarm);
if (alarm && alarm.name == 'watchdog') {
onWatchdog();
}
else {
updateIcon();
}
}
function onWatchdog() {
chrome.alarms.get('refresh', function(alarm) {
if (alarm) {
console.log('Refresh alarm exists. Yay.');
}
else {
console.log('Refresh alarm doesn\'t exist!? ' +
'Refreshing now and rescheduling.');
updateIcon();
}
});
}
if (oldChromeVersion) {
updateIcon();
onInit();
}
else {
chrome.runtime.onInstalled.addListener(onInit);
chrome.alarms.onAlarm.addListener(onAlarm);
}
function updateIcon(){
var urlPrefix = 'http://www.calpinemate.com/employees/attendanceStatus/';
var urlSuffix = '/2';
var req = new XMLHttpRequest();
req.addEventListener("readystatechange", function() {
if (req.readyState == 4) {
if (req.status == 200) {
var item=req.responseText;
if(item==1){
chrome.browserAction.setIcon({path:"calpine_logged_in.png"});
chrome.browserAction.setBadgeBackgroundColor({color:[190, 190, 190, 230]});
chrome.browserAction.setBadgeText({text:""});
chrome.notifications.clear(id1);//this is not working
}
else{
chrome.browserAction.setIcon({path:"calpine_not_logged_in.png"});
chrome.browserAction.setBadgeBackgroundColor({color:[190, 190, 190, 230]});
chrome.browserAction.setBadgeText({text:""});
chrome.notifications.create(
'id1',{
type: 'basic',
iconUrl: '/calpine_not_logged_in.png',
title: 'Warning : Attendance',
message: 'Please mark your Attendance !',
buttons: [{ title: 'Mark',
iconUrl: '/tick.jpg'
},{ title: 'Ignore',
iconUrl: '/cross.jpg'}],
priority: 0},
function(id) { myNotificationID = id;}
);
chrome.notifications.onButtonClicked.addListener(function(notifId, btnIdx) {
if (notifId === myNotificationID) {
if (btnIdx === 0) {
window.open("http://www.calpinemate.com/");
} else if (btnIdx === 1) {
notification.close();
}
}
});
chrome.notifications.onClosed.addListener(function() {
notification.close();
});
}
}
else {
// Handle the error
alert("ERROR: status code " + req.status);
}
}
});
var url = urlPrefix + encodeURIComponent(localStorage.username) + urlSuffix;
req.open("GET", url);
req.send(null);
}
Two possible problems:
You pass an undefined variable named id1 in chrome.notifications.clear(), when you actually mean the string 'id1'.
According to the docs on chrome.notifications.clear() method, the second argument (callback function) is not optional, yet you fail to supply one.
One possible solution:
// Replace that line:
chrome.notifications.clear(id1);//this is not working
// With this line:
chrome.notifications.clear('id1', function(){});//this is working perfectly
// Or with this line:
chrome.notifications.clear(myNotificationID, function(){});//this is working perfectly
BTW, you don't need to provide a notification ID yourself.
You can replace: chrome.notifications.create('id1',...
with: chrome.notifications.create('',...
(and then, of course, use: chrome.notifications.clear(myNotificationID,...)

Resources