how to dynamically change chrome extension icon - javascript

I am developing a chrome extension. The scenario is kind of
When i click on the icon extension send POST request to server and on basis of the GET response it procceds on any of 3 different if/else if/ else statement. I am using page action to show the icon next to address bar.
I want my extension icon to change dynamically on each if/else if /else statement.
this is my backgound.js to make the icon visible next to address bar.
chrome.runtime.onInstalled.addListener(function() {
chrome.declarativeContent.onPageChanged.removeRules(undefined, function() {
chrome.declarativeContent.onPageChanged.addRules([
{
conditions: [
new chrome.declarativeContent.PageStateMatcher({
})
],
actions: [ new chrome.declarativeContent.ShowPageAction() ]
}
]);
});
});
this is my manifest.json
"page_action" :
{
"default_icon" : "icon-191.png",
"default_title" : "xxx",
"default_popup": "popup.html"
},
Any suggestion how can i change extension toolber icon dynamically on diffetent statement?
Thanks In Advance!

Well, it's there in the docs.
declarativeContent API can only execute a very limited number of actions instead of arbitrary code.
Thankfully for you, chrome.declarativeContent.setIcon is an action that does exactly what you need. Use it just like the one you're using already, except it expects a parameter.
And give that docs page a read in general.

Related

Detect page changes that'd don't refresh in Chrome Extension Javascript + Clay.js

