| File: System\Windows\Forms\Controls\WebBrowser\HtmlShimManager.cs | Web Access |
| Project: src\winforms\src\System.Windows.Forms\System.Windows.Forms.csproj (System.Windows.Forms) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using Windows.Win32.Web.MsHtml; namespace System.Windows.Forms; /// <summary> /// HtmlShimManager - this class manages the shims for HtmlWindows, HtmlDocuments, and HtmlElements. /// essentially we need a long-lasting object to call back on events from the web browser, and the /// manager is the one in charge of making sure this list stays around as long as needed. /// /// When a HtmlWindow unloads we prune our list of corresponding document, window, and element shims. /// </summary> internal sealed class HtmlShimManager : IDisposable { private Dictionary<HtmlWindow, HtmlWindow.HtmlWindowShim>? _htmlWindowShims; private Dictionary<HtmlElement, HtmlElement.HtmlElementShim>? _htmlElementShims; private Dictionary<HtmlDocument, HtmlDocument.HtmlDocumentShim>? _htmlDocumentShims; internal HtmlShimManager() { } /// <summary> /// Adds a <see cref="HtmlDocument.HtmlDocumentShim"/> to list of shims to manage. /// Can create a WindowShim as a side effect so it knows when to self prune from the list. /// </summary> public void AddDocumentShim(HtmlDocument doc) { HtmlDocument.HtmlDocumentShim? shim = null; if (_htmlDocumentShims is null) { _htmlDocumentShims = []; shim = new HtmlDocument.HtmlDocumentShim(doc); _htmlDocumentShims[doc] = shim; } else if (!_htmlDocumentShims.ContainsKey(doc)) { shim = new HtmlDocument.HtmlDocumentShim(doc); _htmlDocumentShims[doc] = shim; } if (shim is not null) { OnShimAdded(shim); } } /// <summary> /// Adds a <see cref="HtmlWindow.HtmlWindowShim"/> to list of shims to manage. /// </summary> public void AddWindowShim(HtmlWindow window) { HtmlWindow.HtmlWindowShim? shim = null; if (_htmlWindowShims is null) { _htmlWindowShims = []; shim = new HtmlWindow.HtmlWindowShim(window); _htmlWindowShims[window] = shim; } else if (!_htmlWindowShims.ContainsKey(window)) { shim = new HtmlWindow.HtmlWindowShim(window); _htmlWindowShims[window] = shim; } if (shim is not null) { // strictly not necessary, but here for future use. OnShimAdded(shim); } } /// <summary> AddElementShim - adds a HtmlDocumentShim to list of shims to manage /// Can create a WindowShim as a side effect so it knows when to self prune from the list. /// </summary> public void AddElementShim(HtmlElement element) { HtmlElement.HtmlElementShim? shim = null; if (_htmlElementShims is null) { _htmlElementShims = []; shim = new HtmlElement.HtmlElementShim(element); _htmlElementShims[element] = shim; } else if (!_htmlElementShims.ContainsKey(element)) { shim = new HtmlElement.HtmlElementShim(element); _htmlElementShims[element] = shim; } if (shim is not null) { OnShimAdded(shim); } } internal HtmlDocument.HtmlDocumentShim? GetDocumentShim(HtmlDocument document) { if (_htmlDocumentShims is null) { return null; } if (_htmlDocumentShims.TryGetValue(document, out HtmlDocument.HtmlDocumentShim? value)) { return value; } return null; } internal HtmlElement.HtmlElementShim? GetElementShim(HtmlElement element) { if (_htmlElementShims is null) { return null; } if (_htmlElementShims.TryGetValue(element, out HtmlElement.HtmlElementShim? elementShim)) { return elementShim; } return null; } internal HtmlWindow.HtmlWindowShim? GetWindowShim(HtmlWindow window) { if (_htmlWindowShims is null) { return null; } if (_htmlWindowShims.TryGetValue(window, out HtmlWindow.HtmlWindowShim? windowShim)) { return windowShim; } return null; } private unsafe void OnShimAdded(HtmlShim addedShim) { Debug.Assert(addedShim is not null, "Why are we calling this with a null shim?"); if (addedShim is not null and not HtmlWindow.HtmlWindowShim) { // We need to add a window shim here for documents and elements // so we can sync Window.Unload. The window shim itself will trap // the unload event and call back on us on OnWindowUnloaded. When // that happens we know we can free all our ptrs to COM. AddWindowShim(new HtmlWindow(this, ComHelpers.GetComPointer<IHTMLWindow2>(addedShim.AssociatedWindow))); } } /// <summary> /// HtmlWindowShim calls back on us when it has unloaded the page. At this point we need to /// walk through our lists and make sure we've cleaned up /// </summary> internal void OnWindowUnloaded(HtmlWindow unloadedWindow) { Debug.Assert(unloadedWindow is not null, "Why are we calling this with a null window?"); if (unloadedWindow is not null) { // // prune documents // if (_htmlDocumentShims is not null) { HtmlDocument.HtmlDocumentShim[] shims = new HtmlDocument.HtmlDocumentShim[_htmlDocumentShims.Count]; _htmlDocumentShims.Values.CopyTo(shims, 0); foreach (HtmlDocument.HtmlDocumentShim shim in shims) { if (shim.AssociatedWindow == unloadedWindow.NativeHtmlWindow) { _htmlDocumentShims.Remove(shim.Document); shim.Dispose(); } } } // // prune elements // if (_htmlElementShims is not null) { HtmlElement.HtmlElementShim[] shims = new HtmlElement.HtmlElementShim[_htmlElementShims.Count]; _htmlElementShims.Values.CopyTo(shims, 0); foreach (HtmlElement.HtmlElementShim shim in shims) { if (shim.AssociatedWindow == unloadedWindow.NativeHtmlWindow) { _htmlElementShims.Remove(shim.Element); shim.Dispose(); } } } // Prune the particular window from the list. if (_htmlWindowShims is not null) { if (_htmlWindowShims.Remove(unloadedWindow, out HtmlWindow.HtmlWindowShim? shim)) { shim.Dispose(); } } } } public void Dispose() { Dispose(true); } private void Dispose(bool disposing) { if (disposing) { if (_htmlElementShims is not null) { foreach (HtmlElement.HtmlElementShim shim in _htmlElementShims.Values) { shim.Dispose(); } } if (_htmlDocumentShims is not null) { foreach (HtmlDocument.HtmlDocumentShim shim in _htmlDocumentShims.Values) { shim.Dispose(); } } if (_htmlWindowShims is not null) { foreach (HtmlWindow.HtmlWindowShim shim in _htmlWindowShims.Values) { shim.Dispose(); } } _htmlWindowShims = null; _htmlDocumentShims = null; _htmlWindowShims = null; } } }