Persistent Panel/Context

41 views
Skip to first unread message

Alex

unread,
Sep 24, 2008, 2:39:27 PM9/24/08
to Firebug
Hello everybody and thanks a lot for Firebug!

My question is the following: Is there a simple way (internal option,
say), to build persistent panels, ie. panels that can gather
information over several pages / page reloads? As an example, the
Console panel could then optionally show errors and warnings from
several pages.

I am building an extension for Firebug and am very much interested in
such a feature. If there is no easy option (which is what I suppose),
I would be very glad to get some expert advise (the more detailed the
better) on how an implementation of such a persistent panel should
look like (passing/gathering context information, avoiding the
destruction of a panel, etc).

I really hope someone finds the time to reply to this. Thanks a lot in
advance! Your help is very appreciated.

Best regards,

Alex

John J Barton

unread,
Sep 24, 2008, 3:07:42 PM9/24/08
to Firebug
It would be easy to for the console to show errors and warning for
several pages, as long as by "several pages" you mean "all pages" ;-)

What you probably want is "when this page POSTs and gets a reply, put
the new page errors in the same console as I have now". There are
lots of folks who want this feature.

The problem is "how can Firebug determine that a page load event is
connected in some way to a previous page?"

Here is one idea: when tabWatcher sees a new page it could look up the
URL in the Net panel list of previous requests for every context. If
one of the contexts did a request that matches, set the context of the
new load to the match and issue "loadedContext". You also have to
handle redirects.

If you succeed at that part the rest is simple. Well, I guess it
depends on whether the new page events come before or after the call
to destroy the old page.

By the way our plan is to eventually drop tabWatcher once we get a new
API from Firefox. But that depends on
https://bugzilla.mozilla.org/show_bug.cgi?id=342715
and it could be a ways off.

jjb

Alex

unread,
Sep 24, 2008, 4:23:22 PM9/24/08
to Firebug
Thanks for the quick reply!

In fact, I want the "easy" feature: One panel that doesn't reload when
a new page is loaded into the current browser tab. Let's say I want to
protocol the text content of all title tags of all pages loaded into
the browser tab. Of course, I could just remember all title tags and
write them to the fresh panel, which is (obviously) not what I want to
do. So I guess the question is: How do I communicate to tabWatcher
that I do not want to delete the panel? How can I keep a panel over a
page reload?

History-dependent panels are pretty appealing, too. But the easy
version would do fine for now; in particular, given that you plan to
drop tabWatcher.

Again, I'd like to thank you for your reply. Have a good day,

Alex


On Sep 24, 9:07 pm, John J Barton <johnjbar...@johnjbarton.com> wrote:
> It would be easy to for the console to show errors and warning for
> several pages, as long as by "several pages" you mean "all pages" ;-)
>
> What you probably want is "when this page POSTs and gets a reply, put
> the new page errors in the same console as I have now".  There are
> lots of folks who want this feature.
>
> The problem is "how can Firebug determine that a page load event is
> connected in some way to a previous page?"
>
> Here is one idea: when tabWatcher sees a new page it could look up the
> URL in the Net panel list of previous requests for every context. If
> one of the contexts did a request that matches, set the context of the
> new load to the match and issue "loadedContext".  You also have to
> handle redirects.
>
> If you succeed at that part the rest is simple.  Well, I guess it
> depends on whether the new page events come before or after the call
> to destroy the old page.
>
> By the way our plan is to eventually drop tabWatcher once we get a new
> API from Firefox. But that depends onhttps://bugzilla.mozilla.org/show_bug.cgi?id=342715

John J Barton

unread,
Sep 24, 2008, 4:48:44 PM9/24/08
to Firebug
Each context has a set of panels; each context corresponds to a page.
I'd start by tracing with DBG_WINDOWS to understand the order of
events: does the destroyContext come before the initContext for new
page or not? My guess is yes. So then you need to decide: how to
prevent destroyContext only in cases where I want to retain the
panel?

hth,
jjb

Alex

unread,
Sep 30, 2008, 9:38:01 AM9/30/08
to Firebug
Hello again everybody!

I now implemented a mechanism that allows to persist Firebug panels.
That is, a panel will survive page loads as long as it is marked
persistent. This might be useful when visualing logged information or
events etc (see also above).

The changes necessary to achieve such a behaviour are in fact minimal.
The intuition, as outlined by John, is the following. The moment we
destroy the current webpage's context, we store the panels marked
persistent in persistedState (tabContext.destroy()). Later, when the
new context is about to be created, we recover the persisted panels
from persistedState.

I will now detail the code changes.

1) Firebug.Panel

- We need to add a boolean attribute termed "persistent". If true,
the panel will not be erased when a new context is loaded.
- A method called reinitialize() (or loadedContext() ?!) is used
to handle the actions that shall be executed when a new context has
been loaded. For example, reinitialize would NOT erase the panelNode,
but add a log message.

