Chrome Extensions: How do you gray out icon depending on the url? - javascript

Does chrome have an api to disable (and thus gray out) chrome extensions on certain urls or do I just have to have a if statement that checks the url and switches out the icon accordingly?

You could use chrome.declarativeContent, it allows you to show page action depending on the URL of a web page and the CSS selectors its content matches.
You could create conditions ( yes, you could use regex) and actions ( ShowPageAction SetIcon) via a constructor like new chrome.declarativeContent.PageStateMatcher and new chrome.declarativeContent.ShowPageAction(). Detailed sample are listed in the api documentation.
var rule2 = {
conditions: [
new chrome.declarativeContent.PageStateMatcher({
pageUrl: { hostEquals: 'www.google.com', schemes: ['https'] },
css: ["input[type='password']"]
}),
new chrome.declarativeContent.PageStateMatcher({
css: ["video"]
})
],
actions: [ new chrome.declarativeContent.ShowPageAction() ]
};
chrome.runtime.onInstalled.addListener(function(details) {
chrome.declarativeContent.onPageChanged.removeRules(undefined, function() {
chrome.declarativeContent.onPageChanged.addRules([rule2]);
});
});

add to manifest.js:
"background": { "scripts": ["background.js"] },
"content_scripts" :[
{
"matches" : [
"*://*.example.com/*"
],
"js" : ["main.js"],
"run_at" : "document_idle"
}
]
main.js:
chrome.runtime.sendMessage({type:'showPageAction'});
background.js:
chrome.runtime.onMessage.addListener(function(message,sender,sendResponse){
if(message.type === 'showPageAction'){
chrome.pageAction.show(sender.tab.id);
}
});

You use browserAction instead of browserAction, but there are some things to note:
According to my test, you cannot use "<all_urls>" and "activeTab" in "content_scripts" and "Permissions", otherwise the icon will always be colored.
"content_scripts": [
{
"matches": ["<all_urls>"], // error
"js": ["content.js"],
}
],
"permissions": ["<all_urls>", "activeTab", "storage", "tabs", "declarativeContent"], // error
narrow down
"content_scripts": [
{
"matches": ["http://127.0.0.1/*"],
"js": ["content.js"],
}
],
"permissions": ["storage", "tabs", "declarativeContent"],
background.js
let rule1 = {
conditions: [
new chrome.declarativeContent.PageStateMatcher({
pageUrl: { hostEquals: '127.0.0.1' },
}),
],
actions: [new chrome.declarativeContent.ShowPageAction()],
};
chrome.declarativeContent.onPageChanged.removeRules(undefined, data => {
chrome.declarativeContent.onPageChanged.addRules([rule1], data => {
console.log('addRules', data);
});
});
The removeRules operation is performed because the rule will be added repeatedly every time the extension is refreshed.
Reference question link
https://stackoverflow.com/a/63434774/9854149

Related

Chrome extension - Execute content script for specific site url

At the moment I'm testing this code. My intention is to scrape some data using a content script
This is what I have in background file
chrome.action.onClicked.addListener( (tab) => {
// chrome.scripting.executeScript({
// target: {tabId: tab.id},
// files: ['content.js']
// })
console.log('Requesting tab: \n', tab)
chrome.windows.create({
type: 'popup',
height: 300,
width: 200,
url: chrome.runtime.getURL('popup.html')
})
})
This is what I have in my content script
console.info('chrome-ext template-vue-js content script')
const DOMnodes = document.querySelectorAll('article')
console.log(`Ecco le informazioni sui prezzi per Sole365`)
console.log('\n')
let details = []
DOMnodes.forEach( (node) => {
// Loop nodi prodotto
details.push(node.dataset)
console.log(`----\n`)
console.log(`Nome prodotto: ${node.dataset.productName}`)
console.log(`Descrizione breve: ${node.children[2].children[1].childNodes[1].childNodes[0].innerText}`)
// price
console.log(`Prezzo: ${node.childNodes[2].childNodes[1].childNodes[2].childNodes[0].innerText}`)
//console.log(``)
//descriz. breve
//node.children[2].children[1].childNodes[1].childNodes[0].innerText
})
console.log(details)
The script isn't executed when the website I set in manifest match. here is the dev version
{
"name": "create-chrome-ext",
"description": "Extract data",
"version": "1.0.0",
"manifest_version": 3,
"icons": {
"16": "img/logo-16.png",
"32": "img/logo-34.png",
"48": "img/logo-48.png",
"128": "img/logo-128.png"
},
"action": {
"default_icon": "img/logo-48.png"
},
"options_page": "options.html",
"background": {
"service_worker": "service-worker-loader.js",
"type": "module"
},
"host_permissions": [
"https://www.example.com/mypath/*/*"
],
"content_scripts": [
{
"js": [
"assets/content-script.js"
],
"matches": [
"https://www.example.com/mypath/*/*"
]
}
],
"web_accessible_resources": [
{
"matches": [],
"resources": [
"img/logo-16.png",
"img/logo-34.png",
"img/logo-48.png",
"img/logo-128.png"
],
"use_dynamic_url": false
},
{
"matches": [
"<all_urls>"
],
"resources": [
"**/*",
"*"
],
"use_dynamic_url": true
}
],
"permissions": [
"activeTab",
"tabs",
"alarms"
]
}
Any idea of why? My idea is to call the script when the icon is clicked and open a popup to get the resulting extracted data to use in vue frontent
When you reload the extension you also need to reinject the content script(s).
If the site is a modern SPA (Single Page Application) it uses fake navigation via history.pushState, so the page stays the same and the content script doesn't re-run.
To verify this is the case:
open devtools
open Network panel
click Doc in devtools toolbar filter
click a link in the page to navigate within the site
see if the request for the new URL is added to the network log
If there's no Doc request, you'll need matches for the entire site e.g. *://www.example.com/* and then either a) use MutationObserver in the content script or b) use chrome.webNavigation API onHistoryStateUpdated and onReferenceFragmentUpdated in the background script, which can be limited via URL filter to the site, the listener for these event(s) will send a message to the event's tabId, which will be received by the content script (example).

