1
/* See license.txt for terms of usage */
3
FBL.ns(function() { with (FBL) {
5
// ************************************************************************************************
8
const nsIWebNavigation = CI("nsIWebNavigation");
9
const nsIWebProgressListener = CI("nsIWebProgressListener");
10
const nsIWebProgress = CI("nsIWebProgress");
11
const nsISupportsWeakReference = CI("nsISupportsWeakReference");
12
const nsISupports = CI("nsISupports");
13
const nsIURI = CI("nsIURI");
15
const NOTIFY_STATE_DOCUMENT = nsIWebProgress.NOTIFY_STATE_DOCUMENT;
17
const STATE_IS_WINDOW = nsIWebProgressListener.STATE_IS_WINDOW;
18
const STATE_IS_DOCUMENT = nsIWebProgressListener.STATE_IS_DOCUMENT;
19
const STATE_IS_REQUEST = nsIWebProgressListener.STATE_IS_REQUEST;
21
const STATE_START = nsIWebProgressListener.STATE_START;
22
const STATE_STOP = nsIWebProgressListener.STATE_STOP;
23
const STATE_TRANSFERRING = nsIWebProgressListener.STATE_TRANSFERRING;
25
const STOP_ALL = nsIWebNavigation.STOP_ALL;
27
const dummyURI = "about:layout-dummy-request";
28
const aboutBlank = "about:blank";
30
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
32
const tabBrowser = $("content");
34
// ************************************************************************************************
40
// ************************************************************************************************
44
initialize: function(owner)
46
// Store contexts where they can be accessed externally
47
this.contexts = contexts;
49
this.owner = owner; // Firebug object
50
this.addListener(owner);
53
tabBrowser.addProgressListener(TabProgressListener, NOTIFY_STATE_DOCUMENT);
60
tabBrowser.removeProgressListener(TabProgressListener);
62
for (var i = 0; i < tabBrowser.browsers.length; ++i)
64
var browser = tabBrowser.browsers[i];
65
this.unwatchTopWindow(browser.contentWindow);
69
this.removeListener(this.owner);
73
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
78
this.watchBrowser(tabBrowser.selectedBrowser);
81
deactivate: function()
85
var currentSelected = false;
86
for (var i = 0; i < tabBrowser.browsers.length; ++i)
88
var browser = tabBrowser.browsers[i];
89
if (!this.owner.isURIAllowed(safeGetURI(browser)))
91
this.unwatchTopWindow(browser.contentWindow);
93
if (browser == tabBrowser.selectedBrowser)
94
currentSelected = true;
97
return currentSelected;
101
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
104
* Attaches to a top-level window. Creates context unless we just re-activated on an existing context
106
watchTopWindow: function(win, uri)
108
if (tabBrowser.selectedBrowser.cancelNextLoad)
110
// We need to cancel this load and try again after a delay... this is used
111
// mainly to prevent chaos while when the debugger is active when a page
113
delete tabBrowser.selectedBrowser.cancelNextLoad;
114
tabBrowser.selectedBrowser.webNavigation.stop(STOP_ALL);
115
delayBrowserLoad(tabBrowser.selectedBrowser, win.location.href);
119
var context = this.getContextByWindow(win);
122
if (!this.owner.enableContext(win,uri))
124
return this.watchContext(win, null);
130
var browser = this.getBrowserByWindow(win);
131
if (!fbs.countContext(true))
134
// If the page is reloaded, store the persisted state from the previous
135
// page on the new context
136
var persistedState = browser.persistedState;
137
delete browser.persistedState;
138
if (!persistedState || persistedState.location != win.location.href)
139
persistedState = null;
141
context = this.owner.createTabContext(win, browser, browser.chrome, persistedState);
142
contexts.push(context);
144
this.dispatch("initContext", [context]);
146
win.addEventListener("pagehide", onUnloadTopWindow, true);
147
win.addEventListener("pageshow", onLoadWindowContent, true);
148
win.addEventListener("DOMContentLoaded", onLoadWindowContent, true);
150
// XXXjjb at this point we either have context or we just pushed null into contexts and sent it to init...
152
// This is one of two places that loaded is set. The other is in watchLoadedTopWindow
154
context.loaded = !context.browser.webProgress.isLoadingDocument;
156
this.watchContext(win, context);
160
* Called once the document within a tab is completely loaded.
162
watchLoadedTopWindow: function(win)
164
var isSystem = isSystemPage(win);
166
var context = this.getContextByWindow(win);
167
if ((context && !context.window) || (isSystem && !Firebug.allowSystemPages))
169
this.unwatchTopWindow(win);
170
this.watchContext(win, null, isSystem);
174
if (context && !context.loaded)
176
context.loaded = true;
177
this.dispatch("loadedContext", [context]);
182
* Attaches to a window that may be either top-level or a frame within the page.
184
watchWindow: function(win, context)
187
context = this.getContextByWindow(getRootWindow(win));
189
// Unfortunately, dummy requests that trigger the call to watchWindow
190
// are called several times, so we have to avoid dispatching watchWindow
192
var href = win.location.href;
193
if (context && context.windows.indexOf(win) == -1 && href != aboutBlank)
195
context.windows.push(win);
197
var eventType = (win.parent == win) ? "pagehide" : "unload";
198
win.addEventListener(eventType, onUnloadWindow, false);
199
this.dispatch("watchWindow", [context, win]);
204
* Detaches from a top-level window. Destroys context
206
unwatchTopWindow: function(win)
208
var context = this.getContextByWindow(win);
209
this.unwatchContext(win, context);
213
* Detaches from a window, top-level or not.
215
unwatchWindow: function(win)
217
var context = this.getContextByWindow(win);
219
var index = context ? context.windows.indexOf(win) : -1;
222
context.windows.splice(index, 1);
223
this.dispatch("unwatchWindow", [context, win]); // XXXjjb Joe check
228
* Attaches to the window inside a browser because of user-activation
230
watchBrowser: function(browser)
232
this.watchTopWindow(browser.contentWindow, safeGetURI(browser));
235
unwatchBrowser: function(browser)
237
this.unwatchTopWindow(browser.contentWindow);
240
watchContext: function(win, context, isSystem)
242
var browser = context ? context.browser : this.getBrowserByWindow(win);
244
browser.isSystemPage = isSystem;
246
this.dispatch("showContext", [browser, context]);
249
unwatchContext: function(win, context)
253
var browser = this.getBrowserByWindow(win);
254
this.owner.destroyTabContext(browser, null);
258
var persistedState = {location: context.window.location.href};
259
context.browser.persistedState = persistedState;
261
iterateWindows(context.window, function(win)
263
TabWatcher.dispatch("unwatchWindow", [context, win]);
266
this.dispatch("destroyContext", [context, persistedState]);
268
if (this.cancelNextLoad)
270
delete this.cancelNextLoad;
271
context.browser.cancelNextLoad = true;
276
context.window.removeEventListener("pagehide", onUnloadTopWindow, true);
282
fbs.countContext(false);
284
this.owner.destroyTabContext(context.browser, context);
285
context.destroy(persistedState);
287
remove(contexts, context);
288
for (var p in context)
292
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
294
getContextByWindow: function(win)
296
while (win && win.parent != win)
299
if (!win) // eg search bar, and sometimes win.parent is null??
302
for (var i = 0; i < contexts.length; ++i)
304
var context = contexts[i];
305
if (context.window == win)
310
getBrowserByWindow: function(win)
312
for (var i = 0; i < tabBrowser.browsers.length; ++i)
314
var browser = tabBrowser.browsers[i];
315
if (browser.contentWindow == win)
319
browser.chrome = FirebugChrome;
320
browser.addProgressListener(FrameProgressListener, NOTIFY_STATE_DOCUMENT);
329
iterateContexts: function(fn)
331
for (var i = 0; i < contexts.length; ++i)
335
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
337
addListener: function(listener)
339
listeners.push(listener);
342
removeListener: function(listener)
344
remove(listeners, listener);
347
dispatch: function(name, args)
349
for (var i = 0; i < listeners.length; ++i)
351
var listener = listeners[i];
352
if (name in listener)
356
listener[name].apply(listener, args);
367
// ************************************************************************************************
369
var BaseProgressListener =
371
QueryInterface : function(iid)
373
if (iid.equals(nsIWebProgressListener) ||
374
iid.equals(nsISupportsWeakReference) ||
375
iid.equals(nsISupports))
380
throw Components.results.NS_NOINTERFACE;
383
stateIsRequest: false,
384
onLocationChange: function() {},
385
onStateChange : function() {},
386
onProgressChange : function() {},
387
onStatusChange : function() {},
388
onSecurityChange : function() {},
389
onLinkIconAvailable : function() {}
392
// ************************************************************************************************
394
var TabProgressListener = extend(BaseProgressListener,
396
onLocationChange: function(progress, request, uri)
398
// Only watch windows that are their own parent - e.g. not frames
399
if (progress.DOMWindow.parent == progress.DOMWindow)
401
TabWatcher.watchTopWindow(progress.DOMWindow, uri);
405
onStateChange: function(progress, request, flag, status)
407
/*if (flag & STATE_STOP)
409
var win = progress.DOMWindow;
410
if (win && win.parent == win)
411
TabWatcher.watchLoadedTopWindow(progress.DOMWindow);
416
// ************************************************************************************************
418
var FrameProgressListener = extend(BaseProgressListener,
420
onStateChange: function(progress, request, flag, status)
422
if (flag & STATE_IS_REQUEST && flag & STATE_START)
424
// We need to get the hook in as soon as the new DOMWindow is created, but before
425
// it starts executing any scripts in the page. After lengthy analysis, it seems
426
// that the start of these "dummy" requests is the only state that works.
428
var safeName = safeGetName(request);
429
if (safeName && ((safeName == dummyURI) || safeName == "about:document-onload-blocker") )
431
var win = progress.DOMWindow;
432
// Another weird edge case here - when opening a new tab with about:blank,
433
// "unload" is dispatched to the document, but onLocationChange is not called
434
// again, so we have to call watchTopWindow here
435
//if (win.parent == win && win.location.href == "about:blank")
436
// TabWatcher.watchTopWindow(win, win.location);
438
if (win.parent == win && (win.location.href == "about:blank" ))// || safeName == "about:document-onload-blocker"))
440
TabWatcher.watchTopWindow(win, win.location.href);
441
return; // new one under our thumb
446
// Later I discovered that XHTML documents don't dispatch the dummy requests, so this
447
// is our best shot here at hooking them.
448
if (flag & STATE_IS_DOCUMENT && flag & STATE_TRANSFERRING)
450
TabWatcher.watchWindow(progress.DOMWindow);
457
// ************************************************************************************************
461
function onUnloadTopWindow(event)
463
TabWatcher.unwatchTopWindow(event.currentTarget);
466
function onLoadWindowContent(event)
468
var win = event.currentTarget;
471
win.removeEventListener("pageshow", onLoadWindowContent, true);
477
win.removeEventListener("DOMContentLoaded", onLoadWindowContent, true);
481
// Signal that we got the onLoadWindowContent event. This prevents the FrameProgressListener from sending it.
482
var context = TabWatcher.getContextByWindow(win);
484
context.onLoadWindowContent = true;
486
// Calling this after a timeout because I'm finding some cases where calling
487
// it here causes freezeup when this results in loading a script file. This fixes that.
488
setTimeout(function()
492
TabWatcher.watchLoadedTopWindow(win);
502
function onUnloadWindow(event)
504
var win = event.currentTarget;
505
var eventType = (win.parent == win) ? "pagehide" : "unload";
506
win.removeEventListener(eventType, onUnloadWindow, false);
507
TabWatcher.unwatchWindow(win);
510
function delayBrowserLoad(browser, uri)
512
setTimeout(function() { browser.loadURI(uri); }, 100);
515
function safeGetName(request)
527
function safeGetURI(browser)
531
return browser.currentURI;
539
function getStateDescription(flag) {
541
if (flag & nsIWebProgressListener.STATE_START) state += "STATE_START ";
542
else if (flag & nsIWebProgressListener.STATE_REDIRECTING) state += "STATE_REDIRECTING ";
543
else if (flag & nsIWebProgressListener.STATE_TRANSFERRING) state += "STATE_TRANSFERRING ";
544
else if (flag & nsIWebProgressListener.STATE_NEGOTIATING) state += "STATE_NEGOTIATING ";
545
else if (flag & nsIWebProgressListener.STATE_STOP) state += "STATE_STOP ";
547
if (flag & nsIWebProgressListener.STATE_IS_REQUEST) state += "STATE_IS_REQUEST ";
548
if (flag & nsIWebProgressListener.STATE_IS_DOCUMENT) state += "STATE_IS_DOCUMENT ";
549
if (flag & nsIWebProgressListener.STATE_IS_NETWORK) state += "STATE_IS_NETWORK ";
550
if (flag & nsIWebProgressListener.STATE_IS_WINDOW) state += "STATE_IS_WINDOW ";
551
if (flag & nsIWebProgressListener.STATE_RESTORING) state += "STATE_RESTORING ";
552
if (flag & nsIWebProgressListener.STATE_IS_INSECURE) state += "STATE_IS_INSECURE ";
553
if (flag & nsIWebProgressListener.STATE_IS_BROKEN) state += "STATE_IS_BROKEN ";
554
if (flag & nsIWebProgressListener.STATE_IS_SECURE) state += "STATE_IS_SECURE ";
555
if (flag & nsIWebProgressListener.STATE_SECURE_HIGH) state += "STATE_SECURE_HIGH ";
556
if (flag & nsIWebProgressListener.STATE_SECURE_MED) state += "STATE_SECURE_MED ";
557
if (flag & nsIWebProgressListener.STATE_SECURE_LOW) state += "STATE_SECURE_LOW ";
562
// ************************************************************************************************