File: winforms\Managed\System\WinForms\HtmlDocument.cs
Project: ndp\fx\src\System.Windows.Forms.csproj (System.Windows.Forms)
//------------------------------------------------------------------------------
// <copyright file="HtmlDocument.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------
 
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Drawing.Printing;
using System.Globalization;
using System.IO;
using System.Net;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
 
 
namespace System.Windows.Forms
{
    /// <include file='doc\HtmlDocument.uex' path='docs/doc[@for="HtmlDocument"]/*' />
    /// <devdoc>
    ///    <para>[To be supplied.]</para>
    /// </devdoc>
    [PermissionSetAttribute(SecurityAction.LinkDemand, Name = "FullTrust")]
    public sealed class HtmlDocument
    {
        internal static object EventClick = new object();
        internal static object EventContextMenuShowing = new object();
        internal static object EventFocusing = new object();
        internal static object EventLosingFocus = new object();
        internal static object EventMouseDown = new object();
        internal static object EventMouseLeave = new object();
        internal static object EventMouseMove = new object();
        internal static object EventMouseOver = new object();
        internal static object EventMouseUp = new object();
        internal static object EventStop = new object();
 
        private UnsafeNativeMethods.IHTMLDocument2 htmlDocument2;
        private HtmlShimManager shimManager;
 
