xenadmin/XenAdmin/Core/WebBrowser2.cs
Konstantina Chremmou abec5c4aba CA-284090: Added a dedicated status bar on the TabPageFeature to resemble real
life browsers and allow the user to see the status even if other actions are taking
place. Removed webClient use from the browser; instead try sequentially to navigate
to all Uris until one succeeds; this avoids sending duplicate requests and allows
the user to see all endpoints tried out. Reworked the WebBrowser event handlers
to ensure the UI reflects correctly the browser state.

Signed-off-by: Konstantina Chremmou <konstantina.chremmou@citrix.com>
2018-08-15 09:59:58 +01:00

264 lines
9.7 KiB
C#

/* Copyright (c) Citrix Systems, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms,
* with or without modification, are permitted provided
* that the following conditions are met:
*
* * Redistributions of source code must retain the above
* copyright notice, this list of conditions and the
* following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Windows.Forms;
using XenCenterLib;
namespace XenAdmin.Core
{
/// <summary>
/// This extension to System.Windows.Forms.WebBrowser adds two capabilities: an event fired whenever
/// browser navigation fails (NavigateError), and an event fired whenever the browser wants to
/// prompt for credentials (AuthenticationPrompt).
///
/// The event sink for NavigateError comes from
/// http://msdn.microsoft.com/en-us/library/system.windows.forms.webbrowser.createsink(VS.80).aspx.
/// The handling of authentication prompts comes partially from
/// http://izlooite.blogspot.com/2009/06/bypass-integrated-authentication-using.html.
/// The bit that he missed is that we can override CreateWebBrowserSiteBase to get ourselves
/// registered rather than having to override IOleClientSite as well. See
/// http://msdn2.microsoft.com/en-us/library/system.windows.forms.webbrowser.createwebbrowsersitebase.aspx.
///
/// (Final note: Contrary to the docs, you can't use the CreateWebBrowserSiteBase override to reimplement
/// IDocHostUIHandler. This is an acknowledged Microsoft bug
/// https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=115198.
/// Fortunately, we only need to implement IAuthenticate, not IDocHostUIHandler, so this isn't a problem
/// for us, but it was confusing for a couple of days.)
/// </summary>
public class WebBrowser2 : WebBrowser
{
private AxHost.ConnectionPointCookie cookie;
private WebBrowser2EventHelper helper;
public event Action<WebBrowser2, NavigateErrorEventArgs> NavigateError;
public event EventHandler WindowClosed;
public event Action<WebBrowser2, AuthenticationPromptEventArgs> AuthenticationPrompt;
private bool Authenticate(out string username, out string password)
{
if (AuthenticationPrompt == null)
{
username = "";
password = "";
return false;
}
else
{
var args = new AuthenticationPromptEventArgs();
AuthenticationPrompt(this, args);
username = args.Username;
password = args.Password;
return args.Success;
}
}
[PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")]
protected override void CreateSink()
{
base.CreateSink();
// Create an instance of the client that will handle the event
// and associate it with the underlying ActiveX control.
helper = new WebBrowser2EventHelper(this);
cookie = new AxHost.ConnectionPointCookie(ActiveXInstance, helper, typeof(DWebBrowserEvents2));
}
[PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")]
protected override void DetachSink()
{
// Disconnect the client that handles the event
// from the underlying ActiveX control.
if (cookie != null)
{
cookie.Disconnect();
cookie = null;
}
base.DetachSink();
}
protected override WebBrowserSiteBase CreateWebBrowserSiteBase()
{
return new WebBrowserSite2(this);
}
protected virtual void OnNavigateError(NavigateErrorEventArgs e)
{
if (NavigateError != null)
NavigateError(this, e);
}
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case Win32.WM_PARENTNOTIFY:
if (!DesignMode && (int)m.WParam == Win32.WM_DESTROY)
{
if (WindowClosed != null)
WindowClosed(this, EventArgs.Empty);
}
break;
default:
base.WndProc(ref m);
break;
}
}
#region Nested classes
private class WebBrowserSite2 : WebBrowserSite, Win32.IAuthenticate, Win32.IServiceProvider
{
private readonly WebBrowser2 Browser;
public WebBrowserSite2(WebBrowser2 browser)
: base(browser)
{
Browser = browser;
}
#region IAuthenticate Members
public int Authenticate(ref IntPtr phwnd, ref IntPtr pszUsername, ref IntPtr pszPassword)
{
string username, password;
if (Browser.Authenticate(out username, out password))
{
IntPtr sUser = Marshal.StringToCoTaskMemAuto(username);
IntPtr sPassword = Marshal.StringToCoTaskMemAuto(password);
pszUsername = sUser;
pszPassword = sPassword;
return Win32.S_OK;
}
else
{
return Win32.E_ACCESSDENIED;
}
}
#endregion
#region IServiceProvider Members
public int QueryService(ref Guid guidService, ref Guid riid, out IntPtr ppvObject)
{
if (guidService.CompareTo(Win32.IID_IAuthenticate) == 0 && riid.CompareTo(Win32.IID_IAuthenticate) == 0)
{
ppvObject = Marshal.GetComInterfaceForObject(this, typeof(Win32.IAuthenticate));
return Win32.S_OK;
}
else
{
ppvObject = IntPtr.Zero;
return Win32.INET_E_DEFAULT_ACTION;
}
}
#endregion
}
/// <summary>
/// Handles the NavigateError event from the underlying ActiveX
/// control by raising the NavigateError event defined in this class.
/// </summary>
private class WebBrowser2EventHelper : StandardOleMarshalObject, DWebBrowserEvents2
{
private WebBrowser2 parent;
public WebBrowser2EventHelper(WebBrowser2 parent)
{
this.parent = parent;
}
public void NavigateError(object pDisp, ref object url, ref object frame, ref object statusCode, ref bool cancel)
{
parent.OnNavigateError(new NavigateErrorEventArgs((string)url, (string)frame, (Int32)statusCode, cancel));
}
}
public class NavigateErrorEventArgs : EventArgs
{
public string Url;
public string Frame;
public Int32 StatusCode;
public bool Cancel;
public NavigateErrorEventArgs(string url, string frame, Int32 statusCode, bool cancel)
{
Url = url;
Frame = frame;
StatusCode = statusCode;
Cancel = cancel;
}
}
public class AuthenticationPromptEventArgs : EventArgs
{
/// <summary>
/// Set to true by the event handler if the user clicked OK on the authentication prompt.
/// </summary>
public bool Success;
/// <summary>
/// Set by the event handler, iff Success is true.
/// </summary>
public string Username;
/// <summary>
/// Set by the event handler, iff Success is true.
/// </summary>
public string Password;
}
#endregion
}
[ComImport, Guid("34A715A0-6587-11D0-924A-0020AFC7AC4D"),
InterfaceType(ComInterfaceType.InterfaceIsIDispatch),
TypeLibType(TypeLibTypeFlags.FHidden)]
public interface DWebBrowserEvents2
{
[DispId(271)]
void NavigateError([In, MarshalAs(UnmanagedType.IDispatch)] object pDisp,
[In] ref object URL, [In] ref object frame,
[In] ref object statusCode, [In, Out] ref bool cancel);
}
}