I'm writing a tiny chrome-extension, which redirects the console.log output to a http server via an ajax call. But I discovered, that I cannot overwrite the console.log function. The content script (for testing):
console.log = function() { return 0 }
it keeps the function untouched.
Anybody knows, what could be the reason? Is there maybe another way to archive this?
Content scripts run in isolated contexts. You can workaround this by creating a script tag and injecting it into the DOM.
var script = document.createElement("script")
script.textContent = "console.log = function() { return 0 }"
document.appendChild(script)
I don't have enough reputation to comment or vote, so I just put comment here and please feel free to remove my answer.
I recommend you refer how can I override console.log() and append a word at the beginning of the output? .
Related
I am currently writing a userscript for website A to access another the contents on website B. So I tried to use the GM_xmlhttpRequest to do it. However, a variable on B is written to the window property eg: window.var or responseContent.var.
However, when I tried to get the window.var, the output is undefined, which means the properties under the window variable cannot be read successfully. I guess the window object is refering to the website A but not website B, so the result is undefined (There is no window.var on A).
I am sure that the GM_xmlhttpRequest has successfully read the content of the website B because I have added console.log to see the response.responseText. I have also used the window.var to successfully visit that variable on website B by browser directly.
GM_xmlhttpRequest({
method: "GET",
url: url,
headers: {
referrer: "https://A.com"
},
onload: function (response) {
// console.log(response.responseText);
let responseContent = new Document();
responseContent = new DOMParser().parseFromString(response.responseText, "text/html");
let titleDiv = responseContent.querySelector("title");
if (titleDiv != null) {
if (titleDiv.innerText.includes("404")) {
console.log("404");
return;
} else {
console.log(responseContent.var);
console.log(window.var);
}
}
},
onerror: function (e) {
console.log(e);
}
});
I would like to retrieve content window.var on website B and show it on the console.log of A
Please help me solve the problem. Thank you in advance.
#wOxxOm's comments are on point. You cannot really get the executed javascript of another website like that. One way to go around it is to use <iframe> and post message, just like #wOxxOm said. But that may fail if the other website has policy against iframes.
If this userscript is just for your use, another way is to have two scripts, one for each website and have them both open in browser tabs. Then again you can use postMessage to have those two scripts communicate the information. Dirty solution for your second userscript would be to just post the variable info on regular interval:
// Userscript for website-b.com
// needs #grant for unsafe-window to get the window.var
setInterval(()=>{postMessage(unsafeWindow.var, "website-a.com");}, 1000);
That would send an update of var's value every second. It's not very elegant, but it's simple and works. For a more elegant solution, you may want to first postMessage from website a that will trigger postMessage(unsafeWindow.var, "website-a.com"). Working with that further, you will soon find yourself inventing an asynchronous communication protocol.
Alternatively, if the second website is simple, you can try to parse the value of var directly from HTML, or wherever the value is coming from. That's a preferred solution, but requires reverse-engineering on your part.
As the title says, I'm trying to intercept script requests from the user's page, make a GET request to the script url from the background, add a bit of functionality and send it back to the user.
A few caveats:
I don't want to do this with every script request
I still have to guarantee that the script tags are executed in the original order
So far I came with two solutions, none of which work properly. The basic code:
chrome.webRequest.onBeforeRequest.addListener(
function handleRequest(request) {
// First I make the get request for the script myself SYNCHRONOUSLY,
// because the webRequest API cannot handle async.
const syncRequest = new XMLHttpRequest();
syncRequest.open('GET', request.url, false);
syncRequest.send(null);
const code = syncRequest.responseText;
},
{ urls: ['<all_urls>'] },
['blocking'],
);
Now once we have the code, there are two approaches that I've tried to insert it back into the page.
I send the code through a port to a content script, that will add it to the page inside a <script></script> tag. Along with the code, I also send an index to keep sure the scripts are inserted back into the page in the correct order. This works fine for my dummy website, but it breaks on bigger apps, like youtube, where it fails to load the image of most videos. Any tips on why this happens?
I return a redirect to a data url:
if (condition) return { cancel: false }
else return { redirectUrl: 'data:application/javascript; charset=utf-8,'.concat(alteredCode) };
This second options breaks the code formatting, sometimes removing the space, sometimes cutting it short. I'm not sure on the reason behind this behavior, it might have something to do with data url spec.
I'm stuck. I've researched pretty much every related answer on this website and couldn't find anything. Any help or information is greatly appreciated!
Thanks for your time!!!
This question already has answers here:
Chrome Extension Message passing: response not sent
(3 answers)
Chrome Extension Message Passing [duplicate]
(1 answer)
Closed 8 years ago.
I've got a very odd problem. I have an extension that takes a screenshot of webpages. I created it using this extension as a reference. It works perfectly and the world is happy.
The extension communicates back and forth with a content script and it uses deprecated APIs to do so. In an effort to keep the extension from using outdated code, I went ahead and made the following replacements:
chrome.tabs.sendRequest(...) -> chrome.tabs.sendMessage(...)
chrome.extension.onRequest(...) -> chrome.runtime.onMessage(...)
chrome.extension.sendRequest(...) -> chrome.runtime.sendMessage(...)
Doing that breaks the extension and it no longer works. If I use the deprecated code, it all works fine again.
I did some tracking and discovered that the extension breaks at the following code:
CONTENT SCRIPT
chrome.runtime.sendMessage(sender.id, data, function(captured) {
window.clearTimeout(cleanUpTimeout);
console.log("came back from extension...");
if (captured) {
// Move on to capture next arrangement.
processArrangements(); //function defined elsewhere
} else {
// If there's an error in popup.js, the response value can be
// undefined, so cleanup
cleanUp(); // function defined elsewhere
}
});
EXTENSION
chrome.runtime.onMessage.addListener(function(message, sender, cb){
appendLog("received message from content script. callback is " + cb);
if (message.msg === 'capturePage') {
capturePage(message, sender, cb);
} else {
console.error('Unknown message received from content script: ' + message.msg);
}
});
function capturePage(data, sender, cb) {
... // some code omitted for clarity
chrome.tabs.captureVisibleTab(
null, {format: 'png', quality: 100}, function(dataURI) {
if (dataURI) {
var image = new Image();
image.onload = function() {
screenshot.ctx.drawImage(image, data.x, data.y);
//appendLog("calling callback function. callback is: " + cb);
cb(true); // **callback isn't called on content script**
};
image.src = dataURI;
}
});
}
I've omitted some code for clarity and the missing code is irrelevant. Let me explain what you see up there:
On the extension, inside the capturePage function, the line cb(true) executes. I can confirm it does because I'm able to see the "calling callback function" that comes right before it. However, the callback code on the content script does NOT run. Again, I can confirm this because I don't see the "came back from extension" message.
Here's the bizarre part: if I call cb(true) BEFORE I call chrome.tabs.captureVisibleTab(), the callback code on the content script DOES execute normally. That means chrome.tabs.captureVisibleTab() is interfering with the execution of cb(true) on the content script somehow. Maybe a port is closed when I call captureVisibleTab() - I'm not sure. The fact of the matter is that cb(true) will not execute on the content script after calling captureVisibleTab() but it will execute if I call it before.
On the extension, I tried storing the callback function, cb, in a variable outside capturePage() and then calling another function which would then call cb... but that doesn't work either.
I added the "" permission to my extension thinking maybe that would change things, but no. That changes nothing.
Again, if I replace the 4 lines of code where the deprecated APIs are, my extension works as expected. Thus, the problem is a combination of sendMessage() and captureVisibleTab(). There is no syntax error and I'm not leaving anything out. If I omit the call to captureVisibleTab(), the cb call will execute normally on the content script. What on earth???
How bizarre is that? I don't want to leave those deprecated API calls there. But then again, what could possibly be preventing the call back code to execute on the content script? I'm set on thinking the port is closed/replaced when we call captureVisibleTab(), but I'm not really sure and I've investigating it further.
I came across this online but I'm not sure if it's related to my issue. I also couldn't find anything similar here on stackoverflow and hence I created this question. Can anyone shed some light on this?
Thanks
I have a chrome extension which injects an iframe into every open tab. I have a chrome.runtime.onInstalled listener in my background.js which manually injects the required scripts as follows (Details of the API here : http://developer.chrome.com/extensions/runtime.html#event-onInstalled ) :
background.js
var injectIframeInAllTabs = function(){
console.log("reinject content scripts into all tabs");
var manifest = chrome.app.getDetails();
chrome.windows.getAll({},function(windows){
for( var win in windows ){
chrome.tabs.getAllInWindow(win.id, function reloadTabs(tabs) {
for (var i in tabs) {
var scripts = manifest.content_scripts[0].js;
console.log("content scripts ", scripts);
var k = 0, s = scripts.length;
for( ; k < s; k++ ) {
chrome.tabs.executeScript(tabs[i].id, {
file: scripts[k]
});
}
}
});
}
});
};
This works fine when I first install the extension. I want to do the same when my extension is updated. If I run the same script on update as well, I do not see a new iframe injected. Not only that, if I try to send a message to my content script AFTER the update, none of the messages go through to the content script. I have seen other people also running into the same issue on SO (Chrome: message content-script on runtime.onInstalled). What is the correct way of removing old content scripts and injecting new ones after chrome extension update?
When the extension is updated Chrome automatically cuts off all the "old" content scripts from talking to the background page and they also throw an exception if the old content script does try to communicate with the runtime. This was the missing piece for me. All I did was, in chrome.runtime.onInstalled in bg.js, I call the same method as posted in the question. That injects another iframe that talks to the correct runtime. At some point in time, the old content scripts tries to talk to the runtime which fails. I catch that exception and just wipeout the old content script. Also note that, each iframe gets injected into its own "isolated world" (Isolated world is explained here: http://www.youtube.com/watch?v=laLudeUmXHM) hence newly injected iframe cannot clear out the old lingering iframe.
Hope this helps someone in future!
There is no way to "remove" old content scripts (Apart from reloading the page in question using window.location.reload, which would be bad)
If you want to be more flexible about what code you execute in your content script, use the "code" parameter in the executeScript function, that lets you pass in a raw string with javascript code. If your content script is just one big function (i.e. content_script_function) which lives in background.js
in background.js:
function content_script_function(relevant_background_script_info) {
// this function will be serialized as a string using .toString()
// and will be called in the context of the content script page
// do your content script stuff here...
}
function execute_script_in_content_page(info) {
chrome.tabs.executeScript(tabid,
{code: "(" + content_script_function.toString() + ")(" +
JSON.stringify(info) + ");"});
}
chrome.tabs.onUpdated.addListener(
execute_script_in_content_page.bind( { reason: 'onUpdated',
otherinfo: chrome.app.getDetails() });
chrome.runtime.onInstalled.addListener(
execute_script_in_content_page.bind( { reason: 'onInstalled',
otherinfo: chrome.app.getDetails() });
)
Where relevant_background_script_info contains information about the background page, i.e. which version it is, whether there was an upgrade event, and why the function is being called. The content script page still maintains all its relevant state. This way you have full control over how to handle an "upgrade" event.
I am working with a node.js project (using Wikistream as a basis, so not totally my own code) which streams real-time wikipedia edits. The code breaks each edit down into its component parts and stores it as an object (See the gist at https://gist.github.com/2770152). One of the parts is a URL. I am wondering if it is possible, when parsing each edit, to scrape the URL for each edit that shows the differences between the pre-edited and post edited wikipedia page, grab the difference (inside a span class called 'diffchange diffchange-inline', for example) and add that as another property of the object. Right not it could just be a string, does not have to be fully structured.
I've tried using nodeio and have some code like this (i am specifically trying to only scrape edits that have been marked in the comments (m[6]) as possible vandalism):
if (m[6].match(/vandal/) && namespace === "article"){
nodeio.scrape(function(){
this.getHtml(m[3], function(err, $){
//console.log('getting HTML, boss.');
console.log(err);
var output = [];
$('span.diffchange.diffchange-inline').each(function(scraped){
output.push(scraped.text);
});
vandalContent = output.toString();
});
});
} else {
vandalContent = "no content";
}
When it hits the conditional statement it scrapes one time and then the program closes out. It does not store the desired content as a property of the object. If the condition is not met, it does store a vandalContent property set to "no content".
What I am wondering is: Is it even possible to scrape like this on the fly? is the scraping bogging the program down? Are there other suggested ways to get a similar result?
I haven't used nodeio yet, but the signature looks to be an async callback, so from the program flow perspective, that happens in the background and therefore does not block the next statement from occurring (next statement being whatever is outside your if block).
It looks like you're trying to do it sequentially, which means you need to either rethink what you want your callback to do or else force it to be sequential by putting the whole thing in a while loop that exits only when you have vandalcontent (which I wouldn't recommend).
For a test, try doing a console.log on your vandalContent in the callback and see what it spits out.