SCENARIO
I'm using the windows gadget platform with this gadget:
http://win7gadgets.com/pc-system/sushis_driveinfo.html
PROBLEM
The gadget has a memory leak, If I keep running this gadget +24h. it can increase the RAM consumption up to 1 GB, while other similar gadgets does not procude this, so I discarded that this is a sidebar.exe memory management, not, is an script bug.
When more time is running the gadget, more unresponsiveness becomes the gadget (on click).
My knowledges about JavaScript are NULL, but anyways I can understand the syntax and try to understand what the developer is doing in this code, I think that the problem is when managging the image objects, but at my point of view those objets seems to be properly disposed after each operation.
QUESTION
This is the gadget source.
Someone could help me to discover and fix what is causing the memory leak in this gadget?
sushi_driveinfo.html (main window):
<html>
<head>
<title>Drive Info</title>
<style>
body { margin: 0; padding: 0; width: 156px; height: 200px; background-image: url(images\canvas.png); color: #ffffff; font-family: 'Segoe UI'; }
#targets { position: absolute; top: 0; left: 0; }
.target { position: absolute; width: 156px; height: 48; left: 0; cursor: hand; }
</style>
<script type="text/javascript">
var lst = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
var timeout = null;
var drives = new Array(26);
var drvchk = new Array(26);
var drvspc = new Array(26);
var vizchg = false;
var current_y = 0;
var background,theme,remove,local,network,media,show_pc,show_net;
var item_height=48;
var icon_offset=20;
var text_offset=72;
var meter_offset=24;
function convertBytes(b)
{
var i = 0, u = Array(' MB', ' GB', ' TB');
while (b >= 1024 && (b /= 1024) >= 1) i++;
return (Math.round(b * 100) / 100) + u[i];
}
function openDrive()
{
var d = window.event.srcElement.getAttribute('drive');
System.Shell.execute(d + ':\\');
return;
}
function openNetwork()
{
System.Shell.execute("Explorer", "/N,::{F02C1A0D-BE21-4350-88B0-7367FC96EF3C}");
return;
}
function openComputer()
{
System.Shell.execute("Explorer", "/N,::{20D04FE0-3AEA-1069-A2D8-08002B30309D}");
return;
}
function recheckDrives() {
for(var i = 0; i < 26; i++)
{
if (!drives[i]) {
drives[i] = System.Shell.drive(lst.charAt(i));
if (drives[i]) { vizchg = true; drvchk[i] = true; }
} else {
if (drives[i].isReady != drvchk[i]) { drvchk[i] = !drvchk[i]; vizchg = true; }
if (drives[i].isReady && drives[i].freeSpace != drvspc[i]) { drvspc[i] = drives[i].freeSpace; vizchg = true; }
}
}
}
function calcHeight(h) {
var y=0;
if(show_pc==2) y+=h;
if(show_net==2) y+=h;
for(var i=0;i<26;i++)
if(isDriveVisible(i)) y+=h;
return y;
}
function isDriveVisible(i) {
if(drvchk[i]) {
if (drives[i].driveType == 2 && remove == 1) ;
else if (drives[i].driveType == 3 && local == 1) ;
else if (drives[i].driveType == 4 && network == 1) ;
else if (drives[i].driveType == 5 && media == 1) ;
else if (drives[i].driveType == 1 || drives[i].driveType == 6) ;
else
return true;
}
return false;
}
function paintPC() {
if (show_pc == 2) {
canvas.addImageObject('images/backgrounds/background' + background + 's.png', 0, current_y);
var di=canvas.addImageObject('images/drives/pc'+ theme +'.png', icon_offset, current_y);
di.width*=0.8;
di.height*=0.8;
canvas.addTextObject('Computer', 'Segoe UI', 11, 'white', text_offset, current_y + 5);
var b = document.createElement('DIV');
b.className = 'target';
b.style.posTop = current_y;
b.onclick = openComputer;
targets.appendChild(b);
current_y+=item_height;
}
return;
}
function paintNET() {
if (show_net == 2) {
canvas.addImageObject('images/backgrounds/background' + background + 's.png', 0, current_y);
var di=canvas.addImageObject('images/drives/net'+ theme +'.png', icon_offset, current_y);
di.width*=0.8;
di.height*=0.8;
canvas.addTextObject('Network', 'Segoe UI', 11, 'white', text_offset, current_y + 5);
var b = document.createElement('DIV');
b.className = 'target';
b.style.posTop = current_y;
b.onclick = openNetwork;
targets.appendChild(b);
current_y+=item_height;
}
return;
}
function paintGadget()
{
try {
recheckDrives();
if (!vizchg) return;
var total_height=calcHeight(item_height);
System.Gadget.beginTransition();
document.body.style.height=total_height;
canvas.style.height=total_height;
canvas.removeObjects();
targets.innerHtml = '';
current_y = 0;
paintPC();
paintNET();
for(i = 0; i < 26; i++)
{
if(isDriveVisible(i)) {
if (drives[i].freeSpace != 0) {
canvas.addImageObject('images/backgrounds/background' + background + '.png', 0, current_y);
var f = Math.round(drives[i].freeSpace / drives[i].totalSize * 100);
var u = (100 - f);
canvas.addTextObject(convertBytes(drives[i].freeSpace) + ' / ' + f + '%', 'Segoe UI', 10, 'white', text_offset, current_y + 17);
var m = canvas.addImageObject('images/meter' + (u < 90 ? 'blue': (u < 98 ? 'orange': 'red')) + '.png', meter_offset, current_y + 34);
m.width = Math.floor((u * 128 / 100));
m.left = 24 - Math.floor(((128 - m.width) / 2));
} else {
canvas.addImageObject('images/backgrounds/background' + background + 's.png', 0, current_y);
canvas.addTextObject(convertBytes(drives[i].totalSize), 'Segoe UI', 10, 'white', text_offset, current_y + 17);
}
var di=canvas.addImageObject('images/drives/drive' + drives[i].driveType + theme + '.png', icon_offset, current_y-5);
di.width*=0.8;
di.height*=0.8;
canvas.addTextObject(drives[i].volumeLabel + ' (' + drives[i].driveLetter + ':)', 'Segoe UI', 11, 'white', text_offset, current_y + 5);
var o = document.createElement('DIV');
o.className = 'target';
o.style.posTop = current_y;
o.setAttribute('drive', drives[i].driveLetter);
o.onclick = openDrive;
targets.appendChild(o);
current_y += item_height;
}
System.Gadget.endTransition(System.Gadget.TransitionType.morph,0.1);
window.setTimeout(fixCanvasBackground, 600);
}
} finally {
vizchg = false;
return;
}
}
function fixCanvasBackground() {
canvas.src = canvas.src;
}
function initDrives()
{
for(var i = 0; i < 26; i++) {
drives[i] = System.Shell.drive(lst.charAt(i));
if (drives[i] && drives[i].isReady)
{ drvchk[i] = true ; drvspc[i] = drives[i].freeSpace; }
else { drvchk[i] = false; }
}
return;
}
function onShowSettings() {
window.clearInterval(timeout);
System.Gadget.beginTransition();
window.setTimeout(endTransitionFast, 400);
}
function onSettingsClosed() {
readSettings();
timeout=window.setInterval(paintGadget, 2500);
vizchg=true;
paintGadget();
}
function endTransitionFast() {
System.Gadget.endTransition(System.Gadget.TransitionType.morph, 0.1);
fixCanvasBackground();
}
function readSettings() {
background=System.Gadget.Settings.read("background");
if(background==0) { background=2; System.Gadget.Settings.write("background",2); }
theme=System.Gadget.Settings.read("theme");
if(theme==0) { theme=1; System.Gadget.Settings.write("theme",1); }
show_pc=System.Gadget.Settings.read("showpc");
if(show_pc==0) { show_pc=1; System.Gadget.Settings.write("showpc",1); }
show_net=System.Gadget.Settings.read("shownet");
if(show_net==0) { show_net=1; System.Gadget.Settings.write("shownet",1); }
local=System.Gadget.Settings.read("local");
if(local==0) { local=2; System.Gadget.Settings.write("local",2); }
media=System.Gadget.Settings.read("media");
if(media==0) { media=2; System.Gadget.Settings.write("media",2); }
network=System.Gadget.Settings.read("network");
if(network==0) { network=2; System.Gadget.Settings.write("network",2); }
remove=System.Gadget.Settings.read("remove");
if(remove==0) { remove=2; System.Gadget.Settings.write("remove",2); }
}
function onLoad()
{
System.Gadget.settingsUI = "settings.html";
System.Gadget.onSettingsClosed = onSettingsClosed;
System.Gadget.onShowSettings = onShowSettings;
readSettings();
initDrives();
timeout = window.setInterval(paintGadget, 2500);
vizchg = true;
paintGadget();
return;
}
</script>
</head>
<body onload="onLoad()">
<div id="targets"></div>
<g:background id="canvas" src="images/canvas.png" style="position: absolute; top: 0; left: 0; width: 156; height: 200; z-index: -999;" opacity="0" />
</body>
</html>
settings.html (settings window):
<html>
<head>
<style>
body { width: 250px; height: 800px; padding: 0px; margin: 0px; font-family: Tahoma; }
body,p,div,span,td { font-size: 9pt; }
label { font-weight: bold; }
input,select { font: Arial; font-size: 9pt; }
table { width: 100%; }
</style>
<script>
var background, maxBackgrounds = 3, theme = 1, maxThemes = 7;
function updateBackground()
{
var x = 84, y = 47, m;
canvas.removeObjects();
canvas.addImageObject('images/backgrounds/background' + background + '.png', x, y);
m = canvas.addImageObject('images/meterblue.png', x + 24, y + 34);
m.width = (0.25 * 128);
m.left = x + 24 - ((128 - m.width) / 2);
canvas.addImageObject('images/drives/drive3' + theme + '.png', x, y);
canvas.addTextObject('Vista (C:)', 'Segoe UI', 11, 'white', x + 58, y + 5);
canvas.addTextObject('40GB / 75%', 'Segoe UI', 10, 'white', x + 58, y + 17);
//y -= 20;
//canvas.addImageObject('images/backgrounds/background' + background + '.png', x, y);
//m = canvas.addImageObject('images/meterorange.png', x + 24, y + 34);
//m.width = (0.937 * 128);
//m.left = x + 24 - ((128 - m.width) / 2);
//canvas.addImageObject('images/drives/drive3.png', x, y);
//canvas.addTextObject('Apps (D:)', 'Segoe UI', 11, 'white', x + 58, y + 5);
//canvas.addTextObject('10GB / 6.3%', 'Segoe UI', 10, 'white', x + 58, y + 17);
canvas.addImageObject('images/drives/drive3' + theme + '.png', x-85, y+130);
canvas.addImageObject('images/drives/drive2' + theme + '.png', x-85, y+172);
canvas.addImageObject('images/drives/drive4' + theme + '.png', x-85, y+215);
canvas.addImageObject('images/drives/drive5' + theme + '.png', x-85, y+258);
}
function onBackground()
{
var e = window.event, o = e.srcElement, b = o.getAttribute('base');
o.src = 'images/settings/' + b + (e.type == 'mouseover' || e.type == 'mouseup' ? 'hover': (e.type == 'mousedown' ? 'pressed': '')) + '.png';
if (e.type == 'mouseup')
{
if (b == 'next') background++; else background--;
if (background < 1) background = maxBackgrounds;
if (background > maxBackgrounds) background = 1;
updateBackground();
}
}
function onTheme()
{
var e = window.event, o = e.srcElement, b = o.getAttribute('base');
o.src = 'images/settings/' + b + (e.type == 'mouseover' || e.type == 'mouseup' ? 'hover': (e.type == 'mousedown' ? 'pressed': '')) + '.png';
if (e.type == 'mouseup')
{
if (b == 'next') theme++; else theme--;
if (theme < 1) theme = maxThemes;
if (theme > maxThemes) theme = 1;
updateBackground();
}
}
function onClose(event)
{
if (event.closeAction == event.Action.commit)
{
System.Gadget.Settings.write("background", background);
System.Gadget.Settings.write("theme", theme);
System.Gadget.Settings.write("showpc", document.boxes.mypc.checked ? 2 : 1);
System.Gadget.Settings.write("shownet", document.boxes.netw.checked ? 2 : 1);
System.Gadget.Settings.write("remove", document.boxes.remove.checked ? 2 : 1);
System.Gadget.Settings.write("local", document.boxes.local.checked ? 2 : 1);
System.Gadget.Settings.write("network", document.boxes.network.checked ? 2 : 1);
System.Gadget.Settings.write("media", document.boxes.media.checked ? 2 : 1);
}
event.cancel = false;
// System.Gadget.beginTransition();
// window.setTimeout(endtransit, 400);
}
/* function endtransit() {
System.Gadget.endTransition(System.Gadget.TransitionType.morph, 0.1);
}*/
function onLoad()
{
var box;
System.Gadget.onSettingsClosing = onClose;
background = System.Gadget.Settings.read("background");
if (background == 0) background = 2;
theme = System.Gadget.Settings.read("theme");
if (theme == 0) theme = 1;
System.Gadget.Settings.read("remove") == 2 ? document.boxes.remove.checked = true : false;
System.Gadget.Settings.read("local") == 2 ? document.boxes.local.checked = true : false;
System.Gadget.Settings.read("network") == 2 ? document.boxes.network.checked = true : false;
System.Gadget.Settings.read("media") == 2 ? document.boxes.media.checked = true : false;
System.Gadget.Settings.read("showpc") == 2 ? document.boxes.mypc.checked = true : false;
System.Gadget.Settings.read("shownet") == 2 ? document.boxes.netw.checked = true : false;
updateBackground();
}
</script>
</head>
<body onload="onLoad()">
<g:background id="canvas" src="images/settings/desktop.png" style="position: absolute; left: 1; top: 1; z-index: -999;" />
<div style="position: absolute; left: 0; top: 147px;">
<table cellspacing="0" cellpadding="0">
<tr>
<td style="width: 33%; padding-right: 10px;" align="right"><img src="images/settings/previous.png" base="previous" style="cursor: hand;" onmouseover="onBackground();" onmouseout="onBackground();" onmousedown="onBackground();" onmouseup="onBackground();" /></td>
<td style="width: 33%;" align="center"><label>Backgrounds</label></td>
<td style="width: 33%; padding-left: 10px;" align="left"><img src="images/settings/next.png" base="next" style="cursor: hand;" onmouseover="onBackground();" onmouseout="onBackground();" onmousedown="onBackground();" onmouseup="onBackground();" /></td>
</tr>
<tr>
<td style="width: 33%; padding-right: 10px;" align="right"><img src="images/settings/previous.png" base="previous" style="cursor: hand;" onmouseover="onTheme();" onmouseout="onTheme();" onmousedown="onTheme();" onmouseup="onTheme();" /></td>
<td style="width: 33%;" align="center"><label>Icon Theme</label></td>
<td style="width: 33%; padding-left: 10px;" align="left"><img src="images/settings/next.png" base="next" style="cursor: hand;" onmouseover="onTheme();" onmouseout="onTheme();" onmousedown="onTheme();" onmouseup="onTheme();" /></td>
</tr>
</table>
<table cellspacing="0" cellpadding="0" style="margin-top: 15px;margin-left:60px;">
<tr><td>
<form name="boxes">
<input type="checkbox" name="local">
<font style="font-size: 8pt;">Local Drives</font><p>
<input type="checkbox" name="remove">
<font style="font-size: 8pt;">Removable Drives</font><p>
<input type="checkbox" name="network">
<font style="font-size: 8pt;">Network Drives</font><p>
<input type="checkbox" name="media">
<font style="font-size: 8pt;">Media Drives</font><p>
<input type="checkbox" name="mypc">
<font style="font-size: 8pt;">My Computer link</font><br>
<input type="checkbox" name="netw">
<font style="font-size: 8pt;">Network Link</font>
</form>
</td></tr>
</table>
</div>
</body>
</html>
UPDATE:
Here is the full gadget source if it helps: