]>
Commit | Line | Data |
---|---|---|
6527f429 DM |
1 | /**\r |
2 | * @private\r | |
3 | * Garbage collector for Ext.dom.Element instances. Automatically cleans up Elements\r | |
4 | * that are no longer in the dom, but were not properly destroyed using\r | |
5 | * {@link Ext.dom.Element#destroy destroy()}. Recommended practice is for Components to\r | |
6 | * clean up their own elements, but the GarbageCollector runs on regularly scheduled\r | |
7 | * intervals to attempt to clean up orphaned Elements that may have slipped through the cracks.\r | |
8 | */\r | |
9 | Ext.define('Ext.dom.GarbageCollector', {\r | |
10 | singleton: true,\r | |
11 | \r | |
12 | /**\r | |
13 | * @property {Number}\r | |
14 | * The interval at which to run Element garbage collection. Set this property directly\r | |
15 | * to tune the interval.\r | |
16 | *\r | |
17 | * Ext.dom.GarbageCollector.interval = 60000; // run garbage collection every one minute\r | |
18 | */\r | |
19 | interval: 30000,\r | |
20 | \r | |
21 | constructor: function() {\r | |
22 | var me = this;\r | |
23 | me.collect = Ext.Function.bind(me.collect, me);\r | |
24 | me.lastTime = Ext.now();\r | |
25 | me.resume();\r | |
26 | },\r | |
27 | \r | |
28 | /**\r | |
29 | * Collects orphaned Ext.dom.Elements by removing their listeners and evicting them\r | |
30 | * from the cache. Runs on a regularly scheduled {@link #interval} but can be called\r | |
31 | * directly to force garbage collection.\r | |
32 | * @return {String[]} An array containing the IDs of the elements that were garbage\r | |
33 | * collected, prefixed by their tag names. Only applies in dev mode. Returns nothing\r | |
34 | * in a production build.\r | |
35 | */\r | |
36 | collect: function() {\r | |
37 | var me = this,\r | |
38 | cache = Ext.cache,\r | |
39 | eid, dom, el, t, isGarbage, tagName;\r | |
40 | \r | |
41 | //<debug>\r | |
42 | var collectedIds = [];\r | |
43 | //</debug>\r | |
44 | \r | |
45 | \r | |
46 | for (eid in cache) {\r | |
47 | if (!cache.hasOwnProperty(eid)) {\r | |
48 | continue;\r | |
49 | }\r | |
50 | \r | |
51 | el = cache[eid];\r | |
52 | \r | |
53 | if (el.skipGarbageCollection) {\r | |
54 | continue;\r | |
55 | }\r | |
56 | \r | |
57 | dom = el.dom;\r | |
58 | \r | |
59 | //<debug>\r | |
60 | // Should always have a DOM node\r | |
61 | if (!dom) {\r | |
62 | Ext.raise('Missing DOM node in element garbage collection: ' + eid);\r | |
63 | }\r | |
64 | //</debug>\r | |
65 | \r | |
66 | try {\r | |
67 | // In IE, accessing any properties of the window object of an orphaned iframe\r | |
68 | // can result in a "Permission Denied" error. The same error also occurs\r | |
69 | // when accessing any properties of orphaned documentElement or body inside\r | |
70 | // of an iframe (documentElement and body become orphaned when the iframe\r | |
71 | // contentWindow is unloaded)\r | |
72 | isGarbage = Ext.isGarbage(dom);\r | |
73 | } catch (e) {\r | |
74 | // if an error was thrown in isGarbage it is most likely because we are\r | |
75 | // dealing with an inaccessible window or documentElement inside an orphaned\r | |
76 | // iframe in IE. In this case we can't do anything except remove the\r | |
77 | // cache entry.\r | |
78 | delete cache[eid];\r | |
79 | //<debug>\r | |
80 | collectedIds.push('#' + el.id);\r | |
81 | //</debug>\r | |
82 | continue;\r | |
83 | }\r | |
84 | \r | |
85 | if (isGarbage) {\r | |
86 | if (el && el.dom) {\r | |
87 | //<debug>\r | |
88 | tagName = el.dom.tagName;\r | |
89 | //</debug>\r | |
90 | el.collect();\r | |
91 | //<debug>\r | |
92 | collectedIds.push((tagName ? tagName : '') + '#' + el.id);\r | |
93 | //</debug>\r | |
94 | }\r | |
95 | }\r | |
96 | }\r | |
97 | //<feature legacyBrowser>\r | |
98 | // Cleanup IE Object leaks\r | |
99 | if (Ext.isIE9m) {\r | |
100 | t = {};\r | |
101 | for (eid in cache) {\r | |
102 | if (cache.hasOwnProperty(eid)) {\r | |
103 | t[eid] = cache[eid];\r | |
104 | }\r | |
105 | }\r | |
106 | Ext.cache = Ext.dom.Element.cache = t;\r | |
107 | }\r | |
108 | //</feature>\r | |
109 | \r | |
110 | me.lastTime = Ext.now();\r | |
111 | \r | |
112 | //<debug>\r | |
113 | return collectedIds;\r | |
114 | //</debug>\r | |
115 | },\r | |
116 | \r | |
117 | /**\r | |
118 | * Pauses the timer and stops garbage collection\r | |
119 | */\r | |
120 | pause: function() {\r | |
121 | clearTimeout(this.timerId);\r | |
122 | },\r | |
123 | \r | |
124 | /**\r | |
125 | * Resumes garbage collection at the specified {@link #interval}\r | |
126 | */\r | |
127 | resume: function() {\r | |
128 | var me = this,\r | |
129 | lastTime = me.lastTime;\r | |
130 | \r | |
131 | if (Ext.enableGarbageCollector && (Ext.now() - lastTime > me.interval)) {\r | |
132 | me.collect();\r | |
133 | }\r | |
134 | \r | |
135 | me.timerId = Ext.interval(me.collect, me.interval);\r | |
136 | }\r | |
137 | }); |