2016-08-03 10:59:49 +00:00
//------------------------------------------------------------------------------
// <copyright file="BrowserCapabilitiesCodeGenerator.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
namespace System.Web.Configuration {
using System ;
using System.CodeDom ;
using System.CodeDom.Compiler ;
using System.Configuration ;
using System.Collections ;
using System.Collections.Specialized ;
using System.IO ;
#if ! FEATURE_PAL
using System.ServiceProcess ;
#endif // !FEATURE_PAL
using System.Linq ;
using System.Reflection ;
using System.Security ;
using System.Security.Permissions ;
using System.Text ;
using System.Text.RegularExpressions ;
using System.Web ;
using System.Web.Compilation ;
using System.Web.Configuration ;
using System.Web.Hosting ;
using System.Web.UI ;
using System.Web.Util ;
using System.Xml ;
using System.Xml.Schema ;
using Microsoft.Build.Utilities ;
using Microsoft.CSharp ;
2016-11-10 13:04:39 +00:00
using System.Diagnostics.CodeAnalysis ;
2016-08-03 10:59:49 +00:00
[PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
[PermissionSet(SecurityAction.InheritanceDemand, Unrestricted = true)]
public class BrowserCapabilitiesCodeGenerator {
private static readonly string _browsersDirectory ;
private static readonly string _publicKeyTokenFile ;
private static object _staticLock = new object ( ) ;
private BrowserTree _browserTree ;
private BrowserTree _defaultTree ;
private BrowserDefinitionCollection _browserDefinitionCollection ;
internal const string browserCapsVariable = "browserCaps" ;
internal const string IgnoreApplicationBrowserVariableName = "ignoreApplicationBrowsers" ;
private const string _factoryTypeName = "BrowserCapabilitiesFactory" ;
private const string _headerDictionaryVarName = "_headerDictionary" ;
private const string _disableOptimizedCacheKeyMethodName = "DisableOptimizedCacheKey" ;
private const string _matchedHeadersMethodName = "PopulateMatchedHeaders" ;
private const string _browserElementsMethodName = "PopulateBrowserElements" ;
private const string _dictionaryRefName = "dictionary" ;
private const string _regexWorkerRefName = "regexWorker" ;
private const string _headersRefName = "headers" ;
private const string _resultVarName = "result" ;
private const string _processRegexMethod = "ProcessRegex" ;
private static readonly string _strongNameKeyFileName = browserCapsVariable + ".snk" ;
private static readonly string _publicKeyTokenFileName = browserCapsVariable + ".token" ;
private static bool _publicKeyTokenLoaded ;
private static string _publicKeyToken ;
private CodeVariableReferenceExpression _dictionaryRefExpr = new CodeVariableReferenceExpression ( _dictionaryRefName ) ;
private CodeVariableReferenceExpression _regexWorkerRefExpr = new CodeVariableReferenceExpression ( _regexWorkerRefName ) ;
private CodeVariableReferenceExpression _headersRefExpr = new CodeVariableReferenceExpression ( _headersRefName ) ;
private CodeVariableReferenceExpression _browserCapsRefExpr = new CodeVariableReferenceExpression ( browserCapsVariable ) ;
private ArrayList _browserFileList ;
private ArrayList _customBrowserFileLists ;
private ArrayList _customTreeList ;
private ArrayList _customTreeNames ;
private ArrayList _customBrowserDefinitionCollections ;
private CaseInsensitiveStringSet _headers ;
static BrowserCapabilitiesCodeGenerator ( ) {
#if ! PLATFORM_UNIX // File system paths must account for UNIX
_browsersDirectory = HttpRuntime . ClrInstallDirectoryInternal + "\\config\\browsers" ;
_publicKeyTokenFile = _browsersDirectory + "\\" + _publicKeyTokenFileName ;
#else // !PLATFORM_UNIX
_browsersDirectory = HttpRuntime . ClrInstallDirectoryInternal + "/config/browsers" ;
_publicKeyTokenFile = _browsersDirectory + "/" + _publicKeyTokenFileName ;
#endif // !PLATFORM_UNIX
}
public BrowserCapabilitiesCodeGenerator ( ) {
_headers = new CaseInsensitiveStringSet ( ) ;
}
internal BrowserTree BrowserTree {
get {
return _browserTree ;
}
}
internal BrowserTree DefaultTree {
get {
return _defaultTree ;
}
}
internal ArrayList CustomTreeList {
get {
return _customTreeList ;
}
}
internal ArrayList CustomTreeNames {
get {
return _customTreeNames ;
}
}
internal static string BrowserCapAssemblyPublicKeyToken {
get {
if ( _publicKeyTokenLoaded ) {
return _publicKeyToken ;
}
lock ( _staticLock ) {
if ( _publicKeyTokenLoaded ) {
return _publicKeyToken ;
}
string publicKeyTokenFile ;
if ( MultiTargetingUtil . IsTargetFramework40OrAbove ) {
publicKeyTokenFile = _publicKeyTokenFile ;
}
else {
// If we are targeting pre-4.0, we should be using version 2.0 of the assembly
// ASP.BrowserCapsFactory, so we need to read the token file from the 2.0 path.
// (Dev10 bug 795509)
string subPath = @"config\browsers\" + _publicKeyTokenFileName ;
publicKeyTokenFile = ToolLocationHelper . GetPathToDotNetFrameworkFile ( subPath , TargetDotNetFrameworkVersion . Version20 ) ;
}
_publicKeyToken = LoadPublicKeyTokenFromFile ( publicKeyTokenFile ) ;
_publicKeyTokenLoaded = true ;
return _publicKeyToken ;
}
}
}
internal virtual bool GenerateOverrides { get { return true ; } }
internal virtual string TypeName {
get {
return _factoryTypeName ;
}
}
internal void AddFile ( string filePath ) {
if ( _browserFileList = = null )
_browserFileList = new ArrayList ( ) ;
_browserFileList . Add ( filePath ) ;
}
internal void AddCustomFile ( string filePath ) {
if ( _customBrowserFileLists = = null ) {
_customBrowserFileLists = new ArrayList ( ) ;
}
_customBrowserFileLists . Add ( filePath ) ;
}
//parse the config info and create BrowserTree
//then generate code for, compile, and gac the object
[SecurityPermission(SecurityAction.Demand, Unrestricted=true)]
public virtual void Create ( ) {
DirectoryInfo browserDirInfo = new DirectoryInfo ( _browsersDirectory ) ;
//get all the browser files and put them in the "tree"
FileInfo [ ] browserFiles = browserDirInfo . GetFiles ( "*.browser" ) ;
if ( browserFiles = = null | | browserFiles . Length = = 0 ) {
return ;
}
foreach ( FileInfo browserFile in browserFiles ) {
AddFile ( browserFile . FullName ) ;
}
// First parse the browser files.
ProcessBrowserFiles ( ) ;
// Then parse custom browser files.
ProcessCustomBrowserFiles ( ) ;
// Uninstall previously installed generated assembly.
Uninstall ( ) ;
//generate the source code, compile it, and gac it
GenerateAssembly ( ) ;
// Restart w3svc service
RestartW3SVCIfNecessary ( ) ;
}
internal bool UninstallInternal ( ) {
// Remove existing strong name public token file
if ( File . Exists ( _publicKeyTokenFile ) ) {
File . Delete ( _publicKeyTokenFile ) ;
}
// Removing existing copy from GAC
GacUtil gacutil = new GacUtil ( ) ;
bool assemblyRemoved = gacutil . GacUnInstall ( "ASP.BrowserCapsFactory, Version=" + ThisAssembly . Version + ", Culture=neutral" ) ;
if ( ! assemblyRemoved ) {
return false ;
}
return true ;
}
[SecurityPermission(SecurityAction.Demand, Unrestricted = true)]
public bool Uninstall ( ) {
// Restart w3svc service
RestartW3SVCIfNecessary ( ) ;
if ( ! UninstallInternal ( ) ) {
return false ;
}
// Restart w3svc service again so applications get a fresh copy.
RestartW3SVCIfNecessary ( ) ;
return true ;
}
private void RestartW3SVCIfNecessary ( ) {
#if ! FEATURE_PAL
try {
// Dev10 bug 734918
// We should not fail when the w3svc service is not installed.
ServiceController [ ] services = ServiceController . GetServices ( ) ;
ServiceController controller = services . SingleOrDefault ( s = > String . Equals ( s . ServiceName , "W3SVC" , StringComparison . OrdinalIgnoreCase ) ) ;
if ( controller = = null ) {
return ;
}
ServiceControllerStatus status = controller . Status ;
// Stop the service if it's not currently stopped or pending.
if ( ! status . Equals ( ServiceControllerStatus . Stopped ) & &
! status . Equals ( ServiceControllerStatus . StopPending ) & &
! status . Equals ( ServiceControllerStatus . StartPending ) ) {
controller . Stop ( ) ;
// Give it 5 minutes to stop
controller . WaitForStatus ( ServiceControllerStatus . Stopped , new TimeSpan ( 0 , 5 , 0 ) ) ;
controller . Start ( ) ;
// If the service was paused, pause it.
if ( status . Equals ( ServiceControllerStatus . Paused ) | | status . Equals ( ServiceControllerStatus . PausePending ) ) {
controller . Pause ( ) ;
}
}
}
catch ( Exception ex ) {
throw new InvalidOperationException ( SR . GetString ( SR . Browser_W3SVC_Failure_Helper_Text , ex ) ) ;
}
#endif // !FEATURE_PAL
}
internal void ProcessBrowserFiles ( ) {
ProcessBrowserFiles ( false , String . Empty ) ;
}
private string NoPathFileName ( string fullPath ) {
int lastSlash = fullPath . LastIndexOf ( "\\" , StringComparison . Ordinal ) ;
if ( lastSlash > - 1 ) {
return fullPath . Substring ( lastSlash + 1 ) ;
}
return fullPath ;
}
internal virtual void ProcessBrowserNode ( XmlNode node , BrowserTree browserTree ) {
BrowserDefinition browserInfo = null ;
if ( node . Name = = "gateway" ) {
browserInfo = new GatewayDefinition ( node ) ;
}
else if ( node . Name = = "browser" ) {
browserInfo = new BrowserDefinition ( node ) ;
}
else {
Debug . Assert ( node . Name = = "defaultBrowser" ) ;
browserInfo = new BrowserDefinition ( node , true ) ;
}
BrowserDefinition oldNode = ( BrowserDefinition ) browserTree [ browserInfo . Name ] ;
if ( oldNode ! = null ) {
if ( browserInfo . IsRefID ) {
oldNode . MergeWithDefinition ( browserInfo ) ;
}
else {
throw new ConfigurationErrorsException ( SR . GetString ( SR . Duplicate_browser_id , browserInfo . ID ) , node ) ;
}
}
else {
browserTree [ browserInfo . Name ] = browserInfo ;
}
}
private void NormalizeAndValidateTree ( BrowserTree browserTree , bool isDefaultBrowser ) {
NormalizeAndValidateTree ( browserTree , isDefaultBrowser , false ) ;
}
private void NormalizeAndValidateTree ( BrowserTree browserTree , bool isDefaultBrowser , bool isCustomBrowser ) {
//normalize the tree
foreach ( DictionaryEntry entry in browserTree ) {
BrowserDefinition bd = ( BrowserDefinition ) entry . Value ;
string parentName = bd . ParentName ;
BrowserDefinition parentBrowser = null ;
if ( IsRootNode ( bd . Name ) ) {
continue ;
}
if ( parentName . Length > 0 ) {
parentBrowser = ( BrowserDefinition ) browserTree [ parentName ] ;
}
if ( parentBrowser ! = null ) {
if ( bd . IsRefID ) {
if ( bd is GatewayDefinition ) {
parentBrowser . RefGateways . Add ( bd ) ;
}
else {
parentBrowser . RefBrowsers . Add ( bd ) ;
}
}
else if ( bd is GatewayDefinition ) {
parentBrowser . Gateways . Add ( bd ) ;
}
else {
parentBrowser . Browsers . Add ( bd ) ;
}
}
else {
if ( isCustomBrowser ) {
throw new ConfigurationErrorsException ( SR . GetString ( SR . Browser_parentID_Not_Found , bd . ParentID ) , bd . XmlNode ) ;
}
else {
HandleUnRecognizedParentElement ( bd , isDefaultBrowser ) ;
}
}
}
//validate the tree
//loop check
foreach ( DictionaryEntry entry in browserTree ) {
BrowserDefinition bd = ( BrowserDefinition ) entry . Value ;
Hashtable loopCheck = new Hashtable ( ) ;
BrowserDefinition currentBrowser = bd ;
string currentId = currentBrowser . Name ;
while ( ! IsRootNode ( currentId ) ) {
if ( loopCheck [ currentId ] ! = null ) {
throw new ConfigurationErrorsException ( SR . GetString ( SR . Browser_Circular_Reference , currentId ) , currentBrowser . XmlNode ) ;
}
loopCheck [ currentId ] = currentId ;
currentBrowser = ( BrowserDefinition ) browserTree [ currentBrowser . ParentName ] ;
//in app-level, parent can exist in machine level
if ( currentBrowser = = null ) {
break ;
}
currentId = currentBrowser . Name ;
}
}
}
private void SetCustomTreeRoots ( BrowserTree browserTree , int index ) {
foreach ( DictionaryEntry entry in browserTree ) {
BrowserDefinition bd = ( BrowserDefinition ) entry . Value ;
if ( bd . ParentName = = null ) {
_customTreeNames [ index ] = bd . Name ;
break ;
}
}
}
// Now that we support adding custom browser hierarchies, root nodes other than Default are permitted.
private bool IsRootNode ( string nodeName ) {
if ( String . Compare ( nodeName , "Default" , StringComparison . OrdinalIgnoreCase ) = = 0 )
return true ;
foreach ( string treeRootName in _customTreeNames ) {
if ( String . Compare ( nodeName , treeRootName , StringComparison . OrdinalIgnoreCase ) = = 0 ) {
return true ;
}
}
return false ;
}
2016-11-10 13:04:39 +00:00
[SuppressMessage("Microsoft.Security.Xml", "CA3056:UseXmlReaderForLoad", Justification = "Developer-controlled .xml files in application directory are implicitly trusted by ASP.Net.")]
2016-08-03 10:59:49 +00:00
protected void ProcessBrowserFiles ( bool useVirtualPath , string virtualDir ) {
_browserTree = new BrowserTree ( ) ;
_defaultTree = new BrowserTree ( ) ;
_customTreeNames = new ArrayList ( ) ;
if ( _browserFileList = = null ) {
_browserFileList = new ArrayList ( ) ;
}
_browserFileList . Sort ( ) ;
//#if OPTIMIZE_FOR_DESKTOP_BROWSER
string mozillaFile = null ;
string ieFile = null ;
string operaFile = null ;
// DevDivBugs 180962
// IE, Mozilla and Opera are first-class browsers. Their User-Agent profiles need to be compared to the UA profile
// of the HTTP request before other browsers. We put them to the head of the list so that the generated browser capabilities
// code will try to match them before other browsers.
foreach ( String filePath in _browserFileList ) {
if ( filePath . EndsWith ( "ie.browser" , StringComparison . OrdinalIgnoreCase ) ) {
ieFile = filePath ;
}
else if ( filePath . EndsWith ( "mozilla.browser" , StringComparison . OrdinalIgnoreCase ) ) {
mozillaFile = filePath ;
}
else if ( filePath . EndsWith ( "opera.browser" , StringComparison . OrdinalIgnoreCase ) ) {
operaFile = filePath ;
break ;
}
}
if ( ieFile ! = null ) {
_browserFileList . Remove ( ieFile ) ;
_browserFileList . Insert ( 0 , ieFile ) ;
}
if ( mozillaFile ! = null ) {
_browserFileList . Remove ( mozillaFile ) ;
_browserFileList . Insert ( 1 , mozillaFile ) ;
}
if ( operaFile ! = null ) {
_browserFileList . Remove ( operaFile ) ;
_browserFileList . Insert ( 2 , operaFile ) ;
}
//#endif
foreach ( string fileName in _browserFileList ) {
XmlDocument doc = new ConfigXmlDocument ( ) ;
try {
doc . Load ( fileName ) ;
XmlNode rootNode = doc . DocumentElement ;
if ( rootNode . Name ! = "browsers" ) {
if ( useVirtualPath ) {
throw new HttpParseException ( SR . GetString ( SR . Invalid_browser_root ) , null /*innerException*/ , virtualDir + "/" + NoPathFileName ( fileName ) , null /*sourceCode*/ , 1 ) ;
}
else {
throw new HttpParseException ( SR . GetString ( SR . Invalid_browser_root ) , null /*innerException*/ , fileName , null /*sourceCode*/ , 1 ) ;
}
}
foreach ( XmlNode node in rootNode . ChildNodes ) {
if ( node . NodeType ! = XmlNodeType . Element )
continue ;
if ( node . Name = = "browser" | | node . Name = = "gateway" ) {
ProcessBrowserNode ( node , _browserTree ) ;
}
else if ( node . Name = = "defaultBrowser" ) {
ProcessBrowserNode ( node , _defaultTree ) ;
}
else {
HandlerBase . ThrowUnrecognizedElement ( node ) ;
}
}
}
catch ( XmlException e ) {
if ( useVirtualPath ) {
throw new HttpParseException ( e . Message , null /*innerException*/ , virtualDir + "/" + NoPathFileName ( fileName ) , null /*sourceCode*/ , e . LineNumber ) ;
}
else {
throw new HttpParseException ( e . Message , null /*innerException*/ , fileName , null /*sourceCode*/ , e . LineNumber ) ;
}
}
catch ( XmlSchemaException e ) {
if ( useVirtualPath ) {
throw new HttpParseException ( e . Message , null /*innerException*/ , virtualDir + "/" + NoPathFileName ( fileName ) , null /*sourceCode*/ , e . LineNumber ) ;
}
else {
throw new HttpParseException ( e . Message , null /*innerException*/ , fileName , null /*sourceCode*/ , e . LineNumber ) ;
}
}
}
NormalizeAndValidateTree ( _browserTree , false ) ;
NormalizeAndValidateTree ( _defaultTree , true ) ;
BrowserDefinition defaultBrowser = ( BrowserDefinition ) _browserTree [ "Default" ] ;
if ( defaultBrowser ! = null ) {
AddBrowserToCollectionRecursive ( defaultBrowser , 0 ) ;
}
}
internal void ProcessCustomBrowserFiles ( ) {
ProcessCustomBrowserFiles ( false , String . Empty ) ;
}
2016-11-10 13:04:39 +00:00
[SuppressMessage("Microsoft.Security.Xml", "CA3056:UseXmlReaderForLoad", Justification = "Developer-controlled .xml files in application directory are implicitly trusted by ASP.Net.")]
2016-08-03 10:59:49 +00:00
internal void ProcessCustomBrowserFiles ( bool useVirtualPath , string virtualDir ) {
//get all custom browser files and put them in the "tree"
DirectoryInfo browserDirInfo = null ;
DirectoryInfo [ ] browserSubDirectories = null ;
DirectoryInfo [ ] allBrowserSubDirectories = null ;
ArrayList customBrowserFileNames ;
_customTreeList = new ArrayList ( ) ;
_customBrowserFileLists = new ArrayList ( ) ;
_customBrowserDefinitionCollections = new ArrayList ( ) ;
/* Machine Level Custom Browsers */
if ( useVirtualPath = = false ) {
browserDirInfo = new DirectoryInfo ( _browsersDirectory ) ;
}
/* Application Level Custom Browsers */
else {
browserDirInfo = new DirectoryInfo ( HostingEnvironment . MapPathInternal ( virtualDir ) ) ;
}
allBrowserSubDirectories = browserDirInfo . GetDirectories ( ) ;
int j = 0 ;
int length = allBrowserSubDirectories . Length ;
browserSubDirectories = new DirectoryInfo [ length ] ;
for ( int i = 0 ; i < length ; i + + ) {
if ( ( allBrowserSubDirectories [ i ] . Attributes & FileAttributes . Hidden ) ! = FileAttributes . Hidden ) {
browserSubDirectories [ j ] = allBrowserSubDirectories [ i ] ;
j + + ;
}
}
Array . Resize ( ref browserSubDirectories , j ) ;
for ( int i = 0 ; i < browserSubDirectories . Length ; i + + ) {
/* Recursively Into Subdirectories */
FileInfo [ ] browserFiles = GetFilesNotHidden ( browserSubDirectories [ i ] , browserDirInfo ) ;
if ( browserFiles = = null | | browserFiles . Length = = 0 ) {
continue ;
}
BrowserTree customTree = new BrowserTree ( ) ;
_customTreeList . Add ( customTree ) ;
_customTreeNames . Add ( browserSubDirectories [ i ] . Name ) ;
customBrowserFileNames = new ArrayList ( ) ;
foreach ( FileInfo browserFile in browserFiles ) {
customBrowserFileNames . Add ( browserFile . FullName ) ;
}
_customBrowserFileLists . Add ( customBrowserFileNames ) ;
}
for ( int i = 0 ; i < _customBrowserFileLists . Count ; i + + ) {
ArrayList fileNames = ( ArrayList ) _customBrowserFileLists [ i ] ;
foreach ( string fileName in fileNames ) {
XmlDocument doc = new ConfigXmlDocument ( ) ;
try {
doc . Load ( fileName ) ;
XmlNode rootNode = doc . DocumentElement ;
if ( rootNode . Name ! = "browsers" ) {
if ( useVirtualPath ) {
throw new HttpParseException ( SR . GetString ( SR . Invalid_browser_root ) , null /*innerException*/ , virtualDir + "/" + NoPathFileName ( fileName ) , null /*sourceCode*/ , 1 ) ;
}
else {
throw new HttpParseException ( SR . GetString ( SR . Invalid_browser_root ) , null /*innerException*/ , fileName , null /*sourceCode*/ , 1 ) ;
}
}
foreach ( XmlNode node in rootNode . ChildNodes ) {
if ( node . NodeType ! = XmlNodeType . Element ) {
continue ;
}
if ( node . Name = = "browser" | | node . Name = = "gateway" ) {
ProcessBrowserNode ( node , ( BrowserTree ) _customTreeList [ i ] ) ;
}
else {
HandlerBase . ThrowUnrecognizedElement ( node ) ;
}
}
}
catch ( XmlException e ) {
if ( useVirtualPath ) {
throw new HttpParseException ( e . Message , null /*innerException*/ , virtualDir + "/" + NoPathFileName ( fileName ) , null /*sourceCode*/ , e . LineNumber ) ;
}
else {
throw new HttpParseException ( e . Message , null /*innerException*/ , fileName , null /*sourceCode*/ , e . LineNumber ) ;
}
}
catch ( XmlSchemaException e ) {
if ( useVirtualPath ) {
throw new HttpParseException ( e . Message , null /*innerException*/ , virtualDir + "/" + NoPathFileName ( fileName ) , null /*sourceCode*/ , e . LineNumber ) ;
}
else {
throw new HttpParseException ( e . Message , null /*innerException*/ , fileName , null /*sourceCode*/ , e . LineNumber ) ;
}
}
}
SetCustomTreeRoots ( ( BrowserTree ) _customTreeList [ i ] , i ) ;
NormalizeAndValidateTree ( ( BrowserTree ) _customTreeList [ i ] , false , true ) ;
_customBrowserDefinitionCollections . Add ( new BrowserDefinitionCollection ( ) ) ;
AddCustomBrowserToCollectionRecursive ( ( BrowserDefinition ) ( ( ( BrowserTree ) _customTreeList [ i ] ) [ _customTreeNames [ i ] ] ) , 0 , i ) ;
}
}
internal void AddCustomBrowserToCollectionRecursive ( BrowserDefinition bd , int depth , int index ) {
if ( _customBrowserDefinitionCollections [ index ] = = null ) {
_customBrowserDefinitionCollections [ index ] = new BrowserDefinitionCollection ( ) ;
}
bd . Depth = depth ;
bd . IsDeviceNode = true ;
( ( BrowserDefinitionCollection ) _customBrowserDefinitionCollections [ index ] ) . Add ( bd ) ;
foreach ( BrowserDefinition childBrowser in bd . Browsers ) {
AddCustomBrowserToCollectionRecursive ( childBrowser , depth + 1 , index ) ;
}
}
internal void AddBrowserToCollectionRecursive ( BrowserDefinition bd , int depth ) {
if ( _browserDefinitionCollection = = null ) {
_browserDefinitionCollection = new BrowserDefinitionCollection ( ) ;
}
bd . Depth = depth ;
bd . IsDeviceNode = true ;
_browserDefinitionCollection . Add ( bd ) ;
foreach ( BrowserDefinition childBrowser in bd . Browsers ) {
AddBrowserToCollectionRecursive ( childBrowser , depth + 1 ) ;
}
}
internal virtual void HandleUnRecognizedParentElement ( BrowserDefinition bd , bool isDefault ) {
throw new ConfigurationErrorsException ( SR . GetString ( SR . Browser_parentID_Not_Found , bd . ParentID ) , bd . XmlNode ) ;
}
private static FileInfo [ ] GetFilesNotHidden ( DirectoryInfo rootDirectory , DirectoryInfo browserDirInfo ) {
ArrayList fileList = new ArrayList ( ) ;
FileInfo [ ] files ;
DirectoryInfo [ ] subDirectories = rootDirectory . GetDirectories ( "*" , SearchOption . AllDirectories ) ;
files = rootDirectory . GetFiles ( "*.browser" , SearchOption . TopDirectoryOnly ) ;
fileList . AddRange ( files ) ;
for ( int i = 0 ; i < subDirectories . Length ; i + + ) {
if ( ( HasHiddenParent ( subDirectories [ i ] , browserDirInfo ) = = false ) ) {
files = subDirectories [ i ] . GetFiles ( "*.browser" , SearchOption . TopDirectoryOnly ) ;
fileList . AddRange ( files ) ;
}
}
return ( ( FileInfo [ ] ) fileList . ToArray ( typeof ( FileInfo ) ) ) ;
}
private static bool HasHiddenParent ( DirectoryInfo directory , DirectoryInfo browserDirInfo ) {
while ( ! String . Equals ( directory . Parent . Name , browserDirInfo . Name ) ) {
if ( ( directory . Attributes & FileAttributes . Hidden ) = = FileAttributes . Hidden ) {
return true ;
}
directory = directory . Parent ;
}
return false ;
}
//generate the code from the parsed BrowserDefinitionTree
//compile it, and install it in the gac
private void GenerateAssembly ( ) {
Debug . Assert ( _browserTree ! = null ) ;
BrowserDefinition root = ( BrowserDefinition ) _browserTree [ "Default" ] ;
BrowserDefinition defaultRoot = ( BrowserDefinition ) _defaultTree [ "Default" ] ;
ArrayList customTreeRoots = new ArrayList ( ) ;
for ( int i = 0 ; i < _customTreeNames . Count ; i + + ) {
customTreeRoots . Add ( ( BrowserDefinition ) ( ( ( BrowserTree ) _customTreeList [ i ] ) [ _customTreeNames [ i ] ] ) ) ;
}
//create a CodeCompileUnit
//add a CodeNamespace object to the CodeCompileUnit
//add a CodeTypeDeclaration to the CodeNamespace
//add all the members of the type/class to the CodeTypeDeclaration
//use a CodeGenerator to generate code from the CodeCompileUnit
//a CodeDomProvider can provide a CodeGenerator
//translate the BrowserDefinition tree to code
CSharpCodeProvider cscp = new CSharpCodeProvider ( ) ;
// namespace System.Web.BrowserCapsFactory
CodeCompileUnit ccu = new CodeCompileUnit ( ) ;
//add strong-name key pair for
CodeAttributeDeclaration declaration = new CodeAttributeDeclaration (
"System.Reflection.AssemblyKeyFile" ,
new CodeAttributeArgument [ ] {
new CodeAttributeArgument ( new CodePrimitiveExpression ( _strongNameKeyFileName ) ) } ) ;
CodeAttributeDeclaration aptca = new CodeAttributeDeclaration (
"System.Security.AllowPartiallyTrustedCallers" ) ;
ccu . AssemblyCustomAttributes . Add ( aptca ) ;
ccu . AssemblyCustomAttributes . Add ( declaration ) ;
//add version number for it so it can distinguished in future versions
declaration = new CodeAttributeDeclaration (
"System.Reflection.AssemblyVersion" ,
new CodeAttributeArgument [ ] {
new CodeAttributeArgument ( new CodePrimitiveExpression ( ThisAssembly . Version ) ) } ) ;
ccu . AssemblyCustomAttributes . Add ( declaration ) ;
CodeNamespace cnamespace = new CodeNamespace ( "ASP" ) ;
//GEN: using System;
cnamespace . Imports . Add ( new CodeNamespaceImport ( "System" ) ) ;
//GEN: using System.Web;
cnamespace . Imports . Add ( new CodeNamespaceImport ( "System.Web" ) ) ;
//GEN: using System.Web.Configuration;
cnamespace . Imports . Add ( new CodeNamespaceImport ( "System.Web.Configuration" ) ) ;
//GEN: using System.Reflection;
cnamespace . Imports . Add ( new CodeNamespaceImport ( "System.Reflection" ) ) ;
//GEN: class BrowserCapabilitiesFactory
ccu . Namespaces . Add ( cnamespace ) ;
CodeTypeDeclaration factoryType = new CodeTypeDeclaration ( "BrowserCapabilitiesFactory" ) ;
factoryType . Attributes = MemberAttributes . Private ;
factoryType . IsClass = true ;
factoryType . Name = TypeName ;
factoryType . BaseTypes . Add ( new CodeTypeReference ( "System.Web.Configuration.BrowserCapabilitiesFactoryBase" ) ) ;
cnamespace . Types . Add ( factoryType ) ;
//GEN: protected override object ConfigureBrowserCapabilities(NameValueCollection headers, HttpBrowserCapabilities browserCaps)
CodeMemberMethod method = new CodeMemberMethod ( ) ;
method . Attributes = MemberAttributes . Override | MemberAttributes . Public ;
method . ReturnType = new CodeTypeReference ( typeof ( void ) ) ;
method . Name = "ConfigureBrowserCapabilities" ;
CodeParameterDeclarationExpression cpde = new CodeParameterDeclarationExpression ( typeof ( NameValueCollection ) , _headersRefName ) ;
method . Parameters . Add ( cpde ) ;
cpde = new CodeParameterDeclarationExpression ( typeof ( HttpBrowserCapabilities ) , browserCapsVariable ) ;
method . Parameters . Add ( cpde ) ;
factoryType . Members . Add ( method ) ;
GenerateSingleProcessCall ( root , method ) ;
for ( int i = 0 ; i < customTreeRoots . Count ; i + + ) {
GenerateSingleProcessCall ( ( BrowserDefinition ) customTreeRoots [ i ] , method ) ;
}
//GEN: if(this.IsBrowserUnknown(browserCaps) == false) return;
CodeConditionStatement istatement = new CodeConditionStatement ( ) ;
CodeMethodInvokeExpression cmie = new CodeMethodInvokeExpression ( new CodeThisReferenceExpression ( ) , "IsBrowserUnknown" ) ;
cmie . Parameters . Add ( _browserCapsRefExpr ) ;
istatement . Condition = new CodeBinaryOperatorExpression ( cmie , CodeBinaryOperatorType . ValueEquality , new CodePrimitiveExpression ( false ) ) ;
istatement . TrueStatements . Add ( new CodeMethodReturnStatement ( ) ) ;
method . Statements . Add ( istatement ) ;
if ( defaultRoot ! = null ) {
GenerateSingleProcessCall ( defaultRoot , method , "Default" ) ;
}
for ( int i = 0 ; i < customTreeRoots . Count ; i + + ) {
foreach ( DictionaryEntry entry in ( BrowserTree ) _customTreeList [ i ] ) {
BrowserDefinition bd = entry . Value as BrowserDefinition ;
Debug . Assert ( bd ! = null ) ;
GenerateProcessMethod ( bd , factoryType ) ;
}
}
//GenerateCallsToProcessMethods(root, method);
foreach ( DictionaryEntry entry in _browserTree ) {
BrowserDefinition bd = entry . Value as BrowserDefinition ;
Debug . Assert ( bd ! = null ) ;
GenerateProcessMethod ( bd , factoryType ) ;
}
foreach ( DictionaryEntry entry in _defaultTree ) {
BrowserDefinition bd = entry . Value as BrowserDefinition ;
Debug . Assert ( bd ! = null ) ;
GenerateProcessMethod ( bd , factoryType , "Default" ) ;
}
GenerateOverrideMatchedHeaders ( factoryType ) ;
GenerateOverrideBrowserElements ( factoryType ) ;
//TODO: don't actually generate the code, just compile it in memory
TextWriter twriter = new StreamWriter ( new FileStream ( _browsersDirectory + "\\BrowserCapsFactory.cs" , FileMode . Create ) ) ;
try {
cscp . GenerateCodeFromCompileUnit ( ccu , twriter , null ) ;
}
finally {
if ( twriter ! = null )
twriter . Close ( ) ;
}
CompilationSection compConfig = MTConfigUtil . GetCompilationAppConfig ( ) ;
bool debug = compConfig . Debug ;
#if ! PLATFORM_UNIX // File system paths must account for UNIX
string strongNameFile = _browsersDirectory + "\\" + _strongNameKeyFileName ;
#else // !PLATFORM_UNIX
string strongNameFile = _browsersDirectory + "/" + _strongNameKeyFileName ;
#endif // !PLATFORM_UNIX
// Generate strong name file
StrongNameUtility . GenerateStrongNameFile ( strongNameFile ) ;
//TODO: do not use interim file: CompileAssemblyFromDom instead
string [ ] referencedAssemblies = new string [ 2 ] { "System.dll" , "System.Web.dll" } ;
CompilerParameters compilerParameters = new CompilerParameters ( referencedAssemblies , "ASP.BrowserCapsFactory" , debug /* includeDebugInformation */ ) ;
compilerParameters . GenerateInMemory = false ;
compilerParameters . OutputAssembly = _browsersDirectory + "\\ASP.BrowserCapsFactory.dll" ;
CompilerResults results = null ;
try {
results = cscp . CompileAssemblyFromFile ( compilerParameters , _browsersDirectory + "\\BrowserCapsFactory.cs" ) ;
}
finally {
if ( File . Exists ( strongNameFile ) ) {
File . Delete ( strongNameFile ) ;
}
}
if ( results . NativeCompilerReturnValue ! = 0 | | results . Errors . HasErrors ) {
foreach ( CompilerError error in results . Errors ) {
if ( ! error . IsWarning ) {
throw new HttpCompileException ( error . ErrorText ) ;
}
}
throw new HttpCompileException ( SR . GetString ( SR . Browser_compile_error ) ) ;
}
Assembly resultAssembly = results . CompiledAssembly ;
GacUtil gacutil = new GacUtil ( ) ;
gacutil . GacInstall ( resultAssembly . Location ) ;
SavePublicKeyTokenFile ( _publicKeyTokenFile , resultAssembly . GetName ( ) . GetPublicKeyToken ( ) ) ;
}
private void SavePublicKeyTokenFile ( string filename , byte [ ] publicKeyToken ) {
using ( FileStream pktStream = new FileStream ( filename , FileMode . Create , FileAccess . Write ) ) {
using ( StreamWriter pktWriter = new StreamWriter ( pktStream ) ) {
foreach ( byte b in publicKeyToken ) {
pktWriter . Write ( "{0:X2}" , b ) ;
}
}
}
}
private static string LoadPublicKeyTokenFromFile ( string filename ) {
IStackWalk fileReadAccess = InternalSecurityPermissions . FileReadAccess ( filename ) ;
Debug . Assert ( fileReadAccess ! = null ) ;
fileReadAccess . Assert ( ) ;
if ( ! File . Exists ( filename ) ) {
return null ;
}
try {
using ( FileStream pktStream = new FileStream ( filename , FileMode . Open , FileAccess . Read ) ) {
using ( StreamReader pktReader = new StreamReader ( pktStream ) ) {
return pktReader . ReadLine ( ) ;
}
}
}
catch ( IOException ) {
if ( HttpRuntime . HasFilePermission ( filename ) ) {
throw ;
}
// Don't throw exception if we don't have permission to the file.
return null ;
}
finally {
CodeAccessPermission . RevertAssert ( ) ;
}
}
internal void GenerateOverrideBrowserElements ( CodeTypeDeclaration typeDeclaration ) {
// Don't generate the property if there's nothing to override.
if ( _browserDefinitionCollection = = null ) {
return ;
}
// GEN:
// protected override void PopulateBrowserElements(IDictionary dictionary) {
// dictionary["Default"] = new Triplet(null, "default description", 0 /*depth_of_node */);
// dictionary["Up"] = new Triplet("Default", "up Description", 1 /*depth_of_node */);
// }
CodeMemberMethod method = new CodeMemberMethod ( ) ;
method . Name = _browserElementsMethodName ;
method . Attributes = MemberAttributes . Override | MemberAttributes . Family ;
method . ReturnType = new CodeTypeReference ( typeof ( void ) ) ;
CodeParameterDeclarationExpression parameter =
new CodeParameterDeclarationExpression ( new CodeTypeReference ( typeof ( IDictionary ) ) , _dictionaryRefName ) ;
method . Parameters . Add ( parameter ) ;
typeDeclaration . Members . Add ( method ) ;
CodeMethodReferenceExpression baseMethod = new CodeMethodReferenceExpression ( new CodeBaseReferenceExpression ( ) , _browserElementsMethodName ) ;
CodeMethodInvokeExpression baseInvoke = new CodeMethodInvokeExpression ( baseMethod , new CodeExpression [ ] { _dictionaryRefExpr } ) ;
method . Statements . Add ( baseInvoke ) ;
foreach ( BrowserDefinition bd in _browserDefinitionCollection ) {
if ( ! bd . IsDeviceNode )
continue ;
Debug . Assert ( ! ( bd is GatewayDefinition ) ) ;
CodeAssignStatement cas = new CodeAssignStatement ( ) ;
cas . Left = new CodeIndexerExpression ( _dictionaryRefExpr , new CodeExpression [ ] {
new CodePrimitiveExpression ( bd . ID )
} ) ;
cas . Right = new CodeObjectCreateExpression ( typeof ( Triplet ) ,
new CodeExpression [ ] {
new CodePrimitiveExpression ( bd . ParentName ) ,
new CodePropertyReferenceExpression ( new CodeTypeReferenceExpression ( typeof ( String ) ) , "Empty" ) ,
new CodePrimitiveExpression ( bd . Depth ) } ) ;
method . Statements . Add ( cas ) ;
}
for ( int i = 0 ; i < _customTreeNames . Count ; i + + ) {
foreach ( BrowserDefinition bd in ( BrowserDefinitionCollection ) _customBrowserDefinitionCollections [ i ] ) {
if ( ! bd . IsDeviceNode )
continue ;
Debug . Assert ( ! ( bd is GatewayDefinition ) ) ;
CodeAssignStatement cas = new CodeAssignStatement ( ) ;
cas . Left = new CodeIndexerExpression ( _dictionaryRefExpr , new CodeExpression [ ] {
new CodePrimitiveExpression ( bd . ID )
} ) ;
cas . Right = new CodeObjectCreateExpression ( typeof ( Triplet ) ,
new CodeExpression [ ] {
new CodePrimitiveExpression ( bd . ParentName ) ,
new CodePropertyReferenceExpression ( new CodeTypeReferenceExpression ( typeof ( String ) ) , "Empty" ) ,
new CodePrimitiveExpression ( bd . Depth ) } ) ;
method . Statements . Add ( cas ) ;
}
}
}
internal void GenerateOverrideMatchedHeaders ( CodeTypeDeclaration typeDeclaration ) {
// GEN:
// protected override void PopulateMatchedHeaders(IDictionary dictionary) {
// base.PopulateMatchedHeaders(dictionary);
//
// dictionary["header0"] = null;
// dictionary["header1"] = null;
// }
CodeMemberMethod method = new CodeMemberMethod ( ) ;
method . Name = _matchedHeadersMethodName ;
method . Attributes = MemberAttributes . Override | MemberAttributes . Family ;
method . ReturnType = new CodeTypeReference ( typeof ( void ) ) ;
CodeParameterDeclarationExpression parameter =
new CodeParameterDeclarationExpression ( new CodeTypeReference ( typeof ( IDictionary ) ) , _dictionaryRefName ) ;
method . Parameters . Add ( parameter ) ;
typeDeclaration . Members . Add ( method ) ;
CodeMethodReferenceExpression baseMethod = new CodeMethodReferenceExpression ( new CodeBaseReferenceExpression ( ) , _matchedHeadersMethodName ) ;
CodeMethodInvokeExpression baseInvoke = new CodeMethodInvokeExpression ( baseMethod , new CodeExpression [ ] { _dictionaryRefExpr } ) ;
method . Statements . Add ( baseInvoke ) ;
foreach ( String header in _headers ) {
CodeAssignStatement cas = new CodeAssignStatement ( ) ;
cas . Left = new CodeIndexerExpression ( _dictionaryRefExpr , new CodeExpression [ ] {
new CodePrimitiveExpression ( header )
} ) ;
cas . Right = new CodePrimitiveExpression ( null ) ;
method . Statements . Add ( cas ) ;
}
}
internal void GenerateProcessMethod ( BrowserDefinition bd , CodeTypeDeclaration ctd ) {
GenerateProcessMethod ( bd , ctd , String . Empty ) ;
}
//generate the xxxProcess method for an individual BrowserDefinition
internal void GenerateProcessMethod ( BrowserDefinition bd , CodeTypeDeclaration ctd , string prefix ) {
//GEN: internal bool XxxProcess(NameValueCollection headers, HttpBrowserCapabilities browserCaps)
CodeMemberMethod cmm = new CodeMemberMethod ( ) ;
cmm . Name = prefix + bd . Name + "Process" ;
cmm . ReturnType = new CodeTypeReference ( typeof ( bool ) ) ;
cmm . Attributes = MemberAttributes . Private ;
CodeParameterDeclarationExpression cpde = new CodeParameterDeclarationExpression ( typeof ( NameValueCollection ) , _headersRefName ) ;
cmm . Parameters . Add ( cpde ) ;
cpde = new CodeParameterDeclarationExpression ( typeof ( HttpBrowserCapabilities ) , browserCapsVariable ) ;
cmm . Parameters . Add ( cpde ) ;
bool regexWorkerGenerated = false ;
GenerateIdentificationCode ( bd , cmm , ref regexWorkerGenerated ) ;
GenerateCapturesCode ( bd , cmm , ref regexWorkerGenerated ) ;
GenerateSetCapabilitiesCode ( bd , cmm , ref regexWorkerGenerated ) ;
GenerateSetAdaptersCode ( bd , cmm ) ;
// Only add the browser node to the browser collection if it represents a device.
if ( bd . IsDeviceNode ) {
Debug . Assert ( ! ( bd is GatewayDefinition ) ) ;
//GEN: browserCaps.AddBrowser("xxx");
CodeMethodInvokeExpression cmie = new CodeMethodInvokeExpression ( new CodeVariableReferenceExpression ( browserCapsVariable ) , "AddBrowser" ) ;
cmie . Parameters . Add ( new CodePrimitiveExpression ( bd . ID ) ) ;
cmm . Statements . Add ( cmie ) ;
}
// Generate ref gateway elements
foreach ( BrowserDefinition b in bd . RefGateways ) {
AddComment ( "ref gateways, parent=" + bd . ID , cmm ) ;
GenerateSingleProcessCall ( b , cmm ) ;
}
if ( ( GenerateOverrides ) & & ( prefix . Length = = 0 ) ) {
//Gen: protected virtual void XxxProcessGateways(NameValueCollection headers, HttpBrowserCapabilities browserCaps) ;
string methodName = prefix + bd . Name + "ProcessGateways" ;
GenerateChildProcessMethod ( methodName , ctd , false ) ;
//Gen: XxxProcessGateways(headers, browserCaps) ;
GenerateChildProcessInvokeExpression ( methodName , cmm , false ) ;
}
foreach ( BrowserDefinition b in bd . Gateways ) {
AddComment ( "gateway, parent=" + bd . ID , cmm ) ;
GenerateSingleProcessCall ( b , cmm ) ;
}
if ( GenerateOverrides ) {
//GEN: bool ignoreApplicationBrowsers = true | false; //bd.Browsers.Count != 0
CodeVariableDeclarationStatement cvds = new CodeVariableDeclarationStatement ( typeof ( bool ) ,
IgnoreApplicationBrowserVariableName , new CodePrimitiveExpression ( bd . Browsers . Count ! = 0 ) ) ;
cmm . Statements . Add ( cvds ) ;
}
if ( bd . Browsers . Count > 0 ) {
CodeStatementCollection statements = cmm . Statements ;
AddComment ( "browser, parent=" + bd . ID , cmm ) ;
foreach ( BrowserDefinition b in bd . Browsers ) {
statements = GenerateTrackedSingleProcessCall ( statements , b , cmm , prefix ) ;
}
if ( GenerateOverrides ) {
//GEN: ignoreApplicationBrowsers = false;
CodeAssignStatement codeAssignStmt = new CodeAssignStatement ( ) ;
codeAssignStmt . Left = new CodeVariableReferenceExpression ( IgnoreApplicationBrowserVariableName ) ;
codeAssignStmt . Right = new CodePrimitiveExpression ( false ) ;
statements . Add ( codeAssignStmt ) ;
}
}
// Generate ref browser
foreach ( BrowserDefinition b in bd . RefBrowsers ) {
AddComment ( "ref browsers, parent=" + bd . ID , cmm ) ;
if ( b . IsDefaultBrowser ) {
GenerateSingleProcessCall ( b , cmm , "Default" ) ;
}
else {
GenerateSingleProcessCall ( b , cmm ) ;
}
}
if ( GenerateOverrides ) {
//Gen: protected virtual void XxxProcessBrowsers(bool ignoreApplicationBrowsers, NameValueCollection headers, HttpBrowserCapabilities browserCaps) ;
string methodName = prefix + bd . Name + "ProcessBrowsers" ;
GenerateChildProcessMethod ( methodName , ctd , true ) ;
//Gen: XxxProcessBrowsers(ignoreApplicationBrowsers, headers, browserCaps);
GenerateChildProcessInvokeExpression ( methodName , cmm , true ) ;
}
//GEN: return true;
CodeMethodReturnStatement cmrs = new CodeMethodReturnStatement ( new CodePrimitiveExpression ( true ) ) ;
cmm . Statements . Add ( cmrs ) ;
ctd . Members . Add ( cmm ) ;
}
private void GenerateChildProcessInvokeExpression ( string methodName , CodeMemberMethod cmm , bool generateTracker ) {
//Gen: XxxProcessBrowsers(ignoreApplicationBrowsers, headers, browserCaps) ;
CodeMethodInvokeExpression expr = new CodeMethodInvokeExpression ( new CodeThisReferenceExpression ( ) , methodName ) ;
if ( generateTracker ) {
expr . Parameters . Add ( new CodeVariableReferenceExpression ( IgnoreApplicationBrowserVariableName ) ) ;
}
expr . Parameters . Add ( new CodeVariableReferenceExpression ( _headersRefName ) ) ;
expr . Parameters . Add ( new CodeVariableReferenceExpression ( browserCapsVariable ) ) ;
cmm . Statements . Add ( expr ) ;
}
private void GenerateChildProcessMethod ( string methodName , CodeTypeDeclaration ctd , bool generateTracker ) {
//Gen: protected virtual void XxxProcessBrowsers(bool ignoreApplicationBrowsers, NameValueCollection headers, HttpBrowserCapabilities browserCaps) ;
CodeMemberMethod cmm = new CodeMemberMethod ( ) ;
cmm . Name = methodName ;
cmm . ReturnType = new CodeTypeReference ( typeof ( void ) ) ;
cmm . Attributes = MemberAttributes . Family ;
CodeParameterDeclarationExpression cpde = null ;
if ( generateTracker ) {
cpde = new CodeParameterDeclarationExpression ( typeof ( bool ) , IgnoreApplicationBrowserVariableName ) ;
cmm . Parameters . Add ( cpde ) ;
}
cpde = new CodeParameterDeclarationExpression ( typeof ( NameValueCollection ) , _headersRefName ) ;
cmm . Parameters . Add ( cpde ) ;
cpde = new CodeParameterDeclarationExpression ( typeof ( HttpBrowserCapabilities ) , browserCapsVariable ) ;
cmm . Parameters . Add ( cpde ) ;
ctd . Members . Add ( cmm ) ;
}
private void GenerateRegexWorkerIfNecessary ( CodeMemberMethod cmm , ref bool regexWorkerGenerated ) {
if ( regexWorkerGenerated ) {
return ;
}
regexWorkerGenerated = true ;
//GEN: RegexWorker regexWorker;
cmm . Statements . Add ( new CodeVariableDeclarationStatement ( "RegexWorker" , _regexWorkerRefName ) ) ;
//GEN: regexWorker = new RegexWorker(browserCaps);
cmm . Statements . Add ( new CodeAssignStatement ( _regexWorkerRefExpr , new CodeObjectCreateExpression ( "RegexWorker" , _browserCapsRefExpr ) ) ) ;
}
private void ReturnIfHeaderValueEmpty ( CodeMemberMethod cmm , CodeVariableReferenceExpression varExpr ) {
// GEN: if(String.IsNullOrEmpty(varExpr)) {
// GEN: return false;
// GEN: }
CodeConditionStatement emptyCheckStmt = new CodeConditionStatement ( ) ;
CodeMethodReferenceExpression emptyCheckMethod = new CodeMethodReferenceExpression ( new CodeTypeReferenceExpression ( typeof ( String ) ) , "IsNullOrEmpty" ) ;
CodeMethodInvokeExpression emptyCheckExpr = new CodeMethodInvokeExpression ( emptyCheckMethod , varExpr ) ;
emptyCheckStmt . Condition = emptyCheckExpr ;
emptyCheckStmt . TrueStatements . Add ( new CodeMethodReturnStatement ( new CodePrimitiveExpression ( false ) ) ) ;
cmm . Statements . Add ( emptyCheckStmt ) ;
}
//generate part of the xxxProcess method for handling determining if the requesting
//browser meets the regexes for this browser
private void GenerateIdentificationCode ( BrowserDefinition bd , CodeMemberMethod cmm , ref bool regexWorkerGenerated ) {
//GEN: IDictionary dictionary;
cmm . Statements . Add ( new CodeVariableDeclarationStatement ( typeof ( IDictionary ) , _dictionaryRefName ) ) ;
//GEN: dictionary = browserCaps.Capabilities;
CodeAssignStatement assign = new CodeAssignStatement (
_dictionaryRefExpr ,
new CodePropertyReferenceExpression ( _browserCapsRefExpr , "Capabilities" )
) ;
cmm . Statements . Add ( assign ) ;
bool disableOptimizedKey = false ;
CodeVariableReferenceExpression result = null ;
CodeVariableReferenceExpression headerValue = null ;
if ( bd . IdHeaderChecks . Count > 0 ) {
AddComment ( "Identification: check header matches" , cmm ) ;
for ( int i = 0 ; i < bd . IdHeaderChecks . Count ; i + + ) {
string matchedString = ( ( CheckPair ) bd . IdHeaderChecks [ i ] ) . MatchString ;
// Skip matching ".*"
if ( matchedString . Equals ( ".*" ) ) {
continue ;
}
if ( headerValue = = null ) {
headerValue = GenerateVarReference ( cmm , typeof ( string ) , "headerValue" ) ;
}
CodeAssignStatement valueAssignment = new CodeAssignStatement ( ) ;
cmm . Statements . Add ( valueAssignment ) ;
valueAssignment . Left = headerValue ;
if ( ( ( CheckPair ) bd . IdHeaderChecks [ i ] ) . Header . Equals ( "User-Agent" ) ) {
_headers . Add ( String . Empty ) ;
// GEN: headerValue = ((string)(browserCaps[String.Empty]));
valueAssignment . Right = new CodeCastExpression ( typeof ( string ) ,
new CodeIndexerExpression (
new CodeVariableReferenceExpression ( browserCapsVariable ) ,
new CodeExpression [ ] {
new CodePropertyReferenceExpression (
new CodeTypeReferenceExpression ( typeof ( String ) ) , "Empty" ) } ) ) ;
}
else {
string header = ( ( CheckPair ) bd . IdHeaderChecks [ i ] ) . Header ;
_headers . Add ( header ) ;
//GEN: headerValue = ((String)headers["xxx"]);
valueAssignment . Right = new CodeCastExpression ( typeof ( string ) ,
new CodeIndexerExpression (
_headersRefExpr ,
new CodeExpression [ ] { new CodePrimitiveExpression ( header ) }
)
) ;
disableOptimizedKey = true ;
}
// Don't need to use Regex if matching . only.
if ( matchedString . Equals ( "." ) ) {
// Simply return if the header exists.
ReturnIfHeaderValueEmpty ( cmm , headerValue ) ;
continue ;
}
if ( result = = null ) {
result = GenerateVarReference ( cmm , typeof ( bool ) , _resultVarName ) ;
}
GenerateRegexWorkerIfNecessary ( cmm , ref regexWorkerGenerated ) ;
CodeMethodInvokeExpression cmie = new CodeMethodInvokeExpression ( _regexWorkerRefExpr , _processRegexMethod ) ;
cmie . Parameters . Add ( headerValue ) ;
cmie . Parameters . Add ( new CodePrimitiveExpression ( matchedString ) ) ;
//GEN: result = regexWorker.ProcessRegex(headerValue, {matchedString});
assign = new CodeAssignStatement ( ) ;
assign . Left = result ;
assign . Right = cmie ;
cmm . Statements . Add ( assign ) ;
//GEN: if(result == false) {
//GEN: return false;
//GEN: }
CodeConditionStatement istatement = new CodeConditionStatement ( ) ;
if ( ( ( CheckPair ) bd . IdHeaderChecks [ i ] ) . NonMatch ) {
istatement . Condition = new CodeBinaryOperatorExpression ( result , CodeBinaryOperatorType . ValueEquality , new CodePrimitiveExpression ( true ) ) ;
}
else {
istatement . Condition = new CodeBinaryOperatorExpression ( result , CodeBinaryOperatorType . ValueEquality , new CodePrimitiveExpression ( false ) ) ;
}
istatement . TrueStatements . Add ( new CodeMethodReturnStatement ( new CodePrimitiveExpression ( false ) ) ) ;
cmm . Statements . Add ( istatement ) ;
}
}
if ( bd . IdCapabilityChecks . Count > 0 ) {
AddComment ( "Identification: check capability matches" , cmm ) ;
for ( int i = 0 ; i < bd . IdCapabilityChecks . Count ; i + + ) {
string matchedString = ( ( CheckPair ) bd . IdCapabilityChecks [ i ] ) . MatchString ;
// Skip matching ".*"
if ( matchedString . Equals ( ".*" ) ) {
continue ;
}
if ( headerValue = = null ) {
headerValue = GenerateVarReference ( cmm , typeof ( string ) , "headerValue" ) ;
}
CodeAssignStatement valueAssignment = new CodeAssignStatement ( ) ;
cmm . Statements . Add ( valueAssignment ) ;
valueAssignment . Left = headerValue ;
valueAssignment . Right = ( new CodeCastExpression ( typeof ( string ) ,
new CodeIndexerExpression (
_dictionaryRefExpr ,
new CodeExpression [ ] {
new CodePrimitiveExpression ( ( ( CheckPair ) bd . IdCapabilityChecks [ i ] ) . Header )
}
)
) ) ;
// Don't need to use Regex if matching . only.
if ( matchedString . Equals ( "." ) ) {
continue ;
}
if ( result = = null ) {
result = GenerateVarReference ( cmm , typeof ( bool ) , _resultVarName ) ;
}
GenerateRegexWorkerIfNecessary ( cmm , ref regexWorkerGenerated ) ;
//GEN: result = regexWorker.ProcessRegex((string)dictionary["xxxCapability"], "xxxRegexString");
CodeMethodInvokeExpression cmie = new CodeMethodInvokeExpression ( _regexWorkerRefExpr , _processRegexMethod ) ;
cmie . Parameters . Add ( headerValue ) ;
cmie . Parameters . Add ( new CodePrimitiveExpression ( matchedString ) ) ;
assign = new CodeAssignStatement ( ) ;
assign . Left = result ;
assign . Right = cmie ;
cmm . Statements . Add ( assign ) ;
//GEN: if(result == false) {
//GEN: return false;
//GEN: }
CodeConditionStatement istatement = new CodeConditionStatement ( ) ;
if ( ( ( CheckPair ) bd . IdCapabilityChecks [ i ] ) . NonMatch ) {
istatement . Condition = new CodeBinaryOperatorExpression ( result , CodeBinaryOperatorType . ValueEquality , new CodePrimitiveExpression ( true ) ) ;
}
else {
istatement . Condition = new CodeBinaryOperatorExpression ( result , CodeBinaryOperatorType . ValueEquality , new CodePrimitiveExpression ( false ) ) ;
}
istatement . TrueStatements . Add ( new CodeMethodReturnStatement ( new CodePrimitiveExpression ( false ) ) ) ;
cmm . Statements . Add ( istatement ) ;
}
}
//GEN: browserCaps.DisableOptimizedCacheKey();
if ( disableOptimizedKey ) {
CodeMethodInvokeExpression cme = new CodeMethodInvokeExpression ( _browserCapsRefExpr , _disableOptimizedCacheKeyMethodName ) ;
cmm . Statements . Add ( cme ) ;
}
}
private CodeVariableReferenceExpression GenerateVarReference ( CodeMemberMethod cmm , Type varType , string varName ) {
//GEN: {varType} {varName};
cmm . Statements . Add ( new CodeVariableDeclarationStatement ( varType , varName ) ) ;
return new CodeVariableReferenceExpression ( varName ) ;
}
//generate part of the xxxProcess method for running and storing the capture regexes
private void GenerateCapturesCode ( BrowserDefinition bd , CodeMemberMethod cmm , ref bool regexWorkerGenerated ) {
if ( ( bd . CaptureHeaderChecks . Count = = 0 ) & & ( bd . CaptureCapabilityChecks . Count = = 0 ) ) {
return ;
}
if ( bd . CaptureHeaderChecks . Count > 0 ) {
AddComment ( "Capture: header values" , cmm ) ;
for ( int i = 0 ; i < bd . CaptureHeaderChecks . Count ; i + + ) {
string matchedString = ( ( CheckPair ) bd . CaptureHeaderChecks [ i ] ) . MatchString ;
if ( matchedString . Equals ( ".*" ) ) {
continue ;
}
GenerateRegexWorkerIfNecessary ( cmm , ref regexWorkerGenerated ) ;
CodeMethodInvokeExpression cmie = new CodeMethodInvokeExpression ( _regexWorkerRefExpr , _processRegexMethod ) ;
if ( ( ( CheckPair ) bd . CaptureHeaderChecks [ i ] ) . Header . Equals ( "User-Agent" ) ) {
_headers . Add ( String . Empty ) ;
cmie . Parameters . Add ( new CodeCastExpression ( typeof ( string ) ,
new CodeIndexerExpression ( new CodeVariableReferenceExpression ( browserCapsVariable ) , new CodeExpression [ ] {
new CodePropertyReferenceExpression ( new CodeTypeReferenceExpression ( typeof ( String ) ) , "Empty" ) } ) ) ) ;
}
else {
string header = ( ( CheckPair ) bd . CaptureHeaderChecks [ i ] ) . Header ;
_headers . Add ( header ) ;
//GEN: regexWorker.ProcessRegex((string)headers["xxx"], "xxxRegexString");
cmie . Parameters . Add (
new CodeCastExpression ( typeof ( string ) ,
new CodeIndexerExpression (
_headersRefExpr ,
new CodeExpression [ ] { new CodePrimitiveExpression ( header ) }
)
)
) ;
}
cmie . Parameters . Add ( new CodePrimitiveExpression ( matchedString ) ) ;
cmm . Statements . Add ( cmie ) ;
}
}
if ( bd . CaptureCapabilityChecks . Count > 0 ) {
AddComment ( "Capture: capability values" , cmm ) ;
for ( int i = 0 ; i < bd . CaptureCapabilityChecks . Count ; i + + ) {
string matchedString = ( ( CheckPair ) bd . CaptureCapabilityChecks [ i ] ) . MatchString ;
if ( matchedString . Equals ( ".*" ) ) {
continue ;
}
GenerateRegexWorkerIfNecessary ( cmm , ref regexWorkerGenerated ) ;
//GEN: regexWorker.ProcessRegex((string)dictionary["xxxCapability"], "xxxRegexString");
CodeMethodInvokeExpression cmie = new CodeMethodInvokeExpression ( _regexWorkerRefExpr , _processRegexMethod ) ;
cmie . Parameters . Add (
new CodeCastExpression ( typeof ( string ) ,
new CodeIndexerExpression (
_dictionaryRefExpr ,
new CodeExpression [ ] { new CodePrimitiveExpression ( ( ( CheckPair ) bd . CaptureCapabilityChecks [ i ] ) . Header ) }
)
)
) ;
cmie . Parameters . Add ( new CodePrimitiveExpression ( matchedString ) ) ;
cmm . Statements . Add ( cmie ) ;
}
}
}
//generate part of the xxxProcess method for assigning capability values
private void GenerateSetCapabilitiesCode ( BrowserDefinition bd , CodeMemberMethod cmm , ref bool regexWorkerGenerated ) {
//GEN: browserCaps[aaa] = "bbb";
//GEN: browserCaps[xxx] = "yyy";
NameValueCollection nvc = bd . Capabilities ;
CodeAssignStatement assign ;
AddComment ( "Capabilities: set capabilities" , cmm ) ;
foreach ( string s in nvc . Keys ) {
string capsString = nvc [ s ] ;
//GEN: dictionary["xxx"] = regexWorker["xxx"];
assign = new CodeAssignStatement ( ) ;
assign . Left = new CodeIndexerExpression (
_dictionaryRefExpr ,
new CodeExpression [ ] { new CodePrimitiveExpression ( s ) } ) ;
CodePrimitiveExpression capabilityExpr = new CodePrimitiveExpression ( capsString ) ;
if ( RegexWorker . RefPat . Match ( capsString ) . Success ) {
GenerateRegexWorkerIfNecessary ( cmm , ref regexWorkerGenerated ) ;
assign . Right = new CodeIndexerExpression (
_regexWorkerRefExpr ,
new CodeExpression [ ] { capabilityExpr } ) ;
}
else {
assign . Right = capabilityExpr ;
}
cmm . Statements . Add ( assign ) ;
}
}
//generate part of the xxxProcess method for setting specific adapters for this browser
internal void GenerateSetAdaptersCode ( BrowserDefinition bd , CodeMemberMethod cmm ) {
//GEN: browserCaps.Adapters[xxxControl] = yyyAdapter;
foreach ( DictionaryEntry entry in bd . Adapters ) {
string controlString = ( string ) entry . Key ;
string adapterString = ( string ) entry . Value ;
CodePropertyReferenceExpression cpre = new CodePropertyReferenceExpression ( _browserCapsRefExpr , "Adapters" ) ;
CodeIndexerExpression indexerExpression = new CodeIndexerExpression (
cpre ,
new CodeExpression [ ] { new CodePrimitiveExpression ( controlString ) }
) ;
CodeAssignStatement assignAdapter = new CodeAssignStatement ( ) ;
assignAdapter . Left = indexerExpression ;
assignAdapter . Right = new CodePrimitiveExpression ( adapterString ) ;
cmm . Statements . Add ( assignAdapter ) ;
}
//GEN: browser.HtmlTextWriter = xxxHtmlTextWriter;
if ( bd . HtmlTextWriterString ! = null ) {
CodeAssignStatement assignHtmlTextWriter = new CodeAssignStatement ( ) ;
assignHtmlTextWriter . Left = new CodePropertyReferenceExpression ( _browserCapsRefExpr , "HtmlTextWriter" ) ;
assignHtmlTextWriter . Right = new CodePrimitiveExpression ( bd . HtmlTextWriterString ) ;
cmm . Statements . Add ( assignHtmlTextWriter ) ;
}
return ;
}
internal void AddComment ( string comment , CodeMemberMethod cmm ) {
cmm . Statements . Add ( new CodeCommentStatement ( comment ) ) ;
}
internal CodeStatementCollection GenerateTrackedSingleProcessCall ( CodeStatementCollection stmts , BrowserDefinition bd , CodeMemberMethod cmm ) {
return GenerateTrackedSingleProcessCall ( stmts , bd , cmm , String . Empty ) ;
}
internal CodeStatementCollection GenerateTrackedSingleProcessCall ( CodeStatementCollection stmts , BrowserDefinition bd , CodeMemberMethod cmm , string prefix ) {
//GEN: if (xProcess(headers, browserCaps)) {
// }
// else {
// ...
// }
CodeMethodInvokeExpression xProcess = new CodeMethodInvokeExpression ( new CodeThisReferenceExpression ( ) , prefix + bd . Name + "Process" ) ;
xProcess . Parameters . Add ( new CodeVariableReferenceExpression ( _headersRefName ) ) ;
xProcess . Parameters . Add ( new CodeVariableReferenceExpression ( browserCapsVariable ) ) ;
CodeConditionStatement conditionStmt = new CodeConditionStatement ( ) ;
conditionStmt . Condition = xProcess ;
stmts . Add ( conditionStmt ) ;
return conditionStmt . FalseStatements ;
}
internal void GenerateSingleProcessCall ( BrowserDefinition bd , CodeMemberMethod cmm ) {
GenerateSingleProcessCall ( bd , cmm , String . Empty ) ;
}
//generate code to call the xxxProcess for a given browser
//and store the result in a local variable
internal void GenerateSingleProcessCall ( BrowserDefinition bd , CodeMemberMethod cmm , string prefix ) {
//GEN: xProcess(headers, browserCaps);
CodeMethodInvokeExpression xProcess = new CodeMethodInvokeExpression ( new CodeThisReferenceExpression ( ) , prefix + bd . Name + "Process" ) ;
xProcess . Parameters . Add ( new CodeVariableReferenceExpression ( _headersRefName ) ) ;
xProcess . Parameters . Add ( new CodeVariableReferenceExpression ( browserCapsVariable ) ) ;
cmm . Statements . Add ( xProcess ) ;
}
}
}