|
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
namespace System.Activities.XamlIntegration
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Xaml;
using System.Xml;
using System.Security;
using System.Security.Permissions;
using System.Xaml.Permissions;
using System.Activities.Expressions;
using System.Activities.Validation;
using System.Runtime;
using System.Diagnostics.CodeAnalysis;
using System.Text;
public static class ActivityXamlServices
{
static readonly XamlSchemaContext dynamicActivityReaderSchemaContext = new DynamicActivityReaderSchemaContext();
public static Activity Load(Stream stream)
{
if (stream == null)
{
throw FxTrace.Exception.ArgumentNull("stream");
}
return Load(stream, new ActivityXamlServicesSettings());
}
[SuppressMessage("Microsoft.Security.Xml", "CA3053:UseXmlSecureResolver",
Justification = @"For the call to XmlReader.Create() below, CA3053 recommends setting the
XmlReaderSettings.XmlResolver property to either null or an instance of XmlSecureResolver.
But after setting this property to null, a warning of CA3053 still shows up in FxCop.
So we suppress this error until the reporting for CA3053 has been updated to fix this issue.")]
public static Activity Load(Stream stream, ActivityXamlServicesSettings settings)
{
if (stream == null)
{
throw FxTrace.Exception.ArgumentNull("stream");
}
if (settings == null)
{
throw FxTrace.Exception.ArgumentNull("settings");
}
using (XmlReader xmlReader = XmlReader.Create(stream, new XmlReaderSettings { XmlResolver = null }))
{
return Load(xmlReader, settings);
}
}
public static Activity Load(string fileName)
{
if (fileName == null)
{
throw FxTrace.Exception.ArgumentNull("fileName");
}
return Load(fileName, new ActivityXamlServicesSettings());
}
[SuppressMessage("Microsoft.Security.Xml", "CA3053:UseXmlSecureResolver",
Justification = @"For the call to XmlReader.Create() below, CA3053 recommends setting the
XmlReaderSettings.XmlResolver property to either null or an instance of XmlSecureResolver.
But after setting this property to null, a warning of CA3053 still shows up in FxCop.
So we suppress this error until the reporting for CA3053 has been updated to fix this issue.")]
public static Activity Load(string fileName, ActivityXamlServicesSettings settings)
{
if (fileName == null)
{
throw FxTrace.Exception.ArgumentNull("fileName");
}
if (settings == null)
{
throw FxTrace.Exception.ArgumentNull("settings");
}
using (XmlReader xmlReader = XmlReader.Create(fileName, new XmlReaderSettings { XmlResolver = null }))
{
return Load(xmlReader, settings);
}
}
public static Activity Load(TextReader textReader)
{
if (textReader == null)
{
throw FxTrace.Exception.ArgumentNull("textReader");
}
return Load(textReader, new ActivityXamlServicesSettings());
}
[SuppressMessage("Microsoft.Security.Xml", "CA3053:UseXmlSecureResolver",
Justification = @"For the call to XmlReader.Create() below, CA3053 recommends setting the
XmlReaderSettings.XmlResolver property to either null or an instance of XmlSecureResolver.
But after setting this property to null, a warning of CA3053 still shows up in FxCop.
So we suppress this error until the reporting for CA3053 has been updated to fix this issue.")]
public static Activity Load(TextReader textReader, ActivityXamlServicesSettings settings)
{
if (textReader == null)
{
throw FxTrace.Exception.ArgumentNull("textReader");
}
if (settings == null)
{
throw FxTrace.Exception.ArgumentNull("settings");
}
using (XmlReader xmlReader = XmlReader.Create(textReader, new XmlReaderSettings { XmlResolver = null }))
{
return Load(xmlReader, settings);
}
}
public static Activity Load(XmlReader xmlReader)
{
if (xmlReader == null)
{
throw FxTrace.Exception.ArgumentNull("xmlReader");
}
return Load(xmlReader, new ActivityXamlServicesSettings());
}
public static Activity Load(XmlReader xmlReader, ActivityXamlServicesSettings settings)
{
if (xmlReader == null)
{
throw FxTrace.Exception.ArgumentNull("xmlReader");
}
if (settings == null)
{
throw FxTrace.Exception.ArgumentNull("settings");
}
using (XamlXmlReader xamlReader = new XamlXmlReader(xmlReader, dynamicActivityReaderSchemaContext))
{
return Load(xamlReader, settings);
}
}
public static Activity Load(XamlReader xamlReader)
{
if (xamlReader == null)
{
throw FxTrace.Exception.ArgumentNull("xamlReader");
}
return Load(xamlReader, new ActivityXamlServicesSettings());
}
public static Activity Load(XamlReader xamlReader, ActivityXamlServicesSettings settings)
{
if (xamlReader == null)
{
throw FxTrace.Exception.ArgumentNull("xamlReader");
}
if (settings == null)
{
throw FxTrace.Exception.ArgumentNull("settings");
}
DynamicActivityXamlReader dynamicActivityReader = new DynamicActivityXamlReader(xamlReader);
object xamlObject = XamlServices.Load(dynamicActivityReader);
Activity result = xamlObject as Activity;
if (result == null)
{
throw FxTrace.Exception.Argument("reader", SR.ActivityXamlServicesRequiresActivity(
xamlObject != null ? xamlObject.GetType().FullName : string.Empty));
}
IDynamicActivity dynamicActivity = result as IDynamicActivity;
if (dynamicActivity != null && settings.CompileExpressions)
{
Compile(dynamicActivity, settings.LocationReferenceEnvironment);
}
return result;
}
[SuppressMessage("Microsoft.Security.Xml", "CA3053:UseXmlSecureResolver",
Justification = @"For the call to XmlReader.Create() below, CA3053 recommends setting the
XmlReaderSettings.XmlResolver property to either null or an instance of XmlSecureResolver.
But after setting this property to null, a warning of CA3053 still shows up in FxCop.
So we suppress this error until the reporting for CA3053 has been updated to fix this issue.")]
public static XamlReader CreateReader(Stream stream)
{
if (stream == null)
{
throw FxTrace.Exception.ArgumentNull("stream");
}
return CreateReader(new XamlXmlReader(XmlReader.Create(stream, new XmlReaderSettings { XmlResolver = null }), dynamicActivityReaderSchemaContext), dynamicActivityReaderSchemaContext);
}
public static XamlReader CreateReader(XamlReader innerReader)
{
if (innerReader == null)
{
throw FxTrace.Exception.ArgumentNull("innerReader");
}
return new DynamicActivityXamlReader(innerReader);
}
public static XamlReader CreateReader(XamlReader innerReader, XamlSchemaContext schemaContext)
{
if (innerReader == null)
{
throw FxTrace.Exception.ArgumentNull("innerReader");
}
if (schemaContext == null)
{
throw FxTrace.Exception.ArgumentNull("schemaContext");
}
return new DynamicActivityXamlReader(innerReader, schemaContext);
}
public static XamlReader CreateBuilderReader(XamlReader innerReader)
{
if (innerReader == null)
{
throw FxTrace.Exception.ArgumentNull("innerReader");
}
return new DynamicActivityXamlReader(true, innerReader, null);
}
public static XamlReader CreateBuilderReader(XamlReader innerReader, XamlSchemaContext schemaContext)
{
if (innerReader == null)
{
throw FxTrace.Exception.ArgumentNull("innerReader");
}
if (schemaContext == null)
{
throw FxTrace.Exception.ArgumentNull("schemaContext");
}
return new DynamicActivityXamlReader(true, innerReader, schemaContext);
}
public static XamlWriter CreateBuilderWriter(XamlWriter innerWriter)
{
if (innerWriter == null)
{
throw FxTrace.Exception.ArgumentNull("innerWriter");
}
return new ActivityBuilderXamlWriter(innerWriter);
}
public static Func<object> CreateFactory(XamlReader reader, Type resultType)
{
if (reader == null)
{
throw FxTrace.Exception.ArgumentNull("reader");
}
if (resultType == null)
{
throw FxTrace.Exception.ArgumentNull("resultType");
}
return FuncFactory.CreateFunc(reader, resultType);
}
public static Func<T> CreateFactory<T>(XamlReader reader) where T : class
{
if (reader == null)
{
throw FxTrace.Exception.ArgumentNull("reader");
}
return FuncFactory.CreateFunc<T>(reader);
}
static void Compile(IDynamicActivity dynamicActivity, LocationReferenceEnvironment environment)
{
string language = null;
if (RequiresCompilation(dynamicActivity, environment, out language))
{
TextExpressionCompiler compiler = new TextExpressionCompiler(GetCompilerSettings(dynamicActivity, language));
TextExpressionCompilerResults results = compiler.Compile();
if (results.HasErrors)
{
StringBuilder messages = new StringBuilder();
messages.Append("\r\n");
messages.Append("\r\n");
foreach (TextExpressionCompilerError message in results.CompilerMessages)
{
messages.Append("\t");
if (results.HasSourceInfo)
{
messages.Append(string.Concat(" ", SR.ActivityXamlServiceLineString, " ", message.SourceLineNumber, ": "));
}
messages.Append(message.Message);
}
messages.Append("\r\n");
messages.Append("\r\n");
InvalidOperationException exception = new InvalidOperationException(SR.ActivityXamlServicesCompilationFailed(messages.ToString()));
foreach (TextExpressionCompilerError message in results.CompilerMessages)
{
exception.Data.Add(message, message.Message);
}
throw FxTrace.Exception.AsError(exception);
}
Type compiledExpressionRootType = results.ResultType;
ICompiledExpressionRoot compiledExpressionRoot = Activator.CreateInstance(compiledExpressionRootType, new object[] { dynamicActivity }) as ICompiledExpressionRoot;
CompiledExpressionInvoker.SetCompiledExpressionRootForImplementation(dynamicActivity, compiledExpressionRoot);
}
}
static bool RequiresCompilation(IDynamicActivity dynamicActivity, LocationReferenceEnvironment environment, out string language)
{
language = null;
if (!((Activity)dynamicActivity).IsMetadataCached)
{
IList<ValidationError> validationErrors = null;
if (environment == null)
{
environment = new ActivityLocationReferenceEnvironment();
}
try
{
ActivityUtilities.CacheRootMetadata((Activity)dynamicActivity, environment, ProcessActivityTreeOptions.FullCachingOptions, null, ref validationErrors);
}
catch (Exception e)
{
if (Fx.IsFatal(e))
{
throw;
}
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.CompiledExpressionsCacheMetadataException(dynamicActivity.Name, e.ToString())));
}
}
DynamicActivityVisitor vistor = new DynamicActivityVisitor();
vistor.Visit((Activity)dynamicActivity, true);
if (!vistor.RequiresCompilation)
{
return false;
}
if (vistor.HasLanguageConflict)
{
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.DynamicActivityMultipleExpressionLanguages(vistor.GetConflictingLanguages().AsCommaSeparatedValues())));
}
language = vistor.Language;
return true;
}
static TextExpressionCompilerSettings GetCompilerSettings(IDynamicActivity dynamicActivity, string language)
{
int lastIndexOfDot = dynamicActivity.Name.LastIndexOf('.');
int lengthOfName = dynamicActivity.Name.Length;
string activityName = lastIndexOfDot > 0 ? dynamicActivity.Name.Substring(lastIndexOfDot + 1) : dynamicActivity.Name;
activityName += "_CompiledExpressionRoot";
string activityNamespace = lastIndexOfDot > 0 ? dynamicActivity.Name.Substring(0, lastIndexOfDot) : null;
return new TextExpressionCompilerSettings()
{
Activity = (Activity)dynamicActivity,
ActivityName = activityName,
ActivityNamespace = activityNamespace,
RootNamespace = null,
GenerateAsPartialClass = false,
AlwaysGenerateSource = true,
Language = language
};
}
[Fx.Tag.SecurityNote(Critical = "Critical because we use SecurityCritical methods that do Asserts.",
Safe = "Safe because no critical resources are leaked. And we guarantee that the XAML we are accessing is coming from the assembly to which we are asserting access.")]
[SecuritySafeCritical]
public static void InitializeComponent(
Type componentType,
Object componentInstance
)
{
if (componentType == null)
{
throw FxTrace.Exception.AsError(new ArgumentNullException("componentType"));
}
if (componentInstance == null)
{
throw FxTrace.Exception.AsError(new ArgumentNullException("componentInstance"));
}
Assembly typesAssembly = componentType.Assembly;
// Get the set of resources from the type's assembly.
string typeName = componentType.Name;
string typeNamespace = componentType.Namespace;
string[] resources = typesAssembly.GetManifestResourceNames();
// Look for the special resource that is generated by the BeforeInitializeComponentExtension.
string beforeInitializeResourceName;
if (string.IsNullOrWhiteSpace(typeNamespace))
{
beforeInitializeResourceName = string.Format(CultureInfo.InvariantCulture, "{0}_{1}.{2}", typeName, "BeforeInitializeComponentHelper", "txt");
}
else
{
beforeInitializeResourceName = string.Format(CultureInfo.InvariantCulture, "{0}_{1}_{2}.{3}", typeNamespace, typeName, "BeforeInitializeComponentHelper", "txt");
}
string beforeInitializeResource = FindResource(resources, beforeInitializeResourceName);
if (beforeInitializeResource == null)
{
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.BeforeInitializeComponentXBTExtensionResourceNotFound));
}
// Get the name of the XAML resource from the BeforeInitializeComponentHelper resource.
string xamlResourceName = null;
string helperClassName = null;
GetContentsOfBeforeInitializeExtensionResource(typesAssembly, beforeInitializeResource, out xamlResourceName, out helperClassName);
// Now look for the resource containing the XAML.
string fullXamlResourceName = FindResource(resources, xamlResourceName);
if (fullXamlResourceName == null)
{
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.XamlBuildTaskResourceNotFound(xamlResourceName)));
}
// Get the schema context for the type.
XamlSchemaContext typeSchemaContext = GetXamlSchemaContext(typesAssembly, helperClassName);
InitializeComponentFromXamlResource(componentType, fullXamlResourceName, componentInstance, typeSchemaContext);
}
static string FindResource(string[] resources, string partialResourceName)
{
bool foundResourceString = false;
int resourceIndex;
for (resourceIndex = 0; (resourceIndex < resources.Length); resourceIndex = (resourceIndex + 1))
{
string resource = resources[resourceIndex];
if ((resource.Contains("." + partialResourceName) || resource.Equals(partialResourceName)))
{
foundResourceString = true;
break;
}
}
if (!foundResourceString)
{
return null;
}
return resources[resourceIndex];
}
static void GetContentsOfBeforeInitializeExtensionResource(Assembly assembly, string resource, out string xamlResourceName, out string helperClassName)
{
Stream beforeInitializeStream = assembly.GetManifestResourceStream(resource);
using (StreamReader beforeInitializeReader = new StreamReader(beforeInitializeStream))
{
xamlResourceName = beforeInitializeReader.ReadLine();
helperClassName = beforeInitializeReader.ReadLine();
}
}
[SuppressMessage(FxCop.Category.Security, FxCop.Rule.SecureAsserts,
Justification = "The schema context is not critical data because it is exposed through the assembly manifest and we are asserting to go get that data.")]
[Fx.Tag.SecurityNote(Critical = "Critical because it Asserts ReflectionPermission(MemberAccess) to the calling assembly.")]
[SecurityCritical]
static XamlSchemaContext GetXamlSchemaContext(Assembly assembly, string helperClassName)
{
XamlSchemaContext typeSchemaContext = null;
ReflectionPermission reflectionPerm = new ReflectionPermission(ReflectionPermissionFlag.MemberAccess);
reflectionPerm.Assert();
try
{
Type schemaContextType = assembly.GetType(helperClassName);
if (schemaContextType == null)
{
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.SchemaContextFromBeforeInitializeComponentXBTExtensionNotFound(helperClassName)));
}
// The "official" BeforeInitializeComponent XBT Extension will not create a generic type for this helper class.
// This check is here so that the assembly manifest can't lure us into creating a type with a generic argument from a different assembly.
if (schemaContextType.IsGenericType || schemaContextType.IsGenericTypeDefinition)
{
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.SchemaContextFromBeforeInitializeComponentXBTExtensionCannotBeGeneric(helperClassName)));
}
PropertyInfo schemaContextPropertyInfo = schemaContextType.GetProperty("SchemaContext",
BindingFlags.NonPublic | BindingFlags.Static);
typeSchemaContext = (XamlSchemaContext)schemaContextPropertyInfo.GetValue(null,
BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.GetProperty, null, null, null);
}
finally
{
CodeAccessPermission.RevertAssert();
}
return typeSchemaContext;
}
[SuppressMessage(FxCop.Category.Security, "CA2103:ReviewImperativeSecurity",
Justification = "Passing XamlAccessLevel to XamlLoadPermission is okay.")]
[SuppressMessage(FxCop.Category.Security, FxCop.Rule.SecureAsserts,
Justification = "We are asserting to get private access to the componentType only so that we can initialize it.")]
[SuppressMessage("Microsoft.Security.Xml", "CA3053:UseXmlSecureResolver",
Justification = @"For the call to XmlReader.Create() below, CA3053 recommends setting the
XmlReaderSettings.XmlResolver property to either null or an instance of XmlSecureResolver.
But after setting this property to null, a warning of CA3053 still shows up in FxCop.
So we suppress this error until the reporting for CA3053 has been updated to fix this issue.")]
[Fx.Tag.SecurityNote(Critical = "Critical because it Asserts XamlLoadPermission(XamlAccessLevel.PrivateAccessTo(type).")]
[SecurityCritical]
static void InitializeComponentFromXamlResource(Type componentType, string resource, object componentInstance, XamlSchemaContext schemaContext)
{
Stream initializeXaml = componentType.Assembly.GetManifestResourceStream(resource);
XmlReader xmlReader = null;
XamlReader reader = null;
XamlObjectWriter objectWriter = null;
try
{
xmlReader = XmlReader.Create(initializeXaml, new XmlReaderSettings { XmlResolver = null });
XamlXmlReaderSettings readerSettings = new XamlXmlReaderSettings();
readerSettings.LocalAssembly = componentType.Assembly;
readerSettings.AllowProtectedMembersOnRoot = true;
reader = new XamlXmlReader(xmlReader, schemaContext, readerSettings);
XamlObjectWriterSettings writerSettings = new XamlObjectWriterSettings();
writerSettings.RootObjectInstance = componentInstance;
writerSettings.AccessLevel = XamlAccessLevel.PrivateAccessTo(componentType);
objectWriter = new XamlObjectWriter(schemaContext, writerSettings);
// We need the XamlLoadPermission for the assembly we are dealing with.
XamlLoadPermission perm = new XamlLoadPermission(XamlAccessLevel.PrivateAccessTo(componentType));
perm.Assert();
try
{
XamlServices.Transform(reader, objectWriter);
}
finally
{
CodeAccessPermission.RevertAssert();
}
}
finally
{
if ((xmlReader != null))
{
((IDisposable)(xmlReader)).Dispose();
}
if ((reader != null))
{
((IDisposable)(reader)).Dispose();
}
if ((objectWriter != null))
{
((IDisposable)(objectWriter)).Dispose();
}
}
}
class DynamicActivityReaderSchemaContext : XamlSchemaContext
{
static bool serviceModelLoaded;
const string serviceModelDll = "System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089";
const string serviceModelActivitiesDll = "System.ServiceModel.Activities, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35";
const string serviceModelNamespace = "http://schemas.microsoft.com/netfx/2009/xaml/servicemodel";
// Eventually this will be unnecessary since XAML team has changed the default behavior
public DynamicActivityReaderSchemaContext()
: base(new XamlSchemaContextSettings())
{
}
protected override XamlType GetXamlType(string xamlNamespace, string name, params XamlType[] typeArguments)
{
XamlType xamlType = base.GetXamlType(xamlNamespace, name, typeArguments);
if (xamlType == null)
{
if (xamlNamespace == serviceModelNamespace && !serviceModelLoaded)
{
Assembly.Load(serviceModelDll);
Assembly.Load(serviceModelActivitiesDll);
serviceModelLoaded = true;
xamlType = base.GetXamlType(xamlNamespace, name, typeArguments);
}
}
return xamlType;
}
}
class DynamicActivityVisitor : CompiledExpressionActivityVisitor
{
ISet<string> languages;
public string Language
{
get
{
if (this.languages == null || this.languages.Count == 0 || this.languages.Count > 1)
{
return null;
}
IEnumerator<string> languagesEnumerator = this.languages.GetEnumerator();
if (languagesEnumerator.MoveNext())
{
return languagesEnumerator.Current;
}
return null;
}
}
public bool RequiresCompilation
{
get;
private set;
}
public bool HasLanguageConflict
{
get
{
return this.languages != null && this.languages.Count > 1;
}
}
public IEnumerable<string> GetConflictingLanguages()
{
if (this.languages.Count > 1)
{
return this.languages;
}
else
{
return null;
}
}
protected override void VisitITextExpression(Activity activity, out bool exit)
{
ITextExpression textExpression = activity as ITextExpression;
if (textExpression != null)
{
if (textExpression.RequiresCompilation)
{
this.RequiresCompilation = true;
if (this.languages == null)
{
this.languages = new HashSet<string>();
}
if (!this.languages.Contains(textExpression.Language))
{
this.languages.Add(textExpression.Language);
}
}
}
base.VisitITextExpression(activity, out exit);
}
}
}
}
|