Adding StyleSheets to Firefox Bootstrapped Addon - javascript

Accordion to Using the Stylesheet Service
Above mentioned document also states:
loadAndRegisterSheet fails if CSS contains #id. '#' must be percent-encoded, details see bug 659650.
The bag report was made on 2011-05-25. Is it still a bug or has it been resolved?
There is another way of adding CSS but that is per window and I prefer to get this one sorted.
Update:
Here is the content of the style-sheet
#rpnethelper-separator2:last-child { display: none; }
#rpnethelper-menuitem {
list-style-image: url('icon16.png');
}
This is the actual code (plus added console calls)
register: function(css) {
let sss = Components.classes['#mozilla.org/content/style-sheet-service;1']
.getService(Components.interfaces.nsIStyleSheetService);
let cssURI = Services.io.newURI(css, null, null);
sss.loadAndRegisterSheet(cssURI, sss.USER_SHEET);
},
I tried it with try{} catch{} and I dont get any errors.
How/where can USER_SHEET be viewed?
For now, I am going to use an inline style (which doesn't support the pseudo classes) but I would still like to resolve this issue.
Final Update:
For some reason, the code that wasn't working with USER_SHEET, works fine with AUTHOR_SHEET
Funny thing is, after all that, I decided it is not worth the extra processing just for one pseudo class, so I opted for the (simple) inline style

You forgot to specify the correct namespace. Add the following as the first line to your sheet.
#namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
The docs you already linked state:
Stylesheets added using this service get applied to both chrome and content documents. Remember to declare the correct namespace if you want to apply stylesheets to XUL documents.
Also, if you're targeting Firefox 18 and later (and really, supporting earlier versions has no merit as those are unsupported and contain known security vulnerabilities, so users shouldn't be using them), you should consider using nsIDOMWindowUtils.loadSheet instead. This will only load the sheet into the actual window, instead of applying it globally to all windows incl. websites.
if (window instanceof Ci.nsIInterfaceRequestor) {
let winUtils = window.getInterface(Ci.nsIDOMWindowUtils);
let uri = Services.io.newURI(..., null, null);
winUtils.loadSheet(uri, Ci.nsIDOMWindowUtils.AUTHOR_SHEET);
// Remove with winUtils.removeSheet() again on shutdown
}
Edit You'll want to use AUTHOR_SHEET most of the time (be it with the style sheet service or window utils). This is more equivalent to xml-stylesheet in overlays.
loadAndRegisterSheet fails if CSS contains #id. '#' must be percent-encoded, details see bug 659650.
The bag report was made on 2011-05-25. Is it still a bug or has it been resolved?
That bug report only applies data: URIs. Also, that bug report is invalid, # has special meaning in URIs and therefore you'll have to encode it when it is part of the URI directly (as is the case with data: URIs). If you're registering a regular chrome:/resource:/file:/http: URI, you don't need special encoding.

Related

In newer versions of Firefox, is it still possible to override a web page's JS function?

I am writing an extension to override a web page's JS function, and started from this question, but the answer does not appear to work in Firefox 42 on Linux.
Next, I tried to use exportFunction as described in the documentation, but that also silently failed.
Inside package.json, I have added the following sesction.
"permissions": {
"unsafe-content-script": true
}
Here is my index.js file.
var self = require('sdk/self');
require("sdk/tabs").on("ready", fixGoogle);
function fixGoogle(tab) {
if (tab.url.indexOf("google.com") > -1) {
tab.attach({
contentScriptFile: self.data.url("google-script.js")
});
}
}
Here is my current data/google-script.js.
unsafeWindow.rwt=function(){};
Note that manually typing in rwt=function(){}; to the browser's console achieves the desired effect, as does using a bookmarklet (which requires clicking) but I am writing the plugin to get this automatically every time I use Google.
Is it possible to override the rwt page function using a Firefox extension? If so, what is the correct API to use?
read the documentation you've linked to, specifically the chapter titled Expose functions to page scripts - which links to exportFunction
function blah() {}
exportFunction(blah, unsafeWindow, {defineAs: 'rwt'});
It turns out that the issue is that the redefinition of the function rwt is racing against the original definition and winning. The original runs after and overrides the function I defined, thereby making it look like my redefinition had silently failed.
Once I realized that this was the problem, the easiest hack around it was to add a timeout to the redefinition inside data/google-script.js.
setTimeout(function() {
unsafeWindow.rwt=function(){};
}, 1000);
Thus, the orignal answer is still correct but simply failed to address the race condition.
Even though content scripts share the DOM, they are otherwise isolated from page scripts. As you correctly surmised, one can use unsafeWindow in Firefox to bypass this isolation.
Personally, I don't like the name of unsafeWindow for some reason ;)
Therefore I propose another way to do this: make use of the thing that's shared between these scopes, i. e. DOM.
You can create a page script from a content script:
var script = 'rwt=function()();';
document.addEventListener('DOMContentLoaded', function() {
var scriptEl = document.createElement('script');
scriptEl.textContent = script;
document.head.appendChild(scriptEl);
});
The benefit of this approach is that you can use it in environments without unsafeWindow, e. g. chrome extensions.

