File: src\Framework\MS\Internal\AppModel\RootBrowserWindow.cs
Project: wpf\PresentationFramework.csproj (PresentationFramework)
//---------------------------------------------------------------------------
//
// File: RootBrowserWindow.cs
//
// Description: This class will implement the hosting and bridging logic
//              between the browser and Avalon.  This will be the top
//              level "frame" that hosts all content in IE.  This class
//              will not be public.
//
// Created: 04/28/03
//
// Copyright (C) 2001 by Microsoft Corporation.  All rights reserved.
//
//  History:
//      hamidm  06/11/03    moved over to wcp tree
//---------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Windows;
using System.Windows.Automation.Peers;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Navigation;
using MS.Internal.Commands;
using MS.Internal.Controls;
using MS.Win32;
 
//In order to avoid generating warnings about unknown message numbers and
//unknown pragmas when compiling your C# source code with the actual C# compiler,
//you need to disable warnings 1634 and 1691. (Presharp Documentation)
#pragma warning disable 1634, 1691
 
 
namespace MS.Internal.AppModel
{
 
    /// <summary>
    ///
    /// </summary>
    internal sealed class RootBrowserWindow : NavigationWindow, IWindowService, IJournalNavigationScopeHost
    {
        //----------------------------------------------
        //
        // Constructors
        //
        //----------------------------------------------
        #region Constructors
 
 
        static RootBrowserWindow()
        {
            CommandHelpers.RegisterCommandHandler(typeof(RootBrowserWindow), ApplicationCommands.Print,
                    new ExecutedRoutedEventHandler(OnCommandPrint), new CanExecuteRoutedEventHandler(OnQueryEnabledCommandPrint));
        }
 
        /// <summary>
        ///
        /// </summary>
        /// <SecurityNote>
        ///     Critical:Calls base class constructor that is only present for RBW scenario
        /// </SecurityNote>
        [SecurityCritical]
        private RootBrowserWindow():base(true)
        {
            // Allow tabbing out to the browser - see KeyInputSite and OnKeyDown().
 
            // IE 6 doesn't provide the necessary support; notice IsDownlevelPlatform covers Firefox too,
            // hence the additional IsHostedInIE check.
            // By checking for the negative top-level case, we allow to tab out of XBAPs hosted in an iframe;
            // this support is enabled regardless of the browser we're on (to avoid browser-specifics here).
            bool isIE6 = IsDownlevelPlatform && BrowserInteropHelper.IsHostedInIEorWebOC;
            if (!(isIE6 && BrowserInteropHelper.IsAvalonTopLevel))
            {
                SetValue(KeyboardNavigation.TabNavigationProperty, KeyboardNavigationMode.Continue);
                SetValue(KeyboardNavigation.ControlTabNavigationProperty, KeyboardNavigationMode.Continue);
            }
        }
        #endregion Constructors
 
        //----------------------------------------------
        //
        // Protected Methods
        //
        //----------------------------------------------
        #region Protected Methods
 
        /// <summary>
        /// Creates AutomationPeer (<see cref="UIElement.OnCreateAutomationPeer"/>)
        /// </summary>
        protected override AutomationPeer OnCreateAutomationPeer()
        {
            return new RootBrowserWindowAutomationPeer(this);
        }
 
        /// <SecurityNote>
        ///     Critical - Hooks up the event handler that sets the status bar text (OnRequestSetStatusBar_Hyperlink).
        ///     TreatAsSafe - Safe to set up the handler over here; OnRequestSetStatusBar_Hyperlink is connected to
        ///                   RequestSetStatusBarEvent which is supposed to be raised only by Hyperlink which has the
        ///                   anti-spoofing mitigations in place. The RequestSetStatusBarEventArgs used in the event
        ///                   is protected as well.
        /// </SecurityNote>
        [SecurityCritical, SecurityTreatAsSafe]
        protected override void OnInitialized(EventArgs args)
        {
            AddHandler(Hyperlink.RequestSetStatusBarEvent, new RoutedEventHandler(OnRequestSetStatusBar_Hyperlink));
            base.OnInitialized(args);
        }
 
        protected override Size MeasureOverride(Size constraint)
        {
            return base.MeasureOverride(GetSizeInLogicalUnits());
        }
 
        protected override Size ArrangeOverride(Size arrangeBounds)
        {
            // Get the size of the avalon window and pass it to
            // the base implementation. The return value tells the Size
            // that we are occupying.  Since, we are RBW we will occupy the
            // entire available size and thus we don't care what our child wants.
            base.ArrangeOverride(GetSizeInLogicalUnits());
            return arrangeBounds;
        }
 
        protected override void OnStateChanged(EventArgs e)
        {
        }
 
        protected override void OnLocationChanged(EventArgs e)
        {
        }
 
        protected override void OnClosing(CancelEventArgs e)
        {
        }
 
        protected override void OnClosed(EventArgs e)
        {
        }
 
        ///<SecurityNote>
        /// Critical - calls the SUC'd IBCS.PostReadyStateChange().
        /// TreatAsSafe - Only READYSTATE_COMPLETE is posted, once, at the end of the activation sequence,
        ///     when the browser expects it.
        ///</SecurityNote>
        [SecurityCritical, SecurityTreatAsSafe]
        protected override void OnContentRendered(EventArgs e)
        {
            base.OnContentRendered(e);
 
            // Posting READYSTATE_COMPLETE triggers the WebOC's DocumentComplete event.
            // Media Center, in particular, uses this to make the WebOC it hosts visible.
            if (!_loadingCompletePosted)
            {
                Browser.PostReadyStateChange(READYSTATE_COMPLETE);
                _loadingCompletePosted = true;
            }
        }
 
        protected override void OnKeyDown(KeyEventArgs e)
        {
            // In browser apps, Ctrl+Tab switches tabs. F6 simulates it here.
            if (e.Key == Key.F6 && (e.KeyboardDevice.Modifiers & ~ModifierKeys.Shift) == 0)
            {
                if (KeyboardNavigation.Navigate(
                        e.KeyboardDevice.FocusedElement as DependencyObject ?? this,
                        Key.Tab, e.KeyboardDevice.Modifiers | ModifierKeys.Control))
                {
                    e.Handled = true;
                }
            }
 
            if (!e.Handled)
            {
                base.OnKeyDown(e);
            }
        }
 
        #endregion Protected methods
 
        //----------------------------------------------
        //
        // Internal Methods
        //
        //----------------------------------------------
        #region Internal Methods
 
        /// <summary>
        ///     Creates the RBW object and sets the Style property on it
        ///     to the correct value
        /// </summary>
        /// <SecurityNote>
        ///     Critical: Calls RBW class constructor
        /// </SecurityNote>
        [SecurityCritical]
        internal static RootBrowserWindow CreateAndInitialize()
        {
            RootBrowserWindow rbw = new RootBrowserWindow();
            rbw.InitializeRBWStyle();
            return rbw;
        }
 
        /// <SecurityNote>
        /// Critical: This code elevates to all window permission
        /// </SecurityNote>
        [SecurityCritical]
        internal override void CreateAllStyle()
        {
            Invariant.Assert(App != null, "RootBrowserWindow must be created in an Application");
 
            IHostService ihs = (IHostService)App.GetService(typeof(IHostService));
 
            Invariant.Assert(ihs!=null, "IHostService in RootBrowserWindow cannot be null");
            Invariant.Assert(ihs.HostWindowHandle != IntPtr.Zero, "IHostService.HostWindowHandle in RootBrowserWindow cannot be null");
 
            //This sets the _ownerHandle correctly and will be used to create the _sourceWindow
            //with the correct parent
            UIPermission uip = new UIPermission(UIPermissionWindow.AllWindows);
            uip.Assert();//Blessed Assert to set owner handle and to set styles
            try
            {
                this.OwnerHandle = ihs.HostWindowHandle;
                this.Win32Style = NativeMethods.WS_CHILD | NativeMethods.WS_CLIPCHILDREN | NativeMethods.WS_CLIPSIBLINGS;
            }
            finally
            {
                CodeAccessPermission.RevertAssert();
            }
 
        }
 
        ///<SecurityNote>
        /// Critical: Exposes a window handle (ParentWindow). Base class implementation is also Critical.
        ///</SecurityNote>
        [SecurityCritical]
        internal override HwndSourceParameters CreateHwndSourceParameters()
        {
            HwndSourceParameters parameters = base.CreateHwndSourceParameters();
            parameters.TreatAsInputRoot = true;
            parameters.TreatAncestorsAsNonClientArea = true;
            return parameters;
        }
 
        /// <summary>
        ///     Override for SourceWindow creation.
        ///     Virtual only so that we may assert.
        /// </summary>
        /// <SecurityNote>
        ///     Critical - Calls critical base-class function.
        ///                Calls critical functions GetAncestor and GetForegroundWindow.
        /// </SecurityNote>
        [SecurityCritical]
        internal override void CreateSourceWindowDuringShow()
        {
            Browser.OnBeforeShowNavigationWindow();
 
            base.CreateSourceWindowDuringShow();
            Invariant.Assert(IsSourceWindowNull == false, "Failed to create HwndSourceWindow for browser hosting");
 
            // _sourceWindowCreationCompleted specifies that HwndSource creation has completed.  This is used
            // to minimize the SetWindowPos calls from ResizeMove when Height/Width is set prior to calling
            // show on RBW (this also occurs when Height/Width is set in the style of RBW)
            _sourceWindowCreationCompleted = true;
 
            if (_rectSet)
            {
                _rectSet = false;
                ResizeMove(_xDeviceUnits, _yDeviceUnits, _widthDeviceUnits, _heightDeviceUnits);
            }
            // The above window move (resize) operation causes message dispatching, which allows reentrancy
            // from the browser, which can initiate premature shutdown.
            if (IsSourceWindowNull)
                return;
 
            SetUpInputHooks();
 
            //
            // While the RBW is created and shown, the Top Browser window should have already been created and shown,
            // Browser doesn't notify this RBW of the activation status again unless user activates/deactivates the main
            // window. It is time to transfer the Top Browser Window's activation status to this RBW so that user's
            // code can get correct status through property IsActivate.
            //
            IntPtr topWindow = UnsafeNativeMethods.GetAncestor(new HandleRef(this, CriticalHandle), 2/*GA_ROOT*/);
            Debug.Assert(topWindow != IntPtr.Zero);
            IntPtr activeWindow = UnsafeNativeMethods.GetForegroundWindow();
            HandleActivate(activeWindow == topWindow);
        }
 
        // No need to clear App.MainWindow for RBW case.  It throws an exception if attempted
        internal override void TryClearingMainWindow()
        {
        }
 
        internal override void CorrectStyleForBorderlessWindowCase()
        {
        }
 
        internal override void GetRequestedDimensions(ref double requestedLeft, ref double requestedTop, ref double requestedWidth, ref double requestedHeight)
        {
            requestedTop = 0;
            requestedLeft = 0;
            requestedWidth = this.Width;
            requestedHeight = this.Height;
        }
 
        ///<SecurityNote>
        ///     Critical - It also calls critical method (SetRootVisual)
        ///</SecurityNote>
        [SecurityCritical]
        internal override void SetupInitialState(double requestedTop, double requestedLeft, double requestedWidth, double requestedHeight)
        {
            // If RBW Height/Width was set before calling show in RBW, we need
            // to update the browser with that size now
            SetBrowserSize();
            SetRootVisual();
        }
 
        internal override int nCmdForShow()
        {
            return NativeMethods.SW_SHOW;
        }
 
