Trouble removing event listener - javascript

Trying to get my feet wet with Chrome Extensions. I created a tab limiter, but its buggy.
I want the event of clicking the browser action icon to toggle on and off the extension. I believe I have that set up properly. When toggled on, it creates a listener from the chrome.tabs API, but when toggled off, I'm having a hard time removing this listener.
I've tried some of the common answers on Stack Overflow (one is shown below) for removing listeners, but none are functioning properly for me.
Here is the background.js
var tabUrls, bookmarkFolder, currentWindow;
var toggle = false;
chrome.browserAction.onClicked.addListener(function(tab){
toggle = !toggle;
if(toggle) {
chrome.browserAction.setBadgeText({"text": "ON"});
console.log("I'm listening to the browser action");
runExtension();
} else {
console.log("i shouldn't be working now.");
chrome.browserAction.setBadgeText({"text": ""});
// Need to remove the listener somehow. This isn't working.
chrome.tabs.onCreated.addListener(doStuff);
}
});
// Functions
// Potential function to removeListener. Not working.
function doStuff(tab){
chrome.tabs.onCreated.removeListener(doStuff);
}
var runExtension = function(){
chrome.tabs.onCreated.addListener(function(tab){
// Find and set reference to the current window
chrome.windows.getCurrent(function(window){
currentWindow = window;
});
// Logic to limit tabs (remove window, create bookmarks, play audio, manipulate DOM on new window)
chrome.tabs.getAllInWindow(function(tabs) {
tabUrls = lengthRequirement(tabs);
console.log(tabUrls);
if (typeof tabUrls != "string") {
createBookmarkFolder(tabUrls);
chrome.windows.remove(currentWindow.id);
playAudioClip();
chrome.windows.create({ "url": "http://www.justinstewart.info/"});
chrome.tabs.executeScript({file: "content_script.js"});
}
});
});
}
var playAudioClip = function(){
var audio = new Audio("audio_file.mp3");
audio.play();
}
var createBookmarkFolder = function(tabUrls){
chrome.bookmarks.create(
{'title': "Caroline Strikes Again # " + new Date()},
function(bookmark){
createBookmarks(tabUrls, bookmark);
}
)
}
var createBookmarks = function(urls, folder){
for(i=0; i<urls.length; i++) {
chrome.bookmarks.create({
'parentId': folder.id,
'url': urls[i][0],
'title': urls[i][1]
})
}
}
var lengthRequirement = function(tabsArray){
if (tabsArray.length > 9) {
var urls = [];
for(i=0; i<tabsArray.length; i++) {
info = [];
info.push(tabsArray[i].url);
info.push(tabsArray[i].title);
urls.push(info);
}
return urls;
} else {
return "Not there yet.....";
}
}
manifest.json file:
{
"manifest_version": 2,
"name": "Caroline",
"version": "0.2",
"description": "A fun tab limiter to help increase productivity and focus.",
"icons": {
"16": "icon16.png",
"48": "icon48.png",
"128": "icon128.png"
},
"background": {
"scripts": ["background.js"],
"persistent": false
},
"browser_action": {
"default_icon": "icon48.png",
"default_title": "Sweet Caroline"
},
"permissions": [
"tabs", "bookmarks", "*://*/"
]
}
Any help is greatly appreciated!

.removeListener(func) will only remove a listener if func is identical to one of the previously added event listeners.
First, remove the anonymous function within runExtension, because if it is anonymous, you cannot get a reference to the function object any more:
var runExtension = function(){
chrome.tabs.onCreated.addListener(function(tab){
...
});
};
Then, replace runExtension() with .addListener(runExtension);, and use .removeListener(runExtension); whenever you want to remove the listener:
chrome.browserAction.onClicked.addListener(function(tab){
toggle = !toggle;
if (toggle) {
...
chrome.tabs.onCreated.addListener(runExtension);
} else {
...
chrome.tabs.onCreated.removeListener(runExtension);
}
});

Related

chrome.action.setIcon failureCallback is not a function

