I'm building a Firefox Extension that, among other things, needs to replace body background color on one website. I'm interested in methods that work primarily on Firefox 4, compatibility with older versions of Firefox is not that important.
I have already done this in Chrome, Opera and Safari extensions, but I'm unable to do the same thing in Firefox extension after several hours of reading and trying various stuff. Please help.
I apologize in advance for such a long post, I tried to shorten it as much as possible.
What I tried so far:
1. DOMContentLoaded
This method does replace the color but it's doing it too late, Firefox first shows the original color and than replaces it with my color. It appears that my function is not executed until adsense or facebook widgets from that website do their job. If user has some kind of ad blocker than it works fine, but that obviously doesn't mean anything for my extension.
gBrowser.addEventListener("DOMContentLoaded", my_function, true);
my_function: function(event)
{
var doc = event.originalTarget;
doc.body.style.background = "#f00";
}
2. Observers
I tried these three observer types:
- chrome-document-global-created
- content-document-global-created
- document-element-inserted
I can't access body element from any of those listed observers.
Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService).addObserver(
{
observe: function(aSubject, aTopic, aData)
{
var doc = aSubject;
// doc.location - contains the url
// doc - [object XrayWrapper [object Window]]
// doc.document - [object XrayWrapper [object HTMLDocument]]
// doc.document.body - null
doc.document.body.style.background = "#f00"; // this will obviously not work because body is not yet created or I'm looking in the wrong place
}
}, "content-document-global-created", false);
开发者_开发技巧
3. WebProgressListener with NOTIFY_STATE_DOCUMENT
From STATE_TRANSFERRING I can't access body, from STATE_STOP it's too late.
browser.webProgress.addProgressListener(this, browser.webProgress.NOTIFY_STATE_DOCUMENT);
onStateChange: function(aWebProgress, aRequest, aFlag, aStatus)
{
var doc = aWebProgress.DOMWindow;
if (aFlag & Components.interfaces.nsIWebProgressListener.STATE_TRANSFERRING)
{
// doc.document.body - null
doc.document.body.style.background = "#f00"; // again this will obviously not work because body is not yet created or I'm looking in the wrong place
}
if (aFlag & Components.interfaces.nsIWebProgressListener.STATE_STOP)
{
// doc.document.body - [object XrayWrapper [object HTMLBodyElement]]
doc.document.body.style.background = "#f00"; // works but the same way DOMContentLoaded does, too slow
}
}
4. WebProgressListener with NOTIFY_PROGRESS (or NOTIFY_STATE_REQUEST or NOTIFY_STATE_ALL)
This does change the color right away, but I'm getting SO MUCH events for every website and resource that user accesses that I'm not comfortable using this for such a simple thing as changing a background color for just one website.
browser.webProgress.addProgressListener(this, browser.webProgress.NOTIFY_PROGRESS);
onProgressChange: function(aWebProgress, aRequest, curSelf, maxSelf, curTot, maxTot)
{
var doc = aWebProgress.DOMWindow;
doc.document.body.style.background = "#f00";
}
Is there perhaps any other method where I could access body element as soon as it's created? Or maybe some other method to change webpage styling (without involving some other extensions of course)? Could I maybe somehow attach NOTIFY_PROGRESS listener to just one website?
Finally I made something that shouldn't be a great performance impact and it changes background color right away.
I made two listeners: NOTIFY_LOCATION and NOTIFY_PROGRESS. I use first one (NOTIFY_LOCATION) to see when user opens the website I'm trying to change color on, once user opens that website I set up NOTIFY_PROGRESS listener on that tab. From inside NOTIFY_PROGRESS listener I check for presence of body element, as soon as body is accessible I set the color and remove the NOTIFY_PROGRESS listener because I don't need it anymore.
listener_location =
{
init: function()
{
gBrowser.browsers.forEach(function(browser)
{
browser.webProgress.addProgressListener(this, browser.webProgress.NOTIFY_LOCATION);
}, this);
gBrowser.tabContainer.addEventListener("TabOpen", this, false);
gBrowser.tabContainer.addEventListener("TabClose", this, false);
},
uninit: function()
{
gBrowser.browsers.forEach(function(browser)
{
browser.webProgress.removeProgressListener(this);
}, this);
gBrowser.tabContainer.removeEventListener("TabOpen", this, false);
gBrowser.tabContainer.removeEventListener("TabClose", this, false);
},
handleEvent: function(aEvent)
{
var tab = aEvent.target;
var webProgress = gBrowser.getBrowserForTab(tab).webProgress;
if (aEvent.type === "TabOpen")
{
webProgress.addProgressListener(this, webProgress.NOTIFY_LOCATION);
}
else
{
webProgress.removeProgressListener(this);
}
},
QueryInterface: function(aIID)
{
if (aIID.equals(Components.interfaces.nsIWebProgressListener) ||
aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
aIID.equals(Components.interfaces.nsISupports))
return this;
throw Components.results.NS_NOINTERFACE;
},
onLocationChange: function(aProgress, aRequest, aURI)
{
if (aURI.host.indexOf("www.example.com") !== -1)
{
aProgress.addProgressListener(listener_progress, aProgress.NOTIFY_PROGRESS);
}
}
};
listener_location.init();
listener_progress =
{
QueryInterface: function(aIID)
{
if (aIID.equals(Components.interfaces.nsIWebProgressListener) ||
aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
aIID.equals(Components.interfaces.nsISupports))
return this;
throw Components.results.NS_NOINTERFACE;
},
onProgressChange: function(aWebProgress, aRequest, curSelf, maxSelf, curTot, maxTot)
{
if (aWebProgress.DOMWindow.document.body)
{
aWebProgress.removeProgressListener(this); // remove listener
var doc = aWebProgress.DOMWindow.document;
doc.body.style.background = "#f00"; // set color
}
}
};
In case someone else tries to use this code it might be useful to read these:
https://developer.mozilla.org/en/XUL_School/Intercepting_Page_Loads
https://developer.mozilla.org/en/Code_snippets/Progress_Listeners
https://developer.mozilla.org/en/XPCOM_Interface_Reference/nsIWebProgressListener
It might not be possible to get your hands on the body element before it is displayed so you can change the CSS style. An alternative would be to add a new rule to the document stylesheet, which you should be able to do via document.styleSheets in your web progress listener. Just add a rule (make it !important if necessary) that forces the background of the body to the color you want, and it will be applied when the body is displayed.
精彩评论