Extending <object> in Dart

The Dart <object> element does not support a getter to access <object>.contentDocument and thus I thought about extending the object to add the functionality.
I took a look at the implementation of the ObjectElement and I basically need to add these lines:
#DomName('HTMLObjectElement.contentDocument')
#DocsEditable()
Document get contentDocument => _blink.BlinkHTMLObjectElement.instance.contentDocument_Getter_(this);
However, I have no idea how to do this. The solution I am using at this time is with a proxy which redirects all calls to the underlying JsObject but to be honest, this is not just dirty, it impossible to maintain.
/* Updated to explain the root of all evil */
When starting the project I am working on, I wanted to display SVGs, which are uploaded by the user, on the website and let the user manipulate these SVGs by inserting additional SvgElements or removing others.
When downloading the SVGs as a String and displaying them by
container.append(new SvgElement(svgCode))
I got really strange display bugs such that embeded images in the SVGs are displaced or even removed and other bugs with masks.
The problem was solved by using an <object> tag and set setting its data attribute to the SVG's url. The SVGs are rendered correctly. That being said, another issue came up. I wasn't able to access and manipulate the SVGs DOM because it's inside an <object> tag and the tag's document cannot be accessed by using contentDocument.
When taking all this into account, there are pretty much only two options left:
I use the <object> tag with no display bugs but not being able to manipulate the SVGs or
I create new SvgElements fromt the SVG's source and append them to the DOM which let's me manipulate the SVGs but having display bugs.
Since having display bugs isn't really a solution I can only make use of the first option, using an <object> tag and working around with Javascript to access the object's contentDocument.
As you can see, accessing the contentDocument is not always a security issue and not allowing to make use of it, is just a quick and dirty solution of a problem.
When accessing the contentDocument by using a JsObject, I get a JsObject back and not an Element. Thus I do not only have to update my code pretty much everywhere, but it gets also pretty ugly since I have to use the JsObject with callMethod(blabla).
class MyObjectElement extends ObjectElement {
static bool _isRegistered = false;
static register() {
if (!_isRegistered) {
document.registerElement('my-object', MyObjectElement,
extendsTag: 'object');
_isRegistered = true;
}
}
factory MyObjectElement() {
var result = document.createElement('object', 'my-object');
return result;
}
MyObjectElement.created() : super.created();
js.JsObject get contentDocument {
// doesn't seem to work with a custom element.
return new js.JsObject.fromBrowserObject(this)['contentDocument'];
}
}
use it like
MyObjectElement.register();
var obj = new MyObjectElement()
..data =
"https://www.suntico.com/wp-content/uploads/DemoStampRotate01-e1400242575670.png";
document.body.append(obj);

error with google swiffy calling runtime.js multiple times in the same page

I have converted multiple swf files using google swiffy v5.2 and will have my new animations displayed on many different pages, most of which I do not have control of or access to. In order for the animation to work it needs the swiffy's runtime.js file, which might look something like this on the page:
<script src="https://www.gstatic.com/swiffy/v5.2/runtime.js"></script>
The problem arises when I either have multiple instances of the animation on the same page or a client has this runtime.js file included on their own. When checking the javascript console I get this error:
Uncaught TypeError: Cannot redefine property: __swiffy_override - runtime.js:186
If i was only worried about the conflict with myself I could possibly keep track of a variable or check if the script src existed already, however I do not have this luxury when a client's page may have renamed or changed the source to this file.
Is there a way to prevent the swiffy runtime.js from redefining this property when there are multiple instances of the same javascript file being included on the page?
I imagine you are seeing this problem happen when using AS3 swfs, which have Document classes applied to them. For example, say you have animationAS3.swf, which uses AnimationBaseClass.as. When it is "compiled" by Google Swiffy service the resultant JSON data will contain
{"internedStrings":["...", "AnimationBaseClass", "..."] ....}
The Google Swiffy runtime applies JavaScript's defineProperties() or perhaps defineProperty() to seal an "AnimationBaseClass" object it creates. So, when another instance of the data is loaded the Swiffy runtime attempts to do the same thing again, and the JavaScript interpreter says "Hey, I've already defined that object, I won't redefine it."
The solution I've found, which I believe is inefficient, is to rename the class before giving the data to the Swiffy runtime. Like this:
var classEnumerator = 0;
$.getJSON('animationAS3.json', function(data) {
// Due to "TypeError: Cannot redefine property: AnimationBaseClass",
// we need to enumerate the name of the class. I have no idea about
// the impact on resource usage when doing this.
var classNameIndex;
var i = data.internedStrings.length;
while(i--) {
if (data.internedStrings[i].indexOf("AnimationBaseClass") > -1) {
classNameIndex = i;
}
}
data.internedStrings[classNameIndex] = "AnimationBaseClass_" + (classEnumerator++));
}