I'm trying to change the extension icon depending on what is found in the URL, the below works but i get an error:
Uncaught (in promise) TypeError: failureCallback is not a function
Context
extensions::setIcon
Stack Trace
I feel sure this is a woods for the trees thing. Can anyone help?
//get url of the page we are on in the tab
async function getTab() {
let queryOptions = { active: true, currentWindow: true };
let tabs = await chrome.tabs.query(queryOptions);
return tabs[0];
};
var tab;
//listen for the following two events - when they happen run the colourIcon function
chrome.tabs.onActivated.addListener(colourIcon);
chrome.tabs.onUpdated.addListener(colourIcon);
async function colourIcon(){
let urlCol = await getTab()
let url = urlCol.url
tab = urlCol.id
if (url.indexOf("d") > -1) {
changeIconColour("purple");
} else if (url.indexOf("b") > -1) {
changeIconColour("purple");
} else if (url.indexOf("a") > -1) {
changeIconColour("purple");
} else { // if there is no match, you can't get the data
changeIconColour("grey");
};
}
function changeIconColour(colour) {
let pathToIcons;
//change the path to the icon folder depending on what colour is sent to the function
if (colour == "purple") {
pathToIcons = "/";
} else {
pathToIcons = "grey/";
}
// and set the icon
chrome.action.setIcon({
path: {
"16": "/assets/icons" + pathToIcons + "extension_toolbar_icon16.png",
"32": "/assets/icons" + pathToIcons + "extension_toolbar_icon32.png",
"48": "/assets/icons" + pathToIcons + "extension_toolbar_icon48.png",
"128": "/assets/icons" + pathToIcons + "extension_toolbar_icon128.png"
},
tabId: tab
})
};
The error is at extensions::setIcon:35, which is the line starting "failure", here:
$Promise.catch(imageDataPromise, function(error) {
failureCallback(exceptionHandler.safeErrorToString(error, true));
});
It's a bug in Chromium.
Suggests an invalid filepath. But it works.

Event not triggering on a content scripts Chrome extension

I'm building a content scripts Chrome extension. I've dynamically added a button to a webpage, however the event listener on this button won't trigger.
Here is manifest.json:
{
"manifest_version": 2,
"name": "my extension",
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"permissions": [
"activeTab"
],
"content_scripts": [
{
"matches": ["http://127.0.0.1:8000/*"],
"js": ["jquery.js", "script.js"],
"run_at": "document_end"
}
]
}
This is script.js:
document.body.style.backgroundColor = "yellow";
var button = $('<input id="btn" type="button"/>');
$("body").append(button);
$("#btn").on("click", function(){
document.body.style.backgroundColor = "red";
});
The button is added to the page and background changes to yellow, but it doesn't change to red when clicking button.
If the suspected issue is with the button ID - note that you don't need the ID at all.
document.body.style.backgroundColor = "yellow";
var button = $('<input id="btn" type="button"/>');
$("body").append(button);
button.on("click", function(){ // Notice you don't need to search for it again!
document.body.style.backgroundColor = "red";
});
However, in this case you'll need to keep the button variable to find it again.
This unique id may help you ;)
document.body.style.backgroundColor = "yellow";
uniqueId = 'btn' + Date.now();
var button = $('<input id="' + uniqueId + '" type="button"/>');
$("body").append(button);
$("#" + uniqueId).on("click", function(){
document.body.style.backgroundColor = "red";
});

Saving in local storage not working

I'm building on top of an existing chrome extension, and I'm trying to maintain a consistent style. I need add a new feature, and I use the following script to save a user's choice from the popup selection, and then set a new popup going forward based on the saved choice.
userchoices.js:
require.scopes["userchoices"] = (function() {
var exports = {};
var userChoices = exports.userChoices = {
userchoices: {},
updateChoice: function(){
self = this;
chrome.storage.local.get('userchoices', function(items){
if(!items.userchoices){
chrome.storage.local.set({userchoices: self.userchoices});
return;
}
self.userchoices = items.userchoices;
});
},
getChoice: function(url){
if(this.userchoices[url]){
return this.userchoices[url][choice];
} else {
return {};
}
},
setChoice: function(url, newChoice){
if(!this.userchoices[url]){
this.userchoices[url] = {};
}
this.userchoices[url][choice] = newChoice;
chrome.storage.local.set({userchoices: this.userchoices});
},
removeChoice: function(url){
if(!this.userchoices[url]){
return;
} else {
delete this.userchoices[url]
}
chrome.storage.local.set({userchoices: this.userchoices});
}
}
return exports;
})();
background.js:
var userChoices= require("userchoices").userChoices;
chrome.windows.onCreated.addListener(function(){
CookieBlockList.updateDomains();
BlockedDomainList.updateDomains();
FakeCookieStore.updateCookies();
userChoices.updateChoice();
});
function refreshIconAndContextMenu(tab)
{
// The tab could have been closed by the time this function is called
if(!tab)
return;
var choice = userChoices.getChoice(tab.url);
if(choice) {
if (choice == "one"){
chrome.browserAction.setPopup({tabId: tab.id, popup: "skin/popupDontCare.html"});
} else if(choice=="two"){
chrome.browserAction.setPopup({tabId: tab.id, popup: "skin/popupSortofCare.html"});
} else if(choice=="three") {
chrome.browserAction.setPopup({tabId: tab.id, popup: "skin/popupCare.html"});
} else if(choice=="four") {
chrome.browserAction.setPopup({tabId: tab.id, popup: "skin/popupReallyCare.html"});
} else {
chrome.browserAction.setPopup({tabId: tab.id, popup: "skin/popup.html"});
}}
}
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
if(changeInfo.status == "loading")
refreshIconAndContextMenu(tab);
});
// Update icon if a tab is replaced or loaded from cache
chrome.tabs.onReplaced.addListener(function(addedTabId, removedTabId){
chrome.tabs.get(addedTabId, function(tab){
refreshIconAndContextMenu(tab);
});
});
popup.js:
var userChoices = require("userchoices").userChoices;
function init()
{
console.log("Initializing popup.js");
// Attach event listeners
$("#Dont_Care_btn").click(doNothing);
$("#Sort_of_Care_btn").click(doBadger);
$("#Care_btn").click(giveSecrecyBadger);
$("#Really_Care_btn").click(giveAdvice);
$("#Nuance_btn").click(addNuance);
}
function doNothing() {
$("#startingQuestion").hide();
$("#DontCareResponse").show();
$("#siteControls").hide();
userChoices.setChoice(tab.url, "one");
refreshIconAndContextMenu(tab);
}
function doBadger() {
$("#startingQuestion").hide();
$("#SortofCareResponse").show();
$("#siteControls").hide();
$("#blockedResourcesContainer").hide();
$("#Nuance_btn").show();
userChoices.setChoice(tab.url, "two");
refreshIconAndContextMenu(tab);
}
function giveSecrecyBadger() {
$("#startingQuestion").hide();
$("#siteControls").hide();
$("#CareResponse").show();
$("#siteControls").hide();
$("#blockedResourcesContainer").hide();
$("#Nuance_btn").show();
userChoices.setChoice(tab.url, "three");
refreshIconAndContextMenu(tab);
}
function giveAdvice() {
$("#startingQuestion").hide();
$("#siteControls").hide();
$("#ReallyCareResponse").show();
userChoices.setChoice(tab.url, "four");
refreshIconAndContextMenu(tab);
}
The popup is currently not being set, and I'm not even sure that the selection is saved successfully. Anyone see a problem?
Ha! In the middle of trying to create a minimal example, I figured out the problem. Turns out the problem was the now-deprecated chrome.tabs.getSelected method when it should have been chrome.tabs.query()
Thanks Xan!