        internal override bool HandleWmNcHitTestMsg(IntPtr lParam, ref IntPtr refInt)
        {
            return false;
        }
 
        internal override WindowMinMax GetWindowMinMax()
        {
            return new WindowMinMax(0, double.PositiveInfinity);
        }
 
        // RBW overrides WmMoveChangedHelper default behavior where it calls
        // either SetValue/CoerceValue on Top/Left DPs since that will
        // throw an exception for RBW.  Furthermore, in RBW, we don't want
        // to fire LocationChanged event.
        internal override void WmMoveChangedHelper()
        {
        }
 
        /// <summary>
        ///     Resizes and moves the RBW (which is a WS_CHILD window).  This is called by
        ///     AppProxyInternal when the host callsbacks into it with the new size/location.
        ///     We need this internal since Height/Width/Top/Left on RBW govern the host'ss
        ///     properties.
        /// </summary>
        /// <remarks>
        ///     The location of WS_CHILD window is relative to it parent window thus when the
        ///     browser moves it does not call this method since the relative position of this window
        ///     wrt the browser hasn't changed.
        /// </remarks>
        /// <param name="xDeviceUnits">New left of the RBW</param>
        /// <param name="yDeviceUnits">New top of the RBW</param>
        /// <param name="widthDeviceUnits">New width of the RBW</param>
        /// <param name="heightDeviceUnits">New height of the RBW</param>
        ///<SecurityNote>
        ///     Critical as this method handles critical data.
        ///     TreatAsSafe - as the RBW is always contained within the browser's window.
        ///                          you can move the internal window all you want - but given that you're really
        ///                          contained within the BrowserWindow - the worse you could do is make some of your content not visible.
        ///</SecurityNote>
        [SecurityCritical, SecurityTreatAsSafe]
        internal void ResizeMove(int xDeviceUnits, int yDeviceUnits, int widthDeviceUnits, int heightDeviceUnits)
        {
            // _sourceWindowCreationCompleted specifies that HwndSource creation has completed.  This is used
            // to minimize the SetWindowPos calls from ResizeMove when Height/Width is set prior to calling
            // show on RBW (this also occurs when Height/Width is set in the style of RBW).  Thus, we want to
            // resize the underlying avalon hwnd only after its creation is completed
            if (_sourceWindowCreationCompleted == false)
            {
                _xDeviceUnits = xDeviceUnits;
                _yDeviceUnits = yDeviceUnits;
                _widthDeviceUnits = widthDeviceUnits;
                _heightDeviceUnits = heightDeviceUnits;
 
                _rectSet = true;
 
                return;
            }
 
            Invariant.Assert(IsSourceWindowNull == false, "sourceWindow cannot be null if _sourceWindowCreationCompleted is true");
 
            HandleRef handleRef;
            handleRef = new HandleRef( this, CriticalHandle ) ;
 
            UnsafeNativeMethods.SetWindowPos( handleRef ,
                           NativeMethods.NullHandleRef,
                           xDeviceUnits,
                           yDeviceUnits,
                           widthDeviceUnits,
                           heightDeviceUnits,
                           NativeMethods.SWP_NOZORDER
                           | NativeMethods.SWP_NOACTIVATE
                           | NativeMethods.SWP_SHOWWINDOW);
        }
 
        /// <summary>
        ///     This is called when the Title dependency property changes in the Window.
        /// </summary>
        ///<SecurityNote>
        /// Critical - calls SUC'd IHostBrowser.SetTitle
        /// TreatAsSafe - setting the text on the browser's window is considered safe.
        ///                 - spoofing is not possible as the browser prepends it's own string to the string
        ///                      e.g. Microsoft Internet Explorer
        ///                 - it can be done in partial trust in HTML via the TITLE tag.
        ///</SecurityNote>
        [SecurityCritical, SecurityTreatAsSafe]
        internal override void UpdateTitle(string titleStr)
        {
            IBrowserCallbackServices ibcs = Browser;
            if (ibcs != null) // null on shutdown
            {
                string title = PruneTitleString(titleStr);
 
                // It's not the end of the world if this fails.
                // VS's browser in particular returns RPC_E_CALL_REJECTED.
                // Not sure of other exceptions that might be thrown here,
                // so keeping this scoped to what's been reported.
                const int RPC_E_CALL_REJECTED = unchecked((int)0x80010001);
                try
                {
                    // SHOULD NOT CALL BASE; BASE IMPLEMENTS TEXT PROPERTY ON WINDOW
                    BrowserInteropHelper.HostBrowser.SetTitle(title);
                }
                catch (COMException e)
                {
                    if (e.ErrorCode != RPC_E_CALL_REJECTED)
                    {
                        throw;
                    }
                }
            }
        }
 
        /// <summary>
        ///     This is called by NavigationService to set the status bar content
        ///     for the browser
        /// </summary>
        /// <remarks>
        ///     We propagate object.ToString() to the browser's status bar.
        /// </remarks>
        ///<SecurityNote>
        ///     Critical - calls SetStatusText which is SUC'ed.
        ///
        ///     No longer considered TreatAsSafe in order to protect against hyperlink spoofing.
        ///     Browsers implement access restriction to the status bar from script nowadays.
        ///</SecurityNote>
        [SecurityCritical]
        internal void SetStatusBarText(string statusString)
        {
            if (BrowserInteropHelper.HostBrowser != null) // could be null if shutting down
            {
                BrowserInteropHelper.HostBrowser.SetStatusText(statusString);
            }
        }
 