Detect if stylesheets fail to load (not working on Firefox)

I have cobbled together a script from different sources, that helps me to put some fallbacks in place when a stylesheet fails to load (specifically for me, Pictos server is not always reliable).
This works great, but fails on Firefox for some reason, it doesn't process anything within the if statement. I've tried running it through JSHint and nothing serious is coming up.
Any ideas?
$(document).ready(function(){
$.each(document.styleSheets, function(i,sheet){
if(sheet.href==='http://get.pictos.cc/fonts/357/9') {
var rules = sheet.rules ? sheet.rules : sheet.cssRules; // Assign the stylesheet rules to a variable for testing
$('body').addClass('pictos-working');
$('.pictos-fallback').hide(); // Hide fallbacks
// If the stylesheet fails to load...
if (rules.length === 0) {
$('.pictos').hide(); // Hide Pictos tags so we don't get random letters
$('body').removeClass('pictos-working'); // Remove 'working' class
$('.pictos-fallback').show(); // Show fallbacks
}
}
});
});​
Your style sheet detection method is not reliable. cssRules is null when the style sheet originates from a different domain, because of the Same origin policy.
Instead of detecting the existence of a css rule through the cssRules object, check if a rule from the style sheet is being applied:
if ($('selector').css('property') === 'expectedvalue') {
// Loaded
} else {
// Not loaded.
}
Just some suggestions for improvement that might help:
why a nested if? since you only have two scenarios, use if then else..
are you sure that cssRules is zero when the file doesn't load? maybe there are some headers or metadata that load.. Where did you find this property?
something I just found: http://www.quirksmode.org/dom/w3c_css.html

When to use document.implementation.createHTMLDocument?

What are some use cases and is it deprecated? As I found out at http://groups.google.com/group/envjs/browse_thread/thread/6c22d0f959666009/c389fc11537f2a97 that it's "non-standard and not supported by any modern browser".
About document.implementation at http://javascript.gakaa.com/document-implementation.aspx:
Returns a reference to the W3C DOMImplementation object, which
represents, to a limited degree, the environment that makes up the
document containerthe browser, for our purposes. Methods of the object
let you see which DOM modules the browser reports supporting. This
object is also a gateway to creating virtual W3C Document and
DocumentType objects outside of the current document tree. Thus, in
Netscape 6 you can use the document.implementation property as a start
to generating a nonrendered document for external XML documents. See
the DOMImplementation object for details about the methods and their
browser support.
Given that it provides methods (such as createHTMLDocument) for creating a non-rendered document outside of the current document tree, would it be safe to feed it untrusted third party HTML input that may contain some XSS? I ask because I would like to use createHTMLDocument for traversal purposes of third party HTML input. May that be one of the use cases?
I always use this because it doesn't make requests to images, execute scripts or affect styling:
function cleanHTML( html ) {
var root = document.implementation.createHTMLDocument().body;
root.innerHTML = html;
//Manipulate the DOM here
$(root).find("script, style, img").remove(); //jQuery is not relevant, I just didn't want to write exhausting boilerplate code just to make a point
return root.innerHTML;
}
cleanHTML( '<div>hello</div><img src="google"><script>alert("hello");</script><style type="text/css">body {display: none !important;}</style>' );
//returns "<div>hello</div>" with the page unaffected
Yes. You can use this to load untrusted third-party content and strip it of dangerous tags and attributes before including it into your own document. There is some great research incorporating this trick, described at http://blog.kotowicz.net/2011/10/sad-state-of-dom-security-or-how-we-all.html.
The technique documented by Esailija above is insufficient, however. You also need to strip out most attributes. An attacker could set an onerror or onmouseover element to malicious JS. The style attribute can be used to include CSS that runs malicious JS. Iframe and other embed tags can also be abused. View source at https://html5sec.org/xssme/xssme2 to see a version of this technique.
Just a cleaner answer besides #Esailija and #Greg answers:
This function will create another document outside the tree of current document, and clean all scripts, styles and images from the new document:
function insertDocument (myHTML) {
var newHTMLDocument = document.implementation.createHTMLDocument().body;
newHTMLDocument.innerHTML = myHTML;
[].forEach.call(newHTMLDocument.querySelectorAll("script, style, img"), function(el) {el.remove(); });
documentsList.push(newHTMLDocument);
return $(newHTMLDocument.innerHTML);
}
This one is fantastic for making ajax requests and scraping the content will be faster :)

Categories

Resources