Context menu not being loaded by extension

I'm following this tutorial on how to create a context menu when a user right clicks on selected text, the menu offers the user the option to send the text to a server:
http://vikku.info/programming/chrome-extension/get-selected-text-send-to-web-server-in-chrome-extension-communicate-between-content-script-and-background-page.htm#Comments
Here are the files:
The myscript.js file:
document.addEventListener('mouseup',function(event)
{
var sel = window.getSelection().toString();
alert('selection is '+sel)
if(sel.length)
chrome.extension.sendRequest({'message':'setText','data': sel},function(response){})
})
The background.html file:
<script>
var seltext = null;
chrome.extension.onRequest.addListener(function(request, sender, sendResponse)
{
switch(request.message)
{
case 'setText':
window.seltext = request.data
break;
default:
sendResponse({data: 'Invalid arguments'});
break;
}
});
function savetext(info,tab)
{
var jax = new XMLHttpRequest();
jax.open("POST","http://localhost/text/");
jax.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
jax.send("text="+seltext);
jax.onreadystatechange = function() { if(jax.readyState==4) { alert(jax.responseText); }}
}
alert('here')
var contexts = ["selection"];
for (var i = 0; i < contexts.length; i++)
{
var context = contexts[i];
chrome.contextMenus.create({"title": "Send to Server", "contexts":[context], "onclick": savetext});
}
</script>
The manifest.json file:
<script>
var seltext = null;
chrome.extension.onRequest.addListener(function(request, sender, sendResponse)
{
switch(request.message)
{
case 'setText':
window.seltext = request.data
break;
default:
sendResponse({data: 'Invalid arguments'});
break;
}
});
function savetext(info,tab)
{
var jax = new XMLHttpRequest();
jax.open("POST","http://localhost/text/");
jax.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
jax.send("text="+seltext);
jax.onreadystatechange = function() { if(jax.readyState==4) { alert(jax.responseText); }}
}
alert('here')
var contexts = ["selection"];
for (var i = 0; i < contexts.length; i++)
{
var context = contexts[i];
chrome.contextMenus.create({"title": "Send to Server", "contexts":[context], "onclick": savetext});
}
</script>
The popup.html file:
<body>
Just a sample popup
</body>
In myscript.js the function document.addEventListener('mouseup',function(event) is called every time a mouseup event is fired ed but I think this should be called if the user decides to send the request to the server. The context menu should be fired when the user right clicks selected text but I don't know why this is occurring? I needed to update the manifest version to 2.
i don't know, if i got you right...the user select text, press the right mouse button and than you want to do "something"...
try this:
'get the right mouse click
window.oncontextmenu = function ()
{
showSelection(); // call a function or do something here
return false; // cancel the default right click mouse menu (necessary)
}
'in the function get the selected text and do something
function showSelection()
{
var sel = window.getSelection().toString();
alert('selection is '+sel);
.....
}
i understood that your problem is that context menu is not firing... for that you need to do small changes to your manifest
add manifest vesrion as 2
change background_page as background which is a key and value as {scripts:["background.js"]}
create a new javascript file with name as background.js and copy the script tag which is there in background html.i added manifest and javascript. let me know if you found any difficulty.
manifest:
{
"name": "Word Reminder",
"version": "1.0",
"manifest_version": 2,
"description": "Word Reminder.",
"browser_action": {
"default_icon": "images/stick-man1.gif",
"popup":"popup.html"
},
"background": {
"scripts": ["background.js"]
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["js/myscript.js"]
}
],
"permissions": [
"http://*/*",
"https://*/*",
"contextMenus",
"tabs"
],
"content_security_policy": "script-src 'self' https://ajax.googleapis.com; object-src 'self'"
}
background.js(new file in the same folder should contain below code):
var seltext = null;
chrome.extension.onRequest.addListener(function(request, sender, sendResponse)
{
switch(request.message)
{
case 'setText':
window.seltext = request.data
break;
default:
sendResponse({data: 'Invalid arguments'});
break;
}
});
function savetext(info,tab)
{
var jax = new XMLHttpRequest();
jax.open("POST","http://localhost/text/");
jax.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
jax.send("text="+seltext);
jax.onreadystatechange = function() { if(jax.readyState==4) { alert(jax.responseText); }}
}
var contexts = ["selection"];
for (var i = 0; i < contexts.length; i++)
{
var context = contexts[i];
chrome.contextMenus.create({"title": "Send to Server", "contexts":[context], "onclick": savetext});
}