        /// <summary>
        ///     Called to update Height of the browser.  Currently, this method is called from
        ///     two places:
        ///
        ///     1) OnHeightInvalidated from window.cs
        ///     2) SetBrowserSize in RBW
        /// </summary>
        ///<SecurityNote>
        ///     Critical - Can be used to change the size of the browser
        ///     TreatAsSafe - clamps values so that window cannot be sized greater than desktop bounds.
        ///</SecurityNote>
        [SecurityCritical, SecurityTreatAsSafe]
        internal override void UpdateHeight(double newHeightLogicalUnits)
        {
            Point sizeDeviceUnits = LogicalToDeviceUnits(new Point(0, newHeightLogicalUnits));
            uint heightDeviceUnits = (uint)Math.Round(sizeDeviceUnits.Y);
 
            if (BrowserInteropHelper.HostBrowser != null)
            {
                //
                // Note: right now IE is clipping browser height to desktop size.
                // However it should be clipped to available desktop size. See Windows OS Bug #1045038
                // The code below is to fix this for now.
                // Even if IE's code is changed - likely we keep this as defense in-depth.
                //
 
                uint maxHeightDeviceUnits = GetMaxWindowHeight();
                heightDeviceUnits = heightDeviceUnits > maxHeightDeviceUnits ? maxHeightDeviceUnits : heightDeviceUnits;
                heightDeviceUnits = heightDeviceUnits < MIN_BROWSER_HEIGHT_DEVICE_UNITS ? MIN_BROWSER_HEIGHT_DEVICE_UNITS : heightDeviceUnits;
                BrowserInteropHelper.HostBrowser.SetHeight(heightDeviceUnits);
            }
        }
 
        ///<SecurityNote>
        ///     Critical - Can be used to change the size of the browser
        ///     TreatAsSafe - clamps values so that window cannot be sized greater than desktop bounds.
        ///</SecurityNote>
        [SecurityCritical, SecurityTreatAsSafe]
        internal override void UpdateWidth(double newWidthLogicalUnits)
        {
            Point sizeDeviceUnits = LogicalToDeviceUnits(new Point(newWidthLogicalUnits, 0));
            uint widthDeviceUnits  = (uint)Math.Round(sizeDeviceUnits.X);
 
            if (BrowserInteropHelper.HostBrowser != null)
            {
                //
                // Note: right now IE is clipping browser width to desktop size.
                // However it should be clipped to available desktop size. See Windows OS Bug #1045038
                // The code below is to fix this for now.
                // Even if IE's code is changed - likely we keep this as defense in-depth.
                //
                uint maxWidthDeviceUnits = GetMaxWindowWidth();
                widthDeviceUnits = widthDeviceUnits > maxWidthDeviceUnits ? maxWidthDeviceUnits : widthDeviceUnits;
 
                widthDeviceUnits = widthDeviceUnits < MIN_BROWSER_WIDTH_DEVICE_UNITS ? MIN_BROWSER_WIDTH_DEVICE_UNITS : widthDeviceUnits;
                BrowserInteropHelper.HostBrowser.SetWidth(widthDeviceUnits);
            }
        }
 
        /// <summary>
        /// When being reloaded from history in the browser, we need to
        /// set up the journal again
        /// </summary>
        /// <param name="journal"></param>
        internal void SetJournalForBrowserInterop(Journal journal)
        {
            Invariant.Assert(journal != null, "Failed to get Journal for browser integration");
            base.JournalNavigationScope.Journal = journal;
        }
 
        void IJournalNavigationScopeHost.OnJournalAvailable()
        {
            base.Journal.BackForwardStateChange += new EventHandler(HandleBackForwardStateChange);
        }
 
        // For downlevel platforms, we don't have integration with the journal, so we have to
        // use our own Journal.
        ///<SecurityNote>
        ///     Critical - calls IBCS.GoBack
        ///</SecurityNote>
        [SecurityCritical]
        bool IJournalNavigationScopeHost.GoBackOverride()
        {
            if (HasTravelLogIntegration)
            {
                if (BrowserInteropHelper.HostBrowser != null)
                {
                    try
                    {
                        BrowserInteropHelper.HostBrowser.GoBack();
                    }
#pragma warning disable 6502 // PRESharp - Catch statements should not have empty bodies
                    catch (OperationCanceledException)
                    {
                        // Catch the OperationCanceledException when the navigation is canceled.
                        // See comments in applicationproxyinternal._LoadHistoryStreamDelegate.
                    }
#pragma warning restore 6502
                }
                return true;
            }
            return false; // Proceed with internal GoBack.
        }
 
        // For downlevel platforms, we don't have integration with the journal, so we have to
        // use our own Journal.
        ///<SecurityNote>
        ///     Critical - calls IBCS.GoForward
        ///</SecurityNote>
        [SecurityCritical]
        bool IJournalNavigationScopeHost.GoForwardOverride()
        {
            if (HasTravelLogIntegration)
            {
                if (BrowserInteropHelper.HostBrowser != null)
                {
                    try
                    {
                        BrowserInteropHelper.HostBrowser.GoForward();
                    }
#pragma warning disable 6502 // PRESharp - Catch statements should not have empty bodies
                    catch (OperationCanceledException)
                    {
                        // Catch the OperationCanceledException when the navigation is canceled.
                        // See comments in applicationproxyinternal._LoadHistoryStreamDelegate.
                    }
#pragma warning restore 6502
                }
                return true;
            }
            return false; // Proceed with internal GoForward.
        }
 
        internal override void VerifyApiSupported()
        {
            throw new InvalidOperationException(SR.Get(SRID.NotSupportedInBrowser));
        }
 
        internal override void ClearResizeGripControl(Control oldCtrl)
        {
            // don't do anything here since we do not support
            // ResizeGrip for RBW
        }
 
        internal override void SetResizeGripControl(Control ctrl)
        {
            // don't do anything here since we do not support
            // ResizeGrip for RBW
        }
 
        // This is called in weboc's static constructor.
        internal void AddLayoutUpdatedHandler()
        {
            LayoutUpdated += new EventHandler(OnLayoutUpdated);
        }
 
