Explorar o código

2010-05-14 Marek Habersack <[email protected]>

    	* AssemblyInfo.cs: this assembly should be signed with winfx.pub

2010-05-15  Marek Habersack  <[email protected]>

	* HttpResponse.cs: added internal Context property

	* HttpRequest.cs: added a setter to the internal Context property

	* HttpContext.cs: set request and response Context properties to
	'this' in the constructor.

2010-05-15  Marek Habersack  <[email protected]>

	* Control.cs: implemented 4.0 GetRouteUrl overloads and
	GetUniqueIDRelativeTo

2010-05-15  Marek Habersack  <[email protected]>

	* RouteValueExpressionBuilder.cs: added

	* RouteUrlExpressionBuilder.cs: implemented

svn path=/trunk/mcs/; revision=157378
Marek Habersack %!s(int64=15) %!d(string=hai) anos
pai
achega
fbbf51a5e4
Modificáronse 21 ficheiros con 989 adicións e 10 borrados
  1. 1 1
      mcs/class/System.Web.ApplicationServices/Assembly/AssemblyInfo.cs
  2. 4 0
      mcs/class/System.Web.ApplicationServices/Assembly/ChangeLog
  3. 6 0
      mcs/class/System.Web/System.Web.Compilation/ChangeLog
  4. 124 0
      mcs/class/System.Web/System.Web.Compilation/RouteUrlExpressionBuilder.cs
  5. 61 0
      mcs/class/System.Web/System.Web.Compilation/RouteValueExpressionBuilder.cs
  6. 5 0
      mcs/class/System.Web/System.Web.UI/ChangeLog
  7. 60 0
      mcs/class/System.Web/System.Web.UI/Control.cs
  8. 9 0
      mcs/class/System.Web/System.Web/ChangeLog
  9. 2 0
      mcs/class/System.Web/System.Web/HttpContext.cs
  10. 2 3
      mcs/class/System.Web/System.Web/HttpRequest.cs
  11. 7 2
      mcs/class/System.Web/System.Web/HttpResponse.cs
  12. 1 0
      mcs/class/System.Web/System.Web_standalone_test.dll.sources
  13. 1 0
      mcs/class/System.Web/System.Web_test.dll.sources
  14. 291 0
      mcs/class/System.Web/Test/System.Web.Compilation/RouteUrlExpressionBuilderTest.cs
  15. 123 0
      mcs/class/System.Web/Test/System.Web.UI/ControlTest.cs
  16. 11 4
      mcs/class/System.Web/Test/mainsoft/NunitWeb/NunitWeb/WebTest.cs
  17. 116 0
      mcs/class/System.Web/Test/standalone-tests/Control_GetUniqueIDRelativeTo.cs
  18. 32 0
      mcs/class/System.Web/Test/standalone/Control_GetUniqueIDRelativeTo/default.aspx
  19. 117 0
      mcs/class/System.Web/Test/standalone/Control_GetUniqueIDRelativeTo/default.aspx.cs
  20. 14 0
      mcs/class/System.Web/Test/standalone/Control_GetUniqueIDRelativeTo/web.config
  21. 2 0
      mcs/class/System.Web/net_4_0_System.Web.dll.sources

+ 1 - 1
mcs/class/System.Web.ApplicationServices/Assembly/AssemblyInfo.cs

@@ -62,7 +62,7 @@ using System.Runtime.Versioning;
 [assembly: RuntimeCompatibility (WrapNonExceptionThrows = true)]
 [assembly: SecurityPermission (SecurityAction.RequestMinimum, SkipVerification = true)]
 
-[assembly: AssemblyKeyFile ("../msfinal.pub")]
+[assembly: AssemblyKeyFile ("../winfx.pub")]
 [assembly: InternalsVisibleTo ("System.Web, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")]
 
 #if NET_4_0

+ 4 - 0
mcs/class/System.Web.ApplicationServices/Assembly/ChangeLog

@@ -0,0 +1,4 @@
+2010-05-14  Marek Habersack  <[email protected]>
+
+	* AssemblyInfo.cs: this assembly should be signed with winfx.pub
+

+ 6 - 0
mcs/class/System.Web/System.Web.Compilation/ChangeLog

@@ -1,3 +1,9 @@
+2010-05-15  Marek Habersack  <[email protected]>
+
+	* RouteValueExpressionBuilder.cs: added
+
+	* RouteUrlExpressionBuilder.cs: implemented
+
 2010-05-12  Marek Habersack  <[email protected]>
 
 	* BuildManager.cs: implemented a 4.0 feature - pre-application

+ 124 - 0
mcs/class/System.Web/System.Web.Compilation/RouteUrlExpressionBuilder.cs

@@ -0,0 +1,124 @@
+//
+// RouteUrlExpressionBuilder.cs
+//
+// Authors:
+//   Marek Habersack ([email protected])
+//
+// (C) 2010 Novell, Inc (http://novell.com)
+//
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.CodeDom;
+using System.Web.Routing;
+using System.Web.UI;
+
+namespace System.Web.Compilation
+{
+	public class RouteUrlExpressionBuilder : ExpressionBuilder
+	{
+		static readonly char[] expressionSplitChars = { ',' };
+		static readonly char[] keyValueSplitChars = { '=' };
+		
+		public override bool SupportsEvaluate { get { return true; } }
+		
+		public RouteUrlExpressionBuilder ()
+		{
+		}
+
+		// This method is used only from within pages that aren't compiled
+		public override object EvaluateExpression (object target, BoundPropertyEntry entry, object parsedData, ExpressionBuilderContext context)
+		{
+			if (entry == null)
+				throw new NullReferenceException (".NET emulation (entry == null)");
+
+			if (context == null)
+				throw new NullReferenceException (".NET emulation (context == null)");
+			
+			return GetRouteUrl (context.TemplateControl, entry.Expression);
+		}
+
+		public override CodeExpression GetCodeExpression (BoundPropertyEntry entry, object parsedData, ExpressionBuilderContext context)
+		{
+			if (entry == null)
+				throw new NullReferenceException (".NET emulation (entry == null)");
+			
+			var ret = new CodeMethodInvokeExpression ();
+			ret.Method = new CodeMethodReferenceExpression (new CodeTypeReferenceExpression (typeof (RouteUrlExpressionBuilder)), "GetRouteUrl");
+
+			CodeExpressionCollection parameters = ret.Parameters;
+			parameters.Add (new CodeThisReferenceExpression ());
+			parameters.Add (new CodePrimitiveExpression (entry.Expression));
+
+			return ret;
+		}
+
+		public static string GetRouteUrl (Control control, string expression)
+		{
+			if (control == null)
+				throw new ArgumentNullException ("control");
+			
+			string routeName;
+			var rvd = new RouteValueDictionary ();
+			
+			if (!TryParseRouteExpression (expression, rvd, out routeName))
+				throw new InvalidOperationException ("Invalid expression, RouteUrlExpressionBuilder expects a string with format: RouteName=route,Key1=Value1,Key2=Value2");
+
+			return control.GetRouteUrl (routeName, rvd);
+		}
+
+		public static bool TryParseRouteExpression (string expression, RouteValueDictionary routeValues, out string routeName)
+		{
+			routeName = null;
+			if (String.IsNullOrEmpty (expression))
+				return false;
+
+			if (routeValues == null)
+				throw new NullReferenceException (".NET emulation (routeValues == null)");
+
+			string[] parts = expression.Split (expressionSplitChars);
+			foreach (string part in parts) {
+				string[] keyval = part.Split (keyValueSplitChars);
+				if (keyval.Length != 2)
+					return false;
+
+				string key = keyval [0].Trim ();
+				if (key == String.Empty)
+					return false;
+
+				if (String.Compare (key, "routename", StringComparison.OrdinalIgnoreCase) == 0) {
+					routeName = keyval [1].Trim ();
+					continue;
+				}
+				
+				if (routeValues.ContainsKey (key))
+					routeValues [key] = keyval [1].Trim ();
+				else
+					routeValues.Add (key, keyval [1].Trim ());
+			}
+			
+			return true;
+		}
+	}
+}
+

