1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is Mozilla Foundation.
18 : * Portions created by the Initial Developer are Copyright (C) 2007
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : * Jonas Sicking <jonas@sicking.cc> (Original Author)
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either of the GNU General Public License Version 2 or later (the "GPL"),
26 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include "nsCCUncollectableMarker.h"
39 : #include "nsIObserverService.h"
40 : #include "nsIDocShell.h"
41 : #include "nsIDocShellTreeItem.h"
42 : #include "nsServiceManagerUtils.h"
43 : #include "nsIDOMDocument.h"
44 : #include "nsIContentViewer.h"
45 : #include "nsIDocument.h"
46 : #include "nsIWindowMediator.h"
47 : #include "nsPIDOMWindow.h"
48 : #include "nsIWebNavigation.h"
49 : #include "nsISHistory.h"
50 : #include "nsISHEntry.h"
51 : #include "nsISHContainer.h"
52 : #include "nsIWindowWatcher.h"
53 : #include "mozilla/Services.h"
54 : #include "nsIXULWindow.h"
55 : #include "nsIAppShellService.h"
56 : #include "nsAppShellCID.h"
57 : #include "nsEventListenerManager.h"
58 : #include "nsContentUtils.h"
59 : #include "nsGlobalWindow.h"
60 : #include "nsJSEnvironment.h"
61 : #include "nsInProcessTabChildGlobal.h"
62 : #include "nsFrameLoader.h"
63 : #include "nsGenericElement.h"
64 : #include "xpcpublic.h"
65 :
66 : static bool sInited = 0;
67 : PRUint32 nsCCUncollectableMarker::sGeneration = 0;
68 : #ifdef MOZ_XUL
69 : #include "nsXULPrototypeCache.h"
70 : #endif
71 :
72 44816 : NS_IMPL_ISUPPORTS1(nsCCUncollectableMarker, nsIObserver)
73 :
74 : /* static */
75 : nsresult
76 1404 : nsCCUncollectableMarker::Init()
77 : {
78 1404 : if (sInited) {
79 0 : return NS_OK;
80 : }
81 :
82 2808 : nsCOMPtr<nsIObserver> marker = new nsCCUncollectableMarker;
83 1404 : NS_ENSURE_TRUE(marker, NS_ERROR_OUT_OF_MEMORY);
84 :
85 : nsCOMPtr<nsIObserverService> obs =
86 2808 : mozilla::services::GetObserverService();
87 1404 : if (!obs)
88 0 : return NS_ERROR_FAILURE;
89 :
90 : nsresult rv;
91 :
92 : // This makes the observer service hold an owning reference to the marker
93 1404 : rv = obs->AddObserver(marker, "xpcom-shutdown", false);
94 1404 : NS_ENSURE_SUCCESS(rv, rv);
95 :
96 1404 : rv = obs->AddObserver(marker, "cycle-collector-begin", false);
97 1404 : NS_ENSURE_SUCCESS(rv, rv);
98 1404 : rv = obs->AddObserver(marker, "cycle-collector-forget-skippable", false);
99 1404 : NS_ENSURE_SUCCESS(rv, rv);
100 :
101 1404 : sInited = true;
102 :
103 1404 : return NS_OK;
104 : }
105 :
106 : static void
107 0 : MarkUserData(void* aNode, nsIAtom* aKey, void* aValue, void* aData)
108 : {
109 0 : nsIDocument* d = static_cast<nsINode*>(aNode)->GetCurrentDoc();
110 0 : if (d && nsCCUncollectableMarker::InGeneration(d->GetMarkedCCGeneration())) {
111 0 : nsGenericElement::MarkUserData(aNode, aKey, aValue, aData);
112 : }
113 0 : }
114 :
115 : static void
116 0 : MarkUserDataHandler(void* aNode, nsIAtom* aKey, void* aValue, void* aData)
117 : {
118 0 : nsIDocument* d = static_cast<nsINode*>(aNode)->GetCurrentDoc();
119 0 : if (d && nsCCUncollectableMarker::InGeneration(d->GetMarkedCCGeneration())) {
120 0 : nsGenericElement::MarkUserDataHandler(aNode, aKey, aValue, aData);
121 : }
122 0 : }
123 :
124 : static void
125 0 : MarkMessageManagers()
126 : {
127 : nsCOMPtr<nsIChromeFrameMessageManager> globalMM =
128 0 : do_GetService("@mozilla.org/globalmessagemanager;1");
129 0 : if (!globalMM) {
130 : return;
131 : }
132 :
133 0 : globalMM->MarkForCC();
134 0 : PRUint32 childCount = 0;
135 0 : globalMM->GetChildCount(&childCount);
136 0 : for (PRUint32 i = 0; i < childCount; ++i) {
137 0 : nsCOMPtr<nsITreeItemFrameMessageManager> windowMM;
138 0 : globalMM->GetChildAt(i, getter_AddRefs(windowMM));
139 0 : if (!windowMM) {
140 0 : continue;
141 : }
142 0 : windowMM->MarkForCC();
143 0 : PRUint32 tabChildCount = 0;
144 0 : windowMM->GetChildCount(&tabChildCount);
145 0 : for (PRUint32 j = 0; j < tabChildCount; ++j) {
146 0 : nsCOMPtr<nsITreeItemFrameMessageManager> tabMM;
147 0 : windowMM->GetChildAt(j, getter_AddRefs(tabMM));
148 0 : if (!tabMM) {
149 0 : continue;
150 : }
151 0 : tabMM->MarkForCC();
152 : //XXX hack warning, but works, since we know that
153 : // callback data is frameloader.
154 0 : void* cb = static_cast<nsFrameMessageManager*>(tabMM.get())->
155 0 : GetCallbackData();
156 0 : nsFrameLoader* fl = static_cast<nsFrameLoader*>(cb);
157 0 : if (fl) {
158 0 : nsIDOMEventTarget* et = fl->GetTabChildGlobalAsEventTarget();
159 0 : if (!et) {
160 0 : continue;
161 : }
162 0 : static_cast<nsInProcessTabChildGlobal*>(et)->MarkForCC();
163 0 : nsEventListenerManager* elm = et->GetListenerManager(false);
164 0 : if (elm) {
165 0 : elm->UnmarkGrayJSListeners();
166 : }
167 : }
168 : }
169 : }
170 : }
171 :
172 : void
173 0 : MarkContentViewer(nsIContentViewer* aViewer, bool aCleanupJS,
174 : bool aPrepareForCC)
175 : {
176 0 : if (!aViewer) {
177 0 : return;
178 : }
179 :
180 0 : nsIDocument *doc = aViewer->GetDocument();
181 0 : if (doc &&
182 0 : doc->GetMarkedCCGeneration() != nsCCUncollectableMarker::sGeneration) {
183 0 : doc->MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration);
184 0 : if (aCleanupJS) {
185 0 : nsEventListenerManager* elm = doc->GetListenerManager(false);
186 0 : if (elm) {
187 0 : elm->UnmarkGrayJSListeners();
188 : }
189 0 : nsCOMPtr<nsIDOMEventTarget> win = do_QueryInterface(doc->GetInnerWindow());
190 0 : if (win) {
191 0 : elm = win->GetListenerManager(false);
192 0 : if (elm) {
193 0 : elm->UnmarkGrayJSListeners();
194 : }
195 0 : static_cast<nsGlobalWindow*>(win.get())->UnmarkGrayTimers();
196 : }
197 :
198 : doc->PropertyTable(DOM_USER_DATA_HANDLER)->
199 0 : EnumerateAll(MarkUserDataHandler, &nsCCUncollectableMarker::sGeneration);
200 0 : } else if (aPrepareForCC) {
201 : // Unfortunately we need to still mark user data just before running CC so
202 : // that it has the right generation.
203 : doc->PropertyTable(DOM_USER_DATA)->
204 0 : EnumerateAll(MarkUserData, &nsCCUncollectableMarker::sGeneration);
205 : }
206 : }
207 : }
208 :
209 : void MarkDocShell(nsIDocShellTreeNode* aNode, bool aCleanupJS,
210 : bool aPrepareForCC);
211 :
212 : void
213 0 : MarkSHEntry(nsISHEntry* aSHEntry, bool aCleanupJS, bool aPrepareForCC)
214 : {
215 0 : if (!aSHEntry) {
216 0 : return;
217 : }
218 :
219 0 : nsCOMPtr<nsIContentViewer> cview;
220 0 : aSHEntry->GetContentViewer(getter_AddRefs(cview));
221 0 : MarkContentViewer(cview, aCleanupJS, aPrepareForCC);
222 :
223 0 : nsCOMPtr<nsIDocShellTreeItem> child;
224 0 : PRInt32 i = 0;
225 0 : while (NS_SUCCEEDED(aSHEntry->ChildShellAt(i++, getter_AddRefs(child))) &&
226 0 : child) {
227 0 : MarkDocShell(child, aCleanupJS, aPrepareForCC);
228 : }
229 :
230 0 : nsCOMPtr<nsISHContainer> shCont = do_QueryInterface(aSHEntry);
231 : PRInt32 count;
232 0 : shCont->GetChildCount(&count);
233 0 : for (i = 0; i < count; ++i) {
234 0 : nsCOMPtr<nsISHEntry> childEntry;
235 0 : shCont->GetChildAt(i, getter_AddRefs(childEntry));
236 0 : MarkSHEntry(childEntry, aCleanupJS, aPrepareForCC);
237 : }
238 :
239 : }
240 :
241 : void
242 0 : MarkDocShell(nsIDocShellTreeNode* aNode, bool aCleanupJS, bool aPrepareForCC)
243 : {
244 0 : nsCOMPtr<nsIDocShell> shell = do_QueryInterface(aNode);
245 0 : if (!shell) {
246 : return;
247 : }
248 :
249 0 : nsCOMPtr<nsIContentViewer> cview;
250 0 : shell->GetContentViewer(getter_AddRefs(cview));
251 0 : MarkContentViewer(cview, aCleanupJS, aPrepareForCC);
252 :
253 0 : nsCOMPtr<nsIWebNavigation> webNav = do_QueryInterface(shell);
254 0 : nsCOMPtr<nsISHistory> history;
255 0 : webNav->GetSessionHistory(getter_AddRefs(history));
256 0 : if (history) {
257 : PRInt32 i, historyCount;
258 0 : history->GetCount(&historyCount);
259 0 : for (i = 0; i < historyCount; ++i) {
260 0 : nsCOMPtr<nsIHistoryEntry> historyEntry;
261 0 : history->GetEntryAtIndex(i, false, getter_AddRefs(historyEntry));
262 0 : nsCOMPtr<nsISHEntry> shEntry = do_QueryInterface(historyEntry);
263 :
264 0 : MarkSHEntry(shEntry, aCleanupJS, aPrepareForCC);
265 : }
266 : }
267 :
268 : PRInt32 i, childCount;
269 0 : aNode->GetChildCount(&childCount);
270 0 : for (i = 0; i < childCount; ++i) {
271 0 : nsCOMPtr<nsIDocShellTreeItem> child;
272 0 : aNode->GetChildAt(i, getter_AddRefs(child));
273 0 : MarkDocShell(child, aCleanupJS, aPrepareForCC);
274 : }
275 : }
276 :
277 : void
278 597 : MarkWindowList(nsISimpleEnumerator* aWindowList, bool aCleanupJS,
279 : bool aPrepareForCC)
280 : {
281 1194 : nsCOMPtr<nsISupports> iter;
282 1791 : while (NS_SUCCEEDED(aWindowList->GetNext(getter_AddRefs(iter))) &&
283 597 : iter) {
284 0 : nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(iter);
285 0 : if (window) {
286 : nsCOMPtr<nsIDocShellTreeNode> rootDocShell =
287 0 : do_QueryInterface(window->GetDocShell());
288 :
289 0 : MarkDocShell(rootDocShell, aCleanupJS, aPrepareForCC);
290 : }
291 : }
292 597 : }
293 :
294 : nsresult
295 1727 : nsCCUncollectableMarker::Observe(nsISupports* aSubject, const char* aTopic,
296 : const PRUnichar* aData)
297 : {
298 1727 : if (!strcmp(aTopic, "xpcom-shutdown")) {
299 : nsCOMPtr<nsIObserverService> obs =
300 2808 : mozilla::services::GetObserverService();
301 1404 : if (!obs)
302 0 : return NS_ERROR_FAILURE;
303 :
304 : // No need for kungFuDeathGrip here, yay observerservice!
305 1404 : obs->RemoveObserver(this, "xpcom-shutdown");
306 1404 : obs->RemoveObserver(this, "cycle-collector-begin");
307 1404 : obs->RemoveObserver(this, "cycle-collector-forget-skippable");
308 :
309 1404 : sGeneration = 0;
310 :
311 1404 : return NS_OK;
312 : }
313 :
314 323 : NS_ASSERTION(!strcmp(aTopic, "cycle-collector-begin") ||
315 : !strcmp(aTopic, "cycle-collector-forget-skippable"), "wrong topic");
316 :
317 : // JS cleanup can be slow. Do it only if there has been a GC.
318 : bool cleanupJS =
319 323 : !nsJSContext::CleanupSinceLastGC() &&
320 323 : !strcmp(aTopic, "cycle-collector-forget-skippable");
321 :
322 323 : bool prepareForCC = !strcmp(aTopic, "cycle-collector-begin");
323 :
324 :
325 : // Increase generation to effectivly unmark all current objects
326 323 : if (!++sGeneration) {
327 0 : ++sGeneration;
328 : }
329 :
330 : nsresult rv;
331 :
332 : // Iterate all toplevel windows
333 646 : nsCOMPtr<nsISimpleEnumerator> windowList;
334 : nsCOMPtr<nsIWindowMediator> med =
335 646 : do_GetService(NS_WINDOWMEDIATOR_CONTRACTID);
336 323 : if (med) {
337 323 : rv = med->GetEnumerator(nsnull, getter_AddRefs(windowList));
338 323 : NS_ENSURE_SUCCESS(rv, rv);
339 :
340 317 : MarkWindowList(windowList, cleanupJS, prepareForCC);
341 : }
342 :
343 : nsCOMPtr<nsIWindowWatcher> ww =
344 634 : do_GetService(NS_WINDOWWATCHER_CONTRACTID);
345 317 : if (ww) {
346 317 : rv = ww->GetWindowEnumerator(getter_AddRefs(windowList));
347 317 : NS_ENSURE_SUCCESS(rv, rv);
348 :
349 280 : MarkWindowList(windowList, cleanupJS, prepareForCC);
350 : }
351 :
352 : nsCOMPtr<nsIAppShellService> appShell =
353 560 : do_GetService(NS_APPSHELLSERVICE_CONTRACTID);
354 280 : if (appShell) {
355 560 : nsCOMPtr<nsIXULWindow> hw;
356 280 : appShell->GetHiddenWindow(getter_AddRefs(hw));
357 280 : if (hw) {
358 0 : nsCOMPtr<nsIDocShell> shell;
359 0 : hw->GetDocShell(getter_AddRefs(shell));
360 0 : nsCOMPtr<nsIDocShellTreeNode> shellTreeNode = do_QueryInterface(shell);
361 0 : MarkDocShell(shellTreeNode, cleanupJS, prepareForCC);
362 : }
363 : }
364 :
365 280 : if (cleanupJS) {
366 0 : nsContentUtils::UnmarkGrayJSListenersInCCGenerationDocuments(sGeneration);
367 0 : MarkMessageManagers();
368 0 : xpc_UnmarkSkippableJSHolders();
369 : }
370 :
371 : #ifdef MOZ_XUL
372 280 : nsXULPrototypeCache* xulCache = nsXULPrototypeCache::GetInstance();
373 280 : if (xulCache) {
374 280 : xulCache->MarkInCCGeneration(sGeneration);
375 : }
376 : #endif
377 :
378 280 : return NS_OK;
379 : }
380 :
|