        internal void TabInto(bool forward)
        {
            TraversalRequest tr = new TraversalRequest(
                forward ? FocusNavigationDirection.First : FocusNavigationDirection.Last);
            MoveFocus(tr);
        }
 
        #endregion Internal Methods
 
        //----------------------------------------------
        //
        // Private Methods
        //
        //----------------------------------------------
        #region Private methods
        /// <summary>
        ///     Sets the correct style property on the RBW object based on the
        ///     browser version
        /// </summary>
        private void InitializeRBWStyle()
        {
            // If we are on a downlevel platform, then we don't integrate with the browser's
            // travellog, so we have to use our own Journal and supply our own navigation chrome.
            if (!HasTravelLogIntegration)
            {
                SetResourceReference(StyleProperty, SystemParameters.NavigationChromeDownLevelStyleKey);
 
                // if the Template property is not defined in a custom style, the property system gets
                // the Template property value for the NavigationWindow from the theme file.  Since,
                // we want to get the Template property value from the browser styles, we need to
                // set the DefaultStyleKeyProperty here.
                SetValue(DefaultStyleKeyProperty, SystemParameters.NavigationChromeDownLevelStyleKey);
            }
            else
            {
                SetResourceReference(StyleProperty, SystemParameters.NavigationChromeStyleKey);
 
                // if the Template property is not defined in a custom style, the property system gets
                // the Template property value for the NavigationWindow from the theme file.  Since,
                // we want to get the Template property value from the browser styles, we need to
                // set the DefaultStyleKeyProperty here.
                SetValue(DefaultStyleKeyProperty, SystemParameters.NavigationChromeStyleKey);
            }
        }
 
        /// <SecurityNote>
        ///     Critical - Elevates to get access to the HwndSource and installs hooks.
        ///     TreatAsSafe - The HwndSoure is not exposed. The message hooks installed are internal ones, and they are for the RBW specifically.
        /// </SecurityNote>
        [SecurityCritical, SecurityTreatAsSafe]
        private void SetUpInputHooks()
        {
            IKeyboardInputSink sink;
            new UIPermission(PermissionState.Unrestricted).Assert(); //BlessedAssert
            try
            {
                _inputPostFilter = new HwndWrapperHook(BrowserInteropHelper.PostFilterInput);
                HwndSource hwndSource = base.HwndSourceWindow;
                hwndSource.HwndWrapper.AddHookLast(_inputPostFilter);
 
                sink = (IKeyboardInputSink)hwndSource;
            }
            finally
            {
                UIPermission.RevertAll();
            }
            new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert(); //BlessedAssert
            try
            {
                Debug.Assert(sink.KeyboardInputSite == null);
                sink.KeyboardInputSite = new KeyInputSite(new SecurityCriticalData<IKeyboardInputSink>(sink));
            }
            finally
            {
                SecurityPermission.RevertAll();
            }
        }
 
        /// <summary>
        ///     Updates browser size if Height/Width is not set to default value (NaN).  This
        ///     means that Height/Width was set prior to calling Show on RBW and we need to
        ///     propagate that to the browser.  This method is called from SetupInitialize which
        ///     is called from CreateSourceWindowImpl
        /// </summary>
        private void SetBrowserSize()
        {
            Point requestedSizeDeviceUnits = LogicalToDeviceUnits(new Point(this.Width, this.Height));
 
            // if Width was specified
            if (!DoubleUtil.IsNaN(this.Width))
            {
                // at this stage, ActualWidth/Height is not set since
                // layout has not happened (it happens when we set the
                // RootVisual of the HwndSource)
                UpdateWidth(requestedSizeDeviceUnits.X);
            }
 
            // if Height was specified
            if (!DoubleUtil.IsNaN(this.Height))
            {
                // at this stage, ActualWidth/Height is not set since
                // layout has not happened (it happens when we set the
                // RootVisual of the HwndSource)
                UpdateHeight(requestedSizeDeviceUnits.Y);
            }
        }
 
        private string PruneTitleString(string rawString)
        {
            StringBuilder sb = new StringBuilder();
            bool inMiddleOfWord = false;
 
            for (int i=0; i < rawString.Length; i++)
            {
                if (Char.IsWhiteSpace(rawString[i]) == false)
                {
                    sb.Append(rawString[i]);
                    inMiddleOfWord = true;
                }
                else
                {
                    if (inMiddleOfWord == true)
                    {
                        sb.Append(' ');
                        inMiddleOfWord = false;
                    }
                }
            }
 
            // remove the last space if it exists
            return sb.ToString().TrimEnd(' ');
        }
 
        private void OnLayoutUpdated(object obj, EventArgs args)
        {
            try
            {
                VerifyWebOCOverlap(NavigationService);
            }
            finally
            {
                _webBrowserList.Clear();
            }
        }
 
        private void VerifyWebOCOverlap(NavigationService navigationService)
        {
            for (int i = 0; i < navigationService.ChildNavigationServices.Count; i++)
            {
                NavigationService childNavService = (NavigationService)(navigationService.ChildNavigationServices[i]);
                WebBrowser webBrowser = childNavService.WebBrowser;
                if (webBrowser != null)
                {
                    for (int j = 0; j < _webBrowserList.Count; j++)
                    {
                        // Note: We are using WebBrowser.BoundRect, which is relative to parent window.
                        // Since WebBrowsers are all siblings child windows right now, e.g., we don't allow WebOC inside
                        // Popup window, this is a better performed way. If we change that, we should make sure the rects
                        // to compare are relative to desktop.
                        Rect rect = Rect.Intersect(webBrowser.BoundRect, _webBrowserList[j].BoundRect);
                        // Only when the intersect rect's Width and Height are both bigger than 0, we consider them overlapping.
                        // Even when 2 edges are next to each other, it is considered as intersect by the Rect class.
                        if ((rect.Width > 0) && (rect.Height > 0))
                        {
                            throw new InvalidOperationException(SR.Get(SRID.WebBrowserOverlap));
                        }
                    }
                    _webBrowserList.Add(webBrowser);
                }
                else
                {
                    VerifyWebOCOverlap(childNavService);
                }
            }
        }
 