How to get access/permission for sendMessage to send data from any web pages to background script

I'm building cross browser extention, and face problem, i can't send sendMessage from chrome without 'externally_connectable' and 'matches' in it.
google guide said i can't matche URLs like this "" or this "http:///", only target URL like this: 'http://localhost:3000/', how avoid this and use 'runtime.sendMessage' from any web page?
my inpage scrypt
const ID = 'cb7b13f5efcbcc4453d4ebf63f7';
const send = (data) => new Promise((resolve, reject) => {
extensionizer.runtime.sendMessage(ID, { method: 'send', data }, (res) => {
if (res.error) reject(res.error);
resolve(res);
});
});
my background scrypt
const onExternalMessage = (request, sender, sendResponse) => {
const id = Date.now();
requestQueue.push({
data: request.data, sender, id, cb: sendResponse,
});
};
manifest.json
{
"short_name": "Send",
"name": "Sender",
"manifest_version": 2,
"browser_action": {
"default_popup": "./index.html",
},
"version": "1.0",
"background": {
"scripts": [
"background.js"
]
},
"content_scripts": [
{
"matches": [
"file://*/*",
"http://*/*",
"https://*/*"
],
"js": [
"./content.js"
],
"run_at": "document_start",
"all_frames": true
}
],
"permissions": [
"storage"
],
"web_accessible_resources": [
"inpage.js",
"background.js"
]
}

Firefox extension redirect from one page to another on request

So I have site insecure://site.com, and I want to make an extension that redirects me to insecure://site2.com every time I visit insecure://site.com;
Here is my background.js:
var host = "insecure://site2.com";
chrome.webRequest.onBeforeRequest.addListener(
function(details) {
return {redirectUrl: host + details.url.substr(16,details.url.length)};
},
{
urls: [
"insecure://site.com"
],
types: ["main_frame", "sub_frame", "stylesheet", "script", "image", "object", "xmlinsecurerequest", "other"]
},
["blocking"]
);
And here is part of manifest.json:
"background": {"scripts":["background.js"]},
"content_scripts": [
{
"matches": ["<all_urls>"]
}
],
"permissions": [
"contextMenus",
"webRequest",
"webRequestBlocking",
"activeTab",
"tabs",
"insecure://site.com/*",
"secure://site.com/*",
"insecure://site2.com/*",
"secure://site2.com/*"
]
You could check window.location.protocol and act accordingly based on that:
if (window.location.protocol === 'http:') { // Insecure
window.location.href = "https://www.example.com"; // Redirect to website
}

Chrome extension open links on click and filter by class innertext

