Accessing document's javascript variable from firefox extension - javascript

is it possible for Firefox extension (toolbar) to access document's variables? detailed explanation follows..
loaded document:
<script type="text/javascript">
var variableForExtension = 'something';
</script>
extension:
var win = window.top.getBrowser().selectedBrowser.contentWindow;
alert(win.variableForExtension); // undefined
it was first thing to try, and it's inaccessible this way because of security mechanisms (XPCNativeWrapper). i've read about accessing it trough wrappedJSObject and using events (adding listener to document and dispatching event from extension), but no luck. didn't try too hard, though. so, before i dig deeper ('events method' sounds like a way to go) i'd like to know is this even possible?
thanks

Yes, accessing a JS variable in content is and always was possible. Doing this the naive way wasn't safe (in the sense that a malicious web page could get chrome privileges) in older Firefox versions.
1) If you control the web page and want to pass information to the extension, you should indeed use the events technique. This worked and was/is safe in all Firefox versions.
2) If you want to read a value from the content document, you can just bypass the XPCNativeWrapper:
var win = window.top.getBrowser().selectedBrowser.contentWindow;
// By the way, this could just be
// var win = content;
// or
// var win = gBrowser.contentWindow;
alert(win.variableForExtension); // undefined
win.wrappedJSObject.variableForExtension // voila!
This was unsafe prior to Firefox 3. In Firefox 3 and later it is OK to use, you get another kind of wrapper (XPCSafeJSObjectWrapper), which looks the same as the object from the content page to your code, but ensures the content page won't be able to do anything malicious.
3) If you need to call a function in a content web page or run your own code in the page's context, it's more complicated. It was asked and answered elsewhere many times, but unfortunately never documented fully. Since this is unrelated to your question, I won't go into the details.

not so hard :)
in extension:
var jso=window.content.document.defaultView.wrappedJSObject;
now you can access any function or global variable in the webpage from the extension:
alert(jso.pagevar);
jso.pagefunction("hey");

If you are working with the new High-Level SDKs then accessing the variable via content scripts is a little different. You can't access the JavaScript objects directly from the add on code, but you can reach them from content scripts that have been attached to an open page via the unsafeWindow object. For example:
require("sdk/tabs").open({
url: 'http://www.example.com/some/page/',
onOpen: function(tab) {
var worker = tab.attach({
contentScript: 'unsafeWindow.variableForExtension = 1000;'
});
}
});
To read the variables you'll need to use the port methods on the worker variable as described in Mozilla's content script article.
Note that there are some security restrictions when dealing with objects and functions. See the articles for details.

Related

How can I define a property on the tab's main window object?

