Stop memory leaks

I have a google chrome extension, what it does is, checking for new messages from gmail and notifying user.

The problem is, the extension tend to crash when there is no internet connection, and sometimes, the http requests fail to communicate with gmail server, they got built up over time and end up flooding the memory.

This is the link the extension

https://chrome.google.com/extensions/detail/mgdnblnolcinnndenjnollpiplgkbjcn?hl=en

this is the part of the code the problem would come from

<!DOCTYPE HTML>
<html>
<head>
<script>
var o = JSON.parse(localStorage['options']);


// Notification
var requestFailureCount = 0; // used for exponential backoff
var requestTimeout = 1000 * 5; // 5 seconds
var unreadCount = -1;

var circle = 1000 * 15;

var iconURL = chrome.extension.getURL("icon_128.png");
var lastMessageTime;

function getGmailUrl() {
    var url = "https://mail.google.com/";
    if (localStorage.domain) url += localStorage.domain + "/";
    else url += "mail/"
    return url;
}

function getFeedUrl() {
    return getGmailUrl() + "feed/atom";
}

function isGmailUrl(url) {
    // This is the Gmail we're looking for if:
    // - starts with the correct gmail url
    // - doesn't contain any other path chars
    var gmail = getGmailUrl();
    if (url.indexOf(gmail) != 0) return false;

    return url.length == gmail.length || url[gmail.length] == '?' || url[gmail.length] == '#';
}

function init() {
    startRequest();
}

function scheduleRequest() {
    window.setTimeout(startRequest, circle);
}

// ajax stuff


function startRequest() {
    getInboxCount(

    function (count, items) {
        scheduleRequest();
		//o.message has value either true or false
        if (typeof webkitNotifications != "undefined" && o.message) {
            var item;
            var newestItemTime = lastMessageTime || 0;
            while (item = items.iterateNext()) {
                var time = item.querySelector("modified").textContent;
                time = Date.parse(time.replace("T24", " 00").replace("T", " ").replace("Z", ""));
                if (lastMessageTime && time <= lastMessageTime) continue;

                if (time > newestItemTime) newestItemTime = time;

                var title = item.querySelector("title").textContent;
                var summary = item.querySelector("summary").textContent;
                var authorName = item.querySelector("author name").textContent;
                var link = item.querySelector("link").getAttribute("href");
                var content = summary;

                var item = {
                    websiteName: authorName,
                    websiteURL: undefined,
                    itemURL: link,
                    itemImage: "/icon128.png",
                    itemTitle: title,
                    itemContent: content
                };
                var notifyPageURL = getNotificationURL(item);
                var notification = webkitNotifications.createHTMLNotification(notifyPageURL);
				//o.timeout has value either true or false
                var timeout = parseInt(o.timeout, 10);
                if (isNaN(timeout)) timeout = 60;

                notification.show();
				//o.sound has value either true or false
                if (o.sound) playNotificationSound();

                setTimeout(function () {
                    notification.cancel();
                }, timeout * 1000);

                if (!lastMessageTime) break;
            }
            lastMessageTime = newestItemTime;
        }
    }, function () {
        scheduleRequest();
    });
}

function getInboxCount(onSuccess, onError) {
    var xhr = new XMLHttpRequest();
    var abortTimerId = window.setTimeout(function () {
        xhr.abort(); // synchronously calls onreadystatechange
    }, requestTimeout);

    function handleSuccess(count, items) {
        requestFailureCount = 0;
        window.clearTimeout(abortTimerId);
        if (onSuccess) onSuccess(count, items);
    }

    function handleError() {
        ++requestFailureCount;
        window.clearTimeout(abortTimerId);
        if (onError) onError();
    }

    try {
        xhr.onreadystatechange = function () {
            if (xhr.readyState != 4) return;

            if (xhr.responseXML) {
                var xmlDoc = xhr.responseXML;
                var fullCountSet = xmlDoc.evaluate("/gmail:feed/gmail:fullcount", xmlDoc, gmailNSResolver, XPathResult.ANY_TYPE, null);
                var fullCountNode = fullCountSet.iterateNext();
                if (fullCountNode) {
                    var entrySet = xmlDoc.evaluate("/gmail:feed/gmail:entry", xmlDoc, gmailNSResolver, XPathResult.ANY_TYPE, null);
                    handleSuccess(fullCountNode.textContent, entrySet);
                    return;
                } else {
                    console.error("Error: feed retrieved, but no <fullcount> node " + "found");
                }
            }

            handleError();
        }

        xhr.onerror = function (error) {
            handleError();
        }

        xhr.open("GET", getFeedUrl(), true);
        xhr.send(null);
    } catch (e) {
        console.error("exception: " + e);
        handleError();
    }
}

function gmailNSResolver(prefix) {
    if (prefix == 'gmail') {
        return 'http://purl.org/atom/ns#';
    }
}

function playNotificationSound() {
    try {
        notifyAlert.currentTime = 0;
        notifyAlert.play();
    } catch (ex) {
        console.error(ex);
    }
}

function getNotificationURL(item) {
    var query = "";
    for (var key in item) {
        var value = item[key];
        if (value) query += key + "=" + encodeURIComponent(value) + "&";
    }

    var notifyPageURL = chrome.extension.getURL("notify.html") + "?" + query;
    return notifyPageURL;
}

Date.prototype.setISO8601 = function (string) {
    var regexp = "([0-9]{4})(-([0-9]{2})(-([0-9]{2})" + "(T([0-9]{2}):([0-9]{2})(:([0-9]{2})(\\.([0-9]+))?)?" + "(Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?";
    var d = string.match(new RegExp(regexp));

    var offset = 0;
    var date = new Date(d[1], 0, 1);

    if (d[3]) {
        date.setMonth(d[3] - 1);
    }
    if (d[5]) {
        date.setDate(d[5]);
    }
    if (d[7]) {
        date.setHours(d[7]);
    }
    if (d[8]) {
        date.setMinutes(d[8]);
    }
    if (d[10]) {
        date.setSeconds(d[10]);
    }
    if (d[12]) {
        date.setMilliseconds(Number("0." + d[12]) * 1000);
    }
    if (d[14]) {
        offset = (Number(d[16]) * 60) + Number(d[17]);
        offset *= ((d[15] == '-') ? 1 : -1);
    }

    offset -= date.getTimezoneOffset();
    time = (Number(date) + (offset * 60 * 1000));
    this.setTime(Number(time));
}
</script>
</head>

<!--notification html-->
<body onload="init()">
	<audio id="notifyAlert" src="notify.mp3"></audio>
</body>


</html>

The best person to resolve this problem if at all possible, is the author: Craig Cosmo.

its meh… And I can’t yet

Okay. While I haven’t delved deeply in to this, is it worth considering adding some code to the scheduleRequest function for it to check the nature of things (failures, queue size, etc…), before adding a new request?

make sense, I’ll give it a try