Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Framework / System / Windows / Documents / TextEditorCopyPaste.cs / 1305600 / TextEditorCopyPaste.cs
//---------------------------------------------------------------------------- // // File: TextEditorCopyPaste.cs // // Copyright (C) Microsoft Corporation. All rights reserved. // // Description: A Component of TextEditor supporting Cut/Copy/Paste commands // //--------------------------------------------------------------------------- namespace System.Windows.Documents { using MS.Internal; using System.Globalization; using System.Security; using System.Security.Permissions; using System.Threading; using System.ComponentModel; using System.Text; using System.Xml; using System.IO; using System.Collections; // ArrayList using System.Runtime.InteropServices; using System.Windows.Threading; using System.Windows.Input; using System.Windows.Controls; // ScrollChangedEventArgs using System.Windows.Controls.Primitives; // CharacterCasing, TextBoxBase using System.Windows.Media; using System.Windows.Markup; using MS.Utility; using MS.Win32; using MS.Internal.Documents; using MS.Internal.Commands; // CommandHelpers ////// Text editing service for controls. /// internal static class TextEditorCopyPaste { //----------------------------------------------------- // // Class Internal Methods // //----------------------------------------------------- #region Class Internal Methods // Registers all text editing command handlers for a given control type ////// Critical - elevates to associate a protected command (paste) with keyboard /// TreatAsSafe - Shift+Insert is the correct key binding, and therefore is /// expected by the user. /// [SecurityCritical, SecurityTreatAsSafe] internal static void _RegisterClassHandlers(Type controlType, bool acceptsRichContent, bool readOnly, bool registerEventListeners) { CommandHelpers.RegisterCommandHandler(controlType, ApplicationCommands.Copy, new ExecutedRoutedEventHandler(OnCopy), new CanExecuteRoutedEventHandler(OnQueryStatusCopy), KeyGesture.CreateFromResourceStrings(SR.Get(SRID.KeyCopy), SR.Get(SRID.KeyCopyDisplayString)), KeyGesture.CreateFromResourceStrings(SR.Get(SRID.KeyCtrlInsert), SR.Get(SRID.KeyCtrlInsertDisplayString))); if (acceptsRichContent) { CommandHelpers.RegisterCommandHandler(controlType, EditingCommands.CopyFormat, new ExecutedRoutedEventHandler(OnCopyFormat), new CanExecuteRoutedEventHandler(OnQueryStatusCopyFormat), SRID.KeyCopyFormat, SRID.KeyCopyFormatDisplayString); } if (!readOnly) { CommandHelpers.RegisterCommandHandler(controlType, ApplicationCommands.Cut, new ExecutedRoutedEventHandler(OnCut), new CanExecuteRoutedEventHandler(OnQueryStatusCut), KeyGesture.CreateFromResourceStrings(SR.Get(SRID.KeyCut), SR.Get(SRID.KeyCutDisplayString)), KeyGesture.CreateFromResourceStrings(SR.Get(SRID.KeyShiftDelete), SR.Get(SRID.KeyShiftDeleteDisplayString))); // temp vars to reduce code under elevation ExecutedRoutedEventHandler ExecutedRoutedEventHandler = new ExecutedRoutedEventHandler(OnPaste); CanExecuteRoutedEventHandler CanExecuteRoutedEventHandler = new CanExecuteRoutedEventHandler(OnQueryStatusPaste); InputGesture inputGesture = KeyGesture.CreateFromResourceStrings(SR.Get(SRID.KeyShiftInsert), SR.Get(SRID.KeyShiftInsertDisplayString)); new UIPermission(UIPermissionClipboard.AllClipboard).Assert(); //BlessedAssert try { CommandHelpers.RegisterCommandHandler(controlType, ApplicationCommands.Paste, ExecutedRoutedEventHandler, CanExecuteRoutedEventHandler, inputGesture); } finally { CodeAccessPermission.RevertAssert(); } if (acceptsRichContent) { CommandHelpers.RegisterCommandHandler(controlType, EditingCommands.PasteFormat, new ExecutedRoutedEventHandler(OnPasteFormat), new CanExecuteRoutedEventHandler(OnQueryStatusPasteFormat), SRID.KeyPasteFormat, SRID.KeyPasteFormatDisplayString); } } } ////// Creates DataObject for Copy and Drag operations /// ////// Critical: This code calls into SetData under an assert which has the ability to set xaml content on clipboard. /// [SecurityCritical] internal static DataObject _CreateDataObject(TextEditor This, bool isDragDrop) { DataObject dataObject; // Create the data object for drag and drop. // (new UIPermission(UIPermissionClipboard.AllClipboard)).Assert();//BlessedAssert try { dataObject = new DataObject(); } finally { UIPermission.RevertAssert(); } // Get plain text and copy it into the data object. string textString = This.Selection.Text; if (textString != String.Empty) { // Copy plain text into data object. // ConfirmDataFormatSetting rasies a public event - could throw recoverable exception. if (ConfirmDataFormatSetting(This.UiScope, dataObject, DataFormats.Text)) { CriticalSetDataWrapper(dataObject,DataFormats.Text, textString); } // Copy unicode text into data object. // ConfirmDataFormatSetting rasies a public event - could throw recoverable exception. if (ConfirmDataFormatSetting(This.UiScope, dataObject, DataFormats.UnicodeText)) { CriticalSetDataWrapper(dataObject,DataFormats.UnicodeText, textString); } } // Get the rtf and xaml text and then copy it into the data object after confirm data format. // We do this only if our content is rich if (This.AcceptsRichContent) { // This ensures that in the confines of partial trust RTF is not enabled. // We use unmanaged code permission over clipboard permission since // the latter is available in intranet zone and this is something that will // fail in intranet too. if (SecurityHelper.CheckUnmanagedCodePermission()) { // In FullTrust we allow all rich formats on the clipboard Stream wpfContainerMemory = null; // null wpfContainerMemory on entry means that container is optional // and will be not created when there is no images in the range. // Create in-memory wpf package, and serialize the content of selection into it string xamlTextWithImages = WpfPayload.SaveRange(This.Selection, ref wpfContainerMemory, /*useFlowDocumentAsRoot:*/false); if (xamlTextWithImages.Length > 0) { // ConfirmDataFormatSetting raises a public event - could throw recoverable exception. if (wpfContainerMemory != null && ConfirmDataFormatSetting(This.UiScope, dataObject, DataFormats.XamlPackage)) { dataObject.SetData(DataFormats.XamlPackage, wpfContainerMemory); } // ConfirmDataFormatSetting raises a public event - could throw recoverable exception. if (ConfirmDataFormatSetting(This.UiScope, dataObject, DataFormats.Rtf)) { // Convert xaml to rtf text to set rtf data into data object. string rtfText = ConvertXamlToRtf(xamlTextWithImages, wpfContainerMemory); if (rtfText != String.Empty) { dataObject.SetData(DataFormats.Rtf, rtfText, true); } } } // Add a CF_BITMAP if we have only one image selected. Image image = This.Selection.GetUIElementSelected() as Image; if (image != null && image.Source is System.Windows.Media.Imaging.BitmapSource) { dataObject.SetImage((System.Windows.Media.Imaging.BitmapSource)image.Source); } } // Xaml format is availabe both in Full Trust and in Partial Trust // Need to re-serialize xaml to avoid image references within a container: StringWriter stringWriter = new StringWriter(CultureInfo.InvariantCulture); XmlTextWriter xmlWriter = new XmlTextWriter(stringWriter); TextRangeSerialization.WriteXaml(xmlWriter, This.Selection, /*useFlowDocumentAsRoot:*/false, /*wpfPayload:*/null); string xamlText = stringWriter.ToString(); // if (xamlText.Length > 0) { // ConfirmDataFormatSetting rasies a public event - could throw recoverable exception. if (ConfirmDataFormatSetting(This.UiScope, dataObject, DataFormats.Xaml)) { // Place Xaml data onto the dataobject using safe setter CriticalSetDataWrapper(dataObject, DataFormats.Xaml, xamlText); // The dataobject itself must hold an information about permission set // of the source appdomain. Set it there: // Package permission set for the current appdomain PermissionSet psCurrentAppDomain = SecurityHelper.ExtractAppDomainPermissionSetMinusSiteOfOrigin(); string permissionSetCurrentAppDomain = psCurrentAppDomain.ToString(); CriticalSetDataWrapper(dataObject, DataFormats.ApplicationTrust, permissionSetCurrentAppDomain); } } } // Notify application about our data object preparation completion DataObjectCopyingEventArgs dataObjectCopyingEventArgs = new DataObjectCopyingEventArgs(dataObject, /*isDragDrop:*/isDragDrop); This.UiScope.RaiseEvent(dataObjectCopyingEventArgs); if (dataObjectCopyingEventArgs.CommandCancelled) { dataObject = null; } return dataObject; } ////// Paste contents of data object into text selection /// /// /// /// data object containing data to paste /// /// /// ////// true if successful, false otherwise /// ////// To disable paste in partial trust case, /// this function checks if the current call stack has the all clipboard permission. /// Critical: This code calls into AppDomain methods and enables xaml cut and paste /// TreatAsSafe: It has a demand for All Clipboard permissions /// [SecurityCritical,SecurityTreatAsSafe] internal static bool _DoPaste(TextEditor This, IDataObject dataObject, bool isDragDrop) { // Don't try anything if the caller doesn't have the rights to read from the clipboard... // if (!SecurityHelper.CallerHasAllClipboardPermission()) return false; Invariant.Assert(dataObject != null); // Choose what format we are going to paste string formatToApply; bool pasted; pasted = false; // Get the default paste content applying format formatToApply = GetPasteApplyFormat(This, dataObject); DataObjectPastingEventArgs dataObjectPastingEventArgs; try { // Let the application to participate in Paste process dataObjectPastingEventArgs = new DataObjectPastingEventArgs(dataObject, isDragDrop, formatToApply); } catch (ArgumentException) { // Clipboard can be changed by set new or empty data during creating // DataObjectPastingEvent that check the representing of the // formatToApply. Do nothing if we encounter AgrumentException. return pasted; } // Public event call - could raise recoverable exception. This.UiScope.RaiseEvent(dataObjectPastingEventArgs); if (!dataObjectPastingEventArgs.CommandCancelled) { // When custom handler decides to suggest its own data, // it must create a new instance of DataObject and put it // into DataObjectPastingEventArgs.DataObject property. // Exisiting DataObject is on global Clipboard and can not be changed. // Here we need to get this potentially changed instance // of DataObject IDataObject dataObjectToApply = dataObjectPastingEventArgs.DataObject; formatToApply = dataObjectPastingEventArgs.FormatToApply; // Paste the content data(Text, Unicode, Xaml and Rtf) to the current text selection pasted = PasteContentData(This, dataObject, dataObjectToApply, formatToApply); } return pasted; } // Get the default paste content applying format internal static string GetPasteApplyFormat(TextEditor This, IDataObject dataObject) { string formatToApply; // Currently we won't allow DataFormats.Xaml on the partial trust. // GetDataPresent(DataFormats.Xaml)have a chance to register Xaml format // by calling the unmanaged code which is RegisterClipboardFormat. bool hasUnmanagedCodePermission = SecurityHelper.CheckUnmanagedCodePermission(); if (This.AcceptsRichContent && hasUnmanagedCodePermission && dataObject.GetDataPresent(DataFormats.XamlPackage)) { formatToApply = DataFormats.XamlPackage; } else if (This.AcceptsRichContent && dataObject.GetDataPresent(DataFormats.Xaml)) { formatToApply = DataFormats.Xaml; } else if (This.AcceptsRichContent && hasUnmanagedCodePermission && dataObject.GetDataPresent(DataFormats.Rtf)) { formatToApply = DataFormats.Rtf; } else if (dataObject.GetDataPresent(DataFormats.UnicodeText)) { formatToApply = DataFormats.UnicodeText; } else if (dataObject.GetDataPresent(DataFormats.Text)) { formatToApply = DataFormats.Text; } else if (This.AcceptsRichContent && hasUnmanagedCodePermission && dataObject is DataObject && ((DataObject)dataObject).ContainsImage()) { formatToApply = DataFormats.Bitmap; } else { // Even if we do not see any recognizable formats, // we continue the process because application custom // paste needs it and may do something useful. formatToApply = String.Empty; } return formatToApply; } ////// Cut worker. /// ////// Critical - Sets data on the clipboard and accepts a parameter which indicates /// whether or not this action was user-initiated. /// [SecurityCritical] internal static void Cut(TextEditor This, bool userInitiated) { if (userInitiated) { // Fail silently if the app explicitly denies clipboard access. try { new UIPermission(UIPermissionClipboard.OwnClipboard).Demand(); } catch (SecurityException) { return; } } else if (!SecurityHelper.CallerHasAllClipboardPermission()) { // Fail silently if we don't have clipboard permission. return; } TextEditorTyping._FlushPendingInputItems(This); TextEditorTyping._BreakTypingSequence(This); if (This.Selection != null && !This.Selection.IsEmpty) { // Copy content onto the clipboard // Note: _CreateDataObject raises a public event which might throw a recoverable exception. DataObject dataObject = TextEditorCopyPaste._CreateDataObject(This, /*isDragDrop:*/false); if (dataObject != null) { try { // The copy command was not terminated by application // One of reason should be the opening fail of Clipboard by the destroyed hwnd. Clipboard.CriticalSetDataObject(dataObject, true); } catch (ExternalException) { // Clipboard is failed to set the data object. return; } // Delete selected content using (This.Selection.DeclareChangeBlock()) { // Forget previously suggested horizontal position TextEditorSelection._ClearSuggestedX(This); This.Selection.Text = String.Empty; // Clear springload formatting if (This.Selection is TextSelection) { ((TextSelection)This.Selection).ClearSpringloadFormatting(); } } } } } ////// Copy worker. /// ////// Critical - Sets data on the clipboard and accepts a parameter which indicates /// whether or not this action was user-initiated. /// [SecurityCritical] internal static void Copy(TextEditor This, bool userInitiated) { if (userInitiated) { // Fail silently if the app explicitly denies clipboard access. try { new UIPermission(UIPermissionClipboard.OwnClipboard).Demand(); } catch (SecurityException) { return; } } else if (!SecurityHelper.CallerHasAllClipboardPermission()) { // Fail silently if we don't have clipboard permission. return; } TextEditorTyping._FlushPendingInputItems(This); TextEditorTyping._BreakTypingSequence(This); if (This.Selection != null && !This.Selection.IsEmpty) { // Note: _CreateDataObject raises a public event which might throw a recoverable exception. DataObject dataObject = TextEditorCopyPaste._CreateDataObject(This, /*isDragDrop:*/false); if (dataObject != null) { try { // The copy command was not terminated by application // One of reason should be the opening fail of Clipboard by the destroyed hwnd. Clipboard.CriticalSetDataObject(dataObject, true); } catch (ExternalException) { // Clipboard is failed to set the data object. return; } } } // Do not clear springload formatting } ////// Paste worker. /// ////// Critical:To disable paste in partial trust case, /// TreatAsSafe: this function checks if the current call stack has the all clipboard permission. /// [SecurityCritical, SecurityTreatAsSafe] internal static void Paste(TextEditor This) { // Don't try anything if the caller doesn't have the rights to read from the clipboard... if (!SecurityHelper.CallerHasAllClipboardPermission()) { return; } if (This.Selection.IsTableCellRange) { // return; } TextEditorTyping._FlushPendingInputItems(This); TextEditorTyping._BreakTypingSequence(This); // Get DataObject from the Clipboard IDataObject dataObject; try { dataObject = Clipboard.GetDataObject(); } catch (ExternalException) { // Clipboard is failed to get the data object. // One of reason should be the opening fail of Clipboard by the destroyed hwnd. dataObject = null; // } bool forceLayoutUpdate = This.Selection.CoversEntireContent; if (dataObject != null) { using (This.Selection.DeclareChangeBlock()) { // Forget previously suggested horizontal position TextEditorSelection._ClearSuggestedX(This); // _DoPaste raises a public event -- could raise recoverable exception. if (TextEditorCopyPaste._DoPaste(This, dataObject, /*isDragDrop:*/false)) { // Collapse selection to the end // Use backward direction to stay oriented towards pasted content This.Selection.SetCaretToPosition(This.Selection.End, LogicalDirection.Backward, /*allowStopAtLineEnd:*/false, /*allowStopNearSpace:*/true); // Clear springload formatting if (This.Selection is TextSelection) { ((TextSelection)This.Selection).ClearSpringloadFormatting(); } } } // PUBLIC EVENT RAISED HERE AS CHANGEBLOCK CLOSES! } // If we replaced the entire document content, background layout will // kick in. Force it to complete now. if (forceLayoutUpdate) { This.Selection.ValidateLayout(); } } // Converts xaml content to rtf content. internal static string ConvertXamlToRtf(string xamlContent, Stream wpfContainerMemory) { // Create XamlRtfConverter to process the converting from Xaml to Rtf XamlRtfConverter xamlRtfConverter = new XamlRtfConverter(); if (wpfContainerMemory != null) { xamlRtfConverter.WpfPayload = WpfPayload.OpenWpfPayload(wpfContainerMemory); } // Process Xaml-Rtf converting string rtfContent = xamlRtfConverter.ConvertXamlToRtf(xamlContent); return rtfContent; } // Converts an rtf content to xaml content. internal static MemoryStream ConvertRtfToXaml(string rtfContent) { MemoryStream memoryStream = new MemoryStream(); WpfPayload wpfPayload = WpfPayload.CreateWpfPayload(memoryStream); using (wpfPayload.Package) { using (Stream xamlStream = wpfPayload.CreateXamlStream()) { // Create XamlRtfConverter to process the converting from Rtf to Xaml XamlRtfConverter xamlRtfConverter = new XamlRtfConverter(); xamlRtfConverter.WpfPayload = wpfPayload; string xamlContent = xamlRtfConverter.ConvertRtfToXaml(rtfContent); if (xamlContent != string.Empty) { StreamWriter streamWriter = new StreamWriter(xamlStream); using (streamWriter) { streamWriter.Write(xamlContent); } } else { memoryStream = null; } } // This closes xamlStream } // This closes the package return memoryStream; } #endregion Class Internal Methods //------------------------------------------------------ // // Private Methods // //----------------------------------------------------- #region Private Methods ////// Cut command QueryStatus handler /// private static void OnQueryStatusCut(object target, CanExecuteRoutedEventArgs args) { TextEditor This = TextEditor._GetTextEditor(target); if (This == null || !This._IsEnabled || This.IsReadOnly) { return; } // Ignore the cut event if the editor is on PasswordBox control. if (This.UiScope is PasswordBox) { args.CanExecute = false; args.Handled = true; return; } args.CanExecute = !This.Selection.IsEmpty; args.Handled = true; } ////// Cut command event handler. /// ////// Critical - Calls TextEditorCopyPaste.Cut, using the UserInitiated /// bit in the event args, to set data on the clipboard. /// TreatAsSafe - The bit is protected by the UserIniatedRoutedEvent permission and /// the content being set is based on the active selection. /// [SecurityCritical, SecurityTreatAsSafe] private static void OnCut(object target, ExecutedRoutedEventArgs args) { TextEditor This = TextEditor._GetTextEditor(target); if (This == null || !This._IsEnabled || This.IsReadOnly) { return; } // Ignore the cut event if the editor is on PasswordBox control. if (This.UiScope is PasswordBox) { return; } Cut(This, args.UserInitiated); } ////// Copy command QueryStatus handler /// private static void OnQueryStatusCopy(object target, CanExecuteRoutedEventArgs args) { TextEditor This = TextEditor._GetTextEditor(target); if (This == null || !This._IsEnabled) { return; } // Ignore the copy event if the editor is on PasswordBox control. if (This.UiScope is PasswordBox) { args.CanExecute = false; args.Handled = true; return; } args.CanExecute = !This.Selection.IsEmpty; args.Handled = true; } ////// Copy command event handler. /// This method is used both in Copy, Cut and DragDrop commands. /// ////// Critical - Calls TextEditorCopyPaste.Copy, using the UserInitiated /// bit in the event args, to set data on the clipboard. /// TreatAsSafe - The bit is protected by the UserIniatedRoutedEvent permission and /// the content being set is based on the active selection. /// [SecurityCritical, SecurityTreatAsSafe] private static void OnCopy(object target, ExecutedRoutedEventArgs args) { TextEditor This = TextEditor._GetTextEditor(target); if (This == null || !This._IsEnabled) { return; } // Ignore the copy event if the editor is on PasswordBox control. if (This.UiScope is PasswordBox) { return; } Copy(This, args.UserInitiated); } ////// Paste command QueryStatus handler /// private static void OnQueryStatusPaste(object target, CanExecuteRoutedEventArgs args) { TextEditor This = TextEditor._GetTextEditor(target); if (This == null || !This._IsEnabled || This.IsReadOnly) { return; } args.Handled = true; try { if (SecurityHelper.CallerHasAllClipboardPermission()) { // Define what format our paste mechanism recognizes on the clipbord appropriate for this selection string formatToApply = GetPasteApplyFormat(This, Clipboard.GetDataObject()); args.CanExecute = formatToApply.Length > 0; } else { // Simplified version of clipboard sniffing for partial trust args.CanExecute = Clipboard.IsClipboardPopulated(); } } catch (ExternalException) { // Clipboard is failed to get the data object. // One of reason should be the opening fail of Clipboard while other // process opens the clipboard or missing close of Clipboard. args.CanExecute = false; } } ////// Paste command event handler. /// private static void OnPaste(object target, ExecutedRoutedEventArgs args) { TextEditor This = TextEditor._GetTextEditor(target); if (This == null || !This._IsEnabled || This.IsReadOnly) { return; } Paste(This); } ////// StartInputCorrection command QueryStatus handler /// private static void OnQueryStatusCopyFormat(object target, CanExecuteRoutedEventArgs args) { TextEditor This = TextEditor._GetTextEditor(target); if (This == null || !This._IsEnabled) { return; } // args.CanExecute = false; args.Handled = true; } private static void OnCopyFormat(object sender, ExecutedRoutedEventArgs args) { // } ////// StartInputCorrection command QueryStatus handler /// private static void OnQueryStatusPasteFormat(object target, CanExecuteRoutedEventArgs args) { TextEditor This = TextEditor._GetTextEditor(target); if (This == null || !This._IsEnabled || This.IsReadOnly) { return; } // args.CanExecute = false; args.Handled = true; } private static void OnPasteFormat(object sender, ExecutedRoutedEventArgs args) { // } ////// Critical: This code calls into CriticalSetData which circumvents all checks for setting data /// ////// This code is used to call into an internal overload to set data which circumvents the demand for /// all clipboard permission. Although this is not the cleanest we prefer to cast it to DataObject /// and call the critical overload to reduce the scope of the code that gets called here. /// This saves us one high level assert. /// /// /// /// [SecurityCritical] private static void CriticalSetDataWrapper(IDataObject dataObjectValue, string format, string content) { if (dataObjectValue is DataObject) { ((DataObject)dataObjectValue).CriticalSetData(format, content, format == DataFormats.ApplicationTrust ? /*autoConvert:*/false : true); } } ////// Paste the content data(Text, Unicode, Xaml and Rtf) to the current text selection /// /// /// /// data object containing data to paste /// /// /// /// /// ////// true if successful, false otherwise /// ////// This function paste the content data and also can set new apply format with /// checking the unmanaged code permission if the content data is failed to paste. /// Critical: This function falls back to DataFormats.Rtf in case Xaml paste fails. It calls Critical WpfPayload.SaveImage. /// TreatAsSafe: In partial trust we revert to DataFormats.UnicodeText or DataFormats.Text /// format and hence the risk is mitigated of having Rtf paste enabled /// [SecurityCritical, SecurityTreatAsSafe] private static bool PasteContentData(TextEditor This, IDataObject dataObject, IDataObject dataObjectToApply, string formatToApply) { // CF_BITMAP - pasting a single image. if (formatToApply == DataFormats.Bitmap && dataObjectToApply is DataObject) { // This demand is present to explicitly disable RTF independant of any // asserts in the confines of partial trust // We check unmanaged code instead of all clipboard because in paste // there is a high level assert for all clipboard in commandmanager.cs if (This.AcceptsRichContent && This.Selection is TextSelection && SecurityHelper.CheckUnmanagedCodePermission()) { System.Windows.Media.Imaging.BitmapSource bitmapSource = GetPasteData(dataObjectToApply, DataFormats.Bitmap) as System.Windows.Media.Imaging.BitmapSource; if (bitmapSource != null) { // Pack the image into a WPF container MemoryStream packagedImage = WpfPayload.SaveImage(bitmapSource, WpfPayload.ImageBmpContentType); // Place it onto a data object dataObjectToApply = new DataObject(); formatToApply = DataFormats.XamlPackage; dataObjectToApply.SetData(DataFormats.XamlPackage, packagedImage); } } } if (formatToApply == DataFormats.XamlPackage) { // This demand is present to explicitly disable RTF independant of any // asserts in the confines of partial trust // We check unmanaged code instead of all clipboard because in paste // there is a high level assert for all clipboard in commandmanager.cs if (This.AcceptsRichContent && This.Selection is TextSelection && SecurityHelper.CheckUnmanagedCodePermission()) { object pastedData = GetPasteData(dataObjectToApply, DataFormats.XamlPackage); MemoryStream pastedMemoryStream = pastedData as MemoryStream; if (pastedMemoryStream != null) { object element = WpfPayload.LoadElement(pastedMemoryStream); if ((element is Section || element is Span) && PasteTextElement(This, (TextElement)element)) { return true; } else if (element is FrameworkElement) { ((TextSelection)This.Selection).InsertEmbeddedUIElement((FrameworkElement)element); return true; } } } // Fall to Xaml: dataObjectToApply = dataObject; // go back to source data object if (dataObjectToApply.GetDataPresent(DataFormats.Xaml)) { formatToApply = DataFormats.Xaml; } else if (SecurityHelper.CheckUnmanagedCodePermission() && dataObjectToApply.GetDataPresent(DataFormats.Rtf)) { formatToApply = DataFormats.Rtf; } else if (dataObjectToApply.GetDataPresent(DataFormats.UnicodeText)) { formatToApply = DataFormats.UnicodeText; } else if (dataObjectToApply.GetDataPresent(DataFormats.Text)) { formatToApply = DataFormats.Text; } } if (formatToApply == DataFormats.Xaml) { if (This.AcceptsRichContent && This.Selection is TextSelection) { object pastedData = GetPasteData(dataObjectToApply, DataFormats.Xaml); if (pastedData != null && PasteXaml(This, pastedData.ToString())) { return true; } } // Fall to Rtf: dataObjectToApply = dataObject; // go back to source data object if (SecurityHelper.CheckUnmanagedCodePermission() && dataObjectToApply.GetDataPresent(DataFormats.Rtf)) { formatToApply = DataFormats.Rtf; } else if (dataObjectToApply.GetDataPresent(DataFormats.UnicodeText)) { formatToApply = DataFormats.UnicodeText; } else if (dataObjectToApply.GetDataPresent(DataFormats.Text)) { formatToApply = DataFormats.Text; } } if (formatToApply == DataFormats.Rtf) { // This demand is present to explicitly disable RTF independant of any // asserts in the confines of partial trust // We check unmanaged code instead of all clipboard because in paste // there is a high level assert for all clipboard in commandmanager.cs if (This.AcceptsRichContent && SecurityHelper.CheckUnmanagedCodePermission()) { object pastedData = GetPasteData(dataObjectToApply, DataFormats.Rtf); // Convert rtf to xaml text to paste rtf data into the target. if (pastedData != null) { MemoryStream memoryStream = ConvertRtfToXaml(pastedData.ToString()); if (memoryStream != null) { TextElement textElement = WpfPayload.LoadElement(memoryStream) as TextElement; if ((textElement is Section || textElement is Span) && PasteTextElement(This, textElement)) { return true; } } } } // Fall to plain text: dataObjectToApply = dataObject; // go back to source data object if (dataObjectToApply.GetDataPresent(DataFormats.UnicodeText)) { formatToApply = DataFormats.UnicodeText; } else if (dataObjectToApply.GetDataPresent(DataFormats.Text)) { formatToApply = DataFormats.Text; } } if (formatToApply == DataFormats.UnicodeText) { object pastedData = GetPasteData(dataObjectToApply, DataFormats.UnicodeText); if (pastedData == null) { if (dataObjectToApply.GetDataPresent(DataFormats.Text)) { formatToApply = DataFormats.Text; // fall to plain text dataObjectToApply = dataObject; // go back to source data object } } else { // Dont attempt to recover if pasting Unicode text fails because our only fallback is mbcs text, // which will either evaluate identically (at best) or // produce a string with unexpected text (worse!) from WideCharToMultiByte conversion. return PastePlainText(This, pastedData.ToString()); } } if (formatToApply == DataFormats.Text) { object pastedData = GetPasteData(dataObjectToApply, DataFormats.Text); if (pastedData != null && PastePlainText(This, pastedData.ToString())) { return true; } } return false; } ////// Get the paste data from the specified DataObject and data format. /// ////// Critical: This function calls the critical methods which access the unmanaged code /// to get the pasted data from DataObject /// [SecurityCritical] private static object GetPasteData(IDataObject dataObject, string dataFormat) { object pastedData; try { // We don't need to verify data present here. First, GetPasteApplyFormat() // is already verified the data present, so reduce the perf. Second, we can't // guarantee the presenting data for the some specified data after raising // DataObjectPastingEventArgs which case is that set FormatToApply first then // set the DataObject that doesn't have FormatToApply data format. //Invariant.Assert(dataObject.GetDataPresent(dataFormat)); pastedData = dataObject.GetData(dataFormat, true); } // DataObject data can have the invalid value that throw the Exception. // In case of OutOfMemoryException, ExternalException(and Win32Exception), // we return null quietly and do nothing for paste. // For example(Bug#1391689) , IE set the invalid Rich Text Format data that bring // CLR OutOfMemoryException. catch (OutOfMemoryException) { pastedData = null; } catch (ExternalException) { pastedData = null; } return pastedData; } // Paste flow content into the current text selection // Returns false if pasting was not successful - assuming that the caller will choose another format for pasting private static bool PasteTextElement(TextEditor This, TextElement sectionOrSpan) { bool success = false; This.Selection.BeginChange(); try { ((TextRange)This.Selection).SetXmlVirtual(sectionOrSpan); // Merge new Lists with surrounding Lists. TextRangeEditLists.MergeListsAroundNormalizedPosition((TextPointer)This.Selection.Start); TextRangeEditLists.MergeListsAroundNormalizedPosition((TextPointer)This.Selection.End); // Merge flow direction of the new content if it matches its surroundings. TextRangeEdit.MergeFlowDirection((TextPointer)This.Selection.Start); TextRangeEdit.MergeFlowDirection((TextPointer)This.Selection.End); success = true; } finally { This.Selection.EndChange(); } return success; } // Paste xaml content into the current text selection // Returns false if pasting was not successful - assuming that the caller will choose another format for pasting private static bool PasteXaml(TextEditor This, string pasteXaml) { bool success; if (pasteXaml.Length == 0) { success = false; } else { try { // Parse the fragment into a separate subtree object xamlObject = XamlReader.Load(new XmlTextReader(new System.IO.StringReader(pasteXaml))); TextElement flowContent = xamlObject as TextElement; success = flowContent == null ? false : PasteTextElement(This, flowContent); } catch (XamlParseException e) { // Clipboard data can have the invalid xaml content that will throw // the XamlParseException. // In case of XamlParseException, we shouldn't paste anything and quiet. // Xaml invalid character range is from 0x00 to 0x20. (e.g. x03) // Invariant.Assert(e != null); //to make compiler happy about not using a variable e. This variable is useful in debugging process though - to see a reason of a parsing failure success = false; } } return success; } // Helper for plain text filtering when pasted into rich or plain destination private static bool PastePlainText(TextEditor This, string pastedText) { pastedText = This._FilterText(pastedText, This.Selection); if (pastedText.Length > 0) { if (This.AcceptsRichContent && This.Selection.Start is TextPointer) { // Clear selection content This.Selection.Text = String.Empty; // Ensure that text is insertable at current selection TextPointer start = TextRangeEditTables.EnsureInsertionPosition((TextPointer)This.Selection.Start); // Store boundaries of inserted text start = start.GetPositionAtOffset(0, LogicalDirection.Backward); TextPointer end = start.GetPositionAtOffset(0, LogicalDirection.Forward); // For rich text we need to remove control characters and // replace linebreaks by paragraphs int currentLineStart = 0; for (int i = 0; i < pastedText.Length; i++) { if (pastedText[i] == '\r' || pastedText[i] == '\n') { end.InsertTextInRun(pastedText.Substring(currentLineStart, i - currentLineStart)); if (!This.AcceptsReturn) { return true; // All lined except for the first one are ignored when TextBox does not accept Return key } if (end.HasNonMergeableInlineAncestor) { // We cannot split a Hyperlink or other non-mergeable Inline element, // so insert a space character instead (similar to embedded object). // Note that this means, Paste operation would loose // paragraph break information in this case. end.InsertTextInRun(" "); } else { end = end.InsertParagraphBreak(); } if (pastedText[i] == '\r' && i + 1 < pastedText.Length && pastedText[i + 1] == '\n') { i++; } currentLineStart = i + 1; } } end.InsertTextInRun(pastedText.Substring(currentLineStart, pastedText.Length - currentLineStart)); // Select all pasted content This.Selection.Select(start, end); } else { // For plain text we insert the content as is (including control characters) This.Selection.Text = pastedText; } return true; } return false; } // Event firing helper for DataObjectSettingData event private static bool ConfirmDataFormatSetting(FrameworkElement uiScope, IDataObject dataObject, string format) { DataObjectSettingDataEventArgs dataObjectSettingDataEventArgs; dataObjectSettingDataEventArgs = new DataObjectSettingDataEventArgs(dataObject, format); uiScope.RaiseEvent(dataObjectSettingDataEventArgs); return !dataObjectSettingDataEventArgs.CommandCancelled; } #endregion Private methods } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. //---------------------------------------------------------------------------- // // File: TextEditorCopyPaste.cs // // Copyright (C) Microsoft Corporation. All rights reserved. // // Description: A Component of TextEditor supporting Cut/Copy/Paste commands // //--------------------------------------------------------------------------- namespace System.Windows.Documents { using MS.Internal; using System.Globalization; using System.Security; using System.Security.Permissions; using System.Threading; using System.ComponentModel; using System.Text; using System.Xml; using System.IO; using System.Collections; // ArrayList using System.Runtime.InteropServices; using System.Windows.Threading; using System.Windows.Input; using System.Windows.Controls; // ScrollChangedEventArgs using System.Windows.Controls.Primitives; // CharacterCasing, TextBoxBase using System.Windows.Media; using System.Windows.Markup; using MS.Utility; using MS.Win32; using MS.Internal.Documents; using MS.Internal.Commands; // CommandHelpers ////// Text editing service for controls. /// internal static class TextEditorCopyPaste { //----------------------------------------------------- // // Class Internal Methods // //----------------------------------------------------- #region Class Internal Methods // Registers all text editing command handlers for a given control type ////// Critical - elevates to associate a protected command (paste) with keyboard /// TreatAsSafe - Shift+Insert is the correct key binding, and therefore is /// expected by the user. /// [SecurityCritical, SecurityTreatAsSafe] internal static void _RegisterClassHandlers(Type controlType, bool acceptsRichContent, bool readOnly, bool registerEventListeners) { CommandHelpers.RegisterCommandHandler(controlType, ApplicationCommands.Copy, new ExecutedRoutedEventHandler(OnCopy), new CanExecuteRoutedEventHandler(OnQueryStatusCopy), KeyGesture.CreateFromResourceStrings(SR.Get(SRID.KeyCopy), SR.Get(SRID.KeyCopyDisplayString)), KeyGesture.CreateFromResourceStrings(SR.Get(SRID.KeyCtrlInsert), SR.Get(SRID.KeyCtrlInsertDisplayString))); if (acceptsRichContent) { CommandHelpers.RegisterCommandHandler(controlType, EditingCommands.CopyFormat, new ExecutedRoutedEventHandler(OnCopyFormat), new CanExecuteRoutedEventHandler(OnQueryStatusCopyFormat), SRID.KeyCopyFormat, SRID.KeyCopyFormatDisplayString); } if (!readOnly) { CommandHelpers.RegisterCommandHandler(controlType, ApplicationCommands.Cut, new ExecutedRoutedEventHandler(OnCut), new CanExecuteRoutedEventHandler(OnQueryStatusCut), KeyGesture.CreateFromResourceStrings(SR.Get(SRID.KeyCut), SR.Get(SRID.KeyCutDisplayString)), KeyGesture.CreateFromResourceStrings(SR.Get(SRID.KeyShiftDelete), SR.Get(SRID.KeyShiftDeleteDisplayString))); // temp vars to reduce code under elevation ExecutedRoutedEventHandler ExecutedRoutedEventHandler = new ExecutedRoutedEventHandler(OnPaste); CanExecuteRoutedEventHandler CanExecuteRoutedEventHandler = new CanExecuteRoutedEventHandler(OnQueryStatusPaste); InputGesture inputGesture = KeyGesture.CreateFromResourceStrings(SR.Get(SRID.KeyShiftInsert), SR.Get(SRID.KeyShiftInsertDisplayString)); new UIPermission(UIPermissionClipboard.AllClipboard).Assert(); //BlessedAssert try { CommandHelpers.RegisterCommandHandler(controlType, ApplicationCommands.Paste, ExecutedRoutedEventHandler, CanExecuteRoutedEventHandler, inputGesture); } finally { CodeAccessPermission.RevertAssert(); } if (acceptsRichContent) { CommandHelpers.RegisterCommandHandler(controlType, EditingCommands.PasteFormat, new ExecutedRoutedEventHandler(OnPasteFormat), new CanExecuteRoutedEventHandler(OnQueryStatusPasteFormat), SRID.KeyPasteFormat, SRID.KeyPasteFormatDisplayString); } } } ////// Creates DataObject for Copy and Drag operations /// ////// Critical: This code calls into SetData under an assert which has the ability to set xaml content on clipboard. /// [SecurityCritical] internal static DataObject _CreateDataObject(TextEditor This, bool isDragDrop) { DataObject dataObject; // Create the data object for drag and drop. // (new UIPermission(UIPermissionClipboard.AllClipboard)).Assert();//BlessedAssert try { dataObject = new DataObject(); } finally { UIPermission.RevertAssert(); } // Get plain text and copy it into the data object. string textString = This.Selection.Text; if (textString != String.Empty) { // Copy plain text into data object. // ConfirmDataFormatSetting rasies a public event - could throw recoverable exception. if (ConfirmDataFormatSetting(This.UiScope, dataObject, DataFormats.Text)) { CriticalSetDataWrapper(dataObject,DataFormats.Text, textString); } // Copy unicode text into data object. // ConfirmDataFormatSetting rasies a public event - could throw recoverable exception. if (ConfirmDataFormatSetting(This.UiScope, dataObject, DataFormats.UnicodeText)) { CriticalSetDataWrapper(dataObject,DataFormats.UnicodeText, textString); } } // Get the rtf and xaml text and then copy it into the data object after confirm data format. // We do this only if our content is rich if (This.AcceptsRichContent) { // This ensures that in the confines of partial trust RTF is not enabled. // We use unmanaged code permission over clipboard permission since // the latter is available in intranet zone and this is something that will // fail in intranet too. if (SecurityHelper.CheckUnmanagedCodePermission()) { // In FullTrust we allow all rich formats on the clipboard Stream wpfContainerMemory = null; // null wpfContainerMemory on entry means that container is optional // and will be not created when there is no images in the range. // Create in-memory wpf package, and serialize the content of selection into it string xamlTextWithImages = WpfPayload.SaveRange(This.Selection, ref wpfContainerMemory, /*useFlowDocumentAsRoot:*/false); if (xamlTextWithImages.Length > 0) { // ConfirmDataFormatSetting raises a public event - could throw recoverable exception. if (wpfContainerMemory != null && ConfirmDataFormatSetting(This.UiScope, dataObject, DataFormats.XamlPackage)) { dataObject.SetData(DataFormats.XamlPackage, wpfContainerMemory); } // ConfirmDataFormatSetting raises a public event - could throw recoverable exception. if (ConfirmDataFormatSetting(This.UiScope, dataObject, DataFormats.Rtf)) { // Convert xaml to rtf text to set rtf data into data object. string rtfText = ConvertXamlToRtf(xamlTextWithImages, wpfContainerMemory); if (rtfText != String.Empty) { dataObject.SetData(DataFormats.Rtf, rtfText, true); } } } // Add a CF_BITMAP if we have only one image selected. Image image = This.Selection.GetUIElementSelected() as Image; if (image != null && image.Source is System.Windows.Media.Imaging.BitmapSource) { dataObject.SetImage((System.Windows.Media.Imaging.BitmapSource)image.Source); } } // Xaml format is availabe both in Full Trust and in Partial Trust // Need to re-serialize xaml to avoid image references within a container: StringWriter stringWriter = new StringWriter(CultureInfo.InvariantCulture); XmlTextWriter xmlWriter = new XmlTextWriter(stringWriter); TextRangeSerialization.WriteXaml(xmlWriter, This.Selection, /*useFlowDocumentAsRoot:*/false, /*wpfPayload:*/null); string xamlText = stringWriter.ToString(); // if (xamlText.Length > 0) { // ConfirmDataFormatSetting rasies a public event - could throw recoverable exception. if (ConfirmDataFormatSetting(This.UiScope, dataObject, DataFormats.Xaml)) { // Place Xaml data onto the dataobject using safe setter CriticalSetDataWrapper(dataObject, DataFormats.Xaml, xamlText); // The dataobject itself must hold an information about permission set // of the source appdomain. Set it there: // Package permission set for the current appdomain PermissionSet psCurrentAppDomain = SecurityHelper.ExtractAppDomainPermissionSetMinusSiteOfOrigin(); string permissionSetCurrentAppDomain = psCurrentAppDomain.ToString(); CriticalSetDataWrapper(dataObject, DataFormats.ApplicationTrust, permissionSetCurrentAppDomain); } } } // Notify application about our data object preparation completion DataObjectCopyingEventArgs dataObjectCopyingEventArgs = new DataObjectCopyingEventArgs(dataObject, /*isDragDrop:*/isDragDrop); This.UiScope.RaiseEvent(dataObjectCopyingEventArgs); if (dataObjectCopyingEventArgs.CommandCancelled) { dataObject = null; } return dataObject; } ////// Paste contents of data object into text selection /// /// /// /// data object containing data to paste /// /// /// ////// true if successful, false otherwise /// ////// To disable paste in partial trust case, /// this function checks if the current call stack has the all clipboard permission. /// Critical: This code calls into AppDomain methods and enables xaml cut and paste /// TreatAsSafe: It has a demand for All Clipboard permissions /// [SecurityCritical,SecurityTreatAsSafe] internal static bool _DoPaste(TextEditor This, IDataObject dataObject, bool isDragDrop) { // Don't try anything if the caller doesn't have the rights to read from the clipboard... // if (!SecurityHelper.CallerHasAllClipboardPermission()) return false; Invariant.Assert(dataObject != null); // Choose what format we are going to paste string formatToApply; bool pasted; pasted = false; // Get the default paste content applying format formatToApply = GetPasteApplyFormat(This, dataObject); DataObjectPastingEventArgs dataObjectPastingEventArgs; try { // Let the application to participate in Paste process dataObjectPastingEventArgs = new DataObjectPastingEventArgs(dataObject, isDragDrop, formatToApply); } catch (ArgumentException) { // Clipboard can be changed by set new or empty data during creating // DataObjectPastingEvent that check the representing of the // formatToApply. Do nothing if we encounter AgrumentException. return pasted; } // Public event call - could raise recoverable exception. This.UiScope.RaiseEvent(dataObjectPastingEventArgs); if (!dataObjectPastingEventArgs.CommandCancelled) { // When custom handler decides to suggest its own data, // it must create a new instance of DataObject and put it // into DataObjectPastingEventArgs.DataObject property. // Exisiting DataObject is on global Clipboard and can not be changed. // Here we need to get this potentially changed instance // of DataObject IDataObject dataObjectToApply = dataObjectPastingEventArgs.DataObject; formatToApply = dataObjectPastingEventArgs.FormatToApply; // Paste the content data(Text, Unicode, Xaml and Rtf) to the current text selection pasted = PasteContentData(This, dataObject, dataObjectToApply, formatToApply); } return pasted; } // Get the default paste content applying format internal static string GetPasteApplyFormat(TextEditor This, IDataObject dataObject) { string formatToApply; // Currently we won't allow DataFormats.Xaml on the partial trust. // GetDataPresent(DataFormats.Xaml)have a chance to register Xaml format // by calling the unmanaged code which is RegisterClipboardFormat. bool hasUnmanagedCodePermission = SecurityHelper.CheckUnmanagedCodePermission(); if (This.AcceptsRichContent && hasUnmanagedCodePermission && dataObject.GetDataPresent(DataFormats.XamlPackage)) { formatToApply = DataFormats.XamlPackage; } else if (This.AcceptsRichContent && dataObject.GetDataPresent(DataFormats.Xaml)) { formatToApply = DataFormats.Xaml; } else if (This.AcceptsRichContent && hasUnmanagedCodePermission && dataObject.GetDataPresent(DataFormats.Rtf)) { formatToApply = DataFormats.Rtf; } else if (dataObject.GetDataPresent(DataFormats.UnicodeText)) { formatToApply = DataFormats.UnicodeText; } else if (dataObject.GetDataPresent(DataFormats.Text)) { formatToApply = DataFormats.Text; } else if (This.AcceptsRichContent && hasUnmanagedCodePermission && dataObject is DataObject && ((DataObject)dataObject).ContainsImage()) { formatToApply = DataFormats.Bitmap; } else { // Even if we do not see any recognizable formats, // we continue the process because application custom // paste needs it and may do something useful. formatToApply = String.Empty; } return formatToApply; } ////// Cut worker. /// ////// Critical - Sets data on the clipboard and accepts a parameter which indicates /// whether or not this action was user-initiated. /// [SecurityCritical] internal static void Cut(TextEditor This, bool userInitiated) { if (userInitiated) { // Fail silently if the app explicitly denies clipboard access. try { new UIPermission(UIPermissionClipboard.OwnClipboard).Demand(); } catch (SecurityException) { return; } } else if (!SecurityHelper.CallerHasAllClipboardPermission()) { // Fail silently if we don't have clipboard permission. return; } TextEditorTyping._FlushPendingInputItems(This); TextEditorTyping._BreakTypingSequence(This); if (This.Selection != null && !This.Selection.IsEmpty) { // Copy content onto the clipboard // Note: _CreateDataObject raises a public event which might throw a recoverable exception. DataObject dataObject = TextEditorCopyPaste._CreateDataObject(This, /*isDragDrop:*/false); if (dataObject != null) { try { // The copy command was not terminated by application // One of reason should be the opening fail of Clipboard by the destroyed hwnd. Clipboard.CriticalSetDataObject(dataObject, true); } catch (ExternalException) { // Clipboard is failed to set the data object. return; } // Delete selected content using (This.Selection.DeclareChangeBlock()) { // Forget previously suggested horizontal position TextEditorSelection._ClearSuggestedX(This); This.Selection.Text = String.Empty; // Clear springload formatting if (This.Selection is TextSelection) { ((TextSelection)This.Selection).ClearSpringloadFormatting(); } } } } } ////// Copy worker. /// ////// Critical - Sets data on the clipboard and accepts a parameter which indicates /// whether or not this action was user-initiated. /// [SecurityCritical] internal static void Copy(TextEditor This, bool userInitiated) { if (userInitiated) { // Fail silently if the app explicitly denies clipboard access. try { new UIPermission(UIPermissionClipboard.OwnClipboard).Demand(); } catch (SecurityException) { return; } } else if (!SecurityHelper.CallerHasAllClipboardPermission()) { // Fail silently if we don't have clipboard permission. return; } TextEditorTyping._FlushPendingInputItems(This); TextEditorTyping._BreakTypingSequence(This); if (This.Selection != null && !This.Selection.IsEmpty) { // Note: _CreateDataObject raises a public event which might throw a recoverable exception. DataObject dataObject = TextEditorCopyPaste._CreateDataObject(This, /*isDragDrop:*/false); if (dataObject != null) { try { // The copy command was not terminated by application // One of reason should be the opening fail of Clipboard by the destroyed hwnd. Clipboard.CriticalSetDataObject(dataObject, true); } catch (ExternalException) { // Clipboard is failed to set the data object. return; } } } // Do not clear springload formatting } ////// Paste worker. /// ////// Critical:To disable paste in partial trust case, /// TreatAsSafe: this function checks if the current call stack has the all clipboard permission. /// [SecurityCritical, SecurityTreatAsSafe] internal static void Paste(TextEditor This) { // Don't try anything if the caller doesn't have the rights to read from the clipboard... if (!SecurityHelper.CallerHasAllClipboardPermission()) { return; } if (This.Selection.IsTableCellRange) { // return; } TextEditorTyping._FlushPendingInputItems(This); TextEditorTyping._BreakTypingSequence(This); // Get DataObject from the Clipboard IDataObject dataObject; try { dataObject = Clipboard.GetDataObject(); } catch (ExternalException) { // Clipboard is failed to get the data object. // One of reason should be the opening fail of Clipboard by the destroyed hwnd. dataObject = null; // } bool forceLayoutUpdate = This.Selection.CoversEntireContent; if (dataObject != null) { using (This.Selection.DeclareChangeBlock()) { // Forget previously suggested horizontal position TextEditorSelection._ClearSuggestedX(This); // _DoPaste raises a public event -- could raise recoverable exception. if (TextEditorCopyPaste._DoPaste(This, dataObject, /*isDragDrop:*/false)) { // Collapse selection to the end // Use backward direction to stay oriented towards pasted content This.Selection.SetCaretToPosition(This.Selection.End, LogicalDirection.Backward, /*allowStopAtLineEnd:*/false, /*allowStopNearSpace:*/true); // Clear springload formatting if (This.Selection is TextSelection) { ((TextSelection)This.Selection).ClearSpringloadFormatting(); } } } // PUBLIC EVENT RAISED HERE AS CHANGEBLOCK CLOSES! } // If we replaced the entire document content, background layout will // kick in. Force it to complete now. if (forceLayoutUpdate) { This.Selection.ValidateLayout(); } } // Converts xaml content to rtf content. internal static string ConvertXamlToRtf(string xamlContent, Stream wpfContainerMemory) { // Create XamlRtfConverter to process the converting from Xaml to Rtf XamlRtfConverter xamlRtfConverter = new XamlRtfConverter(); if (wpfContainerMemory != null) { xamlRtfConverter.WpfPayload = WpfPayload.OpenWpfPayload(wpfContainerMemory); } // Process Xaml-Rtf converting string rtfContent = xamlRtfConverter.ConvertXamlToRtf(xamlContent); return rtfContent; } // Converts an rtf content to xaml content. internal static MemoryStream ConvertRtfToXaml(string rtfContent) { MemoryStream memoryStream = new MemoryStream(); WpfPayload wpfPayload = WpfPayload.CreateWpfPayload(memoryStream); using (wpfPayload.Package) { using (Stream xamlStream = wpfPayload.CreateXamlStream()) { // Create XamlRtfConverter to process the converting from Rtf to Xaml XamlRtfConverter xamlRtfConverter = new XamlRtfConverter(); xamlRtfConverter.WpfPayload = wpfPayload; string xamlContent = xamlRtfConverter.ConvertRtfToXaml(rtfContent); if (xamlContent != string.Empty) { StreamWriter streamWriter = new StreamWriter(xamlStream); using (streamWriter) { streamWriter.Write(xamlContent); } } else { memoryStream = null; } } // This closes xamlStream } // This closes the package return memoryStream; } #endregion Class Internal Methods //------------------------------------------------------ // // Private Methods // //----------------------------------------------------- #region Private Methods ////// Cut command QueryStatus handler /// private static void OnQueryStatusCut(object target, CanExecuteRoutedEventArgs args) { TextEditor This = TextEditor._GetTextEditor(target); if (This == null || !This._IsEnabled || This.IsReadOnly) { return; } // Ignore the cut event if the editor is on PasswordBox control. if (This.UiScope is PasswordBox) { args.CanExecute = false; args.Handled = true; return; } args.CanExecute = !This.Selection.IsEmpty; args.Handled = true; } ////// Cut command event handler. /// ////// Critical - Calls TextEditorCopyPaste.Cut, using the UserInitiated /// bit in the event args, to set data on the clipboard. /// TreatAsSafe - The bit is protected by the UserIniatedRoutedEvent permission and /// the content being set is based on the active selection. /// [SecurityCritical, SecurityTreatAsSafe] private static void OnCut(object target, ExecutedRoutedEventArgs args) { TextEditor This = TextEditor._GetTextEditor(target); if (This == null || !This._IsEnabled || This.IsReadOnly) { return; } // Ignore the cut event if the editor is on PasswordBox control. if (This.UiScope is PasswordBox) { return; } Cut(This, args.UserInitiated); } ////// Copy command QueryStatus handler /// private static void OnQueryStatusCopy(object target, CanExecuteRoutedEventArgs args) { TextEditor This = TextEditor._GetTextEditor(target); if (This == null || !This._IsEnabled) { return; } // Ignore the copy event if the editor is on PasswordBox control. if (This.UiScope is PasswordBox) { args.CanExecute = false; args.Handled = true; return; } args.CanExecute = !This.Selection.IsEmpty; args.Handled = true; } ////// Copy command event handler. /// This method is used both in Copy, Cut and DragDrop commands. /// ////// Critical - Calls TextEditorCopyPaste.Copy, using the UserInitiated /// bit in the event args, to set data on the clipboard. /// TreatAsSafe - The bit is protected by the UserIniatedRoutedEvent permission and /// the content being set is based on the active selection. /// [SecurityCritical, SecurityTreatAsSafe] private static void OnCopy(object target, ExecutedRoutedEventArgs args) { TextEditor This = TextEditor._GetTextEditor(target); if (This == null || !This._IsEnabled) { return; } // Ignore the copy event if the editor is on PasswordBox control. if (This.UiScope is PasswordBox) { return; } Copy(This, args.UserInitiated); } ////// Paste command QueryStatus handler /// private static void OnQueryStatusPaste(object target, CanExecuteRoutedEventArgs args) { TextEditor This = TextEditor._GetTextEditor(target); if (This == null || !This._IsEnabled || This.IsReadOnly) { return; } args.Handled = true; try { if (SecurityHelper.CallerHasAllClipboardPermission()) { // Define what format our paste mechanism recognizes on the clipbord appropriate for this selection string formatToApply = GetPasteApplyFormat(This, Clipboard.GetDataObject()); args.CanExecute = formatToApply.Length > 0; } else { // Simplified version of clipboard sniffing for partial trust args.CanExecute = Clipboard.IsClipboardPopulated(); } } catch (ExternalException) { // Clipboard is failed to get the data object. // One of reason should be the opening fail of Clipboard while other // process opens the clipboard or missing close of Clipboard. args.CanExecute = false; } } ////// Paste command event handler. /// private static void OnPaste(object target, ExecutedRoutedEventArgs args) { TextEditor This = TextEditor._GetTextEditor(target); if (This == null || !This._IsEnabled || This.IsReadOnly) { return; } Paste(This); } ////// StartInputCorrection command QueryStatus handler /// private static void OnQueryStatusCopyFormat(object target, CanExecuteRoutedEventArgs args) { TextEditor This = TextEditor._GetTextEditor(target); if (This == null || !This._IsEnabled) { return; } // args.CanExecute = false; args.Handled = true; } private static void OnCopyFormat(object sender, ExecutedRoutedEventArgs args) { // } ////// StartInputCorrection command QueryStatus handler /// private static void OnQueryStatusPasteFormat(object target, CanExecuteRoutedEventArgs args) { TextEditor This = TextEditor._GetTextEditor(target); if (This == null || !This._IsEnabled || This.IsReadOnly) { return; } // args.CanExecute = false; args.Handled = true; } private static void OnPasteFormat(object sender, ExecutedRoutedEventArgs args) { // } ////// Critical: This code calls into CriticalSetData which circumvents all checks for setting data /// ////// This code is used to call into an internal overload to set data which circumvents the demand for /// all clipboard permission. Although this is not the cleanest we prefer to cast it to DataObject /// and call the critical overload to reduce the scope of the code that gets called here. /// This saves us one high level assert. /// /// /// /// [SecurityCritical] private static void CriticalSetDataWrapper(IDataObject dataObjectValue, string format, string content) { if (dataObjectValue is DataObject) { ((DataObject)dataObjectValue).CriticalSetData(format, content, format == DataFormats.ApplicationTrust ? /*autoConvert:*/false : true); } } ////// Paste the content data(Text, Unicode, Xaml and Rtf) to the current text selection /// /// /// /// data object containing data to paste /// /// /// /// /// ////// true if successful, false otherwise /// ////// This function paste the content data and also can set new apply format with /// checking the unmanaged code permission if the content data is failed to paste. /// Critical: This function falls back to DataFormats.Rtf in case Xaml paste fails. It calls Critical WpfPayload.SaveImage. /// TreatAsSafe: In partial trust we revert to DataFormats.UnicodeText or DataFormats.Text /// format and hence the risk is mitigated of having Rtf paste enabled /// [SecurityCritical, SecurityTreatAsSafe] private static bool PasteContentData(TextEditor This, IDataObject dataObject, IDataObject dataObjectToApply, string formatToApply) { // CF_BITMAP - pasting a single image. if (formatToApply == DataFormats.Bitmap && dataObjectToApply is DataObject) { // This demand is present to explicitly disable RTF independant of any // asserts in the confines of partial trust // We check unmanaged code instead of all clipboard because in paste // there is a high level assert for all clipboard in commandmanager.cs if (This.AcceptsRichContent && This.Selection is TextSelection && SecurityHelper.CheckUnmanagedCodePermission()) { System.Windows.Media.Imaging.BitmapSource bitmapSource = GetPasteData(dataObjectToApply, DataFormats.Bitmap) as System.Windows.Media.Imaging.BitmapSource; if (bitmapSource != null) { // Pack the image into a WPF container MemoryStream packagedImage = WpfPayload.SaveImage(bitmapSource, WpfPayload.ImageBmpContentType); // Place it onto a data object dataObjectToApply = new DataObject(); formatToApply = DataFormats.XamlPackage; dataObjectToApply.SetData(DataFormats.XamlPackage, packagedImage); } } } if (formatToApply == DataFormats.XamlPackage) { // This demand is present to explicitly disable RTF independant of any // asserts in the confines of partial trust // We check unmanaged code instead of all clipboard because in paste // there is a high level assert for all clipboard in commandmanager.cs if (This.AcceptsRichContent && This.Selection is TextSelection && SecurityHelper.CheckUnmanagedCodePermission()) { object pastedData = GetPasteData(dataObjectToApply, DataFormats.XamlPackage); MemoryStream pastedMemoryStream = pastedData as MemoryStream; if (pastedMemoryStream != null) { object element = WpfPayload.LoadElement(pastedMemoryStream); if ((element is Section || element is Span) && PasteTextElement(This, (TextElement)element)) { return true; } else if (element is FrameworkElement) { ((TextSelection)This.Selection).InsertEmbeddedUIElement((FrameworkElement)element); return true; } } } // Fall to Xaml: dataObjectToApply = dataObject; // go back to source data object if (dataObjectToApply.GetDataPresent(DataFormats.Xaml)) { formatToApply = DataFormats.Xaml; } else if (SecurityHelper.CheckUnmanagedCodePermission() && dataObjectToApply.GetDataPresent(DataFormats.Rtf)) { formatToApply = DataFormats.Rtf; } else if (dataObjectToApply.GetDataPresent(DataFormats.UnicodeText)) { formatToApply = DataFormats.UnicodeText; } else if (dataObjectToApply.GetDataPresent(DataFormats.Text)) { formatToApply = DataFormats.Text; } } if (formatToApply == DataFormats.Xaml) { if (This.AcceptsRichContent && This.Selection is TextSelection) { object pastedData = GetPasteData(dataObjectToApply, DataFormats.Xaml); if (pastedData != null && PasteXaml(This, pastedData.ToString())) { return true; } } // Fall to Rtf: dataObjectToApply = dataObject; // go back to source data object if (SecurityHelper.CheckUnmanagedCodePermission() && dataObjectToApply.GetDataPresent(DataFormats.Rtf)) { formatToApply = DataFormats.Rtf; } else if (dataObjectToApply.GetDataPresent(DataFormats.UnicodeText)) { formatToApply = DataFormats.UnicodeText; } else if (dataObjectToApply.GetDataPresent(DataFormats.Text)) { formatToApply = DataFormats.Text; } } if (formatToApply == DataFormats.Rtf) { // This demand is present to explicitly disable RTF independant of any // asserts in the confines of partial trust // We check unmanaged code instead of all clipboard because in paste // there is a high level assert for all clipboard in commandmanager.cs if (This.AcceptsRichContent && SecurityHelper.CheckUnmanagedCodePermission()) { object pastedData = GetPasteData(dataObjectToApply, DataFormats.Rtf); // Convert rtf to xaml text to paste rtf data into the target. if (pastedData != null) { MemoryStream memoryStream = ConvertRtfToXaml(pastedData.ToString()); if (memoryStream != null) { TextElement textElement = WpfPayload.LoadElement(memoryStream) as TextElement; if ((textElement is Section || textElement is Span) && PasteTextElement(This, textElement)) { return true; } } } } // Fall to plain text: dataObjectToApply = dataObject; // go back to source data object if (dataObjectToApply.GetDataPresent(DataFormats.UnicodeText)) { formatToApply = DataFormats.UnicodeText; } else if (dataObjectToApply.GetDataPresent(DataFormats.Text)) { formatToApply = DataFormats.Text; } } if (formatToApply == DataFormats.UnicodeText) { object pastedData = GetPasteData(dataObjectToApply, DataFormats.UnicodeText); if (pastedData == null) { if (dataObjectToApply.GetDataPresent(DataFormats.Text)) { formatToApply = DataFormats.Text; // fall to plain text dataObjectToApply = dataObject; // go back to source data object } } else { // Dont attempt to recover if pasting Unicode text fails because our only fallback is mbcs text, // which will either evaluate identically (at best) or // produce a string with unexpected text (worse!) from WideCharToMultiByte conversion. return PastePlainText(This, pastedData.ToString()); } } if (formatToApply == DataFormats.Text) { object pastedData = GetPasteData(dataObjectToApply, DataFormats.Text); if (pastedData != null && PastePlainText(This, pastedData.ToString())) { return true; } } return false; } ////// Get the paste data from the specified DataObject and data format. /// ////// Critical: This function calls the critical methods which access the unmanaged code /// to get the pasted data from DataObject /// [SecurityCritical] private static object GetPasteData(IDataObject dataObject, string dataFormat) { object pastedData; try { // We don't need to verify data present here. First, GetPasteApplyFormat() // is already verified the data present, so reduce the perf. Second, we can't // guarantee the presenting data for the some specified data after raising // DataObjectPastingEventArgs which case is that set FormatToApply first then // set the DataObject that doesn't have FormatToApply data format. //Invariant.Assert(dataObject.GetDataPresent(dataFormat)); pastedData = dataObject.GetData(dataFormat, true); } // DataObject data can have the invalid value that throw the Exception. // In case of OutOfMemoryException, ExternalException(and Win32Exception), // we return null quietly and do nothing for paste. // For example(Bug#1391689) , IE set the invalid Rich Text Format data that bring // CLR OutOfMemoryException. catch (OutOfMemoryException) { pastedData = null; } catch (ExternalException) { pastedData = null; } return pastedData; } // Paste flow content into the current text selection // Returns false if pasting was not successful - assuming that the caller will choose another format for pasting private static bool PasteTextElement(TextEditor This, TextElement sectionOrSpan) { bool success = false; This.Selection.BeginChange(); try { ((TextRange)This.Selection).SetXmlVirtual(sectionOrSpan); // Merge new Lists with surrounding Lists. TextRangeEditLists.MergeListsAroundNormalizedPosition((TextPointer)This.Selection.Start); TextRangeEditLists.MergeListsAroundNormalizedPosition((TextPointer)This.Selection.End); // Merge flow direction of the new content if it matches its surroundings. TextRangeEdit.MergeFlowDirection((TextPointer)This.Selection.Start); TextRangeEdit.MergeFlowDirection((TextPointer)This.Selection.End); success = true; } finally { This.Selection.EndChange(); } return success; } // Paste xaml content into the current text selection // Returns false if pasting was not successful - assuming that the caller will choose another format for pasting private static bool PasteXaml(TextEditor This, string pasteXaml) { bool success; if (pasteXaml.Length == 0) { success = false; } else { try { // Parse the fragment into a separate subtree object xamlObject = XamlReader.Load(new XmlTextReader(new System.IO.StringReader(pasteXaml))); TextElement flowContent = xamlObject as TextElement; success = flowContent == null ? false : PasteTextElement(This, flowContent); } catch (XamlParseException e) { // Clipboard data can have the invalid xaml content that will throw // the XamlParseException. // In case of XamlParseException, we shouldn't paste anything and quiet. // Xaml invalid character range is from 0x00 to 0x20. (e.g. x03) // Invariant.Assert(e != null); //to make compiler happy about not using a variable e. This variable is useful in debugging process though - to see a reason of a parsing failure success = false; } } return success; } // Helper for plain text filtering when pasted into rich or plain destination private static bool PastePlainText(TextEditor This, string pastedText) { pastedText = This._FilterText(pastedText, This.Selection); if (pastedText.Length > 0) { if (This.AcceptsRichContent && This.Selection.Start is TextPointer) { // Clear selection content This.Selection.Text = String.Empty; // Ensure that text is insertable at current selection TextPointer start = TextRangeEditTables.EnsureInsertionPosition((TextPointer)This.Selection.Start); // Store boundaries of inserted text start = start.GetPositionAtOffset(0, LogicalDirection.Backward); TextPointer end = start.GetPositionAtOffset(0, LogicalDirection.Forward); // For rich text we need to remove control characters and // replace linebreaks by paragraphs int currentLineStart = 0; for (int i = 0; i < pastedText.Length; i++) { if (pastedText[i] == '\r' || pastedText[i] == '\n') { end.InsertTextInRun(pastedText.Substring(currentLineStart, i - currentLineStart)); if (!This.AcceptsReturn) { return true; // All lined except for the first one are ignored when TextBox does not accept Return key } if (end.HasNonMergeableInlineAncestor) { // We cannot split a Hyperlink or other non-mergeable Inline element, // so insert a space character instead (similar to embedded object). // Note that this means, Paste operation would loose // paragraph break information in this case. end.InsertTextInRun(" "); } else { end = end.InsertParagraphBreak(); } if (pastedText[i] == '\r' && i + 1 < pastedText.Length && pastedText[i + 1] == '\n') { i++; } currentLineStart = i + 1; } } end.InsertTextInRun(pastedText.Substring(currentLineStart, pastedText.Length - currentLineStart)); // Select all pasted content This.Selection.Select(start, end); } else { // For plain text we insert the content as is (including control characters) This.Selection.Text = pastedText; } return true; } return false; } // Event firing helper for DataObjectSettingData event private static bool ConfirmDataFormatSetting(FrameworkElement uiScope, IDataObject dataObject, string format) { DataObjectSettingDataEventArgs dataObjectSettingDataEventArgs; dataObjectSettingDataEventArgs = new DataObjectSettingDataEventArgs(dataObject, format); uiScope.RaiseEvent(dataObjectSettingDataEventArgs); return !dataObjectSettingDataEventArgs.CommandCancelled; } #endregion Private methods } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- MDIControlStrip.cs
- TextEvent.cs
- FormClosedEvent.cs
- StatusBarItem.cs
- TakeQueryOptionExpression.cs
- ReferenceConverter.cs
- PropertyPathConverter.cs
- DockingAttribute.cs
- FunctionDetailsReader.cs
- SafeFindHandle.cs
- FileNotFoundException.cs
- WebPartRestoreVerb.cs
- ScriptReferenceEventArgs.cs
- EdmItemCollection.cs
- XmlSchemaAnnotated.cs
- CollaborationHelperFunctions.cs
- ListViewGroupConverter.cs
- CopyNodeSetAction.cs
- LayoutEvent.cs
- XmlSchemaSimpleTypeUnion.cs
- LineGeometry.cs
- RelationshipManager.cs
- SelectQueryOperator.cs
- SchemaMerger.cs
- PagedControl.cs
- TextServicesProperty.cs
- RichTextBox.cs
- Util.cs
- CompiledRegexRunnerFactory.cs
- DesignerSerializationVisibilityAttribute.cs
- MarginsConverter.cs
- XmlElementAttribute.cs
- WebMessageEncodingBindingElement.cs
- Paragraph.cs
- FixedFlowMap.cs
- HttpException.cs
- ActivityCodeDomReferenceService.cs
- WizardSideBarListControlItemEventArgs.cs
- CompositeActivityCodeGenerator.cs
- GridViewSelectEventArgs.cs
- GB18030Encoding.cs
- DatePickerTextBox.cs
- OLEDB_Util.cs
- SHA1Cng.cs
- XPathNavigator.cs
- VideoDrawing.cs
- DesignerActionVerbItem.cs
- FileDialog_Vista_Interop.cs
- SamlAuthorizationDecisionClaimResource.cs
- UnionExpr.cs
- HuffCodec.cs
- ChildrenQuery.cs
- OleDbTransaction.cs
- ListViewAutomationPeer.cs
- _TransmitFileOverlappedAsyncResult.cs
- FormatException.cs
- RawStylusActions.cs
- ApplicationFileCodeDomTreeGenerator.cs
- BrowserCapabilitiesCompiler.cs
- XmlQuerySequence.cs
- CaseDesigner.xaml.cs
- storepermissionattribute.cs
- CompilerInfo.cs
- CultureInfo.cs
- HttpRuntimeSection.cs
- TcpClientChannel.cs
- DbgCompiler.cs
- SelectingProviderEventArgs.cs
- ConditionCollection.cs
- WebEvents.cs
- PrintDocument.cs
- DataGridTableStyleMappingNameEditor.cs
- CurrentTimeZone.cs
- XmlCharCheckingWriter.cs
- Guid.cs
- ProcessStartInfo.cs
- TagMapCollection.cs
- WebPartVerb.cs
- DependencyPropertyKey.cs
- AutomationProperty.cs
- TemplatedControlDesigner.cs
- AccessorTable.cs
- AppendHelper.cs
- BuildProvider.cs
- DataControlFieldsEditor.cs
- EntityProviderFactory.cs
- EmptyEnumerable.cs
- NegotiateStream.cs
- VectorAnimationBase.cs
- InternalConfigSettingsFactory.cs
- Pair.cs
- HttpStreams.cs
- StrokeCollection.cs
- XmlSchemaChoice.cs
- InputProviderSite.cs
- IncrementalReadDecoders.cs
- StringToken.cs
- SubclassTypeValidator.cs
- EdmComplexPropertyAttribute.cs
- ExpressionEditorAttribute.cs