I am developing a simple Chrome extension, that does something when a password-input box is focused. I'm using following content script to achieve the same (code is simplified for asking this question). The code does not work on pages like https://accounts.google.com/ but it works perfectly for pages like https://www.linkedin.com/. Is it because of some Javascript/JQuery conflicts? Or some other reason? Please help. I tried using noConflict api, but it didn't help.
Content script:
var inputs = document.getElementsByTagName('input');
function foo() {
for (var i = 0; i < inputs.length; i++) {
if (inputs[i].type.toLowerCase() == 'password')
inputs[i].addEventListener("focus", bar);
}
}
function bar() {
alert(1);
}
foo();
Above script finds input elements of type passwords, but does not add event listener.
Manifest:
{
"manifest_version": 2,
"name": "MyExtension",
"version": "1.0",
"options_page": "options.html",
"browser_action": {
"default_icon": "img/abcd.png",
"default_title": "MyExtension",
"default_popup": "popup.html"
},"icons": { "16": "img/abcd16.png",
"48": "img/abcd48.png",
"128": "img/abcd128.png" },"description":"abcd abcd",
"permissions": [
"storage","tabs"
],
"content_scripts": [
{
"matches": ["<all_urls>"
],
"js": ["js/content.js","js/jquery.1.8.3.min.js"],
"css": ["css/style.css"],
"run_at": "document_end",
"all_frames": true
}
],"web_accessible_resources": [
"img/*.png",
"css/*.css"
]
}
Have you considered that the input field you seek doesn't exist by the time that the script executes? https://accounts.google.com/ is a dynamic form, the password field is created long after the page is loaded (document_end) and therefore long after you collect inputs.
In addition to binding the handler to existing elements, you need to watch for new ones being added. I would suggest using mutation-summary library with a query {element: "input[type=password]"}, but if you don't want to involve a library the basic building block to look at is MutationObserver.
Related
I've been trying to migrate one of my Chrome extensions to manifest v3, and I'm having trouble with the page_action. In manifest v3, the page_action and browser_action are merged into action, which is all good, but it's not clear to me how I can get the behavior I had previously with the new APIs.
A bit of background; the extension in question is only supposed to run on one host (let's say https://example.com). As such, I want to grey out the icon on pages with a different host. It has a popup with some settings but the main functionality is inserted via a content script (this works).
The old extension using manifest v2 used
{
"manifest_version": 2,
"name": "...",
"description": "...",
"version": "...",
"permissions": ["declarativeContent", "storage", "https://example.com/", "tabs"],
"page_action": {
"default_icon": { ... },
"default_popup": "popup.html"
},
"background": {
"scripts": ["background.js"],
"persistent": false
},
"icons": { ... },
"content_scripts": [{
"js": ["content.js"],
"matches": ["https://example.com/*", "https://www.example.com/*"]
}]
}
and in background.js I used
chrome.runtime.onInstalled.addListener(function(){
const pageUrl = {hostEquals: 'www.example.com'};
const {
onPageChanged,
PageStateMatcher,
ShowPageAction
} = chrome.declarativeContent;
onPageChanged.removeRules(undefined, function(){
onPageChanged.addRules([{
conditions: [new PageStateMatcher({pageUrl})],
actions: [new ShowPageAction()]
}]);
});
});
this works fine and the icon gets greyed out except on https://example.com. When migrating, the manifest looks like
{
"manifest_version": 3,
"name": ...,
"description": ...,
"version": ...,
"permissions": ["storage", "tabs", "declarativeContent", "activeTab"],
"background": {"service_worker": "service-worker.js"},
"action": {
"default_icon": { ... },
"default_popup": "popup/index.html"
},
"icons": { ... },
"content_scripts": [{
"matches": ["*://*.example.com/*"],
"js": ["content/detect-theme.js"]
}]
}
I cannot seem to get this to work properly. I've tried adding host_permissions, removing the declarativeContent-related code (as it doesn't seem to affect the icon whatsoever) but the extension stays available on all hosts. I know I can use the chrome.action.enable and chrome.action.disable methods to simulate this behavior but it seems overkill for such a simple use-case.
Actually, the action being available even on other pages is not breaking by any means, but I would like to make it more clear to my users that the extension only does things on https://example.com and nowhere else. Perhaps this is not even the right approach; if it isn't, I accept that as an answer as well.
TLDR; how do I only enable the (page-)action on a specific host with manifest v3?
Had the same issue myself, and I just solved it!
I fixed it by disabling the extension in the handler before adding the activation rule. I've removed the actual handler for brevity.
chrome.runtime.onInstalled.addListener(() => {
chrome.action.disable(); // The important line!
// actual handler...
});
I would guess that ShowPageAction doesn't disable the extension by default anymore.
I am trying to get into chrome extension development and want to make an extension which highlights a specific word on every page from a background script.
If I give my extension the activeTab permission, it is listed as "Access requested" and I need to invoke it by opening the popup, then after reloading the page it will highlight the word.
The documentation says "essentially, activeTab grants the tabs permission temporarily", so I switched to giving the tabs permission because I don't want to open the popup every time I visit a new website. However, now the extension is listed as "no access needed" and the highlighting does not work regardless of whether I invoke the popup or not.
Here is my service-worker background.js:
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab){
if (changeInfo.status == 'complete') {
chrome.scripting.executeScript({
target: { tabId: tabId },
function: highlightwords,
});
}
})
function highlightwords(){
var list = document.getElementsByTagName("p")
var search_words = ["the", "it", "you"]
var words = []
for(var i = 0; i < list.length; i++){
var words = list[i].textContent.split(' ')
for (var j = 0; j < words.length; j++) {
if (search_words.includes(words[j])) {
console.log(words[j]);
var elem = document.createElement("span")
elem.textContent = words[j]
elem.style.color = "red"
words[j]=elem.outerHTML
}
}
list[i].innerHTML = words.join(' ')
}
}
manifest.json:
{
"name": "Getting Started Example",
"description": "Build an Extension!",
"version": "1.0",
"manifest_version": 3,
"background": {
"service_worker": "background.js"
},
"action": {
"default_popup": "popup.html",
"default_icon": {
"16": "/images/get_started16.png",
"32": "/images/get_started32.png",
"48": "/images/get_started48.png",
"128": "/images/get_started128.png"
}
},
"icons": {
"16": "/images/get_started16.png",
"32": "/images/get_started32.png",
"48": "/images/get_started48.png",
"128": "/images/get_started128.png"
},
"options_page": "options.html",
"permissions": ["storage", "scripting", "tabs"]
}
I'd be very grateful for any hint on what's going wrong here or which alternatives there are (apart from using manifest v2, which probably doesn't have much of a future, at least in Chrome).
The only purpose of the tabs permission is to show url in changeInfo and tab objects e.g. in chrome.tabs.onUpdated event. You don't use it and you don't need it for this task.
Remove tabs and scripting permissions, remove the background worker.
Declare a content script in manifest.json
"content_scripts": [{
"matches": ["*://*/*"],
"js": ["content.js"]
}]
content.js will run in all http/https sites (this is what the first * in *://*/* means) and highlight the text.
P.S. Note that your current code destroys event listeners added by the page to any nested elements inside p because you assign to innerHTML. This is bad. A much better approach is to replace the matching parts of text inline by using DOM methods createTreeWalker and splitText, example, or just use mark.js.
im working to create a very simple chrome extension just to remove an annoying tag from a site here is my codes
my manifest.json file include those lines :
{
"manifest_version": 2,
"name": "RemoveMysatgoHelp",
"description": "Remove Mysatgo Help tag",
"version": "1.0",
"icons": {
"16": "imgs/16.png",
"32": "imgs/32.png",
"64": "imgs/64.png",
"128": "imgs/128.png"
},
"content_scripts": [
{
"matches": [
"*://*.mysatgo.com/*"
],
"js": [
"content.js"
],
"run_at": "document_idle"
}
]
}
my content.js file include this single line:
var elements0 = document.getElementById('launcher');
elements0.style.display='none';
i need to remove these "Help window"at the bottom of the page it disturbing the main player control, here is my illustration https://prnt.sc/qaeaf5
i believe it is a dynamic tag inside an anonymous div tag ,the only id name that we have is within the iframe tag and the idname is "launcher"
my question
the problem again the elements0 return null ,because it seems the page not fully loaded with all the sources
any solution ?
Thanks
I have a little problem whit my chrome extension.
The extension works if I'm on a URL that not uses fragment.
Now sure on how to configure it.
Im trying with definition < all_urls > but as I wrote. it does not work on pages with fragment.
manifest.json
{
"update_url": "https://clients2.google.com/service/update2/crx",
"name": "Analytic Live Alarm",
"version": "0.3",
"description": "Felipe the solution provider",
"permissions": [
"tabs","<all_urls>"
],
"browser_action": {
"default_icon": "icon.png"
},
"icons": {
"16": "icon.png",
"48": "icon.png",
"128": "icon_128.png"
},
"content_scripts": [
{
"matches": [
"<all_urls>"
],
"run_at": "document_end" ,
"js": ["jquery.min.js", "script.js"]
}
],
"manifest_version":2
}
This is the url Im using.
https://www.google.com/analytics/web/#dashboard/6I8yfVs_TqSQ_b1tOJYR0Q/a8398197w15972131p74378741/
Any ideas?
Thanks
Taking into account your previous question..
It's not "not working", it's working once; then, as you navigate, the page is not really reloaded, and your script does not run again.
Instead of coloring all elements at document_end, you need to detect new elements that match your selector and re-apply your styles.
This question should help you get started, or better yet mutation-summary library.
I wrote a short content script, which stops a particular site from creating new windows for link clicks.
This is my first Chrome extension, and I've scored this website and the internet for a reason why it won't run, but I can't find any. I'm probably making a fundamental amateur mistake somewhere.
Manifest.json:
{
"manifest_version": 2,
"name": "DHS Links",
"description": "Stops the school's site from constantly opening new windows.",
"version": "1.0",
"browser_action": {
"default_icon": "icon.png"
},
"content_scripts":[
{
"matches": ["*://www.darienps.org/dhs/*"],
"js": ["jquery.js", "makeNormalLinks.js"],
"run_at": "document_end"
}
]
}
I tested the Javascript file by itself on a local version of the site, so I'm pretty sure it's fine, but just in case:
makeNormalLinks.js:
$(document).ready(function() {
$("a").each(function(){
$(this).removeAttr("onclick");
});
});
A copy of jQuery is in the same directory and doesn't seem to have any issues.
Here's the onclick code for many links on the website:
onclick="window.open(this.href,'targetWindow','toolbar=no,location=no,status=no,menubar=no,scrollbars=yes,resizable=yes,')
Thank you for looking this over!
Edit:
I tried two of the injection methods from Rob W's response to another question linked to in the comments by Teepeemm.
Here's the new code for Method 1:
Manifest.json:
{
"manifest_version": 2,
"name": "DHS Links",
"description": "Stops the school's site from constantly opening new windows.",
"version": "1.0",
"browser_action": {
"default_icon": "icon.png"
},
"content_scripts":[
{
"matches": ["*://www.darienps.org/dhs/*"],
"js": ["jquery.js", "scriptLauncher.js"]
}
],
"web_accessible_resources": ["makeNormalLinks.js"]
}
scriptLauncher.js:
var s = document.createElement('script');
// TODO: add "script.js" to web_accessible_resources in manifest.json
s.src = chrome.extension.getURL('makeNormalLinks.js');
s.onload = function() {
this.parentNode.removeChild(this);
};
(document.head||document.documentElement).appendChild(s);
Method 2a:
(Uses old Manifest.js)
makeNormalLinks.js:
var actualCode = ['$(document).ready(function(){',
'$("a").each(function(){',
'$(this).removeAttr("onclick");',
'});',
'});'].join('\n');
var script = document.createElement('script');
script.textContent = actualCode;
(document.head||document.documentElement).appendChild(script);
script.parentNode.removeChild(script);
Unfortunately, neither method seems to work. I'm extremely grateful to those who commented and think we're getting close to an answer.
Solution:
Manifest.json:
{
"manifest_version": 2,
"name": "DHS Links",
"description": "Stops the school's site from constantly opening new windows.",
"version": "1.0",
"browser_action": {
"default_icon": "icon.png"
},
"content_scripts":[
{
"matches": ["*://www.darienps.org/dhs/*"],
"js": ["makeNormalLinks.js"]
}
]
}
makeNormalLinks.js:
document.addEventListener("click", function(e) {
e.stopPropagation();
}, true);
Thanks you, Scott and all who commented!
Using the onclick attribute has many weird side effects. A more up to date approach would be to add a listener to the document that filters unwanted events. Like:
document.addEventListener("click", function(e) {
e.stopPropagation();
}, true);
The true argument is important (see event capture). This will block all click event listeners from firing, but the default click action will still be triggered.
I was having a similiar issue getting the content script to fire in Google Mail. I stumbled upon a page that recommended using the "hashchange" event.
// Event listener
window.addEventListener("hashchange", function () {
alert("hashchanged");
}, false);