From: Gabriel Ivăncescu gabrielopcode@gmail.com
When forcing cycle collection manually, Gecko treats it as a specific type of collection. This kind of collection, unfortunately, will not always clean everything, unlike when shutting down the CC, which results in leaks when closing a doc obj because when we force it we expect it to behave like a shutdown for the given doc obj.
Enabling graph logging *will* however clean everything (with env vars), to be able to track all the nodes. To get the same behavior, we can supply a dummy logger that fails early when opening the files (after which wine-gecko replaces the logger with NULL, except now it always visits everything, which is what we want), without actually shutting down the CC, as we aren't actually shutting down, so another doc obj might be created later. And there's no XPCOM interface for shutting it down, anyway.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/nsembed.c | 116 +++++++++++++++++++++++++++++++++++++++- dlls/mshtml/nsiface.idl | 38 ++++++++++++- 2 files changed, 152 insertions(+), 2 deletions(-)
diff --git a/dlls/mshtml/nsembed.c b/dlls/mshtml/nsembed.c index 30c0ef513b8..8d437278d0b 100644 --- a/dlls/mshtml/nsembed.c +++ b/dlls/mshtml/nsembed.c @@ -41,6 +41,7 @@ WINE_DECLARE_DEBUG_CHANNEL(gecko); #define NS_APPSTARTUPNOTIFIER_CONTRACTID "@mozilla.org/embedcomp/appstartup-notifier;1" #define NS_WEBBROWSER_CONTRACTID "@mozilla.org/embedding/browser/nsWebBrowser;1" #define NS_COMMANDPARAMS_CONTRACTID "@mozilla.org/embedcomp/command-params;1" +#define NS_CYCLECOLLECTORLOGGER_CONTRACTID "@mozilla.org/cycle-collector-logger;1" #define NS_HTMLSERIALIZER_CONTRACTID "@mozilla.org/layout/contentserializer;1?mimetype=text/html" #define NS_EDITORCONTROLLER_CONTRACTID "@mozilla.org/editor/editorcontroller;1" #define NS_PREFERENCES_CONTRACTID "@mozilla.org/preferences;1" @@ -2201,6 +2202,119 @@ static const nsISupportsWeakReferenceVtbl nsSupportsWeakReferenceVtbl = { nsSupportsWeakReference_GetWeakReference };
+static nsICycleCollectorLogSink dummy_log_sink; + +static nsresult NSAPI dummy_log_sink_QueryInterface(nsICycleCollectorLogSink *iface, nsIIDRef riid, void **result) +{ + if(IsEqualGUID(&IID_nsISupports, riid) || IsEqualGUID(&IID_nsICycleCollectorLogSink, riid)) { + *result = &dummy_log_sink; + return NS_OK; + } + return NS_NOINTERFACE; +} + +static nsrefcnt NSAPI dummy_log_sink_AddRef(nsICycleCollectorLogSink *iface) +{ + return 2; +} + +static nsrefcnt NSAPI dummy_log_sink_Release(nsICycleCollectorLogSink *iface) +{ + return 1; +} + +static nsresult NSAPI dummy_log_sink_Open(nsICycleCollectorLogSink *iface, void **aGCLog, void **aCCLog) +{ + /* Always fail in Open, which disables the log entirely, while still forcing the CC */ + return NS_ERROR_NOT_IMPLEMENTED; +} + +static nsresult NSAPI dummy_log_sink_CloseGCLog(nsICycleCollectorLogSink *iface) +{ + return NS_OK; +} + +static nsresult NSAPI dummy_log_sink_CloseCCLog(nsICycleCollectorLogSink *iface) +{ + return NS_OK; +} + +static nsresult NSAPI dummy_log_sink_GetFilenameIdentifier(nsICycleCollectorLogSink *iface, nsAString *aIdentifier) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +static nsresult NSAPI dummy_log_sink_SetFilenameIdentifier(nsICycleCollectorLogSink *iface, const nsAString *aIdentifier) +{ + return NS_OK; +} + +static nsresult NSAPI dummy_log_sink_GetProcessIdentifier(nsICycleCollectorLogSink *iface, LONG *aIdentifier) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +static nsresult NSAPI dummy_log_sink_SetProcessIdentifier(nsICycleCollectorLogSink *iface, LONG aIdentifier) +{ + return NS_OK; +} + +static nsresult NSAPI dummy_log_sink_GetGcLog(nsICycleCollectorLogSink *iface, nsIFile **aPath) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +static nsresult NSAPI dummy_log_sink_GetCcLog(nsICycleCollectorLogSink *iface, nsIFile **aPath) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +static const nsICycleCollectorLogSinkVtbl dummy_log_sink_vtbl = { + dummy_log_sink_QueryInterface, + dummy_log_sink_AddRef, + dummy_log_sink_Release, + dummy_log_sink_Open, + dummy_log_sink_CloseGCLog, + dummy_log_sink_CloseCCLog, + dummy_log_sink_GetFilenameIdentifier, + dummy_log_sink_SetFilenameIdentifier, + dummy_log_sink_GetProcessIdentifier, + dummy_log_sink_SetProcessIdentifier, + dummy_log_sink_GetGcLog, + dummy_log_sink_GetCcLog +}; + +static nsICycleCollectorLogSink dummy_log_sink = { &dummy_log_sink_vtbl }; + +static void force_cycle_collection(nsIDOMWindowUtils *window_utils) +{ + nsICycleCollectorListener *logger, *all_logger; + nsresult nsres; + + /* Use a dummy logger to force the CC to clean up the entire graph, as in shutdown */ + nsres = nsIServiceManager_GetServiceByContractID(pServMgr, NS_CYCLECOLLECTORLOGGER_CONTRACTID, &IID_nsICycleCollectorListener, (void**)&logger); + if(NS_FAILED(nsres)) { + logger = NULL; + goto collect; + } + + nsres = nsICycleCollectorListener_AllTraces(logger, &all_logger); + nsICycleCollectorListener_Release(logger); + logger = NULL; + if(NS_FAILED(nsres)) + goto collect; + logger = all_logger; + + /* The dummy log sink will fail when opening the log, which will ignore it */ + nsres = nsICycleCollectorListener_SetLogSink(logger, &dummy_log_sink); + assert(nsres == NS_OK); + +collect: + nsIDOMWindowUtils_CycleCollect(window_utils, logger, 0); + if(logger) + nsICycleCollectorListener_Release(logger); +} + static HRESULT init_browser(GeckoBrowser *browser) { mozIDOMWindowProxy *mozwindow; @@ -2427,7 +2541,7 @@ void detach_gecko_browser(GeckoBrowser *This)
/* Force cycle collection */ if(window_utils) { - nsIDOMWindowUtils_CycleCollect(window_utils, NULL, 0); + force_cycle_collection(window_utils); nsIDOMWindowUtils_Release(window_utils); } } diff --git a/dlls/mshtml/nsiface.idl b/dlls/mshtml/nsiface.idl index 2189e65a5d5..ccd1f5b82b0 100644 --- a/dlls/mshtml/nsiface.idl +++ b/dlls/mshtml/nsiface.idl @@ -181,7 +181,6 @@ typedef nsISupports nsILocalFile; typedef nsISupports nsIDOMHTMLMenuElement; typedef nsISupports nsIDOMCaretPosition; typedef nsISupports nsIFrameRequestCallback; -typedef nsISupports nsICycleCollectorListener; typedef nsISupports nsIDOMHTMLCanvasElement; typedef nsISupports nsIQueryContentEventResult; typedef nsISupports nsIDOMBlob; @@ -310,6 +309,43 @@ interface nsIThreadManager : nsISupports nsresult GetIsMainThread(bool *aResult); }
+[ + object, + uuid(3ad9875f-d0e4-4ac2-87e3-f127f6c02ce1), + local +] +interface nsICycleCollectorLogSink : nsISupports +{ + nsresult Open(void /*FILE*/ **aGCLog, void /*FILE*/ **aCCLog); + nsresult CloseGCLog(); + nsresult CloseCCLog(); + nsresult GetFilenameIdentifier(nsAString *aIdentifier); + nsresult SetFilenameIdentifier(const nsAString *aIdentifier); + nsresult GetProcessIdentifier(int32_t *aIdentifier); + nsresult SetProcessIdentifier(int32_t aIdentifier); + nsresult GetGcLog(nsIFile **aPath); + nsresult GetCcLog(nsIFile **aPath); +} + +[ + object, + uuid(703b53b6-24f6-40c6-9ea9-aeb2dc53d170), + local +] +interface nsICycleCollectorListener : nsISupports +{ + nsresult AllTraces(nsICycleCollectorListener **aListener); + nsresult GetWantAllTraces(bool *aAllTraces); + nsresult GetDisableLog(bool *aDisableLog); + nsresult SetDisableLog(bool aDisableLog); + nsresult GetLogSink(nsICycleCollectorLogSink **aLogSink); + nsresult SetLogSink(nsICycleCollectorLogSink *aLogSink); + nsresult GetWantAfterProcessing(bool *aWantAfterProcessing); + nsresult SetWantAfterProcessing(bool aWantAfterProcessing); + nsresult ProcessNext(void /*nsICycleCollectorHandler*/ *aHandler, bool *aCanContinue); + nsresult AsLogger(void /*nsCycleCollectorLogger*/ **aRetVal); +} + [ object, uuid(d1899240-f9d2-11d2-bdd6-000064657374),