OK Ive updated the code to be as small as possible and still get the error...although now the question isnt quite right...it should now be "Why does waitFor jump out of the sandbox?" (but Ive been told not to change questions for a post, sorry if this is bad form). If you install this extension and follow the below instructions youll see waitFor get transferred to out side the content scripts sandbox.
To see this error.....
Go to....
https://www.facebook.com/groups/382415791793391/
..wait for the page to fully load.
Then click on the tab "Testy Testy" to cause the page to refresh.
Have a look in the console and youll see "You shouldnt be able to see this".
The console output for me looks like this....
chekcpage
waitFor
(19)waitFor
pop
attach contentNodeRemoved
chekcpage
waitFor
removing
added
attach contentNodeRemoved
chekcpage
waitFor
(156+)You shouldnt be able to see this
myscript.js
script = function(old) {
window.variableInUnsafeWindow = "You shouldnt be able to see this";
}
function exec(fn) {
var script = document.createElement('script');
script.setAttribute("type", "application/javascript");
script.textContent = '(' + fn + ')();';
document.documentElement.appendChild(script); // run the script
document.documentElement.removeChild(script); // clean up
}
exec(script);
window.onpopstate = function(event) {
console.debug('pop');
checkPage();
};
function waitFor(query, delay) {
if (typeof(variableInUnsafeWindow) == "undefined") {
console.debug('waitFor');
found = document.documentElement.querySelector(query);
if (!found) {
window.setTimeout("(" + waitFor + ")('" + query + "'," + delay + ");", delay);
}
} else {
console.debug(variableInUnsafeWindow);
window.setTimeout("(" + waitFor + ")('" + query + "'," + delay + ");", delay);
}
}
function contentNodeRemoved() {
console.debug('removing');
document.querySelector('div#contentCol').removeEventListener('DOMNodeRemoved', contentNodeRemoved, false);
document.querySelector('div#contentCol').addEventListener('DOMNodeInserted', contentNodeAdded, false);
}
function contentNodeAdded() {
console.debug('added');
document.querySelector('div#contentCol').removeEventListener('DOMNodeInserted', contentNodeAdded, false);
checkPage();
}
function checkPage() {
if (document.querySelector('div#contentCol')) {
console.debug('attach contentNodeRemoved');
var node = document.querySelector('div#contentCol');
node.addEventListener('DOMNodeRemoved', contentNodeRemoved, false);
}
console.debug('chekcpage');
waitFor('ul#group_mall_382415791793391', 300);
}
checkPage();
manifest.json
{
"name": "Facebook - Group member ban icon",
"description": "Puts an X after a users name on the groups page for banning. Unfortunately the X show on groups your not an Admin of still.",
"content_scripts": [{
"matches": ["*://*.facebook.com/*"],
"js": ["myscript.js"],
"run_at": "document_start"
}],
"permissions": ["tabs"],
"icons": {
"16": "icon.png",
"48": "icon48.png",
"128": "icon128.png"
},
"version": "1.0"
}
Related
I am trying to make an extension which will run on tumblr.com/dashboard which will hide any div which is "promoted" (i.e. a blog I don't follow, which isn't a paid promotion)
I have the following code in hidestuff.js
$(document).ready(function() {
var hide = $( "div:has(.post_dismiss)" );
for (var i = 0; i < hide.length; i++) {
var div = hide[i].getAttribute('id');
var id = "#"+div;
$(id).hide();
}
});
And the following is what I have in my manifest.json
{
"manifest_version": 2,
"name": "I'm Not Following You",
"version": "0.1",
"content_scripts": [
{
"matches": [
"https://tumblr.com/dashboard", "http://tumblr.com/dashboard"
],
"js": ["hidestuff.js"]
}
]
}
Tumblr uses jQuery, hence me not including a copy.
I have tested the code (by copying the DOM from my Tumblr dashboard, which included the divs I am trying to hide) and the tests worked. However, adding it to Chrome didn't work.
This is my first attempt at making an extension, and I followed an simple tutorial. It may have been outdated, but there was no date on the tutorial, but I don't know where I am going wrong.
Update your matches field in manifest.json, because "https://tumblr.com/dashboard" doesn't match "https://www.tumblr.com/dashboard"
Use your own jquery. Chrome extension doesn't have access to any JavaScript variables or functions created by the page.
So the final version would be:
manifest.json
{
"manifest_version": 2,
"name": "I'm Not Following You",
"version": "0.1",
"content_scripts": [
{
"matches": [
"https://www.tumblr.com/dashboard",
"http://www.tumblr.com/dashboard"
],
"js": [
"jquery.js",
"hidestuff.js"
]
}
]
}
hidestuff.js
$(document).ready(function () {
var hide = $("div:has(.post_dismiss)");
for (var i = 0; i < hide.length; i++) {
var div = hide[i].getAttribute('id');
var id = "#" + div;
$(id).hide();
}
});
Content scripts do not run in the same context as scripts on the page. You will need to include every library you want to use in your package and in the content scripts manifest section.
https://developer.chrome.com/extensions/content_scripts#execution-environment
EDIT: Simplified question with MCVE and screenshot of stack.
My MCVE Extension json:
{
"manifest_version": 2,
"name": "TEST SO",
"version": "0.1",
"permissions": [ "<all_urls>", "tabs" ],
"background": { "scripts": ["background.js"] },
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"],
"all_frames": true
}
]
}
My MCVE content.js:
console.log( "content.js Started for Document at " + document.URL );
window.addEventListener('unload', function(event) {
chrome.runtime.sendMessage( "'unload' Event for Document at " + document.URL );
});
My MCVE background.js:
console.log("background.js Started");
chrome.runtime.onMessage.addListener(
function( message, sender ) {
try {
console.log( "The 'message' arg is : " + JSON.stringify(message) );
console.log( "The 'sender' TAB is : " + sender.tab.id );
} catch ( e ) {
console.log( "Exception in OnMessage Listener : " + e.message );
}
}
);
When I navigate from one site with 3 frames (one main frame and 2 subframes, say for exemple https://developer.chrome.com/extensions/messaging#native-messaging-host) to a simple site with one single frame, my background script receives 3 "messages": one for each "unload" event.
Unfortunately, only the first one (corresponding to the "unloading" of the main frame) contains a "sender" argument with a 'tab' property. The two others have no 'tab' property.
Documentation states, if I correctly read it, that the 'tab' propertie should be there.
Is that by design, a bug, or bad code on my side?
Stack showing that the sender argument has NO tab property.
Note: You may, at the beginning of the test, be able to have correct sender object, but if you then just go back and forth between the 2 sites, you quickly won't be able to have a a tab property (for the messages coming from the unload in the 2 sub frames)
Chrome 48.0.2564.82 m
Windows Seven 64 bits.
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);
I have recently gained interest in Google Chrome Extensions, so I started looking into the basics.
Using the "Getting Started" page, I set off reading the entire thing, and writing the entire code locally to create an unpacked extension.
I ensured that all files exist, and that the code was exactly the same as provided on the "Getting Started" page. For some reason, I am receiving the following error:
XMLHttpRequest cannot load http://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=90485e931f687a9b9c2a66bf58a3861a&text=hello%20world&safe_search=1&content_type=1&sort=relevance&per_page=20.
Origin chrome-extension://hcijmehjcijoldbgapgllpmhebeaiihh is not allowed by Access-Control-Allow-Origin.
I made sure that my permissions were set for "http://api.flickr.com/", as stated in the "Getting Started" page, and pretty much any other documentation I could find. I am also aware of what an Access-Control-Allow-Origin rule is, dealing with the way the request header contains. However, I am unsure as to why I am receiving this error in this case, given the proper permissions in my manifest.json file. I have considered that perhaps this has to do with something locally, perhaps a configuration issue or whatnot -- but I have no idea where else to look.
My question is: How do I resolve this error?
I am running Google Chrome version 15.0.874.121 m
For completeness, here is the code used:
manifest.json:
{
"name": "My First Extension",
"version": "1.0",
"description": "The first extension that I made.",
"browser_action": {
"default_icon": "icon.png",
"popup": "popup.html"
},
"permissions": [
"http://api.flicker.com/"
]
}
popup.html:
body {
min-width:357px;
overflow-x:hidden;
}
img {
margin:5px;
border:2px solid black;
vertical-align:middle;
width:75px;
height:75px;
}
var req = new XMLHttpRequest();
req.open(
"GET",
"http://api.flickr.com/services/rest/?" +
"method=flickr.photos.search&" +
"api_key=90485e931f687a9b9c2a66bf58a3861a&" +
"text=hello%20world&" +
"safe_search=1&" + // 1 is "safe"
"content_type=1&" + // 1 is "photos only"
"sort=relevance&" + // another good one is "interestingness-desc"
"per_page=20",
true);
req.onload = showPhotos;
req.send(null);
function showPhotos() {
var photos = req.responseXML.getElementsByTagName("photo");
for (var i = 0, photo; photo = photos[i]; i++) {
var img = document.createElement("image");
img.src = constructImageURL(photo);
document.body.appendChild(img);
}
}
// See: http://www.flickr.com/services/api/misc.urls.html
function constructImageURL(photo) {
return "http://farm" + photo.getAttribute("farm") +
".static.flickr.com/" + photo.getAttribute("server") +
"/" + photo.getAttribute("id") +
"_" + photo.getAttribute("secret") +
"_s.jpg";
}
Look more closely at the permissions in your manifest.json. I think you'll have better luck with:
"permissions": [
"http://api.flickr.com/"
]
I want to display a different menu option depending on whether a number or text is selected.
I've tried playing with content scripts but I can't get them to work in gmail which is where I need it to work. Here is what I have, it works on sites other than gmail (is it a https thing?)
Background.html
<script src="driver.js"></script>
content_script.js
document.addEventListener("mousedown", function(event){
if(event.button == 2) {
var selection = window.getSelection().toString();
chrome.extension.sendRequest({cmd: selection});
}
}, true);
driver.js
chrome.extension.onRequest.addListener(function(request) {
alert(request.cmd);
});
manifest.json
{
"name": "Context Menu Search",
"description": "Opens the selected text as keyword in a new window",
"version": "0.1",
"permissions": ["contextMenus"],
"content_scripts": [
{
"matches": ["http://*/*","https://*/*"],
"js": ["content_script.js"]
}
],
"background_page": "background.html"
}
Selection type changes context menu using chrome extension
You will have to set a listener for mouse down. There is no other way to get the selected text before the menu is created.
See this SO question:
chrome extension context menus, how to display a menu item only when there is no selection?
Here is part of the code the rest is at the link.
document.addEventListener("mousedown", function(event){
//right click
if(event.button == 2) {
if(window.getSelection().toString()) {
chrome.extension.sendRequest({cmd: "createSelectionMenu"});
} else {
chrome.extension.sendRequest({cmd: "createRegularMenu"});
}
}
}, true);