3 // Marek Habersack <grendel@twistedcode.net>
5 // (C) 2009-2011 Novell, Inc. http://novell.com/
7 // Permission is hereby granted, free of charge, to any person obtaining
8 // a copy of this software and associated documentation files (the
9 // "Software"), to deal in the Software without restriction, including
10 // without limitation the rights to use, copy, modify, merge, publish,
11 // distribute, sublicense, and/or sell copies of the Software, and to
12 // permit persons to whom the Software is furnished to do so, subject to
13 // the following conditions:
15 // The above copyright notice and this permission notice shall be
16 // included in all copies or substantial portions of the Software.
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 using System.Collections.Generic;
28 using System.Collections.Concurrent;
31 using System.ComponentModel;
32 using System.Reflection;
33 using System.Security.Permissions;
34 using System.Threading;
35 using System.Web.UI.WebControls;
37 namespace System.Web.UI
39 [AspNetHostingPermissionAttribute(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
40 [AspNetHostingPermissionAttribute(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
41 public abstract class ScriptReferenceBase
43 static SplitOrderedList <ResourceCacheEntry, bool> resourceCache;
46 public bool NotifyScriptLoaded {
51 get { return _path != null ? _path : String.Empty; }
52 set { _path = value; }
55 [TypeConverterAttribute(typeof(StringArrayConverter))]
56 public string[] ResourceUICultures {
60 public ScriptMode ScriptMode {
64 internal static Assembly ThisAssembly {
68 static ScriptReferenceBase ()
70 ThisAssembly = typeof (ScriptReferenceBase).Assembly;
71 resourceCache = new SplitOrderedList <ResourceCacheEntry, bool> (EqualityComparer <ResourceCacheEntry>.Default);
74 protected ScriptReferenceBase ()
76 this.NotifyScriptLoaded = true;
77 this.ScriptMode = ScriptMode.Auto;
80 protected internal virtual bool IsAjaxFrameworkScript (ScriptManager scriptManager)
85 [Obsolete ("Use IsAjaxFrameworkScript(ScriptManager)")]
87 protected internal abstract bool IsFromSystemWebExtensions ();
88 protected internal abstract string GetUrl (ScriptManager scriptManager, bool zip);
90 // This method is an example of particularily bad coding - .NET performs NO checks
92 protected static string ReplaceExtension (string pathOrName)
94 // emulate .NET behavior
95 if (pathOrName == null)
96 throw new NullReferenceException ();
98 // We should check the length, but since .NET doesn't do that, we won't
100 return pathOrName.Substring (0, pathOrName.Length - 2) + "debug.js";
103 internal static string GetScriptName (string releaseName, bool isDebugMode, string [] supportedUICultures, Assembly assembly, out WebResourceAttribute wra)
105 if (assembly != null)
106 VerifyAssemblyContainsResource (assembly, releaseName, out wra);
110 if (!isDebugMode && (supportedUICultures == null || supportedUICultures.Length == 0))
113 if (releaseName.Length < 3 || !releaseName.EndsWith (".js", StringComparison.OrdinalIgnoreCase))
114 throw new InvalidOperationException (String.Format ("'{0}' is not a valid script path. The path must end in '.js'.", releaseName));
116 StringBuilder sb = new StringBuilder (releaseName);
119 sb.Append (".debug");
120 string culture = Thread.CurrentThread.CurrentUICulture.Name;
121 if (supportedUICultures != null && Array.IndexOf<string> (supportedUICultures, culture) >= 0)
122 sb.AppendFormat (".{0}", culture);
125 string ret = sb.ToString ();
126 WebResourceAttribute debugWra;
127 if (!CheckIfAssemblyContainsResource (assembly, ret, out debugWra))
134 static void VerifyAssemblyContainsResource (Assembly assembly, string resourceName, out WebResourceAttribute wra)
136 var rce = new ResourceCacheEntry {
138 ResourceName = resourceName
141 WebResourceAttribute attr = null;
142 if (!resourceCache.InsertOrGet ((uint)rce.GetHashCode (), rce, false, () => CheckIfAssemblyContainsResource (assembly, resourceName, out attr)))
143 throw new InvalidOperationException (String.Format ("Assembly '{0}' does not contain a Web resource with name '{1}'.",
144 assembly.FullName, resourceName));
148 static bool CheckIfAssemblyContainsResource (Assembly assembly, string resourceName, out WebResourceAttribute wra)
150 foreach (WebResourceAttribute attr in assembly.GetCustomAttributes (typeof (WebResourceAttribute), false)) {
151 if (String.Compare (resourceName, attr.WebResource, StringComparison.Ordinal) == 0) {
152 using (Stream rs = assembly.GetManifestResourceStream (resourceName)) {
154 throw new InvalidOperationException (
155 String.Format ("Assembly '{0}' contains a Web resource with name '{1}' but does not contain an embedded resource with name '{1}'.",
156 assembly.FullName, resourceName)
168 sealed class ResourceCacheEntry
170 public Assembly Assembly;
171 public string ResourceName;
173 public override int GetHashCode ()
176 if (Assembly != null)
177 ret ^= Assembly.GetHashCode ();
178 if (ResourceName != null)
179 ret ^= ResourceName.GetHashCode ();