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 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsCCUncollectableMarker.h"
7 #include "nsIObserverService.h"
8 #include "nsIDocShell.h"
9 #include "nsServiceManagerUtils.h"
10 #include "nsIContentViewer.h"
11 #include "nsIDocument.h"
12 #include "XULDocument.h"
13 #include "nsIWindowMediator.h"
14 #include "nsPIDOMWindow.h"
15 #include "nsIWebNavigation.h"
16 #include "nsISHistory.h"
17 #include "nsISHEntry.h"
18 #include "nsISHContainer.h"
19 #include "nsIWindowWatcher.h"
20 #include "mozilla/Services.h"
21 #include "nsIXULWindow.h"
22 #include "nsIAppShellService.h"
23 #include "nsAppShellCID.h"
24 #include "nsContentUtils.h"
25 #include "nsGlobalWindow.h"
26 #include "nsJSEnvironment.h"
27 #include "nsInProcessTabChildGlobal.h"
28 #include "nsFrameLoader.h"
29 #include "mozilla/EventListenerManager.h"
30 #include "mozilla/dom/Element.h"
31 #include "xpcpublic.h"
32 #include "nsObserverService.h"
33 #include "nsFocusManager.h"
35 using namespace mozilla;
36 using namespace mozilla::dom;
38 static bool sInited = 0;
39 uint32_t nsCCUncollectableMarker::sGeneration = 0;
40 #ifdef MOZ_XUL
41 #include "nsXULPrototypeCache.h"
42 #endif
44 NS_IMPL_ISUPPORTS(nsCCUncollectableMarker, nsIObserver)
46 /* static */
47 nsresult
48 nsCCUncollectableMarker::Init()
49 {
50 if (sInited) {
51 return NS_OK;
52 }
54 nsCOMPtr<nsIObserver> marker = new nsCCUncollectableMarker;
55 NS_ENSURE_TRUE(marker, NS_ERROR_OUT_OF_MEMORY);
57 nsCOMPtr<nsIObserverService> obs =
58 mozilla::services::GetObserverService();
59 if (!obs)
60 return NS_ERROR_FAILURE;
62 nsresult rv;
64 // This makes the observer service hold an owning reference to the marker
65 rv = obs->AddObserver(marker, "xpcom-shutdown", false);
66 NS_ENSURE_SUCCESS(rv, rv);
68 rv = obs->AddObserver(marker, "cycle-collector-begin", false);
69 NS_ENSURE_SUCCESS(rv, rv);
70 rv = obs->AddObserver(marker, "cycle-collector-forget-skippable", false);
71 NS_ENSURE_SUCCESS(rv, rv);
73 sInited = true;
75 return NS_OK;
76 }
78 static void
79 MarkUserData(void* aNode, nsIAtom* aKey, void* aValue, void* aData)
80 {
81 nsIDocument* d = static_cast<nsINode*>(aNode)->GetCurrentDoc();
82 if (d && nsCCUncollectableMarker::InGeneration(d->GetMarkedCCGeneration())) {
83 Element::MarkUserData(aNode, aKey, aValue, aData);
84 }
85 }
87 static void
88 MarkUserDataHandler(void* aNode, nsIAtom* aKey, void* aValue, void* aData)
89 {
90 nsIDocument* d = static_cast<nsINode*>(aNode)->GetCurrentDoc();
91 if (d && nsCCUncollectableMarker::InGeneration(d->GetMarkedCCGeneration())) {
92 Element::MarkUserDataHandler(aNode, aKey, aValue, aData);
93 }
94 }
96 static void
97 MarkMessageManagers()
98 {
99 // The global message manager only exists in the root process.
100 if (XRE_GetProcessType() != GeckoProcessType_Default) {
101 return;
102 }
103 nsCOMPtr<nsIMessageBroadcaster> strongGlobalMM =
104 do_GetService("@mozilla.org/globalmessagemanager;1");
105 if (!strongGlobalMM) {
106 return;
107 }
108 nsIMessageBroadcaster* globalMM = strongGlobalMM;
109 strongGlobalMM = nullptr;
111 globalMM->MarkForCC();
112 uint32_t childCount = 0;
113 globalMM->GetChildCount(&childCount);
114 for (uint32_t i = 0; i < childCount; ++i) {
115 nsCOMPtr<nsIMessageListenerManager> childMM;
116 globalMM->GetChildAt(i, getter_AddRefs(childMM));
117 if (!childMM) {
118 continue;
119 }
120 nsCOMPtr<nsIMessageBroadcaster> strongWindowMM = do_QueryInterface(childMM);
121 nsIMessageBroadcaster* windowMM = strongWindowMM;
122 childMM = nullptr;
123 strongWindowMM = nullptr;
124 windowMM->MarkForCC();
125 uint32_t tabChildCount = 0;
126 windowMM->GetChildCount(&tabChildCount);
127 for (uint32_t j = 0; j < tabChildCount; ++j) {
128 nsCOMPtr<nsIMessageListenerManager> childMM;
129 windowMM->GetChildAt(j, getter_AddRefs(childMM));
130 if (!childMM) {
131 continue;
132 }
133 nsCOMPtr<nsIMessageSender> strongTabMM = do_QueryInterface(childMM);
134 nsIMessageSender* tabMM = strongTabMM;
135 childMM = nullptr;
136 strongTabMM = nullptr;
137 tabMM->MarkForCC();
138 //XXX hack warning, but works, since we know that
139 // callback is frameloader.
140 mozilla::dom::ipc::MessageManagerCallback* cb =
141 static_cast<nsFrameMessageManager*>(tabMM)->GetCallback();
142 if (cb) {
143 nsFrameLoader* fl = static_cast<nsFrameLoader*>(cb);
144 EventTarget* et = fl->GetTabChildGlobalAsEventTarget();
145 if (!et) {
146 continue;
147 }
148 static_cast<nsInProcessTabChildGlobal*>(et)->MarkForCC();
149 EventListenerManager* elm = et->GetExistingListenerManager();
150 if (elm) {
151 elm->MarkForCC();
152 }
153 }
154 }
155 }
156 if (nsFrameMessageManager::sParentProcessManager) {
157 nsFrameMessageManager::sParentProcessManager->MarkForCC();
158 uint32_t childCount = 0;
159 nsFrameMessageManager::sParentProcessManager->GetChildCount(&childCount);
160 for (uint32_t i = 0; i < childCount; ++i) {
161 nsCOMPtr<nsIMessageListenerManager> childMM;
162 nsFrameMessageManager::sParentProcessManager->
163 GetChildAt(i, getter_AddRefs(childMM));
164 if (!childMM) {
165 continue;
166 }
167 nsIMessageListenerManager* child = childMM;
168 childMM = nullptr;
169 child->MarkForCC();
170 }
171 }
172 if (nsFrameMessageManager::sSameProcessParentManager) {
173 nsFrameMessageManager::sSameProcessParentManager->MarkForCC();
174 }
175 if (nsFrameMessageManager::sChildProcessManager) {
176 nsFrameMessageManager::sChildProcessManager->MarkForCC();
177 }
178 }
180 void
181 MarkContentViewer(nsIContentViewer* aViewer, bool aCleanupJS,
182 bool aPrepareForCC)
183 {
184 if (!aViewer) {
185 return;
186 }
188 nsIDocument *doc = aViewer->GetDocument();
189 if (doc &&
190 doc->GetMarkedCCGeneration() != nsCCUncollectableMarker::sGeneration) {
191 doc->MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration);
192 if (aCleanupJS) {
193 EventListenerManager* elm = doc->GetExistingListenerManager();
194 if (elm) {
195 elm->MarkForCC();
196 }
197 nsCOMPtr<EventTarget> win = do_QueryInterface(doc->GetInnerWindow());
198 if (win) {
199 elm = win->GetExistingListenerManager();
200 if (elm) {
201 elm->MarkForCC();
202 }
203 static_cast<nsGlobalWindow*>(win.get())->UnmarkGrayTimers();
204 }
206 doc->PropertyTable(DOM_USER_DATA_HANDLER)->
207 EnumerateAll(MarkUserDataHandler, &nsCCUncollectableMarker::sGeneration);
208 } else if (aPrepareForCC) {
209 // Unfortunately we need to still mark user data just before running CC so
210 // that it has the right generation.
211 doc->PropertyTable(DOM_USER_DATA)->
212 EnumerateAll(MarkUserData, &nsCCUncollectableMarker::sGeneration);
213 }
214 }
215 if (doc) {
216 nsPIDOMWindow* inner = doc->GetInnerWindow();
217 if (inner) {
218 inner->MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration);
219 }
220 nsPIDOMWindow* outer = doc->GetWindow();
221 if (outer) {
222 outer->MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration);
223 }
224 }
225 }
227 void MarkDocShell(nsIDocShellTreeItem* aNode, bool aCleanupJS,
228 bool aPrepareForCC);
230 void
231 MarkSHEntry(nsISHEntry* aSHEntry, bool aCleanupJS, bool aPrepareForCC)
232 {
233 if (!aSHEntry) {
234 return;
235 }
237 nsCOMPtr<nsIContentViewer> cview;
238 aSHEntry->GetContentViewer(getter_AddRefs(cview));
239 MarkContentViewer(cview, aCleanupJS, aPrepareForCC);
241 nsCOMPtr<nsIDocShellTreeItem> child;
242 int32_t i = 0;
243 while (NS_SUCCEEDED(aSHEntry->ChildShellAt(i++, getter_AddRefs(child))) &&
244 child) {
245 MarkDocShell(child, aCleanupJS, aPrepareForCC);
246 }
248 nsCOMPtr<nsISHContainer> shCont = do_QueryInterface(aSHEntry);
249 int32_t count;
250 shCont->GetChildCount(&count);
251 for (i = 0; i < count; ++i) {
252 nsCOMPtr<nsISHEntry> childEntry;
253 shCont->GetChildAt(i, getter_AddRefs(childEntry));
254 MarkSHEntry(childEntry, aCleanupJS, aPrepareForCC);
255 }
257 }
259 void
260 MarkDocShell(nsIDocShellTreeItem* aNode, bool aCleanupJS, bool aPrepareForCC)
261 {
262 nsCOMPtr<nsIDocShell> shell = do_QueryInterface(aNode);
263 if (!shell) {
264 return;
265 }
267 nsCOMPtr<nsIContentViewer> cview;
268 shell->GetContentViewer(getter_AddRefs(cview));
269 MarkContentViewer(cview, aCleanupJS, aPrepareForCC);
271 nsCOMPtr<nsIWebNavigation> webNav = do_QueryInterface(shell);
272 nsCOMPtr<nsISHistory> history;
273 webNav->GetSessionHistory(getter_AddRefs(history));
274 if (history) {
275 int32_t i, historyCount;
276 history->GetCount(&historyCount);
277 for (i = 0; i < historyCount; ++i) {
278 nsCOMPtr<nsISHEntry> shEntry;
279 history->GetEntryAtIndex(i, false, getter_AddRefs(shEntry));
281 MarkSHEntry(shEntry, aCleanupJS, aPrepareForCC);
282 }
283 }
285 int32_t i, childCount;
286 aNode->GetChildCount(&childCount);
287 for (i = 0; i < childCount; ++i) {
288 nsCOMPtr<nsIDocShellTreeItem> child;
289 aNode->GetChildAt(i, getter_AddRefs(child));
290 MarkDocShell(child, aCleanupJS, aPrepareForCC);
291 }
292 }
294 void
295 MarkWindowList(nsISimpleEnumerator* aWindowList, bool aCleanupJS,
296 bool aPrepareForCC)
297 {
298 nsCOMPtr<nsISupports> iter;
299 while (NS_SUCCEEDED(aWindowList->GetNext(getter_AddRefs(iter))) &&
300 iter) {
301 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(iter);
302 if (window) {
303 nsCOMPtr<nsIDocShell> rootDocShell = window->GetDocShell();
305 MarkDocShell(rootDocShell, aCleanupJS, aPrepareForCC);
306 }
307 }
308 }
310 nsresult
311 nsCCUncollectableMarker::Observe(nsISupports* aSubject, const char* aTopic,
312 const char16_t* aData)
313 {
314 if (!strcmp(aTopic, "xpcom-shutdown")) {
315 Element::ClearContentUnbinder();
317 nsCOMPtr<nsIObserverService> obs =
318 mozilla::services::GetObserverService();
319 if (!obs)
320 return NS_ERROR_FAILURE;
322 // No need for kungFuDeathGrip here, yay observerservice!
323 obs->RemoveObserver(this, "xpcom-shutdown");
324 obs->RemoveObserver(this, "cycle-collector-begin");
325 obs->RemoveObserver(this, "cycle-collector-forget-skippable");
327 sGeneration = 0;
329 return NS_OK;
330 }
332 NS_ASSERTION(!strcmp(aTopic, "cycle-collector-begin") ||
333 !strcmp(aTopic, "cycle-collector-forget-skippable"), "wrong topic");
335 // JS cleanup can be slow. Do it only if there has been a GC.
336 bool cleanupJS =
337 nsJSContext::CleanupsSinceLastGC() == 0 &&
338 !strcmp(aTopic, "cycle-collector-forget-skippable");
340 bool prepareForCC = !strcmp(aTopic, "cycle-collector-begin");
341 if (prepareForCC) {
342 Element::ClearContentUnbinder();
343 }
345 // Increase generation to effectively unmark all current objects
346 if (!++sGeneration) {
347 ++sGeneration;
348 }
350 nsFocusManager::MarkUncollectableForCCGeneration(sGeneration);
352 nsresult rv;
354 // Iterate all toplevel windows
355 nsCOMPtr<nsISimpleEnumerator> windowList;
356 nsCOMPtr<nsIWindowMediator> med =
357 do_GetService(NS_WINDOWMEDIATOR_CONTRACTID);
358 if (med) {
359 rv = med->GetEnumerator(nullptr, getter_AddRefs(windowList));
360 NS_ENSURE_SUCCESS(rv, rv);
362 MarkWindowList(windowList, cleanupJS, prepareForCC);
363 }
365 nsCOMPtr<nsIWindowWatcher> ww =
366 do_GetService(NS_WINDOWWATCHER_CONTRACTID);
367 if (ww) {
368 rv = ww->GetWindowEnumerator(getter_AddRefs(windowList));
369 NS_ENSURE_SUCCESS(rv, rv);
371 MarkWindowList(windowList, cleanupJS, prepareForCC);
372 }
374 nsCOMPtr<nsIAppShellService> appShell =
375 do_GetService(NS_APPSHELLSERVICE_CONTRACTID);
376 if (appShell) {
377 nsCOMPtr<nsIXULWindow> hw;
378 appShell->GetHiddenWindow(getter_AddRefs(hw));
379 if (hw) {
380 nsCOMPtr<nsIDocShell> shell;
381 hw->GetDocShell(getter_AddRefs(shell));
382 MarkDocShell(shell, cleanupJS, prepareForCC);
383 }
384 bool hasHiddenPrivateWindow = false;
385 appShell->GetHasHiddenPrivateWindow(&hasHiddenPrivateWindow);
386 if (hasHiddenPrivateWindow) {
387 appShell->GetHiddenPrivateWindow(getter_AddRefs(hw));
388 if (hw) {
389 nsCOMPtr<nsIDocShell> shell;
390 hw->GetDocShell(getter_AddRefs(shell));
391 MarkDocShell(shell, cleanupJS, prepareForCC);
392 }
393 }
394 }
396 #ifdef MOZ_XUL
397 nsXULPrototypeCache* xulCache = nsXULPrototypeCache::GetInstance();
398 if (xulCache) {
399 xulCache->MarkInCCGeneration(sGeneration);
400 }
401 #endif
403 static bool previousWasJSCleanup = false;
404 if (cleanupJS) {
405 nsContentUtils::UnmarkGrayJSListenersInCCGenerationDocuments(sGeneration);
406 MarkMessageManagers();
408 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
409 static_cast<nsObserverService *>(obs.get())->UnmarkGrayStrongObservers();
411 previousWasJSCleanup = true;
412 } else if (previousWasJSCleanup) {
413 previousWasJSCleanup = false;
414 if (!prepareForCC) {
415 xpc_UnmarkSkippableJSHolders();
416 }
417 }
419 return NS_OK;
420 }
422 struct TraceClosure
423 {
424 TraceClosure(JSTracer* aTrc, uint32_t aGCNumber)
425 : mTrc(aTrc), mGCNumber(aGCNumber)
426 {}
427 JSTracer* mTrc;
428 uint32_t mGCNumber;
429 };
431 static PLDHashOperator
432 TraceActiveWindowGlobal(const uint64_t& aId, nsGlobalWindow*& aWindow, void* aClosure)
433 {
434 if (aWindow->GetDocShell() && aWindow->IsOuterWindow()) {
435 TraceClosure* closure = static_cast<TraceClosure*>(aClosure);
436 aWindow->TraceGlobalJSObject(closure->mTrc);
437 #ifdef MOZ_XUL
438 nsIDocument* doc = aWindow->GetExtantDoc();
439 if (doc && doc->IsXUL()) {
440 XULDocument* xulDoc = static_cast<XULDocument*>(doc);
441 xulDoc->TraceProtos(closure->mTrc, closure->mGCNumber);
442 }
443 #endif
444 }
445 return PL_DHASH_NEXT;
446 }
448 void
449 mozilla::dom::TraceBlackJS(JSTracer* aTrc, uint32_t aGCNumber, bool aIsShutdownGC)
450 {
451 #ifdef MOZ_XUL
452 // Mark the scripts held in the XULPrototypeCache. This is required to keep
453 // the JS script in the cache live across GC.
454 nsXULPrototypeCache* cache = nsXULPrototypeCache::MaybeGetInstance();
455 if (cache) {
456 if (aIsShutdownGC) {
457 cache->FlushScripts();
458 } else {
459 cache->MarkInGC(aTrc);
460 }
461 }
462 #endif
464 if (!nsCCUncollectableMarker::sGeneration) {
465 return;
466 }
468 TraceClosure closure(aTrc, aGCNumber);
470 // Mark globals of active windows black.
471 nsGlobalWindow::WindowByIdTable* windowsById =
472 nsGlobalWindow::GetWindowsTable();
473 if (windowsById) {
474 windowsById->Enumerate(TraceActiveWindowGlobal, &closure);
475 }
477 // Mark the safe context black
478 nsContentUtils::TraceSafeJSContext(aTrc);
479 }