        [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
        internal HtmlDocument(HtmlShimManager shimManager, UnsafeNativeMethods.IHTMLDocument doc)
        {
            this.htmlDocument2 = (UnsafeNativeMethods.IHTMLDocument2)doc;
            Debug.Assert(this.NativeHtmlDocument2 != null, "The document should implement IHtmlDocument2");
 
            this.shimManager = shimManager;
 
        }
 
        internal UnsafeNativeMethods.IHTMLDocument2 NativeHtmlDocument2
        {
            get
            {
                return this.htmlDocument2;
            }
        }
 
        private HtmlDocumentShim DocumentShim
        {
            get
            {
                if (ShimManager != null)
                {
                    HtmlDocumentShim shim = ShimManager.GetDocumentShim(this);
                    if (shim == null)
                    {
                        shimManager.AddDocumentShim(this);
                        shim = ShimManager.GetDocumentShim(this);
                    }
                    return shim;
                }
                return null;
            }
        }
 
        private HtmlShimManager ShimManager
        {
            get
            {
                return this.shimManager;
            }
        }
 
        /// <include file='doc\HtmlDocument.uex' path='docs/doc[@for="HtmlDocument.ActiveElement"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public HtmlElement ActiveElement
        {
            get
            {
                UnsafeNativeMethods.IHTMLElement iHtmlElement = this.NativeHtmlDocument2.GetActiveElement();
                return iHtmlElement != null ? new HtmlElement(ShimManager, iHtmlElement) : null;
            }
        }
 
        /// <include file='doc\HtmlDocument.uex' path='docs/doc[@for="HtmlDocument.Body"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public HtmlElement Body
        {
            get
            {
                UnsafeNativeMethods.IHTMLElement iHtmlElement = this.NativeHtmlDocument2.GetBody();
                return iHtmlElement != null ? new HtmlElement(ShimManager, iHtmlElement) : null;
            }
        }
 
        /// <include file='doc\HtmlDocument.uex' path='docs/doc[@for="HtmlDocument.Domain"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public string Domain
        {
            get
            {
                return this.NativeHtmlDocument2.GetDomain();
            }
            set
            {
                try
                {
                    this.NativeHtmlDocument2.SetDomain(value);
                }
                catch (ArgumentException)
                {
                    // Give a better message describing the error
                    throw new ArgumentException(SR.GetString(SR.HtmlDocumentInvalidDomain));
                }
            }
        }
 
        /// <include file='doc\HtmlDocument.uex' path='docs/doc[@for="HtmlDocument.Title"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public string Title
        {
            get
            {
                return this.NativeHtmlDocument2.GetTitle();
            }
            set
            {
                this.NativeHtmlDocument2.SetTitle(value);
            }
        }
 
        /// <include file='doc\HtmlDocument.uex' path='docs/doc[@for="HtmlDocument.Location"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public Uri Url
        {
            get
            {
                UnsafeNativeMethods.IHTMLLocation iHtmlLocation = this.NativeHtmlDocument2.GetLocation();
                string stringLocation = (iHtmlLocation == null) ? "" : iHtmlLocation.GetHref();
                return string.IsNullOrEmpty(stringLocation) ? null : new Uri(stringLocation);
            }
        }
 
        /// <include file='doc\HtmlDocument.uex' path='docs/doc[@for="HtmlDocument.Window"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public HtmlWindow Window
        {
            get
            {
                UnsafeNativeMethods.IHTMLWindow2 iHTMLWindow2 = this.NativeHtmlDocument2.GetParentWindow();
                return iHTMLWindow2 != null ? new HtmlWindow(ShimManager, iHTMLWindow2) : null;
            }
        }
 
        /// <include file='doc\HtmlDocument.uex' path='docs/doc[@for="HtmlDocument.BackColor"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public Color BackColor
        {
            get
            {
                Color c = Color.Empty;
                try
                {
                    c = this.ColorFromObject(this.NativeHtmlDocument2.GetBgColor());
                }
                catch (Exception ex)
                {
                    if (ClientUtils.IsSecurityOrCriticalException(ex))
                    {
                        throw;
                    }
                }
                return c;
            }
            set
            {
                int color = value.R << 16 | value.G << 8 | value.B;
                this.NativeHtmlDocument2.SetBgColor(color);
            }
        }
 
        /// <include file='doc\HtmlDocument.uex' path='docs/doc[@for="HtmlDocument.ForeColor"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public Color ForeColor
        {
            get
            {
                Color c = Color.Empty;
                try
                {
                    c = this.ColorFromObject(this.NativeHtmlDocument2.GetFgColor());
                }
                catch (Exception ex)
                {
                    if (ClientUtils.IsSecurityOrCriticalException(ex))
                    {
                        throw;
                    }
                }
                return c;
            }
            set
            {
                int color = value.R << 16 | value.G << 8 | value.B;
                this.NativeHtmlDocument2.SetFgColor(color);
            }
        }
 
        /// <include file='doc\HtmlDocument.uex' path='docs/doc[@for="HtmlDocument.LinkColor"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public Color LinkColor
        {
            get
            {
                Color c = Color.Empty;
                try
                {
                    c = this.ColorFromObject(this.NativeHtmlDocument2.GetLinkColor());
                }
                catch (Exception ex)
                {
                    if (ClientUtils.IsSecurityOrCriticalException(ex))
                    {
                        throw;
                    }
                }
                return c;
            }
            set
            {
                int color = value.R << 16 | value.G << 8 | value.B;
                this.NativeHtmlDocument2.SetLinkColor(color);
            }
        }
 
        /// <include file='doc\HtmlDocument.uex' path='docs/doc[@for="HtmlDocument.ActiveLinkColor"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public Color ActiveLinkColor
        {
            get
            {
                Color c = Color.Empty;
                try
                {
                    c = this.ColorFromObject(this.NativeHtmlDocument2.GetAlinkColor());
                }
                catch (Exception ex)
                {
                    if (ClientUtils.IsSecurityOrCriticalException(ex))
                    {
                        throw;
                    }
                }
                return c;
            }
            set
            {
                int color = value.R << 16 | value.G << 8 | value.B;
                this.NativeHtmlDocument2.SetAlinkColor(color);
            }
        }
 
        /// <include file='doc\HtmlDocument.uex' path='docs/doc[@for="HtmlDocument.VisitedLinkColor"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public Color VisitedLinkColor
        {
            get
            {
                Color c = Color.Empty;
                try
                {
                    c = this.ColorFromObject(this.NativeHtmlDocument2.GetVlinkColor());
                }
                catch (Exception ex)
                {
                    if (ClientUtils.IsSecurityOrCriticalException(ex))
                    {
                        throw;
                    }
                }
                return c;
            }
            set
            {
                int color = value.R << 16 | value.G << 8 | value.B;
                this.NativeHtmlDocument2.SetVlinkColor(color);
            }
        }
 
        /// <include file='doc\HtmlDocument.uex' path='docs/doc[@for="HtmlDocument.Focused"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public bool Focused
        {
            get
            {
                return ((UnsafeNativeMethods.IHTMLDocument4)this.NativeHtmlDocument2).HasFocus();
            }
        }
 
        /// <include file='doc\HtmlDocument.uex' path='docs/doc[@for="HtmlDocument.DomDocument"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public object DomDocument
        {
            get
            {
                return this.NativeHtmlDocument2;
            }
        }
 
        /// <include file='doc\HtmlDocument.uex' path='docs/doc[@for="HtmlDocument.Cookie"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public string Cookie
        {
            get
            {
                return this.NativeHtmlDocument2.GetCookie();
            }
            set
            {
                this.NativeHtmlDocument2.SetCookie(value);
            }
        }
 
        /// <include file='doc\HtmlDocument.uex' path='docs/doc[@for="HtmlDocument.RightToLeft"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public bool RightToLeft
        {
            get
            {
                return ((UnsafeNativeMethods.IHTMLDocument3)this.NativeHtmlDocument2).GetDir() == "rtl";
            }
            set
            {
                ((UnsafeNativeMethods.IHTMLDocument3)this.NativeHtmlDocument2).SetDir(value ? "rtl" : "ltr");
            }
        }
 
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public string Encoding
        {
            get
            {
                return this.NativeHtmlDocument2.GetCharset();
            }
            set
            {
                this.NativeHtmlDocument2.SetCharset(value);
            }
        }
 
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public string DefaultEncoding
        {
            get
            {
                return this.NativeHtmlDocument2.GetDefaultCharset();
            }
        }
 
        /// <include file='doc\HtmlDocument.uex' path='docs/doc[@for="HtmlDocument.All"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public HtmlElementCollection All
        {
            get
            {
                UnsafeNativeMethods.IHTMLElementCollection iHTMLElementCollection = this.NativeHtmlDocument2.GetAll();
                return iHTMLElementCollection != null ? new HtmlElementCollection(ShimManager, iHTMLElementCollection) : new HtmlElementCollection(ShimManager);
            }
        }
 
        /// <include file='doc\HtmlDocument.uex' path='docs/doc[@for="HtmlDocument.Links"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public HtmlElementCollection Links
        {
            get
            {
                UnsafeNativeMethods.IHTMLElementCollection iHTMLElementCollection = this.NativeHtmlDocument2.GetLinks();
                return iHTMLElementCollection != null ? new HtmlElementCollection(ShimManager, iHTMLElementCollection) : new HtmlElementCollection(ShimManager);
            }
        }
 
        /// <include file='doc\HtmlDocument.uex' path='docs/doc[@for="HtmlDocument.Images"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public HtmlElementCollection Images
        {
            get
            {
                UnsafeNativeMethods.IHTMLElementCollection iHTMLElementCollection = this.NativeHtmlDocument2.GetImages();
                return iHTMLElementCollection != null ? new HtmlElementCollection(ShimManager, iHTMLElementCollection) : new HtmlElementCollection(ShimManager);
            }
        }
 
        /// <include file='doc\HtmlDocument.uex' path='docs/doc[@for="HtmlDocument.Forms"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public HtmlElementCollection Forms
        {
            get
            {
                UnsafeNativeMethods.IHTMLElementCollection iHTMLElementCollection = this.NativeHtmlDocument2.GetForms();
                return iHTMLElementCollection != null ? new HtmlElementCollection(ShimManager, iHTMLElementCollection) : new HtmlElementCollection(ShimManager);
            }
        }
 
        /// <include file='doc\HtmlDocument.uex' path='docs/doc[@for="HtmlDocument.Write"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public void Write(string text)
        {
            object[] strs = new object[] { (object)text };
            this.NativeHtmlDocument2.Write(strs);
        }
 
        /// <devdoc>
        ///    <para>Executes a command on the document</para>
        /// </devdoc>
        public void ExecCommand(string command, bool showUI, object value)
        {
            this.NativeHtmlDocument2.ExecCommand(command, showUI, value);
        }
 
        /// <include file='doc\HtmlDocument.uex' path='docs/doc[@for="HtmlDocument.Focus"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        [EditorBrowsable(EditorBrowsableState.Advanced)]
        public void Focus()
        {
            ((UnsafeNativeMethods.IHTMLDocument4)this.NativeHtmlDocument2).Focus();
            // Seems to have a problem in really setting focus the first time
            ((UnsafeNativeMethods.IHTMLDocument4)this.NativeHtmlDocument2).Focus();
        }
 
        /// <include file='doc\HtmlDocument.uex' path='docs/doc[@for="HtmlDocument.GetElementById"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public HtmlElement GetElementById(string id)
        {
            UnsafeNativeMethods.IHTMLElement iHTMLElement = ((UnsafeNativeMethods.IHTMLDocument3)this.NativeHtmlDocument2).GetElementById(id);
            return iHTMLElement != null ? new HtmlElement(ShimManager, iHTMLElement) : null;
        }
 
        /// <include file='doc\HtmlDocument.uex' path='docs/doc[@for="HtmlDocument.GetElementFromPoint"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public HtmlElement GetElementFromPoint(Point point)
        {
            UnsafeNativeMethods.IHTMLElement iHTMLElement = this.NativeHtmlDocument2.ElementFromPoint(point.X, point.Y);
            return iHTMLElement != null ? new HtmlElement(ShimManager, iHTMLElement) : null;
        }
 
        /// <include file='doc\HtmlDocument.uex' path='docs/doc[@for="HtmlDocument.GetElementsByTagName"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public HtmlElementCollection GetElementsByTagName(string tagName)
        {
            UnsafeNativeMethods.IHTMLElementCollection iHTMLElementCollection = ((UnsafeNativeMethods.IHTMLDocument3)this.NativeHtmlDocument2).GetElementsByTagName(tagName);
            return iHTMLElementCollection != null ? new HtmlElementCollection(ShimManager, iHTMLElementCollection) : new HtmlElementCollection(ShimManager);
        }
 
        /// <include file='doc\HtmlDocument.uex' path='docs/doc[@for="HtmlDocument.OpenNew"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public HtmlDocument OpenNew(bool replaceInHistory)
        {
            object name = (object)(replaceInHistory ? "replace" : "");
            object nullObject = null;
            object ohtmlDocument = this.NativeHtmlDocument2.Open("text/html", name, nullObject, nullObject);
            UnsafeNativeMethods.IHTMLDocument iHTMLDocument = ohtmlDocument as UnsafeNativeMethods.IHTMLDocument;
            return iHTMLDocument != null ? new HtmlDocument(ShimManager, iHTMLDocument) : null;
        }
 
        /// <include file='doc\HtmlDocument.uex' path='docs/doc[@for="HtmlDocument.CreateElement"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public HtmlElement CreateElement(string elementTag)
        {
            UnsafeNativeMethods.IHTMLElement iHTMLElement = this.NativeHtmlDocument2.CreateElement(elementTag);
            return iHTMLElement != null ? new HtmlElement(ShimManager, iHTMLElement) : null;
        }
 
        /// <include file='doc\HtmlDocument.uex' path='docs/doc[@for="HtmlDocument.InvokeScript"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public object InvokeScript(string scriptName, object[] args)
        {
            object retVal = null;
            NativeMethods.tagDISPPARAMS dp = new NativeMethods.tagDISPPARAMS();
            dp.rgvarg = IntPtr.Zero;
            try
            {
                UnsafeNativeMethods.IDispatch scriptObject = this.NativeHtmlDocument2.GetScript() as UnsafeNativeMethods.IDispatch;
                if (scriptObject != null)
                {
                    Guid g = Guid.Empty;
                    string[] names = new string[] { scriptName };
                    int[] dispids = new int[] { NativeMethods.ActiveX.DISPID_UNKNOWN };
                    int hr = scriptObject.GetIDsOfNames(ref g, names, 1,
                                                   SafeNativeMethods.GetThreadLCID(), dispids);
                    if (NativeMethods.Succeeded(hr) && (dispids[0] != NativeMethods.ActiveX.DISPID_UNKNOWN))
                    {
                        if (args != null)
                        {
                            // Reverse the arg order so that parms read naturally after IDispatch. (bug 187662)
                            Array.Reverse(args);
                        }
                        dp.rgvarg = (args == null) ? IntPtr.Zero : HtmlDocument.ArrayToVARIANTVector(args);
                        dp.cArgs = (args == null) ? 0 : args.Length;
                        dp.rgdispidNamedArgs = IntPtr.Zero;
                        dp.cNamedArgs = 0;
 
                        object[] retVals = new object[1];
 
                        hr = scriptObject.Invoke(dispids[0], ref g, SafeNativeMethods.GetThreadLCID(),
                                NativeMethods.DISPATCH_METHOD, dp,
                                retVals, new NativeMethods.tagEXCEPINFO(), null);
                        if (hr == NativeMethods.S_OK)
                        {
                            retVal = retVals[0];
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                if (ClientUtils.IsSecurityOrCriticalException(ex))
                {
                    throw;
                }
            }
            finally
            {
                if (dp.rgvarg != IntPtr.Zero)
                {
                    HtmlDocument.FreeVARIANTVector(dp.rgvarg, args.Length);
                }
            }
            return retVal;
        }
 
        /// <include file='doc\HtmlDocument.uex' path='docs/doc[@for="HtmlDocument.InvokeScript1"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public object InvokeScript(string scriptName)
        {
            return InvokeScript(scriptName, null);
        }
 
 
        /// <include file='doc\HtmlDocument.uex' path='docs/doc[@for="HtmlDocument.AttachEventHandler"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public void AttachEventHandler(string eventName, EventHandler eventHandler)
        {
            HtmlDocumentShim shim = DocumentShim;
            if (shim != null)
            {
                shim.AttachEventHandler(eventName, eventHandler);
            }
        }
 
        /// <include file='doc\HtmlDocument.uex' path='docs/doc[@for="HtmlDocument.DetachEventHandler"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public void DetachEventHandler(string eventName, EventHandler eventHandler)
        {
            HtmlDocumentShim shim = DocumentShim;
            if (shim != null)
            {
                shim.DetachEventHandler(eventName, eventHandler);
            }
        }
 
        /// <include file='doc\HtmlDocument.uex' path='docs/doc[@for="HtmlDocument.Click"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public event HtmlElementEventHandler Click
        {
            add
            {
                DocumentShim.AddHandler(EventClick, value);
            }
            remove
            {
                DocumentShim.RemoveHandler(EventClick, value);
            }
        }
 
        /// <include file='doc\HtmlDocument.uex' path='docs/doc[@for="HtmlDocument.ContextMenuShowing"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public event HtmlElementEventHandler ContextMenuShowing
        {
            add
            {
                DocumentShim.AddHandler(EventContextMenuShowing, value);
            }
            remove
            {
                DocumentShim.RemoveHandler(EventContextMenuShowing, value);
            }
        }
 
        /// <include file='doc\HtmlDocument.uex' path='docs/doc[@for="HtmlDocument.Focusing"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public event HtmlElementEventHandler Focusing
        {
            add
            {
                DocumentShim.AddHandler(EventFocusing, value);
            }
            remove
            {
                DocumentShim.RemoveHandler(EventFocusing, value);
            }
        }
 
        /// <include file='doc\HtmlDocument.uex' path='docs/doc[@for="HtmlDocument.LosingFocus"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public event HtmlElementEventHandler LosingFocus
        {
            add
            {
                DocumentShim.AddHandler(EventLosingFocus, value);
            }
            remove
            {
                DocumentShim.RemoveHandler(EventLosingFocus, value);
            }
        }
 
        /// <include file='doc\HtmlDocument.uex' path='docs/doc[@for="HtmlDocument.MouseDown"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public event HtmlElementEventHandler MouseDown
        {
            add
            {
                DocumentShim.AddHandler(EventMouseDown, value);
            }
            remove
            {
                DocumentShim.RemoveHandler(EventMouseDown, value);
            }
        }
 
        /// <include file='doc\HtmlDocument.uex' path='docs/doc[@for="HtmlDocument.MouseLeave"]/*' />
        /// <devdoc>
        ///    <para>Occurs when the mouse leaves the document</para>
        /// </devdoc>
        public event HtmlElementEventHandler MouseLeave
        {
            add
            {
                DocumentShim.AddHandler(EventMouseLeave, value);
            }
            remove
            {
                DocumentShim.RemoveHandler(EventMouseLeave, value);
            }
        }
 
        /// <include file='doc\HtmlDocument.uex' path='docs/doc[@for="HtmlDocument.MouseMove"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public event HtmlElementEventHandler MouseMove
        {
            add
            {
                DocumentShim.AddHandler(EventMouseMove, value);
            }
            remove
            {
                DocumentShim.RemoveHandler(EventMouseMove, value);
            }
        }
 
        /// <include file='doc\HtmlDocument.uex' path='docs/doc[@for="HtmlDocument.MouseOver"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public event HtmlElementEventHandler MouseOver
        {
            add
            {
                DocumentShim.AddHandler(EventMouseOver, value);
            }
            remove
            {
                DocumentShim.RemoveHandler(EventMouseOver, value);
            }
 
        }
 
        /// <include file='doc\HtmlDocument.uex' path='docs/doc[@for="HtmlDocument.MouseUp"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public event HtmlElementEventHandler MouseUp
        {
            add
            {
                DocumentShim.AddHandler(EventMouseUp, value);
            }
            remove
            {
                DocumentShim.RemoveHandler(EventMouseUp, value);
            }
        }
 
        /// <include file='doc\HtmlDocument.uex' path='docs/doc[@for="HtmlDocument.Stop"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public event HtmlElementEventHandler Stop
        {
            add
            {
                DocumentShim.AddHandler(EventStop, value);
            }
            remove
            {
                DocumentShim.RemoveHandler(EventStop, value);
            }
        }
 
        //
        // Private helper methods:
        //
        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        private struct FindSizeOfVariant
        {
            [MarshalAs(UnmanagedType.Struct)]
            public object var;
            public byte b;
        }
        private static readonly int VariantSize = (int)Marshal.OffsetOf(typeof(FindSizeOfVariant), "b");
        //
        // Convert a object[] into an array of VARIANT, allocated with CoTask allocators.
        internal unsafe static IntPtr ArrayToVARIANTVector(object[] args)
        {
            int len = args.Length;
            IntPtr mem = Marshal.AllocCoTaskMem(len * VariantSize);
            byte* a = (byte*)(void*)mem;
            for (int i = 0; i < len; ++i)
            {
                Marshal.GetNativeVariantForObject(args[i], (IntPtr)(a + VariantSize * i));
            }
            return mem;
        }
 
        //
        // Free a Variant array created with the above function
        internal unsafe static void FreeVARIANTVector(IntPtr mem, int len)
        {
            byte* a = (byte*)(void*)mem;
            for (int i = 0; i < len; ++i)
            {
                SafeNativeMethods.VariantClear(new HandleRef(null, (IntPtr)(a + VariantSize * i)));
            }
            Marshal.FreeCoTaskMem(mem);
        }
 
        private Color ColorFromObject(object oColor)
        {
            try
            {
                if (oColor is string)
                {
                    string strColor = oColor as String;
                    int index = strColor.IndexOf('#');
                    if (index >= 0)
                    {
                        // The string is of the form: #ff00a0. Skip past the #
                        string hexColor = strColor.Substring(index + 1);
                        // The actual color is non-transparent. So set alpha = 255.
                        return Color.FromArgb(255, Color.FromArgb(int.Parse(hexColor, NumberStyles.HexNumber, CultureInfo.InvariantCulture)));
                    }
                    else
                    {
                        return Color.FromName(strColor);
                    }
                }
                else if (oColor is int)
                {
                    // The actual color is non-transparent. So set alpha = 255.
                    return Color.FromArgb(255, Color.FromArgb((int)oColor));
                }
            }
            catch (Exception ex)
            {
                if (ClientUtils.IsSecurityOrCriticalException(ex))
                {
                    throw;
                }
            }
 
            return Color.Empty;
        }
 
 
        ///<devdoc>
        /// HtmlDocumentShim - this is the glue between the DOM eventing mechanisms
        ///                    and our CLR callbacks.  
        ///             
        ///  There are two kinds of events: HTMLWindowEvents2 and IHtmlWindow3.AttachHandler style
        ///     HTMLDocumentEvents2: we create an IConnectionPoint (via ConnectionPointCookie) between us and MSHTML and it calls back
        ///                        on our an instance of HTMLDocumentEvents2.  The HTMLDocumentEvents2 class then fires the event.
        ///
        ///     IHTMLDocument3.AttachHandler: MSHML calls back on an HtmlToClrEventProxy that we've created, looking
        ///                                 for a method named DISPID=0.  For each event that's subscribed, we create 
        ///                                 a new HtmlToClrEventProxy, detect the callback and fire the corresponding
        ///                                 CLR event.
        ///</devdoc>      
        internal class HtmlDocumentShim : HtmlShim
        {
            private AxHost.ConnectionPointCookie cookie;
            private HtmlDocument htmlDocument;
            private UnsafeNativeMethods.IHTMLWindow2 associatedWindow = null;
 
            internal HtmlDocumentShim(HtmlDocument htmlDocument)
            {
                this.htmlDocument = htmlDocument;
                // snap our associated window so we know when to disconnect.
                if (this.htmlDocument != null)
                {
                    HtmlWindow window = htmlDocument.Window;
                    if (window != null)
                    {
                        associatedWindow = window.NativeHtmlWindow;
                    }
                }
            }
 
            public override UnsafeNativeMethods.IHTMLWindow2 AssociatedWindow
            {
                get { return associatedWindow; }
            }
 
            public UnsafeNativeMethods.IHTMLDocument2 NativeHtmlDocument2
            {
                get { return htmlDocument.NativeHtmlDocument2; }
            }
 
            internal HtmlDocument Document
            {
                get { return htmlDocument; }
            }
 
            /// Support IHtmlDocument3.AttachHandler
            public override void AttachEventHandler(string eventName, EventHandler eventHandler)
            {
 
                // IE likes to call back on an IDispatch of DISPID=0 when it has an event, 
                // the HtmlToClrEventProxy helps us fake out the CLR so that we can call back on 
                // our EventHandler properly.
 
                HtmlToClrEventProxy proxy = AddEventProxy(eventName, eventHandler);
                bool success = ((UnsafeNativeMethods.IHTMLDocument3)this.NativeHtmlDocument2).AttachEvent(eventName, proxy);
                Debug.Assert(success, "failed to add event");
            }
 
            /// Support IHtmlDocument3.DetachHandler
            public override void DetachEventHandler(string eventName, EventHandler eventHandler)
            {
                HtmlToClrEventProxy proxy = RemoveEventProxy(eventHandler);
                if (proxy != null)
                {
                    ((UnsafeNativeMethods.IHTMLDocument3)this.NativeHtmlDocument2).DetachEvent(eventName, proxy);
                }
 
            }
 
            //
            // Connect to standard events
            //
            public override void ConnectToEvents()
            {
                if (cookie == null || !cookie.Connected)
                {
                    this.cookie = new AxHost.ConnectionPointCookie(this.NativeHtmlDocument2,
                                                                          new HTMLDocumentEvents2(htmlDocument),
                                                                          typeof(UnsafeNativeMethods.DHTMLDocumentEvents2),
                                                                          /*throwException*/ false);
                    if (!cookie.Connected) 
                    {
                        cookie = null;
                    }
                }
            }
 
            //
            // Disconnect from standard events
            //
            public override void DisconnectFromEvents()
            {
                if (this.cookie != null)
                {
                    this.cookie.Disconnect();
                    this.cookie = null;
                }
            }
 
            protected override void Dispose(bool disposing)
            {
                base.Dispose(disposing);
                if (disposing)
                {
                    if (htmlDocument != null)
                    {
                        Marshal.FinalReleaseComObject(htmlDocument.NativeHtmlDocument2);
                    }
                    htmlDocument = null;
                }
 
            }
 
            protected override object GetEventSender()
            {
                return htmlDocument;
            }
        }
 
        //
        // Private classes:
        //
        [ClassInterface(ClassInterfaceType.None)]
        private class HTMLDocumentEvents2 : StandardOleMarshalObject, /*Enforce calling back on the same thread*/
                                            UnsafeNativeMethods.DHTMLDocumentEvents2
        {
            private HtmlDocument parent;
 
            public HTMLDocumentEvents2(HtmlDocument htmlDocument)
            {
                this.parent = htmlDocument;
            }
 
            private void FireEvent(object key, EventArgs e)
            {
                if (this.parent != null)
                {
                    parent.DocumentShim.FireEvent(key, e);
                }
            }
 
            public bool onclick(UnsafeNativeMethods.IHTMLEventObj evtObj)
            {
                HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj);
                FireEvent(HtmlDocument.EventClick, e);
 
                return e.ReturnValue;
            }
 
            public bool oncontextmenu(UnsafeNativeMethods.IHTMLEventObj evtObj)
            {
                HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj);
                FireEvent(HtmlDocument.EventContextMenuShowing, e);
                return e.ReturnValue;
            }
 
            public void onfocusin(UnsafeNativeMethods.IHTMLEventObj evtObj)
            {
                HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj);
                FireEvent(HtmlDocument.EventFocusing, e);
            }
 
            public void onfocusout(UnsafeNativeMethods.IHTMLEventObj evtObj)
            {
                HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj);
                FireEvent(HtmlDocument.EventLosingFocus, e);
            }
 
            public void onmousemove(UnsafeNativeMethods.IHTMLEventObj evtObj)
            {
                HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj);
                FireEvent(HtmlDocument.EventMouseMove, e);
            }
 