2) Firebug.TabContext

- destroy():

We will save persistent panels in persistedState.panelMap.
First, initialize such an object:
state.panelMap = {};

Then, if a panel is marked persistent, store it in
persistedState.panelMap, otherwise destroy it as usual:

for (var panelName in this.panelMap)
{

if (panel.persistent) {
// Do not destroy persistent panel
state.panelMap[panelName] = panel;
} else {

// Destroy the panel and allow it to persist extra
info to the state object
panel.destroy(panelState);

// Remove the panel node from the DOM
var panelNode = panel.panelNode;
if (panelNode && panelNode.parentNode && !
panel.persistent)
panelNode.parentNode.removeChild(panelNode);
}
}

- TabContext constructor:

Here we need to restore the saved panels from persistedState
and put them into the new context's panelMap. In addition, we call the
panel's reinitialize() method here.

Firebug.TabContext = function(win, browser, chrome,
persistedState, persistedPanelMap)
{
this.panelMap = persistedPanelMap ? persistedPanelMap : {};

for (var panelName in this.panelMap) {
var panel = this.panelMap[panelName];
var panelType = Firebug.getPanelType(panelName);
var doc = panelType ?
this.chrome.getPanelDocument(panelType) : null;
panel.reinitialize(this, doc);
}
};


3) TabWatcher

- watchTopWindow:

Here we need to recover the panelMap from persistedState and
call TabContext constructor with the persisted panelMap. This looks as
follows:

// Restore persistent panels.
var persistedPanelMap = persistedState &&
persistedState.panelMap ? persistedState.panelMap : null;

and shortly after

context = this.owner.createTabContext(win, browser,
browser.chrome, persistedState, persistedPanelMap);

4) Firebug.createTabContext needs to become


Firebug.createTabContext: function(win, browser, chrome, state,
panelMap)
{
return new Firebug.TabContext(win, browser, chrome, state,
panelMap);
}


That's it. I will finally provide patches for tabContext.js and
tabWatcher.js (based on revision r1119 from today) and a simple panel
that logs urls to demonstrate the idea. =)

tabContext.js:

17c17
< Firebug.TabContext = function(win, browser, chrome, persistedState)
---
> Firebug.TabContext = function(win, browser, chrome, persistedState, persistedPanelMap)
32c32
< this.panelMap = {};
---
> this.panelMap = persistedPanelMap ? persistedPanelMap : {};
35a36,44
> for (var panelName in this.panelMap) {
> var panel = this.panelMap[panelName];
>
> var panelType = Firebug.getPanelType(panelName);
> var doc = panelType ? this.chrome.getPanelDocument(panelType) : null;
>
> if (panel.persistent) panel.reinitialize(this, doc);
> }
>
82a92
> state.panelMap = {};
99,105c109,120
< // Destroy the panel and allow it to persist extra info
to the state object
< panel.destroy(panelState);
<
< // Remove the panel node from the DOM
< var panelNode = panel.panelNode;
< if (panelNode && panelNode.parentNode)
< panelNode.parentNode.removeChild(panelNode);
---
> if (panel.persistent) {
> // Do not destroy persistent panel
> state.panelMap[panelName] = panel;
> } else {
> // Destroy the panel and allow it to persist extra info to the state object
> panel.destroy(panelState);
>
> // Remove the panel node from the DOM
> var panelNode = panel.panelNode;
> if (panelNode && panelNode.parentNode)
> panelNode.parentNode.removeChild(panelNode);
> }


tabWatcher.js:

125a126,129
>
> // Restore persistent panels.
> var persistedPanelMap = persistedState && persistedState.panelMap ? persistedState.panelMap : null;
>
129c133,134
< context = this.owner.createTabContext(win, browser,
browser.chrome, persistedState);
---
> context = this.owner.createTabContext(win, browser, browser.chrome, persistedState, persistedPanelMap);
>


And the panel:

FBL.ns(function() { with (FBL) {


Firebug.PersistentPanel = function() {};

Firebug.PersistentPanel.prototype = extend(Firebug.Panel,
{
name: "persistent",
title: "Persistent",
//parentPanel: "html",
persistent: true,
counter: 0,

initialize: function() {
Firebug.Panel.initialize.apply(this, arguments);
},

initializeNode: function(oldPanelNode) {

this.persistent = true;
this.counter += 1;
var newPage = this.document.createElement("p");
newPage.textContent = "["+this.counter+"] Current URL: " +
this.context.window.document.URL;

this.panelNode.appendChild(newPage);
},

reinitialize: function(context, doc) {
this.context = context;
this.document = doc;

this.counter += 1;
var newPage = this.document.createElement("p");
newPage.textContent = "["+this.counter+"] Current URL: " +
this.context.window.document.URL;

this.panelNode.appendChild(newPage);

if (this.counter == 5)
this.persistent = false;
},

clear: function() {}

});

Firebug.registerPanel(Firebug.PersistentPanel);

}});