        /// <summary>
        /// We are not using the CanGoBack/CanGoForward property change since that is fired only
        /// if the value changes eg. if we had 3 entries in the backstack, then a back navigation
        /// won't fire this since the value is still 'true' and has not changed.
        /// What we need is the UpdateView() notification or the BackForwardState change
        /// notification which is fired from UpdateView() of the Journal.
        /// Trying to hook the event will create the journal even if there was no navigation
        /// so just using an virtual override to do the work.
        /// </summary>
        ///<SecurityNote>
        ///     Critical - as this method calls into Browser call back method for back and forward.
        ///                This is a pinoke call.
        ///     TreatAsSafe - as this is a safe operation.
        ///</SecurityNote>
        [SecurityCritical, SecurityTreatAsSafe]
        private void HandleBackForwardStateChange(object sender, EventArgs args)
        {
            //Nothing to do for downlevel platform
            if (!HasTravelLogIntegration)
                return;
 
            IBrowserCallbackServices ibcs = Browser;
            if (ibcs != null)
            {
                ibcs.UpdateBackForwardState();
            }
        }
 
 
        ///<summary>
        /// Given a proposed width - and curWidth - return the MaxWidth the window can be opened to.
        /// Used to prevent sizing of window > desktop bounds in browser.
        ///</summary>
        ///<SecurityNote>
        /// Critical - calls back to browser to get the current left.
        /// TreatAsSafe - this value isn't returned or stored.
        ///               value returned is the current maximum width allowed considered safe.
        ///</SecurityNote>
        [SecurityCritical, SecurityTreatAsSafe ]
        private uint GetMaxWindowWidth()
        {
            NativeMethods.RECT desktopArea = WorkAreaBoundsForNearestMonitor;
            int browserLeft = BrowserInteropHelper.HostBrowser.GetLeft();
            uint curBrowserWidth = BrowserInteropHelper.HostBrowser.GetWidth();
 
            uint availableWidth = (uint)(desktopArea.right - browserLeft);
            uint maxWidth = availableWidth > curBrowserWidth ? availableWidth : curBrowserWidth;
            return maxWidth;
        }
 
        ///<summary>
        /// Given a proposed height - and curHeight - return the MaxHeight the window can be opened to.
        /// Used to prevent sizing of window > desktop bounds in browser.
        ///</summary>
        ///<SecurityNote>
        /// Critical - calls back to browser to get the current top.
        /// TreatAsSafe - this value isn't returned or stored.
        ///               value returned is the current maximum height allowed considered safe.
        ///</SecurityNote>
        [SecurityCritical, SecurityTreatAsSafe ]
        private uint GetMaxWindowHeight()
        {
            NativeMethods.RECT desktopArea = WorkAreaBoundsForNearestMonitor;
            int browserTop = BrowserInteropHelper.HostBrowser.GetTop();
            uint curBrowserHeight = BrowserInteropHelper.HostBrowser.GetHeight();
 
            uint availableHeight = (uint)(desktopArea.bottom - browserTop);
            uint maxHeight = availableHeight > curBrowserHeight ? availableHeight : curBrowserHeight;
            return maxHeight;
        }
 
 
        ///<summary>
        ///For browser hosting cases, we get the rects through OLE activation before we create the
        ///RootBrowserWindow. Even if SourceWindow or Handle are null, we can return the cached rects
        ///</summary>
        private Size GetSizeInLogicalUnits()
        {
            Size size;
 
            // Adding check for IsCompositionTargetInvalid as part of the fix for WOSB 1453012
            if (IsSourceWindowNull || IsCompositionTargetInvalid)
            {
                // return _widthDeviceUnits & _heightDeviceUnits if hwndsource is not yet available.
                // We will resize when hwnd becomes available, because the DeviceToLogicalUnits calculation
                // depends on hwnd being available. If it's not high dpi, the second resize will be optimized
                // by layout system.
                size = new Size(_widthDeviceUnits, _heightDeviceUnits);
            }
            else
            {
                // It's better to get WindowSize instead of doing WindowSize.Width & WindowSize.Height
                // because WindowSize queries HwndSource.
                size = WindowSize;
                Point ptLogicalUnits = DeviceToLogicalUnits(new Point(size.Width, size.Height));
                size = new Size(ptLogicalUnits.X, ptLogicalUnits.Y);
            }
 
            return size;
        }
 
        /// <SecurityNote>
        ///     Critical - Sets the status bar text. Can be used to do URL spoofing.
        /// </SecurityNote>
        [SecurityCritical]
        private void OnRequestSetStatusBar_Hyperlink(object sender, RoutedEventArgs e)
        {
            RequestSetStatusBarEventArgs statusEvent = e as RequestSetStatusBarEventArgs;
 
            if ( statusEvent != null )
            {
                SetStatusBarText(statusEvent.Text);
            }
        }
 
