FYP_Final_Report_2.doc.doc
-
Upload
sampetruda -
Category
Documents
-
view
2.374 -
download
0
description
Transcript of FYP_Final_Report_2.doc.doc
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
DE3
Authors: Kou Man Tong, Fung Chak Fai 1
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
Powerful AJAX Tools for Lightweight Portable Web User Interfaces
by
Kou Man Tong Fung Chak Fai
Advised by
Prof. Dekai Wu
Submitted in partial fulfillment
Of the requirement for COMP 396
in the
Department of Computer Science
Hong Kong University of Science and Technology
2006-2007
Name of Student: Signature: Date:
Name of Student: Signature: Date:
Authors: Kou Man Tong, Fung Chak Fai 2
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
Table of Contents
1 Introduction........................................................................................................5
1.1 Overview..................................................................................................5
1.2 Objectives................................................................................................5
1.3 History......................................................................................................6
1.4 Work Completed....................................................................................6
2 Object Model and Event System.................................................................13
2.1 Introduction..................................................................................................13
2.2 Circular References and Internet Explorer 6.......................................14
2.3 The Lapsed Listener Problem..................................................................24
2.4 Performance Implications.........................................................................29
2.5 The Search for Solutions..........................................................................39
2.6 Object Model of WT Toolkit.......................................................................47
2.7 Weak Reference Emulation......................................................................51
2.8 Event System................................................................................................56
3 AJAX RPC System..........................................................................................58
3.1 Using the AJAX RPC System in WT Toolkit.........................................58
3.2 The JSON Data Exchange Format..........................................................60
3.3 Using the AJAX RPC System in Web Server.......................................61
4 Regular Widgets System...............................................................................65
4.1 Introduction..................................................................................................65
4.2 The wtWidget Class....................................................................................65
4.3 Example Widget Classes...........................................................................67
5 AJAX Forms System......................................................................................69
5.1 Introduction..................................................................................................69
5.2 Using AJAX Forms in WT Toolkit............................................................69
5.3 Example Input Form...................................................................................72
6 Vector Graphical Widgets System..............................................................75
6.1 Introduction..................................................................................................75
6.2 Base Components of the Vector Graphics System............................76
6.3 Charting Engine...........................................................................................78
6.4 G=(V,E) Graph Engine................................................................................82
6.5 3D Objects.....................................................................................................83
6.6 Live Demonstrations..................................................................................83
7 Conclusions......................................................................................................86
8 References........................................................................................................88
9 Appendix A: Source Code for Finding Programming Patterns Prone
Authors: Kou Man Tong, Fung Chak Fai 3
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
to Memory Leaks in Internet Explorer 6............................................................91
9.1 Leak Experiments with Simple Circular References.........................91
9.2 Leak Experiments with Closures............................................................95
9.3 Leak Experiment with Function Objects...............................................98
10 Appendix B: Source Code for Finding JavaScript Operations
Affected by Memory Leaks in Internet Explorer 6........................................100
11 Appendix C: Source Code for Showing JavaScript Object Creation
Latency Deteriorating with Number of Reachable JavaScript Objects
under Internet Explorer........................................................................................107
12 Appendix D: Conversations in Dojo Interest Mailing List
Regarding the Lapsed Listeners in Dojo Toolkit...........................................111
Authors: Kou Man Tong, Fung Chak Fai 4
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
1 Introduction1.1 Overview
Web applications are taking a more and more important part in our daily
lives, and they are certainly not limited to convenience applications like
Google Maps - the use and development of web applications is penetrating
all kinds of modern institutions. For example, enterprise resource planning
systems has been implemented as web applications by major ERP vendors
since 2000 [14]; web applications are playing a crucial role in the
deployment of citywide wireless Internet projects in some cities of the
United States [18]. In HKUST, web applications, such as WebCT[12],
VELA[11], LMES[24] are playing a more and more important part in
teaching activities.
As web applications has progressed from simple HTML tables and forms to
modern rich Internet applications [32] that incorporate features and
functionalities of desktop applications, there is a great need for robust web
application development frameworks that let application developers write
web applications with the same kind of ease as developing desktop
applications. Recently, a powerful paradigm called AJAX [31]
(Asynchronous JavaScript and XML) has emerged in the web development
community that makes it possible to write rich, responsive web applications
without the use of proprietary browser plug-ins. The AJAX approach was
first popularized by Google Maps and Gmail and later adopted by many
websites and web applications such as Flickr[5], Google Docs and
Spreadsheets[13], EditGrid[26] and PCMS[16]. A significant problem with
AJAX is that many people are regarding it as something very difficult and
exotic. Some people think that AJAX is so hard that a manager from
Microsoft was quoted as saying, “People who do (AJAX development) are
rocket scientists” [15]. In this project, we are going to design and implement
an open source toolkit, called WT Toolkit, which makes it easy for
application developers to write rich AJAX-style web applications.
1.2 Objectives
The objectives of this project are the following:
Authors: Kou Man Tong, Fung Chak Fai 5
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
1. To make AJAX RPC requests as simple as possible to the eye of a web
developer, such that he can have more time to focus on the business logic
instead of doing a lot of mechanical coding to handle browser
irregularities;
2. To build a rich and robust graphical widget library, such that web
developers using WT Toolkit can easily draw convincing user interfaces
that give users an impression that what he is using is indeed a serious
application, rather than just a web page with forms;
3. To implement a robust event handling mechanism in JavaScript, based
on the tried-and-true observer pattern often seen in desktop application
development environments;
4. To make WT Toolkit agnostic to the server side architecture.
1.3 History
WT Toolkit is an open source re-implementation of a proprietary AJAX
toolkit developed for the PCMS project (PePWave Centralized Management
System) in PePLink Ltd., an R&D oriented company headquartered in Hong
Kong. PCMS has been deployed in various city-wide wireless Internet
installations in the United States. For example, it is currently deployed in the
City of St. Cloud, Florida [18]. It is also mentioned in one of the press
releases of Tropos Networks [20], a US company specialized in citywide
Wi-Fi Internet service. One of the project team members of this project, Kou
Man Tong, is the original developer of both the PCMS system and WT
Toolkit.
1.4 Work Completed
AJAX RPC, Event System, Basic Widget Library
A signal-slot event handling system, a set of DHTML widgets, and the
AJAX RPC class (wtRemoteProcedureCall) were implemented in January
2006. These formed the base feature set of WT Toolkit and were being
incrementally improved upon during the course of the project.
A real application demo of WT Toolkit, “francium’s Xanga Tracker” [27]
was written in January 2006 and has been opened to public registration from
Authors: Kou Man Tong, Fung Chak Fai 6
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
February 2006. It has 113 registered users as of 7th April, 2007. Some of the
registered users of “francium’s Xanga Tracker” are current Computer
Science and Computer Engineering students in HKUST. The earliest
versions of “francium’s Xanga Tacker” had a total of less than 10 lines of
HTML code – all of the client side user interface logic was implemented in
JavaScript with WT Toolkit. The server side logic was implemented in
Python. mod_python [9] was used in implementing the server side logic.
Figure 1 – Pressing the “Xanga Log” tab triggers a refresh of displayed contents. This operation
would leak more than 10MB of memory every time it is executed on the earliest versions of
francium’s Xanga Tracker.
It was discovered with the application demo that earlier versions of WT
Toolkit had a severe memory leak problem. An early user refreshing his
Xanga Log contents in “francium’s Xanga Tracker” for a few times would
see the committed memory space of his web browser increase by more than
10MB, for each time he clicked on the “Xanga Log” tab, which triggered the
refresh.
Investigations into the memory leak problem showed that two memory
management problems existed in the earliest versions of WT Toolkit. One of
them was specific to Internet Explorer 6, in which the browser’s garbage
collector was unable to determine the liveliness of objects when there exist
Authors: Kou Man Tong, Fung Chak Fai 7
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
circular references with DOM objects. Another problem discovered was the
lapsed listener problem [25], which is a programming anti-pattern related to
primitive implementations of the observer design pattern. It was also found
that another popular open source web UI toolkit, Dojo Toolkit [6] suffers
from the lapsed listener problem.
To combat the memory management problems in WT Toolkit, weak
reference emulation logic was implemented in WT Toolkit. The weak
reference emulation logic also provided various side benefits to WT Toolkit
in addition to solving the memory leak.
The base features of WT Toolkit described above are reported in details in
Sections 2, 3 and 4 of this report.
AJAX Forms, Form Widget Library
One of the most obvious uses for AJAX RPC is to submit user inputs, and
get feedback from the web server, without refreshing the web page. An
AJAX form class, wtForm, with a set of associated form widgets was
implemented in WT Toolkit. The AJAX form system was completed in
January 2006 and was being incrementally improved upon during the course
of the project.
Similar to the AJAX RPC class, the AJAX form class and all associated
form widgets are built upon the toolkit’s signal-slot event handling system.
This gives the web developer an extra degree of flexibility in programming
input forms – for example, a web developer can use the “ValueChanged”
signal of form widgets to notify other JavaScript objects that an input’s value
has been changed.
Figure 2 - A wtSpreadsheet instance.
Since the AJAX form and the form widget library in WT Toolkit are built
Authors: Kou Man Tong, Fung Chak Fai 8
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
upon object oriented principles, sophisticated form widgets can be inherited
and aggregated from more basic form widgets in WT Toolkit. For example,
the wtSpreadsheet object is made by inheriting from a wtDisplay object and
aggregating a number of wtPopupTextbox objects. WT Toolkit is more
advanced in this respect when compared to some other popular open source
AJAX toolkit like Dojo Toolkit. In Dojo Toolkit, AJAX form is merely a
hack to bind AJAX RPC to traditional HTML form DOM nodes [7].
Building new form widgets by aggregation and inheritance in Dojo Toolkit
would be much more difficult than in WT Toolkit.
Figure 3 - A wtSpreadsheet object is made by inheriting from a wtDisplay object and aggregating
multiple wtPopupTextbox objects.
The AJAX form system in WT Toolkit is reported in details in Section 5 of
this report.
SVG/VML Vector Graphics
Under the advice of Dr. Dekai Wu, it was discovered in April 2006 that
vector graphics can be drawn under Internet Explorer with VML [19] and
under Firefox with inline SVG [30]. Initial versions and demonstrations of
our SVG/VML module were implemented December 2006, but public
release of the code module was only available from March 2007 with the
release of WT Toolkit 0.2.1:
Authors: Kou Man Tong, Fung Chak Fai 9
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
http://sourceforge.net/project/showfiles.php?group_id=157600&package_id=176278
Apart from being able to draw basic geometric shapes like lines, polygons,
curves, and ellipses. WT Toolkit’s SVG/VML module is capable of drawing
tangible widgets aggregated from geometric shapes. For example, WT
Toolkit has a wide variety of charting widget classes, such as the bar chart
widget class (wtBarChart), the pie chart widget classes (wtPieChart and
wtAutoPieChart), the line chart widget class (wtLineChart), the radar chart
widget class (wtRadarChart), the scatter chart widget class
(wtScatterChart), and many more.
Figure 4 - Statistical charts drawn with WT Toolkit's vector graphics system.
Another notable feature of WT Toolkit’s vector graphics system is the ability
to draw interactive G=(V,E) graphs. This made it possible for us to write a
UML class diagram editor based on WT Toolkit, as one of the application
demonstrations of WT Toolkit.
Authors: Kou Man Tong, Fung Chak Fai 10
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
Figure 5 - UML class diagram drawn with WT Toolkit's vector graphics system. All UML class
diagrams in this report are drawn with this application demo.
The SVG/VML vector graphics system in WT Toolkit is reported in details
in Section 6 of this report.
API Documentation
An API documentation describing WT Toolkit’s entire public API was
released with WT Toolkit 0.3.0. The documentation was generated with
JSDoc [29], a documentation generator very similar to the javadoc tool
distributed with Sun Microsystem’s Java Development Kit.
WT Toolkit’s API documentation can also be accessed online, without
downloading our code package, by the following URL:
http://wt-toolkit.sourceforge.net/doc-0.3.2/
Open Source
WT Toolkit is an open source project. The entirety of our project’s source
code are released to be public and licensed under the GNU Lesser General
Public License (LGPL) [8].
Our project is hosted on SourceForge:
http://sourceforge.net/projects/wt-toolkit
Authors: Kou Man Tong, Fung Chak Fai 11
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
Packaged code releases of WT Toolkit can be found in the “Download”
section of our SourceForge website.
Our development code can be checked out from the SourceForge SVN
repository by the command (assuming you have the svn tool installed):
svn co https://wt-toolkit.svn.sourceforge.net/svnroot/wt-toolkit wt-toolkit
Authors: Kou Man Tong, Fung Chak Fai 12
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
2 Object Model and Event System2.1 Introduction
The earliest versions of WT Toolkit did not have a comprehensive object
model – it was just a collection of code wrapped up in functions to be reused
by a programmer. This ad-hoc implementation style had some serious
deficiencies, however:
1. It leads to memory leaks, especially under Internet Explorer 6 where the
JavaScript garbage collector cannot handle circular references involving
DOM nodes.
2. It does not take advantage of built-in OOP features in JavaScript such as
prototype inheritance.
3. It made implementing the signal-slot event handling system tricky and
prone to errors.
A more comprehensive object model was implemented in WT Toolkit since
version 0.2.0 to solve the three problems above. Most of the WT Toolkit
code from 0.2.0 and above is encapsulated into classes inherited from the
toolkit’s base class, wtObject. The base class provides signal-slot event
handling and the ability to act as a proxy to memory expensive (and leak
prone) objects like DOM nodes via weak references.
In the subsections that follow from here, section 2.2 explains how memory
leaks can occur with circular references in Internet Explorer 6. Section 2.3
explains how memory leaks can occur in naïve signal-slot event handling, or
more generally, observer design pattern implementations via the lapsed
listener problem [25]. Section 2.4 explains the performance implications to
Internet Explorer 6 when there are memory leaks. Section 2.5 explains how
the project group searched for solutions for the memory leak problems
observed in early versions of WT Toolkit, and how we discovered other
popular web UI toolkits to be vulnerable to memory leaks as well. Section
2.6 explains the current object model in WT Toolkit in detail. Section 2.7
explains the weak reference emulation logic, the accompanying automatic
garbage collection logic, and a proof of correctness to our garbage collector.
Section 2.8 talks about how the event system in WT Toolkit can be used by
the web application developer.
Authors: Kou Man Tong, Fung Chak Fai 13
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
2.2 Circular References and Internet Explorer 6While Internet Explorer is often criticized for its numerous problems with
standard compliance (e.g. poor CSS support) – a less well known, but very
significant problem with Internet Explorer 6 is memory leakage. Memory
leakage is arguably a more painful problem compared to poor standards
compliance. This is because while problems with standard compliance (e.g.
a broken CSS attribute) can be easily discovered with test cases, memory
leak problems can easily go unnoticed until the very late stages of an
application development process.
As discussed in the MSDN article, “Understanding and Solving Internet
Explorer Leak Patterns” by Microsoft [22], one of the most common causes
for memory leaks under Internet Explorer is circular referencing. The
Javascript garbage collector implemented in Internet Explorer uses reference
counting for determining the liveliness of an object. As a result, Internet
Explorer 6’s garbage collector is unable to determine a set of circular
referencing objects to be unreachable in some circumstances, even though
the set of objects is no longer used by the web application.
function createEl(i) {
var span = document.createElement("span");
span.className = "muci";
span.innerHTML = " foobar #"+i+" ";
span.onclick = function() { alert(this.innerHTML + "\n" + i); };
document.body.appendChild(span);
};
function start() {
var T1 = (new Date()).getTime(); // DEBUG.PROFILE
for (var i = 0; i < 3000; ++i)
createEl(i);
alert(((new Date()).getTime() - T1) / 1000); // DEBUG.PROFILE
};
Figure 6 - Bazon's code sample for demonstrating memory leaks in Internet Explorer 6.
Symptoms of memory leaks in Internet Explorer 6 are often encountered by
Authors: Kou Man Tong, Fung Chak Fai 14
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
AJAX application developers. An often-cited technical article by Mihai
Bazon [2] states the following symptoms:
1. Progressively degrading performance;
2. Unbound growth of memory space occupied by iexplorer.exe;
3. The symptoms of memory leak do not disappear even after a page
refresh, or navigating to another URL.
For the purpose of making a web development toolkit, we need to
understand how circular references can be formed during application
development, and devise solutions to prevent the web developer from too
easily forming the above mentioned circular referencing patterns without
putting unreasonable burden on the web developer.
Both Microsoft’s and Bazon’s articles did not clearly describe why memory
leaks occurred, although they both pointed to circular references as the
culprit. For example, in Error: Reference source not found, Bazon claimed
that reference cycles are created inside the function createEl(), with two
edges in the reference cycle:
1. The variable “span” refers to the closure via its “onclick” attribute;
2. The closure refers back to the variable “span”.
Bazon did not specify why reference number 2 was formed, however; he
only specified and demonstrated that reference number 2 exists. In this
specific case, it is essential to understand how reference number 2 came into
existence even without the identifier “span” being referred to within the
closure, such that the appropriate preventive or mitigative measures can be
determined for plugging the corresponding memory leak hole. Does the
“span” get attached to the closure because Internet Explorer would
automatically attach every object reference it can find from the outer scope
to a closure? Does the “span” get attached simply because Internet Explorer
has determined that the “this” identifier within the closure is identical to
“span”, when the closure is assigned to “span” as its onclick attribute? The
solution for tackling this memory leak pattern could be different depending
on how Internet Explorer decided to attach the outer scope variable’s
reference to the closure.
Authors: Kou Man Tong, Fung Chak Fai 15
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
As a result, we conducted some experiments in October 2006 to explore the
finer details of memory leaks under Internet Explorer 6.
Methodology of Experiments
While it is possible to determine whether a programming pattern is leaking
by looping it a lot of times over and observing whether iexplorer.exe’s
committed memory space increases in an unbounded way, even though we
are sure that the number of reachable objects in our Javascript program is
not increasing; this method is not perfect because it can always be argued
that the seemingly unbounded committed memory size growth is observed
only because Internet Explorer’s garbage collector has not swept away the
unreachable objects, it is no proof to the garbage collector’s inability to
determine the unreachability of objects.
Figure 7 - It is always possible to argue that the seemingly unbounded allocated memory size
growth is only observed because the garbage collector has not yet swept away the unreachable
objects, it might do so later. Graph adapted from Chapter 6 of the book “Bitter Java” by Bruce
A. Tate. [25]
Luckily, there is a more accurate open source tool for detecting memory
leaks in Internet Explorer, called Drip [28]. Drip has an important limitation,
however – it cannot detect memory leaks that are not persistent after page
refreshes, which are called “pseudo-leak” in Microsoft’s article. This
limitation can be deduced by reading Drip’s source code
(http://ieleak.svn.sourceforge.net/viewvc/ieleak/trunk/drip/). By reading the
source files MainBrowserDlg.cpp and JSHook.cpp in Drip 0.5’s source tree,
Authors: Kou Man Tong, Fung Chak Fai 16
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
it can be deduced that the leak check button of Drip 0.5 works by
performing the following procedure:
1. When the “Show DOM Leaks” button is clicked, navigate away from the
current URL to “about:blank” to trigger a Javascript garbage collection.
2. When about:blank has completed loading, any DOM objects from the
previous page that have not been unloaded is displayed as leaked DOM
nodes.
Figure 8 - Drip 0.5 is accurate enough to detect a single DOM object being leaked.
It is obvious that DOM objects not “carried over” to the about:blank page
could not be detected by Drip as memory leaks.
void CMainBrowserDlg::OnBnClickedCheckLeaks() {
// When the leak test button is pressed, navigate to the blank document
// so that the browser will release all of its elements. Set
// m_waitingForBlankDoc to true so that the DocumentComplete event
// handler will know to check for leaks when the blank document
// finishes loading.
//
requestClosePopups();
Authors: Kou Man Tong, Fung Chak Fai 17
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
Navigate(L"about:blank");
m_waitingForBlankDoc = true;
}
Figure 9 - Code snippet from MainBrowserDlg.cpp of Drip 0.5. This function is the click event
handler for the “Show DOM Leaks” button. Notice the function changes the current URL to
“about:blank”.
For the purpose of this report, we will still call unbounded memory size
growth not persistent after page refreshes as memory leaks, because it is
possible and desirable for AJAX applications to operate without any page
refreshes at all.
Even with this limitation, Drip is good enough for this part of our
investigation, because it has been asserted in both Bazon’s and Microsoft’s
articles that memory leaks involving DOM node circular references are
persistent even after page refreshes.
With the knowledge that Drip can be used for detecting circular reference
memory leaks in Internet Explorer, we can now define an experiment
procedure for showing whether a particular programming pattern would leak
memory or not:
1. Write a simple JavaScript program with the allegedly leaky
programming pattern and embed that JavaScript program in an HTML
file.
2. Prepare one or more very similar HTML files, each with only one or two
lines of JavaScript essential to the programming pattern modified or
removed, for control experiment.
3. Load the HTML file from step 1 to Drip, and see if any memory leak is
detected.
4. Load the HTML file from step 2 to Drip, and see if any memory leak is
detected.
5. If Drip reports positive result on step 3 and negative result on step 4,
then the programming pattern is determined to leak memory in Internet
Explorer 6.
Experiment with Bazon’s Code Sample
Authors: Kou Man Tong, Fung Chak Fai 18
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
To show that our experiment procedure works, we tried the procedure with
one of Bazon’s code samples.
Authors: Kou Man Tong, Fung Chak Fai 19
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html> <head>
<title></title>
<script type="text/javascript">
function createEl(i) {
var span = document.createElement("span");
span.className = "muci";
span.innerHTML = " foobar #"+i+" ";
span.onclick = function() { alert(this.innerHTML + "\n" + i); };
document.body.appendChild(span);
};
function start() {
var T1 = (new Date()).getTime(); // DEBUG.PROFILE
for (var i = 0; i < 3000; ++i)
createEl(i);
alert(((new Date()).getTime() - T1) / 1000); // DEBUG.PROFILE
};
</script>
</head>
<body>
<h1></h1>
<a href="javascript:start()">start</a>
<hr />
</body> </html>
Figure 10 - Bazon's code sample from Error: Reference source not found, embedded into HTML.
Authors: Kou Man Tong, Fung Chak Fai 20
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html> <head>
<title></title>
<script type="text/javascript">
function createEl(i) {
var span = document.createElement("span");
span.className = "muci";
span.innerHTML = " foobar #"+i+" ";
span.onclick = null;
document.body.appendChild(span);
};
function start() {
var T1 = (new Date()).getTime(); // DEBUG.PROFILE
for (var i = 0; i < 3000; ++i)
createEl(i);
alert(((new Date()).getTime() - T1) / 1000); // DEBUG.PROFILE
};
</script>
</head>
<body>
<h1></h1>
<a href="javascript:start()">start</a>
<hr />
</body> </html>
Figure 11 - Control experiment, with the modified code (1 line) underlined.
Since part of the alleged reference cycle in Bazon’s code sample is the
closure assigned as span’s click handler, we replaced the closure with null in
our control experiment.
Authors: Kou Man Tong, Fung Chak Fai 21
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
Figure 12 - Drip test results of Bazon’s code sample (left) and control experiment (right).
From the screenshots above, Drip showed positive result for the code sample
and negative result for the control experiment. Therefore, we arrive at the
same conclusion as Bazon – the closure was part of the reference cycle,
which induced memory leak. Therefore, our experiment procedure worked.
Experiments for Finding Leaky Programming Patterns under
Internet Explorer 6
We designed a total of 8 experiments for determining which programming
patterns would leak in Internet Explorer 6, and which would not. We do not
believe that our list contains all possible programming patterns that would
induce memory leaks by forming circular references, but all our cases are all
very simple that we believe should provide significant insight for the
construction of WT Toolkit.
The source code for the 8 experiments can be found in Appendix A of this
report. The source code for the corresponding control experiments is not
included because it is wasteful to print out the same code samples again with
only one to two lines changed. Instead, we included instructions for
producing the control experiments in the code samples that are known to
leak, for readers who would like to reproduce our experimental results.
Authors: Kou Man Tong, Fung Chak Fai 22
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
Below is a table of our experiment results:
# Reference Description of Pattern Does it Leak?
1 Figure ?? A DOM node referring to itself via an attribute. Yes
2 Figure ?? Two DOM nodes referring to each other. Yes
3 Figure ?? A DOM node referring to itself via a Javascript
object.
Yes
4 Figure ?? Two Javascript objects referring to each other, with a
DOM node attached to one of the objects as an
indicator such that any leak can be detected via Drip.
No
5 Figure ?? Closure declared in the same scope as a DOM node,
and assigning that closure as the DOM node's
attribute.
Yes
6 Figure ?? Same as the last experiment, except that the DOM
node is put inside a Javascript object such that
identifiers inside the closure cannot be bound to the
DOM object directly.
Yes
7 Figure ?? Trying to avoid circular references via closures with
even more closures. This test case is modified from
one of the suggested solutions mentioned in Bazon's
article.
No
8 Figure ?? Making a function object by calling the Function
class constructor within the same scope of a DOM
node, and assigning the function object as a DOM
node attribute.
No
Two emerging patterns can be deduced from the experiment results above:
Hypothesis 1 - Javascript circular references in Internet Explorer 6 create
cross-page memory leaks if and only if the reference cycle includes one or
more DOM nodes in it.
Hypothesis 2 - Javascript closures in Internet Explorer 6 retain references to
all outer scope variables, even though the corresponding same-name
identifier is never written inside of the closure. It is therefore very easy to
create circular references involving DOM nodes with closures.
Authors: Kou Man Tong, Fung Chak Fai 23
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
Figure 13– Relations between objects created in init() from our first four code samples,
illustrated as UML instance diagram. Notice that the DOM node is not involved in the reference
cycle for the fourth code sample.
Error: Reference source not found can be justified by looking at the results
from the first four code samples and comparing their source code. The first
three cases all leaks memory, but the last case does not. All four cases
contain circular references; the only difference is, for the last case, the DOM
object is not involved in the reference cycle.
Authors: Kou Man Tong, Fung Chak Fai 24
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
Figure 14 – Relations between objects created in init() from code samples 5 and 6, illustrated as
UML instance diagram.
Hypothesis 2 can be justified by looking at the source code of code samples
number 5 and 6. Memory leak occurred in both code samples, and control
experiments confirmed that the memory leak is solely resulted from the
closures. Since the only known way for including the closure to leak
memory is by circular reference, the closure must be part of a reference
cycle. If a closure forms a part of a reference cycle, then the closure must be
referring to some other objects, otherwise no cycle can be formed. In code
samples 5 and 6, discounting the extra closure created for control experiment
(which can be removed and there would still be memory leak), the only
other object that closure “f” can refer to is “node” and “jso” respectively.
The identifiers “node” or “jso” is never written inside the closure “f”. So “f”
retains references from the outer scope even though the identifiers “node” or
“jso” was never written inside “f”. The circular reference has nothing to do
with the “this” identifier inside the closure, because the control experiment’s
closure “g” in the code sample does not leak memory.
2.3 The Lapsed Listener Problem
Authors: Kou Man Tong, Fung Chak Fai 25
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
Figure 15 – An observer pattern implementation shown on Wikipedia, notice that the event
publisher class Ball keeps strong references to its event subscribers of the IObserver class. In
the programmer’s eyes, instances of Ball can have a very long lifecycle while instances of
IObserver can have a very short lifecycle (e.g. football players may leave and join the game, yet
the football is the same). This implementation is prone to the lapsed listener problem.
[33]
The lapsed listener problem is a common programming anti-pattern that
causes the failure to release memory occupied by objects, even though said
objects are no longer needed in the programmer’s eyes [10]. It is often the
consequence of a naïvely implemented event handling system designed upon
the observer design pattern. It does not go away even if the garbage collector
of the underlying programming language works perfectly.
“A lapsed listener is when an object is added to a collection but never
removed. The most common example of this is an event listener, where the
object is added to a listener list, but never removed once it is no longer
needed. So the object's usefulness has lapsed because although it's still in the
list, receiving events, it no longer performs any useful function.”
Figure 16 – Quote from the article “How Do You Plug Java Memory Leaks?” from the February
Authors: Kou Man Tong, Fung Chak Fai 26
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
2000 issue of Dr. Dobbs Journal.
Not being aware of the potential danger of lapsed listeners, the signal-slot
event handling system implemented in WT Toolkit prior to version 0.2.0
leaked memory whenever the programmer forgets to disconnect the slots of
an unused object from the signals of other live objects, before that object is
left to disuse (and supposedly left to be cleaned up by the JavaScript garbage
collector). The first usable application demo written with WT Toolkit,
“francium’s Xanga Tracker” [27] showed symptoms of the lapsed listener
problem in its earliest versions. These included:
1. Unbounded growth of allocated memory space for all browsers (Firefox,
IE6, IE7), as the user refreshes his Xanga log entries.
2. A Xanga blog page being tracked by “francium’s Xanga Tracker v0.1.3”
would freeze the browser if left opened for too long (say, a few days)
and is closed.
Figure 17– Unbounded growth in allocated memory space observed with firefox.exe as the user
refreshes his Xanga Log entries repeatedly in francium’s Xanga Tracker v0.1.3.
Authors: Kou Man Tong, Fung Chak Fai 27
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
A quick check on our Xanga Tracker’s source code found a very dangerous
lapsed listener pattern in the code concerning refreshing Xanga Log entries:
1. There are many short-lived DOM objects as the cells and rows in the
table displaying the Xanga log entries.
2. Each one of the short-lived DOM objects is subscribed to a permanent
mouse event publisher, so the mouse event publisher keeps references to
all the short-lived table cells and rows.
3. Every time the Xanga log entries are refreshed, the table cells and rows
disappear and are of no use to the programmer. However, the global
mouse event publisher is still keeping references to them and thus they
cannot be garbage collected.
It is certain that many more such lapsed listener patterns can be found by
inspecting the source code further, because all events in the Xanga Tracker
are handled by the signal-slot system (which is a kind of event handling
mechanism based on the observer pattern) implemented in WT Toolkit. It is
thus very desirable to have a general solution that can mitigate the risk of
lapsed listeners in our signal-slot event handling system.
Lapsed Listeners in JavaScript
At the first glance, it seems that lapsed listeners is purely the programmer’s
fault, and it should be the programmer’s job to solve lapsed listener
problems in his program, instead of WT Toolkit’s job. There are even
superficial parallels that one can easily draw for this argument – the “delete”
keyword in C++, the free() standard library function in C - if it is acceptable
for the programmer to free up the memory taken by an object or a variable in
two of the most popular programming languages, why would it be
unacceptable for the programmer to perform the necessary cleanup of signal-
slot connections before or after an object is left to disuse in JavaScript?
There’s even a “delete” keyword in JavaScript, it seems like talking about
lapsed listeners in JavaScript is nonsensical.
a = {"a":20, "b":30};
b = a;
a.a = 80;
delete a;
Authors: Kou Man Tong, Fung Chak Fai 28
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
alert(b.a + "," + b.b); // The string ”80,30” appears on screen
Figure 18 – Code snippet in JavaScript. Deleting the object “a” does not erase the memory
occupied by its value because the same object reference is held by the identifier “b”.
First, the semantics of the “delete” keyword in JavaScript is different from
that in C++. In C++, “delete” means freeing up the memory held by a
pointer unconditionally – deleting a pointer that is still being held by other
objects in a C++ program would create dangling pointers, which is a
potential danger. The “delete” keyword in JavaScript, however, only means
unbinding an identifier from its value – if the value’s object reference is held
by more than one identifier, deleting just one identifier would not free up the
memory used by the value. The “delete” call in JavaScript is very much like
the unlink() system function or the rm command in Unix systems in this
respect – unlinking a file in Unix does not automatically mean the space
held by the file is freed up, because there may be other hard links bound to
the same file content.
Second, the relation between a pointer returned by malloc() or the new
operator and its allocated memory area is always one-to-one in C or C++.
The relationship between signals and slots in a signal-slot event handling
system, however, can be many-to-many. In talking about lapsed listeners in
signal-slot systems, we are mainly concerned about slots. A slot in WT
Toolkit can be connected from multiple signals at the same time, and as with
any decent observer design pattern implementation, the programmer should
not need to know how many signals are actually connected to his slot at any
moment. Therefore, requiring the programmer to disconnect each and every
signal-slot connection of an object before it is left to disuse is not always
possible.
What if we make the slots automatically aware of the signals that are
connected to it, by, say, adding an array to the slot that remembers what
signals are currently connected to it, and modifying that array in the
wtObject.connect() and wtObject.disconnect() operations? This approach
could be correct in other programming languages. Unfortunately, this
approach is not correct in JavaScript. As shown in Section 2.2 in this report,
a closure in JavaScript, under Internet Explorer at least, holds references to
all locally defined variables in its outer scope even if those variables are
Authors: Kou Man Tong, Fung Chak Fai 29
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
never referenced in its inner scope. How is this relevant to lapsed listeners?
Look at the following pseudo-code snippet:
function foo()
{
global variable a = new wtObject();
global variable b = new wtObject();
local variable c = a;
bar1 = function(){}
bar2 = function(){}
assign bar1 as a slot in a;
assign bar2 as a slot in b;
create the signal "Sig" in a;
create the signal "Sig" in b;
connect b.Sig to a.bar1;
connect a.Sig to b.bar2;
}
foo();
disconnect all signals-slot connections connecting to and from of a;
delete a;
Figure 19 – Pseudo-code snippet showing a possible solution for the lapsed listener problem in a
signal-slot event handling system.
Assuming the above pseudo-code snippet was realized in JavaScript. In the
end of the code snippet, although we have already disconnected any signal-
slot connections connecting to and from the object “a” before leaving it to
disuse, the object “b” is still holding at least one reference to the object
reference that used to be “a”. Why? This is because one of the slot functions
of object “b” have retained implicit references back to foo()’s local variable
“c”, which holds the same object reference of the previous “a”.
Therefore, in implementing the signal-slot event handling system in WT
Toolkit, we cannot rely on the programmer being careful enough to
disconnect all incoming or outgoing signal-slot connections before leaving
an object to disuse. Because even if he is so careful, there can be still lapsed
listeners. A more aggressive solution is needed.
Authors: Kou Man Tong, Fung Chak Fai 30
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
Lapsed Listeners in Other Web UI Toolkits
It has to be noted that the lapsed listener problem is not unique to the signal-
slot event system of early WT Toolkit versions. The problem is present in
the current versions of many popular web UI toolkits. In our project, it was
found that Dojo Toolkit [6] and Yahoo! UI Library
(http://developer.yahoo.com/yui/) are both vulnerable to lapsed listeners as
of April 2007. In this aspect, it can be said that WT Toolkit’s event system is
more advanced when compared to Dojo Toolkit and Yahoo! UI Library.
More details about the vulnerabilities in Dojo Toolkit and Yahoo! UI Library
are reported in Section 2.5 of this report.
2.4 Performance ImplicationsDespite unwanted memory consumption, memory leaks in JavaScript can
have negative consequences on the latencies of operations. Some of the
consequences are expected – for example, when we have lapsed listeners in
an event system which is based on the observer design pattern, the time to
process events would rise naturally. This is because when an event is fired,
the lapsed listeners are still being notified of the event, despite the fact that
they are useless. What is interesting is that we have found some unexpected
consequences of JavaScript memory leaks in this project, specifically,
memory leaks under Internet Explorer 6.
Object Creation Latency and Internet Explorer 6 Memory Leaks
In the article “IE: Where’s my memory?” by Mihai Bazon [2], Bazon
asserted and demonstrated that the general performance of a web application
with memory leak would deteriorate incrementally under Internet Explorer
6. During the development of WT Toolkit, however, it was discovered that
Bazon’s statement is not generally true. During our investigation, it was
discovered that not all JavaScript operations were affected by memory leak
performance-wise.
For this part of our investigation, four HTML files were created, with their
embedded Javascript derived from four of the leaky Javascript programs
found in Section 2.2. Therefore, all four code samples used in this section
shows cross-page memory leaks in Internet Explorer 6.
Since we are testing for performance in this section, it is important to
mention the machines on which the experiments were performed. We
Authors: Kou Man Tong, Fung Chak Fai 31
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
executed each of the four code samples on the two machines below:
Machine 1 – A high end desktop
Intel Core 2 Duo E6400 overclocked to 3GHz
1GB memory
Windows XP SP2 with IE6
Machine 2 – Any random machine in FYP lab
Intel Pentium 4 3.2GHz
1GB memory
Windows XP SP2 with IE6
The four code samples:
# Reference Description
1 Figure ?? Each time the link “Create 2000 DIV nodes” is clicked, 2000
self-referencing DOM nodes are created.
2 Figure ?? Each time the link “Create 2000 DIV nodes” is clicked, 1000
pairs of circular referencing DIV nodes are created.
3 Figure ?? Each time the link “Create 2000 DIV nodes” is clicked, 2000
DIV nodes each with a reference to itself via a Javascript
object is created.
4 Figure ?? Each time the “Create 2000 DIV nodes” link is clicked, 2000
DIV nodes each with a reference to itself via a closure is
created.
The operation flow of the four code samples is very similar to Bazon’s code
samples for demonstrating memory leak and deteriorating performance in
Internet Explorer 6:
1. A single link, “Create 2000 DIV nodes” appears when the HTML file is
loaded.
2. Each time the link is clicked
i. The system time is recorded as the start time.
ii. 2000 DIV nodes are added to the page by Javascript
iii. The system time is recorded as the end time.
iv. The operation latency is calculated by (end time – start time) and
displayed.
Authors: Kou Man Tong, Fung Chak Fai 32
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
The initial experiment procedure is as follows:
1. Load one of the HTML files to Internet Explorer 6;
2. Click on the “Create 2000 DIV nodes” link;
3. Record the committed address space size of iexplorer.exe by reading the
VM size column from Task Manager;
4. Record the operation latency displayed by the code sample;
5. Refresh the page;
6. Repeat steps 2 to 5 by 10 times;
7. Repeat steps 1 to 6 until all (machine, code sample) combinations have
been tested.
No control experiment was designed initially because it was not known
which JavaScript operation would be affected by the memory leak. Control
experiments were performed after hypotheses were drawn on the affected
operations.
Authors: Kou Man Tong, Fung Chak Fai 33
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
Here are the initial experiment results:
Allocated Memory (High End Desktop)
1159613620 14960
1669218100
2021621944 23252
2498026468
1166813640
1518016916 18240
2011221836
2350825192
26724
1258815716
1816420768
2345225996
2855631336
3396436392
13520
17444
21468
24976
28896
32724
36352
39984
43820
47388
0
5000
10000
15000
20000
25000
30000
35000
40000
45000
50000
1 2 3 4 5 6 7 8 9 10
Trial
KB
ytes
Code Sample #1 Code Sample #2 Code Sample #3 Code Sample #4
Figure 20- Allocated Memory vs. Trial on High End Desktop.
Operation Latency (High End Desktop)
296 297 297 282 297 297 297 297 297 297282 281 297 297 281 297 297 297 297 297312 375 485 562 625 687 788 844 891 953
407
766
1140
1547
1891
2296
2657
3031
3422
3797
0
500
1000
1500
2000
2500
3000
3500
4000
1 2 3 4 5 6 7 8 9 10
Trial
Tim
e (m
s)
Code Sample #1 Code Sample #2 Code Sample #3 Code Sample #4
Figure 21 - Operation Latency vs. Trial on High End Desktop.
Authors: Kou Man Tong, Fung Chak Fai 34
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
Allocated Memory (FYP Lab PC)
1038412364 13924
1562017392
18952 2052822220 23784 25316
1038012352
14112 1560417268
1902820672 22180
2407225704
1134014316
1689619804
2232024808
2746430076
3293635404
12488
1660420388
24024
2785231568
3538839048
4274846404
0
5000
10000
15000
20000
25000
30000
35000
40000
45000
50000
1 2 3 4 5 6 7 8 9 10
Trial
KB
ytes
Code sample #1 Code Sample #2 Code Sample #3 Code Sample #4
Figure 22 - Allocated Memory vs. Trial on FYP Lab PC.
Operation Latency (FYP Lab PC)
625 609 593 609 609 609 625 594 610 610609 625 610 625 640 625 609 625 625 625734 875 1016 1140 1313 1437 1578 1687 1844 2031
10001640
22972922
35474156
47815406
60316704
0
1000
2000
3000
4000
5000
6000
7000
8000
1 2 3 4 5 6 7 8 9 10
Trial
Tim
e (m
s)
Code sample #1 Code Sample #2 Code Sample #3 Code Sample #4
Figure 23 - Operation Latency vs. Trial on FYP Lab PC.
Although the memory consumption graphs showed that all four test cases
leaks memory incrementally between trials, the operation latency of code
samples 1 and 2 stayed constant. This observation comes in contrary to what
Bazon claimed.
Comparing the source code of code samples 1, 2 with code samples 3, 4, one
of the significant differences between them is that JavaScript objects were
created and subsequently leaked away in circular references with DOM
nodes in code samples 3, 4, while no JavaScript objects were created with
each DOM node in code samples 1, 2. This might seem ridiculous in the first
glance, isn’t a DOM node also a JavaScript object? They are not.
Authors: Kou Man Tong, Fung Chak Fai 35
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
So we draw the following hypothesis:
Hypothesis 3 – JavaScript object creation latency deteriorates with the
number of JavaScript objects leaked in circular references involving DOM
nodes, under Internet Explorer 6.
To verify our hypothesis, we can attach JavaScript objects to the leaked
DOM nodes in code samples 1 and 2, and see if the operation latencies
would increase or not.
So we performed control experiments with code samples 1 and 2 with a
modified placeNode() below:
function placeNode(node)
{
// place the node randomly on the page
var x = parseInt(Math.random() * 800) + "px";
var y = parseInt(50 + Math.random() * 750) + "px";
node.someAttr = new Object;
node.style.position = "absolute";
node.style.top = y;
node.style.left = x;
node.style.width = "1px";
node.style.height = "1px";
node.style.fontSize = "1px";
node.style.backgroundColor = "black";
}
Figure 24 - Modified placeNode() function that is predicted to show deteriorating performance
with memory leak. Modified code is underlined.
The control experiments were performed on the high-end desktop machine.
The results are as follows:
Authors: Kou Man Tong, Fung Chak Fai 36
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
Operation Latency (High End Desktop)
297 312 313 328 343 359391 406
437469
297 313 313343 344 359
390 406 422453
050
100150200250300350400450500
1 2 3 4 5 6 7 8 9 10
Trial
Tim
e (m
s)
Modif ied code sample #1 Modif ied code sample #2
Figure 25 – Operation Latency vs. Trial on High End Desktop, control experiment.
A slight, but obvious increasing trend in operation latency was observed in
the control experiment. This confirms our hypothesis that the creation and
subsequent leak of JavaScript objects is the real reason for the deteriorating
performance.
General JavaScript Object Creation Latencies in Internet
Explorer 6
The experiments above showed that when JavaScript objects are being
leaked away with DOM circular references, subsequent JavaScript object
creation latencies would increase. However, during the development of our
project, it was noticed that deteriorating object creation latencies can still
happen under Internet Explorer 6 even if there are no memory leaks.
We found that in Internet Explorer 6, JavaScript object creation latencies
would increase with the number of reachable JavaScript objects in the web
browser, in addition to the number of JavaScript objects leaked away with
DOM circular references. The same kind of performance deterioration is not
present in Firefox or Internet Explorer 7.
Hypothesis 4 – JavaScript object creation latency deteriorates with the
number of JavaScript objects that are still in memory, under Internet
Explorer 6.
Authors: Kou Man Tong, Fung Chak Fai 37
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
Two experiments were designed to justify our hypothesis:
# Reference Description
1 Figure ?? Demonstration code for showing JavaScript object creation
latency deteriorating with number of reachable JavaScript
Objects, under Internet Explorer 6 and 7.
2 Figure ?? Control experiment. Here, although the memory usage of
iexplorer.exe is also increasing progressively, JavaScript
object creation latency does not deteriorate because the
number of reachable JavaScript objects is not increasing.
The experiments above were designed to be fully automated. A “Run
Benchmark” link appears in the browser window once the above code
samples are loaded.
Authors: Kou Man Tong, Fung Chak Fai 38
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
Figure 26 – Screenshot of code sample 1 running.
The program flow of code sample 1 after clicking “Run Benchmark” is as
follows:
1. Go to step 3.
2. Create 20000 JavaScript objects and store them to a global array.
3. Record the current system time as startTime.
4. Create 10000 JavaScript objects and throw them away.
5. Record the current system time as endTime.
6. Print out the time taken to create the 10000 JavaScript objects by
calculating endTime – startTime.
7. Repeat steps 2 to 6 until there are 400000 or more objects in the global
array.
Figure 27 - Screenshot of code sample 2 running.
Authors: Kou Man Tong, Fung Chak Fai 39
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
Code sample 2 is a control experiment which creates DOM nodes instead of
JavaScript objects for filling up the browser’s memory space:
1. Go to step 3.
2. Create 2000 DOM nodes and add them under document.body.
3. Record the current system time as startTime.
4. Create 10000 JavaScript objects and throw them away.
5. Record the current system time as endTime.
6. Print out the time taken to create the 10000 JavaScript objects by
calculating endTime – startTime.
7. Repeat steps 2 to 6 until there are 40000 or more DOM nodes under
document.body.
Notice that the number of “filler” DOM nodes in code sample 2 is only one
tenth the number of “filler” JavaScript objects in code sample 1. This is
because DOM nodes take considerably more memory than empty JavaScript
objects.
Object Creation Latency vs. Number of Reachable JavaScript Objects
16 62 125 187282 329 391 438 515 579 641 719 766 844 890
985 1031 1094 1156 1219 1266
47141
234344
438547
640735
860953
10461172
12651360
14691609 1672
1781 18591953
2093
0
500
1000
1500
2000
2500
020
000
4000
060
000
8000
010
0000
1200
0014
0000
1600
0018
0000
2000
0022
0000
2400
0026
0000
2800
0030
0000
3200
0034
0000
3600
0038
0000
4000
00
No. of Reachable JavaScript Objects
Tim
e(m
s)
Code sample 1@High End Desktop Code sample 2@FYP Lab PC
Figure 28 – Experiment with code sample 1 shows increasing object creation latency with the
number of reachable JavaScript objects.
Authors: Kou Man Tong, Fung Chak Fai 40
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
Object Creation Latency vs. Number of DOM Nodes
15 16 15 15 16 15 15 15 15 16 16 15 15 15 16 16 16 16 15 16 16
47
31 31 31 31 31
47
32 32 31 32 31 31 32 31 31 31 31 31 32 31
05
101520253035404550
020
0040
0060
0080
00
1000
0
1200
0
1400
0
1600
0
1800
0
2000
0
2200
0
2400
0
2600
0
2800
0
3000
0
3200
0
3400
0
3600
0
3800
0
4000
0
No. of DOM Nodes
Tim
e (m
s)
Code sample 2@High End Desktop Code sample 2@FYP Lab PC
Figure 29 - Control experiment (code sample 2) with DOM nodes instead of JavaScript objects
that are stored in the memory.
The experimental results above showed that, under Internet Explorer 6,
JavaScript object creation latencies increases with the number of reachable
JavaScript objects, but not other factors that would also increase memory
usage (like number of DOM nodes in the current web page).
2.5 The Search for SolutionsWe tried to find solutions to the memory leak problems we discovered by
first looking at how other popular client side Web UI toolkits approached
them, and failing that, we looked for solutions in other programming
languages.
Case Study 1: Dojo Toolkit
We studied Dojo Toolkit first, because from reading one of the Alex
Russell’s (Project founder of Dojo Toolkit) blog entries [23], it seems that he
was very well aware of the memory leak problems and he had already
implemented a reasonable solution.
Actually writing test cases for Dojo Toolkit, unfortunately, turned up with
the same symptoms of lapsed listeners as WT Toolkit does. Two code
samples were written for experimentation:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
Authors: Kou Man Tong, Fung Chak Fai 41
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
<title>Dojo Button Widget Test</title>
<script type="text/javascript" src="dojo.js"></script>
<script type="text/javascript">
dojo.require("dojo.widget.Button");
</script>
</head>
<body>
<script type="text/javascript">
var widgetArray = [];
function createOne()
{
var widget = dojo.widget.createWidget("Button", {"caption":"Hi!"});
var node = widget.domNode;
node.style.position = "absolute";
node.style.top = parseInt(Math.random() * 800) + "px";
node.style.left = parseInt(Math.random() * 800) + "px";
dojo.body().appendChild(node);
widgetArray.push(widget);
}
function destroyAll()
{
while(widgetArray.length > 0)
{
var widget = widgetArray[0];
var node = widget.domNode;
widgetArray.splice(0,1);
node.parentNode.removeChild(node);
dojo.event.browser.clean(node);
}
}
function createMany()
{
for(var i=0;i<100;i++)
Authors: Kou Man Tong, Fung Chak Fai 42
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
createOne();
}
</script>
<a href="javascript: createMany();">Create 100 buttons</a>
<br>
<a href="javascript: destroyAll();">Destory all</a>
</body>
</html>
Figure 30 – First code sample for testing Dojo Toolkit. Here, 100 buttons are created each time
the “Create 100 buttons” link is clicked, and all of the are destroyed when the “Destroy All” link
is clicked.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Dojo Button Widget Test</title>
<script type="text/javascript" src="dojo.js"></script>
<script type="text/javascript">
dojo.require("dojo.widget.Button");
</script>
</head>
<body>
<script type="text/javascript">
var widgetArray = [];
function createOne()
{
var node = document.createElement("div");
node.innerHTML = "Hello World!";
node.style.position = "absolute";
node.style.top = parseInt((100 + Math.random() * 800)) + "px";
node.style.left = parseInt(Math.random() * 800) + "px";
document.body.appendChild(node);
Authors: Kou Man Tong, Fung Chak Fai 43
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
widgetArray.push(node);
var clickHandler = function()
{
alert("Hello there!");
}
dojo.event.connect(node, "onclick", clickHandler);
}
function destroyAll()
{
while(widgetArray.length > 0)
{
var node = widgetArray[0];
widgetArray.splice(0,1);
node.parentNode.removeChild(node);
dojo.event.browser.clean(node);
}
}
function createMany()
{
for(var i=0;i<200;i++)
createOne();
}
</script>
<a href="javascript: createMany();">Create 200 messages</a>
<br>
<a href="javascript: destroyAll();">Destory all</a>
</body>
</html>
Figure 31 - Second code sample for testing Dojo Toolkit. Here, 200 DIV nodes are created each
time the “Create 200 messages” link is clicked, and all of them are destroyed when the “Destroy
all” link is clicked.
Authors: Kou Man Tong, Fung Chak Fai 44
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
Here is the experimental procedure for the above two code samples:
1. Place the two HTML files at the root of the source tree of Dojo 0.4.1;
2. Load the two HTML files to any browser;
3. Click the “Create …” link;
4. Click the “Destroy all” link;
5. Repeat 3 and 4 for an arbitrary number of times, and observe the VM
Size of the browser process in Windows Task Manager.
The first code sample tests Dojo Toolkit’s widget system by creating Dojo-
specific widgets, while the second code sample test Dojo Toolkit’s event
handling system by using the signal-slot event handling mechanism in Dojo
Toolkit. Assuming Dojo Toolkit’s widget system and event handling system
are immune to the lapsed listener problem, the observed VM Size in step 5
of the experiment procedure should not show a trend of unbounded growth.
Unfortunately, unbounded committed address space growth was exactly
what happened when we conducted the experiment.
A video was captured and posted to YouTube for demonstrating the
unbounded growth of committed address space of the test cases under
Internet Explorer 6. It can also be observed from the video that the latency
of creating the 200 DIV nodes in the video was deteriorating as steps 3 and 4
of the experiment procedure were repeated:
http://www.youtube.com/watch?v=DVcUidSRoLQ
The issue was raised in the Dojo Toolkit Interest mailing list in January 2007
(http://dojotoolkit.org/pipermail/dojo-interest/2007-January/thread.html). A
lot of people responded to our question, including Alex Russell. We got third
party confirmations of the problem from the mailing list, and Alex’s answer
was that the development team of Dojo Toolkit was already preoccupied
with the Internet Explorer 6 specific memory leaks discussed in Section 2.2
of this report, and memory leaks resulted from the lapsed listener problem
was not their priority.
The original question, and a few interesting replies to our question on the
Dojo Interest mailing list are included in Appendix D.
Case Study 2: Yahoo! UI Toolkit
Yahoo! UI Toolkit included two functions purgeElement() and
Authors: Kou Man Tong, Fung Chak Fai 45
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
removeListener() under the package YAHOO.util.Event for unlinking event
subscribers from an event publisher. We wrote a simple test case to see if
purgeElement() worked:
<html>
<head>
<script type="text/javascript" src="build/yahoo/yahoo-min.js"></script>
<script type="text/javascript" src="build/event/event-min.js"></script>
<script type="text/javascript" src="build/dom/dom-min.js"></script>
<script type="text/javascript" src="build/dragdrop/dragdrop-min.js"></script>
<script type="text/javascript" src="build/logger/logger-min.js"></script>
<script language="javascript">
var widgetArray = [];
function createOne()
{
var node = document.createElement("div");
node.style.position = "absolute";
node.style.left = parseInt(Math.random() * 800) + "px";
node.style.top = parseInt(Math.random() * 800) + "px";
node.innerHTML = "Hello World!";
document.body.appendChild(node);
widgetArray.push(node);
var handler = function()
{
alert("Hi there!");
}
YAHOO.util.Event.addListener(node, "click", handler);
}
function createMany()
{
for(var i=0;i<200;i++)
createOne();
}
Authors: Kou Man Tong, Fung Chak Fai 46
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
function destroyAll()
{
while(widgetArray.length > 0)
{
document.body.removeChild(widgetArray[0]);
// Yahoo UI library's implementation would leak memory if we don't purge
// every node's event handlers when we detach the nodes from the document
// tree
YAHOO.util.Event.purgeElement(widgetArray[0]);
widgetArray.splice(0,1);
}
}
function init()
{
// nothing to init here now... maybe add the logger window
}
</script>
</head>
<body onload="init()">
<a href="javascript: createMany()">Create 200 messages</a>
<br>
<a href="javascript: destroyAll()">Destroy all</a>
</body>
</html>
Figure 32 – Test case for finding if Yahoo! UI Toolkit would leak memory via lapsed listeners
when YAHOO.util.Event.purgeElement() is used for cleanup.
The experiment procedure for the above code sample is the same as the
experiment with Dojo Toolkit, except that we have to place the HTML file to
the root of Yahoo! UI Toolkit’s source tree this time.
No trend of unbounded allocated memory size growth was observed from
the above code sample. Removing the YAHOO.util.Event.purgeElement(…)
line from the function destroyAll() above would make the code sample leak
Authors: Kou Man Tong, Fung Chak Fai 47
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
memory again. So for this case, Yahoo! UI Toolkit does not suffer from
memory leak due to lapsed listeners as long as the web developer is careful
enough to unlink all short-lived objects from longer-lived event publisher
objects when the short-lived objects are no longer needed.
However, it was already shown in Section 2.3 of this report that, even
including a function that would automatically disconnect all incoming and
outgoing observer connections, and assuming the web developer is careful
enough to remember calling that automatic function before leaving objects
into disuse, lapsed listeners can still appear in JavaScript. Therefore, while
Yahoo! UI Toolkit’s solution might work correctly in most circumstances,
there are still some cases where it would not work. Therefore, the solution
implemented in Yahoo! UI Toolkit is not a complete solution.
Case Study 3: Java
We found a chapter in the book Bitter Java [25] which is dedicated to
discussing lapsed listeners and solutions to mitigate the effects of lapsed
listeners. It mentioned three general solutions to the lapsed listener problem.
“Solution 1: Explicitly remove the listeners”
“Solution 2: Shorten the life cycle of the anchor”
“Solution 3: Weaken the reference”Figure 33 – The three general solutions to the lapsed listener problem suggested by the book
Bitter Java.
Yahoo! UI Toolkit’s solution matches the first solution, but there are cases
where it wouldn’t work in JavaScript.
The Bitter Java textbook does not recommend the second solution, although
it is listed.
This leaves us with the third solution – trying to emulate weak references in
JavaScript to mitigate memory leaks caused by lapsed listeners. Weak
references can also be used to replace strong references in circular
references among DOM nodes, so that we can still have circular references
without confusing the garbage collector in Internet Explorer 6. It has to be
Authors: Kou Man Tong, Fung Chak Fai 48
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
noted that, while weak references can reduce memory leaks by a large
margin, it cannot truly eliminate memory leaks if the developer is extremely
careless. The reason is even the weak references themselves take up some
memory. Therefore, the third solution has to be implemented along with the
first solution for WT Toolkit.
Authors: Kou Man Tong, Fung Chak Fai 49
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
2.6 Object Model of WT Toolkit
Figure 34 - Diagram of the wtObject class, which is the base class of all nontrivial classes in WT
Toolkit.
From version 0.2.0 and on, WT Toolkit has an object model where all
nontrivial classes inherit from the base class wtObject. There are three main
goals in designing the wtObject base class:
1. It does not leak memory, or leaks far less than what would be possible if
raw DOM node references were used instead, as long as the programmer
follows a few very trivial rules.
2. It does provide a unified interface for signal-slot event handling.
3. It allows object oriented development based on JavaScript prototype
inheritance, even for visible widget classes.
The wtObject class includes a set of operations that is common to all its
subclasses. These operations serve four main purposes: event handling,
managing weak references, accessing attributes stored in the expensive
object, and garbage collection.
Name Function
Authors: Kou Man Tong, Fung Chak Fai 50
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
connect() Connects a signal of this wtObject to a slot of another wtObject.
disconnect() Disconnects a signal of this wtObject from a slot of another
wtObject.
emit() Emits a signal, notifies all the slots connected to this signal.
setSignal() Creates a signal for this object.
setSlot() Creates a slot for this object.
removeSignal() Removes a signal in this object.
removeSlot() Removes a slot in this object.
startProxy() Establishes a weak reference to a foreign object.
endProxy() Detaches the foreign object weak reference.
set() Sets an attribute in the foreign object.
get() Tells an attribute value in the foreign object.
addDependency() Establishes a dependency relation of this wtObject to another
wtObject. If the wtObject being depended on no longer refers to a
foreign object, then the foreign object corresponding to this
wtObject will be deleted.
Figure 35 – The default operations of a wtObject.
A wtObject is a low memory footprint proxy to a more expensive object,
which may be prone to causing memory leaks in circular references or as
lapsed listeners. For example, it might be a proxy to a DOM node. In the
absence of an expensive object, a wtObject can also proxy itself.
The proxy relation between a wtObject and its corresponding expensive
object (e.g. a DOM node) is maintained by a weak reference in the wtObject.
The weak reference is actually just a random string, which can be used find
back the expensive object from a global lookup table. This string reference is
weak because unlike strong references which we use usually, the string
reference does not protect the object that is being referred to from garbage
collection (It does protect that object from being deleted by WT Toolkit’s
garbage collector, however. This will be explained in Section 2.7). It allows
you to “refer to an object without keeping it from being collected”
(http://java.sun.com/developer/technicalArticles/ALT/RefObj/). When used
properly, having weak references can completely avoid memory leaking
circular references, which we encountered in Section 2.2. It can also be used
avoid or mitigate memory leaks caused by lapsed listeners.
Authors: Kou Man Tong, Fung Chak Fai 51
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
In our object model, all normal attributes, event listeners (which are stored
in the wtSignals collection) and event handling functions (which are stored
in the wtSlots collection) are stored in the expensive object, instead of inside
the wtObject directly. This arrangement is used to break strong circular
references which would confuse the garbage collector in Internet Explorer 6.
It also makes the memory footprint of a wtObject very small, such that even
if it becomes temporarily a lapsed listener in some other wtObject’s wtSignal
collection, it would not occupy a lot of memory before it is cleaned up. The
only attribute that is held by the wtObject itself (and not the expensive
object) is the weak reference string, wtObjId.
Hypothesis 5 – Strong circular references with wtObjects are impossible in
WT Toolkit as long as the programmer use wtObject instance references as
attribute values, and never use the expensive object references as attribute
values.
Figure 36 - Two visible widgets, each weakly referencing a DOM Node, with an attribute pointing
to each other. Compare this graph with Figure 13 in Section 2.2, there are no strong circular
references in this graph, so this does not confuse Internet Explorer 6's garbage collector.
Hypothesis 5 is trivial to prove, and it can be visualized in a diagram like
Figure 36. As long as the programmer always uses wtObject proxies in place
of their corresponding expensive object as attribute values, there will be
always at least one weak reference in any reference cycle. Thus, strong
circular references can not be formed.
An exception to hypothesis 5 is when a wtObject is acting as a proxy to
itself. In this case, strong circular references are possible. However, as
demonstrated in Section 2.2 in this report, strong circular references
involving only JavaScript objects do not leak memory under Internet
Authors: Kou Man Tong, Fung Chak Fai 52
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
Explorer 6, so this special case is not important when memory leak is
concerned.
In the discussion that follows, it is assumed that the programmer does not
keep any long lived strong references to the expensive objects. Examples of
such strong references include assigning the expensive objects’ references as
attributes to other objects, keeping a JavaScript identifier with an expensive
object’s strong reference as it value outside a long lived closure, etc. It is
essential that a programmer using WT Toolkit avoid the above practices.
As an event listener, a wtObject is said to be lapsed if it no longer maintains
a weak reference to an expensive object. Having the weak reference gives us
one simple criterion for determining whether a wtObject is lapsed or not,
because in the absence of a weak reference, the wtObject’s event handling
slots would no longer be accessible, and the wtObject would obviously be
useless as an event listener. The signal-slot event handling logic built into
WT Toolkit can thus automatically remove lapsed event listeners based on
this simple decision criterion.
Figure 37 – The existence of the weak reference link gives us a simple criterion for deciding
whether an event listener has lapsed. In comparison, if we use the expensive object directly as
an event listener (this may be a DOM node, an XMLHttpRequest object, an ActiveX object, an
SVG matrix, among many other things), there is no simple way of determining whether that
object is still useful or not.
Therefore, when a programmer has determined that a wtObject is no longer
useful, all he has to do is to simply disconnect the wtObject’s weak
reference. Having the wtSignals collections disconnected from the wtObject,
the wtObject can no longer emit signals; having the wtSlots collections
Authors: Kou Man Tong, Fung Chak Fai 53
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
disconnected from the wtObject, the wtObject can no longer handle events;
when some event is fired and the wtObject is still in that event’s listener
collection, the event handling logic of WT Toolkit will automatically
determine that the wtObject has lapsed, and remove it from the listener
collection. In the next time the same event is fired, the lapsed wtObject will
no longer be in the listener collection, and so the event processing latency is
not increased. Due to the fact that wtObject’s memory footprint is very
small, during the temporary state where the lapsed wtObject is still inside
some other object’s wtSignals collection, memory consumption is not
increased significantly compared to without the wtObject. Therefore the
wtObject does not pose a threat as a lapsed listener.
Moreover, we have also considered the case in which when one wtObject
has lapsed, it would lead to many other wtObjects to become lapsed. For
example, consider a wtObject which functions as a popup menu (we have
implemented this as the wtPopupMenu class in WT Toolkit). The popup
menu depends on another visible widget, for example, a button. This is
because if the visible widget from which the popup menu pops up no longer
exists, the popup menu would no longer be useful. Therefore, we allow each
wtObject to be dependent on at most one other wtObject. If a wtObject’s
depended object has lapsed, the wtObject itself would become lapsed as
well, and it will no longer maintain its weak reference to the expensive
object. This functionality is implemented as the addDependency() operation
of the wtObject class.
2.7 Weak Reference EmulationSince JavaScript provides no built-in weak references, we had to emulate
weak reference with strings and a global lookup table. Every active wtObject
holds a string key to the global lookup table, where the string key can be
used to lookup the corresponding expensive object.
Simply moving all the expensive objects into a lookup table, however, is just
like to moving all the lapsed listeners to a lookup table, and that does not
solve the problem. This is because the lookup table retains strong references
to the expensive objects. Without some algorithm to remove the expensive
objects and their corresponding weak references at the appropriate time, the
expensive objects will live beyond the time when they still useful, and
Authors: Kou Man Tong, Fung Chak Fai 54
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
become lapsed.
The only way available to the web developer in WT Toolkit for adding
entries to the global lookup table is by calling the startProxy() method of a
wtObject instance; and the only way a web developer can explicitly remove
an entry in the lookup table is by calling the endProxy() method of a
wtObject instance. This works a lot like new and delete in C++. Compared to
the delete keyword in JavaScript, calling endProxy()actually removes the
expensive object because the global lookup table retains the only strong
reference to it. An exception to this rule is DOM nodes which are typically
tied to visible widget classes in WT Toolkit. A DOM node can have another
strong reference linking to it from the document tree. We have taken care of
this exception in WT Toolkit by including a removeSelf() operation to the
wtWidget and wtVectorWidget visible widget base classes, which removes
the DOM node from the document tree as well.
While our implementation is already correct in the sense that the
programmer has a way to reliably remove expensive objects in WT Toolkit –
unlike in Dojo Toolkit and Yahoo! UI Toolkit where the expensive objects
can become lapsed indefinitely – having the programmer call endProxy() or
removeSelf() every time he needs to remove an object is still inconvenient
and awkward in JavaScript. After all, JavaScript is a garbage collected
programming language.
When the programmer forgets to call endProxy() or the equivalent method
before leaving a wtObject instance to disuse, the wtObject instance becomes
lapsed. The essence of a lapsed listener problem is not about whether the
object is reachable or not, but rather about whether the object is still useful
or not. WT Toolkit is a GUI toolkit, so much of classes implemented in WT
Toolkit are visible widgets. Visible widgets on a browser window are always
DOM nodes, a DOM node not connected to the main document tree is never
visible and cannot fire events (e.g. there is no way you can click on
something not displayed). So in a sense, a DOM node not attached to the
main document tree is not useful.
Hypothesis 6 – A visible widget whose corresponding DOM node stays
disconnected from the document tree forever, is useless, and thus it is lapsed.
Authors: Kou Man Tong, Fung Chak Fai 55
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
Hypothesis 6 is obvious, if I can’t and I won’t see the visible widget ever,
then it is not useful.
When we are making application demonstrations for WT Toolkit, we found
that visible widgets are often rendered not useful when an ancestor DOM
node becomes detached from the document tree, usually from the result of a
removeSelf() call of its corresponding visible widget. While it is possible to
run a depth first search or breadth first search across all DOM node children
and call removeSelf() in a recursive manner to remove the lapsed DOM
nodes from WT Toolkit’s global lookup table, this approach assumes that the
whole web application UI is written with WT Toolkit, and is not correct in
all cases. What if the web developer only uses WT Toolkit in a part of his
web application UI, say, in a DIV cell placed at the upper right corner of the
browser window, and uses the DOM removeChild() call to remove that DIV
node and leaves it to disuse? In this case, all the WT Toolkit widget
instances were rendered useless, but the DFS or BFS in the widget class’s
removeSelf() operation would be useless in cleaning up lapsed objects
because it was never called.
The DOM nodes we just discussed – who became detached from the
document tree because some of its ancestors were disconnected from the
document tree – match the description in Hypothesis 6, and are useless.
They should be automatically cleaned up as well, because it is unreasonable
for the programmer clean up all the DOM child nodes manually every time
he removes a visible widget or removes a DOM node from the document
tree. Therefore, an automatic algorithm is implemented in WT Toolkit to
clean up the lapsed DOM nodes from the global lookup table. Otherwise,
WT Toolkit would leak memory. For convenience, we shall refer to the
automatic clean up algorithm as WT Toolkit’s garbage collector. Notice that
this is not the same garbage collector as the JavaScript collector built into
the web browser, our garbage collector serves only to clean up lapsed
objects from the global lookup table.
WT Toolkit’s garbage collector is a stop the world, mark and sweep garbage
collector. It is meant to be called automatically by WT Toolkit – the web
developer does not need to call it manually. It cleans up weak references and
the corresponding expensive objects from the global lookup table if the
expensive objects are determined to be lapsed. The garbage collector works
Authors: Kou Man Tong, Fung Chak Fai 56
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
as follows:
Mark phase:
1. Create a disjoint set forest, and create a disjoint set with
document.documentElement as its only element. Call this the root set.
2. For each object in the global lookup table:
2.1. If the object does not depend on another wtObject, and it is a DOM
node, look for the closest ancestor in the DOM tree which either
has a wtObject proxy or is document.documentElement:
2.1.1.If such an ancestor is found, union the current object with that.
2.1.2.If such an ancestor is not found, no nothing.
2.2. Else, if the object depends on another wtObject:
2.2.1.Union the current object with the object that it depends on.
2.3. Else:
2.3.1.Union the current object with the root set.
Sweep phase:
1. For each object in the global lookup table:
1.1. Find the set that the object belongs to. If the object does not belong
to the root set, remove it from global lookup table and remove the
weak reference in its wtObject proxy.
Figure 38 - Algorithm of WT Toolkit's garbage collector.
The disjoint set data structure used in the above algorithm is based on the
one described in Chapter 21 of the Introduction to Algorithms textbook [3],
with union-by-rank and path compression optimizations. As a result, each
union and find operation has an asymptotic running time that grows much
slower than O(log n) and each create element or create set operation takes
O(1) on most web browsers, with the exception of Internet Explorer 6 in
which object creation latency was observed to be non-constant.
Hypothesis 7 – After the garbage collector is executed, all DOM nodes that
are not attached to the document tree, has no dependency, and whose
ancestors have no dependencies, are removed from the global lookup table.
Hypothesis 7 is true because at the end of the mark phase, there is no way
for the said DOM nodes to be in the root set. Any elements not belonging to
Authors: Kou Man Tong, Fung Chak Fai 57
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
the root set are removed at the sweep phase. This property is useful for
removing widgets whose corresponding DOM nodes that are no longer in
the DOM tree.
Hypothesis 8 – After the garbage collector is executed, all DOM nodes that
have no dependency, and have a DOM tree ancestor with a dependency that
does not belong to the root set, are removed.
Hypothesis 8 is true because at the end of the mark phase, said DOM nodes
would belong to the same set as the depended object of the closest DOM tree
ancestor with a dependency. If that set is not the root set, all elements in that
set, including the DOM nodes, would be removed at the sweep phase. This
property is useful for removing widgets whose usefulness does not depend
on whether they are in the DOM tree or not, but whether another object is
lapsed or not. An example for this is a popup menu.
Hypothesis 9 – After the garbage collector is executed, all DOM nodes that
have a dependency not belonging to the root set are removed.
The proof and application for hypothesis 9 is the same as hypothesis 8.
Hypothesis 10 – All DOM nodes whose depended object, or the depended
object of its closest ancestor (whichever that is closer) belongs to the root
set, are not removed.
This is true because said DOM nodes would belong to the root set as well at
the end of the mark phase. The sweep phase does not remove objects in the
root set.
Hypothesis 11 – All DOM nodes that are inside the document tree, that have
no depended object, and whose ancestors have no dependencies, are not
removed.
This is true because at the end of the mark phase, they will belong to the
same set as the DOM node document.documentElement, which is the root
node of the document tree, and belongs to our root set. Hypotheses 10 and
11 make sure those DOM nodes that are still displayed or are marked as
“useful” by the programmer via addDependency() are not removed.
Authors: Kou Man Tong, Fung Chak Fai 58
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
Hypothesis 12 – All objects that are not DOM nodes, and has no
dependency, are not removed.
This is true because they are merged with the root set at step 2.3.1 of the
mark phase. There is no way for WT Toolkit to automatically determine
whether they are still useful or not. Such objects must be removed by the
programmer by calling endProxy() at the appropriate time. For example, this
can be an AJAX RPC object (i.e. a wtRemoteProcedureCall instance) that is
not related to any visible widgets of a web application.
Hypothesis 13 – All objects that are not DOM nodes and has a dependency
that belongs to the root set, are not removed.
Hypothesis 14 – All objects that are not DOM nodes and has a dependency
that does not belong to the root set, are removed.
Hypotheses 13 and 14 are true because the object would belong to the same
set as their depended object. This property is useful when the usefulness of
an object depends on whether some other objects have lapsed. For example,
an AJAX form (i.e. a wtForm instance) is usually dependent on a visible
widget on which the user can do data input. If that visible widget is
removed, then the AJAX form should be removed as well.
2.8 Event SystemThe event system implemented in WT Toolkit is modeled after the signal-
slot system in Qt (http://doc.trolltech.com/4.2/signalsandslots.html). In WT
Toolkit, each wtObject instance is allowed to have an arbitrary number of
signals and slots. A signal is just a name that represents a type of event that
the wtObject instance can fire. Signals can be created by the setSignal()
operation of the wtObject class, for example:
// fpsPlayer1 is a wtObject with an active weak reference
fpsPlayer1.setSignal(“Hit”);
Figure 39 - Creating a signal in a wtObject instance.
Authors: Kou Man Tong, Fung Chak Fai 59
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
When a wtObject instance fires an event, it is said to emit a signal, which is
the same terminology used in Qt’s documentation. A signal can be emitted as
an argument which tells the event listeners more information about the event
that has happened.
// fpsPlayer1 has hit fpsPlayer2 in the head
fpsPlayer1.emit(“Hit”, {“who”: “fpsPlayer2”, “where”: “in the head”});
Figure 40 – Emitting a signal with an argument in WT Toolkit.
A slot in WT Toolkit is a named function in a wtObject instance that is
dedicated to handling events. The function receives 3 arguments: the first
one is the object that received the event, the second one is the event
arguments that was sent with emit(), the third one is the object that emitted
the signal. A slot can be created by the setSlot() operation of the wtObject
class.
var announceSlot = function(myself, evt, source)
{
// source is an FPS Player, with a getName() operation
var name = source.getName();
alert(name + " has hit " + evt.who + " " + evt.where + "!");
}
// gameAnnouncer is a wtObject with an active weak reference
gameAnnouncer.setSlot("Announce", announceSlot);
Figure 41 - Creating a slot in a wtObject instance.
To assign a slot as an event handler to a signal, use the connect() operation
with the signal emitter as the subject. The connect() operation takes four
arguments: the first one is the connection identifier, which can be any string
of the programmer’s choice. The connection identifier can be used to
disconnect a signal-slot pair when the programmer no longer wants the slots
to be notified of the event. The second one is the signal name. The third one
is the object holding the slot, and the fourth argument is the slot’s name.
// Assign gameAnnouncer’s Announce slot as an event listener of
// fpsPlayer1’s Hit event
fpsPlayer1.connect("conn_id", "Hit", gameAnnouncer, "Announce");
Authors: Kou Man Tong, Fung Chak Fai 60
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
Figure 42 - Connecting a signal to a slot.
When a signal is fired, WT Toolkit’s event system guarantees that all
connected slots that have not lapsed are notified of the event. For example,
when the “Hit” signal of fpsPlayer1 is emitted as shown in Figure 40, the
application user would see the following:
Figure 43 - gameAnnounce's "Announce" slot is notified when the "Hit" signal is emitted.
3 AJAX RPC System3.1 Using the AJAX RPC System in WT Toolkit
A large part of AJAX is about sending and receiving data objects to and
from the web server without refreshing the page. Without a toolkit like Dojo
Toolkit or WT Toolkit to help in the process, this is usually accomplished
with the XMLHttpRequest object. The XMLHttpRequest object, however, is
very difficult to use. For example, the method of creating an
XMLHttpRequest object is different on different browsers
(http://developer.apple.com/internet/webcontent/xmlhttpreq.html). Another
deficiency of the XMLHttpRequest object is that it does not include a data
serialization service, so if the programmer wants to send anything more
complicated than a simple string to the web server, he will have to find a
way to serialize the data before sending it out.
Figure 44 - Public operations and attributes of the wtRemoteProcedureCall class.
Authors: Kou Man Tong, Fung Chak Fai 61
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
WT Toolkit includes a class dedicated to handling AJAX-style RPCs, called
wtRemoteProcedureCall. This class provides a few services that makes
using AJAX RPCs much easier than using the raw XMLHttpRequest object:
1. It provides a simple and unified interface that works the same across
different web browsers.
2. It automatically serializes outgoing data and de-serializes incoming data
to and from JSON format.
3. It fires a “Success” or “Failure” event after an RPC request is submitted
to the web server. Event handlers can be programmed by WT Toolkit’s
signal-slot system explained in Section 2.8, or by calling the
setFailureHandler() and setSuccessHandler() operations with the event
handling function as a shorthand.
The wtRemoteProcedureCall constructor takes three arguments: the URL,
the RPC call name, and the data object. The data object is typically a
JavaScript object containing strings, numbers, arrays and more JavaScript
objects. Non-primitive data types can be serialized as long as it has a
toString() operation. However, WT Toolkit can not de-serialize a JSON
strings received from the web server into non-primitive data types. So, if
exchanging non-primitive data types is needed, the web developer still has to
write some custom object conversion logic.
var rpc = new wtRemoteProcedureCall(“cgi-bin/sayhello.pl”, “SayHello”, {“fromWho”: “Martin”, “toWho”:
Marco, “time”: “20070416”, “payload”:
{“question”: “when are you going to finish the XOOPS portal in our project site?”}});
Figure 45 - Creating an AJAX RPC object in WT Toolkit.
Sending the RPC request to web server can be accomplished by calling the
submit() operation.
rpc.submit();
Figure 46 - Sending the RPC request to server.
An RPC request can be either successful or a failure. A “Failure” signal is
emitted if any of the following occurs:
Authors: Kou Man Tong, Fung Chak Fai 62
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
1. A socket error is encountered.
2. The web server returns with an HTTP status code that is not 200.
3. The web server does not respond within 5 seconds.
On the other hand, if the web server responds within 5 seconds after a
submit() call, with a HTTP status of “200 OK”, a “Success” signal is
emitted.
When a “Success” signal is emitted, the de-serialized data object is passed to
all event listeners as the event’s argument. When a “Failure” signal is
emitted, the HTTP status code (if available) or 0 (if no status code) is the
event’s argument.
var rpc = new wtRemoteProcedureCall(“cgi-bin/sayhello.pl”, “SayHello”, {“fromWho”: “Martin”, “toWho”:
Marco, “time”: “20070416”, “payload”:
{“question”: “when are you going to finish the XOOPS portal in our project site?”}});
var successHandler = function(myself, evt, source)
{
// suppose the web server sent us JavaScript object with a single field "ack"
alert(evt.ack);
}
rpc.setSuccessHandler(successHandler);
rpc.submit();
Figure 47 - Setting up and sending an RPC request with a success handler.
Figure 48 - What appears on the screen if the server sends back the string "Never!" as the "ack"
field of the returned JavaScript object.
The submitExclude attribute is a lookup table. A programmer can store
widget references in it that he needs be disabled during the time an RPC
request in sent by not yet returned. This is useful for avoiding concurrency
Authors: Kou Man Tong, Fung Chak Fai 63
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
issues where the user triggers some events that makes the program’s internal
states inconsistent before the RPC has returned. For example, we may have
an RPC call that would update the contents of a wtWindow widget when it is
returned successfully. If the user closes that wtWindow widget while the
RPC is being processed at the web server, then there would be nothing for it
to update when it is returned, and there would a JavaScript error. To avoid
this, the web developer can put the wtWindow widget into the RPC object’s
submitExclude lookup table, or the button widget that the user could use to
close the window into submitExclude. This disables the window widget or
the close window button during the time the RPC request is being submitted,
and so the application’s states would be consistent when the RPC has
returned.
3.2 The JSON Data Exchange FormatAlthough the name AJAX (Asynchronous JavaScript and XML) implies the
use of XML as the standard data exchange format, the XMLHttpRequest
object places no restriction on the data exchange format used in AJAX RPC
requests. Other data exchange formats can be used in AJAX remote
procedure calls, even binary data.
A popular alternative to XML that is used in AJAX RPC requests is JSON
(JavaScript Object Notation) [4]. The primary advantage of using JSON
instead of XML in web applications is that JSON can be directly converted
to and from JavaScript objects, while an XML string can only be directly
converted to and from a DOM node representing an XML document. This
makes JSON much easier to use than XML when client-side JavaScript
programming is concerned. As a result, JSON is used as the data exchange
format in place of XML in many popular web UI toolkits, such as in Dojo
Toolkit – “In addition, Dojo supports RPC using the JSON-RPC standard.”
[1] and in MochiKit – “… While this isn't directly relevant to MochiKit,
server-side code that produces JSON…”
(http://mochikit.com/doc/html/MochiKit/Async.html).
A related standard of using JSON as the data exchange format in web
applications is JSON-RPC (http://json-rpc.org/). WT Toolkit does not
support JSON-RPC at the moment, however.
Authors: Kou Man Tong, Fung Chak Fai 64
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
3.3 Using the AJAX RPC System in Web ServerWhen an AJAX RPC is submitted from the client side via WT Toolkit, an
HTTP POST request is sent to the web server with two arguments. The first
POST argument is called ProcName, whose value is RPC call name entered
as the second argument when the wtRemoteProcedureCall object instance
was constructed. The purpose of the ProcName field is for the web server to
distinguish which function is requested by the RPC request. The second
POST argument is called ProcArgs, which is the JSON representation of the
data object.
To return data back to the client side, the web server simply needs to respond
with the JSON encoded data object.
To show how the server side programming works in a web application
written with WT Toolkit, we will use some server side code snippets from
one of our application demo – “francium’s Xanga Tracker” [27].
Figure 49 - The login window of francium's Xanga Tracker 0.1.8.
Authors: Kou Man Tong, Fung Chak Fai 65
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
francium’s Xanga Tracker was written with Python and mod_python on the
server side. It uses a MySQL database as its permanent data store, the
SQLObject (http://www.sqlobject.org/ ) object relational manager for
database access, and the simplejson library
(http://cheeseshop.python.org/pypi/simplejson ) for encoding and decoding
JSON strings.
When an RPC request is received by the server part of francium’s Xanga
Tracker, the first thing it does it decoding the JSON string. This is done by a
very simple function called wtDecode() written in Python:
def wtDecode(reqObj):
if (not reqObj.has_key("ProcName")) or (not reqObj.has_key("ProcArgs")):
return None, {}
procName = reqObj["ProcName"]
procArgs = simplejson.loads(reqObj["ProcArgs"])
return procName, procArgs
Figure 50 - wtDecode() function in the server side of francium's Xanga Tracker.
In the code snippet above, the POST request fields are already de-serialized
into the reqObj argument – which is a mod_python FieldStorage object,
which works like a hash table (http://www.modpython.org/live/current/doc-
html/pyapi-util-fstor.html ). It took only one single line of code to decode the
incoming JSON string to a Python object, the “procArgs =
simplejson.loads(…” line.
def getSessionLog(req, **kw):
procName, procArgs = tracker_util.wtDecode(kw)
if procName == None:
return tracker_util.INVALID_RPC
login = tracker_util.getLogin(req)
if login == None:
return tracker_util.NOT_AUTHORIZED
user = db.User.getUser(login)
sortBy = procArgs["sortBy"]
sortOrder = procArgs["sortOrder"]
Authors: Kou Man Tong, Fung Chak Fai 66
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
offset = procArgs["offset"]
sessionLog = user.getSessions(sortBy, sortOrder, offset)
return simplejson.dumps(sessionLog)
Figure 51 – Sending out the tracker log after the user clicks on the “Xanga Log” tab in Figure 17.
Figure 51 above is a mod_python publisher handler
(http://www.modpython.org/live/current/doc-html/hand-pub.html ) that is
used to send out the Xanga Tracker’s log to the web browser from the web
server. The procArgs variable seen here is the data object sent from the
browser in the RPC request. It can be seen in the last 5 lines of the program
that procArgs is used just like a regular Python dictionary in the server side.
The second last line of the program is a database query that returns an array
of log entries and the total number of log entries. The last line of the
program encodes the log entry array into a JSON string and sends that back
to the web client.
Figure 52 - Part of the returned string sent to the browser by getSessionLog().
Authors: Kou Man Tong, Fung Chak Fai 67
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
4 Regular Widgets System4.1 Introduction
Figure 53 - Regular widget classes in WT Toolkit.
WT Toolkit includes a moderately large collection of regular GUI widgets.
The regular widgets cannot be used for form input and cannot be used for
drawing vector graphics.
API documentation of WT Tookit’s regular widgets system can be found at:
http://wt-toolkit.sourceforge.net/doc-0.3.2/overview-summary-wt_widget.js.html
4.2 The wtWidget ClassThe base class for most of the regular widget classes (except
wtNotebookPage) is the wtWidget class. It contains a set of operations that is
common to all regular widgets and form input widgets (which are explained
in Section 5).
Authors: Kou Man Tong, Fung Chak Fai 68
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
Figure 54 - Operations of wtWidget class.
Some of the more interesting operations of the wtWidget class that are not
often found in other web UI toolkits and are quite troublesome to code
without a toolkit:
1. setAlpha() – Sets the alpha transparency of a widget.
2. setDraggable() – Makes the widget draggable.
Besides the operations, the wtWidget class also has a set of predefined
signals. Most of the wtWidget signals are user initiated events:
1. Click – the widget has been clicked.
2. MouseDown – the user has pressed a mouse button on this widget.
3. MouseOver – the mouse cursor is over this widget.
4. MouseOut – this mouse cursor has just pass away from this widget.
5. MouseUp – the user has released a mouse button on this widget.
6. Focus – this widget is being selected.
7. Blur – this widget is being deselected.
8. DoubleClick – the user has double clicked on the widget.
9. KeyDown – the user has lowered a key while this widget is selected.
10. KeyUp – this user has released a key while this widget is selected.
11. KeyPress – this lowered and released a key while this widget is selected.
Authors: Kou Man Tong, Fung Chak Fai 69
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
12. Resize – the widget has been resized.
13. Move – the widget has been moved.
14. DragStart – the user has started dragging the widget around.
15. DragEnd – the user has finished dragging the widget around.
4.3 Example Widget ClasseswtWindow
Figure 55 - A few instances of wtWindow.
A wtWindow instance is a draggable container for other visible widgets. It
has a title, a scrollable container area and a soft shadow around the edges. It
can be disabled which turns it translucent and makes the contained widgets
unable to respond to user events.
wtImage
Authors: Kou Man Tong, Fung Chak Fai 70
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
Figure 56 – The translucent backgrounds in the UML class title above are wtImage instances.
wtImage can be used to draw translucent .png images correclt under Internet Explorer 6 and 7.
A wtImage instance is very much like an <img/> tag in HTML. It provides
an extra functionality under Internet Explorer, however. In Internet Explorer
6, regular <img/> tags cannot render translucent .png files correctly; In
Internet Explorer 7, although <img/> tags can render translucent .png files,
setting alpha translucency on a translucent .png image would make .png
file’s translucency disappear. The wtImage class, on the other hand, can
render translucent .png files correctly under both Internet Explorer 6 and 7,
even if additional alpha translucency is applied to the image.
Authors: Kou Man Tong, Fung Chak Fai 71
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
5 AJAX Forms System5.1 Introduction
One of the most obvious uses for AJAX RPC calls is to send user inputs to
the web server, and to receive response from the web server, without
refreshing the page. WT Toolkit has included an AJAX form system for this
task.
Figure 57 - Class diagram of WT Toolkit's AJAX form system.
API documentation of WT Toolkit’s AJAX form system can be found in the
following URL:
http://wt-toolkit.sourceforge.net/doc-0.3.2/overview-summary-wt_form.js.html
5.2 Using AJAX Forms in WT ToolkitThe wtForm Class
The wtForm class is a container for form input widgets. It contains a URL
and a RPC call name which are used to submit values to the server. It is very
much like a wtRemoteProcedureCall object where the data object is defined
by name and values of the form input widgets, instead of by the
programmer.
Authors: Kou Man Tong, Fung Chak Fai 72
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
var form = new wtForm(“cgi/give_me_data.cgi”, “ProcessData”);
Figure 58 - Constructing an AJAX form.
The operations and events of the wtForm class are also very similar to the
wtRemoteProcedureCall class. Some of the similar operations include:
1. setSuccessHandler() – Sets the event handling function which would be
called back when form submission is successful.
2. setFailureHandler() – Sets the event handling function which would be
called back when form submission failed.
3. submit() – Submits the input form as an AJAX RPC request.
4. abort() – Aborts the current form submission.
The criteria of deciding whether a form submission is successful are also the
same as in wtRemoteProcedureCall. For example, if an AJAX form was
submitted and the web server did not respond within 5 seconds, the form
submission would be treated as a failure.
Some of the similar signals include:
1. SubmitSuccess – Same as the “Success” signal in
wtRemoteProcedureCall.
2. SubmitFailure – Same as the “Failure” signal in
wtRemoteProcedureCall.
Some events are unique to the wtForm class, however. For example:
1. ValueChanged – Emitted when one of the form widget’s value has
changed.
The “ValueChanged” signal in wtForm class is useful in verifying user
inputs, or notifying other objects in the web application UI of a change in
user input.
AJAX Form Widgets
With the exception of the classes wtSpreadSheet and wtVariableSpreadSheet,
most AJAX form widgets inherit from the wtFormWidget class. Every
wtFormWidget instance has a name and a value. Many wtFormWidget
descendents can also have a visible interface that lets the user change the
Authors: Kou Man Tong, Fung Chak Fai 73
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
widget’s value.
Figure 59 - Some of the visible form widgets available in WT Toolkit
All form widgets have a setValue() and a getValue() operation for retrieving
and modifying its value. The name of a form widget is set at the constructor
and can not be changed once they have been constructed.
All form widget also have a common signal, ValueChanged. It can be used
to notify other objects in the web application UI of a specific form widget
being changed by the user, or by a setValue() call.
Submitting an AJAX Form
When an AJAX form is submitted, an AJAX RPC call is made to the web
server with the data object composed out of the (name, value) pairs of its
constituent form input widgets.
For example, if we have a wtForm instance who has two single-line
textboxes, named “input1” and “input2” respectively. The value of “input1”
at the time of AJAX form submission is “Hello World!”, and the value of
“input2” at the time of form submission is “Goodbye World!” The JSON
string sent to the web server as the ProcArg POST request field would be:
{“input1”:”Hello World!”, “input2”:“Goodbye World!”}
Handling Concurrency Issues
Authors: Kou Man Tong, Fung Chak Fai 74
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
Similar to the wtRemoteProcedureCall class, submitting an AJAX form may
incur concurrency issues. For example, the user may change the form’s
values while the AJAX form is being submitted. The wtForm class
implemented in WT Toolkit is able to disable any constituent form widgets
while the form is being submitted and the RPC call has not yet returned.
Moreover, if the programmer wishes to disable other visible widgets, he can
add that visible widget into the submitExclude lookup table of the wtForm
instance, which works the same as in wtRemoteProcedureCall.
5.3 Example Input Form
Figure 60 - Login window of francium's Xanga Tracker.
The login window in francium’s Xanga Tracker, one of WT Toolkit’s
application demonstrations, contains an AJAX form. The AJAX form here
has two user inputs, the login name, and the password. The “Login” button
submits the form to web server.
Authors: Kou Man Tong, Fung Chak Fai 75
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
Figure 61 - Input form is grayed out and disabled when AJAX form is being submitted.
When the form is being submitted, i.e. the “Login” button has been pressed
but the server has not replied yet. The whole form would be grayed out and
is disabled to the user. In the figure above, the “New User…” button, which
is not part of the AJAX form, is gray out and disabled as well. This is
because the web developer has added the “New User…” button to the
submitExclude lookup table of login form. Another change that has occurred
during form submission is that the submit button has changed to an “Abort”
button.
Figure 62 - The user is notified of a failed login if he enters a wrong username, password pair.
If the user has entered a wrong username, password pair to the login form,
he will be notified of a login failure when the client side UI has received a
response from the web server.
Authors: Kou Man Tong, Fung Chak Fai 76
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
Figure 63 - Another window with Xanga log entries are displayed after a successful login.
If the user has entered the correct username, password pair in the login form,
then the login form window would disappear, and a new window appears
which displays his Xanga page visitor log.
Authors: Kou Man Tong, Fung Chak Fai 77
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
6 Vector Graphical Widgets System6.1 Introduction
The vector graphical widgets system in WT Toolkit allows drawing vector
graphics in Internet Explorer 6+ and Gecko-based browsers (e.g. Firefox)
without requiring any third party plug-in. Vector graphics are drawn by
adding VML [19] nodes under the document tree in Internet Explorer, and
by adding inline SVG [30] nodes to the document tree in web browsers
using the Gecko engine.
Apart from being able to draw simple shapes like ellipses, polygons and
lines, the vector graphics system in WT Toolkit is also able to construct
more sophisticated and tangible widgets. For example, we can construct 3D
objects and many different kinds of charts, such as 3D bar charts, 3D pie
charts, broken line charts, curve line charts, scatter charts, radar charts, etc.
Figure 64 - Vector graphics drawn with WT Toolkit.
API documentation for the vector graphical widgets system can be found in
the following URL:
http://wt-toolkit.sourceforge.net/doc-0.3.2/overview-summary-wt_vector.js.html
6.2 Base Components of the Vector Graphics
Authors: Kou Man Tong, Fung Chak Fai 78
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
SystemThe wtVectorWidget Class
All vector graphical widgets in WT Toolkit are subclasses of the
wtVectorWidget Class. Unlike its HTML counterpart, wtWidget, the
wtVectorWidget class does not offer a lot of features. It also does not emit
any signals. The purpose of wtVectorWidget is purely for drawing graphics,
not for interacting with the user.
Figure 65 - Although there are a lot of operations under the wtVectorWidget class, most of them
serve only to change the appearance of the graphical widget.
The wtVectorGroup Class
To make vector graphical widget emit user events, graphical widgets must
be group together in a wtVectorGroup instance. The wtVectorGroup class
offers some operations that are similar to those found in wtWidget. For
example:
1. setDraggable() – makes a group of vector graphical widgets draggable.
2. setAbsolutePosition() – adds an (x,y) coordinate offset to the graphical
widget’s original positions.
The wtColor Class
The fill and stroke color of all simple vector graphics widgets can be
modified by the setFillColor() and setStrokeColor() operations. These two
Authors: Kou Man Tong, Fung Chak Fai 79
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
operations accept many kinds of color arguments. For example, they can
accept CSS color code words, like “red”, “blue”, “gray”, “darkblue”, etc.
They can also accept a wtColor instance as the argument with which the user
can define colors as RGB, HSV and RGBA values. For example:
vectorWidget.setFillColor(new wtColor("rgba", {"red":255, "green":255, "blue":255, "alpha":127}));
Figure 66 - Setting a graphical widget's fill color to be white with 50% opacity.
The alpha channel in wtColor allows graphical widgets to be colored with
translucency. For three circles shown in Figure 64, for example, are colored
with a translucent alpha channel.
Figure 67 - Three overlapping circles colored with a translucent alpha channel.
The wtColor class can also generate new colors based on the current
wtColor instance. It does so by adding or subtracting RGB or HSV values to
the current color. This is useful in drawing 3D objects. For example, there is
a green 3D box in Figure 65 which was constructed from the wtBar class.
Figure 68 - Green 3D box in Figure 65.
window.myBar1 = new wtBar(canvas,30,50,30,45,"green");
Figure 69 – The green 3D box in JavaScript.
Authors: Kou Man Tong, Fung Chak Fai 80
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
The green 3D box has three visible sides, with the top side and the right side
being somewhat darker than the front side. In constructing the green 3D box,
the programmer only has to tell WT Toolkit that the box’s color is “green”,
he does not have to tell WT Toolkit how to color the darker sides. This is
possible because the wtBar class has automatically generated the darker
shades with wtColor by subtracting the value of V channel in the HSV
representation of “green”.
6.3 Charting EngineMany different kinds of charts can be drawn with WT Toolkit’s charting
engine. These include:
3D bar charts;
Figure 70 - A 3D bar chart.
Stacked bar charts;
Authors: Kou Man Tong, Fung Chak Fai 81
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
Figure 71 - A 3D stacked bar chart.
Bar charts with data items separated on the category axis;
Figure 72 - Another 3D bar chart.
Authors: Kou Man Tong, Fung Chak Fai 82
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
Line charts, with curves or broken lines;
Figure 73 - A line chart with curves and broken lines.
Stacked line charts;
Figure 74 - A stacked line chart.
Authors: Kou Man Tong, Fung Chak Fai 83
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
Scatter charts;
Figure 75 - A scatter chart drawn with WT Toolkit.
3D pie charts, with the “pies” separated or combined;
Figure 76 - A few pie charts drawn with WT Toolkit.
Authors: Kou Man Tong, Fung Chak Fai 84
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
Radar charts;
Figure 77 - Two radar charts drawn with WT Toolkit.
6.4 G=(V,E) Graph EngineThe G=(V,E) graph engine in WT Toolkit allows user interactive G=(V,E) to
be constructed in web applications. A demonstration video has been posted
to YouTube showing the interactive features of a G=(V,E) graph drawn with
WT Toolkit, in the following URL:
http://www.youtube.com/watch?v=QmSpj7MOo6k
Authors: Kou Man Tong, Fung Chak Fai 85
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
Figure 78 - Interactive G=(V,E) video demonstration in YouTube.
6.5 3D ObjectsSimple 3D objects can be constructed in WT Toolkit. A 3D object animation
demonstration video has been posted to YouTube:
http://www.youtube.com/watch?v=KCKsGOff1vI
Figure 79 - Video showing rotating cubes drawn with WT Toolkit.
6.6 Live DemonstrationsA few live demonstrations showing the features of WT Toolkit’s vector
graphical engine has been posted to our SourceForge project website.
1. Vector widgets demonstration:
Authors: Kou Man Tong, Fung Chak Fai 86
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
http://wt-toolkit.sourceforge.net/demo/vector_widget.html
Many of the screen captures shown in Section 6 are captured from this
demonstration page.
Figure 80 - Vector widgets demonstration running in Firefox 2.0.
Authors: Kou Man Tong, Fung Chak Fai 87
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
2. Interactive charting demonstration:
http://wt-toolkit.sourceforge.net/demo/charting_demo.html
Figure 81 - A screenshot of our interactive charting demonstration.
3. Interactive UML editor:
http://wt-toolkit.sourceforge.net/demo/test_wtumlclass.html
All the UML class and instance diagrams in this report are drawn with
our UML editor built upon WT Toolkit. So, while you are reading this
report, you are already looking at a partial demonstration of the UML
editor.
Authors: Kou Man Tong, Fung Chak Fai 88
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
7 ConclusionsIn this final year project report, we have described the object model and event
handling system of WT Toolkit. We have compared our object model and event
handling system to two other popular toolkits: Dojo Toolkit and Yahoo! UI Toolkit.
We have shown that our object model and event handling system are immune to cross
page memory leaks in Internet Explorer 6, and are less vulnerable to lapsed listeners
when compared to Dojo Toolkit or Yahoo! UI Toolkit.
Then, we have described our AJAX RPC system. Our RPC system uses JSON as the
data exchange format in sending and receiving data objects to and from the web
server. Our AJAX RPC system does not support the JSON-RPC standard yet, which
other similar toolkits like Dojo Toolkits and MochiKit have already supported. Our
AJAX RPC system has built-in mechanisms for avoiding concurrency issues with
user initiated events that were emitted when the RPC is being processed.
We have built a fairly comprehensive UI widget system that includes a moderately
wide range of visible widgets. User interactive features can be implemented by
connecting event listeners to the user signals of UI widgets. Notable features of our
toolkit include alpha transparency, transparent PNG image support under Internet
Explorer, and translucent windows.
An AJAX form system has been implemented in WT Toolkit. It works very similarly
compared to the AJAX RPC system. Our form widget system is more flexible when
compared to dojo.io.bind() method in Dojo Toolkit because we allow building new
and custom form widgets by object oriented methods like aggregation and inheritance.
We have implemented a vector graphics system in WT Toolkit. Our vector graphics
system allows the use of colors with alpha channel for coloring widgets. Graphics
widgets can be made user interactive as widget groups, which is useful when we need
make an aggregated graphical widget interactive instead of having every single basic
shape in the graphical widget being interactive.
Finally, some sophisticated graphical widgets and tangible widgets with real
applications have been implemented in WT Toolkit’s vector graphical engine. Many
different kinds of charts, 2D and 3D, can be constructed with WT Toolkit. Interactive
G=(V,E) graphs can be constructed with WT Toolkit. A UML editor application has
been implemented as a live demonstration of WT Toolkit’s G=(V,E) graph
Authors: Kou Man Tong, Fung Chak Fai 89
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
capabilities. All class diagrams and instance diagrams in this report are drawn with
our UML Editor.
Authors: Kou Man Tong, Fung Chak Fai 90
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
8 References[1] Roland Barcia. Build enterprise soa ajax clients with the dojo toolkit and
json-rpc. http://www-128.ibm.com/developerworks/websphere/library/techarticles/
0606 barcia/0606 barcia.html?ca=dgr-lnxw07Dojo-Ajax-SOA, June 2006.
[2] Mihai Bazon. Ie: where’s my memory?
http://www.bazon.net/mishoo/articles.epl?art id=824, August 2004.
[3] Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, and Clifford
Stein. Introduction to Algorithms, chapter 21. MIT Press, 2001.
[4] Douglas Crockford. Json: The fat-free alternative to xml.
http://2006.xmlconference.org/proceedings/176/presentation.html, 2006.
[5] Flickr. Welcome to flickr - photo sharing. http://www.flikr.com, 2007.
[6] Dojo Foundation. The dojo toolkit. http://www.dojotoolkit.org, 2007.
[7] Dojy Foundation. Degradable ajax forms with dojo.
http://dojotoolkit.org/node/227, January 2006.
[8] Free Software Foundation. Gnu lesser general public license (lgpl).
http://www.opensource.org/osi3.0/licenses/lgpl-license.php.
[9] The Apache Software Foundation. Apache/python integration.
http://www.modpython.org/, February 2007.
[10] Ethan Henry and Ed Lycklama. How do you plug java memory leaks? Dr.
Dobbs Journal, February 2000.
[11] HKUST. Vela startup. http://vela.ust.hk, 2006.
[12] HKUST. Hkust - webct homepage. http://webct.ust.hk/, April 2007.
[13] Google
Inc. Welcome to google docs and spreadsheets. http://docs.google.com,
2007.
[14] Carol King. Peoplesoft ships web-based erp solu-
tion. http://www.internetnews.com/infra/article.php/452911, September
2000.
[15] Martin LaMonica. Microsoft gets
hip to ajax. http://news.com.com/Microsoft+gets+hip+to+AJAX/2100-10073-
5765197.html, June 2005.
[16] PePLink Ltd. Introduction to peplink central management system.
http://www.peplink.com/document/PePLink_PCMS_whitepaper_introduction.pdf,
2006.
[17] PePLink Ltd. Peplink announces integrated remote management solution.
http://www.pepwave.com/index.php?view=news&news_category id=2&news_id=9,
April 2006.
Authors: Kou Man Tong, Fung Chak Fai 91
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
[18] PePLink Ltd. Peplink supplies wi-fi cpe to st. cloud, fl.
http://www.pepwave.com/index.php?view=news&news_category id=2&news_id=7,
March 2006.
[19] Brian Mathews, Daniel Lee, Brian Dister, John Bowler, Howard Coop-
erstein, Ajay Jindal, Tuan Nguyen, Peter Wu, and Troy Sandal. Vector
markup language (vml). http://www.w3.org/TR/NOTE-VML.html, May
1998.
[20] Tropos Networks. Tropos networks
introduces first metro-compliant specification for wi-fi access equipment.
http://www.tropos.com/news/pressreleases/2006_05_03.php, May 2006.
[21] Monica Pawlan. Reference objects and garbage collection. Reference Ob-
jects and Garbage Collection, August 1998.
[22] Justin Rogers. Understanding and solving internet explorer leak pat-
terns. http://msdn.microsoft.com/library/default.asp?url=/library/en-
us/ietechcol/dnwebgen/ie_leak_patterns.asp, June 2005.
[23] Alex Russell. Now whered i put that reference.
http://alex.dojotoolkit.org/?p=526, December 2005.
[24] sakaiproject.org. Lmes 2.1a. http://lmes2.ust.hk, 2007.
[25] Bruce A. Tate. Bitter Java, chapter 6. Manning Publications Co., 2002.
[26] Team and Concepts Ltd. Online spreadsheets - editgrid.
http://www.editgrid.com, 2007.
[27] Kou Man Tong. francium’s xanga tracker. http://hkpcug.homeftp.net,
2007.
[28] (Unknown). Drip ie leak detector.
http://www.outofhanwell.com/ieleak/index.php?title=Main_Page.
[29] (Unknown). Jsdoc - javascript documentation tool.
http://jsdoc.sourceforge.net/.
[30] SVG Wiki. Inline svg. http://wiki.svg.org/index.php?title=Inline_SVG,
March 2007.
[31] Wikipedia. Ajax (programming).
http://en.wikipedia.org/w/index.php?title=Ajax_%28programming
%29&oldid=76779850, June 2006.
[32] Wikipedia. Rich internet application.
http://en.wikipedia.org/w/index.php?
title=Rich_Internet_application&oldid=75889738, June 2006.
[33] Wikipedia. Observer pattern.
http://en.wikipedia.org/w/index.php?
title=Observer_pattern&direction=prev&oldid=105135192, February 2007.
Authors: Kou Man Tong, Fung Chak Fai 92
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
9 Appendix A: Source Code for Finding Programming Patterns Prone to Memory Leaks in Internet Explorer 69.1 Leak Experiments with Simple Circular
References<html>
<head>
<title>Circular reference within a DOM object leaks memory in Internet Explorer</title>
<script language="javascript">
function init()
{
// first, we construct a DOM node
var node = document.createElement("div");
node.innerHTML = "I'm leaked away!";
// then, we make a circular reference like this:
// DOM node -> DOM node
// for control experiment, assign null instead.
node.circularReference = node;
// finally, we attach the DOM node to the document tree
// this step is not required for the leak to happen
document.body.appendChild(node);
}
</script>
</head>
<body onload="init()">
</body>
</html>
Figure 82 - A DOM node referring to itself via an attribute.
Authors: Kou Man Tong, Fung Chak Fai 93
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
<html>
<head>
<title>Circular reference between a DOM object and another DOM object leaks memory
in Internet Explorer</title>
<script language="javascript">
// this is actually worse than the other cases - both nodes are leaked because both are referenced in a
// circle
function init()
{
// construct the first DOM node and attach it to the document tree
var node = document.createElement("div");
node.innerHTML = "I'm leaked away!";
node.id = "node";
document.body.appendChild(node);
// construct the second DOM node and attach it to the document tree
var intermediateNode = document.createElement("div");
intermediateNode.id = "intermediateNode";
intermediateNode.innerHTML = "I'm also leaked away!";
document.body.appendChild(intermediateNode);
// now, make a circular reference path like this:
// DOM node A -> DOM node B -> DOM node A
// for control experiment, comment out any one of the following two lines
node.circularReferencePart1 = intermediateNode;
intermediateNode.circularReferencePart2 = node;
}
</script>
</head>
<body onload="init()">
</body>
</html>
Figure 83 - Two DOM nodes referring to each other.
Authors: Kou Man Tong, Fung Chak Fai 94
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
<html>
<head>
<title>Circular reference between a JS object and a DOM object leaks memory in Internet
Explorer</title>
<script language="javascript">
function init()
{
// first, we construct a DOM node
var node = document.createElement("div");
node.innerHTML = "I'm leaked away!";
// then, we make a Javascript object
var jsobj = new Object();
// then, we make a circular reference like this:
// DOM node -> JS object -> DOM node
// for control experiment, comment out any one of the two lines below
node.circularReferencePart1 = jsobj;
jsobj.circularReferencePart2 = node;
// finally, we attach the DOM node to the document tree
// this step is not required for the leak to happen
document.body.appendChild(node);
}
</script>
</head>
<body onload="init()">
</body>
</html>
Figure 84 - A DOM node referring to itself via a Javascript object.
Authors: Kou Man Tong, Fung Chak Fai 95
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
<html>
<head>
<title>Circular reference between two Javascript objects does not leak memory in Internet
Explorer</title>
<script language="javascript">
function init()
{
// first, we construct a DOM node and attach it the the document tree
var node = document.createElement("div");
node.innerHTML = "I'm NOT leaked away!";
node.id = "node";
document.body.appendChild(node);
// then, we construct a Javascript object and add the DOM node to it.
var jsobj = new Object();
jsobj.domNode = node;
// then, we construct another Javascript object
var jsobj2 = new Object();
// finally, we create a circular reference like this:
// JS object A -> JS object B -> JS object A
jsobj.circularReference1 = jsobj2;
jsobj2.circularReference2 = jsobj;
}
</script>
</head>
<body onload="init()">
</body>
</html>
Figure 85 - Two Javascript objects referring to each other, with a DOM node attached to one of
the objects as an indicator such that any leak can be detected via Drip.
Authors: Kou Man Tong, Fung Chak Fai 96
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
9.2 Leak Experiments with Closures<html>
<head>
<title>Circular reference between DOM node and a closure via implicit binding of
variables leaks memory in Internet Explorer</title>
<script language="javascript">
function makeAnotherClosureForControlExperiment()
{
window.g = function(){alert(this);}
}
function init()
{
// first, we construct a DOM node
var node = document.createElement("div");
node.innerHTML = "I'm leaked away!";
// then, we create an empty closure
// it doesn't even need to refer to "node" or "this"
// for the circular reference to happen
var f = function(){}
// then, we create a circular reference like this:
// DOM node -> closure -> DOM node
// if you want to perform a control experiment, comment out the line below
node.f = f;
// for a control experiment, we make another closure
// that cannot possibly refer back to "node" implicitly
// but rather, it can refer back to "node" via the "this" reference
// interestingly, this one does NOT leak
makeAnotherClosureForControlExperiment();
node.g = window.g;
// finally, we attach the DOM node to the document tree
// this step is not required for the leak to happen
Authors: Kou Man Tong, Fung Chak Fai 97
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
document.body.appendChild(node);
}
</script>
</head>
<body onload="init()">
</body>
</html>
Figure 86 - Closure declared in the same scope as a DOM node, and assigning that closure as
the DOM node's attribute.
<html>
<head>
<title>This example shows that containing a DOM node within a Javascript object cannot
prevent the memory leak</title>
<script language="javascript">
function makeAnotherClosureForControlExperiment()
{
window.g = function(){alert(this);}
}
function init()
{
// first, we construct a DOM node, contained in a JS object
var jso = {"node" : document.createElement("div")};
jso.node.innerHTML = "I'm leaked away!";
// then, we create an empty closure
// it doesn't even need to refer to "node" or "this"
// for the circular reference to happen
// if you want to perform a control experiment, comment out the line below
var f = function(){}
// then, we create a circular reference like this:
// DOM node -> closure -> JS Object -> DOM node
jso.node.f = f;
Authors: Kou Man Tong, Fung Chak Fai 98
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
// for a control experiment, we make another closure
// that cannot possibly refer back to "node" implicitly
// but rather, it can refer back to "node" via the "this" reference
// interestingly, this one does NOT leak
makeAnotherClosureForControlExperiment();
jso.node.g = window.g;
// finally, we attach the DOM node to the document tree
// this step is not required for the leak to happen
document.body.appendChild(jso.node);
}
</script>
</head>
<body onload="init()">
</body>
</html>
Figure 87 – Same as the last experiment, except that the DOM node is put inside a Javascript
object such that identifiers inside the closure cannot be bound to the DOM object directly.
<html>
<head>
<!--
This test case came from http://www.bazon.net/mishoo/articles.epl?art_id=824
where the author used one more closure to fix the closure memory leak bug
-->
<title>This examples shows that creating the DOM node with another closure prevents
the memory leak</title>
<script language="javascript">
function init()
{
// first, create an empty closure
var f = function(){}
var h = function()
{
// now, we construct a DOM node
var node = document.createElement("div");
Authors: Kou Man Tong, Fung Chak Fai 99
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
node.innerHTML = "I'm NOT leaked away!";
// notice that no circular reference has been created
// because the closure f cannot refer back to our DOM node via "node"
// but it can still do so via "this"
node.f = f;
// finally, we attach the DOM node to the document tree
// this step is not required for the leak to happen
document.body.appendChild(node);
}
h();
}
</script>
</head>
<body onload="init()">
</body>
</html>
Figure 88 - Trying to avoid circular references via closures with even more closures. This test
case is modified from one of the suggested solutions mentioned in Bazon's article.
9.3 Leak Experiment with Function Objects<html>
<head>
<title>Circular reference between DOM node and a non-closure function object does not
leaks memory in Internet Explorer</title>
<script language="javascript">
function init()
{
// first, we construct a DOM node
var node = document.createElement("div");
node.innerHTML = "I'm NOT leaked away!";
// then, we create an empty function object
// a function object created this way cannot refer to variables defined in the outer scope
var f = new Function("");
Authors: Kou Man Tong, Fung Chak Fai 100
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
// then, we create a circular reference like this:
// DOM node -> function object -> DOM node
node.f = f;
// finally, we attach the DOM node to the document tree
document.body.appendChild(node);
}
</script>
</head>
<body onload="init()">
</body>
</html>
Figure 89 - Making a function object by calling the Function class constructor within the same
scope of a DOM node, and assigning the function object as a DOM node attribute.
Authors: Kou Man Tong, Fung Chak Fai 101
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
10 Appendix B: Source Code for Finding JavaScript Operations Affected by Memory Leaks in Internet Explorer 6
<html>
<head>
<title>Circular reference within a DOM object leaks memory in Internet Explorer</title>
<script language="javascript">
function leakOne()
{
// first, we construct a DOM node
var node = document.createElement("div");
// then, we make a circular reference like this:
// DOM node -> DOM node
node.circularReference = node;
// finally, we attach the DOM node to the document tree
// this step is not required for the leak to happen
document.body.appendChild(node);
placeNode(node);
}
function placeNode(node)
{
// place the node randomly on the page
var x = parseInt(Math.random() * 800) + "px";
var y = parseInt(50 + Math.random() * 750) + "px";
node.style.position = "absolute";
node.style.top = y;
node.style.left = x;
node.style.width = "1px";
node.style.height = "1px";
node.style.fontSize = "1px";
node.style.backgroundColor = "black";
}
function getTime()
Authors: Kou Man Tong, Fung Chak Fai 102
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
{
return (new Date()).getTime();
}
function leak2000()
{
var begin = getTime();
for(var i=0;i<2000;i++)
leakOne();
var end = getTime();
alert("Creating 2000 nodes took " + (end - begin) + "ms");
}
function init(){}
</script>
</head>
<body onload="init()">
<a href="javascript: leak2000();">Create 2000 DIV nodes</a>
</body>
</html>
Figure 90 – Each time the link “Create 2000 DIV nodes” is clicked, 2000 self-referencing DOM
nodes are created.
<html>
<head>
<title>Circular reference between a DOM object and another DOM object leaks memory
in Internet Explorer</title>
<script language="javascript">
// this is actually worse than the other cases - both nodes are leaked because both are referenced in a
circle
function leakOne()
{
// construct the first DOM node and attach it to the document tree
var node = document.createElement("div");
document.body.appendChild(node);
Authors: Kou Man Tong, Fung Chak Fai 103
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
// construct the second DOM node and attach it to the document tree
var intermediateNode = document.createElement("div");
document.body.appendChild(intermediateNode);
// now, make a circular reference path like this:
// DOM node A -> DOM node B -> DOM node A
node.circularReferencePart1 = intermediateNode;
intermediateNode.circularReferencePart2 = node;
placeNode(node);
placeNode(intermediateNode);
}
function placeNode(node)
{
// place the node randomly on the page
var x = parseInt(Math.random() * 800) + "px";
var y = parseInt(50 + Math.random() * 750) + "px";
node.style.position = "absolute";
node.style.top = y;
node.style.left = x;
node.style.width = "1px";
node.style.height = "1px";
node.style.fontSize = "1px";
node.style.backgroundColor = "black";
}
function getTime()
{
return (new Date()).getTime();
}
function leak2000()
{
var begin = getTime();
for(var i=0;i<1000;i++)
leakOne();
Authors: Kou Man Tong, Fung Chak Fai 104
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
var end = getTime();
alert("Creating 2000 nodes took " + (end - begin) + "ms");
}
</script>
</head>
<body onload="init()">
<a href="javascript: leak2000();">Create 2000 DIV nodes</a>
</body>
</html>
Figure 91 – Each time the link “Create 2000 DIV nodes” is clicked, 1000 pairs of circular
referencing DIV nodes are created.
<html>
<head>
<title>Circular reference between a JS object and a DOM object leaks memory in Internet
Explorer</title>
<script language="javascript">
function leakOne()
{
// first, we construct a DOM node
var node = document.createElement("div");
// then, we make a Javascript object
var jsobj = new Object();
// then, we make a circular reference like this:
// DOM node -> JS object -> DOM node
node.circularReferencePart1 = jsobj;
jsobj.circularReferencePart2 = node;
// finally, we attach the DOM node to the document tree
// this step is not required for the leak to happen
document.body.appendChild(node);
placeNode(node);
}
function placeNode(node)
Authors: Kou Man Tong, Fung Chak Fai 105
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
{
// place the node randomly on the page
var x = parseInt(Math.random() * 800) + "px";
var y = parseInt(50 + Math.random() * 750) + "px";
node.style.position = "absolute";
node.style.top = y;
node.style.left = x;
node.style.width = "1px";
node.style.height = "1px";
node.style.fontSize = "1px";
node.style.backgroundColor = "black";
}
function getTime()
{
return (new Date()).getTime();
}
function leak2000()
{
var begin = getTime();
for(var i=0;i<2000;i++)
leakOne();
var end = getTime();
alert("Creating 2000 nodes took " + (end - begin) + "ms");
}
</script>
</head>
<body onload="">
<a href="javascript: leak2000();">Create 2000 DIV nodes</a>
</body>
</html>
Figure 92 – Each time the link “Create 2000 DIV nodes” is clicked, 2000 DIV nodes each with a
reference to itself via a Javascript object is created.
<html>
Authors: Kou Man Tong, Fung Chak Fai 106
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
<head>
<title>Circular reference between DOM node and a closure via implicit binding of
variables leaks memory in Internet Explorer</title>
<script language="javascript">
function leakOne()
{
// first, we construct a DOM node
var node = document.createElement("div");
// then, we create an empty closure
// it doesn't even need to refer to "node" or "this"
// for the circular reference to happen
var f = function(){}
// then, we create a circular reference like this:
// DOM node -> closure -> DOM node
node.f = f;
// finally, we attach the DOM node to the document tree
// this step is not required for the leak to happen
document.body.appendChild(node);
placeNode(node);
}
function placeNode(node)
{
// place the node randomly on the page
var x = parseInt(Math.random() * 800) + "px";
var y = parseInt(50 + Math.random() * 750) + "px";
node.style.position = "absolute";
node.style.top = y;
node.style.left = x;
node.style.width = "1px";
node.style.height = "1px";
node.style.fontSize = "1px";
node.style.backgroundColor = "black";
}
Authors: Kou Man Tong, Fung Chak Fai 107
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
function getTime()
{
return (new Date()).getTime();
}
function leak2000()
{
var begin = getTime();
for(var i=0;i<2000;i++)
leakOne();
var end = getTime();
alert("Creating 2000 nodes took " + (end - begin) + "ms");
}
</script>
</head>
<body onload="">
<a href="javascript: leak2000();">Create 2000 DIV nodes</a>
</body>
</html>
Figure 93 – Each time the “Create 2000 DIV nodes” link is clicked, 2000 DIV nodes each with a
reference to itself via a closure is created.
Authors: Kou Man Tong, Fung Chak Fai 108
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
11 Appendix C: Source Code for Showing JavaScript Object Creation Latency Deteriorating with Number of Reachable JavaScript Objects under Internet Explorer
<html>
<head>
<script>
var arr = [];
function createObjects()
{
var o = null;
for(var i=0;i<10000;i++)
o = new Object;
}
function createDummyObjects(n)
{
for(var i=0;i<n;i++)
arr.push(new Object);
}
function benchmark()
{
var startTime = (new Date()).getTime();
createObjects();
var endTime = (new Date()).getTime();
var node = document.createElement("div");
var time = endTime - startTime;
node.appendChild(document.createTextNode("Creating 10000 objects in addition to " + arr.length +
" objects: " + time + "ms"));
document.body.appendChild(node);
return time;
}
Authors: Kou Man Tong, Fung Chak Fai 109
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
function makeDummies()
{
createDummyObjects(20000);
}
function runAPass()
{
makeDummies();
benchmark();
if (arr.length < 400000)
setTimeout(runAPass, 500);
}
function clicked()
{
benchmark();
setTimeout(runAPass, 500);
}
</script>
</head>
<body>
<a href="javascript: clicked()">Run Benchmark</a>
</body>
</html>
Figure 94 - Demonstration code for showing JavaScript object creation latency deteriorating with
number of reachable JavaScript Objects, under Internet Explorer 6 and 7.
<html>
<head>
<script>
var nodeCount = 0;
function createObjects()
{
var o = null;
for(var i=0;i<10000;i++)
o = new Object;
Authors: Kou Man Tong, Fung Chak Fai 110
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
}
function createOneDummy()
{
var div = document.createElement("div");
div.innerHTML = "Hello World!";
div.style.position = "absolute";
div.style.left = "-1000px";
div.style.top = "-1000px";
document.body.appendChild(div);
nodeCount++;
}
function createDummyObjects(n)
{
for(var i=0;i<n;i++)
createOneDummy();
}
function benchmark()
{
var startTime = (new Date()).getTime();
createObjects();
var endTime = (new Date()).getTime();
var node = document.createElement("div");
var time = endTime - startTime;
node.appendChild(document.createTextNode("Creating 10000 objects in addition to " + nodeCount
+ " DOM nodes: " + time + "ms"));
document.body.appendChild(node);
return time;
}
function makeDummies()
{
createDummyObjects(2000);
}
Authors: Kou Man Tong, Fung Chak Fai 111
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
function runAPass()
{
makeDummies();
benchmark();
if (nodeCount < 40000)
setTimeout(runAPass, 500);
}
function clicked()
{
benchmark();
setTimeout(runAPass, 500);
}
</script>
</head>
<body>
<a href="javascript: clicked()">Run Benchmark</a>
</body>
</html>
Figure 95 - Control experiment. Here, although the memory usage of iexplorer.exe is also
increasing progressively, JavaScript object creation latency does not deteriorate because the
number of reachable JavaScript objects is not increasing.
Authors: Kou Man Tong, Fung Chak Fai 112
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
12 Appendix D: Conversations in Dojo Interest Mailing List Regarding the Lapsed Listeners in Dojo Toolkit
Hi,
Since my server is in Hong Kong where the international links are down at
the moment, you may have difficulty reaching my server. Therefore I'm
attaching my testing code in a .zip file attachment.
Thanks.
Best Regards,
Martin Kou
From: "Kou Man Tong, Martin" <eg_kmt at stu.ust.hk>
To: <dojo-interest at dojotoolkit.org>
Cc: <cs_fcf at stu.ust.hk>
Sent: Monday, January 01, 2007 1:31 PM
Subject: [Dojo-interest] Memory leakage in dynamically created widgets?
> Hi,
>
> I've sent this question three times to the mailing list yesterday and for
> the first two times the mailing server thought I was spamming it. The
> mailing server didn't respond to my third mail, but by now I suspect it
> was silently marked as spam and dropped. So I'm sending this question
> again from a different email address.
>
> Here's my previous mail, if it is repeated, sorry about that:
>
> I'm new to Dojo Toolkit - I've just started learning it last week. I
> started learning it by writing some simple, proof-of-concept dynamic pages
> (where widgets can be created and destoryed without being refreshed - I
> haven't touched on the ajax stuff yet). I think I've done something wrong,
> and I've tried things like dojo.event.browser.clean() that popped up in
> this mailing list and Alex's December 2005 blog entries for similar
Authors: Kou Man Tong, Fung Chak Fai 113
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
> problems, but to no avail.
>
> First page I constructed with Dojo:
> http://hkpcug.homeftp.net/~francium/dojo-0.4.1-ajax/dojo_leak_dom.html
>
> Second page I constructed with Dojo:
> http://hkpcug.homeftp.net/~francium/dojo-0.4.1-ajax/dojo_leak_widget.html
>
> In both examples, clicking "Creating xxx widgets" and then "Destroy All"
> repeatedly in IE6 or IE7 would lead to the vmsizes of both browsers (as
> observed from Window's Task Manager) increase rapidly. How can I correct
> this problem? And could anyone kindly point to the reason why is it
> happening?
>
> Best regards,
> Martin Kou
>
> _______________________________________________
> Dojo FAQ: http://dojo.jot.com/FAQ
> Dojo Book: http://manual.dojotoolkit.org/DojoDotBook
> Dojo-interest at dojotoolkit.org
> http://dojotoolkit.org/mailman/listinfo/dojo-interest
Figure 96 – Original problem statement.
URL: http://dojotoolkit.org/pipermail/dojo-interest/2007-January/023056.html
Thanks for the information. It does indeed like what you say. I tested on
two browsers(IE 7.0 and Opear9.01).
Well the destroy is only cleaning up the browser but not the memory. In fact
it adds to the memeory size after cleaning the browser!.
In my Task manager I cannot find the Memory Usage History tab which would
have given more information. References on the web gives a hint as to why
this may be happening. The leakage problem is often associated with the dom
elements (the widget) being created inside a script.
The following IE specific article may give you a hint:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/IETechCol/dnwebgen/
Authors: Kou Man Tong, Fung Chak Fai 114
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
ie_leak_patterns.asp
Jayaram
Figure 97 – Third party confirmation of problem.
URL: http://dojotoolkit.org/pipermail/dojo-interest/2007-January/023132.html
In all fairness to dojo the increase is not exponential for Martin's example code.
At first 19240 K
First click 25108 K
Second Click 29960 K
Third click 35544 K
Fourth click 40540 K
Fifth click 46104 k
At Destroy 583428K
But the effect is substantial slowdown.
Figure 98 – Third party confirmation of problem.
URL: http://dojotoolkit.org/pipermail/dojo-interest/2007-January/023180.html
Hi Martin,
On Wednesday 03 January 2007 10:22 am, Martin Kou wrote:
> I've read that MSDN article quite a long while ago (I think January
> 2006? I so wished Microsoft would crash and burn upon reading that
> article for the first time), it mostly deals with memory leaks
> related to circular references in which IE's garbage collector cannot
> clean up. As far as I know, I believe such memory leaks can always be
> detected with Drip if the circular references still exist after the
> page is unloaded.
Drip tries to "force" the issue in a wrapped IE ActiveX control, but
I've observed leakage that Drip can't find (in the XHR object, for
instance).
Authors: Kou Man Tong, Fung Chak Fai 115
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
> I think the leak I'm seeing here is different. Testing the pages with
> Drip turns up with negative results. Also, as I understand, Dojo's
> event system does not store the event handler given directly under
> the subject - it stores the handler elsewhere and puts a generic
> handler function to, say, the onclick attribute. So I believe it is
> not possible for the "traditional" DOM object -> closure -> DOM
> object kind of leak to happen under Dojo, as long as I use its event
> system instead of assigning closures to DOM nodes directly.
>
> In the createOne() function of the dojo_leak_dom.html test case,
> there exists a closure -> DOM object link between "node" and
> "clickHandler", because clickHandler can refer back to node
> implicitly. Just take my word that such a reference exists even if
> the clickHandler is an empty closure, but I can provide a proof to
> you if you care.
No, I believe you. There's enough else wrong w/ the JScript heuristics
around memory that it wouldn't surprise me that the closure dehydration
code is over-broad. That, or it's a hedge against runtime deref w/
something like obj["propName"], but even that doesn't make much sense.
> Now, assume Dojo's event system does not create circular references
> with dojo.event.connect() - this assumption is reasonable because
> Dojo's event system was claimed to be designed specifically to avoid
> this problem (http://alex.dojotoolkit.org/?p=526). It can, however,
> still leak memory before a page is unloaded by doing one of the two
> things:
It should be noted that the reference is *created*, we just take pains
to clean out the dom -> js references before final page GC happens.
> 1. Keeping a reference to the DOM node, even after it is detached
> from the document tree
Shouldn't be a problem. It's the reference in the other direction that
the collector can't find (and therefore can't free). That's the
reference set we try hard to break.
Authors: Kou Man Tong, Fung Chak Fai 116
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
> 2. Keeping a reference to the clickHandler, even after its associated
> DOM node is detached from the document tree
See last note.
> These items "leak" memory because as long as I keep a reference that
> can be traced back to the DOM node, the DOM node would not be freed,
> even by a correctly implemented garbage collector (So Firefox leaks
> memory in my dojo_leak_dom.html test cases as well... it leaks slower
> than IE, but the leak is still observable).
>
> Item 2 can leak memory because the clickHandler keeps an implicit
> reference back to the DOM node. But right now I'm guessing point 1 is
> true now because, following Matthew's advice, I've tried to set node
> = null at the end of the createOne() function, thus breaking the
> clickHandler -> node link
See above note. The DOM object is a COM-bound C++ representation of the
DOM item, and it's the reference held *by that C++ structure* that the
JScript interpreter's GC chokes on. References in JS to the object
should be OK since the refcount can be decremented correctly when the
environment is cleared out.
Obviously, this is predicated on pseudo-sane behavior from IE, an
assumption that has failed me in the past, but my experimental evidence
tends to suggest that removing the dom -> js linkage is enough to
prevent leakage.
You might add a call to JScript-specific window.CollectGarbage() method
to ensure that your test page is really showing you what you think it's
showing you.
Regards
[snipped]
--
Alex Russell
Authors: Kou Man Tong, Fung Chak Fai 117
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
alex at sitepen.com A99F 8785 F491 D5FD 04D7 ACD9 4158 FFDF 2894 6876
alex at dojotoolkit.org BE03 E88D EABB 2116 CC49 8259 CF78 E242 59C3 9723
Figure 99 – First response from Alex Russell, Dojo project founder.
http://dojotoolkit.org/pipermail/dojo-interest/2007-January/023143.html
On Wednesday 03 January 2007 1:12 am, Martin Kou wrote:
> Here's a video of what happend, notice the VM size was increasing
> rapidly as I clicked create and destroy. This is incorrect behavior
> since the VM size indicates the virtual memory address space
> allocated to the browser.
>
> http://hkpcug.homeftp.net/~francium/leak_demo.swf.html
>
> (2.1MB video warning)
Just to be very clear, unless I'm seeing the video wrong, what you're
describing is increasing memory usage *in a single page*. Leakage, as
is usually defined WRT MSIE, is defined by un-recovered memory across
page loads, not an increase in memory footprint inside the same page
environment.
We may be able to do something about the later, but we've spend our time
dealing with the former to date, as it has truly debilitating
consequences for long-term usage.
Regards
[snipped]
--
Alex Russell
alex at sitepen.com A99F 8785 F491 D5FD 04D7 ACD9 4158 FFDF 2894 6876
alex at dojotoolkit.org BE03 E88D EABB 2116 CC49 8259 CF78 E242 59C3 9723
Figure 100 – Second response from Alex Russell, showing that Dojo is still focused on solving
memory leakage problems caused by circular referencing in Internet Explorer 6.
URL: http://dojotoolkit.org/pipermail/dojo-interest/2007-January/023144.html
Authors: Kou Man Tong, Fung Chak Fai 118
Final Report – Powerful AJAX Tools for Lightweight Portable Web User Interfaces
Authors: Kou Man Tong, Fung Chak Fai 119