+ 61 - 0
mcs/class/System.Web/System.Web.Compilation/RouteValueExpressionBuilder.cs

@@ -0,0 +1,61 @@
+//
+// RouteValueExpressionBuilder.cs
+//
+// Authors:
+//   Marek Habersack ([email protected])
+//
+// (C) 2010 Novell, Inc (http://novell.com)
+//
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.CodeDom;
+using System.Web.UI;
+
+namespace System.Web.Compilation
+{
+	public class RouteValueExpressionBuilder : ExpressionBuilder
+	{
+		public override bool SupportsEvaluate { get { return true; } }
+		
+		public RouteValueExpressionBuilder ()
+		{
+		}
+
+		// This method is used only from within pages that aren't compiled
+		public override object EvaluateExpression (object target, BoundPropertyEntry entry, object parsedData, ExpressionBuilderContext context)
+		{
+			throw new NotImplementedException ();
+		}
+
+		public override CodeExpression GetCodeExpression (BoundPropertyEntry entry, object parsedData, ExpressionBuilderContext context)
+		{
+			throw new NotImplementedException ();
+		}
+
+		public static object GetRouteValue (Page page, string key, Type controlType, string propertyName)
+		{
+			throw new NotImplementedException ();
+		}
+	}
+}

+ 5 - 0
mcs/class/System.Web/System.Web.UI/ChangeLog

@@ -1,3 +1,8 @@
+2010-05-15  Marek Habersack  <[email protected]>
+
+	* Control.cs: implemented 4.0 GetRouteUrl overloads and
+	GetUniqueIDRelativeTo
+
 2010-05-05  Marek Habersack  <[email protected]>
 
 	* CollectionBuilder.cs: if a type has more than on indexer, check

+ 60 - 0
mcs/class/System.Web/System.Web.UI/Control.cs

@@ -46,6 +46,10 @@ using System.Web;
 using System.Web.Util;
 using System.Web.UI.Adapters;
 
+#if NET_4_0
+using System.Web.Routing;
+#endif
+
 namespace System.Web.UI
 {
 	// CAS
@@ -1823,5 +1827,61 @@ namespace System.Web.UI
 				return false;
 			}
 		}
+#if NET_4_0
+		public string GetRouteUrl (object routeParameters)
+		{
+			return GetRouteUrl (null, new RouteValueDictionary (routeParameters));
+		}
+
+		public string GetRouteUrl (RouteValueDictionary routeParameters)
+		{
+			return GetRouteUrl (null, routeParameters);
+		}
+
+		public string GetRouteUrl (string routeName, object routeParameters)
+		{
+			return GetRouteUrl (routeName, new RouteValueDictionary (routeParameters));
+		}
+
+		public string GetRouteUrl (string routeName, RouteValueDictionary routeParameters)
+		{
+			HttpContext ctx = Context ?? HttpContext.Current;
+			HttpRequest req = ctx != null ? ctx.Request : null;
+
+			if (req == null)
+				return null;
+
+			VirtualPathData vpd = RouteTable.Routes.GetVirtualPath (req.RequestContext, routeName, routeParameters);
+			if (vpd == null)
+				return null;
+
+			return vpd.VirtualPath;
+		}
+
+		public string GetUniqueIDRelativeTo (Control control)
+		{
+			if (control == null)
+				throw new ArgumentNullException ("control");
+
+			Control parent = this;
+			Page page = Page;
+			Control namingContainer = control.NamingContainer;
+			
+			if (namingContainer != null)
+				while (parent != null && parent != namingContainer)
+					parent = parent.Parent;
+
+			if (parent != namingContainer)
+				throw new InvalidOperationException (
+					String.Format ("This control is not a descendant of the NamingContainer of '{0}'", control.UniqueID)
+				);
+
+			int idx = control.UniqueID.LastIndexOf (IdSeparator);
+			if (idx < 0)
+				return UniqueID;
+
+			return UniqueID.Substring (idx + 1);
+		}
+#endif
 	}
 }

+ 9 - 0
mcs/class/System.Web/System.Web/ChangeLog

@@ -1,3 +1,12 @@
+2010-05-15  Marek Habersack  <[email protected]>
+
+	* HttpResponse.cs: added internal Context property
+
+	* HttpRequest.cs: added a setter to the internal Context property
+
+	* HttpContext.cs: set request and response Context properties to
+	'this' in the constructor.
+
 2010-05-12  Marek Habersack  <[email protected]>
 
 	* HttpApplicationFactory.cs: when initializing new application

+ 2 - 0
mcs/class/System.Web/System.Web/HttpContext.cs

@@ -119,6 +119,8 @@ namespace System.Web
 		{
 			this.request = request;
 			this.response = response;
+			this.request.Context = this;
+			this.response.Context = this;
 #if NET_4_0
 			SessionStateBehavior = SessionStateBehavior.Default;
 #endif

+ 2 - 3
mcs/class/System.Web/System.Web/HttpRequest.cs

@@ -1517,9 +1517,8 @@ namespace System.Web
 		}
 
 		internal HttpContext Context {
-			get {
-				return context;
-			}
+			get { return context; }
+			set { context = value; }
 		}
 
 		static void ValidateNameValueCollection (string name, NameValueCollection coll)