Keycode not working in chrome extension

I am writing a chrome extension which should change the content-type of http responses. It is working fine. Now, I want to control it using keypress i.e. user should be able to bypass the default action of extension on keypress. I tried this, but it doesn't work:
background.js
var enableEXT = true;
window.onkeydown = function()
{
console.log('Testing hello');
if (event.keyCode == 70)
enableEXT = false;
console.log(event.keyCode);
console.log(enableEXT);
};
window.onkeyup = function()
{
enableEXT = true;
console.log(event.keyCode);
};
chrome.webRequest.onHeadersReceived.addListener(function(details) {
for (var i = 0; i < details.responseHeaders.length; ++i)
{
if (details.responseHeaders[i].name.toLowerCase() == 'content-type' && !enableEXT)
details.responseHeaders[i].value = 'application/xyz';
}
return {responseHeaders: details.responseHeaders};
}, {urls: ["<all_urls>"]}, ['blocking', 'responseHeaders']);
Try using a content script, injected into the web page, instead of a background script. Use this in your manifest.json:
"content_scripts" : [{
"js" : ["myScript.js"]
}]
"permissions": [
"tabs", "http://*/*", "https://*/*"
]
However, chrome extensions are sandboxed from other scripts in the page. However, you do have access to the DOM and you can inject your own tags. So, create a new <script> tag and append it to the DOM, and the new script (script.js) it references can include the event listeners you want.
background.js:
chrome.runtime.onConnect.addListener(function(port) {
port.onMessage.addListener(function(msg) {
enableEXT = msg.enableEXT;
});
});
chrome.webRequest.onHeadersReceived.addListener(function(details) {
for (var i = 0; i < details.responseHeaders.length; ++i)
{
if (details.responseHeaders[i].name.toLowerCase() == 'content-type' && !enableEXT)
details.responseHeaders[i].value = 'application/xyz';
}
return {responseHeaders: details.responseHeaders};
}, {urls: ["<all_urls>"]}, ['blocking', 'responseHeaders']);
myScript.js:
//to inject into the page
var s = document.createElement('script');
s.src = chrome.extension.getURL("script.js");
s.onload = function() {
this.parentNode.removeChild(this);
};
(document.head||document.documentElement).appendChild(s);
//to communicate with our background
var port = chrome.runtime.connect();
window.addEventListener("message", function(event) {
// We only accept messages from ourselves
if (event.source != window)
return;
if (event.data.type && (event.data.type == "FROM_PAGE")) {
port.postMessage(event.data);
}
}, false);
script.js:
window.onkeydown = function()
{
if (event.keyCode == 70)
window.postMessage({ enableEXT: true, "*");
};
window.onkeyup = function()
{
window.postMessage({ enableEXT: false, "*");
};
To communicate between your injected code, your content script, and your background page is a very awkward situation, but can be dealt with, with all the postMessage APIs...
Add a parameter named event to the event handlers you assign to the events like this:
window.onkeydown = function(event)
{
console.log(event.keyCode);
};

Categories