2015-04-07 09:35:12 +01:00
//------------------------------------------------------------------------------
// <copyright file="TemplateControl.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
/ *
* Page class definition
*
* Copyright ( c ) 1998 Microsoft Corporation
* /
namespace System.Web.UI {
using System ;
using System.Collections ;
using System.Collections.Generic ;
using System.Collections.Specialized ;
using System.ComponentModel ;
using System.Configuration ;
using System.Diagnostics ;
using System.Diagnostics.CodeAnalysis ;
using System.Globalization ;
using System.IO ;
using System.Reflection ;
using System.Resources ;
using System.Runtime.CompilerServices ;
using System.Security.Permissions ;
using System.Text ;
using System.Threading ;
using System.Threading.Tasks ;
using System.Web ;
using System.Web.Caching ;
using System.Web.Compilation ;
using System.Web.Util ;
using System.Xml ;
using Debug = System . Web . Util . Debug ;
/ *
* Base class for Pages and UserControls
* /
/// <devdoc>
/// <para>Provides the <see cref='System.Web.UI.Page'/> class and the <see cref='System.Web.UI.UserControl'/> class with a base set of functionality.</para>
/// </devdoc>
public abstract class TemplateControl : Control , INamingContainer , IFilterResolutionService {
// Used for the literal string optimization (reading strings from resource)
private IntPtr _stringResourcePointer ;
private int _maxResourceOffset ;
private static object _lockObject = new object ( ) ;
// Caches the list of auto-hookup methods for each compiled Type (Hashtable<Type,ListDictionary>).
// We use a Hashtable instead of the central Cache for optimal performance (VSWhidbey 479476)
private static Hashtable _eventListCache = new Hashtable ( ) ;
private static object _emptyEventSingleton = new EventList ( ) ;
private VirtualPath _virtualPath ;
private IResourceProvider _resourceProvider ;
private const string _pagePreInitEventName = "Page_PreInit" ;
private const string _pageInitEventName = "Page_Init" ;
private const string _pageInitCompleteEventName = "Page_InitComplete" ;
private const string _pageLoadEventName = "Page_Load" ;
private const string _pagePreLoadEventName = "Page_PreLoad" ;
private const string _pageLoadCompleteEventName = "Page_LoadComplete" ;
private const string _pagePreRenderCompleteEventName = "Page_PreRenderComplete" ;
private const string _pagePreRenderCompleteAsyncEventName = "Page_PreRenderCompleteAsync" ;
private const string _pageDataBindEventName = "Page_DataBind" ;
private const string _pagePreRenderEventName = "Page_PreRender" ;
private const string _pageSaveStateCompleteEventName = "Page_SaveStateComplete" ;
private const string _pageUnloadEventName = "Page_Unload" ;
private const string _pageErrorEventName = "Page_Error" ;
private const string _pageAbortTransactionEventName = "Page_AbortTransaction" ;
private const string _onTransactionAbortEventName = "OnTransactionAbort" ;
private const string _pageCommitTransactionEventName = "Page_CommitTransaction" ;
private const string _onTransactionCommitEventName = "OnTransactionCommit" ;
private static IDictionary _eventObjects ;
// Used to implement no-compile pages/uc
private BuildResultNoCompileTemplateControl _noCompileBuildResult ;
static TemplateControl ( ) {
_eventObjects = new Hashtable ( 16 ) ;
_eventObjects . Add ( _pagePreInitEventName , Page . EventPreInit ) ;
_eventObjects . Add ( _pageInitEventName , EventInit ) ;
_eventObjects . Add ( _pageInitCompleteEventName , Page . EventInitComplete ) ;
_eventObjects . Add ( _pageLoadEventName , EventLoad ) ;
_eventObjects . Add ( _pagePreLoadEventName , Page . EventPreLoad ) ;
_eventObjects . Add ( _pageLoadCompleteEventName , Page . EventLoadComplete ) ;
_eventObjects . Add ( _pagePreRenderCompleteEventName , Page . EventPreRenderComplete ) ;
_eventObjects . Add ( _pageDataBindEventName , EventDataBinding ) ;
_eventObjects . Add ( _pagePreRenderEventName , EventPreRender ) ;
_eventObjects . Add ( _pageSaveStateCompleteEventName , Page . EventSaveStateComplete ) ;
_eventObjects . Add ( _pageUnloadEventName , EventUnload ) ;
_eventObjects . Add ( _pageErrorEventName , EventError ) ;
_eventObjects . Add ( _pageAbortTransactionEventName , EventAbortTransaction ) ;
_eventObjects . Add ( _onTransactionAbortEventName , EventAbortTransaction ) ;
_eventObjects . Add ( _pageCommitTransactionEventName , EventCommitTransaction ) ;
_eventObjects . Add ( _onTransactionCommitEventName , EventCommitTransaction ) ;
}
protected TemplateControl ( ) {
Construct ( ) ;
}
/// <devdoc>
/// <para>Do construction time logic (ASURT 66166)</para>
/// </devdoc>
protected virtual void Construct ( ) { }
private static readonly object EventCommitTransaction = new object ( ) ;
/// <devdoc>
/// <para>Occurs when a user initiates a transaction.</para>
/// </devdoc>
[
WebSysDescription ( SR . Page_OnCommitTransaction )
]
public event EventHandler CommitTransaction {
add {
Events . AddHandler ( EventCommitTransaction , value ) ;
}
remove {
Events . RemoveHandler ( EventCommitTransaction , value ) ;
}
}
/// <devdoc>
/// <para>Gets and sets a value indicating whether theme is enabled.</para>
/// </devdoc>
[
Browsable ( true )
]
public override bool EnableTheming {
get {
return base . EnableTheming ;
}
set {
base . EnableTheming = value ;
}
}
/// <devdoc>
/// <para>Raises the <see langword='CommitTransaction'/> event. You can use this method
/// for any transaction processing logic in which your page or user control
/// participates.</para>
/// </devdoc>
protected virtual void OnCommitTransaction ( EventArgs e ) {
EventHandler handler = ( EventHandler ) Events [ EventCommitTransaction ] ;
if ( handler ! = null ) handler ( this , e ) ;
}
private static readonly object EventAbortTransaction = new object ( ) ;
/// <devdoc>
/// <para>Occurs when a user aborts a transaction.</para>
/// </devdoc>
[
WebSysDescription ( SR . Page_OnAbortTransaction )
]
public event EventHandler AbortTransaction {
add {
Events . AddHandler ( EventAbortTransaction , value ) ;
}
remove {
Events . RemoveHandler ( EventAbortTransaction , value ) ;
}
}
/// <devdoc>
/// <para>Raises the <see langword='AbortTransaction'/> event.</para>
/// </devdoc>
protected virtual void OnAbortTransaction ( EventArgs e ) {
EventHandler handler = ( EventHandler ) Events [ EventAbortTransaction ] ;
if ( handler ! = null ) handler ( this , e ) ;
}
// Page_Error related events/methods
private static readonly object EventError = new object ( ) ;
/// <devdoc>
/// <para>Occurs when an uncaught exception is thrown.</para>
/// </devdoc>
[
2016-02-22 11:00:01 -05:00
WebSysDescription ( SR . Page_ErrorDescription )
2015-04-07 09:35:12 +01:00
]
public event EventHandler Error {
add {
Events . AddHandler ( EventError , value ) ;
}
remove {
Events . RemoveHandler ( EventError , value ) ;
}
}
/// <devdoc>
/// <para>Raises the <see langword='Error'/> event.
/// </para>
/// </devdoc>
protected virtual void OnError ( EventArgs e ) {
EventHandler handler = ( EventHandler ) Events [ EventError ] ;
if ( handler ! = null ) handler ( this , e ) ;
}
/ *
* Receive a no - compile build result that we call during FrameworkInitialize
* /
internal void SetNoCompileBuildResult ( BuildResultNoCompileTemplateControl noCompileBuildResult ) {
_noCompileBuildResult = noCompileBuildResult ;
}
internal bool NoCompile {
get { return _noCompileBuildResult ! = null ; }
}
/ *
* Method sometime overidden by the generated sub classes . Users
* should not override .
* /
/// <internalonly/>
/// <devdoc>
/// <para>Initializes the requested page. While this is sometimes
/// overridden when the page is generated at runtime, you should not explicitly override this method.</para>
/// </devdoc>
[EditorBrowsable(EditorBrowsableState.Never)]
protected virtual void FrameworkInitialize ( ) {
// If it's non-compiled, perform the FrameworkInitialize logic.
if ( NoCompile ) {
if ( ! HttpRuntime . DisableProcessRequestInApplicationTrust ) {
// If PermitOnly was disabled in config, we call PermitOnly from here so that at least
// the control tree creation is protected, as it always is for compiled pages (VSWhidbey 449666)
if ( HttpRuntime . NamedPermissionSet ! = null & & ! HttpRuntime . ProcessRequestInApplicationTrust ) {
HttpRuntime . NamedPermissionSet . PermitOnly ( ) ;
}
}
_noCompileBuildResult . FrameworkInitialize ( this ) ;
}
}
/ *
* This property is overriden by the generated classes ( hence it cannot be internal )
* If false , we don ' t do the HookUpAutomaticHandlers ( ) magic .
* /
/// <internalonly/>
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[EditorBrowsable(EditorBrowsableState.Never)]
protected virtual bool SupportAutoEvents {
get { return true ; }
}
/ *
* Returns a pointer to the resource buffer , and the largest valid offset
* in the buffer ( for security reason )
* /
internal IntPtr StringResourcePointer { get { return _stringResourcePointer ; } }
internal int MaxResourceOffset { get { return _maxResourceOffset ; } }
// This method is now obsolete. Ideally, we should get rid of it altogether, but that
// would be a breaking change (VSWhidbey 464430)
// We now use the parameter-less override, which is simpler
[EditorBrowsable(EditorBrowsableState.Never)]
public static object ReadStringResource ( Type t ) {
return StringResourceManager . ReadSafeStringResource ( t ) ;
}
[EditorBrowsable(EditorBrowsableState.Never)]
public object ReadStringResource ( ) {
return StringResourceManager . ReadSafeStringResource ( GetType ( ) ) ;
}
/// <internalonly/>
/// <devdoc>
/// <para>This method is called by the generated classes (hence it cannot be internal)</para>
/// </devdoc>
protected LiteralControl CreateResourceBasedLiteralControl ( int offset , int size , bool fAsciiOnly ) {
return new ResourceBasedLiteralControl ( this , offset , size , fAsciiOnly ) ;
}
/// <internalonly/>
/// <devdoc>
/// <para>This method is called by the generated classes (hence it cannot be internal)</para>
/// </devdoc>
[EditorBrowsable(EditorBrowsableState.Never)]
protected void SetStringResourcePointer ( object stringResourcePointer , int maxResourceOffset ) {
// Ignore the passed in maxResourceOffset, which cannot be trusted. Instead, use
// the resource size that we obtained from the resource (ASURT 122759)
SafeStringResource ssr = ( SafeStringResource ) stringResourcePointer ;
_stringResourcePointer = ssr . StringResourcePointer ;
_maxResourceOffset = ssr . ResourceSize ;
}
internal VirtualPath VirtualPath {
get {
return _virtualPath ;
}
}
[
EditorBrowsable ( EditorBrowsableState . Advanced ) ,
Browsable ( false )
]
public string AppRelativeVirtualPath {
get {
return VirtualPath . GetAppRelativeVirtualPathString ( TemplateControlVirtualPath ) ;
}
set {
// Set the TemplateSourceDirectory based on the VirtualPath
this . TemplateControlVirtualPath = VirtualPath . CreateNonRelative ( value ) ;
}
}
internal VirtualPath TemplateControlVirtualPath {
get {
return _virtualPath ;
}
set {
_virtualPath = value ;
// Set the TemplateSourceDirectory based on the VirtualPath
this . TemplateControlVirtualDirectory = _virtualPath . Parent ;
}
}
/// <devdoc>
/// <para>Tests if a device filter applies to this request</para>
/// </devdoc>
public virtual bool TestDeviceFilter ( string filterName ) {
return ( Context . Request . Browser . IsBrowser ( filterName ) ) ;
}
/// <internalonly/>
/// <devdoc>
/// <para>This method is called by the generated classes (hence it cannot be internal)</para>
/// </devdoc>
[EditorBrowsable(EditorBrowsableState.Never)]
protected void WriteUTF8ResourceString ( HtmlTextWriter output , int offset , int size , bool fAsciiOnly ) {
// Make sure we don't access invalid data
checked {
if ( offset < 0 | | size < 0 | | offset + size > _maxResourceOffset )
throw new ArgumentOutOfRangeException ( "offset" ) ;
}
output . WriteUTF8ResourceString ( StringResourcePointer , offset , size , fAsciiOnly ) ;
}
/ *
* This method is overriden by the generated classes ( hence it cannot be internal )
* /
/// <internalonly/>
/// <devdoc>
/// </devdoc>
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("Use of this property is not recommended because it is no longer useful. http://go.microsoft.com/fwlink/?linkid=14202")]
protected virtual int AutoHandlers {
get { return 0 ; }
set { }
}
internal override TemplateControl GetTemplateControl ( ) {
return this ;
}
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "See comment on GetDelegateInformationWithAssert.")]
internal void HookUpAutomaticHandlers ( ) {
// Do nothing if auto-events are not supported
if ( ! SupportAutoEvents ) {
return ;
}
// Get the event list for this Type from our cache, if possible
object o = _eventListCache [ GetType ( ) ] ;
EventList eventList ;
// Try to find what handlers are implemented if not tried before
if ( o = = null ) {
lock ( _lockObject ) {
// Try the cache again, in case another thread took care of it
o = ( EventList ) _eventListCache [ GetType ( ) ] ;
if ( o = = null ) {
eventList = new EventList ( ) ;
GetDelegateInformation ( eventList ) ;
// Cannot find any known handlers.
if ( eventList . IsEmpty ) {
o = _emptyEventSingleton ;
}
else {
o = eventList ;
}
// Cache it for next time
_eventListCache [ GetType ( ) ] = o ;
}
}
}
// Don't do any thing if no known handlers are found.
if ( o = = _emptyEventSingleton ) {
return ;
}
eventList = ( EventList ) o ;
IDictionary < string , SyncEventMethodInfo > syncEvents = eventList . SyncEvents ;
// Hook up synchronous events
foreach ( var entry in syncEvents ) {
string key = entry . Key ;
SyncEventMethodInfo info = entry . Value ;
Debug . Assert ( _eventObjects [ key ] ! = null ) ;
bool eventExists = false ;
MethodInfo methodInfo = info . MethodInfo ;
Delegate eventDelegates = Events [ _eventObjects [ key ] ] ;
if ( eventDelegates ! = null ) {
foreach ( Delegate eventDelegate in eventDelegates . GetInvocationList ( ) ) {
// Ignore if this method is already added to the events list.
if ( eventDelegate . Method . Equals ( methodInfo ) ) {
eventExists = true ;
break ;
}
}
}
if ( ! eventExists ) {
// Create a new Calli delegate proxy
IntPtr functionPtr = methodInfo . MethodHandle . GetFunctionPointer ( ) ;
EventHandler handler = ( new CalliEventHandlerDelegateProxy ( this , functionPtr , info . IsArgless ) ) . Handler ;
// Adds the delegate to events list.
Events . AddHandler ( _eventObjects [ key ] , handler ) ;
}
}
// Hook up asynchronous events
IDictionary < string , AsyncEventMethodInfo > asyncEvents = eventList . AsyncEvents ;
AsyncEventMethodInfo preRenderCompleteAsyncEvent ;
if ( asyncEvents . TryGetValue ( _pagePreRenderCompleteAsyncEventName , out preRenderCompleteAsyncEvent ) ) {
Page page = ( Page ) this ; // this event handler only exists for the Page type
if ( preRenderCompleteAsyncEvent . RequiresCancellationToken ) {
var handler = FastDelegateCreator < Func < CancellationToken , Task > > . BindTo ( this , preRenderCompleteAsyncEvent . MethodInfo ) ;
page . RegisterAsyncTask ( new PageAsyncTask ( handler ) ) ;
}
else {
var handler = FastDelegateCreator < Func < Task > > . BindTo ( this , preRenderCompleteAsyncEvent . MethodInfo ) ;
page . RegisterAsyncTask ( new PageAsyncTask ( handler ) ) ;
}
}
}
private void GetDelegateInformation ( EventList eventList ) {
if ( HttpRuntime . IsFullTrust ) {
GetDelegateInformationWithNoAssert ( eventList ) ;
}
else {
GetDelegateInformationWithAssert ( eventList ) ;
}
}
// Make sure we have reflection permission to discover the handlers (ASURT 105965)
// Using this permission is bad practice; we should use RMA instead of full MemberAccess,
// or we should force the instance methods to be public. But this is a legacy behavior
// and we can't change it without breaking the world.
[ReflectionPermission(SecurityAction.Assert, Flags = ReflectionPermissionFlag.MemberAccess)]
[SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Justification = "See comment above.")]
private void GetDelegateInformationWithAssert ( EventList eventList ) {
GetDelegateInformationWithNoAssert ( eventList ) ;
}
private void GetDelegateInformationWithNoAssert ( EventList eventList ) {
IDictionary < string , SyncEventMethodInfo > syncEventDictionary = eventList . SyncEvents ;
IDictionary < string , AsyncEventMethodInfo > asyncEventDictionary = eventList . AsyncEvents ;
if ( this is Page ) {
/* SYNCHRONOUS - Page */
GetDelegateInformationFromSyncMethod ( _pagePreInitEventName , syncEventDictionary ) ;
GetDelegateInformationFromSyncMethod ( _pagePreLoadEventName , syncEventDictionary ) ;
GetDelegateInformationFromSyncMethod ( _pageLoadCompleteEventName , syncEventDictionary ) ;
GetDelegateInformationFromSyncMethod ( _pagePreRenderCompleteEventName , syncEventDictionary ) ;
GetDelegateInformationFromSyncMethod ( _pageInitCompleteEventName , syncEventDictionary ) ;
GetDelegateInformationFromSyncMethod ( _pageSaveStateCompleteEventName , syncEventDictionary ) ;
/* ASYNCHRONOUS - Page */
GetDelegateInformationFromAsyncMethod ( _pagePreRenderCompleteAsyncEventName , asyncEventDictionary ) ;
}
/* SYNCHRONOUS - Control */
GetDelegateInformationFromSyncMethod ( _pageInitEventName , syncEventDictionary ) ;
GetDelegateInformationFromSyncMethod ( _pageLoadEventName , syncEventDictionary ) ;
GetDelegateInformationFromSyncMethod ( _pageDataBindEventName , syncEventDictionary ) ;
GetDelegateInformationFromSyncMethod ( _pagePreRenderEventName , syncEventDictionary ) ;
GetDelegateInformationFromSyncMethod ( _pageUnloadEventName , syncEventDictionary ) ;
GetDelegateInformationFromSyncMethod ( _pageErrorEventName , syncEventDictionary ) ;
if ( ! GetDelegateInformationFromSyncMethod ( _pageAbortTransactionEventName , syncEventDictionary ) ) {
GetDelegateInformationFromSyncMethod ( _onTransactionAbortEventName , syncEventDictionary ) ;
}
if ( ! GetDelegateInformationFromSyncMethod ( _pageCommitTransactionEventName , syncEventDictionary ) ) {
GetDelegateInformationFromSyncMethod ( _onTransactionCommitEventName , syncEventDictionary ) ;
}
/* ASYNCHRONOUS - Control */
}
private bool GetDelegateInformationFromAsyncMethod ( string methodName , IDictionary < string , AsyncEventMethodInfo > dictionary ) {
// First, try to get a delegate to the single-parameter handler
MethodInfo parameterfulMethod = GetInstanceMethodInfo ( typeof ( Func < CancellationToken , Task > ) , methodName ) ;
if ( parameterfulMethod ! = null ) {
dictionary [ methodName ] = new AsyncEventMethodInfo ( parameterfulMethod , requiresCancellationToken : true ) ;
return true ;
}
// If there isn't one, try the argless one
MethodInfo parameterlessMethod = GetInstanceMethodInfo ( typeof ( Func < Task > ) , methodName ) ;
if ( parameterlessMethod ! = null ) {
dictionary [ methodName ] = new AsyncEventMethodInfo ( parameterlessMethod , requiresCancellationToken : false ) ;
return true ;
}
return false ;
}
private bool GetDelegateInformationFromSyncMethod ( string methodName , IDictionary < string , SyncEventMethodInfo > dictionary ) {
// First, try to get a delegate to the two parameter handler
MethodInfo parameterfulMethod = GetInstanceMethodInfo ( typeof ( EventHandler ) , methodName ) ;
if ( parameterfulMethod ! = null ) {
dictionary [ methodName ] = new SyncEventMethodInfo ( parameterfulMethod , isArgless : false ) ;
return true ;
}
// If there isn't one, try the argless one
MethodInfo parameterlessMethod = GetInstanceMethodInfo ( typeof ( VoidMethod ) , methodName ) ;
if ( parameterlessMethod ! = null ) {
dictionary [ methodName ] = new SyncEventMethodInfo ( parameterlessMethod , isArgless : true ) ;
return true ;
}
return false ;
}
private MethodInfo GetInstanceMethodInfo ( Type delegateType , string methodName ) {
Delegate del = Delegate . CreateDelegate (
type : delegateType ,
target : this ,
method : methodName ,
ignoreCase : true ,
throwOnBindFailure : false ) ;
return ( del ! = null ) ? del . Method : null ;
}
/// <devdoc>
/// <para>Obtains a <see cref='System.Web.UI.UserControl'/> object from a user control file.</para>
/// </devdoc>
public Control LoadControl ( string virtualPath ) {
return LoadControl ( VirtualPath . Create ( virtualPath ) ) ;
}
internal Control LoadControl ( VirtualPath virtualPath ) {
// If it's relative, make it *app* relative. Treat is as relative to this
// user control (ASURT 55513)
virtualPath = VirtualPath . Combine ( this . TemplateControlVirtualDirectory , virtualPath ) ;
// Process the user control and get its BuildResult
BuildResult result = BuildManager . GetVPathBuildResult ( Context , virtualPath ) ;
return LoadControl ( ( IWebObjectFactory ) result , virtualPath , null /*Type*/ , null /*parameters*/ ) ;
}
// Make sure we have reflection permission to use GetMethod below (ASURT 106196)
[ReflectionPermission(SecurityAction.Assert, Flags=ReflectionPermissionFlag.MemberAccess)]
private void AddStackContextToHashCode ( HashCodeCombiner combinedHashCode ) {
StackTrace st = new StackTrace ( ) ;
// First, skip all the stack frames that are in the TemplateControl class, as
// they are irrelevant to the hash. Start the search at 2 since we know for sure
// that this method and its caller are in TemplateControl.
int startingUserFrame = 2 ;
for ( ; ; startingUserFrame + + ) {
StackFrame f = st . GetFrame ( startingUserFrame ) ;
if ( f . GetMethod ( ) . DeclaringType ! = typeof ( TemplateControl ) ) {
break ;
}
}
// Get a cache key based on the top two items of the caller's stack.
// It's not guaranteed unique, but for all common cases, it will be
for ( int i = startingUserFrame ; i < startingUserFrame + 2 ; i + + ) {
StackFrame f = st . GetFrame ( i ) ;
MethodBase m = f . GetMethod ( ) ;
combinedHashCode . AddObject ( m . DeclaringType . AssemblyQualifiedName ) ;
combinedHashCode . AddObject ( m . Name ) ;
combinedHashCode . AddObject ( f . GetNativeOffset ( ) ) ;
}
}
public Control LoadControl ( Type t , object [ ] parameters ) {
return LoadControl ( null /*IWebObjectFactory*/ , null /*virtualPath*/ , t , parameters ) ;
}
private Control LoadControl ( IWebObjectFactory objectFactory , VirtualPath virtualPath , Type t , object [ ] parameters ) {
// Make sure we get an object factory or a type, but not both
Debug . Assert ( ( objectFactory = = null ) ! = ( t = = null ) ) ;
BuildResultCompiledType compiledUCResult = null ;
BuildResultNoCompileUserControl noCompileUCResult = null ;
if ( objectFactory ! = null ) {
// It can be a compiled or no-compile user control
compiledUCResult = objectFactory as BuildResultCompiledType ;
if ( compiledUCResult ! = null ) {
t = compiledUCResult . ResultType ;
Debug . Assert ( t ! = null ) ;
// Make sure it's a user control (VSWhidbey 428718)
Util . CheckAssignableType ( typeof ( UserControl ) , t ) ;
}
else {
noCompileUCResult = ( BuildResultNoCompileUserControl ) objectFactory ;
Debug . Assert ( noCompileUCResult ! = null ) ;
}
}
else {
// Make sure the type has the correct base class (ASURT 123677)
if ( t ! = null )
Util . CheckAssignableType ( typeof ( Control ) , t ) ;
}
PartialCachingAttribute cacheAttrib ;
// Check if the user control has a PartialCachingAttribute attribute
if ( t ! = null ) {
cacheAttrib = ( PartialCachingAttribute )
TypeDescriptor . GetAttributes ( t ) [ typeof ( PartialCachingAttribute ) ] ;
}
else {
cacheAttrib = noCompileUCResult . CachingAttribute ;
}
if ( cacheAttrib = = null ) {
// The control is not cached. Just create it.
Control c ;
if ( objectFactory ! = null ) {
c = ( Control ) objectFactory . CreateInstance ( ) ;
}
else {
c = ( Control ) HttpRuntime . CreatePublicInstance ( t , parameters ) ;
}
// If it's a user control, do some extra initialization
UserControl uc = c as UserControl ;
if ( uc ! = null ) {
Debug . Assert ( virtualPath ! = null ) ;
if ( virtualPath ! = null )
uc . TemplateControlVirtualPath = virtualPath ;
uc . InitializeAsUserControl ( Page ) ;
}
return c ;
}
HashCodeCombiner combinedHashCode = new HashCodeCombiner ( ) ;
// Start by adding the type or object factory of the user control to the hash.
// This guarantees that two unrelated user controls don't share the same cached data.
if ( objectFactory ! = null ) {
combinedHashCode . AddObject ( objectFactory ) ;
}
else {
combinedHashCode . AddObject ( t ) ;
}
// If it's not shared, add some stack frames to the hash
if ( ! cacheAttrib . Shared ) {
AddStackContextToHashCode ( combinedHashCode ) ;
}
string cacheKey = combinedHashCode . CombinedHashString ;
// Wrap it to allow it to be cached
return new PartialCachingControl ( objectFactory , t , cacheAttrib , "_" + cacheKey , parameters ) ;
}
// Class that implements the templates returned by LoadTemplate (ASURT 94138)
internal class SimpleTemplate : ITemplate {
private IWebObjectFactory _objectFactory ;
internal SimpleTemplate ( ITypedWebObjectFactory objectFactory ) {
// Make sure it's a user control (VSWhidbey 428718)
Util . CheckAssignableType ( typeof ( UserControl ) , objectFactory . InstantiatedType ) ;
_objectFactory = objectFactory ;
}
public virtual void InstantiateIn ( Control control ) {
UserControl uc = ( UserControl ) _objectFactory . CreateInstance ( ) ;
uc . InitializeAsUserControl ( control . Page ) ;
control . Controls . Add ( uc ) ;
}
}
/// <devdoc>
/// <para>
/// Obtains an instance of the <see langword='ITemplate'/> interface from an
/// external file.
/// </para>
/// </devdoc>
public ITemplate LoadTemplate ( string virtualPath ) {
return LoadTemplate ( VirtualPath . Create ( virtualPath ) ) ;
}
internal ITemplate LoadTemplate ( VirtualPath virtualPath ) {
// If it's relative, make it *app* relative. Treat is as relative to this
// user control (ASURT 55513)
virtualPath = VirtualPath . Combine ( TemplateControlVirtualDirectory , virtualPath ) ;
// Compile the declarative template and get its object factory
ITypedWebObjectFactory objectFactory = ( ITypedWebObjectFactory ) BuildManager . GetVPathBuildResult (
Context , virtualPath ) ;
return new SimpleTemplate ( objectFactory ) ;
}
/// <devdoc>
/// <para> Parse the input string into a Control. Looks for the first control
/// in the input. Returns null if none is found.</para>
/// </devdoc>
public Control ParseControl ( string content ) {
return ParseControl ( content , true ) ;
}
public Control ParseControl ( string content , bool ignoreParserFilter ) {
return TemplateParser . ParseControl ( content , VirtualPath . Create ( AppRelativeVirtualPath ) , ignoreParserFilter ) ;
}
#if NOTYET
/// <devdoc>
/// <para> Parse the input string into an ITemplate.</para>
/// </devdoc>
internal ITemplate ParseTemplate ( string content ) {
return TemplateParser . ParseTemplate ( content , AppRelativeTemplateSourceDirectory ) ;
}
#endif
/// <devdoc>
/// Used by simplified databinding methods to ensure they can only be called when the control is on a page.
/// </devdoc>
private void CheckPageExists ( ) {
if ( Page = = null ) {
throw new InvalidOperationException ( SR . GetString ( SR . TemplateControl_DataBindingRequiresPage ) ) ;
}
}
/// <devdoc>
/// Simplified databinding Eval() method. This method uses the current data item to evaluate an expression using DataBinder.Eval().
/// The data item is retrieved using either the IDataItemContainer interface or by looking for a property called 'DataItem'.
/// If the data item is not found, an exception is thrown.
/// </devdoc>
protected internal object Eval ( string expression ) {
CheckPageExists ( ) ;
return DataBinder . Eval ( Page . GetDataItem ( ) , expression ) ;
}
/// <devdoc>
/// Simplified databinding Eval() method with a format expression. This method uses the current data item to evaluate an expression using DataBinder.Eval().
/// The data item is retrieved using either the IDataItemContainer interface or by looking for a property called 'DataItem'.
/// If the data item is not found, an exception is thrown.
/// </devdoc>
protected internal string Eval ( string expression , string format ) {
CheckPageExists ( ) ;
return DataBinder . Eval ( Page . GetDataItem ( ) , expression , format ) ;
}
/// <devdoc>
/// Simplified databinding XPath() method. This method uses the current data item to evaluate an XPath expression using XPathBinder.Eval().
/// The data item is retrieved using either the IDataItemContainer interface or by looking for a property called 'DataItem'.
/// If the data item is not found, an exception is thrown.
/// </devdoc>
protected internal object XPath ( string xPathExpression ) {
CheckPageExists ( ) ;
return XPathBinder . Eval ( Page . GetDataItem ( ) , xPathExpression ) ;
}
/// <devdoc>
/// Simplified databinding XPath() method. This method uses the current data item and a namespace resolver
/// to evaluate an XPath expression using XPathBinder.Eval().
/// The data item is retrieved using either the IDataItemContainer interface or by looking for a property called 'DataItem'.
/// If the data item is not found, an exception is thrown.
/// </devdoc>
protected internal object XPath ( string xPathExpression , IXmlNamespaceResolver resolver ) {
CheckPageExists ( ) ;
return XPathBinder . Eval ( Page . GetDataItem ( ) , xPathExpression , resolver ) ;
}
/// <devdoc>
/// Simplified databinding XPath() method with a format expression. This method uses the current data item to evaluate an XPath expression using XPathBinder.Eval().
/// The data item is retrieved using either the IDataItemContainer interface or by looking for a property called 'DataItem'.
/// If the data item is not found, an exception is thrown.
/// </devdoc>
protected internal string XPath ( string xPathExpression , string format ) {
CheckPageExists ( ) ;
return XPathBinder . Eval ( Page . GetDataItem ( ) , xPathExpression , format ) ;
}
/// <devdoc>
/// Simplified databinding XPath() method with a format expression. This method uses the current data item and a namespace resolver
/// to evaluate an XPath expression using XPathBinder.Eval().
/// The data item is retrieved using either the IDataItemContainer interface or by looking for a property called 'DataItem'.
/// If the data item is not found, an exception is thrown.
/// </devdoc>
protected internal string XPath ( string xPathExpression , string format , IXmlNamespaceResolver resolver ) {
CheckPageExists ( ) ;
return XPathBinder . Eval ( Page . GetDataItem ( ) , xPathExpression , format , resolver ) ;
}
/// <devdoc>
/// Simplified databinding XPathSelect() method. This method uses the current data item to evaluate an XPath expression that returns a node list using XPathBinder.Select().
/// The data item is retrieved using either the IDataItemContainer interface or by looking for a property called 'DataItem'.
/// If the data item is not found, an exception is thrown.
/// </devdoc>
protected internal IEnumerable XPathSelect ( string xPathExpression ) {
CheckPageExists ( ) ;
return XPathBinder . Select ( Page . GetDataItem ( ) , xPathExpression ) ;
}
/// <devdoc>
/// Simplified databinding XPathSelect() method. This method uses the current data item and a namespace resolver
/// to evaluate an XPath expression that returns a node list using XPathBinder.Select().
/// The data item is retrieved using either the IDataItemContainer interface or by looking for a property called 'DataItem'.
/// If the data item is not found, an exception is thrown.
/// </devdoc>
protected internal IEnumerable XPathSelect ( string xPathExpression , IXmlNamespaceResolver resolver ) {
CheckPageExists ( ) ;
return XPathBinder . Select ( Page . GetDataItem ( ) , xPathExpression , resolver ) ;
}
/// <devdoc>
/// Return a Page-level resource object
/// </devdoc>
protected object GetLocalResourceObject ( string resourceKey ) {
// Cache the resource provider in the template control, so that if a Page needs to call
// this multiple times, we don't need to call ResourceExpressionBuilder.GetLocalResourceProvider
// every time.
if ( _resourceProvider = = null )
_resourceProvider = ResourceExpressionBuilder . GetLocalResourceProvider ( this ) ;
return ResourceExpressionBuilder . GetResourceObject ( _resourceProvider , resourceKey , null /*culture*/ ) ;
}
protected object GetLocalResourceObject ( string resourceKey , Type objType , string propName ) {
// Cache the resource provider in the template control, so that if a Page needs to call
// this multiple times, we don't need to call ResourceExpressionBuilder.GetLocalResourceProvider
// every time.
if ( _resourceProvider = = null )
_resourceProvider = ResourceExpressionBuilder . GetLocalResourceProvider ( this ) ;
return ResourceExpressionBuilder . GetResourceObject ( _resourceProvider ,
resourceKey , null /*culture*/ , objType , propName ) ;
}
/// <devdoc>
/// Return an App-level resource object
/// </devdoc>
protected object GetGlobalResourceObject ( string className , string resourceKey ) {
return ResourceExpressionBuilder . GetGlobalResourceObject ( className , resourceKey , null , null , null ) ;
}
protected object GetGlobalResourceObject ( string className , string resourceKey , Type objType , string propName ) {
return ResourceExpressionBuilder . GetGlobalResourceObject ( className , resourceKey , objType , propName , null ) ;
}
#region IFilterResolutionService
/// <internalonly/>
bool IFilterResolutionService . EvaluateFilter ( string filterName ) {
return TestDeviceFilter ( filterName ) ;
}
/// <internalonly/>
int IFilterResolutionService . CompareFilters ( string filter1 , string filter2 ) {
return BrowserCapabilitiesCompiler . BrowserCapabilitiesFactory . CompareFilters ( filter1 , filter2 ) ;
}
#endregion
private class EventList {
internal readonly IDictionary < string , AsyncEventMethodInfo > AsyncEvents = new Dictionary < string , AsyncEventMethodInfo > ( StringComparer . Ordinal ) ;
internal readonly IDictionary < string , SyncEventMethodInfo > SyncEvents = new Dictionary < string , SyncEventMethodInfo > ( StringComparer . Ordinal ) ;
internal bool IsEmpty {
get {
return ( AsyncEvents . Count = = 0 & & SyncEvents . Count = = 0 ) ;
}
}
}
// Internal helper class for storing the event info
private class SyncEventMethodInfo {
internal SyncEventMethodInfo ( MethodInfo methodInfo , bool isArgless ) {
if ( IsAsyncVoidMethod ( methodInfo ) ) {
SynchronizationContextUtil . ValidateModeForPageAsyncVoidMethods ( ) ;
}
MethodInfo = methodInfo ;
IsArgless = isArgless ;
}
internal bool IsArgless { get ; private set ; }
internal MethodInfo MethodInfo { get ; private set ; }
private static bool IsAsyncVoidMethod ( MethodInfo methodInfo ) {
// When the C# / VB compilers generate an 'async void' method, they'll put
// an [AsyncStateMachine] attribute on the entry point. This marker attribute
// can be used to detect these methods. It's not 100% reliable, since it's
// possible that a normal void method simply calls an async void method, and
// the 'outer' method won't contain this attribute. But the heuristic is
// good enough to help developers land in the pit of success re: async.
return methodInfo . IsDefined ( typeof ( AsyncStateMachineAttribute ) , inherit : false ) ;
}
}
private class AsyncEventMethodInfo {
internal AsyncEventMethodInfo ( MethodInfo methodInfo , bool requiresCancellationToken ) {
MethodInfo = methodInfo ;
RequiresCancellationToken = requiresCancellationToken ;
}
internal MethodInfo MethodInfo { get ; private set ; }
internal bool RequiresCancellationToken { get ; private set ; }
}
}
}