Accessing the console log commands via chrome extension - javascript

I'm looking to override the existing console commands via my Chrome extension - the reason for this is I wish to record the console logs for a specific site.
Unfortunately I cannot seem to update the DOM, this is what i've tried so far:
// Run functions on page change
chrome.tabs.onUpdated.addListener( function (tabId, changeInfo, tab) {
var s = document.createElement('script');
// TODO: add "script.js" to web_accessible_resources in manifest.json
s.src = chrome.runtime.getURL('core/js/app/console.js');
s.onload = function() {
this.remove();
};
(document.head || document.documentElement).appendChild(s);
});
console.js
// Replace functionality of console log
console.defaultLog = console.log.bind(console);
console.logs = [];
console.log = function(){
console.defaultLog.apply(console, arguments);
console.logs.push(Array.from(arguments));
};
// Replace functionality of console error
console.defaultError = console.error.bind(console);
console.errors = [];
console.error = function(){
console.defaultError.apply(console, arguments);
console.errors.push(Array.from(arguments));
};
// Replace functionality of console warn
console.defaultWarn = console.warn.bind(console);
console.warns = [];
console.warn = function(){
console.defaultWarn.apply(console, arguments);
console.warns.push(Array.from(arguments));
};
// Replace functionality of console debug
console.defaultDebug = console.debug.bind(console);
console.debugs = [];
console.debug = function(){
console.defaultDebug.apply(console, arguments);
console.debugs.push(Array.from(arguments));
};
The script runs successfully with an alert().
The goal for me is to access console.logs - but its undefined which means I haven't gotten access to the DOM, despite injecting a script.
If not possible, even a third party integration would be helpful i.e. Java or C?
Any thoughts would be greatly appreciated :)

I found this post and I think Tampermonkey injects a script with the immediate function that you add in the Tampermonkey Chrome extension page, I found something similar in extensions like Wappalyzer, and looks good and safe, you could use WebRequest to inject to your website the new "polyfill" before the page is fully loaded as the post says.
Here the example of Wappalyzer that I mentioned before, this is the JS load in StackOverflow with Wappalyzer using the code injection, I didn't test it with Tampermonkey yet
EDIT
Checking Wappalyzer, how to inject the code is the easy part, you can use (Wappalyzer github example):
const script = document.createElement('script')
script.setAttribute('src', chrome.extension.getURL('js/inject.js'))
This probably will not fix your problem, this code is executed after all the content was loaded in the DOM. But, you can find how to fix that problem in this post
I'll suggest to use onCommitted event (doc1/doc2)
Using the mozilla.org example you will have something like
const filter = {
url: //website to track logs
[
{hostContains: "example.com"},
{hostPrefix: "developer"}
]
}
function logOnCommitted(details) {
//Inject Script on webpage
}
browser.webNavigation.onCommitted.addListener(logOnCommitted, filter);

It might be worth trying to redefine the entire console object:
const saved = window.console
window.console = {...saved, log: function(...args){ saved.log("Hello", ...args) }}
But it's probably impossible, because content scripts live in an isolated world:
Isolated worlds do not allow for content scripts, the extension, and the web page to access any variables or functions created by the others. This also gives content scripts the ability to enable functionality that should not be accessible to the web page.
Although in Tampermonkey this script works.
I believe Tampermonkey handles this by knowing the subtleties and tracking changes in the extensions host's protection mechanism.
BTW, for small tasks, there is a decent alternative to chrome extensions in the form of code snippets.

Related

How to make a simple firefox addon, which just executes stand-alone JS script on every new page?

