Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 /**
6 * Memory leak hunter. Walks a tree of objects looking for DOM nodes.
7 * Usage:
8 * leakHunt({
9 * thing: thing,
10 * otherthing: otherthing
11 * });
12 */
13 function leakHunt(root) {
14 var path = [];
15 var seen = [];
17 try {
18 var output = leakHunt.inner(root, path, seen);
19 output.forEach(function(line) {
20 dump(line + '\n');
21 });
22 }
23 catch (ex) {
24 dump(ex + '\n');
25 }
26 }
28 leakHunt.inner = function LH_inner(root, path, seen) {
29 var prefix = new Array(path.length).join(' ');
31 var reply = [];
32 function log(msg) {
33 reply.push(msg);
34 }
36 var direct
37 try {
38 direct = Object.keys(root);
39 }
40 catch (ex) {
41 log(prefix + ' Error enumerating: ' + ex);
42 return reply;
43 }
45 try {
46 var index = 0;
47 for (var data of root) {
48 var prop = '' + index;
49 leakHunt.digProperty(prop, data, path, seen, direct, log);
50 index++;
51 }
52 }
53 catch (ex) { /* Ignore things that are not enumerable */ }
55 for (var prop in root) {
56 var data;
57 try {
58 data = root[prop];
59 }
60 catch (ex) {
61 log(prefix + ' ' + prop + ' = Error: ' + ex.toString().substring(0, 30));
62 continue;
63 }
65 leakHunt.digProperty(prop, data, path, seen, direct, log);
66 }
68 return reply;
69 }
71 leakHunt.hide = [ /^string$/, /^number$/, /^boolean$/, /^null/, /^undefined/ ];
73 leakHunt.noRecurse = [
74 /^string$/, /^number$/, /^boolean$/, /^null/, /^undefined/,
75 /^Window$/, /^Document$/,
76 /^XULDocument$/, /^XULElement$/,
77 /^DOMWindow$/, /^HTMLDocument$/, /^HTML.*Element$/, /^ChromeWindow$/
78 ];
80 leakHunt.digProperty = function LH_digProperty(prop, data, path, seen, direct, log) {
81 var newPath = path.slice();
82 newPath.push(prop);
83 var prefix = new Array(newPath.length).join(' ');
85 var recurse = true;
86 var message = leakHunt.getType(data);
88 if (leakHunt.matchesAnyPattern(message, leakHunt.hide)) {
89 return;
90 }
92 if (message === 'function' && direct.indexOf(prop) == -1) {
93 return;
94 }
96 if (message === 'string') {
97 var extra = data.length > 10 ? data.substring(0, 9) + '_' : data;
98 message += ' "' + extra.replace(/\n/g, "|") + '"';
99 recurse = false;
100 }
101 else if (leakHunt.matchesAnyPattern(message, leakHunt.noRecurse)) {
102 message += ' (no recurse)'
103 recurse = false;
104 }
105 else if (seen.indexOf(data) !== -1) {
106 message += ' (already seen)';
107 recurse = false;
108 }
110 if (recurse) {
111 seen.push(data);
112 var lines = leakHunt.inner(data, newPath, seen);
113 if (lines.length == 0) {
114 if (message !== 'function') {
115 log(prefix + prop + ' = ' + message + ' { }');
116 }
117 }
118 else {
119 log(prefix + prop + ' = ' + message + ' {');
120 lines.forEach(function(line) {
121 log(line);
122 });
123 log(prefix + '}');
124 }
125 }
126 else {
127 log(prefix + prop + ' = ' + message);
128 }
129 };
131 leakHunt.matchesAnyPattern = function LH_matchesAnyPattern(str, patterns) {
132 var match = false;
133 patterns.forEach(function(pattern) {
134 if (str.match(pattern)) {
135 match = true;
136 }
137 });
138 return match;
139 };
141 leakHunt.getType = function LH_getType(data) {
142 if (data === null) {
143 return 'null';
144 }
145 if (data === undefined) {
146 return 'undefined';
147 }
149 var type = typeof data;
150 if (type === 'object' || type === 'Object') {
151 type = leakHunt.getCtorName(data);
152 }
154 return type;
155 };
157 leakHunt.getCtorName = function LH_getCtorName(aObj) {
158 try {
159 if (aObj.constructor && aObj.constructor.name) {
160 return aObj.constructor.name;
161 }
162 }
163 catch (ex) {
164 return 'UnknownObject';
165 }
167 // If that fails, use Objects toString which sometimes gives something
168 // better than 'Object', and at least defaults to Object if nothing better
169 return Object.prototype.toString.call(aObj).slice(8, -1);
170 };