+ 7 - 2
mcs/class/System.Web/System.Web/HttpResponse.cs

@@ -151,7 +151,12 @@ namespace System.Web
 				return version_header;
 			}
 		}
-		
+
+		internal HttpContext Context {
+			get { return context; }
+			set { context = value; }
+		}
+			
 		internal string[] FileDependencies {
 			get {
 				if (fileDependencies == null || fileDependencies.Count == 0)
@@ -537,7 +542,7 @@ namespace System.Web
 		
 		public string ApplyAppPathModifier (string virtualPath)
 		{
-			if (virtualPath == null)
+			if (virtualPath == null || context == null)
 				return null;
 		
 			if (virtualPath.Length == 0)

+ 1 - 0
mcs/class/System.Web/System.Web_standalone_test.dll.sources

@@ -7,4 +7,5 @@ Test/standalone-tests/SiteMapDuplicateEntries_Bug570194.cs
 Test/standalone-tests/Unhandled_Exception_Global_Asax.cs
 Test/standalone-tests/BuildManagerCacheFiles.cs
 Test/standalone-tests/ApplicationPreStartMethods.cs
+Test/standalone-tests/Control_GetUniqueIDRelativeTo.cs
 

+ 1 - 0
mcs/class/System.Web/System.Web_test.dll.sources

@@ -67,6 +67,7 @@ System.Web.Compilation/ClientBuildManagerParameterTest.cs
 System.Web.Compilation/TemplateControlCompilerTest.cs
 System.Web.Compilation/AppSettingsExpressionBuilderTest.cs
 System.Web.Compilation/AppResourcesCompilerTest.cs
+System.Web.Compilation/RouteUrlExpressionBuilderTest.cs
 System.Web.Configuration/AnonymousIdentificationSectionTest.cs
 System.Web.Configuration/AssemblyCollectionTest.cs
 System.Web.Configuration/AssemblyInfoTest.cs

+ 291 - 0
mcs/class/System.Web/Test/System.Web.Compilation/RouteUrlExpressionBuilderTest.cs

@@ -0,0 +1,291 @@
+//
+// RouteUrlExpressionBuilder.cs
+//
+// Authors:
+//      Marek Habersack <[email protected]>
+//
+// Copyright (C) 2010 Novell, Inc. (http://novell.com/)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+#if NET_4_0
+using System;
+using System.CodeDom;
+using System.IO;
+using System.Reflection;
+using System.Web;
+using System.Web.Compilation;
+using System.Web.UI;
+using System.Web.UI.WebControls;
+using System.Web.Routing;
+
+using NUnit.Framework;
+
+using MonoTests.SystemWeb.Framework;
+using MonoTests.stand_alone.WebHarness;
+using MonoTests.Common;
+
+namespace MonoTests.System.Web.Compilation
+{
+#if NET_4_0
+	[TestFixture]
+	public class RouteUrlExpressionBuilderTest
+	{
+		[Test]
+		public void EvaluateExpression ()
+		{
+			RouteTable.Routes.Clear ();
+			RouteTable.Routes.Add (new Route ("{foo}-foo", new PageRouteHandler ("~/default.aspx")));
+			RouteTable.Routes.Add ("bar1", new Route ("{bar}-foo", new PageRouteHandler ("~/bar.aspx")));
+			RouteTable.Routes.Add ("bar2", new Route ("some-{bar}", new PageRouteHandler ("~/some-bar.aspx")));
+
+			var bldr = new RouteUrlExpressionBuilder ();
+			var entry = CreatePropertyEntry ("foo=test", "RouteUrl");
+			var context = new ExpressionBuilderContext (new FakePage ());
+			object obj = bldr.EvaluateExpression (null, entry, null, context);
+			Assert.AreEqual ("/test-foo", obj, "#A1");
+
+			entry = CreatePropertyEntry ("bar=test", "RouteUrl");
+			obj = bldr.EvaluateExpression (null, entry, null, context);
+			Assert.AreEqual ("/test-foo", obj, "#A2-1");
+
+			entry = CreatePropertyEntry ("bar=test,routename=bar2", "RouteUrl");
+			obj = bldr.EvaluateExpression (null, entry, null, context);
+			Assert.AreEqual ("/some-test", obj, "#A2-2");
+
+			entry = CreatePropertyEntry ("bar=test,routename=bar1", "RouteUrl");
+			obj = bldr.EvaluateExpression (null, entry, null, context);
+			Assert.AreEqual ("/test-foo", obj, "#A2-3");
+
+			entry = CreatePropertyEntry ("bar=test,routename=noroute", "RouteUrl");
+			try {
+				obj = bldr.EvaluateExpression (null, entry, null, context);
+				Assert.Fail ("#A3");
+			} catch (ArgumentException) {
+				// success
+			}
+
+			entry = CreatePropertyEntry ("nosuchparam=test", "RouteUrl");
+			obj = bldr.EvaluateExpression (null, entry, null, context);
+			Assert.IsNull (obj, "#A4");
+
+			AssertExtensions.Throws<NullReferenceException> (() => {
+				bldr.EvaluateExpression (null, null, null, context);
+			}, "#A5-1");
+
+			AssertExtensions.Throws<NullReferenceException> (() => {
+				bldr.EvaluateExpression (null, entry, null, null);
+			}, "#A5-2");
+		}
+
+		BoundPropertyEntry CreatePropertyEntry (string expression, string expressionPrefix)
+		{
+			var entry = Activator.CreateInstance (typeof (BoundPropertyEntry), true) as BoundPropertyEntry;
+			entry.Expression = expression;
+			entry.ExpressionPrefix = expressionPrefix;
+
+			return entry;
+		}
+
+		[Test]
+		public void GetRouteUrl ()
+		{
+			AssertExtensions.Throws<ArgumentNullException> (() => {
+				RouteUrlExpressionBuilder.GetRouteUrl (null, "bar=test");
+			}, "#A1-1");
+
+			var t = new WebTest (PageInvoker.CreateOnLoad (GetRouteUrl_Load));
+			t.Run ();
+		}
+
+		public static void GetRouteUrl_Load (Page p)
+		{
+			RouteTable.Routes.Clear ();
+			RouteTable.Routes.Add (new Route ("{foo}-foo", new PageRouteHandler ("~/default.aspx")));
+			RouteTable.Routes.Add ("bar1", new Route ("{bar}-foo", new PageRouteHandler ("~/bar.aspx")));
+
+			var ctl = new Control ();
+			string url = RouteUrlExpressionBuilder.GetRouteUrl (new Control (), "foobar=test");
+			Assert.IsNull (url, "#A2");
+
+			url = RouteUrlExpressionBuilder.GetRouteUrl (new Control (), "bar=test");
+			Assert.IsNotNull (url, "#A3-1");
+			Assert.AreEqual ("/NunitWeb/test-foo", url, "#A3-2");
+
+			url = RouteUrlExpressionBuilder.GetRouteUrl (new Control (), "routename=bar1,bar=test");
+			Assert.IsNotNull (url, "#A4-1");
+			Assert.AreEqual ("/NunitWeb/test-foo", url, "#A4-2");
+
+			url = RouteUrlExpressionBuilder.GetRouteUrl (new Control (), "ROUTEname=bar1,bar=test");
+			Assert.IsNotNull (url, "#A5-1");
+			Assert.AreEqual ("/NunitWeb/test-foo", url, "#A5-2");
+
+			url = RouteUrlExpressionBuilder.GetRouteUrl (new Control (), "   routename  =   bar1,    bar  =    test     ");
+			Assert.IsNotNull (url, "#A6-1");
+			Assert.AreEqual ("/NunitWeb/test-foo", url, "#A6-2");
+
+			AssertExtensions.Throws<InvalidOperationException> (() => {
+				url = RouteUrlExpressionBuilder.GetRouteUrl (new Control (), "routename");
+			}, "#A7-1");
+
+			AssertExtensions.Throws<InvalidOperationException> (() => {
+				url = RouteUrlExpressionBuilder.GetRouteUrl (new Control (), String.Empty);
+			}, "#A7-2");
+
+			AssertExtensions.Throws<InvalidOperationException> (() => {
+				url = RouteUrlExpressionBuilder.GetRouteUrl (new Control (), null);
+			}, "#A7-2");
+		}
+
+		[Test]
+		public void TryParseRouteExpression ()
+		{
+			string routeName;
+			var rvd = new RouteValueDictionary ();
+			
+			Assert.IsFalse (RouteUrlExpressionBuilder.TryParseRouteExpression (null, rvd, out routeName), "#A1-1");
+			Assert.IsFalse (RouteUrlExpressionBuilder.TryParseRouteExpression (String.Empty, rvd, out routeName), "#A1-2");
+			Assert.IsFalse (RouteUrlExpressionBuilder.TryParseRouteExpression ("route", rvd, out routeName), "#A1-3");
+			Assert.IsTrue (RouteUrlExpressionBuilder.TryParseRouteExpression ("route=", rvd, out routeName), "#A1-4");
+			Assert.AreEqual (1, rvd.Count, "#A1-4-1");
+			Assert.AreEqual (String.Empty, rvd ["route"], "#A1-4-2");
+
+			AssertExtensions.Throws<NullReferenceException> (() => {
+				RouteUrlExpressionBuilder.TryParseRouteExpression ("foo=bar", null, out routeName);
+			}, "#A1-5");
+
+			rvd.Clear ();
+			Assert.IsTrue (RouteUrlExpressionBuilder.TryParseRouteExpression ("foo=bar", rvd, out routeName), "#A2-1");
+			Assert.AreEqual (1, rvd.Count, "#A2-2");
+			Assert.AreEqual ("bar", rvd ["foo"], "#A2-3");
+			Assert.IsNull (routeName, "#A2-4");
+
+			rvd.Clear ();
+			Assert.IsTrue (RouteUrlExpressionBuilder.TryParseRouteExpression ("routeName=route,foo=bar,baz=zonk", rvd, out routeName), "#A3-1");
+			Assert.AreEqual (2, rvd.Count, "#A3-2");
+			Assert.AreEqual ("bar", rvd ["foo"], "#A3-3");
+			Assert.AreEqual ("zonk", rvd ["baz"], "#A3-3");
+			Assert.IsNotNull (routeName, "#A3-5");
+			Assert.AreEqual ("route", routeName, "#A3-6");
+
+			rvd.Clear ();
+			Assert.IsTrue (RouteUrlExpressionBuilder.TryParseRouteExpression ("  rOUteName=route      ,  foo=bar\t,   baz   =\t  zonk   \t ", rvd, out routeName), "#A4-1");
+			Assert.AreEqual (2, rvd.Count, "#A4-2");
+			Assert.AreEqual ("bar", rvd ["foo"], "#A4-3");
+			Assert.AreEqual ("zonk", rvd ["baz"], "#A4-3");
+			Assert.IsNotNull (routeName, "#A4-5");
+			Assert.AreEqual ("route", routeName, "#A4-6");
+
+			rvd.Clear ();
+			Assert.IsFalse (RouteUrlExpressionBuilder.TryParseRouteExpression ("foo=bar,route,baz=zonk", rvd, out routeName), "#A5-1");
+			Assert.AreEqual (1, rvd.Count, "#A5-2");
+			Assert.AreEqual ("bar", rvd ["foo"], "#A5-3");
+
+			rvd.Clear ();
+			Assert.IsFalse (RouteUrlExpressionBuilder.TryParseRouteExpression ("foo=bar,route==,baz=zonk", rvd, out routeName), "#A6-1");
+			Assert.AreEqual (1, rvd.Count, "#A6-2");
+			Assert.AreEqual ("bar", rvd ["foo"], "#A6-3");
+
+			rvd.Clear ();
+			Assert.IsFalse (RouteUrlExpressionBuilder.TryParseRouteExpression ("route==", rvd, out routeName), "#A7-1");
+			Assert.AreEqual (0, rvd.Count, "#A7-2");
+
+			rvd.Clear ();
+			Assert.IsFalse (RouteUrlExpressionBuilder.TryParseRouteExpression ("route==stuff", rvd, out routeName), "#A8-1");
+			Assert.AreEqual (0, rvd.Count, "#A8-2");
+
+			rvd.Clear ();
+			Assert.IsTrue (RouteUrlExpressionBuilder.TryParseRouteExpression ("route=stuff1,route=stuff2", rvd, out routeName), "#A9-1");
+			Assert.AreEqual (1, rvd.Count, "#A9-2");
+			Assert.AreEqual ("stuff2", rvd ["route"], "#A9-3");
+
+			rvd.Clear ();
+			Assert.IsFalse (RouteUrlExpressionBuilder.TryParseRouteExpression ("=stuff", rvd, out routeName), "#A10-1");
+			Assert.AreEqual (0, rvd.Count, "#A10-2");
+
+			rvd.Clear ();
+			Assert.IsTrue (RouteUrlExpressionBuilder.TryParseRouteExpression ("routeName=route,routename=route2,foo=bar,baz=zonk", rvd, out routeName), "#A11-1");
+			Assert.AreEqual (2, rvd.Count, "#A11-2");
+			Assert.AreEqual ("bar", rvd ["foo"], "#A11-3");
+			Assert.AreEqual ("zonk", rvd ["baz"], "#A11-3");
+			Assert.IsNotNull (routeName, "#A11-5");
+			Assert.AreEqual ("route2", routeName, "#A11-6");
+		}
+
+		[Test]
+		public void GetCodeExpression ()
+		{
+			var bldr = new RouteUrlExpressionBuilder ();
+			var entry = CreatePropertyEntry ("foo=test", "RouteUrl");
+			var context = new ExpressionBuilderContext (new FakePage ());
+			CodeExpression expr;
+
+			AssertExtensions.Throws<NullReferenceException> (() => {
+				expr = bldr.GetCodeExpression (null, "data", context);
+			}, "#A1-1");
+
+			expr = bldr.GetCodeExpression (entry, null, context);
+			Assert.IsNotNull (expr, "#A2");
+
+			expr = bldr.GetCodeExpression (entry, "data", null);
+			Assert.IsNotNull (expr, "#A3");
+
+			expr = bldr.GetCodeExpression (entry, null, null);
+			Assert.IsNotNull (expr, "#A4-1");
+			Assert.AreEqual (typeof (CodeMethodInvokeExpression), expr.GetType (), "#A4-2");
+
+			var invoke = expr as CodeMethodInvokeExpression;
+			Assert.AreEqual (typeof (CodeTypeReferenceExpression), invoke.Method.TargetObject.GetType (), "#A4-3");
+
+			var tref = invoke.Method.TargetObject as CodeTypeReferenceExpression;
+			Assert.AreEqual ("System.Web.Compilation.RouteUrlExpressionBuilder", tref.Type.BaseType, "#A4-4");
+			Assert.AreEqual ("GetRouteUrl", invoke.Method.MethodName, "#A4-5");
+			
+			Assert.AreEqual (2, invoke.Parameters.Count, "#A5-1");
+			Assert.AreEqual (typeof (CodeThisReferenceExpression), invoke.Parameters [0].GetType (), "#A5-2");
+			Assert.AreEqual (typeof (CodePrimitiveExpression), invoke.Parameters [1].GetType (), "#A5-3");
+
+			var pex = invoke.Parameters [1] as CodePrimitiveExpression;
+			Assert.AreEqual ("foo=test", pex.Value, "#A5-4");
+		}
+	}
+
+	class FakePage : Page
+	{
+		private HttpContext ctx;
+
+		// don't call base class (so _context is never set to a non-null value)
+		protected override HttpContext Context
+		{
+			get
+			{
+				if (ctx == null) {
+					ctx = new HttpContext (
+						new HttpRequest ("default.aspx", "http://mono-project.com/", "q=1&q2=2"),
+						new HttpResponse (new StringWriter ())
+						);
+				}
+				return ctx;
+			}
+		}
+	}
+#endif
+}
+#endif

+ 123 - 0
mcs/class/System.Web/Test/System.Web.UI/ControlTest.cs

@@ -40,11 +40,16 @@ using System.Web.UI;
 using System.Web.UI.WebControls;
 using MonoTests.SystemWeb.Framework;
 using MonoTests.stand_alone.WebHarness;
+using MonoTests.Common;
 
 #if NET_2_0
 using System.Web.UI.Adapters;
 #endif
 
+#if NET_4_0
+using System.Web.Routing;
+#endif
+
 namespace MonoTests.System.Web.UI
 {
 	[TestFixture]
@@ -1014,7 +1019,125 @@ namespace MonoTests.System.Web.UI
 		}
 
 #endif
+#if NET_4_0
+		[Test]
+		public void GetRouteUrl_Object ()
+		{
+			var t = new WebTest (PageInvoker.CreateOnLoad (GetRouteUrl_Object_Load));
+			t.Run ();
+		}
+
+		public static void GetRouteUrl_Object_Load (Page p)
+		{
+			RouteTable.Routes.Clear ();
+
+			var ctl = new Control ();
+			object obj = new { foo = "one", bar = "two" };
+			string path = ctl.GetRouteUrl (obj);
+			Assert.IsNull (path, "#A1");
+
+			RouteTable.Routes.Add (new Route ("{foo}-{bar}", new PageRouteHandler ("~/default.aspx")));
+			path = ctl.GetRouteUrl (obj);
+			Assert.IsNotNull (path, "#A2-1");
+			Assert.AreEqual ("/NunitWeb/one-two", path, "#A2-2");
+
+			path = ctl.GetRouteUrl ((object)null);
+			Assert.IsNull (path, "#A3");
+		}
 
+		[Test]
+		public void GetRouteUrl_RouteValueDictionary ()
+		{
+			var t = new WebTest (PageInvoker.CreateOnLoad (GetRouteUrl_RouteValueDictionary_Load));
+			t.Run ();
+		}
+
+		public static void GetRouteUrl_RouteValueDictionary_Load (Page p)
+		{
+			RouteTable.Routes.Clear ();
+
+			var ctl = new Control ();
+			var rvd = new RouteValueDictionary {
+				{"foo", "one"},
+				{"bar", "two"}
+			};
+			string path = ctl.GetRouteUrl (rvd);
+			Assert.IsNull (path, "#A1");
+
+			RouteTable.Routes.Add (new Route ("{foo}-{bar}", new PageRouteHandler ("~/default.aspx")));
+			path = ctl.GetRouteUrl (rvd);
+			Assert.IsNotNull (path, "#A2-1");
+			Assert.AreEqual ("/NunitWeb/one-two", path, "#A2-2");
+
+			path = ctl.GetRouteUrl ((RouteValueDictionary) null);
+			Assert.IsNull (path, "#A3");
+		}
+
+		[Test]
+		public void GetRouteUrl_String_Object ()
+		{
+			var t = new WebTest (PageInvoker.CreateOnLoad (GetRouteUrl_String_Object_Load));
+			t.Run ();
+		}
+
+		public static void GetRouteUrl_String_Object_Load (Page p)
+		{
+			RouteTable.Routes.Clear ();
+
+			var ctl = new Control ();
+			object obj = new { foo = "one", bar = "two" };
+			string path;
+			AssertExtensions.Throws<ArgumentException> (() => {
+				path = ctl.GetRouteUrl ("myroute1", obj);
+			}, "#A1");
+
+			RouteTable.Routes.Add (new Route ("{foo}-{bar}", new PageRouteHandler ("~/default.aspx")));
+			RouteTable.Routes.Add ("myroute1", new Route ("{bar}-{foo}", new PageRouteHandler ("~/default.aspx")));
+			path = ctl.GetRouteUrl ("myroute1", obj);
+			Assert.IsNotNull (path, "#A2-1");
+			Assert.AreEqual ("/NunitWeb/two-one", path, "#A2-2");
+
+			path = ctl.GetRouteUrl ("myroute1", (object) null);
+			Assert.IsNull (path, "#A3");
+		}
+
+		[Test]
+		public void GetRouteUrl_String_RouteValueDictionary ()
+		{
+			var t = new WebTest (PageInvoker.CreateOnLoad (GetRouteUrl_String_RouteValueDictionary_Load));
+			t.Run ();
+		}
+
+		public static void GetRouteUrl_String_RouteValueDictionary_Load (Page p)
+		{
+			RouteTable.Routes.Clear ();
+
+			var ctl = new Control ();
+			var rvd = new RouteValueDictionary {
+				{"foo", "one"},
+				{"bar", "two"}
+			};
+			string path;
+			AssertExtensions.Throws<ArgumentException> (() => {
+				path = ctl.GetRouteUrl ("myroute", rvd);
+			}, "#A1");
+
+			RouteTable.Routes.Add (new Route ("{foo}-{bar}", new PageRouteHandler ("~/default.aspx")));
+			RouteTable.Routes.Add ("myroute", new Route ("{bar}-{foo}", new PageRouteHandler ("~/default.aspx")));
+			path = ctl.GetRouteUrl ("myroute", rvd);
+			Assert.IsNotNull (path, "#A2-1");
+			Assert.AreEqual ("/NunitWeb/two-one", path, "#A2-2");
+
+			path = ctl.GetRouteUrl ("myroute", (RouteValueDictionary) null);
+			Assert.IsNull (path, "#A3-1");
+
+			path = ctl.GetRouteUrl (null, (RouteValueDictionary) null);
+			Assert.IsNull (path, "#A3-2");
+
+			path = ctl.GetRouteUrl (String.Empty, (RouteValueDictionary) null);
+			Assert.IsNull (path, "#A3-3");
+		}
+#endif
 		#region helpcalsses
 #if NET_2_0
 		class ControlWithState : Control

+ 11 - 4
mcs/class/System.Web/Test/mainsoft/NunitWeb/NunitWeb/WebTest.cs

@@ -123,8 +123,9 @@ namespace MonoTests.SystemWeb.Framework
 		/// <seealso cref="MonoTests.SystemWeb.Framework.Response.Body"/>
 		public string Run ()
 		{
+#if !DOTNET
 			SystemWebTestShim.BuildManager.SuppressDebugModeMessages ();
-
+#endif
 			if (Request.Url == null)
 				Request.Url = Invoker.GetDefaultUrl ();
 			_unloadHandler.StartingRequest();
@@ -330,8 +331,10 @@ namespace MonoTests.SystemWeb.Framework
 				
 				if (!resource.StartsWith (namePrefix))
 					continue;
-				
-				CopyResource (type, resource, Path.Combine (targetDir, resource.Substring (namePrefix.Length)));
+				 
+				// The Replace part is for VisualStudio which compiles .resx files despite them being marked as
+				// embedded resources, which breaks the tests.
+				CopyResource (type, resource, Path.Combine (targetDir, resource.Substring (namePrefix.Length).Replace (".remove_extension", String.Empty)));
 			}
 		}
 		
@@ -597,11 +600,15 @@ namespace MonoTests.SystemWeb.Framework
 			CopyResource (myself, "My.ashx", "My.ashx");
 			CopyResource (myself, "Global.asax", "Global.asax");
 #if NET_2_0
-#if INSIDE_SYSTEM_WEB
+#if INSIDE_SYSTEM_WEB || DOTNET
 			CopyPrefixedResources (myself, "App_GlobalResources/", "App_GlobalResources");
 			CopyPrefixedResources (myself, "App_Code/", "App_Code");
 #endif
+#if DOTNET
+			CopyResource (myself, "Web.config", "Web.config");
+#else
 			CopyResource (myself, "Web.mono.config", "Web.config");
+#endif
 #else
 			CopyResource (myself, "Web.mono.config.1.1", "Web.config");
 #endif

+ 116 - 0
mcs/class/System.Web/Test/standalone-tests/Control_GetUniqueIDRelativeTo.cs

@@ -0,0 +1,116 @@
+//
+// Authors:
+//   Marek Habersack ([email protected])
+//
+// (C) 2010 Novell, Inc http://novell.com/
+//
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+#if NET_4_0
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Web.Util;
+
+using StandAloneRunnerSupport;
+using StandAloneTests;
+
+using NUnit.Framework;
+
+
+namespace StandAloneTests.Control_GetUniqueIDRelativeTo
+{
+	[TestCase ("Control_GetUniqueIDRelativeTo", "Control.GetUniqueIDRelativeTo tests")]
+	public sealed class Control_GetUniqueIDRelativeTo : ITestCase
+	{
+		public string PhysicalPath {
+			get { return Path.Combine (Consts.BasePhysicalDir, "Control_GetUniqueIDRelativeTo"); }
+		}
+		
+		public string VirtualPath  {
+			get { return "/"; }
+		}
+
+		public bool SetUp (List <TestRunItem> runItems)
+		{
+			runItems.Add (new TestRunItem ("default.aspx", Default_Aspx));
+			
+			return true;
+		}
+		
+		void Default_Aspx (string result, TestRunItem runItem)
+		{
+			string originalHtml = @"<pre id=""log"">Page; Relative to: null; Result: exception System.ArgumentNullException (expected)
+A control; Relative to: __Page; Result: exception System.InvalidOperationException (expected)
+TextBox; Relative to: __Page; Result: exception System.InvalidOperationException (expected)
+Item: 0; Relative to: repeater1$ctl00; Result: &#39;ctl00$label1&#39;
+Item: 0; Relative to: repeater1; Result: &#39;repeater1$ctl00$label1&#39;
+Item: 0; Relative to: __Page; Result: exception System.InvalidOperationException (expected)
+	Item: 0; Relative to: repeater1$ctl00$innerRepeater1$ctl00; Result: &#39;ctl00$innerLabel1&#39;
+	Item: 0; Relative to: repeater1; Result: &#39;repeater1$ctl00$innerRepeater1$ctl00$innerLabel1&#39;
+	Item: 0; Relative to: repeater1$ctl00$innerRepeater1; Result: &#39;innerRepeater1$ctl00$innerLabel1&#39;
+	Item: 0; Relative to: __Page; Result: exception System.InvalidOperationException (expected)
+	Item: 1; Relative to: repeater1$ctl00$innerRepeater1$ctl01; Result: &#39;ctl01$innerLabel1&#39;
+	Item: 1; Relative to: repeater1; Result: &#39;repeater1$ctl00$innerRepeater1$ctl01$innerLabel1&#39;
+	Item: 1; Relative to: repeater1$ctl00$innerRepeater1; Result: &#39;innerRepeater1$ctl01$innerLabel1&#39;
+	Item: 1; Relative to: __Page; Result: exception System.InvalidOperationException (expected)
+	Item: 2; Relative to: repeater1$ctl00$innerRepeater1$ctl02; Result: &#39;ctl02$innerLabel1&#39;
+	Item: 2; Relative to: repeater1; Result: &#39;repeater1$ctl00$innerRepeater1$ctl02$innerLabel1&#39;
+	Item: 2; Relative to: repeater1$ctl00$innerRepeater1; Result: &#39;innerRepeater1$ctl02$innerLabel1&#39;
+	Item: 2; Relative to: __Page; Result: exception System.InvalidOperationException (expected)
+Item: 1; Relative to: repeater1$ctl02; Result: &#39;ctl02$label1&#39;
+Item: 1; Relative to: repeater1; Result: &#39;repeater1$ctl02$label1&#39;
+Item: 1; Relative to: __Page; Result: exception System.InvalidOperationException (expected)
+	Item: 0; Relative to: repeater1$ctl02$innerRepeater1$ctl00; Result: &#39;ctl00$innerLabel1&#39;
+	Item: 0; Relative to: repeater1; Result: &#39;repeater1$ctl02$innerRepeater1$ctl00$innerLabel1&#39;
+	Item: 0; Relative to: repeater1$ctl02$innerRepeater1; Result: &#39;innerRepeater1$ctl00$innerLabel1&#39;
+	Item: 0; Relative to: __Page; Result: exception System.InvalidOperationException (expected)
+	Item: 1; Relative to: repeater1$ctl02$innerRepeater1$ctl01; Result: &#39;ctl01$innerLabel1&#39;
+	Item: 1; Relative to: repeater1; Result: &#39;repeater1$ctl02$innerRepeater1$ctl01$innerLabel1&#39;
+	Item: 1; Relative to: repeater1$ctl02$innerRepeater1; Result: &#39;innerRepeater1$ctl01$innerLabel1&#39;
+	Item: 1; Relative to: __Page; Result: exception System.InvalidOperationException (expected)
+	Item: 2; Relative to: repeater1$ctl02$innerRepeater1$ctl02; Result: &#39;ctl02$innerLabel1&#39;
+	Item: 2; Relative to: repeater1; Result: &#39;repeater1$ctl02$innerRepeater1$ctl02$innerLabel1&#39;
+	Item: 2; Relative to: repeater1$ctl02$innerRepeater1; Result: &#39;innerRepeater1$ctl02$innerLabel1&#39;
+	Item: 2; Relative to: __Page; Result: exception System.InvalidOperationException (expected)
+Item: 2; Relative to: repeater1$ctl04; Result: &#39;ctl04$label1&#39;
+Item: 2; Relative to: repeater1; Result: &#39;repeater1$ctl04$label1&#39;
+Item: 2; Relative to: __Page; Result: exception System.InvalidOperationException (expected)
+	Item: 0; Relative to: repeater1$ctl04$innerRepeater1$ctl00; Result: &#39;ctl00$innerLabel1&#39;
+	Item: 0; Relative to: repeater1; Result: &#39;repeater1$ctl04$innerRepeater1$ctl00$innerLabel1&#39;
+	Item: 0; Relative to: repeater1$ctl04$innerRepeater1; Result: &#39;innerRepeater1$ctl00$innerLabel1&#39;
+	Item: 0; Relative to: __Page; Result: exception System.InvalidOperationException (expected)
+	Item: 1; Relative to: repeater1$ctl04$innerRepeater1$ctl01; Result: &#39;ctl01$innerLabel1&#39;
+	Item: 1; Relative to: repeater1; Result: &#39;repeater1$ctl04$innerRepeater1$ctl01$innerLabel1&#39;
+	Item: 1; Relative to: repeater1$ctl04$innerRepeater1; Result: &#39;innerRepeater1$ctl01$innerLabel1&#39;
+	Item: 1; Relative to: __Page; Result: exception System.InvalidOperationException (expected)
+	Item: 2; Relative to: repeater1$ctl04$innerRepeater1$ctl02; Result: &#39;ctl02$innerLabel1&#39;
+	Item: 2; Relative to: repeater1; Result: &#39;repeater1$ctl04$innerRepeater1$ctl02$innerLabel1&#39;
+	Item: 2; Relative to: repeater1$ctl04$innerRepeater1; Result: &#39;innerRepeater1$ctl02$innerLabel1&#39;
+	Item: 2; Relative to: __Page; Result: exception System.InvalidOperationException (expected)
+</pre>";
+			Helpers.ExtractAndCompareCodeFromHtml (result, originalHtml, "#A1");
+		}
+	}
+}
+
+#endif

+ 32 - 0
mcs/class/System.Web/Test/standalone/Control_GetUniqueIDRelativeTo/default.aspx

@@ -0,0 +1,32 @@
+<%@ Page Language="C#" AutoEventWireup="true" CodeFile="default.aspx.cs" Inherits="_default" %>
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head runat="server">
+    <title></title>
+</head>
+<body>
+    <form id="form1" runat="server">
+    <div>
+	Text Box: <asp:TextBox runat="server" ID="textBox1" /><br />
+	<asp:Repeater runat="server" ID="repeater1" OnItemDataBound="OnItemDataBound_Repeater1">
+		<ItemTemplate>
+			<asp:Label runat="server" ID="label1" Text="<%# Container.DataItem %>" />
+			<asp:Repeater runat="server" ID="innerRepeater1" OnItemDataBound="OnItemDataBound_InnerRepeater1">
+				<ItemTemplate>
+					<blockquote><asp:Label runat="server" ID="innerLabel1" Text="<%# Container.DataItem %>" /></blockquote>
+				</ItemTemplate>
+			</asp:Repeater>
+		</ItemTemplate>
+
+		<SeparatorTemplate>
+			<hr />
+		</SeparatorTemplate>
+	</asp:Repeater>
+    </div>
+    <div>Log:</div>
+    <%= AppDomain.CurrentDomain.GetData ("BEGIN_CODE_MARKER") %><pre runat="server" id="log"></pre><%= AppDomain.CurrentDomain.GetData ("END_CODE_MARKER") %>
+    </form>
+</body>
+</html>

+ 117 - 0
mcs/class/System.Web/Test/standalone/Control_GetUniqueIDRelativeTo/default.aspx.cs

@@ -0,0 +1,117 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Web;
+using System.Web.UI;
+using System.Web.UI.WebControls;
+
+public partial class _default : System.Web.UI.Page
+{
+	protected void Page_Load (object sender, EventArgs e)
+	{
+		var list = new List<string> {
+		    "One",
+		    "Two",
+		    "Three"
+		};
+
+		try {
+			GetUniqueIDRelativeTo (null);
+		} catch (ArgumentNullException ex) {
+			log.InnerText += String.Format ("Page; Relative to: null; Result: exception {0} (expected)\n", ex.GetType ());
+		} catch (Exception ex) {
+			log.InnerText += String.Format ("Page; Relative to: null; Result: exception {0}\n", ex.GetType ());
+		}
+
+		var ctl = new Control ();
+		try {
+			ctl.GetUniqueIDRelativeTo (this);
+		} catch (InvalidOperationException ex) {
+			log.InnerText += String.Format ("A control; Relative to: {0}; Result: exception {1} (expected)\n", this.UniqueID, ex.GetType ());
+		} catch (Exception ex) {
+			log.InnerText += String.Format ("A control; Relative to: {0}; Result: exception {1}\n", this.UniqueID, ex.GetType ());
+		}
+
+		try {
+			textBox1.GetUniqueIDRelativeTo (this);
+		} catch (InvalidOperationException ex) {
+			log.InnerText += String.Format ("TextBox; Relative to: {0}; Result: exception {1} (expected)\n", this.UniqueID, ex.GetType ());
+		} catch (Exception ex) {
+			log.InnerText += String.Format ("TextBox; Relative to: {0}; Result: exception {1}\n", this.UniqueID, ex.GetType ());
+		}
+
+		repeater1.DataSource = list;
+		repeater1.DataBind ();
+	}
+
+	protected void OnItemDataBound_Repeater1 (object sender, RepeaterItemEventArgs args)
+	{
+		if (args.Item.ItemType == ListItemType.Separator)
+			return;
+		
+		int index = args.Item.ItemIndex;
+		var sb = new StringBuilder ();
+		Label label = args.Item.FindControl ("label1") as Label;
+
+		string id = label.GetUniqueIDRelativeTo (args.Item);
+		LogItem (index, label, args.Item, sb);
+
+		id = label.GetUniqueIDRelativeTo (repeater1);
+		LogItem (index, label, repeater1, sb);
+
+		try {
+			id = label.GetUniqueIDRelativeTo (this);
+		} catch (InvalidOperationException ex) {
+			sb.AppendFormat ("Item: {0}; Relative to: {1}; Result: exception {2} (expected)\n", args.Item.ItemIndex, this.UniqueID, ex.GetType ());
+		} catch (Exception ex) {
+			sb.AppendFormat ("Item: {0}; Relative to: {1}; Result: exception {2}\n", args.Item.ItemIndex, this.UniqueID, ex.GetType ());
+		}
+
+		log.InnerText += sb.ToString ();
+
+		int listStart = index * 3;
+		var list = new List<int> {
+			listStart,
+			listStart + 1,
+			listStart + 2
+		};
+		Repeater innerRepeater = args.Item.FindControl ("innerRepeater1") as Repeater;
+		innerRepeater.DataSource = list;
+		innerRepeater.DataBind ();
+	}
+
+	protected void OnItemDataBound_InnerRepeater1 (object sender, RepeaterItemEventArgs args)
+	{
+		if (args.Item.ItemType == ListItemType.Separator)
+			return;
+
+		int index = args.Item.ItemIndex;
+		var sb = new StringBuilder ();
+		Label label = args.Item.FindControl ("innerLabel1") as Label;
+
+		string id = label.GetUniqueIDRelativeTo (args.Item);
+		LogItem (index, label, args.Item, sb, "\t");
+
+		id = label.GetUniqueIDRelativeTo (repeater1);
+		LogItem (index, label, repeater1, sb, "\t");
+
+		id = label.GetUniqueIDRelativeTo (args.Item.Parent);
+		LogItem (index, label, args.Item.Parent, sb, "\t");
+
+		try {
+			id = label.GetUniqueIDRelativeTo (this);
+		} catch (InvalidOperationException ex) {
+			sb.AppendFormat ("\tItem: {0}; Relative to: {1}; Result: exception {2} (expected)\n", args.Item.ItemIndex, this.UniqueID, ex.GetType ());
+		} catch (Exception ex) {
+			sb.AppendFormat ("\tItem: {0}; Relative to: {1}; Result: exception {2}\n", args.Item.ItemIndex, this.UniqueID, ex.GetType ());
+		}
+
+		log.InnerText += sb.ToString ();
+	}
+
+	void LogItem (int index, Control ctl, Control relativeTo, StringBuilder sb, string indent = "")
+	{
+		string id = ctl.GetUniqueIDRelativeTo (relativeTo);
+		sb.AppendFormat ("{0}Item: {1}; Relative to: {2}; Result: '{3}'\n", indent, index, relativeTo.UniqueID, id);
+	}
+}

+ 14 - 0
mcs/class/System.Web/Test/standalone/Control_GetUniqueIDRelativeTo/web.config

@@ -0,0 +1,14 @@
+<?xml version="1.0"?>
+
+<!--
+  For more information on how to configure your ASP.NET application, please visit
+  http://go.microsoft.com/fwlink/?LinkId=169433
+  -->
+
+<configuration>
+
+    <system.web>
+        <compilation debug="true" targetFramework="4.0" />
+    </system.web>
+
+</configuration>

+ 2 - 0
mcs/class/System.Web/net_4_0_System.Web.dll.sources

@@ -13,6 +13,8 @@ System.Web.Caching/OutputCacheProviderCollection.cs
 System.Web.Caching/ResponseElement.cs
 System.Web.Caching/SubstitutionResponseElement.cs
 System.Web.Configuration_2.0/VersionConverter.cs
+System.Web.Compilation/RouteUrlExpressionBuilder.cs
+System.Web.Compilation/RouteValueExpressionBuilder.cs
 System.Web.SessionState_2.0/SessionStateBehavior.cs
 System.Web.Routing/PageRouteHandler.cs
 System.Web.UI.WebControls/IDataBoundControl.cs