I'm trying to make a simple firefox add-on, executing a simple stand-alone JS-script on every new page (say, just a simple alert after page loaded).
First, I've tried add-on sdk. It have been installed successfully, run tests, but was not able to execute even examples from any tutorial, so i tried to try XUL.
I downloaded 'xulschoolhello.xpi', unpacked it, modified content/browserOverlay.xul to this:
<?xml version="1.0"?>
<!DOCTYPE overlay SYSTEM
"chrome://xulschoolhello/locale/browserOverlay.dtd">
<overlay id="xulschoolhello-browser-overlay"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/x-javascript"
src="chrome://xulschoolhello/content/browserOverlay.js" />
</overlay>
and content/browserOverlay.js to this just to see if something happens:
window.alert(123)
but nothing happens after zip packing, installation and rebooting.
I'm quiet new to firefox extensions, so thanks for any help.
UPD.
I tried to make a very simple bootstrap.js:
var WindowListener = {
onOpenWindow: function(window) {
window.alert(123)
}
}
function startup(data, reason) {
var wm = Components.classes["#mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator);
wm.addListener(WindowListener);
}
function shutdown(data, reason) {}
function install(data, reason) {}
function uninstall(data, reason) {}
But it doestn alert. What am i doing wrong here?
This is a fully working bootstap addon that runs javascript everytime a url matching hostPatern of bing.com is loaded. Download the xpi from this gist here and install it to see it working:
https://gist.github.com/Noitidart/9287185
If you want to use this for yourself, then just edit the addDiv and removeDiv functions on the js you want to run. And to control which site edit hostPattern global var and decide if you want to listen to frames by setting global var of ignoreFrames.
To accomplish this with addon-sdk you do it like this:
var pageMod = require("page-mod");
const data = require("self").data;
exports.main = function() {
pageMod.PageMod({
include: ["https://www.bing.com/*","http://www.google.com/*"],
contentScriptWhen: 'ready',
/*contentScriptFile: [data.url("jquery.js"),data.url("script.js")],*/
onAttach: function(worker) {
console.log('loaded a page of interest');
}
});
I'm not too familiar with addon-sdk like i dont know how to set up the environment, but i heard once you get it setup its pretty simple. As you can see by comparing the amount of code. But in the bootstrap version you have fine control over everything thats why I prefer bootstrap, and most of the codes are cookie-cutter copy and paste.

Possible to run userscript in IE11

I have a custom userscript that I'm running in Chrome and Firefox using Tampermonkey/Greasemonkey.
Is there any way of using this script in IE11? Or is there any plugins for IE11 that does what Tampermonkey/Greasemonkey does?
TrixIE WPF4.5 claims to emulate Greasemonkey on IE11.
Unfortunately, the original Trixie and IE7Pro stopped working around IE8-ish.
I use localStorage to make it work, which is supported by IE8 or later.
Steps:
Run the following code in IE's developer tool when the current window is in the domain where you want the script to run in:
var scriptName = 'Hello world';
function scriptBody(){
//---userscript starts--->
document.body.innerHTML = '<h1>Hello world!</h1>';
//---userscript ends--->
}
var script = scriptBody.toString()
.split('//---userscript starts--->')[1]
.split('//---userscript ends--->')[0];
localStorage.setItem(scriptName, script);
Create a bookmark and modify the URL into:
javascript:(function(){eval(localStorage.getItem('Hello world'));})()
Advantages:
No additional plugin needed.
Almost no script text length limit.
Disadvantages:
Need a user to click on a bookmark to run the script.
Need a reinstall if a user clears the browser cache.
A simple Google Search (I searched "greasemonkey for IE") yields various alternatives available for other browsers:
http://en.wikipedia.org/wiki/Greasemonkey#Equivalents_for_other_browsers
For Internet Explorer, similar functionality is offered by IE7Pro,[19] Sleipnir,[20] and iMacros.
Fiddler supports modifying the response of http requests.
We can use this feature to load userscript in any browser, include IE8.
This is an example:
static function OnBeforeResponse(oSession: Session) {
if (m_Hide304s && oSession.responseCode == 304) {
oSession["ui-hide"] = "true";
}
// match url
if (oSession.fullUrl == "http://apply.ccopyright.com.cn/goadatadic/getR11List.do") {
oSession.utilDecodeResponse();
var script = System.IO.File.ReadAllText("C:\\GitHub\\#selpic\\P660_printer\\Printer\\scripts\\form-save-load.js")
oSession.utilReplaceOnceInResponse("</body>", "<script>"+script+"</script></body>", true);
}
}
doc: Modifying a Request or Response
Just open Developer tools (press F12) and paste your script to Console, and then run it (Ctrl + Enter).

Simplest way to launch Firefox, drive 3rd party site using privileged nsI* APIs

What's the simplest way to launch Firefox, load a 3rd party website (which I'm authorised to "automate"), and run some "privileged" APIs against that site? (e.g: nsIProgressListener, nsIWindowMediator, etc).
I've tried a two approaches:
Create a tabbed browser using XULrunner, "plumbing" all the appropriate APIs required for the 3rd party site to open new windows, follow 302 redirects, etc. Doing it this way, it's an aweful lot of code, and requires (afaict) that the user installs the app, or runs Firefox with -app. It's also extremely fragile. :-/
Launch Firefox passing URL of the 3rd party site, with MozRepl already listening. Then shortly after startup, telnet from the "launch" script to MozRepl, use mozIJSSubScriptLoader::loadSubScript to load my code, then execute my code from MozRepl in the context of the 3rd party site -- this is the way I'm currently doing it.
With the first approach, I'm getting lots of security issues (obviously) to work around, and it seems like I'm writing 10x more browser "plumbing" code then automation code.
With the second approach, I'm seeing lots of "timing issues", i.e:
the 3rd party site is somehow prevented from loading by MozRepl (or the execution of the privileged code I supply)???, or
the 3rd party site loads, but code executed by MozRepl doesn't see it load, or
the 3rd party site loads, and MozRepl isn't ready to take requests (despite other JavaScript running in the page, and port 4242 being bound by the Firefox process),
etc.
I thought about maybe doing something like this:
Modify the MozRepl source in some way to load privileged JavaScript from a predictable place in the filesystem at start-up (or interact with Firefox command-line arguments) and execute it in the context of the 3rd party website.
... or even write another similar add-on which is more dedicated to the task.
Any simpler ideas?
Update:
After a lot of trial-and-error, answered my own question (below).
I found the easiest way was to write a purpose-built Firefox extension!
Step 1. I didn't want to do a bunch of unnecessary XUL/addon related stuff that wasn't necessary; A "Bootstrapped" (or re-startless) extension needs only an install.rdf file to identify the addon, and a bootstrap.js file to implement the bootstrap interface.
Bootstrapped Extension: https://developer.mozilla.org/en-US/docs/Extensions/Bootstrapped_extensions
Good example: http://blog.fpmurphy.com/2011/02/firefox-4-restartless-add-ons.html
The bootstrap interface can be implemented very simply:
const path = '/PATH/TO/EXTERNAL/CODE.js';
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
var loaderSvc = Cc["#mozilla.org/moz/jssubscript-loader;1"];
.getService(Ci.mozIJSSubScriptLoader);
function install() {}
function uninstall() {}
function shutdown(data, reason) {}
function startup(data, reason) { loaderSvc.loadSubScript("file://"+path); }
You compile the extension by putting install.rdf and bootstrap.js into the top-level of a new zip file, and rename the zip file extension to .xpi.
Step 2. To have a repeatable environment for production & testing, I found the easiest way was to launch Firefox with a profile dedicated to the automation task:
Launch the Firefox profile manager: firefox -ProfileManager
Create a new profile, specifying the location for easy re-use (I called mine testing-profile) and then exit the profile manager.
Remove the new profile from profiles.ini in your user's mozilla config (so that it won't interfere with normal browsing).
Launch Firefox with that profile: firefox -profile /path/to/testing-profile
Install the extension from the file-system (rather than addons.mozilla.org).
Do anything else needed to prepare the profile. (e.g: I needed to add 3rd party certificates and allow pop-up windows for the relevant domain.)
Leave a single about:blank tab open, then exit Firefox.
Snapshot the profile: tar cvf testing-profile-snapshot.tar /path/to/testing-profile
From that point onward, every time I run the automation, I unpack testing-profile-snapshot.tar over the existing testing-profile folder and run firefox -profile /path/to/testing-profile about:blank to use the "pristine" profile.
Step 3. So now when I launch Firefox with the testing-profile it will "include" the external code at /PATH/TO/EXTERNAL/CODE.js on each start-up.
NOTE: I found that I had to move the /PATH/TO/EXTERNAL/ folder elsewhere during step 2 above, as the external JavaScript code would be cached (!!! - undesirable during development) inside the profile (i.e: changes to the external code wouldn't be seen on next launch).
The external code is privileged and can use any of the Mozilla platform APIs. There is however an issue of timing. The moment-in-time at which the external code is included (and hence executed) is one at which no Chrome window objects (and so no DOMWindow objects) yet exist.
So then we need to wait around until there's a useful DOMWindow object:
// useful services.
Cu.import("resource://gre/modules/Services.jsm");
var loader = Cc["#mozilla.org/moz/jssubscript-loader;1"]
.getService(Ci.mozIJSSubScriptLoader);
var wmSvc = Cc["#mozilla.org/appshell/window-mediator;1"]
.getService(Ci.nsIWindowMediator);
var logSvc = Cc["#mozilla.org/consoleservice;1"]
.getService(Ci.nsIConsoleService);
// "user" code entry point.
function user_code() {
// your code here!
// window, gBrowser, etc work as per MozRepl!
}
// get the gBrowser, first (about:blank) domWindow,
// and set up common globals.
var done_startup = 0;
var windowListener;
function do_startup(win) {
if (done_startup) return;
done_startup = 1;
wm.removeListener(windowListener);
var browserEnum = wm.getEnumerator("navigator:browser");
var browserWin = browserEnum.getNext();
var tabbrowser = browserWin.gBrowser;
var currentBrowser = tabbrowser.getBrowserAtIndex(0);
var domWindow = currentBrowser.contentWindow;
window = domWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShellTreeItem)
.rootTreeItem.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
gBrowser = window.gBrowser;
setTimeout = window.setTimeout;
setInterval = window.setInterval;
alert = function(message) {
Services.prompt.alert(null, "alert", message);
};
console = {
log: function(message) {
logSvc.logStringMessage(message);
}
};
// the first domWindow will finish loading a little later than gBrowser...
gBrowser.addEventListener('load', function() {
gBrowser.removeEventListener('load', arguments.callee, true);
user_code();
}, true);
}
// window listener implementation
windowListener = {
onWindowTitleChange: function(aWindow, aTitle) {},
onCloseWindow: function(aWindow) {},
onOpenWindow: function(aWindow) {
var win = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
win.addEventListener("load", function(aEvent) {
win.removeEventListener("load", arguments.callee, false);
if (aEvent.originalTarget.nodeName != "#document") return;
do_startup();
}
};
// CODE ENTRY POINT!
wm.addListener(windowListener);
Step 4. All of that code executes in the "global" scope. If you later need to load other JavaScript files (e.g: jQuery), call loadSubscript explicitly within the null (global!) scope
function some_user_code() {
loader.loadSubScript.call(null,"file:///PATH/TO/SOME/CODE.js");
loader.loadSubScript.call(null,"http://HOST/PATH/TO/jquery.js");
$ = jQuery = window.$;
}
Now we can use jQuery on any DOMWindow by passing <DOMWindow>.document as the second parameter to the selector call!

How to overwrite a function in the web page with Chrome extension?

Suppose there is a web site has a global namespace Q = {}, and there is a function under it: Q.foo
I'd like to overwrite this function in my chrome extension, so when the web page calls Q.foo, it would do what I like.
I tried to write:
Q.foo = function(){
alert("over written");
}
with content script. But it doesn't work....
thanks.
The main problem iis that a chrome extension exists in a separated enviornment which was created so that extension developers cant screw with the existing page's javascript and vice versa.
However geeky people can do this:
document.head.innerHTML += '<script>Q.foo = function(){alert("over written");}</script>';
Basically what this does is that it appends a script tag into the dom which is then instantly eval'd in the context of the page.
Q.prototype.foo = function(){
alert("over written");
}

Accessing XUL Elements using Firefox Add-on SDK

I'm trying to manipulate the XUL elements in the Firefox add-on page using the Add-on SDK. I wouldn't mind using lower-level modules. I used DOM inspector to see the structure for the add-on page. It looks like this for the add-on page:
#document
--page (id='addons-page', windowtype='Addons:Manager', etc.)
----...
----hbox
----hbox
----etc.
So I tried this bit of code in exports.main:
let delegate = {
onTrack: function(window) {
console.log('window is being tracked: ' + window); // outputs [object ChromeWindow
let doc = window.document;
var addOnPage = doc.getElementById('addons-page');
console.log(window.document.page); // outputs undefined
console.log(addOnPage); // outputs null
var xulElements = window.document.getElementsByClassName('addon-control');
console.log('our elements: ' + xulElements); // outputs [object HTMLCollection]
console.log('our elements length: ' + xulElements.length); // outputs length of 0
}
};
var tracker = new winUtils.WindowTracker(delegate);
The first problem is that the window tracker only open when Firefox is first started. How can I get it to listen and wait for the add-on page to be opened?
The second problem (probably related to the first) is that getting the elements doesn't seem to be working (xulElements.length is 0).
Any ideas?
Two issues here:
Add-on Manager doesn't usually open as a separate window so using WindowTracker is pointless. It is a page loaded into the browser.
You access the window before it has a chance to load to it isn't surprising that you don't see any elements.
Given that page-mod module doesn't seem to work for this page, listening to the chrome-document-global-created notification is probably the best solution. This code works for me:
var observers = require("observer-service");
observers.add("chrome-document-global-created", function(wnd)
{
if (wnd.location.href == "about:addons")
{
// Wait for the window to load before accessing it
wnd.addEventListener("load", function()
{
console.log(wnd.document.getElementsByClassName('addon-control').length);
}, false);
}
});

Categories

Resources