Background
I am trying to write a Google Chrome extension to detect whenever a user scrolls down on Facebook, and if they successfully load a new set of posts, change the icon for one of the reaction options for all posts. I am using clay.js to detect if the div that contains the Facebook feed has resized, which means more posts have loaded / comments have been posted. This works fine.
Problem
The problem arises when you swap between pages on Facebook without refreshing. For example, if you start on your Home page, this will work fine. However, when you swap to your profile, the script no longer runs, until you refresh the page. Once refreshed, the script works perfectly again. I know I'm missing something about how my file is being loaded, so my question is: how do I run my script on every Facebook page, without having to refresh between each type of page?
Relevant Code (reaction-changer.js)
const fbContentId = "#content"
// on DOM load, watch for future feed scrolling
document.addEventListener('DOMContentLoaded', checkFeedUpdate(), false);
function checkFeedUpdate(){
let currFeed = new Clay(fbContentId)
// resize occurs whenever the user scrolls down or a comment loads
// on a prexisting post
currFeed.on('resize', function() {
switchAllIcons()
});
}
Manifest (some elements omitted for simplicity, notated by ...). change-icons.js is the script that actually changes icons, which will run fine, if the reaction-changer.js script actually runs.
{
...
"version": "1.0",
"manifest_version": 2,
"content_scripts": [
{
"matches": ["https://www.facebook.com/*"],
"js": ["extension/clay.js", "change-icons.js", "reaction-changer.js"],
"all_frames": true
}
],
"web_accessible_resources": [
"img/*.png"
]
...
}
Any help would be greatly appreciated! I've read the Chrome Extension documentation, as well as a bunch of other stack overflow posts, but must have missed a solution somewhere.
Alrighty, I spent the last 2 hours working on this, and I found a solution that I'm happy with for now (albeit not content with -- but what'll ya do). Basically, the big question that I had in my OP was:
how do I run my script on every Facebook page, without having to refresh between each type of page?
Well, what I realized is that, yes, refreshing is the solution. So... what if we force a refresh on Facebook's end, allowing the DOM to refresh, and the code to run as expected? I believe that this PROBABLY is actually an underlying issue with how the Clay.js library I'm using is implemented. Anyway, I basically approached the solution by:
First, creating a background.js file that takes advantage of chrome.tabs.onUpdated.addListener -- this function basically let me detect if a tab changed or if the page status was "completed" indicating it has loaded.
If it loaded, then I run the function checkFeedUpdate() exactly as above.
If it changed to a new page (e.g., user clicked from Home to Profile), I force a reload, and then wait for point 2 above to fire.
'background.jsis detecting whether or not these states have happened yet, and relaying the information toreaction-changer.js`.
Here's the updated bit of reaction-changer.js (in place of document.addEventListener):
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
// listen for messages sent from background.js
if (request.message === 'reload') {
location.reload();
} else if (request.message === 'start'){
checkFeedUpdate()
}
});
Here's the updated manifest:
"background": {
"scripts": ["background.js"]
},
"content_scripts": [
{
"matches": ["https://www.facebook.com/*"],
"js": ["extension/clay.js", "change-icons.js", "reaction-changer.js"],
"all_frames": true
}
],
"permissions": [
"tabs"
],
"web_accessible_resources": [
"img/*.png"
]
And here's what I created for background.js:
chrome.tabs.onUpdated.addListener(
function(tabId, changeInfo) {
// read changeInfo data and do something with it
if (changeInfo.url) {
chrome.tabs.sendMessage( tabId, {
message: 'reload'
})
} else if (changeInfo.status === 'complete'){
chrome.tabs.sendMessage( tabId, {
message: 'start'
})
}
}
);
If anyone ends up facing a similar issue (it seems like refreshing does the trick, but you can't get it to work without refreshing), it seems that just forcing a refresh might be a good solution. If there's a better one, please let me know!

Chrome Extension Popup Action not Working

I am working on an extension for Google Chrome. This allows our users to submit support tickets from our online product directly from the page they are on.
I have been using page rules to limit the extension to only our products pages. Some of our users are experiencing an issue were they popup no longer works.
This is what some of our users are seeing when they are one our products pages. Also the extensions icon is in full colour when the user sees this. Not sure if that is useful information.
I am having trouble recreating the issue. Most of our users are remote making is difficult to diagnose.
To recreate the issue I have tried force quitting chrome and reopening. Also a restart of my computer didn't recreate it.
It does seem that removing the extension and reinstalling it fixes the issue.
This is the code that I am using to enable/disable the extension.
const PAGE_RULE = [{
id: 'DISPLAY_RULE_SS',
conditions: [
new chrome.declarativeContent.PageStateMatcher({
pageUrl: {hostSuffix: '.localhost.com', pathPrefix: '/app/'},
}),
],
actions: [new chrome.declarativeContent.ShowPageAction()],
},]
chrome.runtime.onInstalled.addListener(function() {
chrome.tabs.onActivated.addListener((activeInfo) => {
chrome.declarativeContent.onPageChanged.getRules(['DISPLAY_RULE_SS'], (rules) => {
if(rules.length !== 0){
return;
}
chrome.declarativeContent.onPageChanged.addRules(PAGE_RULE);
});
});
});
This is my manifest.
{
"name" : "Support App",
"version" : "0.0.3",
"description" : "Fill in a brief description of your issue, along with a few details, and our support team will be notified immediately.",
"manifest_version" : 2,
"icons": {
"128" : "./img/murmuration_square_transparent.png"
},
"background" : {
"scripts" : ["./backgrounds/default_background.js"],
"persistent" : false
},
"permissions" : [
"history",
"tabs",
"declarativeContent",
"activeTab",
"https://*.localhost.com/*",
],
"page_action" : {
"default_icon" : "./img/murmuration_square_transparent.png",
"default_popup" : "../default_interface.html"
},
"web_accessible_resources": [
"src/default_index.js"
]
}
I suspect that the issue is related to the fact that all the logic is within chrome.runtime.onInstalled.addListener(function() {...} but I'm unsure.
Any help is appreciated.
I have returned to this after some downtime and found the solution in the Google documentation. The documentation on this page state
Listeners must be registered synchronously from the start of the page.
which is exactly what I was doing when setting the page rules.
I also found on this page that
Rules are persistent across browsing sessions. Therefore, you should
install rules during extension installation time using the
runtime.onInstalled event. Note that this event is also triggered when
an extension is updated. Therefore, you should first clear previously
installed rules and then register new rules.
So I have updated my background script to run the following
chrome.runtime.onInstalled.addListener(details => {
chrome.declarativeContent.onPageChanged.removeRules(undefined, () => {
chrome.declarativeContent.onPageChanged.addRules(PAGE_RULE);
});
});
Which is what is recommended in the documentation. This seems to have solved the issue we were encountering.

Can't successfully run executeScript from the background script unless I load the popup page/script first

I've trying to run execute script from my background script using keyboard shortcuts, it doesn't work and returns:
Error: No window matching {"matchesHost":[]}
But if I just open the popup page, close it, and do the same, everything works.
I've recreated the problem in using the Beastify example with minimal changes. Here's the code:
manifest.json
{
... (not interesting part, same as in beastify)
"permissions": [
"activeTab"
],
"browser_action": {
"default_icon": "icons/beasts-32.png",
"default_title": "Beastify",
"default_popup": "popup/choose_beast.html"
},
"web_accessible_resources": [
"beasts/frog.jpg",
"beasts/turtle.jpg",
"beasts/snake.jpg"
],
My additions start here:
"background": {
"scripts": ["background_scripts/background_script.js"]
},
"commands": {
"run_content_test": {
"suggested_key": {
"default": "Alt+Shift+W"
}
}
}
}
popup/choose_beast.js (same as in original)
/*
Given the name of a beast, get the URL to the corresponding image.
*/
function beastNameToURL(beastName) {
switch (beastName) {
case "Frog":
return browser.extension.getURL("beasts/frog.jpg");
case "Snake":
return browser.extension.getURL("beasts/snake.jpg");
case "Turtle":
return browser.extension.getURL("beasts/turtle.jpg");
}
}
/*
Listen for clicks in the popup.
If the click is on one of the beasts:
Inject the "beastify.js" content script in the active tab.
Then get the active tab and send "beastify.js" a message
containing the URL to the chosen beast's image.
If it's on a button wich contains class "clear":
Reload the page.
Close the popup. This is needed, as the content script malfunctions after page reloads.
*/
document.addEventListener("click", (e) => {
if (e.target.classList.contains("beast")) {
var chosenBeast = e.target.textContent;
var chosenBeastURL = beastNameToURL(chosenBeast);
browser.tabs.executeScript(null, {
file: "/content_scripts/beastify.js"
});
var gettingActiveTab = browser.tabs.query({active: true, currentWindow: true});
gettingActiveTab.then((tabs) => {
browser.tabs.sendMessage(tabs[0].id, {beastURL: chosenBeastURL});
});
}
else if (e.target.classList.contains("clear")) {
browser.tabs.reload();
window.close();
return;
}
});
background_scripts/background_script.js (added by me)
browser.commands.onCommand.addListener(function(command) {
var executing = browser.tabs.executeScript(
null,
{file: "/content_scripts/content_test.js"});
executing.then(
function (res){
console.log("started content_test.js: " + res);
},
function (err){
console.log("haven't started, error: " + err);
});
});
content_scripts/content_test.js (added by me)
alert("0");
I'm skipping the whole content_scripts/beastify.js cause it has nothing to do with it (IMO), but it can be found here.
Now, I know that the background script runs and receives the messages even when the popup page hasn't been opened before, because I see it failing executing the script. I have no idea what causes this behavior and if there's a way to fix it.
Note: I tried adding permissions such as "tabs" and even "all_urls", but it didn't change anything.
Note 2: I'm running the add-on as a temporary add-on from the about:debugging page, but I'm trying to execute the script on a normal non-restricted page (on this page for example I can recreate the problem).
Thanks a lot guys!
// in manifest.json
"permissions": [
"<all_urls>",
"activeTab"
],
DOES work for me (Firefox 50, Mac OS X 10.11.6).
I had gotten the exact same error message you described when I had used the original
"permissions": [
"activeTab"
],
So the addition of "<all_urls>" seems to fix the problem. However, you said that you were still experiencing the issue when you included "all_urls" in your permissions, so I am not sure whether the way I did it fixes the issue in your own setup.
edit: Whether giving any webextension such broad permissions would be wise in terms of the security risks it might pose is a separate, important consideration, I would imagine.
(I would have posted this as a comment, but I don't have enough reputation yet to be able to add comments.)

How to trigger a function on specific websites from a chrome extension?

Recently started working with Chrome Extensions.
I am trying to figure out how I can execute a function on a specific website only.
For instance, I want to show an alert box on Stack Overflow only. I am using Chrome's Declarative Content API for this to match the host.
I haven't been able to find a similar question on SO for this.
My manifest.json file is running the following block of code in the background.
'use strict';
chrome.runtime.onInstalled.addListener(function() {
chrome.declarativeContent.onPageChanged.removeRules(undefined, function() {
chrome.declarativeContent.onPageChanged.addRules([{
conditions: [
new chrome.declarativeContent.PageStateMatcher({
pageUrl: {
hostEquals: 'stackoverflow.com'
}
})
],
actions: [new chrome.declarativeContent.SOOnlyFunction()]
}]);
});
});
function SOOnlyFunction()
{
alert('On SO');
}
You Can use the Chrome API to achieve this behaviour:
When a new Page is Loaded, you can call
chrome.tabs.onUpdated
here you can filter the url
and To Create Notifications.
chrome.notifications.create
In your Manifest add these objects:
"permissions": [ "activeTab","tabs","notifications" ],
"background": {
"scripts": ["background.js"],
"persistent": false
}
Here is How my background.js looks like.
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab){
if(changeInfo.url != null){
console.log("onUpdated." + " url is "+changeInfo.url);
// creates Notification.
showNotification();
}
});
function showNotification() {
chrome.notifications.create( {
iconUrl: chrome.runtime.getURL('img/logo128.png'),
title: 'Removal required',
type: 'basic',
message: 'URL is',
buttons: [{ title: 'Learn More' }],
isClickable: true,
priority: 2,
}, function() {});
}
It is incomplete but you will get the Idea.
You can't use chrome.declarativeContent for anything but what's already built in as actions.
Which is currently specifically ShowPageAction, SetIcon and RequestContentScript (still experimental).
The reason that this can't be easily extended is because declarativeContent is implemented in native code as a more efficient alternative to JavaScript event handlers.
In general, chalk it up as an ambitious but essentially unviable/underdeveloped/abandoned idea from Chrome devs, similar fate to declarativeWebRequest.
See the other answer for implementation ideas using said JS handlers.
Or alternatively, you can still make it "declarative" by using content scripts declared in the manifest that will only activate on the predefined domain (as long as you know the domain in advance as a constant). They can then do something themselves or poke the event/background page to do something.
Finally, if your goal is to redirect/block the request, webRequest API is the one to look at.

How do I get this very simple pageAction Chrome Extension ported to run on Firefox and Safari

This is a very simple Chrome Extension that uses pageAction. It evaluates the current URL, places an icon in the address bar if the URL matches a condition (location, really) and changes the URL (using a bit from the original location) to a new location when the user clicks the icon.
This was simple and straightforward to build the Chrome Extension. The docs are simple and Google provide some code examples that could be adapted and built upon. Finally, the CWS is easy to deploy to and from.
However, I have no experience whatsoever trying to do the same in FF or Safari.
Can someone please give me some guidance with some code examples and packaging advice?
Thanks!
Background.js
function checkForValidUrl(d, c, e) {
if (c.status === "loading") {
var b = e.url.split("/")[2];
var a = e.url.split("/")[3];
if (b === "www.somewhere.com" && a === "unfiled") {
chrome.pageAction.show(d)
}
}
}
chrome.tabs.onUpdated.addListener(checkForValidUrl);
chrome.pageAction.onClicked.addListener(function (b) {
var a = b.url.split("/")[4].split("+").slice(0, 1);
chrome.tabs.update(b.id, {
url: "http://www.somewhere.com/filed/" + a
})
});
Manifest
{
"name": "MyExtension",
"version": "1.0",
"description": "This is nifty",
"background": { "scripts": ["background.js"] },
"page_action" :
{
"default_icon" : "icon-19.png",
"default_title" : "Click to do your stuff"
},
"permissions" : [
"tabs"
],
"icons" : {
"48" : "icon-48.png",
"128" : "icon-128.png"
},
"manifest_version": 2
}
This answer is for Safari only.
For a Safari version of this extension, the raw materials you will need are:
a "global" HTML page (Safari's equivalent of a Chrome extension's background page)
a script that runs on the global page, provided either as an inline <script> or as a .js file
an icon for the toolbar button, called a "toolbar item" in Safari extension parlance
After creating a new, empty extension with Extension Builder, create the aforementioned files in, or move them into, the extension's folder. In Extension Builder, select your global page. Create a toolbar item, give it a label and an identifier, and select the toolbar button image. You'll also need to specify a command for the toolbar item; just enter any string, like "munge-url".
In your global script, you will add a listener for the "command" event, which Safari will send to the global page when the user clicks your toolbar button. The listening function will read the URL of the current tab, munge it, and set the URL of the tab to the munged one. Like this:
safari.application.addEventListener('command', function (evt) {
if (evt.command == 'munge-url') {
var currentTab = safari.application.activeBrowserWindow.activeTab;
var oldUrl = currentTab.url;
var newUrl = mungeUrl(oldUrl);
currentTab.url = newUrl;
}
}, false);
The mungeUrl function needs to be defined, of course.
That should be about it. If you want to get fancy, you can add code that will disable or enable the toolbar button based on the URL of the current tab; for that you will need a listener for the "validate" event, which is discussed on this page of the Safari Extensions Development Guide.

Categories

Resources