Created
January 30, 2014 07:30
-
-
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 …
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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