        ///<summary>
        /// Prints the content of the App's MainWindow.  The logic is that if the content is not visual but IInputElement
        /// we will try to let it handle the command first. If it does not handle the print command we will get the corresponding
        /// visual in the visual tree and use that to print.
        ///</summary>
        private static void OnCommandPrint(object sender, ExecutedRoutedEventArgs e)
        {
#if !DONOTREFPRINTINGASMMETA
            RootBrowserWindow rbw = sender as RootBrowserWindow;
            Invariant.Assert(rbw != null);
 
            if (! rbw._isPrintingFromRBW)
            {
                Visual vis = rbw.Content as Visual;
 
                if (vis == null)
                {
                    // If the content is not Visual but IInputElement, try to let it handle the command first.
                    // This is for the document scenario. Printing a document is different from printing a visual.
                    // Printing a visual is to print how it is rendered on screen. Printing a doc prints the full
                    // doc inculding the part that is not visible. There might be other functionalities that are
                    // specific for document. FlowDocument's viewer knows how to print the doc.
                    IInputElement target = rbw.Content as IInputElement;
 
                    if (target != null)
                    {
                        // CanExecute will bubble up. If nobody between the content and rbw can handle it,
                        // It would call back on RBW again. Use _isPrintingFromRBW to prevent the loop.
                        rbw._isPrintingFromRBW = true;
                        try
                        {
                            if (ApplicationCommands.Print.CanExecute(null, target))
                            {
                                ApplicationCommands.Print.Execute(null, target);
                                return;
                            }
                        }
                        finally
                        {
                            rbw._isPrintingFromRBW = false;
                        }
                    }
                }
 
                // Let the user choose a printer and set print options.
                PrintDialog printDlg = new PrintDialog();
 
                // If the user pressed the OK button on the print dialog, we proceed
                if (printDlg.ShowDialog() == true)
                {
                    string printJobDescription = GetPrintJobDescription(App.MainWindow);
 
                    // If the root is not visual and does not know how to print itself, we find the
                    // corresponding visual and use that to print.
                    if (vis == null)
                    {
                        INavigatorImpl navigator = rbw as INavigatorImpl;
                        Invariant.Assert(navigator != null);
 
                        vis = navigator.FindRootViewer();
                        Invariant.Assert(vis != null);
                    }
 
                    // Area we can print to for the chosen printer
                    Rect imageableRect = GetImageableRect(printDlg);
 
                    // We print Visuals aligned with the top/left corner of the printable area.
                    // We do not attempt to print very large Visuals across multiple pages.
                    // Any portion that doesn't fit on a single page will get cropped by the
                    // print system.
 
                    // Used to draw our visual into another visual for printing purposes
                    VisualBrush visualBrush = new VisualBrush(vis);
                    visualBrush.Stretch = Stretch.None;
 
                    // Visual we will print - containing a rectangle the size of our
                    // original Visual but offset into the printable area
                    DrawingVisual drawingVisual = new DrawingVisual();
                    DrawingContext context = drawingVisual.RenderOpen();
                    context.DrawRectangle(visualBrush,
                                          null,
                                          new Rect(imageableRect.X,
                                                   imageableRect.Y,
                                                   vis.VisualDescendantBounds.Width,
                                                   vis.VisualDescendantBounds.Height));
                    context.Close();
 
                    printDlg.PrintVisual(drawingVisual, printJobDescription);
                }
            }
#endif // DONOTREFPRINTINGASMMETA
        }
 
        private static void OnQueryEnabledCommandPrint(object sender, CanExecuteRoutedEventArgs  e)
        {
            RootBrowserWindow rbw = sender as RootBrowserWindow;
            Invariant.Assert(rbw != null);
 
            if ((!e.Handled) && (!rbw._isPrintingFromRBW))
            {
                // While we could print null it doesn't really make sense to do so
                e.CanExecute = rbw.Content != null;
            }
        }
 
#if !DONOTREFPRINTINGASMMETA   
        ///<SecurityNote> 
        /// Critical - calls out to PrintDialog to get the PrintQueue and its PrintCapabilities
        /// TreatAsSafe - these values aren't returned or stored
        ///</SecurityNote>
        [SecurityCritical, SecurityTreatAsSafe]
        private static Rect GetImageableRect(PrintDialog dialog)
        {
            Rect imageableRect = Rect.Empty;
            
            Invariant.Assert(dialog != null, "Dialog should not be null.");
 
            System.Printing.PrintQueue queue = null;
            System.Printing.PrintCapabilities capabilities = null;
            System.Printing.PageImageableArea imageableArea = null;
            
            // This gets the PringDocumentImageableArea.OriginWidth/OriginHeight 
            // of the PrintQueue the user chose in the dialog.
            CodeAccessPermission printp = SystemDrawingHelper.NewDefaultPrintingPermission();
            printp.Assert();//Blessed Assert to get PrintQueue and call GetPrintCapabilities
            try
            {
                queue = dialog.PrintQueue;
                if (queue != null)
                {
                    capabilities = queue.GetPrintCapabilities();
                }
            }
            finally
            {
                CodeAccessPermission.RevertAssert();
            }
 
            if (capabilities != null)
            {
                imageableArea = capabilities.PageImageableArea;
                if (imageableArea != null)
                {
                    imageableRect = new Rect(imageableArea.OriginWidth,
                                             imageableArea.OriginHeight,
                                             imageableArea.ExtentWidth,
                                             imageableArea.ExtentHeight);
                }
            }
 
            // If for any reason we couldn't get the actual printer's values
            // we fallback to a constant and the values available from the
            // PrintDialog.
            if (imageableRect == Rect.Empty)
            {
                imageableRect = new Rect(NON_PRINTABLE_MARGIN,
                    NON_PRINTABLE_MARGIN,
                    dialog.PrintableAreaWidth,
                    dialog.PrintableAreaHeight);
            }
            
            return imageableRect;
        }
#endif
 