I'm having trouble in letting an extension open links in chrome tab, and filter it by some class inner text.
I want it to open all the links that starts with 'http://www.ebay.com/usr/',
and after that filter the open windows by class "mem_loc" if it's innerText is for example "United States" (then close it). Else, If it's false, I want it to get the href attribute of button id="AnalyzeLink" and open it.
manifest.json:
{
"name": "Asaf's Extension",
"version": "1.0",
"manifest_version": 2,
"description": "Asaf's ZIK Extension",
"permissions": [
"<all_urls>", "tabs", "activeTab"
],
"background": {
"matches": [ "*://*.zikanalytics.com/Analyze/Index/*" ],
"scripts" : [ "event.js" ],
"persistent": false
},
"browser_action": {
"default_icon": "icon.png"
},
"content_scripts": [
{
"matches": [ "*://*.ebay.com/usr/*" ],
"css": [ "ebayButton.css" ],
"js": [ "ebayButton.js" ],
"run_at": "document_end"
},
{
"matches": [ "*://*.zikanalytics.com/Analyze/Index/*" ],
"css": [ "ZIKeyword.css" ],
"js": [ "ZIKeyword.js" ],
"persistent": false
}
]
}
event.js:
chrome.browserAction.onClicked.addListener(sellersList);
function sellersList(tab) {
chrome.tabs.executeScript(tab.id, {file: 'sellersList.js'}, results => {
results[0].forEach(openSeller);
});
}
function openSeller(url) {
chrome.tabs.create({url, pinned: true, active: true}, collectCountry);
}
function collectCountry(tab) {
chrome.tabs.executeScript(tab.id, {file: 'collectCountry.js'}, results => {
chrome.tabs.remove(tab.id);
results[0].forEach(analyzeSeller);
});
}
function analyzeSeller(url) {
chrome.tabs.create({url, active: false});
}
sellersList.js:
let urls = [...document.links]
.filter(a => a.href.startsWith('http://www.ebay.com/usr/'))
.map(a => a.href);
[...new Set(urls)];
collectCountry.js:
[...document.getElementsByClassName("mem_loc")]
.filter(a => a.textContent.trim().endsWith('s'))
.map(a=> document.URL)
It's opening the links, and filter them, but I don't know how to save the previous url if it finds a match, so it will open it later.
Thanks in advance! :)

chrome extension: run script after page has finished loading javascript

this is not firing at all when the page finishes loading. Basically when I click the browser action button, it will trigger it on, and on page load, it will run a script. In my background.js
var toggle = false;
chrome.browserAction.onClicked.addListener(function(tab) {
toggle = !toggle;
if(toggle){
chrome.browserAction.setIcon({path: "icons/logo.png", tabId:tab.id});
// chrome.tabs.executeScript(tab.id, {file:"SCRIPT.user.js"});
chrome.tabs.executeScript(tab.id, {code:"alert('aaxxxbbaa')"});
}
else{
chrome.browserAction.setIcon({path: "icons/icon48.png", tabId:tab.id});
chrome.tabs.executeScript(tab.id, {code:"alert('bbdxdb')"});
}
});
var filter = {'url': [
{hostSuffix: '*', pathPrefix: ''},
{hostSuffix: '*', pathPrefix: ''}
]};
chrome.webNavigation.onDOMContentLoaded.addListener(function(tab){
if (toggle)
chrome.tabs.executeScript(tab.id,{code:"alert('loaded')"});
},filter);
I've also tried to set it in the manifest
{
"name": "Tool",
"version": "0.0.1",
"manifest_version": 2,
"description": "Te",
"homepage_url": "",
"icons": {
"16": "icons/logo.png",
"48": "icons/logo.png",
"128": "icons/logo.png"
},
"default_locale": "en",
"background": {
"page": "src/bg/background.html",
"persistent": true
},
"browser_action": {
"default_icon": "icons/logo.png",
"default_title": "browser action demo"
},
"permissions": [
"<all_urls>"
],
"content_scripts": [
{
"run_at": "document_end",
"matches": [
"https://www.google.ca/*"
],
"css": [
"src/inject/inject.css"
]
},
{
"run_at": "document_end",
"matches": [
"https://www.google.ca/*"
],
"js": [
"src/inject/inject.js"
]
}
]
}
and in my inject.js
chrome.extension.sendMessage({}, function(response) {
var readyStateCheckInterval = setInterval(function() {
if (document.readyState === "complete") {
clearInterval(readyStateCheckInterval);
// ----------------------------------------------------------
// This part of the script triggers when page is done loading
console.log("Hello. This message was sent from scripts/inject.js");
// ----------------------------------------------------------
}
}, 10);
});
window.addEventListener ("load", myMain, false);
function myMain (evt) {
console.log('aaann');
var jsInitChecktimer = setInterval (checkForJS_Finish, 111);
function checkForJS_Finish () {
if ( typeof SOME_GLOBAL_VAR != "undefined"
|| document.querySelector ("SOME_INDICATOR_NODE_css_SELECTOR")
) {
clearInterval (jsInitChecktimer);
// DO YOUR STUFF HERE.
console.log('hi');
}
}
}
In your manifest file, you have duplicate content scripts, one with CSS and one with JS. It should look like this:
"content_scripts": [
{
"run_at": "document_end",
"matches": [
"https://www.google.ca/*"
],
"js": [
"src/inject/inject.js"
],
"css": [
"src/inject/inject.css"
]
}
]
Also, if you want it to match other urls, you will need to add them specifically, or use
"matches": ["<all_urls>"]
As for your proposed background script, that is essentially re-inventing the concept of content scripts, and it may not be in your best interest. I suggest sticking with the content script route.

Categories