Skip to content

Instantly share code, notes, and snippets.

@Noitidart
Created January 29, 2014 10:44
Show Gist options
  • Save Noitidart/8685542 to your computer and use it in GitHub Desktop.
Save Noitidart/8685542 to your computer and use it in GitHub Desktop.
Reseraching how to watch non-gBrowser'ed windows with window listener. Extension of: 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.
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;
}
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;
}
var alreadyThere = theDoc.querySelector('#listenPageLoadDemo_div');
if (alreadyThere) {
alreadyThere.parentNode.removeChild(alreadyThere);
}
}
function listenPageLoad(event) {
var win = event.originalTarget.defaultView;
var doc = win.document;
if (win.frameElement) {
//its a frame
//Cu.reportError('a frame loaded');
}
addDiv(doc);
}
/*start - windowlistener*/
var windowListener = {
//DO NOT EDIT HERE
onOpenWindow: function (aXULWindow) {
// Wait for the window to finish loading
Cu.reportError('aXULWindow.addEventListener = ' + aXULWindow.addEventListener);
let aDOMWindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
//im thinking if it has no gBrowser i should let onWindowTitleChange take care of it all, meaning no need to route it to loadIntoWindow, because even on open of window, onWindowTitleChange gets triggered
if (!aDOMWindow.gBrowser) {
//aDOMWindow.cTitleChanged = true; //we dont want to false trigger the onWindowTitleChange, because we treat that ias a load, and this will be caught as windowopen
Cu.reportError('can set cTitleChanged to true here in onOpenWindow')
}
aDOMWindow.addEventListener("load", function () {
if (!aDOMWindow.gBrowser) {
Cu.reportError('non-gBrowser-ed aDOMWindow finished loading - from load event listener in onOpenWindow')
}
aDOMWindow.removeEventListener("load", arguments.callee, false);
windowListener.loadIntoWindow(aDOMWindow, aXULWindow);
}, false);
},
onCloseWindow: function (aXULWindow) {},
onWindowTitleChange: function (aXULWindow, aNewTitle) {
//this is a trick to watch loading of windows with no gBrowser
let aDOMWindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
if (!aDOMWindow) {
Cu.reportError('no domwindow - onWindowTitleChange')
return;
}
if (aDOMWindow.gBrowser) {
return; //if the window has a gBrowser then there is no need for this trick, the listenPageLoad will catch it
}
/*
if (!('cTitleChanged' in aDOMWindow)) {
//likely its in window opening
Cu.reportError('cTitleChanged is not in aDOMWindow so likely this window is opening or something so dont check for cTitleChanged === true yet');
return;
}
*/
Cu.reportError('title changed');
Cu.reportError('new title: ' + aNewTitle);
Cu.reportError('old title: ' + aDOMWindow.document.title);
Cu.reportError('aDOMWindow.cTitleChanged = ' + aDOMWindow.cTitleChanged);
if (aDOMWindow.cTitleChanged) {
Cu.reportError('title already changed so window did not load another page');
} else {
Cu.reportError('this is a new page load as it couldnt find cTitleChanged');
//aDOMWindow.cTitleChanged = true; //note: c stands for custom, just want to diffrentiate it from what mozilla has in its properties by default //cTitleChanged should never be set from here, it should only be set from startup getEnumerator or from onOpenWindow
aDOMWindow.addEventListener('DOMContentLoaded',tcDCL, false);
aDOMWindow.addEventListener('load',tcLoad, false); //this i think is equiv of "aDOMWindow.addEventListener("load", function () {" from onOpenWindow
//trial 1: i opened scratchpad: "can set cTitleChanged to true here in onOpenWindow" at 45.918, "title changed" at 45.991, "non-gBrowser-ed aDOMWindow finished loading - from load event listener in onOpenWindow" at 46.002, "non-gBrowser-ed window finished loading" from loadIntoWindow attached from onOpenWindow at 46.002 ln162, tcLoad at 46.004 then tcDCL at 46.239
//trial 2: changed location of dWin to about:blank: "title changed" at 21.439, "tcDCL - DOMContentLoaded" at 21.440, "tcLoad - loaded" at 21.441
//trial 3: changed location of dWin to bing: "title changed" at 51.023,4x "tcDCL"" all at 51.048, tcLoad at 51.224, 4x "tcDCL" 51.667;51.941;52.749;53.774..... tested at end and it has 4 frames
//trial 4: opend scratchpad window: "can set cTitleChanged to true here in onOpenWindow" at 22.983, "title changed" at 23.054, "non-gBrowser-ed aDOMWindow finished ... load listener in onOpenWindow" at 23.058, "non-gBrowser-ed window finished loading" from inside loadIntoWindow at 23.059, "tcLoad" at 23.061, "tcDCL" at 23.297
//note on trail 1 and trial 4: very interesting that "tcDCL - DOMContentLoaded" fires 200ms after final (theres only one) "tcLoad"
//trial 5: opend browser console: "can set cTitleChanged to true here in onOpenWindow" at 41.750, "title changed" at 41.856, "non-gBrowser-ed aDOMWindow finished loading - from load event listener in onOpenWindow" at 41.860, "non-gBrowser-ed window finished loading" from loadIntoWindow at 41.860, "tcLoad - loaded" at 41.861
//note on trial 5, it is interesting that DOMContentLoaded (tcDCL) does not fire, i closed then tried to open browser console again, and it did not trigger tcDCL again, interesting. I suspect that tcDCL fires so fast, it fires before my code gets to the point of attaching tcDCL after title change
//trial 6: set dWin location to "http://www.w3schools.com/js/tryit.asp?filename=tryjs_myfirst": "title changed" at 2.427, "tcDCL" at 2.725, "tcDCL" at 2.919, "tcDCL" at 3.370, "tcLoad" at 3.370 //frames in here: 2, so note: first 2 frames, fired the first 2 tcDCL
//trial 7: clicking try it in the w3schools try it editor: "tcDCL" at 44.174 //this is it, nothing else fired, this was expected because it changes location of a frame
}
},
register: function () {
// Load into any existing windows
let XULWindows = Services.wm.getXULWindowEnumerator(null);
while (XULWindows.hasMoreElements()) {
let aXULWindow = XULWindows.getNext();
let aDOMWindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
windowListener.loadIntoWindow(aDOMWindow, aXULWindow);
}
// Listen to new windows
Services.wm.addListener(windowListener);
},
unregister: function () {
// Unload from any existing windows
let XULWindows = Services.wm.getXULWindowEnumerator(null);
while (XULWindows.hasMoreElements()) {
let aXULWindow = XULWindows.getNext();
let aDOMWindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
windowListener.unloadFromWindow(aDOMWindow, aXULWindow);
}
//Stop listening so future added windows dont get this attached
Services.wm.removeListener(windowListener);
},
//END - DO NOT EDIT HERE
loadIntoWindow: function (aDOMWindow, aXULWindow) {
if (!aDOMWindow) {
return;
}
if (aDOMWindow.gBrowser) {
aDOMWindow.gBrowser.addEventListener('DOMContentLoaded', listenPageLoad, false);
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('non-gBrowser-ed window finished loading');
//aDOMWindow.cTitleChanged = true;
//loadIntoContentWindowAndItsFrames(aDOMWindow);
}
},
unloadFromWindow: function (aDOMWindow, aXULWindow) {
if (!aDOMWindow) {
return;
}
if (aDOMWindow.gBrowser) {
aDOMWindow.gBrowser.removeEventListener('DOMContentLoaded', listenPageLoad, false);
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
delete aDOMWindow.cTitleChanged;
unloadFromContentWindowAndItsFrames(aDOMWindow);
}
}
};
/*end - windowlistener*/
function tcDCL() {
Cu.reportError('tcDCL - DOMContentLoaded');
}
function tcLoad() {
Cu.reportError('tcLoad - loaded');
}
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) {
windowListener.register();
}
function shutdown(aData, aReason) {
if (aReason == APP_SHUTDOWN) return;
windowListener.unregister();
}
function install() {}
function uninstall() {}
/*xulWin cDump not deep
BSTR::
QueryInterface=function QueryInterface() {
[native code]
}
docShell=[xpconnect wrapped (nsISupports, nsIWebNavigation, nsIDocShellTreeItem, nsIDocShell)]
intrinsicallySized=false
primaryContentShell=null
getContentShellById=function getContentShellById() {
[native code]
}
addChildWindow=function addChildWindow() {
[native code]
}
removeChildWindow=function removeChildWindow() {
[native code]
}
center=function center() {
[native code]
}
showModal=function showModal() {
[native code]
}
zLevel=5
contextFlags=0
chromeFlags=2281702958
assumeChromeFlagsAreFrozen=function assumeChromeFlagsAreFrozen() {
[native code]
}
createNewWindow=function createNewWindow() {
[native code]
}
XULBrowserWindow=null
lowestZ=0
loweredZ=4
normalZ=5
raisedZ=6
highestZ=9
FSTR::
*/
/*docShell dump of xulWin, to make it deep
click to toggle
QueryInterface=function QueryInterface() {
[native code]
}
treeOwner=[xpconnect wrapped (nsISupports, nsIDocShellTreeOwner, nsIInterfaceRequestor)]
canGoBack=e0=deep_fstr=[Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsIWebNavigation.canGoBack]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: Scratchpad/3 :: cDump :: line 34" data: no]
canGoForward=e0=deep_fstr=[Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsIWebNavigation.canGoForward]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: Scratchpad/3 :: cDump :: line 34" data: no]
goBack=function goBack() {
[native code]
}
goForward=function goForward() {
[native code]
}
gotoIndex=function gotoIndex() {
[native code]
}
loadURI=function loadURI() {
[native code]
}
reload=function reload() {
[native code]
}
stop=function stop() {
[native code]
}
document=[object XrayWrapper [object HTMLDocument]]
currentURI=[xpconnect wrapped nsIURI]
referringURI=null
sessionHistory=null
LOAD_FLAGS_MASK=65535
LOAD_FLAGS_NONE=0
LOAD_FLAGS_IS_REFRESH=16
LOAD_FLAGS_IS_LINK=32
LOAD_FLAGS_BYPASS_HISTORY=64
LOAD_FLAGS_REPLACE_HISTORY=128
LOAD_FLAGS_BYPASS_CACHE=256
LOAD_FLAGS_BYPASS_PROXY=512
LOAD_FLAGS_CHARSET_CHANGE=1024
LOAD_FLAGS_STOP_CONTENT=2048
LOAD_FLAGS_FROM_EXTERNAL=4096
LOAD_FLAGS_ALLOW_MIXED_CONTENT=8192
LOAD_FLAGS_FIRST_LOAD=16384
LOAD_FLAGS_ALLOW_POPUPS=32768
LOAD_FLAGS_BYPASS_CLASSIFIER=65536
LOAD_FLAGS_FORCE_ALLOW_COOKIES=131072
LOAD_FLAGS_DISALLOW_INHERIT_OWNER=262144
LOAD_FLAGS_URI_IS_UTF8=524288
LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP=1048576
STOP_NETWORK=1
STOP_CONTENT=2
STOP_ALL=3
childCount=4
addChild=function addChild() {
[native code]
}
removeChild=function removeChild() {
[native code]
}
getChildAt=function getChildAt() {
[native code]
}
findChildWithName=function findChildWithName() {
[native code]
}
name=
nameEquals=function nameEquals() {
[native code]
}
itemType=0
parent=null
sameTypeParent=null
rootTreeItem=[xpconnect wrapped (nsISupports, nsIWebNavigation, nsIDocShellTreeItem, nsIDocShell)]
sameTypeRootTreeItem=[xpconnect wrapped (nsISupports, nsIWebNavigation, nsIDocShellTreeItem, nsIDocShell)]
findItemWithName=function findItemWithName() {
[native code]
}
typeChrome=0
typeContent=1
typeContentWrapper=2
typeChromeWrapper=3
typeAll=2147483647
addState=function addState() {
[native code]
}
createLoadInfo=function createLoadInfo() {
[native code]
}
prepareForNewContentModel=function prepareForNewContentModel() {
[native code]
}
setCurrentURI=function setCurrentURI() {
[native code]
}
contentViewer=[xpconnect wrapped nsIContentViewer]
chromeEventHandler=null
allowPlugins=true
allowJavascript=true
allowMetaRedirects=true
allowSubframes=true
allowImages=true
allowMedia=true
allowDNSPrefetch=true
allowWindowControl=true
allowContentRetargeting=true
getDocShellEnumerator=function getDocShellEnumerator() {
[native code]
}
appType=0
allowAuth=false
zoom=1
marginWidth=-1
marginHeight=-1
tabToTreeOwner=function tabToTreeOwner() {
[native code]
}
busyFlags=0
loadType=134217729
defaultLoadFlags=0
isBeingDestroyed=function isBeingDestroyed() {
[native code]
}
isExecutingOnLoadHandler=false
layoutHistoryState=e0=deep_fstr=[Exception... "Cannot find interface information for parameter arg 0 [nsIDocShell.layoutHistoryState]" nsresult: "0x80570006 (NS_ERROR_XPC_CANT_GET_PARAM_IFACE_INFO)" location: "JS frame :: Scratchpad/3 :: cDump :: line 34" data: no]
shouldSaveLayoutState=false
securityUI=null
suspendRefreshURIs=function suspendRefreshURIs() {
[native code]
}
resumeRefreshURIs=function resumeRefreshURIs() {
[native code]
}
beginRestore=function beginRestore() {
[native code]
}
finishRestore=function finishRestore() {
[native code]
}
restoringDocument=false
useErrorPages=true
displayLoadError=function displayLoadError() {
[native code]
}
previousTransIndex=-1
loadedTransIndex=-1
historyPurged=function historyPurged() {
[native code]
}
getSessionStorageForPrincipal=function getSessionStorageForPrincipal() {
[native code]
}
addSessionStorage=function addSessionStorage() {
[native code]
}
currentDocumentChannel=[xpconnect wrapped nsIChannel]
isInUnload=false
channelIsUnsafe=false
hasMixedActiveContentLoaded=false
hasMixedActiveContentBlocked=false
hasMixedDisplayContentLoaded=false
hasMixedDisplayContentBlocked=false
isOffScreenBrowser=false
printPreview=[xpconnect wrapped nsIWebBrowserPrint]
canExecuteScripts=true
isActive=true
historyID=76
isAppTab=false
createAboutBlankContentViewer=function createAboutBlankContentViewer() {
[native code]
}
charset=UTF-8
gatherCharsetMenuTelemetry=function gatherCharsetMenuTelemetry() {
[native code]
}
forcedCharset=
parentCharset=
parentCharsetSource=0
addWeakPrivacyTransitionObserver=function addWeakPrivacyTransitionObserver() {
[native code]
}
addWeakReflowObserver=function addWeakReflowObserver() {
[native code]
}
removeWeakReflowObserver=function removeWeakReflowObserver() {
[native code]
}
isBrowserElement=false
isApp=false
isBrowserOrApp=false
isInBrowserElement=false
isInBrowserOrApp=false
setIsApp=function setIsApp() {
[native code]
}
setIsBrowserInsideApp=function setIsBrowserInsideApp() {
[native code]
}
appId=0
appManifestURL=
getSameTypeParentIgnoreBrowserAndAppBoundaries=function getSameTypeParentIgnoreBrowserAndAppBoundaries() {
[native code]
}
asyncPanZoomEnabled=false
sandboxFlags=0
mixedContentChannel=null
GetAllowMixedContentAndConnectionData=function GetAllowMixedContentAndConnectionData() {
[native code]
}
fullscreenAllowed=true
setFullscreenAllowed=function setFullscreenAllowed() {
[native code]
}
mayEnableCharacterEncodingMenu=false
editor=null
editable=false
hasEditingSession=false
makeEditable=function makeEditable() {
[native code]
}
getChildSHEntry=function getChildSHEntry() {
[native code]
}
addChildSHEntry=function addChildSHEntry() {
[native code]
}
useGlobalHistory=false
removeFromSessionHistory=function removeFromSessionHistory() {
[native code]
}
createdDynamically=false
getCurrentSHEntry=function getCurrentSHEntry() {
[native code]
}
isCommandEnabled=function isCommandEnabled() {
[native code]
}
doCommand=function doCommand() {
[native code]
}
INTERNAL_LOAD_FLAGS_NONE=0
INTERNAL_LOAD_FLAGS_INHERIT_OWNER=1
INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER=2
INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP=4
INTERNAL_LOAD_FLAGS_FIRST_LOAD=8
INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER=16
INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES=32
INTERNAL_LOAD_FLAGS_IS_SRCDOC=64
ENUMERATE_FORWARDS=0
ENUMERATE_BACKWARDS=1
APP_TYPE_UNKNOWN=0
APP_TYPE_MAIL=1
APP_TYPE_EDITOR=2
BUSY_FLAGS_NONE=0
BUSY_FLAGS_BUSY=1
BUSY_FLAGS_BEFORE_PAGE_LOAD=2
BUSY_FLAGS_PAGE_LOADING=4
LOAD_CMD_NORMAL=1
LOAD_CMD_RELOAD=2
LOAD_CMD_HISTORY=4
LOAD_CMD_PUSHSTATE=8
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment