Abstract: The client was building a downloader tool for customers to get software directly and maintain updates, manage digital rights, and more. The tool also had to incorporate the content of the online store and other online marketing content. It had already been determined that the tool was going to use an embedded browser component for the UI. Now we just had to make it work.
An embedded browser was chosen for the UI layer because they wanted to be able to forward people to the online store and other web-based marketing material without leaving the application. Marketing also wanted to re-use Adobe Flash-based ad banners meant for online ad campaigns. But the tool also needed to act like a desktop application in many ways, including managing installs and modifying registry entries. We couldn't just make it a browser application but we needed to have browser technology at our disposal for the UI.
The Challenges
- Two-way communication between browser and C++ application Since the application needed to run as a privileged install, it was first and foremost a Windows application written in C++. It had to modify registries and launch executables, which a browser can't do. But, some of the user operations (e.g. clicking an HTML button) needed to result in C++ execution, and some of the C++ needed to affect the UI which was all rendered in a browser using HTML and CSS. We needed to have a way for both layers to communicate to each other easily and robustly. It was a wholly different way of thinking about the UI in a browser.
- A custom rendering framework We needed to be able to build everything in UI dynamically. There were no HTML pages; there was no application server for rendering HTML. All of it had to be done via a javascript rendering framework and it had to be all managed well enough that modifications, replacements, and changes could be effected with simple calls.
- Extreme memory issues All that DOM swapping was going to be a problem. It's well-known that DOM modifications in javascript cause memory leaks in Internet Explorer, the browser we were using. This is normally minimized when the user loads a new page. But we had no "pages" in this application. It as never going to be reloaded while in use. We had to make sure the memory leaks from DOM manipulation were minimized.
Our Solutions
- The two way communication: The browser-to-C++ communication was relatively simple. The application could simply interrupt the navigation event and inspect the URL. We created a new protocol (note?) which the c++ could recognize and upon doing so it would cancel the navigation event and parse arguments from the "URL". For example, if the user action is supposed to initiate a command to the C++ layer, the browser location would be set to a new href that starts with the custom protocal "foo://". The navigate event would be intercepted by the C++ intead of actually navigating to that non-existent URL.
clicks a button to download "Call of Duty" the url for that link would be "foo://action=download&id=1234". This was simple and robust, and easy to expand to cover any kind of message we wanted to send to the C++ layer.
The most complicated task was effecting communication the other way. We needed the C++ to be able to execute javascript operations within the browser runtime. The browser object allows for javascript code to be executed in the global scope. We created a singleton object with an API for all the operations the C++ needed to execute in the UI. We implemented the observer pattern with all our javascript components so they could register as listeners to the main singleton and be notified when the C++ made its calls to the javascript layer. This elegant solution let us expose everything in the UI to the C++ layer in a scalable, flexible way.
- UI Rendering: None of the existing javascript frameworks such as prototype or jquery were equipped to handle our needs. We needed more than just a set of tools for manipulating existing HTML. We needed to render it all dynamically and manage it at a low level. We needed a UI rendering framework similar to other programmatic platforms like Java Swing, Microsoft Foundation Classes, etc. We needed a full fledged, robust UI library.
So we built one. We patterned a lot of the functionality after existing successful UI libraries like Swing. (Although we didn't go so far as to intentionally inflict GridBagLayout on ourselves!). The result of this project was a highly developed, comprehensive robust UI rendering library in javascript which served our purposes and is still a proprietary internally used library for the client.
- Internet Explorer memory leaks:
Rapidly creating and destroying DOM Elements with the rendering framework would cause massive memory leaks in Internet Explorer. We couldn't just move forward with that technique. This was our biggest challenge. We built some of our own tools for helping us get a handle on the memory issues. We built a test suite in javascript that let us run each of our custom components through massive iterations of creation and removal so that we could test and track our improvements to the memory management. We tried a few different approaches, some of which mainly served to illuminate what not to do.
We settled on a robust solution of cacheing DOM constructs and reusing them whenever possible. Each major UI component was set up to utilize a DOM caching service that let it store DOM structures for reuse later. While there was no way to ensure perfect handling of IE memory leak issues, we were able to get it down to acceptable levels for all the use cases we needed to support.
The Results
All the technical and design goals were met. The product went live in 2006 and has remained in production through several iterations. We gave a presentation on the project and the technology involved at the October 2006 Ajax Experience conference.
More case studies