1 //------------------------------------------------------------------------------ 2 // <copyright file="BrowserCapabilitiesCodeGenerator.cs" company="Microsoft"> 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // </copyright> 5 //------------------------------------------------------------------------------ 6 7 namespace System.Web.Configuration { 8 using System; 9 using System.CodeDom; 10 using System.CodeDom.Compiler; 11 using System.Configuration; 12 using System.Collections; 13 using System.Collections.Specialized; 14 using System.IO; 15 #if !FEATURE_PAL 16 using System.ServiceProcess; 17 #endif // !FEATURE_PAL 18 using System.Linq; 19 using System.Reflection; 20 using System.Security; 21 using System.Security.Permissions; 22 using System.Text; 23 using System.Text.RegularExpressions; 24 using System.Web; 25 using System.Web.Compilation; 26 using System.Web.Configuration; 27 using System.Web.Hosting; 28 using System.Web.UI; 29 using System.Web.Util; 30 using System.Xml; 31 using System.Xml.Schema; 32 33 using Microsoft.Build.Utilities; 34 using Microsoft.CSharp; 35 using System.Diagnostics.CodeAnalysis; 36 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; 42 43 private static object _staticLock = new object(); 44 45 private BrowserTree _browserTree; 46 private BrowserTree _defaultTree; 47 private BrowserDefinitionCollection _browserDefinitionCollection; 48 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; 65 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); 70 71 private ArrayList _browserFileList; 72 73 private ArrayList _customBrowserFileLists; 74 private ArrayList _customTreeList; 75 private ArrayList _customTreeNames; 76 private ArrayList _customBrowserDefinitionCollections; 77 78 private CaseInsensitiveStringSet _headers; 79 BrowserCapabilitiesCodeGenerator()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; 87 88 #endif // !PLATFORM_UNIX 89 } 90 BrowserCapabilitiesCodeGenerator()91 public BrowserCapabilitiesCodeGenerator() { 92 _headers = new CaseInsensitiveStringSet(); 93 } 94 95 internal BrowserTree BrowserTree { 96 get { 97 return _browserTree; 98 } 99 } 100 101 internal BrowserTree DefaultTree { 102 get { 103 return _defaultTree; 104 } 105 } 106 107 internal ArrayList CustomTreeList { 108 get { 109 return _customTreeList; 110 } 111 } 112 113 internal ArrayList CustomTreeNames { 114 get { 115 return _customTreeNames; 116 } 117 } 118 119 internal static string BrowserCapAssemblyPublicKeyToken { 120 get { 121 if (_publicKeyTokenLoaded) { 122 return _publicKeyToken; 123 } 124 125 lock (_staticLock) { 126 if (_publicKeyTokenLoaded) { 127 return _publicKeyToken; 128 } 129 130 string publicKeyTokenFile; 131 if (MultiTargetingUtil.IsTargetFramework40OrAbove) { 132 publicKeyTokenFile = _publicKeyTokenFile; 133 } 134 else { 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); 140 } 141 _publicKeyToken = LoadPublicKeyTokenFromFile(publicKeyTokenFile); 142 _publicKeyTokenLoaded = true; 143 144 return _publicKeyToken; 145 } 146 } 147 } 148 149 internal virtual bool GenerateOverrides { get { return true; } } 150 151 internal virtual string TypeName { 152 get { 153 return _factoryTypeName; 154 } 155 } 156 AddFile(string filePath)157 internal void AddFile(string filePath) { 158 if (_browserFileList == null) 159 _browserFileList = new ArrayList(); 160 161 _browserFileList.Add(filePath); 162 } 163 AddCustomFile(string filePath)164 internal void AddCustomFile(string filePath) { 165 if (_customBrowserFileLists == null) { 166 _customBrowserFileLists = new ArrayList(); 167 } 168 169 _customBrowserFileLists.Add(filePath); 170 } 171 172 //parse the config info and create BrowserTree 173 //then generate code for, compile, and gac the object 174 [SecurityPermission(SecurityAction.Demand, Unrestricted=true)] Create()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"); 179 180 if (browserFiles == null || browserFiles.Length == 0) { 181 return; 182 } 183 184 foreach(FileInfo browserFile in browserFiles) { 185 AddFile(browserFile.FullName); 186 } 187 188 // First parse the browser files. 189 ProcessBrowserFiles(); 190 191 // Then parse custom browser files. 192 ProcessCustomBrowserFiles(); 193 194 // Uninstall previously installed generated assembly. 195 Uninstall(); 196 197 //generate the source code, compile it, and gac it 198 GenerateAssembly(); 199 200 // Restart w3svc service 201 RestartW3SVCIfNecessary(); 202 } 203 UninstallInternal()204 internal bool UninstallInternal() { 205 // Remove existing strong name public token file 206 if (File.Exists(_publicKeyTokenFile)) { 207 File.Delete(_publicKeyTokenFile); 208 } 209 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) { 214 return false; 215 } 216 217 return true; 218 } 219 220 [SecurityPermission(SecurityAction.Demand, Unrestricted = true)] Uninstall()221 public bool Uninstall() { 222 // Restart w3svc service 223 RestartW3SVCIfNecessary(); 224 225 if (!UninstallInternal()) { 226 return false; 227 } 228 229 // Restart w3svc service again so applications get a fresh copy. 230 RestartW3SVCIfNecessary(); 231 232 return true; 233 } 234 RestartW3SVCIfNecessary()235 private void RestartW3SVCIfNecessary() { 236 #if !FEATURE_PAL 237 try { 238 // Dev10 bug 734918 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) { 243 return; 244 } 245 246 ServiceControllerStatus status = controller.Status; 247 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)) { 252 controller.Stop(); 253 254 // Give it 5 minutes to stop 255 controller.WaitForStatus(ServiceControllerStatus.Stopped, new TimeSpan(0, 5, 0)); 256 controller.Start(); 257 258 // If the service was paused, pause it. 259 if (status.Equals(ServiceControllerStatus.Paused) || status.Equals(ServiceControllerStatus.PausePending)) { 260 controller.Pause(); 261 } 262 } 263 } 264 catch (Exception ex) { 265 throw new InvalidOperationException(SR.GetString(SR.Browser_W3SVC_Failure_Helper_Text, ex)); 266 } 267 #endif // !FEATURE_PAL 268 } 269 ProcessBrowserFiles()270 internal void ProcessBrowserFiles() { 271 ProcessBrowserFiles(false, String.Empty); 272 } 273 NoPathFileName(string fullPath)274 private string NoPathFileName(string fullPath) { 275 int lastSlash = fullPath.LastIndexOf("\\", StringComparison.Ordinal); 276 if(lastSlash > -1) { 277 return fullPath.Substring(lastSlash + 1); 278 } 279 return fullPath; 280 } 281 ProcessBrowserNode(XmlNode node, BrowserTree browserTree)282 internal virtual void ProcessBrowserNode(XmlNode node, BrowserTree browserTree) { 283 284 BrowserDefinition browserInfo = null; 285 286 if (node.Name == "gateway") { 287 browserInfo = new GatewayDefinition(node); 288 } 289 else if (node.Name == "browser") { 290 browserInfo = new BrowserDefinition(node); 291 } 292 else { 293 Debug.Assert(node.Name == "defaultBrowser"); 294 browserInfo = new BrowserDefinition(node, true); 295 } 296 297 BrowserDefinition oldNode = (BrowserDefinition)browserTree[browserInfo.Name]; 298 299 if (oldNode != null) { 300 if (browserInfo.IsRefID) { 301 oldNode.MergeWithDefinition(browserInfo); 302 } 303 else { 304 throw new ConfigurationErrorsException(SR.GetString(SR.Duplicate_browser_id, browserInfo.ID), node); 305 } 306 } 307 else { 308 browserTree[browserInfo.Name] = browserInfo; 309 } 310 } 311 NormalizeAndValidateTree(BrowserTree browserTree, bool isDefaultBrowser)312 private void NormalizeAndValidateTree(BrowserTree browserTree, bool isDefaultBrowser) { 313 NormalizeAndValidateTree(browserTree, isDefaultBrowser, false); 314 } 315 NormalizeAndValidateTree(BrowserTree browserTree, bool isDefaultBrowser, bool isCustomBrowser)316 private void NormalizeAndValidateTree(BrowserTree browserTree, bool isDefaultBrowser, bool isCustomBrowser) { 317 //normalize the tree 318 foreach (DictionaryEntry entry in browserTree) { 319 BrowserDefinition bd = (BrowserDefinition)entry.Value; 320 string parentName = bd.ParentName; 321 BrowserDefinition parentBrowser = null; 322 323 if (IsRootNode(bd.Name)) { 324 continue; 325 } 326 327 if (parentName.Length > 0) { 328 parentBrowser = (BrowserDefinition)browserTree[parentName]; 329 } 330 331 if (parentBrowser != null) { 332 if (bd.IsRefID) { 333 if (bd is GatewayDefinition) { 334 parentBrowser.RefGateways.Add(bd); 335 } 336 else { 337 parentBrowser.RefBrowsers.Add(bd); 338 } 339 } 340 else if (bd is GatewayDefinition) { 341 parentBrowser.Gateways.Add(bd); 342 } 343 else { 344 parentBrowser.Browsers.Add(bd); 345 } 346 } 347 else { 348 if (isCustomBrowser) { 349 throw new ConfigurationErrorsException(SR.GetString(SR.Browser_parentID_Not_Found, bd.ParentID), bd.XmlNode); 350 } 351 else { 352 HandleUnRecognizedParentElement(bd, isDefaultBrowser); 353 } 354 } 355 } 356 357 //validate the tree 358 //loop check 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); 367 } 368 loopCheck[currentId] = currentId; 369 currentBrowser = (BrowserDefinition)browserTree[currentBrowser.ParentName]; 370 //in app-level, parent can exist in machine level 371 if (currentBrowser == null) { 372 break; 373 } 374 375 currentId = currentBrowser.Name; 376 } 377 } 378 } 379 SetCustomTreeRoots(BrowserTree browserTree, int index)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; 385 break; 386 } 387 } 388 } 389 390 // Now that we support adding custom browser hierarchies, root nodes other than Default are permitted. IsRootNode(string nodeName)391 private bool IsRootNode(string nodeName) { 392 if (String.Compare(nodeName, "Default", StringComparison.OrdinalIgnoreCase) == 0) 393 return true; 394 395 foreach (string treeRootName in _customTreeNames) { 396 if (String.Compare(nodeName, treeRootName, StringComparison.OrdinalIgnoreCase) == 0) { 397 return true; 398 } 399 } 400 401 return false; 402 } 403 404 [SuppressMessage("Microsoft.Security.Xml", "CA3056:UseXmlReaderForLoad", Justification = "Developer-controlled .xml files in application directory are implicitly trusted by ASP.Net.")] ProcessBrowserFiles(bool useVirtualPath, string virtualDir)405 protected void ProcessBrowserFiles(bool useVirtualPath, string virtualDir) { 406 _browserTree = new BrowserTree(); 407 _defaultTree = new BrowserTree(); 408 _customTreeNames = new ArrayList(); 409 410 if (_browserFileList == null) { 411 _browserFileList = new ArrayList(); 412 } 413 414 _browserFileList.Sort(); 415 //#if OPTIMIZE_FOR_DESKTOP_BROWSER 416 string mozillaFile = null; 417 string ieFile = null; 418 string operaFile = null; 419 420 // DevDivBugs 180962 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)) { 426 ieFile = filePath; 427 } 428 else if (filePath.EndsWith("mozilla.browser", StringComparison.OrdinalIgnoreCase)) { 429 mozillaFile = filePath; 430 } 431 else if (filePath.EndsWith("opera.browser", StringComparison.OrdinalIgnoreCase)) { 432 operaFile = filePath; 433 break; 434 } 435 } 436 437 if (ieFile != null) { 438 _browserFileList.Remove(ieFile); 439 _browserFileList.Insert(0, ieFile); 440 } 441 442 if (mozillaFile != null) { 443 _browserFileList.Remove(mozillaFile); 444 _browserFileList.Insert(1, mozillaFile); 445 } 446 447 if (operaFile != null) { 448 _browserFileList.Remove(operaFile); 449 _browserFileList.Insert(2, operaFile); 450 } 451 //#endif 452 foreach (string fileName in _browserFileList) { 453 XmlDocument doc = new ConfigXmlDocument(); 454 try { 455 doc.Load(fileName); 456 457 XmlNode rootNode = doc.DocumentElement; 458 if(rootNode.Name != "browsers") { 459 if(useVirtualPath) { 460 throw new HttpParseException(SR.GetString(SR.Invalid_browser_root), null /*innerException*/, virtualDir + "/" + NoPathFileName(fileName), null /*sourceCode*/, 1); 461 } 462 else { 463 throw new HttpParseException(SR.GetString(SR.Invalid_browser_root), null /*innerException*/, fileName, null /*sourceCode*/, 1); 464 } 465 } 466 467 foreach (XmlNode node in rootNode.ChildNodes) { 468 if (node.NodeType != XmlNodeType.Element) 469 continue; 470 if (node.Name == "browser" || node.Name == "gateway") { 471 ProcessBrowserNode(node, _browserTree); 472 } 473 else if (node.Name == "defaultBrowser") { 474 ProcessBrowserNode(node, _defaultTree); 475 } 476 else { 477 HandlerBase.ThrowUnrecognizedElement(node); 478 } 479 } 480 } 481 catch (XmlException e) { 482 if(useVirtualPath) { 483 throw new HttpParseException(e.Message, null /*innerException*/, virtualDir + "/" + NoPathFileName(fileName), null /*sourceCode*/, e.LineNumber); 484 } 485 else { 486 throw new HttpParseException(e.Message, null /*innerException*/, fileName, null /*sourceCode*/, e.LineNumber); 487 } 488 } 489 catch (XmlSchemaException e) { 490 if(useVirtualPath) { 491 throw new HttpParseException(e.Message, null /*innerException*/, virtualDir + "/" + NoPathFileName(fileName), null /*sourceCode*/, e.LineNumber); 492 } 493 else { 494 throw new HttpParseException(e.Message, null /*innerException*/, fileName, null /*sourceCode*/, e.LineNumber); 495 } 496 } 497 } 498 NormalizeAndValidateTree(_browserTree, false); 499 NormalizeAndValidateTree(_defaultTree, true); 500 501 BrowserDefinition defaultBrowser = (BrowserDefinition)_browserTree["Default"]; 502 503 if (defaultBrowser != null) { 504 AddBrowserToCollectionRecursive(defaultBrowser, 0); 505 } 506 } 507 ProcessCustomBrowserFiles()508 internal void ProcessCustomBrowserFiles() { 509 ProcessCustomBrowserFiles(false, String.Empty); 510 } 511 512 [SuppressMessage("Microsoft.Security.Xml", "CA3056:UseXmlReaderForLoad", Justification = "Developer-controlled .xml files in application directory are implicitly trusted by ASP.Net.")] ProcessCustomBrowserFiles(bool useVirtualPath, string virtualDir)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(); 522 523 /* Machine Level Custom Browsers */ 524 if (useVirtualPath == false) { 525 browserDirInfo = new DirectoryInfo(_browsersDirectory); 526 } 527 /* Application Level Custom Browsers */ 528 else { 529 browserDirInfo = new DirectoryInfo(HostingEnvironment.MapPathInternal(virtualDir)); 530 } 531 532 allBrowserSubDirectories = browserDirInfo.GetDirectories(); 533 534 int j = 0; 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]; 540 j++; 541 } 542 } 543 Array.Resize(ref browserSubDirectories, j); 544 545 for (int i = 0; i < browserSubDirectories.Length; i++) { 546 /* Recursively Into Subdirectories */ 547 FileInfo[] browserFiles = GetFilesNotHidden(browserSubDirectories[i], browserDirInfo); 548 549 if (browserFiles == null || browserFiles.Length == 0) { 550 continue; 551 } 552 BrowserTree customTree = new BrowserTree(); 553 _customTreeList.Add(customTree); 554 _customTreeNames.Add(browserSubDirectories[i].Name); 555 customBrowserFileNames = new ArrayList(); 556 557 foreach (FileInfo browserFile in browserFiles) { 558 customBrowserFileNames.Add(browserFile.FullName); 559 } 560 _customBrowserFileLists.Add(customBrowserFileNames); 561 } 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(); 566 try { 567 doc.Load(fileName); 568 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); 573 } 574 else { 575 throw new HttpParseException(SR.GetString(SR.Invalid_browser_root), null /*innerException*/, fileName, null /*sourceCode*/, 1); 576 } 577 } 578 foreach (XmlNode node in rootNode.ChildNodes) { 579 if (node.NodeType != XmlNodeType.Element) { 580 continue; 581 } 582 if (node.Name == "browser" || node.Name == "gateway") { 583 ProcessBrowserNode(node, (BrowserTree)_customTreeList[i]); 584 } 585 else { 586 HandlerBase.ThrowUnrecognizedElement(node); 587 } 588 } 589 } 590 catch (XmlException e) { 591 if (useVirtualPath) { 592 throw new HttpParseException(e.Message, null /*innerException*/, virtualDir + "/" + NoPathFileName(fileName), null /*sourceCode*/, e.LineNumber); 593 } 594 else { 595 throw new HttpParseException(e.Message, null /*innerException*/, fileName, null /*sourceCode*/, e.LineNumber); 596 } 597 } 598 catch (XmlSchemaException e) { 599 if (useVirtualPath) { 600 throw new HttpParseException(e.Message, null /*innerException*/, virtualDir + "/" + NoPathFileName(fileName), null /*sourceCode*/, e.LineNumber); 601 } 602 else { 603 throw new HttpParseException(e.Message, null /*innerException*/, fileName, null /*sourceCode*/, e.LineNumber); 604 } 605 } 606 } 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); 611 } 612 } 613 AddCustomBrowserToCollectionRecursive(BrowserDefinition bd, int depth, int index)614 internal void AddCustomBrowserToCollectionRecursive(BrowserDefinition bd, int depth, int index) { 615 if(_customBrowserDefinitionCollections[index] == null) { 616 _customBrowserDefinitionCollections[index] = new BrowserDefinitionCollection(); 617 } 618 bd.Depth = depth; 619 bd.IsDeviceNode = true; 620 ((BrowserDefinitionCollection)_customBrowserDefinitionCollections[index]).Add(bd); 621 622 foreach (BrowserDefinition childBrowser in bd.Browsers) { 623 AddCustomBrowserToCollectionRecursive(childBrowser, depth + 1, index); 624 } 625 } 626 AddBrowserToCollectionRecursive(BrowserDefinition bd, int depth)627 internal void AddBrowserToCollectionRecursive(BrowserDefinition bd, int depth) { 628 if (_browserDefinitionCollection == null) { 629 _browserDefinitionCollection = new BrowserDefinitionCollection(); 630 } 631 632 bd.Depth = depth; 633 bd.IsDeviceNode = true; 634 _browserDefinitionCollection.Add(bd); 635 636 foreach(BrowserDefinition childBrowser in bd.Browsers) { 637 AddBrowserToCollectionRecursive(childBrowser, depth + 1); 638 } 639 } 640 HandleUnRecognizedParentElement(BrowserDefinition bd, bool isDefault)641 internal virtual void HandleUnRecognizedParentElement(BrowserDefinition bd, bool isDefault) { 642 throw new ConfigurationErrorsException(SR.GetString(SR.Browser_parentID_Not_Found, bd.ParentID), bd.XmlNode); 643 } 644 GetFilesNotHidden(DirectoryInfo rootDirectory, DirectoryInfo browserDirInfo)645 private static FileInfo[] GetFilesNotHidden(DirectoryInfo rootDirectory, DirectoryInfo browserDirInfo) { 646 ArrayList fileList = new ArrayList(); 647 FileInfo[] files; 648 DirectoryInfo[] subDirectories = rootDirectory.GetDirectories("*", SearchOption.AllDirectories); 649 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); 656 } 657 } 658 return ((FileInfo [])fileList.ToArray(typeof(FileInfo))); 659 } 660 HasHiddenParent(DirectoryInfo directory, DirectoryInfo browserDirInfo)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) { 664 return true; 665 } 666 directory = directory.Parent; 667 } 668 return false; 669 } 670 671 //generate the code from the parsed BrowserDefinitionTree 672 //compile it, and install it in the gac GenerateAssembly()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]])); 680 } 681 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 688 689 //translate the BrowserDefinition tree to code 690 CSharpCodeProvider cscp = new CSharpCodeProvider(); 691 692 // namespace System.Web.BrowserCapsFactory 693 CodeCompileUnit ccu = new CodeCompileUnit(); 694 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))}); 700 701 CodeAttributeDeclaration aptca = new CodeAttributeDeclaration( 702 "System.Security.AllowPartiallyTrustedCallers"); 703 ccu.AssemblyCustomAttributes.Add(aptca); 704 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); 712 713 CodeNamespace cnamespace = new CodeNamespace("ASP"); 714 //GEN: using System; 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); 724 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); 731 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"; 737 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); 743 744 GenerateSingleProcessCall(root, method); 745 746 for (int i = 0; i < customTreeRoots.Count; i++) { 747 GenerateSingleProcessCall((BrowserDefinition)customTreeRoots[i], method); 748 } 749 750 //GEN: if(this.IsBrowserUnknown(browserCaps) == false) return; 751 CodeConditionStatement istatement = new CodeConditionStatement(); 752 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); 758 759 if(defaultRoot != null) { 760 GenerateSingleProcessCall(defaultRoot, method, "Default"); 761 } 762 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); 768 } 769 } 770 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); 776 } 777 778 foreach (DictionaryEntry entry in _defaultTree) { 779 BrowserDefinition bd = entry.Value as BrowserDefinition; 780 Debug.Assert(bd != null); 781 GenerateProcessMethod(bd, factoryType, "Default"); 782 } 783 784 GenerateOverrideMatchedHeaders(factoryType); 785 GenerateOverrideBrowserElements(factoryType); 786 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)); 789 try { 790 cscp.GenerateCodeFromCompileUnit(ccu, twriter, null); 791 } 792 finally { 793 if(twriter != null) 794 twriter.Close(); 795 } 796 797 CompilationSection compConfig = MTConfigUtil.GetCompilationAppConfig(); 798 799 bool debug = compConfig.Debug; 800 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 806 807 // Generate strong name file 808 StrongNameUtility.GenerateStrongNameFile(strongNameFile); 809 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; 816 817 try { 818 results = cscp.CompileAssemblyFromFile(compilerParameters, _browsersDirectory + "\\BrowserCapsFactory.cs"); 819 } 820 finally { 821 if (File.Exists(strongNameFile)) { 822 File.Delete(strongNameFile); 823 } 824 } 825 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); 830 } 831 } 832 833 throw new HttpCompileException(SR.GetString(SR.Browser_compile_error)); 834 } 835 836 Assembly resultAssembly = results.CompiledAssembly; 837 838 GacUtil gacutil = new GacUtil(); 839 gacutil.GacInstall(resultAssembly.Location); 840 841 SavePublicKeyTokenFile(_publicKeyTokenFile, resultAssembly.GetName().GetPublicKeyToken()); 842 } 843 SavePublicKeyTokenFile(string filename, byte[] publicKeyToken)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); 849 } 850 } 851 } 852 } 853 LoadPublicKeyTokenFromFile(string filename)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)) { 859 return null; 860 } 861 862 try { 863 using (FileStream pktStream = new FileStream(filename, FileMode.Open, FileAccess.Read)) { 864 using (StreamReader pktReader = new StreamReader(pktStream)) { 865 return pktReader.ReadLine(); 866 } 867 } 868 } 869 catch (IOException) { 870 if (HttpRuntime.HasFilePermission(filename)) { 871 throw; 872 } 873 874 // Don't throw exception if we don't have permission to the file. 875 return null; 876 } 877 finally { 878 CodeAccessPermission.RevertAssert(); 879 } 880 } 881 GenerateOverrideBrowserElements(CodeTypeDeclaration typeDeclaration)882 internal void GenerateOverrideBrowserElements(CodeTypeDeclaration typeDeclaration) { 883 884 // Don't generate the property if there's nothing to override. 885 if (_browserDefinitionCollection == null) { 886 return; 887 } 888 889 // GEN: 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 */); 893 // } 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); 900 901 method.Parameters.Add(parameter); 902 typeDeclaration.Members.Add(method); 903 904 CodeMethodReferenceExpression baseMethod = new CodeMethodReferenceExpression(new CodeBaseReferenceExpression(), _browserElementsMethodName); 905 CodeMethodInvokeExpression baseInvoke = new CodeMethodInvokeExpression(baseMethod, new CodeExpression[] { _dictionaryRefExpr }); 906 method.Statements.Add(baseInvoke); 907 908 foreach(BrowserDefinition bd in _browserDefinitionCollection) { 909 if (!bd.IsDeviceNode) 910 continue; 911 912 Debug.Assert(!(bd is GatewayDefinition)); 913 914 CodeAssignStatement cas = new CodeAssignStatement(); 915 cas.Left = new CodeIndexerExpression(_dictionaryRefExpr, new CodeExpression[] { 916 new CodePrimitiveExpression(bd.ID) 917 }); 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)}); 923 924 method.Statements.Add(cas); 925 } 926 927 for (int i = 0; i < _customTreeNames.Count; i++) { 928 foreach (BrowserDefinition bd in (BrowserDefinitionCollection)_customBrowserDefinitionCollections[i]) { 929 if (!bd.IsDeviceNode) 930 continue; 931 932 Debug.Assert(!(bd is GatewayDefinition)); 933 934 CodeAssignStatement cas = new CodeAssignStatement(); 935 cas.Left = new CodeIndexerExpression(_dictionaryRefExpr, new CodeExpression[] { 936 new CodePrimitiveExpression(bd.ID) 937 }); 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)}); 943 944 method.Statements.Add(cas); 945 } 946 } 947 } 948 GenerateOverrideMatchedHeaders(CodeTypeDeclaration typeDeclaration)949 internal void GenerateOverrideMatchedHeaders(CodeTypeDeclaration typeDeclaration) { 950 // GEN: 951 // protected override void PopulateMatchedHeaders(IDictionary dictionary) { 952 // base.PopulateMatchedHeaders(dictionary); 953 // 954 // dictionary["header0"] = null; 955 // dictionary["header1"] = null; 956 // } 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); 963 964 method.Parameters.Add(parameter); 965 typeDeclaration.Members.Add(method); 966 967 CodeMethodReferenceExpression baseMethod = new CodeMethodReferenceExpression(new CodeBaseReferenceExpression(), _matchedHeadersMethodName); 968 CodeMethodInvokeExpression baseInvoke = new CodeMethodInvokeExpression(baseMethod, new CodeExpression[] { _dictionaryRefExpr }); 969 method.Statements.Add(baseInvoke); 970 971 foreach(String header in _headers) { 972 CodeAssignStatement cas = new CodeAssignStatement(); 973 cas.Left = new CodeIndexerExpression(_dictionaryRefExpr, new CodeExpression[] { 974 new CodePrimitiveExpression(header) 975 }); 976 cas.Right = new CodePrimitiveExpression(null); 977 978 method.Statements.Add(cas); 979 } 980 } 981 GenerateProcessMethod(BrowserDefinition bd, CodeTypeDeclaration ctd)982 internal void GenerateProcessMethod(BrowserDefinition bd, CodeTypeDeclaration ctd) { 983 GenerateProcessMethod(bd, ctd, String.Empty); 984 } 985 986 //generate the xxxProcess method for an individual BrowserDefinition GenerateProcessMethod(BrowserDefinition bd, CodeTypeDeclaration ctd, string prefix)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); 997 998 bool regexWorkerGenerated = false; 999 1000 GenerateIdentificationCode(bd, cmm, ref regexWorkerGenerated); 1001 GenerateCapturesCode(bd, cmm, ref regexWorkerGenerated); 1002 GenerateSetCapabilitiesCode(bd, cmm, ref regexWorkerGenerated); 1003 GenerateSetAdaptersCode(bd, cmm); 1004 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)); 1008 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); 1013 } 1014 1015 // Generate ref gateway elements 1016 foreach (BrowserDefinition b in bd.RefGateways) { 1017 AddComment("ref gateways, parent=" + bd.ID, cmm); 1018 GenerateSingleProcessCall(b, cmm); 1019 } 1020 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); 1025 1026 //Gen: XxxProcessGateways(headers, browserCaps) ; 1027 GenerateChildProcessInvokeExpression(methodName, cmm, false); 1028 } 1029 1030 foreach(BrowserDefinition b in bd.Gateways) { 1031 AddComment("gateway, parent=" + bd.ID, cmm); 1032 GenerateSingleProcessCall(b, cmm); 1033 } 1034 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); 1040 } 1041 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); 1047 } 1048 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); 1055 } 1056 } 1057 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"); 1063 } 1064 else { 1065 GenerateSingleProcessCall(b, cmm); 1066 } 1067 } 1068 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); 1073 1074 //Gen: XxxProcessBrowsers(ignoreApplicationBrowsers, headers, browserCaps); 1075 GenerateChildProcessInvokeExpression(methodName, cmm, true); 1076 } 1077 1078 //GEN: return true; 1079 CodeMethodReturnStatement cmrs = new CodeMethodReturnStatement(new CodePrimitiveExpression(true)); 1080 cmm.Statements.Add(cmrs); 1081 1082 ctd.Members.Add(cmm); 1083 } 1084 GenerateChildProcessInvokeExpression(string methodName, CodeMemberMethod cmm, bool generateTracker)1085 private void GenerateChildProcessInvokeExpression(string methodName, CodeMemberMethod cmm, bool generateTracker) { 1086 //Gen: XxxProcessBrowsers(ignoreApplicationBrowsers, headers, browserCaps) ; 1087 CodeMethodInvokeExpression expr = new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), methodName); 1088 1089 if (generateTracker) { 1090 expr.Parameters.Add(new CodeVariableReferenceExpression(IgnoreApplicationBrowserVariableName)); 1091 } 1092 expr.Parameters.Add(new CodeVariableReferenceExpression(_headersRefName)); 1093 expr.Parameters.Add(new CodeVariableReferenceExpression(browserCapsVariable)); 1094 1095 cmm.Statements.Add(expr); 1096 } 1097 GenerateChildProcessMethod(string methodName, CodeTypeDeclaration ctd, bool generateTracker)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; 1105 1106 if (generateTracker) { 1107 cpde = new CodeParameterDeclarationExpression(typeof(bool), IgnoreApplicationBrowserVariableName); 1108 cmm.Parameters.Add(cpde); 1109 } 1110 1111 cpde = new CodeParameterDeclarationExpression(typeof(NameValueCollection), _headersRefName); 1112 cmm.Parameters.Add(cpde); 1113 cpde = new CodeParameterDeclarationExpression(typeof(HttpBrowserCapabilities), browserCapsVariable); 1114 cmm.Parameters.Add(cpde); 1115 1116 ctd.Members.Add(cmm); 1117 } 1118 GenerateRegexWorkerIfNecessary(CodeMemberMethod cmm, ref bool regexWorkerGenerated)1119 private void GenerateRegexWorkerIfNecessary(CodeMemberMethod cmm, ref bool regexWorkerGenerated) { 1120 if (regexWorkerGenerated) { 1121 return; 1122 } 1123 1124 regexWorkerGenerated = true; 1125 1126 //GEN: RegexWorker regexWorker; 1127 cmm.Statements.Add(new CodeVariableDeclarationStatement("RegexWorker", _regexWorkerRefName)); 1128 1129 //GEN: regexWorker = new RegexWorker(browserCaps); 1130 cmm.Statements.Add(new CodeAssignStatement(_regexWorkerRefExpr, new CodeObjectCreateExpression("RegexWorker", _browserCapsRefExpr))); 1131 } 1132 ReturnIfHeaderValueEmpty(CodeMemberMethod cmm, CodeVariableReferenceExpression varExpr)1133 private void ReturnIfHeaderValueEmpty(CodeMemberMethod cmm, CodeVariableReferenceExpression varExpr) { 1134 // GEN: if(String.IsNullOrEmpty(varExpr)) { 1135 // GEN: return false; 1136 // GEN: } 1137 CodeConditionStatement emptyCheckStmt = new CodeConditionStatement(); 1138 CodeMethodReferenceExpression emptyCheckMethod = new CodeMethodReferenceExpression(new CodeTypeReferenceExpression(typeof(String)), "IsNullOrEmpty"); 1139 CodeMethodInvokeExpression emptyCheckExpr = new CodeMethodInvokeExpression(emptyCheckMethod, varExpr); 1140 1141 emptyCheckStmt.Condition = emptyCheckExpr; 1142 emptyCheckStmt.TrueStatements.Add(new CodeMethodReturnStatement(new CodePrimitiveExpression(false))); 1143 cmm.Statements.Add(emptyCheckStmt); 1144 } 1145 1146 //generate part of the xxxProcess method for handling determining if the requesting 1147 //browser meets the regexes for this browser GenerateIdentificationCode(BrowserDefinition bd, CodeMemberMethod cmm, ref bool regexWorkerGenerated)1148 private void GenerateIdentificationCode(BrowserDefinition bd, CodeMemberMethod cmm, ref bool regexWorkerGenerated) { 1149 1150 //GEN: IDictionary dictionary; 1151 cmm.Statements.Add(new CodeVariableDeclarationStatement(typeof(IDictionary), _dictionaryRefName)); 1152 1153 //GEN: dictionary = browserCaps.Capabilities; 1154 CodeAssignStatement assign = new CodeAssignStatement( 1155 _dictionaryRefExpr, 1156 new CodePropertyReferenceExpression(_browserCapsRefExpr, "Capabilities") 1157 ); 1158 cmm.Statements.Add(assign); 1159 1160 bool disableOptimizedKey = false; 1161 CodeVariableReferenceExpression result = null; 1162 CodeVariableReferenceExpression headerValue = null; 1163 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; 1168 1169 // Skip matching ".*" 1170 if (matchedString.Equals(".*")) { 1171 continue; 1172 } 1173 1174 if (headerValue == null) { 1175 headerValue = GenerateVarReference(cmm, typeof(string), "headerValue"); 1176 } 1177 1178 CodeAssignStatement valueAssignment = new CodeAssignStatement(); 1179 cmm.Statements.Add(valueAssignment); 1180 valueAssignment.Left = headerValue; 1181 1182 if (((CheckPair)bd.IdHeaderChecks[i]).Header.Equals("User-Agent")) { 1183 _headers.Add(String.Empty); 1184 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") })); 1192 } 1193 else { 1194 string header = ((CheckPair)bd.IdHeaderChecks[i]).Header; 1195 _headers.Add(header); 1196 1197 //GEN: headerValue = ((String)headers["xxx"]); 1198 valueAssignment.Right = new CodeCastExpression(typeof(string), 1199 new CodeIndexerExpression( 1200 _headersRefExpr, 1201 new CodeExpression[] { new CodePrimitiveExpression(header) } 1202 ) 1203 ); 1204 1205 disableOptimizedKey = true; 1206 } 1207 1208 // Don't need to use Regex if matching . only. 1209 if (matchedString.Equals(".")) { 1210 1211 // Simply return if the header exists. 1212 ReturnIfHeaderValueEmpty(cmm, headerValue); 1213 1214 continue; 1215 } 1216 1217 if (result == null) { 1218 result = GenerateVarReference(cmm, typeof(bool), _resultVarName); 1219 } 1220 1221 GenerateRegexWorkerIfNecessary(cmm, ref regexWorkerGenerated); 1222 CodeMethodInvokeExpression cmie = new CodeMethodInvokeExpression(_regexWorkerRefExpr, _processRegexMethod); 1223 1224 cmie.Parameters.Add(headerValue); 1225 cmie.Parameters.Add(new CodePrimitiveExpression(matchedString)); 1226 1227 //GEN: result = regexWorker.ProcessRegex(headerValue, {matchedString}); 1228 assign = new CodeAssignStatement(); 1229 assign.Left = result; 1230 assign.Right = cmie; 1231 cmm.Statements.Add(assign); 1232 1233 //GEN: if(result == false) { 1234 //GEN: return false; 1235 //GEN: } 1236 CodeConditionStatement istatement = new CodeConditionStatement(); 1237 if(((CheckPair)bd.IdHeaderChecks[i]).NonMatch) { 1238 istatement.Condition = new CodeBinaryOperatorExpression(result, CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(true)); 1239 } 1240 else { 1241 istatement.Condition = new CodeBinaryOperatorExpression(result, CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(false)); 1242 } 1243 istatement.TrueStatements.Add(new CodeMethodReturnStatement(new CodePrimitiveExpression(false))); 1244 cmm.Statements.Add(istatement); 1245 } 1246 } 1247 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; 1252 1253 // Skip matching ".*" 1254 if (matchedString.Equals(".*")) { 1255 continue; 1256 } 1257 1258 if (headerValue == null) { 1259 headerValue = GenerateVarReference(cmm, typeof(string), "headerValue"); 1260 } 1261 1262 CodeAssignStatement valueAssignment = new CodeAssignStatement(); 1263 cmm.Statements.Add(valueAssignment); 1264 valueAssignment.Left = headerValue; 1265 valueAssignment.Right = (new CodeCastExpression(typeof(string), 1266 new CodeIndexerExpression( 1267 _dictionaryRefExpr, 1268 new CodeExpression[] { 1269 new CodePrimitiveExpression(((CheckPair)bd.IdCapabilityChecks[i]).Header) 1270 } 1271 ) 1272 )); 1273 1274 // Don't need to use Regex if matching . only. 1275 if (matchedString.Equals(".")) { 1276 continue; 1277 } 1278 1279 if (result == null) { 1280 result = GenerateVarReference(cmm, typeof(bool), _resultVarName); 1281 } 1282 1283 GenerateRegexWorkerIfNecessary(cmm, ref regexWorkerGenerated); 1284 //GEN: result = regexWorker.ProcessRegex((string)dictionary["xxxCapability"], "xxxRegexString"); 1285 CodeMethodInvokeExpression cmie = new CodeMethodInvokeExpression(_regexWorkerRefExpr, _processRegexMethod); 1286 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); 1293 1294 //GEN: if(result == false) { 1295 //GEN: return false; 1296 //GEN: } 1297 CodeConditionStatement istatement = new CodeConditionStatement(); 1298 if (((CheckPair)bd.IdCapabilityChecks[i]).NonMatch) { 1299 istatement.Condition = new CodeBinaryOperatorExpression(result, CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(true)); 1300 } 1301 else { 1302 istatement.Condition = new CodeBinaryOperatorExpression(result, CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(false)); 1303 } 1304 istatement.TrueStatements.Add(new CodeMethodReturnStatement(new CodePrimitiveExpression(false))); 1305 cmm.Statements.Add(istatement); 1306 } 1307 } 1308 1309 //GEN: browserCaps.DisableOptimizedCacheKey(); 1310 if (disableOptimizedKey) { 1311 CodeMethodInvokeExpression cme = new CodeMethodInvokeExpression(_browserCapsRefExpr, _disableOptimizedCacheKeyMethodName); 1312 cmm.Statements.Add(cme); 1313 } 1314 } 1315 GenerateVarReference(CodeMemberMethod cmm, Type varType, string varName)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); 1320 } 1321 1322 //generate part of the xxxProcess method for running and storing the capture regexes GenerateCapturesCode(BrowserDefinition bd, CodeMemberMethod cmm, ref bool regexWorkerGenerated)1323 private void GenerateCapturesCode(BrowserDefinition bd, CodeMemberMethod cmm, ref bool regexWorkerGenerated) { 1324 if ((bd.CaptureHeaderChecks.Count == 0) && (bd.CaptureCapabilityChecks.Count == 0)) { 1325 return; 1326 } 1327 1328 if(bd.CaptureHeaderChecks.Count > 0) { 1329 AddComment("Capture: header values", cmm); 1330 for(int i = 0; i < bd.CaptureHeaderChecks.Count; i++) { 1331 1332 string matchedString = ((CheckPair)bd.CaptureHeaderChecks[i]).MatchString; 1333 if (matchedString.Equals(".*")) { 1334 continue; 1335 } 1336 1337 GenerateRegexWorkerIfNecessary(cmm, ref regexWorkerGenerated); 1338 CodeMethodInvokeExpression cmie = new CodeMethodInvokeExpression(_regexWorkerRefExpr, _processRegexMethod); 1339 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") }))); 1345 } 1346 else { 1347 string header = ((CheckPair)bd.CaptureHeaderChecks[i]).Header; 1348 _headers.Add(header); 1349 1350 //GEN: regexWorker.ProcessRegex((string)headers["xxx"], "xxxRegexString"); 1351 cmie.Parameters.Add( 1352 new CodeCastExpression(typeof(string), 1353 new CodeIndexerExpression( 1354 _headersRefExpr, 1355 new CodeExpression[] { new CodePrimitiveExpression(header) } 1356 ) 1357 ) 1358 ); 1359 } 1360 1361 cmie.Parameters.Add(new CodePrimitiveExpression(matchedString)); 1362 cmm.Statements.Add(cmie); 1363 } 1364 } 1365 1366 if (bd.CaptureCapabilityChecks.Count > 0) { 1367 AddComment("Capture: capability values", cmm); 1368 for(int i = 0; i < bd.CaptureCapabilityChecks.Count; i++) { 1369 1370 string matchedString = ((CheckPair)bd.CaptureCapabilityChecks[i]).MatchString; 1371 if (matchedString.Equals(".*")) { 1372 continue; 1373 } 1374 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( 1381 _dictionaryRefExpr, 1382 new CodeExpression[] { new CodePrimitiveExpression(((CheckPair)bd.CaptureCapabilityChecks[i]).Header) } 1383 ) 1384 ) 1385 ); 1386 1387 cmie.Parameters.Add(new CodePrimitiveExpression(matchedString)); 1388 cmm.Statements.Add(cmie); 1389 } 1390 } 1391 } 1392 1393 //generate part of the xxxProcess method for assigning capability values GenerateSetCapabilitiesCode(BrowserDefinition bd, CodeMemberMethod cmm, ref bool regexWorkerGenerated)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; 1399 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( 1406 _dictionaryRefExpr, 1407 new CodeExpression[] { new CodePrimitiveExpression(s) } ); 1408 1409 CodePrimitiveExpression capabilityExpr = new CodePrimitiveExpression(capsString); 1410 if (RegexWorker.RefPat.Match(capsString).Success) { 1411 1412 GenerateRegexWorkerIfNecessary(cmm, ref regexWorkerGenerated); 1413 assign.Right = new CodeIndexerExpression( 1414 _regexWorkerRefExpr, 1415 new CodeExpression[] {capabilityExpr}); 1416 } 1417 else { 1418 assign.Right = capabilityExpr; 1419 } 1420 1421 cmm.Statements.Add(assign); 1422 } 1423 } 1424 1425 //generate part of the xxxProcess method for setting specific adapters for this browser GenerateSetAdaptersCode(BrowserDefinition bd, CodeMemberMethod cmm)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( 1433 cpre, 1434 new CodeExpression[] { new CodePrimitiveExpression(controlString) } 1435 ); 1436 CodeAssignStatement assignAdapter = new CodeAssignStatement(); 1437 assignAdapter.Left = indexerExpression; 1438 assignAdapter.Right = new CodePrimitiveExpression(adapterString); 1439 cmm.Statements.Add(assignAdapter); 1440 } 1441 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); 1448 } 1449 return; 1450 } 1451 AddComment(string comment, CodeMemberMethod cmm)1452 internal void AddComment(string comment, CodeMemberMethod cmm) { 1453 cmm.Statements.Add(new CodeCommentStatement(comment)); 1454 } 1455 GenerateTrackedSingleProcessCall(CodeStatementCollection stmts, BrowserDefinition bd, CodeMemberMethod cmm)1456 internal CodeStatementCollection GenerateTrackedSingleProcessCall(CodeStatementCollection stmts, BrowserDefinition bd, CodeMemberMethod cmm) { 1457 return GenerateTrackedSingleProcessCall(stmts, bd, cmm, String.Empty); 1458 } 1459 GenerateTrackedSingleProcessCall(CodeStatementCollection stmts, BrowserDefinition bd, CodeMemberMethod cmm, string prefix)1460 internal CodeStatementCollection GenerateTrackedSingleProcessCall(CodeStatementCollection stmts, BrowserDefinition bd, CodeMemberMethod cmm, string prefix) { 1461 //GEN: if (xProcess(headers, browserCaps)) { 1462 // } 1463 // else { 1464 // ... 1465 // } 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)); 1469 1470 CodeConditionStatement conditionStmt = new CodeConditionStatement(); 1471 conditionStmt.Condition = xProcess; 1472 1473 stmts.Add(conditionStmt); 1474 1475 return conditionStmt.FalseStatements; 1476 } 1477 GenerateSingleProcessCall(BrowserDefinition bd, CodeMemberMethod cmm)1478 internal void GenerateSingleProcessCall(BrowserDefinition bd, CodeMemberMethod cmm) { 1479 GenerateSingleProcessCall(bd, cmm, String.Empty); 1480 } 1481 1482 //generate code to call the xxxProcess for a given browser 1483 //and store the result in a local variable GenerateSingleProcessCall(BrowserDefinition bd, CodeMemberMethod cmm, string prefix)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); 1490 } 1491 } 1492 } 1493