I'm trying to define the property jQuery on the window object from a content script with code in the setter. That way, when the actual jQuery object is defined, I can use it right away. I seem to be unable to get it right, though.
The target website is Outlook.com. That's the webmail version of Outlook.
I tried to put the code in the content script directly, but even if I put "all_frames": true in the content_scripts section of the manifest (so the code gets injected into every frame), it isn't working.
function afterInit(){
var _jQuery;
console.log('This gets logged');
Object.defineProperty(window, 'jQuery', {
get: function() { return _jQuery; },
set: function(newValue) {
_jQuery = $ = newValue;
console.log('This is never logged!');
$(document.body).append($('<span>I want to use jQuery here</span>'));
}
});
}
afterInit();
I verified that window.jQuery is properly defined afterwards by the actual jQuery function/object, but my setter code is never executed.
I also tried it with message passing: I send a message with the code as a string to a background script, and use executeScript to execute it on the correct tab, but this also doesn't work.
chrome.runtime.sendMessage(
{action:'jQueryPreInit', value: '('+afterInit.toString()+')();'});
And in my background script:
chrome.runtime.onMessage.addListener(function(message, sender, callback) {
switch (message.action){
case "jQueryPreInit": chrome.tabs.executeScript(sender.tab.id, {code: message.value});
}
});
If I put something else than the Object.defineProperty code in the executeScript code, that works fine. I only have problems defining the property.
(quotes are from a comment)
I want to use the jQuery provided by the page itself. I could try inserting the same jQuery file as Outlook does and hope it gets loaded from cache, but I'd rather just keep my extension as clean as possible, and use what is already available.
Your attempt at optimizing the extension is not workable / not recommended.
First off, you will not be able to use the page's code anyway because of the isolation between content script and webpage code. You cannot obtain a reference to page's own jQuery/$.
But let's for a moment suppose that you could. And then the site updates jQuery to another version, renames the jQuery object or stops using it entirely, which is outside your control. Result: your extension is broken. This is, partially, the rationale behind the isolation in the first place.
As a result of the context isolation, you are guaranteed there are no conflicts between your copy of jQuery and whatever runs on the site. So you don't need to worry about that: use your copy, and use the standard $ to access it.
Bundling a <100 KB file with your extension as a one-time download that makes sure code is available 100% of the time and with at worst disk-access latency is not making it less "clean", quite the opposite. It's a common practice and is enshrined in the docs.
Looking at your actual code, it executes in the content script context (regardless whether it's through manifest or executeScript), not in the page context. As such, no matter what the page does, $ will not be defined there.
I verified that window.jQuery is properly defined afterwards by the actual jQuery function/object [...]
I assume that you tried to execute window.jQuery in the console; by default, that executes it in the page context, not in your content script context (therefore, not reflecting the state of the content script context and not invoking your getter/setter). If you want to test your content script, you need to change top in the context drop-down above the console to your extension's context.
All that said, however,
When all is said and done, I want to use jQuery's ajaxSuccess function to execute code every time an e-mail is opened in the read pane.
Here we've got a problem. Since the content script code and webpage code are isolated, your code will never know about AJAX executing in the page's copy (not through ajaxSuccess, anyway).
Possible courses of action:
Rely on other methods to detect the event you want. Perhaps monitoring the DOM.
Inject some code into the page itself; the only way to do so is by injecting a <script> tag into the page from the content script. There, you can access the page's copy of jQuery, attach your listener and message your content script when something happens.
Rely on the background page to detect activity you need with webRequest API. This will likely intercept the AJAX calls, but will not give you the reply contents.
Final note: this may not be as simple as AJAX calls; perhaps the page maintains a WebSocket connection to get realtime push updates. Tapping into this is trickier.
Thanks to Xan, I found there are only two ways to do this.
The first is by adding a <script> element to the DOM containing the appropriate code. This is a pretty extensive StackOverflow answer on how to do that: https://stackoverflow.com/a/9517879/125938.
The second is using Javascript pseudo-URLs and the window.location object. By assigning window.location a bookmarklet-style URL containing Javascript, you also bypass certain security measures. In the content script, put:
location = 'javascript:(' + (function(){
var _jQuery;
Object.defineProperty(window, 'jQuery', {
get: function() { return _jQuery; },
set: function(newValue) {
_jQuery = $ = newValue;
console.log('totally logged!');
$('<span>jQuery stuff here</span>');
}
});
}).toString().replace(/\n/g, ' ')+')();';
The reason I/you were originally failing to define it, was because both methods of code injection we were using, caused our code to be sandboxed into isolated worlds: https://developer.chrome.com/extensions/content_scripts#execution-environment. Meaning, they share the page's DOM and could communicate through it, but they can't access each other's window object directly.

Get DOM values in browser extension

I want to read a value of DOM element. I am new to browser extensions. I came across codes which provided help in executing a code in browser context but how do I fetch a value and use in extension's context?
var value = document.getElementById('id1'); // Here document should be of browser context.
Try
var value = document.getElementById('id1').value;
// note the extra .value.
You have to access the DOM from content scripts.
If you are looking to get the currently active tab, you would do this: https://developer.chrome.com/extensions/activeTab
How this will work will depend on whether you are in browser context, content scripts or background though.
I highly recommend reading the chrome extension guides: https://developer.chrome.com/extensions/getstarted. They help quite a bit, and the different scopes of the chrome extension are crucial to understand to successfully develop on it.

Use content script to define global variables

I am creating a Firefox extension, and one feature of it that I would like is the ability for the user to inject a script or stylesheet into a specific website, rather like Greasemonkey (except that this will only be for one site). I am adding some functions for the scripts to make use of, which I intended to add from the Content Script into the main (unsafe) window. On the MDN blog, it says that they have made changes to how it should be implemented, so I have based my code on the new implementation as advised in the post, so this is what I have:
var $jq = jQuery.noConflict();//Yes, I am also injecting jQuery at the same time
console.log("created jquery object"); //This works
exportFunction($jq, unsafeWindow, {defineAs: "$jq"});
console.log("This will never be called");
But execution of the script just stops, and in the console it prints Message: TypeError: window is null.
I am testing in Firefox 28 predominantly (I can't seem to get Firefox for Ubuntu to update beyond that right now, and a whole load of issues are forcing me to use Ubuntu in a VM for this), but in Nightly 31a1 (Win7) nothing is ever injected, including a hardcoded style (that works on FF28) so I will have to figure that out at some point. (The PageMod code is here:
var lttWorker = sdk.pageMod.PageMod({
include:["*"],
/*contentScriptFile: [sdk.data.url("jquery.large.js"), sdk.data.url("scripts/bootstrapper.js")],
contentScriptWhen: "ready",*/ //This is commented to test whether it was an issue with the script. It's not.
contentStyle: "#header_bar{background-color:green;}", //This is injected in FF28 but not 31
attachTo: ["existing", "top"],
onAttach: function(){desktopNotifications({title:"attached content worker", text:"The content worker has been successfully attached"})} //This is called in FF28 but not 31
});
lttWorker.on("error", function(){callError("pageWorker failed");}); //This never gets called. Ever.
if anybody is interested)
EDIT: I have now tried it on Firefox 30b and there are still a load of issues, although they seem to be slightly different to both FF28 and 31...
First of all: These new functions are supported in Firefox 30 and later. See #canuckistani answer.
The exportFunction API is way too limited to actually inject something like jQuery with all the complex objects being or containing DOM nodes. That simply won't fly with the structured-clone algorithm that is applied to arguments.
The API is meant as a way for add-ons to communicate with pages bi-directionally, and not to inject complex libraries.
Your best bet is actually creating a script tag using the DOM APIs and putting jQuery there.

Is it possible to make an object created in console persistent throughout the session?

Goal: making a standalone modular JavaScript debuggin-utility (includes custom dom and event manipulation methods) to be used in the console (preferably Chrome) on any random sites of interests (with no backend access).
Usage: initially include module script directly via copy-paste to console or by creating a new script element pointing at myhomepage.com/shortandeasytoremember.js and call methods on the namespace from there on.
Problem: how to best make it persistent throughout the session on that webpage (so that I wouldn't need to reinclude it after every refresh) ?
Note: any additional browser compatibility is not required - as long as it works in the latest Chrome, it's all fine by me (but any effort in the compatibility department is always much appreciated for the sake of others). IF YOU READ THIS IN A FAR FUTURE and by then there exists a better solution than what is written down below, please take a moment to contribute with your superior knowledge.
What I currently have is an event listener on window.unload to save any state data to localStorage and a string to make it easier to reinclude the module after page reload using eval(localStorage.getItem('loadMyNS'));.
(function(ns, undefined){
'use strict';
//util methods on ns and few monkey patches for debugging ...
var store = 'if(!window.MyNS){' +
'var xyz9=document.createElement("script");' +
'xyz9.src="http://myhomepage.com/shortandeasytoremember.js";' +
'document.head.appendChild(xyz9);}';
localStorage.setItem('loadMyNS', store);
ns.save = function () {
// and use localStorage for some more data
// to be used by other methods after page reload
};
window.addEventListener('unload', ns.save, false);
}(window.MyNS = window.MyNS || {}));
(browsers with no localStorage or addEventListener may benifit from this article)
I've also concidered using the same schema with window.name instead of localStorage (as long as this still seems legid) just because writing eval(window.name) would take less typing ^^.
The trouble (one of them) I have with the "eval-script-tag-inclusion" is on the sites which block external non-https script sources. An ideal solution would be a globally accessible module which would live with state and methods included (and no initialization required after refresh) at least until I close the the window (or overwrite the ref ofc).
If that is currently absolutely not possible, a lesser solution yet still superior to my current code would suffice.

Javascript scope / security concern in Firefox extension

I am developing a FireFox extension and have to store some values that I need to be secure and inaccessible from any other extension/page etc.
I am using a setup for my extension code like seen here:
if(!namesp) var namesp={};
if(!namesp.anothernamesp) namesp.anothernamesp={};
namesp.anothernamesp = function() {
var mySecureValue = ''; //is this variable accessible from anything aside from inside the namesp.anothernamesp scope?
return {
useSecureValue: function() {
//do something here with mySecureValue
}
};
function getSecureValue() { //can this method be called from anywhere besides inside the namesp.anothernamesp scope?
return mySecureValue;
}
}();
Is there any way that anything other than my own extension can access "mySecureValue"? To keep this object global accessible to any windows I might open in my extension etc, I pass the object to the window in the window.openDialog() method and use the window.arguments to access it from the newly created windows. Thank you.
Seems pretty correct. In fact that's a way the majority of tutorials and books teach to simulate private methods and properties.
No, there is no way you can keep one extension from impacting another extension.
The reasons for that are:
extensions are Zip-archive-files renamed to have a *.xpi filename extension.
the extensions are writen in plaintextfiles using a JavaScript dialect
any other extension can at will open and access any file that your browser can access.
If some other extension wants to read your variable mySecureValue it can do so by:
accessing the your extensions *.xpi file (using nsIFile to read it from the profile/extensions folder)
unzip it nsIZipReader
read the variable mySecureValue from your source file!
The most unfortunate reason for all that is that Mozilla firefox does not implement any form of right separation between the extensions. Every extension can do everything to everybody. It can even excecute a shellcode and do arbitraty other damage.
The only thing you can try is to obfuscate your secret data. This will though not prevent but maybe only complicate the attack.

Categories