Skip to content

Instantly share code, notes, and snippets.

@Noitidart
Created January 30, 2014 07:30
Show Gist options
  • Save Noitidart/8704127 to your computer and use it in GitHub Desktop.
Save Noitidart/8704127 to your computer and use it in GitHub Desktop.
Demo on how to create ff-addon that listens to page loads and inserts a DIV element into it. This example inserts a blue div 200x200 pixels at top left if mozillazine is found in the address. This demo uses an alternative method to window listeners, it uses the observer service and document-element-inserted, this watches chrome windows which do …
const {interfaces: Ci, utils: Cu} = Components;
Cu.import('resource://gre/modules/Services.jsm');
const locationToMatch = 'mozillazine';
function addDiv(theDoc) {
//Cu.reportError('addDiv');
if (!theDoc) {
Cu.reportError('no theDoc!')
//document not provided, it is undefined likely
return;
}
var location = (theDoc.location + '');
if (location.toLowerCase().indexOf(locationToMatch) == -1) {
return;
}
//Cu.reportError('theDoc location matches locationToMatch:' + theDoc.location)
if (!theDoc instanceof Ci.nsIDOMHTMLDocument) {
//not html document, so its likely an xul document
Cu.reportError('theDoc is not an HTML document, it is probably XUL, since i chose to add HTML element, i dont want to add to xul elements, so exit');
return;
}
if (!theDoc.querySelector) {
theDoc = theDoc.document; //no idea but this is the magic fix for when document-element-inserted calls add div
}
removeDiv(theDoc);
var div = theDoc.createElement('div');
div.setAttribute('style', 'background-color:steelblue;width:200px;height:200px;position:absolute;top:0;left:0;')
div.innerHTML = 'hiiiii';
div.setAttribute('id', 'listenPageLoadDemo_div')
theDoc.documentElement.appendChild(div);
}
function removeDiv(theDoc) {
//Cu.reportError('removeDiv');
if (!theDoc) {
Cu.reportError('no theDoc!')
//document not provided, it is undefined likely
return;
}
var location = (theDoc.location + '');
if (location.toLowerCase().indexOf(locationToMatch) == -1) {
return;
}
//Cu.reportError('theDoc location matches locationToMatch:' + theDoc.location)
if (!theDoc instanceof Ci.nsIDOMHTMLDocument) {
//not html document, so its likely an xul document
Cu.reportError('theDoc is not an HTML document, it is probably XUL, since i chose to add HTML element, i dont want to add to xul elements, so exit');
return;
}
//cDump(theDoc, 'theDoc', 1);
if (!theDoc.querySelector) {
theDoc = theDoc.document; //no idea but this is the magic fix for when document-element-inserted calls add div
}
var alreadyThere = theDoc.querySelector('#listenPageLoadDemo_div');
if (alreadyThere) {
alreadyThere.parentNode.removeChild(alreadyThere);
}
}
var observer = {
docElmIns: {
observe: function(aSubject, aTopic, aData) {
//aSubject is i dont know what, but aSubject.documentElement is what we are after
Cu.reportError('incoming observe docElmIns - aSubject: "'+aSubject+'" | aTopic: "'+aTopic+'" | aData: "'+aData+'"');
if (aSubject instanceof Ci.nsIDOMHTMLDocument && aSubject.location) {
//cDump(aSubject, 'aSubject', 1);
aSubject.defaultView.addEventListener('DOMContentLoaded',function(e) { //added 1234
var contentWin = e.originalTarget.defaultView;
//if (contentWin.frameElement) {Cu.reportError('DOMCONTENTLOADED but frame'); return; } //because frame elements are already caught by document-element-inserted observer, this will double on that //actually this is not true, man im confused, so we need this line commented out
Cu.reportError('DOMCONTENTLOADED');
aSubject.removeEventListener('DOMContentLoaded', arguments.callee, false); //removes "added 1234"
addDiv(contentWin);
}, false);
/* //can do DOMContentLoaded method above (or this one "onreadystatechange that is block commented out)
aSubject.onreadystatechange = function() {
//have to do this because body is not available until loaded
////order of events:
//////readystate changes to interactive (i think multiple times, not sure)
//////readystate changes to complete
//////DOMContentLoaded event fires
//////Load event fire
Cu.reportError('readyState changed to: "' + aSubject.readyState + '"');
if (aSubject.readyState == 'complete') {
//cDump(aSubject, 'rs=complete aSubject', 1);
addDiv(aSubject.defaultView);
}
}
*/
}
},
register: function() {
Services.obs.addObserver(observer.docElmIns, 'document-element-inserted', false);
},
unregister: function() {
Services.obs.removeObserver(observer.docElmIns, 'document-element-inserted');
}
}
};
function loadIntoWindow (aDOMWindow, aXULWindow) {
if (!aDOMWindow) {
return;
}
if (aDOMWindow.gBrowser) {
if (aDOMWindow.gBrowser.tabContainer) {
//has tabContainer
//start - go through all tabs in this window we just added to
var tabs = aDOMWindow.gBrowser.tabContainer.childNodes;
for (var i = 0; i < tabs.length; i++) {
Cu.reportError('DOING tab: ' + i);
var tabBrowser = tabs[i].linkedBrowser;
var win = tabBrowser.contentWindow;
loadIntoContentWindowAndItsFrames(win);
}
//end - go through all tabs in this window we just added to
} else {
//does not have tabContainer
var win = aDOMWindow.gBrowser.contentWindow;
loadIntoContentWindowAndItsFrames(win);
}
} else {
//window does not have gBrowser
Cu.reportError('match in non-gBrowser-ed window');
loadIntoContentWindowAndItsFrames(aDOMWindow);
}
}
function unloadFromWindow(aDOMWindow, aXULWindow) {
if (!aDOMWindow) {
return;
}
if (aDOMWindow.gBrowser) {
if (aDOMWindow.gBrowser.tabContainer) {
//has tabContainer
//start - go through all tabs in this window we just added to
var tabs = aDOMWindow.gBrowser.tabContainer.childNodes;
for (var i = 0; i < tabs.length; i++) {
Cu.reportError('DOING tab: ' + i);
var tabBrowser = tabs[i].linkedBrowser;
var win = tabBrowser.contentWindow;
unloadFromContentWindowAndItsFrames(win);
}
//end - go through all tabs in this window we just added to
} else {
//does not have tabContainer
var win = aDOMWindow.gBrowser.contentWindow;
unloadFromContentWindowAndItsFrames(win);
}
} else {
//window does not have gBrowser
Cu.reportError('match in non-gBrowser-ed window');
unloadFromContentWindowAndItsFrames(aDOMWindow);
}
}
function loadIntoContentWindowAndItsFrames(theWin) {
var frames = theWin.frames;
var winArr = [theWin];
for (var j = 0; j < frames.length; j++) {
winArr.push(frames[j].window);
}
Cu.reportError('# of frames in tab: ' + frames.length);
for (var j = 0; j < winArr.length; j++) {
if (j == 0) {
Cu.reportError('**checking win: ' + j + ' location = ' + winArr[j].document.location);
} else {
Cu.reportError('**checking frame win: ' + j + ' location = ' + winArr[j].document.location);
}
var doc = winArr[j].document;
//START - edit below here
addDiv(doc);
//break; //uncomment this line if you don't want to add to frames
//END - edit above here
}
}
function unloadFromContentWindowAndItsFrames(theWin) {
var frames = theWin.frames;
var winArr = [theWin];
for (var j = 0; j < frames.length; j++) {
winArr.push(frames[j].window);
}
Cu.reportError('# of frames in tab: ' + frames.length);
for (var j = 0; j < winArr.length; j++) {
if (j == 0) {
Cu.reportError('**checking win: ' + j + ' location = ' + winArr[j].document.location);
} else {
Cu.reportError('**checking frame win: ' + j + ' location = ' + winArr[j].document.location);
}
var doc = winArr[j].document;
//START - edit below here
removeDiv(doc);
//break; //uncomment this line if you don't want to remove from frames
//END - edit above here
}
}
function startup(aData, aReason) {
let XULWindows = Services.wm.getXULWindowEnumerator(null);
while (XULWindows.hasMoreElements()) {
let aXULWindow = XULWindows.getNext();
let aDOMWindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
loadIntoWindow(aDOMWindow, aXULWindow);
}
observer.docElmIns.register();
}
function shutdown(aData, aReason) {
if (aReason == APP_SHUTDOWN) return;
observer.docElmIns.unregister();
let XULWindows = Services.wm.getXULWindowEnumerator(null);
while (XULWindows.hasMoreElements()) {
let aXULWindow = XULWindows.getNext();
let aDOMWindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
unloadFromWindow(aDOMWindow, aXULWindow);
}
}
function install() {}
function uninstall() {}
<?xml version="1.0" encoding="utf-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>demo---document-element-inserted@jetpack</em:id>
<em:version>initial</em:version>
<em:type>2</em:type>
<em:bootstrap>true</em:bootstrap>
<em:unpack>false</em:unpack>
<!-- Firefox -->
<em:targetApplication>
<Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
<em:minVersion>7.0</em:minVersion>
<em:maxVersion>10.0a1</em:maxVersion>
</Description>
</em:targetApplication>
<!-- Front End MetaData -->
<em:name>demo - document element inserted</em:name>
<em:description>This addon demonstrates how page loads can be watched with observer service and content inserted into these pages.</em:description>
<em:creator>Noitidart</em:creator>
<em:iconURL/>
<em:icon64URL/>
<em:optionsType>2</em:optionsType>
</Description>
</RDF>
@Noitidart
Copy link
Author

Advantage to using observer service here, rather than window listener (here: https://gist.github.com/Noitidart/8673632) is that it watches page loads in chrome windows that do not have a gBrowser.

There is some stuff in it I can't explain so I very much look forward to input.

Like what is aSubject in the observer, it seems to have documentElement as it's property, but I thought aSubject was documentElement. The big issue is when I pass aSubject.defaultView to addDiv, it cannot access querySelector or createElement without doing another .document on it. Please see lines 22 and 51.

Anyways notes on this:
-loadIntoWindow and unloadFromWindow are only for startup and shutdown, in all other cases the document-element-inserted observer will take care of running addDiv
-For people that want to copy and paste this:
---Edit the location you want pages to match, so thats line 3, it currently pages pages with mozillazine in it. You can use regex but will have to change some stuff up.
---Edit the addDiv (from line 26 - 30) and removeDiv (from line 54 - 57) functions, whatever you add to the document in addDiv, make sure you remove it all in removeDiv

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment