mirror of
https://github.com/QL-Win/QuickLook.git
synced 2025-12-22 03:00:14 +08:00
Go back from Webkit to IE render engine
This commit is contained in:
@@ -0,0 +1,230 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Forms;
|
||||
using System.Windows.Navigation;
|
||||
using SHDocVw;
|
||||
using HorizontalAlignment = System.Windows.HorizontalAlignment;
|
||||
using WebBrowser = System.Windows.Controls.WebBrowser;
|
||||
|
||||
namespace QuickLook.Plugin.HtmlViewer
|
||||
{
|
||||
/// <summary>
|
||||
/// Class wraps a Browser (which itself is a bad designed WPF control) and presents itself as
|
||||
/// a better designed WPF control. For example provides a bindable source property or commands.
|
||||
/// </summary>
|
||||
public class WpfWebBrowserWrapper : ContentControl, IDisposable
|
||||
{
|
||||
private static readonly Guid SidSWebBrowserApp = new Guid("0002DF05-0000-0000-C000-000000000046");
|
||||
|
||||
private WebBrowser _innerBrowser;
|
||||
private bool _loaded;
|
||||
private int _zoom;
|
||||
|
||||
public WpfWebBrowserWrapper()
|
||||
{
|
||||
_innerBrowser = new WebBrowser
|
||||
{
|
||||
HorizontalAlignment = HorizontalAlignment.Stretch,
|
||||
VerticalAlignment = VerticalAlignment.Stretch
|
||||
};
|
||||
|
||||
Content = _innerBrowser;
|
||||
_innerBrowser.Navigated += InnerBrowserNavigated;
|
||||
_innerBrowser.Navigating += InnerBrowserNavigating;
|
||||
_innerBrowser.LoadCompleted += InnerBrowserLoadCompleted;
|
||||
_innerBrowser.Loaded += InnerBrowserLoaded;
|
||||
_innerBrowser.SizeChanged += InnerBrowserSizeChanged;
|
||||
}
|
||||
|
||||
public string Url { get; private set; }
|
||||
|
||||
public int Zoom
|
||||
{
|
||||
get => _zoom;
|
||||
set
|
||||
{
|
||||
_zoom = value;
|
||||
ApplyZoom();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// gets the browser control's underlying activeXcontrol. Ready only from within Loaded-event but before loaded Document!
|
||||
// do not use prior loaded event.
|
||||
public InternetExplorer ActiveXControl
|
||||
{
|
||||
get
|
||||
{
|
||||
// this is a brilliant way to access the WebBrowserObject prior to displaying the actual document (eg. Document property)
|
||||
var fiComWebBrowser =
|
||||
typeof(WebBrowser).GetField("_axIWebBrowser2", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
if (fiComWebBrowser == null) return null;
|
||||
var objComWebBrowser = fiComWebBrowser.GetValue(_innerBrowser);
|
||||
return objComWebBrowser as InternetExplorer;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_innerBrowser.Source = null;
|
||||
_innerBrowser.Dispose();
|
||||
_innerBrowser = null;
|
||||
Content = null;
|
||||
}
|
||||
|
||||
private void InnerBrowserSizeChanged(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
ApplyZoom();
|
||||
}
|
||||
|
||||
private void InnerBrowserLoaded(object sender, EventArgs e)
|
||||
{
|
||||
// make browser control not silent: allow HTTP-Auth-dialogs. Requery command availability
|
||||
var ie = ActiveXControl;
|
||||
ie.Silent = true;
|
||||
}
|
||||
|
||||
// called when the loading of a web page is done
|
||||
private void InnerBrowserLoadCompleted(object sender, NavigationEventArgs e)
|
||||
{
|
||||
ApplyZoom(); // apply later and not only at changed event, since only works if browser is rendered.
|
||||
}
|
||||
|
||||
// called when the browser started to load and retrieve data.
|
||||
private void InnerBrowserNavigating(object sender, NavigatingCancelEventArgs e)
|
||||
{
|
||||
if (_loaded)
|
||||
e.Cancel = true;
|
||||
_loaded = true;
|
||||
}
|
||||
|
||||
// re query the commands once done navigating.
|
||||
private void InnerBrowserNavigated(object sender, NavigationEventArgs e)
|
||||
{
|
||||
RegisterWindowErrorHanlder_();
|
||||
}
|
||||
|
||||
public void Navigate(string uri)
|
||||
{
|
||||
Url = uri;
|
||||
|
||||
if (_innerBrowser == null)
|
||||
return;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(uri) && Uri.IsWellFormedUriString(uri, UriKind.Absolute))
|
||||
try
|
||||
{
|
||||
_innerBrowser.Source = new Uri(uri);
|
||||
}
|
||||
catch (UriFormatException)
|
||||
{
|
||||
// just don't crash because of a malformed url
|
||||
}
|
||||
else
|
||||
_innerBrowser.Source = null;
|
||||
}
|
||||
|
||||
public void Navigate(Stream stream)
|
||||
{
|
||||
if (_innerBrowser == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
_innerBrowser.NavigateToStream(stream);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
// register script errors handler on DOM - document.window
|
||||
private void RegisterWindowErrorHanlder_()
|
||||
{
|
||||
object parwin = ((dynamic) _innerBrowser.Document).parentWindow;
|
||||
var cookie = new AxHost.ConnectionPointCookie(parwin, new HtmlWindowEvents2Impl(this),
|
||||
typeof(IIntHTMLWindowEvents2));
|
||||
// MemoryLEAK? No: cookie has a Finalize() to Disconnect istelf. We'll rely on that. If disconnected too early,
|
||||
// though (eg. in LoadCompleted-event) scripts continue to run and can cause error messages to appear again.
|
||||
// --> forget cookie and be happy.
|
||||
}
|
||||
|
||||
private void ApplyZoom()
|
||||
{
|
||||
if (_innerBrowser == null || !_innerBrowser.IsLoaded)
|
||||
return;
|
||||
|
||||
// grab a handle to the underlying ActiveX object
|
||||
IServiceProvider serviceProvider = null;
|
||||
if (_innerBrowser.Document != null)
|
||||
serviceProvider = (IServiceProvider) _innerBrowser.Document;
|
||||
if (serviceProvider == null)
|
||||
return;
|
||||
|
||||
var serviceGuid = SidSWebBrowserApp;
|
||||
var iid = typeof(IWebBrowser2).GUID;
|
||||
var browserInst =
|
||||
(IWebBrowser2) serviceProvider.QueryService(ref serviceGuid, ref iid);
|
||||
|
||||
try
|
||||
{
|
||||
object zoomPercObj = _zoom;
|
||||
// send the zoom command to the ActiveX object
|
||||
browserInst.ExecWB(OLECMDID.OLECMDID_OPTICAL_ZOOM,
|
||||
OLECMDEXECOPT.OLECMDEXECOPT_DONTPROMPTUSER,
|
||||
ref zoomPercObj,
|
||||
IntPtr.Zero);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignore this dynamic call if it fails.
|
||||
}
|
||||
}
|
||||
|
||||
// needed to implement the Event for script errors
|
||||
[Guid("3050f625-98b5-11cf-bb82-00aa00bdce0b")]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
|
||||
[TypeLibType(TypeLibTypeFlags.FHidden)]
|
||||
[ComImport]
|
||||
private interface IIntHTMLWindowEvents2
|
||||
{
|
||||
[DispId(1002)]
|
||||
bool onerror(string description, string url, int line);
|
||||
}
|
||||
|
||||
// needed to implement the Event for script errors
|
||||
private class HtmlWindowEvents2Impl : IIntHTMLWindowEvents2
|
||||
{
|
||||
private readonly WpfWebBrowserWrapper _control;
|
||||
|
||||
public HtmlWindowEvents2Impl(WpfWebBrowserWrapper control)
|
||||
{
|
||||
_control = control;
|
||||
}
|
||||
|
||||
// implementation of the onerror Javascript error. Return true to indicate a "Handled" state.
|
||||
public bool onerror(string description, string urlString, int line)
|
||||
{
|
||||
Debug.WriteLine(description + "@" + urlString + ": " + line);
|
||||
// Handled:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Needed to expose the WebBrowser's underlying ActiveX control for zoom functionality
|
||||
[ComImport]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
[Guid("6d5140c1-7436-11ce-8034-00aa006009fa")]
|
||||
internal interface IServiceProvider
|
||||
{
|
||||
[return: MarshalAs(UnmanagedType.IUnknown)]
|
||||
object QueryService(ref Guid guidService, ref Guid riid);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user