Browser plugin life cycle


#1

I’m experimenting with the Browser plugin system (I’d like to be able to do web apps in PILS using the Juce bindings). But I’m confused about the life cycle of a browser plugin instantiation, specifically how to deal with initialization, cleanup, threading etc.

The life cycle of my desktop app (which lives in a single exe file) is like this:

:arrow: 1 . The PILS interpreter kernel and the Juce bindings are initialized using static objects. The C++ runtime will initialize these objects before running main().

:arrow: 2. The details of initializing Juce is handled by START_JUCE_APPLICATION (JPilsApplication)

:arrow: 3. JPilsApplication::initialise() executes a PILS boot expression which returns a PILS function object called the command line handler. (This object actually contains the PILS programming system.)

:arrow: 4. The command line handler is applied to the command line, and to any subsequent command lines passed by anotherInstanceStarted(). (Often, the handler will open a document window, using a file name given in the command line.)

:arrow: 5. The PILS wrappers for Juce components keep a count of the desktop components. When this count reaches zero, quit() is called.

:arrow: 6. JPilsApplication::shutdown() releases the command line handler, and also some objects created by the constructors of the static objects in step 1.

The above is a simplified version that doesn’t discuss threads, allocators etc. etc.

Anyway, I’d like to keep as much as possible of this architecture in the plugin, so that I can create apps on the desktop and turn them into web apps later, without too many changes.

:slight_smile: 1 . The PILS interpreter kernel and the Juce can still be initialized using static objects. The C++ runtime will initialize them when the plugin is loaded.

:oops: 2. There is no JUCEApplication object, or is there? I first assumed the BrowserObject had this role but this object seems to be destroyed and recreated if you go to another URL and then back.

:wink: 3. It seems I can use a classic singleton pattern for the PILS command line handler, storing a pointer in a static member, creating it (and booting PILS) at the first use.

:wink: 4. The command line handler gets fed by commands passed to it by Javascript. This might be an URL from whence the handler can load the PILS application.

:?: 5. The BrowserPluginComponent - or a component inside it - will play the role of the main window (I wonder if I can equip it with a Juce menu bar???) - but the deletion of this window doesn’t imply that the plug is unloaded.

:twisted: 6. I can release the command line handler and the objects created by the constructors of the static objects, in a destructor of a static object, which will get called when the plugin gets unloaded. But I’m not sure this is the right way to go. The command line handler might hold Juce objects that might not be working correctly because Juce might be nuked at this stage. Is there some clean way to be told that Juce is about to go under?

(I tried using the BrowserPlugin and BrowserObject destructors but it turns out these objects aren’t as long-lived as the plugin.)

Then, there is the issue of threading. What can be expected from browser plugins under various architectures? Is the browser plugin model inherently single-threaded, or random-threaded?


#2

Sounds like a good project.

No, there’s no JUCEApplication object - the way they work is that each time an instance of the plugin is created, it has a corresponding BrowserPluginComponent which gets deleted when the user moves away from the page.

You’re ok to use singletons, but a better way to do it is probably to reference-count your shared objects, rather than use static objects, so that they only exist when there’s 1 or more BrowserPluginComponents in use.


#3

I changed to refcounting and this works ok except for a small memory leak.

The leak seems unrelated to what I’m doing, and sure enough, your demo shows it when debugged with MSIE 7.

:arrow: Software: XP sp3, MSIE 7.0.5730.13, VC2008 Express 9.0.21022.8 RTM
For convenience, in MSIE settings, Advanced tab, allow active content on this computer.

:arrow: Open JuceBrowserPluginDemo.sln with VC 2008 Express

:arrow: Build in Debug mode (JUCE_ASIO=0 and JUCE_QUICKTIME=0)

:arrow: Command prompt, cd …\Build\Win32\Debug, regsvr32 npJuceBrowserPluginDemo.dll

:arrow: In Project properties, Debug, set Command=“C:\Program files\Internet Explorer\iexplore.exe”, argument=“C:\juce\extras\browser plugins\demo\test.html”

:arrow: Now start debugging with F5. MSIE shows the plugin.

:arrow: End the debugging session by closing MSIE, without doing anything with the plugin.

:oops: In the debug output pane, scroll up 30-odd lines of Unload/Thread exit lines, and this appears:

DLL_PROCESS_DETACH shutdownJuce_GUI() Detected memory leaks! Dumping objects -> {598} normal block at 0x04260130, 36 bytes long. Data: < r h & P q > C0 E7 72 03 68 00 26 04 01 00 00 00 50 E8 71 03 {131} normal block at 0x04256FA8, 52 bytes long. Data: < r r r r > 20 FE 72 03 F8 FD 72 03 DC FD 72 03 C0 FD 72 03 Object dump complete. Detected memory leaks! Dumping objects -> {598} normal block at 0x04260130, 36 bytes long. Data: < r h & P q > C0 E7 72 03 68 00 26 04 01 00 00 00 50 E8 71 03 {131} normal block at 0x04256FA8, 52 bytes long. Data: < r r r r > 20 FE 72 03 F8 FD 72 03 DC FD 72 03 C0 FD 72 03 Object dump complete. 'iexplore.exe': Unloaded 'C:\juce\extras\browser plugins\demo\build\win32\Debug\npJuceBrowserPluginDemo.dll'

(I don’t know why it appears twice. When the plugin buttons are pushed, more blocks are reported.)

The leaks may seem insignificant - but a clean exit would make it easier to debug my own code for leaks.

When I tried to chase the leak (believing it was caused by my own code), I found that one of the blocks seemed to be used for a JuceDropTarget object in a win32 ComponentPeer, don’t know if this helps.


#4

In plugins you can sometimes get leaks of a few Message objects that have been posted to the event queue but which didn’t arrive before the host closed down the plugin. What you’re seeing looks like that to me.

It’s not really something that could be easily avoided, except by expensive techniques like keeping a list of all the active messages and cleaning them up on shutdown. It’s debatable whether it’d be worth the extra overhead and complexity to avoid a few bytes occasionally leaking.


#5

I had the JuceDropTarget leak issue the other day. It came from the IDropTarget refcount never being fully decremented, and was to do with my custom heavyweight window not being properly detached from its parent window. It was odd, because everything i tried to do to fix it (and i was already detaching it) didn’t change anything. Then, later, i changed something else and it stopped.
[in particular i made sure that my window was always attached to the correct window when the parent peer changed, via the peer change callback in ComponentMovementWatcher. if the peer was null, i made sure it was properly detached, otherwise it was attached to the window]. However, none of this will likely be of any use to you, because i’m sitting quite outside the juce box with my strange window requirements/system…

perhaps it might give a clue though

*edit: when i say ‘properly detached’ and ‘attach’ etc… i’m talking about using the win32 SetParent() call.