Please note that I cannot make this work as an extension (overwriting
TabContext constructor and TabWatcher.watchTopWindow has strange side
effects). I would thus be glad if this code will somehow be included
in future versions of Firebug (in particular, given that the changes
are of minor nature).

Alright. That's it already! =)

Have a good day!

Alex




On Sep 24, 10:48 pm, John J Barton <johnjbar...@johnjbarton.com>

John J Barton

unread,
Sep 30, 2008, 6:37:34 PM9/30/08
to Firebug
Hi Alex. This looks promising as a solution to problems many folks
have. We'd like to take a look as soon as we can. Also we'll see if we
can allow this kind of work in an extension.

Please open a bug report with your patch as an attachment (against
branches/firebug1.3)
http://code.google.com/p/fbug/issues/list
Please include a testcase and procedure to see the effect of the
patch. Be explicit in the report that you contribute the code under
BSD and that any one employing you for coding is ok with that.

John
> ...
>
> read more »

Alex

unread,
Oct 2, 2008, 9:53:40 AM10/2/08
to Firebug
Done!

See issue 1187: http://code.google.com/p/fbug/issues/detail?id=1187

Hope this helps!

Kind regards,
Alex


On Oct 1, 12:37 am, John J Barton <johnjbar...@johnjbarton.com> wrote:
> Hi Alex. This looks promising as a solution to problems many folks
> have. We'd like to take a look as soon as we can. Also we'll see if we
> can allow this kind of work in an extension.
>
> Please open a bug report with your patch as an attachment (against
> branches/firebug1.3)http://code.google.com/p/fbug/issues/list
> ...
>
> read more »

Rob Campbell

unread,
Oct 2, 2008, 10:48:24 AM10/2/08
to Firebug
nice! Very cool idea and implementation, Alex. Thanks!
> ...
>
> read more »

John J Barton

unread,
Oct 3, 2008, 12:53:33 PM10/3/08
to Firebug


On Oct 2, 6:53 am, Alex <m3nikm...@yahoo.fr> wrote:
> Done!
>
> See issue 1187:http://code.google.com/p/fbug/issues/detail?id=1187
>
Thanks. The next step is to figure out how to activate persistence. I
suppose we could have users select this as an option, but that sounds
very clunky. Isn't there a way to determine from actions on a page
whether the load that replaces the page "goes with it" and thus the
console should persist?

jjb

Alex

unread,
Oct 3, 2008, 2:27:52 PM10/3/08
to Firebug
My goal was to give the developer (and not necessarily the user) the
choice of whether she wants to use a persistent or non-persistent
panel.

What concerns the console, in my (humble) opinion the user might well
decide whether he wants to keep old messages or not. I wasn't
primarily implementing this feature for the console panel though (and
haven't tested this at all either). I'm personally using it to
visualize aggregated webpage information. I could imagine a persistent
configuration panel (where you can decide on the activation of
Console, Script, Net, ... or enter other configuration details...). I
could also imagine a panel that holds some sort of history of actions/
state info with undo/redo features (at least for extensions). Most of
the time though, panels will be/stay non-persistent, I guess.

If you want to decide on the persistence of a panel based on some
actions, you need to make sure to do that before TabContext.destroy()
destroys the correpsonding panel. My first idea is to attach a
listener (owned by a panel) to your action/event of choice and let
this listener decide on the persistence (setting the boolean
'persistent' attribute of the panel). It remains to be seen whether
this leads to timing issues (my guess is no). Another simple solution/
idea might be to check interesting actions in
TabWatcher.unwatchContext() (somewhere in the region where you
initialize persistedState with the current page's location), but
before the destruction of the context.

Hope this helps!

Alex


PS. Please note that I haven't extensively tested the contributed
code, but tried minimizing sideeffect in the implementation and it
works without problems on my Windows and Linux machines. I'm not
exactly sure about the etiquette here either, and am aware that I
haven't introduced myself yet. So for those who are interested in it,
my surname is Spengler and I'm a PhD in Paris who's currently working
on web content extraction. =)

Antonin Hildebrand

unread,
Oct 7, 2008, 2:23:34 PM10/7/08
to Firebug
Hi,
I needed same feature as Alex last year for XRefresh extension.
I ended up by keeping all data printed to my panel in a separate data
structure.
When new panel is initialized it can decide to reprint them again
(based on url).

This is simple and dumb solution and panel content blinks when
refreshed. But it works without side effects.

restoreState function is called in panel.initialize method:
http://github.com/woid/xrefresh/tree/master/src/firefox/chrome/content/panel.js#L1396

If you guys implement a robust solution for persistent panels, I'll
adapt my code.
thanks!
Antonin
Reply all
Reply to author
Forward
0 new messages