        /// <summary>
        ///    Generate the title of the print job for a given Window.
        /// </summary>
        private static string GetPrintJobDescription(Window window)
        {
            Invariant.Assert(window != null, "Window should not be null.");
 
            string description = null;
            string pageTitle = null;
 
            // Get the window title
            string windowTitle = window.Title;
            if (windowTitle != null)
            {
                windowTitle = windowTitle.Trim();
            }
 
            // Get the page title, if available
            Page page = window.Content as Page;
            if (page != null)
            {
                pageTitle = page.Title;
                if (pageTitle != null)
                {
                    pageTitle = pageTitle.Trim();
                }
            }
 
            // If window and page title are available, use them together,
            // otherwise use which ever is available
            if (!String.IsNullOrEmpty(windowTitle))
            {
                if (!String.IsNullOrEmpty(pageTitle))
                {
                    description = SR.Get(SRID.PrintJobDescription, windowTitle, pageTitle);
                }
                else
                {
                    description = windowTitle;
                }
            }
 
            // No window title so use the page title on its own
            if (description == null && !String.IsNullOrEmpty(pageTitle))
            {
                description = pageTitle;
            }
 
            // If neither window or page titles are available, try and use
            // the source URI for the content
            if (description == null && BrowserInteropHelper.Source != null)
            {
                Uri source = BrowserInteropHelper.Source;
                if (source.IsFile)
                {
                    description = source.LocalPath;
                }
                else
                {
                    description = source.ToString();
                }
            }
 
            // If no other option, use a localized constant
            if (description == null)
            {
                description = SR.Get(SRID.UntitledPrintJobDescription);
            }
 
            return description;
        }
 
 
        #endregion Private methods
 
        //----------------------------------------------
        //
        // Private Properties
        //
        //----------------------------------------------
        #region Private properties
        private static Application App
        {
            get { return Application.Current; }
        }
 
 
        private static IBrowserCallbackServices Browser
        {
            get
            {
                // There will be no IBrowserCallbackServices available in some situations, e.g., during shutdown
                IBrowserCallbackServices ibcs = (App == null ? null : App.BrowserCallbackServices);
                return ibcs;
            }
        }
 
        ///<SecurityNote>
        /// Critical - calls the SUC'd IBCS.IsDownlevelPlatform.
        /// TreatAsSafe - this information is okay to give out.
        ///</SecurityNote>
        private bool IsDownlevelPlatform
        {
            [SecurityCritical, SecurityTreatAsSafe]
            get
            {
                if (!_isDownlevelPlatformValid)
                {
                    IBrowserCallbackServices ibcs = Browser;
                    _isDownlevelPlatform = (ibcs != null) ? ibcs.IsDownlevelPlatform() : false;
 
                    _isDownlevelPlatformValid = true;
                }
 
                return _isDownlevelPlatform;
            }
        }
 
        internal bool HasTravelLogIntegration
        {
            get
            {
                return !IsDownlevelPlatform && BrowserInteropHelper.IsAvalonTopLevel;
            }
        }
 
        #endregion Private properties
 
        //----------------------------------------------
        //
        // Private Classes
        //
        //----------------------------------------------
        #region Private Classes
 
        /// <summary>
        /// This class is used to print objects which are not directly supported by XpsDocumentWriter.
        /// It's sole purpose is to make a non-abstract version of Visual.
        /// </summary>
        private class PrintVisual : ContainerVisual { }
 
 
        private class KeyInputSite : IKeyboardInputSite
        {
            internal KeyInputSite(SecurityCriticalData<IKeyboardInputSink> sink)
            {
                _sink = sink;
            }
 
            /// <SecurityNote>
            /// Critical: sets critical data.
            /// Safe: setting to null is okay.
            /// </SecurityNote>
            [SecurityCritical, SecurityTreatAsSafe]
            void IKeyboardInputSite.Unregister()
            {
                _sink = new SecurityCriticalData<IKeyboardInputSink>(null);
            }
 
            /// <SecurityNote>
            /// Critical: IKeyboardInputSink could be used to spoof input.
            /// </SecurityNote>
            IKeyboardInputSink IKeyboardInputSite.Sink
            {
                [SecurityCritical]
                get { return _sink.Value; }
            }
 
            /// <SecurityNote>
            /// Critical: calls the SUC'd IBCS.TabOut().
            /// Safe: tabbing out of the application is safe.
            /// </SecurityNote>
            [SecurityCritical, SecurityTreatAsSafe]
            bool IKeyboardInputSite.OnNoMoreTabStops(TraversalRequest request)
            {
                return Browser.TabOut(request.FocusNavigationDirection == FocusNavigationDirection.Next);
                // i. Tabbing-in is handled by ApplicationProxyInternal.
            }
 
            /// <SecurityNote>
            /// Critical: The encapsulated interface could be used to spoof input.
            /// </SecurityNote>
            SecurityCriticalData<IKeyboardInputSink> _sink;
        };
 
        #endregion Private Classes
 
 
        //----------------------------------------------
        //
        // Private Members
        //
        //----------------------------------------------
        #region private members
 
        //Cache the values until the HwndSourceWindow is created
        private int  _xDeviceUnits, _yDeviceUnits, _widthDeviceUnits, _heightDeviceUnits;
        private bool _rectSet;
 
        private bool _isPrintingFromRBW;
        private bool _isDownlevelPlatformValid;
        private bool _isDownlevelPlatform;
        private bool _sourceWindowCreationCompleted;
 
        private List<WebBrowser> _webBrowserList = new List<WebBrowser>();
 
        /// <summary>
        /// The event delegate has to be stored because HwndWrapper keeps only a weak reference to it.
        /// </summary>
        /// <SecurityNote>
        /// Critical: could be used to spoof input
        /// </SecurityNote>
        [SecurityCritical]
        private HwndWrapperHook _inputPostFilter;
 
        private bool _loadingCompletePosted;
        const int READYSTATE_COMPLETE = 4;
 
        // Fallback constant for the size of non-printable margin.  This is used if
        // we cant retrieve the actual size from the PrintQueue.
        private const int NON_PRINTABLE_MARGIN = 15;
 
        //
        // Setting these as the min-browser width & height.
        // Note that we can't use User's min window-width & height - these are specifically about browser window.
        //
        private const int MIN_BROWSER_WIDTH_DEVICE_UNITS = 200;
 
        private const int MIN_BROWSER_HEIGHT_DEVICE_UNITS = 200;
        #endregion private members
    }
}