1 //------------------------------------------------------------------------------
2 // <copyright file="BrowserCapabilitiesCodeGenerator.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //------------------------------------------------------------------------------
7 namespace System.Web.Configuration {
10 using System.CodeDom.Compiler;
11 using System.Configuration;
12 using System.Collections;
13 using System.Collections.Specialized;
16 using System.ServiceProcess;
17 #endif // !FEATURE_PAL
19 using System.Reflection;
20 using System.Security;
21 using System.Security.Permissions;
23 using System.Text.RegularExpressions;
25 using System.Web.Compilation;
26 using System.Web.Configuration;
27 using System.Web.Hosting;
29 using System.Web.Util;
31 using System.Xml.Schema;
33 using Microsoft.Build.Utilities;
34 using Microsoft.CSharp;
35 using System.Diagnostics.CodeAnalysis;
37 [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
38 [PermissionSet(SecurityAction.InheritanceDemand, Unrestricted = true)]
39 public class BrowserCapabilitiesCodeGenerator {
40 private static readonly string _browsersDirectory;
41 private static readonly string _publicKeyTokenFile;
43 private static object _staticLock = new object();
45 private BrowserTree _browserTree;
46 private BrowserTree _defaultTree;
47 private BrowserDefinitionCollection _browserDefinitionCollection;
49 internal const string browserCapsVariable = "browserCaps";
50 internal const string IgnoreApplicationBrowserVariableName = "ignoreApplicationBrowsers";
51 private const string _factoryTypeName = "BrowserCapabilitiesFactory";
52 private const string _headerDictionaryVarName = "_headerDictionary";
53 private const string _disableOptimizedCacheKeyMethodName = "DisableOptimizedCacheKey";
54 private const string _matchedHeadersMethodName = "PopulateMatchedHeaders";
55 private const string _browserElementsMethodName = "PopulateBrowserElements";
56 private const string _dictionaryRefName = "dictionary";
57 private const string _regexWorkerRefName = "regexWorker";
58 private const string _headersRefName = "headers";
59 private const string _resultVarName = "result";
60 private const string _processRegexMethod = "ProcessRegex";
61 private static readonly string _strongNameKeyFileName = browserCapsVariable + ".snk";
62 private static readonly string _publicKeyTokenFileName = browserCapsVariable + ".token";
63 private static bool _publicKeyTokenLoaded;
64 private static string _publicKeyToken;
66 private CodeVariableReferenceExpression _dictionaryRefExpr = new CodeVariableReferenceExpression(_dictionaryRefName);
67 private CodeVariableReferenceExpression _regexWorkerRefExpr = new CodeVariableReferenceExpression(_regexWorkerRefName);
68 private CodeVariableReferenceExpression _headersRefExpr = new CodeVariableReferenceExpression(_headersRefName);
69 private CodeVariableReferenceExpression _browserCapsRefExpr = new CodeVariableReferenceExpression(browserCapsVariable);
71 private ArrayList _browserFileList;
73 private ArrayList _customBrowserFileLists;
74 private ArrayList _customTreeList;
75 private ArrayList _customTreeNames;
76 private ArrayList _customBrowserDefinitionCollections;
78 private CaseInsensitiveStringSet _headers;
80 static BrowserCapabilitiesCodeGenerator() {
81 #if !PLATFORM_UNIX // File system paths must account for UNIX
82 _browsersDirectory = HttpRuntime.ClrInstallDirectoryInternal + "\\config\\browsers";
83 _publicKeyTokenFile = _browsersDirectory + "\\" + _publicKeyTokenFileName;
84 #else // !PLATFORM_UNIX
85 _browsersDirectory = HttpRuntime.ClrInstallDirectoryInternal + "/config/browsers";
86 _publicKeyTokenFile = _browsersDirectory + "/" + _publicKeyTokenFileName;
88 #endif // !PLATFORM_UNIX
91 public BrowserCapabilitiesCodeGenerator() {
92 _headers = new CaseInsensitiveStringSet();
95 internal BrowserTree BrowserTree {
101 internal BrowserTree DefaultTree {
107 internal ArrayList CustomTreeList {
109 return _customTreeList;
113 internal ArrayList CustomTreeNames {
115 return _customTreeNames;
119 internal static string BrowserCapAssemblyPublicKeyToken {
121 if (_publicKeyTokenLoaded) {
122 return _publicKeyToken;
126 if (_publicKeyTokenLoaded) {
127 return _publicKeyToken;
130 string publicKeyTokenFile;
131 if (MultiTargetingUtil.IsTargetFramework40OrAbove) {
132 publicKeyTokenFile = _publicKeyTokenFile;
135 // If we are targeting pre-4.0, we should be using version 2.0 of the assembly
136 // ASP.BrowserCapsFactory, so we need to read the token file from the 2.0 path.
137 // (Dev10 bug 795509)
138 string subPath = @"config\browsers\" + _publicKeyTokenFileName;
139 publicKeyTokenFile = ToolLocationHelper.GetPathToDotNetFrameworkFile(subPath, TargetDotNetFrameworkVersion.Version20);
141 _publicKeyToken = LoadPublicKeyTokenFromFile(publicKeyTokenFile);
142 _publicKeyTokenLoaded = true;
144 return _publicKeyToken;
149 internal virtual bool GenerateOverrides { get { return true; } }
151 internal virtual string TypeName {
153 return _factoryTypeName;
157 internal void AddFile(string filePath) {
158 if (_browserFileList == null)
159 _browserFileList = new ArrayList();
161 _browserFileList.Add(filePath);
164 internal void AddCustomFile(string filePath) {
165 if (_customBrowserFileLists == null) {
166 _customBrowserFileLists = new ArrayList();
169 _customBrowserFileLists.Add(filePath);
172 //parse the config info and create BrowserTree
173 //then generate code for, compile, and gac the object
174 [SecurityPermission(SecurityAction.Demand, Unrestricted=true)]
175 public virtual void Create() {
176 DirectoryInfo browserDirInfo = new DirectoryInfo(_browsersDirectory);
177 //get all the browser files and put them in the "tree"
178 FileInfo[] browserFiles = browserDirInfo.GetFiles("*.browser");
180 if (browserFiles == null || browserFiles.Length == 0) {
184 foreach(FileInfo browserFile in browserFiles) {
185 AddFile(browserFile.FullName);
188 // First parse the browser files.
189 ProcessBrowserFiles();
191 // Then parse custom browser files.
192 ProcessCustomBrowserFiles();
194 // Uninstall previously installed generated assembly.
197 //generate the source code, compile it, and gac it
200 // Restart w3svc service
201 RestartW3SVCIfNecessary();
204 internal bool UninstallInternal() {
205 // Remove existing strong name public token file
206 if (File.Exists(_publicKeyTokenFile)) {
207 File.Delete(_publicKeyTokenFile);
210 // Removing existing copy from GAC
211 GacUtil gacutil = new GacUtil();
212 bool assemblyRemoved = gacutil.GacUnInstall("ASP.BrowserCapsFactory, Version=" + ThisAssembly.Version + ", Culture=neutral");
213 if (!assemblyRemoved) {
220 [SecurityPermission(SecurityAction.Demand, Unrestricted = true)]
221 public bool Uninstall() {
222 // Restart w3svc service
223 RestartW3SVCIfNecessary();
225 if (!UninstallInternal()) {
229 // Restart w3svc service again so applications get a fresh copy.
230 RestartW3SVCIfNecessary();
235 private void RestartW3SVCIfNecessary() {
239 // We should not fail when the w3svc service is not installed.
240 ServiceController[] services = ServiceController.GetServices();
241 ServiceController controller = services.SingleOrDefault(s => String.Equals(s.ServiceName, "W3SVC", StringComparison.OrdinalIgnoreCase));
242 if (controller == null) {
246 ServiceControllerStatus status = controller.Status;
248 // Stop the service if it's not currently stopped or pending.
249 if (!status.Equals(ServiceControllerStatus.Stopped) &&
250 !status.Equals(ServiceControllerStatus.StopPending) &&
251 !status.Equals(ServiceControllerStatus.StartPending)) {
254 // Give it 5 minutes to stop
255 controller.WaitForStatus(ServiceControllerStatus.Stopped, new TimeSpan(0, 5, 0));
258 // If the service was paused, pause it.
259 if (status.Equals(ServiceControllerStatus.Paused) || status.Equals(ServiceControllerStatus.PausePending)) {
264 catch (Exception ex) {
265 throw new InvalidOperationException(SR.GetString(SR.Browser_W3SVC_Failure_Helper_Text, ex));
267 #endif // !FEATURE_PAL
270 internal void ProcessBrowserFiles() {
271 ProcessBrowserFiles(false, String.Empty);
274 private string NoPathFileName(string fullPath) {
275 int lastSlash = fullPath.LastIndexOf("\\", StringComparison.Ordinal);
277 return fullPath.Substring(lastSlash + 1);
282 internal virtual void ProcessBrowserNode(XmlNode node, BrowserTree browserTree) {
284 BrowserDefinition browserInfo = null;
286 if (node.Name == "gateway") {
287 browserInfo = new GatewayDefinition(node);
289 else if (node.Name == "browser") {
290 browserInfo = new BrowserDefinition(node);
293 Debug.Assert(node.Name == "defaultBrowser");
294 browserInfo = new BrowserDefinition(node, true);
297 BrowserDefinition oldNode = (BrowserDefinition)browserTree[browserInfo.Name];
299 if (oldNode != null) {
300 if (browserInfo.IsRefID) {
301 oldNode.MergeWithDefinition(browserInfo);
304 throw new ConfigurationErrorsException(SR.GetString(SR.Duplicate_browser_id, browserInfo.ID), node);
308 browserTree[browserInfo.Name] = browserInfo;
312 private void NormalizeAndValidateTree(BrowserTree browserTree, bool isDefaultBrowser) {
313 NormalizeAndValidateTree(browserTree, isDefaultBrowser, false);
316 private void NormalizeAndValidateTree(BrowserTree browserTree, bool isDefaultBrowser, bool isCustomBrowser) {
318 foreach (DictionaryEntry entry in browserTree) {
319 BrowserDefinition bd = (BrowserDefinition)entry.Value;
320 string parentName = bd.ParentName;
321 BrowserDefinition parentBrowser = null;
323 if (IsRootNode(bd.Name)) {
327 if (parentName.Length > 0) {
328 parentBrowser = (BrowserDefinition)browserTree[parentName];
331 if (parentBrowser != null) {
333 if (bd is GatewayDefinition) {
334 parentBrowser.RefGateways.Add(bd);
337 parentBrowser.RefBrowsers.Add(bd);
340 else if (bd is GatewayDefinition) {
341 parentBrowser.Gateways.Add(bd);
344 parentBrowser.Browsers.Add(bd);
348 if (isCustomBrowser) {
349 throw new ConfigurationErrorsException(SR.GetString(SR.Browser_parentID_Not_Found, bd.ParentID), bd.XmlNode);
352 HandleUnRecognizedParentElement(bd, isDefaultBrowser);
359 foreach (DictionaryEntry entry in browserTree) {
360 BrowserDefinition bd = (BrowserDefinition)entry.Value;
361 Hashtable loopCheck = new Hashtable();
362 BrowserDefinition currentBrowser = bd;
363 string currentId = currentBrowser.Name;
364 while (!IsRootNode(currentId)) {
365 if (loopCheck[currentId] != null) {
366 throw new ConfigurationErrorsException(SR.GetString(SR.Browser_Circular_Reference, currentId), currentBrowser.XmlNode);
368 loopCheck[currentId] = currentId;
369 currentBrowser = (BrowserDefinition)browserTree[currentBrowser.ParentName];
370 //in app-level, parent can exist in machine level
371 if (currentBrowser == null) {
375 currentId = currentBrowser.Name;
380 private void SetCustomTreeRoots(BrowserTree browserTree, int index) {
381 foreach (DictionaryEntry entry in browserTree) {
382 BrowserDefinition bd = (BrowserDefinition)entry.Value;
383 if (bd.ParentName == null) {
384 _customTreeNames[index] = bd.Name;
390 // Now that we support adding custom browser hierarchies, root nodes other than Default are permitted.
391 private bool IsRootNode(string nodeName) {
392 if (String.Compare(nodeName, "Default", StringComparison.OrdinalIgnoreCase) == 0)
395 foreach (string treeRootName in _customTreeNames) {
396 if (String.Compare(nodeName, treeRootName, StringComparison.OrdinalIgnoreCase) == 0) {
404 [SuppressMessage("Microsoft.Security.Xml", "CA3056:UseXmlReaderForLoad", Justification = "Developer-controlled .xml files in application directory are implicitly trusted by ASP.Net.")]
405 protected void ProcessBrowserFiles(bool useVirtualPath, string virtualDir) {
406 _browserTree = new BrowserTree();
407 _defaultTree = new BrowserTree();
408 _customTreeNames = new ArrayList();
410 if (_browserFileList == null) {
411 _browserFileList = new ArrayList();
414 _browserFileList.Sort();
415 //#if OPTIMIZE_FOR_DESKTOP_BROWSER
416 string mozillaFile = null;
417 string ieFile = null;
418 string operaFile = null;
421 // IE, Mozilla and Opera are first-class browsers. Their User-Agent profiles need to be compared to the UA profile
422 // of the HTTP request before other browsers. We put them to the head of the list so that the generated browser capabilities
423 // code will try to match them before other browsers.
424 foreach (String filePath in _browserFileList) {
425 if (filePath.EndsWith("ie.browser", StringComparison.OrdinalIgnoreCase)) {
428 else if (filePath.EndsWith("mozilla.browser", StringComparison.OrdinalIgnoreCase)) {
429 mozillaFile = filePath;
431 else if (filePath.EndsWith("opera.browser", StringComparison.OrdinalIgnoreCase)) {
432 operaFile = filePath;
437 if (ieFile != null) {
438 _browserFileList.Remove(ieFile);
439 _browserFileList.Insert(0, ieFile);
442 if (mozillaFile != null) {
443 _browserFileList.Remove(mozillaFile);
444 _browserFileList.Insert(1, mozillaFile);
447 if (operaFile != null) {
448 _browserFileList.Remove(operaFile);
449 _browserFileList.Insert(2, operaFile);
452 foreach (string fileName in _browserFileList) {
453 XmlDocument doc = new ConfigXmlDocument();
457 XmlNode rootNode = doc.DocumentElement;
458 if(rootNode.Name != "browsers") {
460 throw new HttpParseException(SR.GetString(SR.Invalid_browser_root), null /*innerException*/, virtualDir + "/" + NoPathFileName(fileName), null /*sourceCode*/, 1);
463 throw new HttpParseException(SR.GetString(SR.Invalid_browser_root), null /*innerException*/, fileName, null /*sourceCode*/, 1);
467 foreach (XmlNode node in rootNode.ChildNodes) {
468 if (node.NodeType != XmlNodeType.Element)
470 if (node.Name == "browser" || node.Name == "gateway") {
471 ProcessBrowserNode(node, _browserTree);
473 else if (node.Name == "defaultBrowser") {
474 ProcessBrowserNode(node, _defaultTree);
477 HandlerBase.ThrowUnrecognizedElement(node);
481 catch (XmlException e) {
483 throw new HttpParseException(e.Message, null /*innerException*/, virtualDir + "/" + NoPathFileName(fileName), null /*sourceCode*/, e.LineNumber);
486 throw new HttpParseException(e.Message, null /*innerException*/, fileName, null /*sourceCode*/, e.LineNumber);
489 catch (XmlSchemaException e) {
491 throw new HttpParseException(e.Message, null /*innerException*/, virtualDir + "/" + NoPathFileName(fileName), null /*sourceCode*/, e.LineNumber);
494 throw new HttpParseException(e.Message, null /*innerException*/, fileName, null /*sourceCode*/, e.LineNumber);
498 NormalizeAndValidateTree(_browserTree, false);
499 NormalizeAndValidateTree(_defaultTree, true);
501 BrowserDefinition defaultBrowser = (BrowserDefinition)_browserTree["Default"];
503 if (defaultBrowser != null) {
504 AddBrowserToCollectionRecursive(defaultBrowser, 0);
508 internal void ProcessCustomBrowserFiles() {
509 ProcessCustomBrowserFiles(false, String.Empty);
512 [SuppressMessage("Microsoft.Security.Xml", "CA3056:UseXmlReaderForLoad", Justification = "Developer-controlled .xml files in application directory are implicitly trusted by ASP.Net.")]
513 internal void ProcessCustomBrowserFiles(bool useVirtualPath, string virtualDir) {
514 //get all custom browser files and put them in the "tree"
515 DirectoryInfo browserDirInfo = null;
516 DirectoryInfo[] browserSubDirectories = null;
517 DirectoryInfo[] allBrowserSubDirectories = null;
518 ArrayList customBrowserFileNames;
519 _customTreeList = new ArrayList();
520 _customBrowserFileLists = new ArrayList();
521 _customBrowserDefinitionCollections = new ArrayList();
523 /* Machine Level Custom Browsers */
524 if (useVirtualPath == false) {
525 browserDirInfo = new DirectoryInfo(_browsersDirectory);
527 /* Application Level Custom Browsers */
529 browserDirInfo = new DirectoryInfo(HostingEnvironment.MapPathInternal(virtualDir));
532 allBrowserSubDirectories = browserDirInfo.GetDirectories();
535 int length = allBrowserSubDirectories.Length;
536 browserSubDirectories = new DirectoryInfo[length];
537 for (int i = 0; i < length; i++) {
538 if ((allBrowserSubDirectories[i].Attributes & FileAttributes.Hidden) != FileAttributes.Hidden) {
539 browserSubDirectories[j] = allBrowserSubDirectories[i];
543 Array.Resize(ref browserSubDirectories, j);
545 for (int i = 0; i < browserSubDirectories.Length; i++) {
546 /* Recursively Into Subdirectories */
547 FileInfo[] browserFiles = GetFilesNotHidden(browserSubDirectories[i], browserDirInfo);
549 if (browserFiles == null || browserFiles.Length == 0) {
552 BrowserTree customTree = new BrowserTree();
553 _customTreeList.Add(customTree);
554 _customTreeNames.Add(browserSubDirectories[i].Name);
555 customBrowserFileNames = new ArrayList();
557 foreach (FileInfo browserFile in browserFiles) {
558 customBrowserFileNames.Add(browserFile.FullName);
560 _customBrowserFileLists.Add(customBrowserFileNames);
562 for (int i = 0; i < _customBrowserFileLists.Count; i++) {
563 ArrayList fileNames = (ArrayList)_customBrowserFileLists[i];
564 foreach (string fileName in fileNames) {
565 XmlDocument doc = new ConfigXmlDocument();
569 XmlNode rootNode = doc.DocumentElement;
570 if (rootNode.Name != "browsers") {
571 if (useVirtualPath) {
572 throw new HttpParseException(SR.GetString(SR.Invalid_browser_root), null /*innerException*/, virtualDir + "/" + NoPathFileName(fileName), null /*sourceCode*/, 1);
575 throw new HttpParseException(SR.GetString(SR.Invalid_browser_root), null /*innerException*/, fileName, null /*sourceCode*/, 1);
578 foreach (XmlNode node in rootNode.ChildNodes) {
579 if (node.NodeType != XmlNodeType.Element) {
582 if (node.Name == "browser" || node.Name == "gateway") {
583 ProcessBrowserNode(node, (BrowserTree)_customTreeList[i]);
586 HandlerBase.ThrowUnrecognizedElement(node);
590 catch (XmlException e) {
591 if (useVirtualPath) {
592 throw new HttpParseException(e.Message, null /*innerException*/, virtualDir + "/" + NoPathFileName(fileName), null /*sourceCode*/, e.LineNumber);
595 throw new HttpParseException(e.Message, null /*innerException*/, fileName, null /*sourceCode*/, e.LineNumber);
598 catch (XmlSchemaException e) {
599 if (useVirtualPath) {
600 throw new HttpParseException(e.Message, null /*innerException*/, virtualDir + "/" + NoPathFileName(fileName), null /*sourceCode*/, e.LineNumber);
603 throw new HttpParseException(e.Message, null /*innerException*/, fileName, null /*sourceCode*/, e.LineNumber);
607 SetCustomTreeRoots((BrowserTree)_customTreeList[i], i);
608 NormalizeAndValidateTree((BrowserTree)_customTreeList[i], false, true);
609 _customBrowserDefinitionCollections.Add(new BrowserDefinitionCollection());
610 AddCustomBrowserToCollectionRecursive((BrowserDefinition)(((BrowserTree)_customTreeList[i])[_customTreeNames[i]]), 0, i);
614 internal void AddCustomBrowserToCollectionRecursive(BrowserDefinition bd, int depth, int index) {
615 if(_customBrowserDefinitionCollections[index] == null) {
616 _customBrowserDefinitionCollections[index] = new BrowserDefinitionCollection();
619 bd.IsDeviceNode = true;
620 ((BrowserDefinitionCollection)_customBrowserDefinitionCollections[index]).Add(bd);
622 foreach (BrowserDefinition childBrowser in bd.Browsers) {
623 AddCustomBrowserToCollectionRecursive(childBrowser, depth + 1, index);
627 internal void AddBrowserToCollectionRecursive(BrowserDefinition bd, int depth) {
628 if (_browserDefinitionCollection == null) {
629 _browserDefinitionCollection = new BrowserDefinitionCollection();
633 bd.IsDeviceNode = true;
634 _browserDefinitionCollection.Add(bd);
636 foreach(BrowserDefinition childBrowser in bd.Browsers) {
637 AddBrowserToCollectionRecursive(childBrowser, depth + 1);
641 internal virtual void HandleUnRecognizedParentElement(BrowserDefinition bd, bool isDefault) {
642 throw new ConfigurationErrorsException(SR.GetString(SR.Browser_parentID_Not_Found, bd.ParentID), bd.XmlNode);
645 private static FileInfo[] GetFilesNotHidden(DirectoryInfo rootDirectory, DirectoryInfo browserDirInfo) {
646 ArrayList fileList = new ArrayList();
648 DirectoryInfo[] subDirectories = rootDirectory.GetDirectories("*", SearchOption.AllDirectories);
650 files = rootDirectory.GetFiles("*.browser", SearchOption.TopDirectoryOnly);
651 fileList.AddRange(files);
652 for (int i = 0; i < subDirectories.Length; i++) {
653 if ((HasHiddenParent(subDirectories[i], browserDirInfo) == false)) {
654 files = subDirectories[i].GetFiles("*.browser", SearchOption.TopDirectoryOnly);
655 fileList.AddRange(files);
658 return ((FileInfo [])fileList.ToArray(typeof(FileInfo)));
661 private static bool HasHiddenParent(DirectoryInfo directory, DirectoryInfo browserDirInfo) {
662 while(!String.Equals(directory.Parent.Name, browserDirInfo.Name)) {
663 if ((directory.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden) {
666 directory = directory.Parent;
671 //generate the code from the parsed BrowserDefinitionTree
672 //compile it, and install it in the gac
673 private void GenerateAssembly() {
674 Debug.Assert(_browserTree != null);
675 BrowserDefinition root = (BrowserDefinition)_browserTree["Default"];
676 BrowserDefinition defaultRoot = (BrowserDefinition)_defaultTree["Default"];
677 ArrayList customTreeRoots = new ArrayList();
678 for (int i = 0; i < _customTreeNames.Count; i++) {
679 customTreeRoots.Add((BrowserDefinition)(((BrowserTree)_customTreeList[i])[_customTreeNames[i]]));
682 //create a CodeCompileUnit
683 //add a CodeNamespace object to the CodeCompileUnit
684 //add a CodeTypeDeclaration to the CodeNamespace
685 //add all the members of the type/class to the CodeTypeDeclaration
686 //use a CodeGenerator to generate code from the CodeCompileUnit
687 //a CodeDomProvider can provide a CodeGenerator
689 //translate the BrowserDefinition tree to code
690 CSharpCodeProvider cscp = new CSharpCodeProvider();
692 // namespace System.Web.BrowserCapsFactory
693 CodeCompileUnit ccu = new CodeCompileUnit();
695 //add strong-name key pair for
696 CodeAttributeDeclaration declaration = new CodeAttributeDeclaration(
697 "System.Reflection.AssemblyKeyFile",
698 new CodeAttributeArgument[] {
699 new CodeAttributeArgument(new CodePrimitiveExpression(_strongNameKeyFileName))});
701 CodeAttributeDeclaration aptca = new CodeAttributeDeclaration(
702 "System.Security.AllowPartiallyTrustedCallers");
703 ccu.AssemblyCustomAttributes.Add(aptca);
705 ccu.AssemblyCustomAttributes.Add(declaration);
706 //add version number for it so it can distinguished in future versions
707 declaration = new CodeAttributeDeclaration(
708 "System.Reflection.AssemblyVersion",
709 new CodeAttributeArgument[] {
710 new CodeAttributeArgument(new CodePrimitiveExpression(ThisAssembly.Version))});
711 ccu.AssemblyCustomAttributes.Add(declaration);
713 CodeNamespace cnamespace = new CodeNamespace("ASP");
715 cnamespace.Imports.Add(new CodeNamespaceImport("System"));
716 //GEN: using System.Web;
717 cnamespace.Imports.Add(new CodeNamespaceImport("System.Web"));
718 //GEN: using System.Web.Configuration;
719 cnamespace.Imports.Add(new CodeNamespaceImport("System.Web.Configuration"));
720 //GEN: using System.Reflection;
721 cnamespace.Imports.Add(new CodeNamespaceImport("System.Reflection"));
722 //GEN: class BrowserCapabilitiesFactory
723 ccu.Namespaces.Add(cnamespace);
725 CodeTypeDeclaration factoryType = new CodeTypeDeclaration("BrowserCapabilitiesFactory");
726 factoryType.Attributes = MemberAttributes.Private;
727 factoryType.IsClass = true;
728 factoryType.Name = TypeName;
729 factoryType.BaseTypes.Add(new CodeTypeReference("System.Web.Configuration.BrowserCapabilitiesFactoryBase"));
730 cnamespace.Types.Add(factoryType);
732 //GEN: protected override object ConfigureBrowserCapabilities(NameValueCollection headers, HttpBrowserCapabilities browserCaps)
733 CodeMemberMethod method = new CodeMemberMethod();
734 method.Attributes = MemberAttributes.Override | MemberAttributes.Public;
735 method.ReturnType = new CodeTypeReference(typeof(void));
736 method.Name = "ConfigureBrowserCapabilities";
738 CodeParameterDeclarationExpression cpde = new CodeParameterDeclarationExpression(typeof(NameValueCollection), _headersRefName);
739 method.Parameters.Add(cpde);
740 cpde = new CodeParameterDeclarationExpression(typeof(HttpBrowserCapabilities), browserCapsVariable);
741 method.Parameters.Add(cpde);
742 factoryType.Members.Add(method);
744 GenerateSingleProcessCall(root, method);
746 for (int i = 0; i < customTreeRoots.Count; i++) {
747 GenerateSingleProcessCall((BrowserDefinition)customTreeRoots[i], method);
750 //GEN: if(this.IsBrowserUnknown(browserCaps) == false) return;
751 CodeConditionStatement istatement = new CodeConditionStatement();
753 CodeMethodInvokeExpression cmie = new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), "IsBrowserUnknown");
754 cmie.Parameters.Add(_browserCapsRefExpr);
755 istatement.Condition = new CodeBinaryOperatorExpression(cmie, CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(false));
756 istatement.TrueStatements.Add(new CodeMethodReturnStatement());
757 method.Statements.Add(istatement);
759 if(defaultRoot != null) {
760 GenerateSingleProcessCall(defaultRoot, method, "Default");
763 for (int i = 0; i < customTreeRoots.Count; i++) {
764 foreach (DictionaryEntry entry in (BrowserTree)_customTreeList[i]) {
765 BrowserDefinition bd = entry.Value as BrowserDefinition;
766 Debug.Assert(bd != null);
767 GenerateProcessMethod(bd, factoryType);
771 //GenerateCallsToProcessMethods(root, method);
772 foreach (DictionaryEntry entry in _browserTree) {
773 BrowserDefinition bd = entry.Value as BrowserDefinition;
774 Debug.Assert(bd != null);
775 GenerateProcessMethod(bd, factoryType);
778 foreach (DictionaryEntry entry in _defaultTree) {
779 BrowserDefinition bd = entry.Value as BrowserDefinition;
780 Debug.Assert(bd != null);
781 GenerateProcessMethod(bd, factoryType, "Default");
784 GenerateOverrideMatchedHeaders(factoryType);
785 GenerateOverrideBrowserElements(factoryType);
787 //TODO: don't actually generate the code, just compile it in memory
788 TextWriter twriter = new StreamWriter(new FileStream(_browsersDirectory + "\\BrowserCapsFactory.cs", FileMode.Create));
790 cscp.GenerateCodeFromCompileUnit(ccu, twriter, null);
797 CompilationSection compConfig = MTConfigUtil.GetCompilationAppConfig();
799 bool debug = compConfig.Debug;
801 #if !PLATFORM_UNIX // File system paths must account for UNIX
802 string strongNameFile = _browsersDirectory + "\\" + _strongNameKeyFileName;
803 #else // !PLATFORM_UNIX
804 string strongNameFile = _browsersDirectory + "/" + _strongNameKeyFileName;
805 #endif // !PLATFORM_UNIX
807 // Generate strong name file
808 StrongNameUtility.GenerateStrongNameFile(strongNameFile);
810 //TODO: do not use interim file: CompileAssemblyFromDom instead
811 string[] referencedAssemblies = new string[2] { "System.dll", "System.Web.dll" };
812 CompilerParameters compilerParameters = new CompilerParameters(referencedAssemblies, "ASP.BrowserCapsFactory", debug /* includeDebugInformation */ );
813 compilerParameters.GenerateInMemory = false;
814 compilerParameters.OutputAssembly = _browsersDirectory + "\\ASP.BrowserCapsFactory.dll";
815 CompilerResults results = null;
818 results = cscp.CompileAssemblyFromFile(compilerParameters, _browsersDirectory + "\\BrowserCapsFactory.cs");
821 if (File.Exists(strongNameFile)) {
822 File.Delete(strongNameFile);
826 if (results.NativeCompilerReturnValue != 0 || results.Errors.HasErrors) {
827 foreach (CompilerError error in results.Errors) {
828 if (!error.IsWarning) {
829 throw new HttpCompileException(error.ErrorText);
833 throw new HttpCompileException(SR.GetString(SR.Browser_compile_error));
836 Assembly resultAssembly = results.CompiledAssembly;
838 GacUtil gacutil = new GacUtil();
839 gacutil.GacInstall(resultAssembly.Location);
841 SavePublicKeyTokenFile(_publicKeyTokenFile, resultAssembly.GetName().GetPublicKeyToken());
844 private void SavePublicKeyTokenFile(string filename, byte[] publicKeyToken) {
845 using (FileStream pktStream = new FileStream(filename, FileMode.Create, FileAccess.Write)) {
846 using (StreamWriter pktWriter = new StreamWriter(pktStream)) {
847 foreach (byte b in publicKeyToken) {
848 pktWriter.Write("{0:X2}", b);
854 private static string LoadPublicKeyTokenFromFile(string filename) {
855 IStackWalk fileReadAccess = InternalSecurityPermissions.FileReadAccess(filename);
856 Debug.Assert(fileReadAccess != null);
857 fileReadAccess.Assert();
858 if (!File.Exists(filename)) {
863 using (FileStream pktStream = new FileStream(filename, FileMode.Open, FileAccess.Read)) {
864 using (StreamReader pktReader = new StreamReader(pktStream)) {
865 return pktReader.ReadLine();
869 catch (IOException) {
870 if (HttpRuntime.HasFilePermission(filename)) {
874 // Don't throw exception if we don't have permission to the file.
878 CodeAccessPermission.RevertAssert();
882 internal void GenerateOverrideBrowserElements(CodeTypeDeclaration typeDeclaration) {
884 // Don't generate the property if there's nothing to override.
885 if (_browserDefinitionCollection == null) {
890 // protected override void PopulateBrowserElements(IDictionary dictionary) {
891 // dictionary["Default"] = new Triplet(null, "default description", 0 /*depth_of_node */);
892 // dictionary["Up"] = new Triplet("Default", "up Description", 1 /*depth_of_node */);
894 CodeMemberMethod method = new CodeMemberMethod();
895 method.Name = _browserElementsMethodName;
896 method.Attributes = MemberAttributes.Override | MemberAttributes.Family;
897 method.ReturnType = new CodeTypeReference(typeof(void));
898 CodeParameterDeclarationExpression parameter =
899 new CodeParameterDeclarationExpression(new CodeTypeReference(typeof(IDictionary)), _dictionaryRefName);
901 method.Parameters.Add(parameter);
902 typeDeclaration.Members.Add(method);
904 CodeMethodReferenceExpression baseMethod = new CodeMethodReferenceExpression(new CodeBaseReferenceExpression(), _browserElementsMethodName);
905 CodeMethodInvokeExpression baseInvoke = new CodeMethodInvokeExpression(baseMethod, new CodeExpression[] { _dictionaryRefExpr });
906 method.Statements.Add(baseInvoke);
908 foreach(BrowserDefinition bd in _browserDefinitionCollection) {
909 if (!bd.IsDeviceNode)
912 Debug.Assert(!(bd is GatewayDefinition));
914 CodeAssignStatement cas = new CodeAssignStatement();
915 cas.Left = new CodeIndexerExpression(_dictionaryRefExpr, new CodeExpression[] {
916 new CodePrimitiveExpression(bd.ID)
918 cas.Right = new CodeObjectCreateExpression(typeof(Triplet),
919 new CodeExpression[] {
920 new CodePrimitiveExpression(bd.ParentName),
921 new CodePropertyReferenceExpression(new CodeTypeReferenceExpression(typeof(String)), "Empty"),
922 new CodePrimitiveExpression(bd.Depth)});
924 method.Statements.Add(cas);
927 for (int i = 0; i < _customTreeNames.Count; i++) {
928 foreach (BrowserDefinition bd in (BrowserDefinitionCollection)_customBrowserDefinitionCollections[i]) {
929 if (!bd.IsDeviceNode)
932 Debug.Assert(!(bd is GatewayDefinition));
934 CodeAssignStatement cas = new CodeAssignStatement();
935 cas.Left = new CodeIndexerExpression(_dictionaryRefExpr, new CodeExpression[] {
936 new CodePrimitiveExpression(bd.ID)
938 cas.Right = new CodeObjectCreateExpression(typeof(Triplet),
939 new CodeExpression[] {
940 new CodePrimitiveExpression(bd.ParentName),
941 new CodePropertyReferenceExpression(new CodeTypeReferenceExpression(typeof(String)), "Empty"),
942 new CodePrimitiveExpression(bd.Depth)});
944 method.Statements.Add(cas);
949 internal void GenerateOverrideMatchedHeaders(CodeTypeDeclaration typeDeclaration) {
951 // protected override void PopulateMatchedHeaders(IDictionary dictionary) {
952 // base.PopulateMatchedHeaders(dictionary);
954 // dictionary["header0"] = null;
955 // dictionary["header1"] = null;
957 CodeMemberMethod method = new CodeMemberMethod();
958 method.Name = _matchedHeadersMethodName;
959 method.Attributes = MemberAttributes.Override | MemberAttributes.Family;
960 method.ReturnType = new CodeTypeReference(typeof(void));
961 CodeParameterDeclarationExpression parameter =
962 new CodeParameterDeclarationExpression(new CodeTypeReference(typeof(IDictionary)), _dictionaryRefName);
964 method.Parameters.Add(parameter);
965 typeDeclaration.Members.Add(method);
967 CodeMethodReferenceExpression baseMethod = new CodeMethodReferenceExpression(new CodeBaseReferenceExpression(), _matchedHeadersMethodName);
968 CodeMethodInvokeExpression baseInvoke = new CodeMethodInvokeExpression(baseMethod, new CodeExpression[] { _dictionaryRefExpr });
969 method.Statements.Add(baseInvoke);
971 foreach(String header in _headers) {
972 CodeAssignStatement cas = new CodeAssignStatement();
973 cas.Left = new CodeIndexerExpression(_dictionaryRefExpr, new CodeExpression[] {
974 new CodePrimitiveExpression(header)
976 cas.Right = new CodePrimitiveExpression(null);
978 method.Statements.Add(cas);
982 internal void GenerateProcessMethod(BrowserDefinition bd, CodeTypeDeclaration ctd) {
983 GenerateProcessMethod(bd, ctd, String.Empty);
986 //generate the xxxProcess method for an individual BrowserDefinition
987 internal void GenerateProcessMethod(BrowserDefinition bd, CodeTypeDeclaration ctd, string prefix) {
988 //GEN: internal bool XxxProcess(NameValueCollection headers, HttpBrowserCapabilities browserCaps)
989 CodeMemberMethod cmm = new CodeMemberMethod();
990 cmm.Name = prefix + bd.Name + "Process";
991 cmm.ReturnType = new CodeTypeReference(typeof(bool));
992 cmm.Attributes = MemberAttributes.Private;
993 CodeParameterDeclarationExpression cpde = new CodeParameterDeclarationExpression(typeof(NameValueCollection), _headersRefName);
994 cmm.Parameters.Add(cpde);
995 cpde = new CodeParameterDeclarationExpression(typeof(HttpBrowserCapabilities), browserCapsVariable);
996 cmm.Parameters.Add(cpde);
998 bool regexWorkerGenerated = false;
1000 GenerateIdentificationCode(bd, cmm, ref regexWorkerGenerated);
1001 GenerateCapturesCode(bd, cmm, ref regexWorkerGenerated);
1002 GenerateSetCapabilitiesCode(bd, cmm, ref regexWorkerGenerated);
1003 GenerateSetAdaptersCode(bd, cmm);
1005 // Only add the browser node to the browser collection if it represents a device.
1006 if (bd.IsDeviceNode) {
1007 Debug.Assert(!(bd is GatewayDefinition));
1009 //GEN: browserCaps.AddBrowser("xxx");
1010 CodeMethodInvokeExpression cmie = new CodeMethodInvokeExpression(new CodeVariableReferenceExpression(browserCapsVariable), "AddBrowser");
1011 cmie.Parameters.Add(new CodePrimitiveExpression(bd.ID));
1012 cmm.Statements.Add(cmie);
1015 // Generate ref gateway elements
1016 foreach (BrowserDefinition b in bd.RefGateways) {
1017 AddComment("ref gateways, parent=" + bd.ID, cmm);
1018 GenerateSingleProcessCall(b, cmm);
1021 if ((GenerateOverrides) && (prefix.Length == 0)) {
1022 //Gen: protected virtual void XxxProcessGateways(NameValueCollection headers, HttpBrowserCapabilities browserCaps) ;
1023 string methodName = prefix + bd.Name + "ProcessGateways";
1024 GenerateChildProcessMethod(methodName, ctd, false);
1026 //Gen: XxxProcessGateways(headers, browserCaps) ;
1027 GenerateChildProcessInvokeExpression(methodName, cmm, false);
1030 foreach(BrowserDefinition b in bd.Gateways) {
1031 AddComment("gateway, parent=" + bd.ID, cmm);
1032 GenerateSingleProcessCall(b, cmm);
1035 if (GenerateOverrides) {
1036 //GEN: bool ignoreApplicationBrowsers = true | false; //bd.Browsers.Count != 0
1037 CodeVariableDeclarationStatement cvds = new CodeVariableDeclarationStatement(typeof(bool),
1038 IgnoreApplicationBrowserVariableName, new CodePrimitiveExpression(bd.Browsers.Count != 0));
1039 cmm.Statements.Add(cvds);
1042 if (bd.Browsers.Count > 0) {
1043 CodeStatementCollection statements = cmm.Statements;
1044 AddComment("browser, parent=" + bd.ID, cmm);
1045 foreach (BrowserDefinition b in bd.Browsers) {
1046 statements = GenerateTrackedSingleProcessCall(statements, b, cmm, prefix);
1049 if (GenerateOverrides) {
1050 //GEN: ignoreApplicationBrowsers = false;
1051 CodeAssignStatement codeAssignStmt = new CodeAssignStatement();
1052 codeAssignStmt.Left = new CodeVariableReferenceExpression(IgnoreApplicationBrowserVariableName);
1053 codeAssignStmt.Right = new CodePrimitiveExpression(false);
1054 statements.Add(codeAssignStmt);
1058 // Generate ref browser
1059 foreach (BrowserDefinition b in bd.RefBrowsers) {
1060 AddComment("ref browsers, parent=" + bd.ID, cmm);
1061 if (b.IsDefaultBrowser) {
1062 GenerateSingleProcessCall(b, cmm, "Default");
1065 GenerateSingleProcessCall(b, cmm);
1069 if (GenerateOverrides) {
1070 //Gen: protected virtual void XxxProcessBrowsers(bool ignoreApplicationBrowsers, NameValueCollection headers, HttpBrowserCapabilities browserCaps) ;
1071 string methodName = prefix + bd.Name + "ProcessBrowsers";
1072 GenerateChildProcessMethod(methodName, ctd, true);
1074 //Gen: XxxProcessBrowsers(ignoreApplicationBrowsers, headers, browserCaps);
1075 GenerateChildProcessInvokeExpression(methodName, cmm, true);
1079 CodeMethodReturnStatement cmrs = new CodeMethodReturnStatement(new CodePrimitiveExpression(true));
1080 cmm.Statements.Add(cmrs);
1082 ctd.Members.Add(cmm);
1085 private void GenerateChildProcessInvokeExpression(string methodName, CodeMemberMethod cmm, bool generateTracker) {
1086 //Gen: XxxProcessBrowsers(ignoreApplicationBrowsers, headers, browserCaps) ;
1087 CodeMethodInvokeExpression expr = new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), methodName);
1089 if (generateTracker) {
1090 expr.Parameters.Add(new CodeVariableReferenceExpression(IgnoreApplicationBrowserVariableName));
1092 expr.Parameters.Add(new CodeVariableReferenceExpression(_headersRefName));
1093 expr.Parameters.Add(new CodeVariableReferenceExpression(browserCapsVariable));
1095 cmm.Statements.Add(expr);
1098 private void GenerateChildProcessMethod(string methodName, CodeTypeDeclaration ctd, bool generateTracker) {
1099 //Gen: protected virtual void XxxProcessBrowsers(bool ignoreApplicationBrowsers, NameValueCollection headers, HttpBrowserCapabilities browserCaps) ;
1100 CodeMemberMethod cmm= new CodeMemberMethod();
1101 cmm.Name = methodName;
1102 cmm.ReturnType = new CodeTypeReference(typeof(void));
1103 cmm.Attributes = MemberAttributes.Family;
1104 CodeParameterDeclarationExpression cpde = null;
1106 if (generateTracker) {
1107 cpde = new CodeParameterDeclarationExpression(typeof(bool), IgnoreApplicationBrowserVariableName);
1108 cmm.Parameters.Add(cpde);
1111 cpde = new CodeParameterDeclarationExpression(typeof(NameValueCollection), _headersRefName);
1112 cmm.Parameters.Add(cpde);
1113 cpde = new CodeParameterDeclarationExpression(typeof(HttpBrowserCapabilities), browserCapsVariable);
1114 cmm.Parameters.Add(cpde);
1116 ctd.Members.Add(cmm);
1119 private void GenerateRegexWorkerIfNecessary(CodeMemberMethod cmm, ref bool regexWorkerGenerated) {
1120 if (regexWorkerGenerated) {
1124 regexWorkerGenerated = true;
1126 //GEN: RegexWorker regexWorker;
1127 cmm.Statements.Add(new CodeVariableDeclarationStatement("RegexWorker", _regexWorkerRefName));
1129 //GEN: regexWorker = new RegexWorker(browserCaps);
1130 cmm.Statements.Add(new CodeAssignStatement(_regexWorkerRefExpr, new CodeObjectCreateExpression("RegexWorker", _browserCapsRefExpr)));
1133 private void ReturnIfHeaderValueEmpty(CodeMemberMethod cmm, CodeVariableReferenceExpression varExpr) {
1134 // GEN: if(String.IsNullOrEmpty(varExpr)) {
1135 // GEN: return false;
1137 CodeConditionStatement emptyCheckStmt = new CodeConditionStatement();
1138 CodeMethodReferenceExpression emptyCheckMethod = new CodeMethodReferenceExpression(new CodeTypeReferenceExpression(typeof(String)), "IsNullOrEmpty");
1139 CodeMethodInvokeExpression emptyCheckExpr = new CodeMethodInvokeExpression(emptyCheckMethod, varExpr);
1141 emptyCheckStmt.Condition = emptyCheckExpr;
1142 emptyCheckStmt.TrueStatements.Add(new CodeMethodReturnStatement(new CodePrimitiveExpression(false)));
1143 cmm.Statements.Add(emptyCheckStmt);
1146 //generate part of the xxxProcess method for handling determining if the requesting
1147 //browser meets the regexes for this browser
1148 private void GenerateIdentificationCode(BrowserDefinition bd, CodeMemberMethod cmm, ref bool regexWorkerGenerated) {
1150 //GEN: IDictionary dictionary;
1151 cmm.Statements.Add(new CodeVariableDeclarationStatement(typeof(IDictionary), _dictionaryRefName));
1153 //GEN: dictionary = browserCaps.Capabilities;
1154 CodeAssignStatement assign = new CodeAssignStatement(
1156 new CodePropertyReferenceExpression(_browserCapsRefExpr, "Capabilities")
1158 cmm.Statements.Add(assign);
1160 bool disableOptimizedKey = false;
1161 CodeVariableReferenceExpression result = null;
1162 CodeVariableReferenceExpression headerValue = null;
1164 if(bd.IdHeaderChecks.Count > 0) {
1165 AddComment("Identification: check header matches", cmm);
1166 for (int i = 0; i < bd.IdHeaderChecks.Count; i++) {
1167 string matchedString = ((CheckPair)bd.IdHeaderChecks[i]).MatchString;
1169 // Skip matching ".*"
1170 if (matchedString.Equals(".*")) {
1174 if (headerValue == null) {
1175 headerValue = GenerateVarReference(cmm, typeof(string), "headerValue");
1178 CodeAssignStatement valueAssignment = new CodeAssignStatement();
1179 cmm.Statements.Add(valueAssignment);
1180 valueAssignment.Left = headerValue;
1182 if (((CheckPair)bd.IdHeaderChecks[i]).Header.Equals("User-Agent")) {
1183 _headers.Add(String.Empty);
1185 // GEN: headerValue = ((string)(browserCaps[String.Empty]));
1186 valueAssignment.Right = new CodeCastExpression(typeof(string),
1187 new CodeIndexerExpression(
1188 new CodeVariableReferenceExpression(browserCapsVariable),
1189 new CodeExpression[] {
1190 new CodePropertyReferenceExpression(
1191 new CodeTypeReferenceExpression(typeof(String)), "Empty") }));
1194 string header = ((CheckPair)bd.IdHeaderChecks[i]).Header;
1195 _headers.Add(header);
1197 //GEN: headerValue = ((String)headers["xxx"]);
1198 valueAssignment.Right = new CodeCastExpression(typeof(string),
1199 new CodeIndexerExpression(
1201 new CodeExpression[] { new CodePrimitiveExpression(header) }
1205 disableOptimizedKey = true;
1208 // Don't need to use Regex if matching . only.
1209 if (matchedString.Equals(".")) {
1211 // Simply return if the header exists.
1212 ReturnIfHeaderValueEmpty(cmm, headerValue);
1217 if (result == null) {
1218 result = GenerateVarReference(cmm, typeof(bool), _resultVarName);
1221 GenerateRegexWorkerIfNecessary(cmm, ref regexWorkerGenerated);
1222 CodeMethodInvokeExpression cmie = new CodeMethodInvokeExpression(_regexWorkerRefExpr, _processRegexMethod);
1224 cmie.Parameters.Add(headerValue);
1225 cmie.Parameters.Add(new CodePrimitiveExpression(matchedString));
1227 //GEN: result = regexWorker.ProcessRegex(headerValue, {matchedString});
1228 assign = new CodeAssignStatement();
1229 assign.Left = result;
1230 assign.Right = cmie;
1231 cmm.Statements.Add(assign);
1233 //GEN: if(result == false) {
1234 //GEN: return false;
1236 CodeConditionStatement istatement = new CodeConditionStatement();
1237 if(((CheckPair)bd.IdHeaderChecks[i]).NonMatch) {
1238 istatement.Condition = new CodeBinaryOperatorExpression(result, CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(true));
1241 istatement.Condition = new CodeBinaryOperatorExpression(result, CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(false));
1243 istatement.TrueStatements.Add(new CodeMethodReturnStatement(new CodePrimitiveExpression(false)));
1244 cmm.Statements.Add(istatement);
1248 if (bd.IdCapabilityChecks.Count > 0) {
1249 AddComment("Identification: check capability matches", cmm);
1250 for (int i = 0; i < bd.IdCapabilityChecks.Count; i++) {
1251 string matchedString = ((CheckPair)bd.IdCapabilityChecks[i]).MatchString;
1253 // Skip matching ".*"
1254 if (matchedString.Equals(".*")) {
1258 if (headerValue == null) {
1259 headerValue = GenerateVarReference(cmm, typeof(string), "headerValue");
1262 CodeAssignStatement valueAssignment = new CodeAssignStatement();
1263 cmm.Statements.Add(valueAssignment);
1264 valueAssignment.Left = headerValue;
1265 valueAssignment.Right = (new CodeCastExpression(typeof(string),
1266 new CodeIndexerExpression(
1268 new CodeExpression[] {
1269 new CodePrimitiveExpression(((CheckPair)bd.IdCapabilityChecks[i]).Header)
1274 // Don't need to use Regex if matching . only.
1275 if (matchedString.Equals(".")) {
1279 if (result == null) {
1280 result = GenerateVarReference(cmm, typeof(bool), _resultVarName);
1283 GenerateRegexWorkerIfNecessary(cmm, ref regexWorkerGenerated);
1284 //GEN: result = regexWorker.ProcessRegex((string)dictionary["xxxCapability"], "xxxRegexString");
1285 CodeMethodInvokeExpression cmie = new CodeMethodInvokeExpression(_regexWorkerRefExpr, _processRegexMethod);
1287 cmie.Parameters.Add(headerValue);
1288 cmie.Parameters.Add(new CodePrimitiveExpression(matchedString));
1289 assign = new CodeAssignStatement();
1290 assign.Left = result;
1291 assign.Right = cmie;
1292 cmm.Statements.Add(assign);
1294 //GEN: if(result == false) {
1295 //GEN: return false;
1297 CodeConditionStatement istatement = new CodeConditionStatement();
1298 if (((CheckPair)bd.IdCapabilityChecks[i]).NonMatch) {
1299 istatement.Condition = new CodeBinaryOperatorExpression(result, CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(true));
1302 istatement.Condition = new CodeBinaryOperatorExpression(result, CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(false));
1304 istatement.TrueStatements.Add(new CodeMethodReturnStatement(new CodePrimitiveExpression(false)));
1305 cmm.Statements.Add(istatement);
1309 //GEN: browserCaps.DisableOptimizedCacheKey();
1310 if (disableOptimizedKey) {
1311 CodeMethodInvokeExpression cme = new CodeMethodInvokeExpression(_browserCapsRefExpr, _disableOptimizedCacheKeyMethodName);
1312 cmm.Statements.Add(cme);
1316 private CodeVariableReferenceExpression GenerateVarReference(CodeMemberMethod cmm, Type varType, string varName) {
1317 //GEN: {varType} {varName};
1318 cmm.Statements.Add(new CodeVariableDeclarationStatement(varType, varName));
1319 return new CodeVariableReferenceExpression(varName);
1322 //generate part of the xxxProcess method for running and storing the capture regexes
1323 private void GenerateCapturesCode(BrowserDefinition bd, CodeMemberMethod cmm, ref bool regexWorkerGenerated) {
1324 if ((bd.CaptureHeaderChecks.Count == 0) && (bd.CaptureCapabilityChecks.Count == 0)) {
1328 if(bd.CaptureHeaderChecks.Count > 0) {
1329 AddComment("Capture: header values", cmm);
1330 for(int i = 0; i < bd.CaptureHeaderChecks.Count; i++) {
1332 string matchedString = ((CheckPair)bd.CaptureHeaderChecks[i]).MatchString;
1333 if (matchedString.Equals(".*")) {
1337 GenerateRegexWorkerIfNecessary(cmm, ref regexWorkerGenerated);
1338 CodeMethodInvokeExpression cmie = new CodeMethodInvokeExpression(_regexWorkerRefExpr, _processRegexMethod);
1340 if (((CheckPair)bd.CaptureHeaderChecks[i]).Header.Equals("User-Agent")) {
1341 _headers.Add(String.Empty);
1342 cmie.Parameters.Add(new CodeCastExpression(typeof(string),
1343 new CodeIndexerExpression(new CodeVariableReferenceExpression(browserCapsVariable), new CodeExpression[] {
1344 new CodePropertyReferenceExpression(new CodeTypeReferenceExpression(typeof(String)), "Empty") })));
1347 string header = ((CheckPair)bd.CaptureHeaderChecks[i]).Header;
1348 _headers.Add(header);
1350 //GEN: regexWorker.ProcessRegex((string)headers["xxx"], "xxxRegexString");
1351 cmie.Parameters.Add(
1352 new CodeCastExpression(typeof(string),
1353 new CodeIndexerExpression(
1355 new CodeExpression[] { new CodePrimitiveExpression(header) }
1361 cmie.Parameters.Add(new CodePrimitiveExpression(matchedString));
1362 cmm.Statements.Add(cmie);
1366 if (bd.CaptureCapabilityChecks.Count > 0) {
1367 AddComment("Capture: capability values", cmm);
1368 for(int i = 0; i < bd.CaptureCapabilityChecks.Count; i++) {
1370 string matchedString = ((CheckPair)bd.CaptureCapabilityChecks[i]).MatchString;
1371 if (matchedString.Equals(".*")) {
1375 GenerateRegexWorkerIfNecessary(cmm, ref regexWorkerGenerated);
1376 //GEN: regexWorker.ProcessRegex((string)dictionary["xxxCapability"], "xxxRegexString");
1377 CodeMethodInvokeExpression cmie = new CodeMethodInvokeExpression(_regexWorkerRefExpr, _processRegexMethod);
1378 cmie.Parameters.Add(
1379 new CodeCastExpression(typeof(string),
1380 new CodeIndexerExpression(
1382 new CodeExpression[] { new CodePrimitiveExpression(((CheckPair)bd.CaptureCapabilityChecks[i]).Header) }
1387 cmie.Parameters.Add(new CodePrimitiveExpression(matchedString));
1388 cmm.Statements.Add(cmie);
1393 //generate part of the xxxProcess method for assigning capability values
1394 private void GenerateSetCapabilitiesCode(BrowserDefinition bd, CodeMemberMethod cmm, ref bool regexWorkerGenerated) {
1395 //GEN: browserCaps[aaa] = "bbb";
1396 //GEN: browserCaps[xxx] = "yyy";
1397 NameValueCollection nvc = bd.Capabilities;
1398 CodeAssignStatement assign;
1400 AddComment("Capabilities: set capabilities", cmm);
1401 foreach (string s in nvc.Keys) {
1402 string capsString = nvc[s];
1403 //GEN: dictionary["xxx"] = regexWorker["xxx"];
1404 assign = new CodeAssignStatement();
1405 assign.Left = new CodeIndexerExpression(
1407 new CodeExpression[] { new CodePrimitiveExpression(s) } );
1409 CodePrimitiveExpression capabilityExpr = new CodePrimitiveExpression(capsString);
1410 if (RegexWorker.RefPat.Match(capsString).Success) {
1412 GenerateRegexWorkerIfNecessary(cmm, ref regexWorkerGenerated);
1413 assign.Right = new CodeIndexerExpression(
1414 _regexWorkerRefExpr,
1415 new CodeExpression[] {capabilityExpr});
1418 assign.Right = capabilityExpr;
1421 cmm.Statements.Add(assign);
1425 //generate part of the xxxProcess method for setting specific adapters for this browser
1426 internal void GenerateSetAdaptersCode(BrowserDefinition bd, CodeMemberMethod cmm) {
1427 //GEN: browserCaps.Adapters[xxxControl] = yyyAdapter;
1428 foreach (DictionaryEntry entry in bd.Adapters) {
1429 string controlString = (string)entry.Key;
1430 string adapterString = (string)entry.Value;
1431 CodePropertyReferenceExpression cpre = new CodePropertyReferenceExpression(_browserCapsRefExpr, "Adapters");
1432 CodeIndexerExpression indexerExpression = new CodeIndexerExpression(
1434 new CodeExpression[] { new CodePrimitiveExpression(controlString) }
1436 CodeAssignStatement assignAdapter = new CodeAssignStatement();
1437 assignAdapter.Left = indexerExpression;
1438 assignAdapter.Right = new CodePrimitiveExpression(adapterString);
1439 cmm.Statements.Add(assignAdapter);
1442 //GEN: browser.HtmlTextWriter = xxxHtmlTextWriter;
1443 if(bd.HtmlTextWriterString != null) {
1444 CodeAssignStatement assignHtmlTextWriter = new CodeAssignStatement();
1445 assignHtmlTextWriter.Left = new CodePropertyReferenceExpression(_browserCapsRefExpr, "HtmlTextWriter");
1446 assignHtmlTextWriter.Right = new CodePrimitiveExpression(bd.HtmlTextWriterString);
1447 cmm.Statements.Add(assignHtmlTextWriter);
1452 internal void AddComment(string comment, CodeMemberMethod cmm) {
1453 cmm.Statements.Add(new CodeCommentStatement(comment));
1456 internal CodeStatementCollection GenerateTrackedSingleProcessCall(CodeStatementCollection stmts, BrowserDefinition bd, CodeMemberMethod cmm) {
1457 return GenerateTrackedSingleProcessCall(stmts, bd, cmm, String.Empty);
1460 internal CodeStatementCollection GenerateTrackedSingleProcessCall(CodeStatementCollection stmts, BrowserDefinition bd, CodeMemberMethod cmm, string prefix) {
1461 //GEN: if (xProcess(headers, browserCaps)) {
1466 CodeMethodInvokeExpression xProcess = new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), prefix + bd.Name + "Process");
1467 xProcess.Parameters.Add(new CodeVariableReferenceExpression(_headersRefName));
1468 xProcess.Parameters.Add(new CodeVariableReferenceExpression(browserCapsVariable));
1470 CodeConditionStatement conditionStmt = new CodeConditionStatement();
1471 conditionStmt.Condition = xProcess;
1473 stmts.Add(conditionStmt);
1475 return conditionStmt.FalseStatements;
1478 internal void GenerateSingleProcessCall(BrowserDefinition bd, CodeMemberMethod cmm) {
1479 GenerateSingleProcessCall(bd, cmm, String.Empty);
1482 //generate code to call the xxxProcess for a given browser
1483 //and store the result in a local variable
1484 internal void GenerateSingleProcessCall(BrowserDefinition bd, CodeMemberMethod cmm, string prefix) {
1485 //GEN: xProcess(headers, browserCaps);
1486 CodeMethodInvokeExpression xProcess = new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), prefix + bd.Name + "Process");
1487 xProcess.Parameters.Add(new CodeVariableReferenceExpression(_headersRefName));
1488 xProcess.Parameters.Add(new CodeVariableReferenceExpression(browserCapsVariable));
1489 cmm.Statements.Add(xProcess);