            public void onmousedown(UnsafeNativeMethods.IHTMLEventObj evtObj)
            {
                HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj);
                FireEvent(HtmlDocument.EventMouseDown, e);
            }
 
            public void onmouseout(UnsafeNativeMethods.IHTMLEventObj evtObj)
            {
                HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj);
                FireEvent(HtmlDocument.EventMouseLeave, e);
            }
 
            public void onmouseover(UnsafeNativeMethods.IHTMLEventObj evtObj)
            {
                HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj);
                FireEvent(HtmlDocument.EventMouseOver, e);
            }
 
            public void onmouseup(UnsafeNativeMethods.IHTMLEventObj evtObj)
            {
                HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj);
                FireEvent(HtmlDocument.EventMouseUp, e);
            }
 
            public bool onstop(UnsafeNativeMethods.IHTMLEventObj evtObj)
            {
                HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj);
                FireEvent(HtmlDocument.EventStop, e);
                return e.ReturnValue;
            }
 
            public bool onhelp(UnsafeNativeMethods.IHTMLEventObj evtObj)
            {
                HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj);
                return e.ReturnValue;
            }
 
            public bool ondblclick(UnsafeNativeMethods.IHTMLEventObj evtObj)
            {
                HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj);
                return e.ReturnValue;
            }
 
            public void onkeydown(UnsafeNativeMethods.IHTMLEventObj evtObj) { }
            
            public void onkeyup(UnsafeNativeMethods.IHTMLEventObj evtObj) { }
            
            public bool onkeypress(UnsafeNativeMethods.IHTMLEventObj evtObj)
            {
                HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj);
                return e.ReturnValue;
            }
            
            public void onreadystatechange(UnsafeNativeMethods.IHTMLEventObj evtObj) { }
            
            public bool onbeforeupdate(UnsafeNativeMethods.IHTMLEventObj evtObj)
            {
                HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj);
                return e.ReturnValue;
            }
            
            public void onafterupdate(UnsafeNativeMethods.IHTMLEventObj evtObj) { }
            
            public bool onrowexit(UnsafeNativeMethods.IHTMLEventObj evtObj)
            {
                HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj);
                return e.ReturnValue;
            }
            
            public void onrowenter(UnsafeNativeMethods.IHTMLEventObj evtObj) { }
            
            public bool ondragstart(UnsafeNativeMethods.IHTMLEventObj evtObj)
            {
                HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj);
                return e.ReturnValue;
            }
            
            public bool onselectstart(UnsafeNativeMethods.IHTMLEventObj evtObj)
            {
                HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj);
                return e.ReturnValue;
            }
            
            public bool onerrorupdate(UnsafeNativeMethods.IHTMLEventObj evtObj)
            {
                HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj);
                return e.ReturnValue;
            }
            
            public void onrowsdelete(UnsafeNativeMethods.IHTMLEventObj evtObj) { }
            
            public void onrowsinserted(UnsafeNativeMethods.IHTMLEventObj evtObj) { }
            
            public void oncellchange(UnsafeNativeMethods.IHTMLEventObj evtObj) { }
            
            public void onpropertychange(UnsafeNativeMethods.IHTMLEventObj evtObj) { }
            
            public void ondatasetchanged(UnsafeNativeMethods.IHTMLEventObj evtObj) { }
            
            public void ondataavailable(UnsafeNativeMethods.IHTMLEventObj evtObj) { }
            
            public void ondatasetcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj) { }
            
            public void onbeforeeditfocus(UnsafeNativeMethods.IHTMLEventObj evtObj) { }
            
            public void onselectionchange(UnsafeNativeMethods.IHTMLEventObj evtObj) { }
            
            public bool oncontrolselect(UnsafeNativeMethods.IHTMLEventObj evtObj)
            {
                HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj);
                return e.ReturnValue;
            }
            
            public bool onmousewheel(UnsafeNativeMethods.IHTMLEventObj evtObj)
            {
                HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj);
                return e.ReturnValue;
            }
            
            public void onactivate(UnsafeNativeMethods.IHTMLEventObj evtObj) { }
            
            public void ondeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj) { }
            
            public bool onbeforeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj)
            {
                HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj);
                return e.ReturnValue;
            }
            
            public bool onbeforedeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj)
            {
                HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj);
                return e.ReturnValue;
            }
        }
 
            #region operators
 
        /// <include file='doc\HtmlWindow.uex' path='docs/doc[@for="HtmlElement.operatorEQ"]/*' />
        [SuppressMessage("Microsoft.Design", "CA1046:DoNotOverrideOperatorEqualsOnReferenceTypes")]
        public static bool operator ==(HtmlDocument left, HtmlDocument right)
        {
            //Not equal if only one's null.
            if (object.ReferenceEquals(left, null) != object.ReferenceEquals(right, null))
            {
                return false;
            }
 
            //Equal if both are null.
            if (object.ReferenceEquals(left, null))
            {
                return true;
            }
 
            //Neither are null.  Get the IUnknowns and compare them.
            IntPtr leftPtr = IntPtr.Zero;
            IntPtr rightPtr = IntPtr.Zero;
            try
            {
                leftPtr = Marshal.GetIUnknownForObject(left.NativeHtmlDocument2);
                rightPtr = Marshal.GetIUnknownForObject(right.NativeHtmlDocument2);
                return leftPtr == rightPtr;
            }
            finally
            {
                if (leftPtr != IntPtr.Zero)
                {
                    Marshal.Release(leftPtr);
                }
                if (rightPtr != IntPtr.Zero)
                {
                    Marshal.Release(rightPtr);
                }
            }
        }
 
        /// <include file='doc\HtmlWindow.uex' path='docs/doc[@for="HtmlWindow.operatorNE"]/*' />
        public static bool operator !=(HtmlDocument left, HtmlDocument right)
        {
            return !(left == right);
        }
 
        /// <include file='doc\HtmlWindow.uex' path='docs/doc[@for="HtmlWindow.GetHashCode"]/*' />
        public override int GetHashCode()
        {
            return htmlDocument2 == null ? 0 : htmlDocument2.GetHashCode();
        }
 
        /// <include file='doc\HtmlWindow.uex' path='docs/doc[@for="HtmlWindow.Equals"]/*' />
        public override bool Equals(object obj)
        {
            return (this == (HtmlDocument)obj);
        }
            #endregion
 
    }
}