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);
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 working to add the Object watch polyfill by Eli Grey through a Chrome Extension. The script should be executed before any page gets parsed. When the javascript files or inline scripts get executed, I am looking to watch cookie property of document object.
Here is what I tried:
manifest.json
{
"manifest_version": 2,
"name": "Cookie Tracker",
"description": "Tracks cookie",
"version": "1.0",
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html",
"default_title": "Click here"
},
"background": {
"scripts": ["scripts/object-watch.js", "myScript.js"]
},
"content_scripts" : [{
"matches" : ["http://*/*", "https://*/*"],
"run_at": "document_start",
"js": ["scripts/object-watch.js", "myScript.js"]
}],
"web_accessible_resources": ["scripts/object-watch.js"]
}
myScript.js
document.watch('cookie', function(id, oldValue, newValue) {
console.log("document.cookie oldValue-",oldValue, " newValue-", newValue);
});
console.log("myScript executed");
scripts/object-watch.js
Content from -- https://gist.githubusercontent.com/eligrey/384583/raw/96dd5cd2fd6b752aa3ec0630a003e7a920313d1a/object-watch.js
I tried both content-script and background-pages. When I load the extension from directory and load the page. I only see in my browser console:
>Object-watch executed
>myScript triggered
I do not see the changes to document object's cookie property when I load any page though the page is setting cookie's. I am not sure what I am missing? Can someone provide some hints.
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.
I am writing a Chrome Extension mainly for Pull Requests on Github Enterprise and have ran into an issue. When the page is loaded via a refresh or direct entering of the url from your browser it runs, when it is ran from clicking a link within Github it does not.
For instance if you go to this page with Pull Requests and click into one of them it will not run. But if you refresh that same page it will run.
manifest.json
{
"name": "Github Sample",
"manifest_version": 2,
"version": "0.0.1",
"description": "Sample",
"permissions": [
"tabs", "<all_urls>",
"http://github.com/*"
],
"content_scripts": [
{
"matches": [ "https://github.com/*" ],
"js": ["github.sample.js"],
"run_at": "document_end"
}
]
}
github.sample.json
// ==UserScript==
// #author Jacob Schoen
// ==/UserScript==
alert("Extension has Ran");
To make this easier I have pushed this to github.
Any ideas on how to address this?
GitHub site uses jquery-pjax library (see How to make github style page transitions by pjax).
Basically you only need to run the content script once and attach pjax event handlers inside an injected <script> element code which will be reusing a jQueryor $ variable of the site.
In those handlers you can send a message with document.dispatchEvent to your content script that will receive it in its window.addEventListener("blabla", ...)
or you can allow the access to chrome.runtime.sendMessage on the github site in manifest.json so that page-injected code will be able to send a message that can be received by the extension in chrome.runtime.onMessageExternal listener.
Alternatively you can use chrome.webNavigation.onHistoryStateUpdated in the background script but that will cause a warning during the installation that the extension can "Read your browsing history" which is a global permission unlike the content script solution.
The working example I came up with, in case it helps anyone else.
manifest.json
{
"name": "Github Sample",
"manifest_version": 2,
"version": "0.0.1",
"description": "Sample",
"permissions": [
"activeTab",
"tabs", "<all_urls>",
"http://github.com/*"
],
"content_scripts": [
{
"matches": [ "https://github.com/*" ],
"js": ["github.sample.js"],
"run_at": "document_idle"
}
],
"web_accessible_resources": ["inject.js"]
}
github.sample.js
// ==UserScript==
// #author Jacob Schoen
// ==/UserScript==
function myAlert() {
alert("Extension has Ran");
}
window.addEventListener("pageLoadTransition", myAlert);
var s = document.createElement('script');
// TODO: add "script.js" to web_accessible_resources in manifest.json
s.src = chrome.extension.getURL('inject.js');
s.onload = function() {
this.parentNode.removeChild(this);
};
(document.head||document.documentElement).appendChild(s);
//still have to load this one for direct page loads
myAlert();
inject.js
$(document).on('pjax:success', function() {
var evt=document.createEvent("CustomEvent");
evt.initCustomEvent("pageLoadTransition", true, true, null);
document.dispatchEvent(evt);
})
I also updated the Github repository with the working example.
I am trying to integrate CodeMirror's syntax highlighting with a textarea using chrome extensions.
Here is my test.js...
var srcArray = ["lib/codemirror.js",
"lib/util/matchbrackets.js",
"lib/util/continuecomment.js",
"mode/htmlmixed/htmlmixed.js",
"mode/xml/xml.js",
"mode/javascript/javascript.js",
"mode/css/css.js",
"mode/clike/clike.js",
"mode/php/php.js"];
function AddScript(value)
{
var s = document.createElement("SCRIPT")
s.src = chrome.extension.getURL(value);
s.onload = function() {
this.parentNode.removeChild(this);
};
(document.head||document.documentElement).appendChild(s);
}
srcArray.forEach(AddScript);
... and manifest.json:
{
"name":"Test",
"description":"Test description",
"version":"1.0",
"manifest_version": 2,
"browser_action": {
"default_icon": "icon.png"
},
"content_scripts": [
{
"matches": ["file:///*Test*"],
"js": ["test.js"]
}
],
"web_accessible_resources": ["lib/codemirror.js",
"lib/util/matchbrackets.js",
"lib/util/continuecomment.js",
"mode/htmlmixed/htmlmixed.js",
"mode/xml/xml.js",
"mode/javascript/javascript.js",
"mode/css/css.js",![enter image description here][1]
"mode/clike/clike.js",
"mode/php/php.js"]
}
And here is the issue I am facing:
<script>...</script> are being added to the page, however when the last one is added they all disappear.
I suspect this has someting to do with security mechanisms introduced in 2nd version of the manifest. I cannot however figure out what is missing.
Your advice would be greatly appreciated, thanks.