[asp.net] HtmlForum.Name rendering updates + test updates
[mono.git] / mcs / class / System.Web / System.Web.UI / ClientScriptManager.cs
index 89840267d1ac5fbb549f7f00cff925773fd8e7cf..0ad416d30f211adde4e86ddedee0872bae3adb0e 100644 (file)
@@ -8,7 +8,7 @@
 //   Lluis Sanchez (lluis@novell.com)
 //
 // (C) 2002,2003 Ximian, Inc. (http://www.ximian.com)
-// (c) 2003 Novell, Inc. (http://www.novell.com)
+// (c) 2003-2010 Novell, Inc. (http://www.novell.com)
 //
 
 //
 
 using System;
 using System.Collections;
-#if NET_2_0
 using System.Collections.Generic;
-#endif
 using System.Text;
+using System.Collections.Specialized;
+using System.Web.Util;
+using System.Globalization;
 
 namespace System.Web.UI
 {
-       #if NET_2_0
-       public sealed
-       #else
-       internal
-       #endif
-               class ClientScriptManager
+       public sealed partial class ClientScriptManager
        {
+               internal const string EventStateFieldName = "__EVENTVALIDATION";
+               
                Hashtable registeredArrayDeclares;
                ScriptEntry clientScriptBlocks;
                ScriptEntry startupScriptBlocks;
-               Hashtable hiddenFields;
+               internal Hashtable hiddenFields;
                ScriptEntry submitStatements;
-               ScriptEntry scriptIncludes;
                Page page;
-#if NET_2_0
-               List <int> eventValidationValues;
-#endif
+               int [] eventValidationValues;
+               int eventValidationPos = 0;
+               Hashtable expandoAttributes;
+               bool _hasRegisteredForEventValidationOnCallback;
+               bool _pageInRender;
+               bool _initCallBackRegistered;
+               bool _webFormClientScriptRendered;
+               bool _webFormClientScriptRequired;
+               
+               internal bool ScriptsPresent {
+                       get {
+                               return _webFormClientScriptRequired ||
+                                       _initCallBackRegistered ||
+                                       _hasRegisteredForEventValidationOnCallback ||
+                                       clientScriptBlocks != null ||
+                                       startupScriptBlocks != null ||
+                                       submitStatements != null ||
+                                       registeredArrayDeclares != null ||
+                                       expandoAttributes != null;
+                       }
+               }
                
                internal ClientScriptManager (Page page)
                {
                        this.page = page;
                }
 
-#if !NET_2_0
-               public string GetPostBackClientEvent (Control control, string argument)
-               {
-                       return GetPostBackEventReference (control, argument);
-               }
-#endif
-
                public string GetPostBackClientHyperlink (Control control, string argument)
                {
                        return "javascript:" + GetPostBackEventReference (control, argument);
                }
        
-#if NET_2_0
                public string GetPostBackClientHyperlink (Control control, string argument, bool registerForEventValidation)
                {
                        if (registerForEventValidation)
                                RegisterForEventValidation (control.UniqueID, argument);
                        return "javascript:" + GetPostBackEventReference (control, argument);
                }
-#endif
-               
+
                public string GetPostBackEventReference (Control control, string argument)
                {
+                       if (control == null)
+                               throw new ArgumentNullException ("control");
+                       
                        page.RequiresPostBackScript ();
-                       return String.Format ("__doPostBack('{0}','{1}')", control.UniqueID, argument);
+                       if(page.IsMultiForm)
+                               return page.theForm + ".__doPostBack('" + control.UniqueID + "','" + argument + "')";
+                       else
+                               return "__doPostBack('" + control.UniqueID + "','" + argument + "')";
+               }
+
+               public string GetPostBackEventReference (Control control, string argument, bool registerForEventValidation)
+               {
+                       if (control == null)
+                               throw new ArgumentNullException ("control");
+                       
+                       if (registerForEventValidation)
+                               RegisterForEventValidation (control.UniqueID, argument);
+                       return GetPostBackEventReference (control, argument);
+               }
+               
+               public string GetPostBackEventReference (PostBackOptions options, bool registerForEventValidation)
+               {
+                       if (options == null)
+                               throw new ArgumentNullException ("options");
+                       if (registerForEventValidation)
+                               RegisterForEventValidation (options);
+                       return GetPostBackEventReference (options);
                }
                
-#if NET_2_0
                public string GetPostBackEventReference (PostBackOptions options)
                {
+                       if (options == null)
+                               throw new ArgumentNullException ("options");
+
                        if (options.ActionUrl == null && options.ValidationGroup == null && !options.TrackFocus && 
                                !options.AutoPostBack && !options.PerformValidation)
                        {
@@ -105,28 +138,49 @@ namespace System.Web.UI
                                else
                                        return GetPostBackEventReference (options.TargetControl, options.Argument);
                        }
-                       
-                       if (!IsClientScriptIncludeRegistered (typeof(Page), "webform")) {
-                               RegisterClientScriptInclude (typeof(Page), "webform", GetWebResourceUrl (typeof(Page), "webform.js"));
-                       }
-                       
-                       if (options.ActionUrl != null)
+
+                       RegisterWebFormClientScript ();
+
+                       string actionUrl = options.ActionUrl;
+                       if (actionUrl != null)
                                RegisterHiddenField (Page.PreviousPageID, page.Request.FilePath);
-                       
-                       if (options.ClientSubmit || options.ActionUrl != null)
-                               page.RequiresPostBackScript ();
-                       
-                       return String.Format ("{0}WebForm_DoPostback({1},{2},{3},{4},{5},{6},{7},{8})", 
-                                       options.RequiresJavaScriptProtocol ? "javascript:" : "",
-                                       ClientScriptManager.GetScriptLiteral (options.TargetControl.UniqueID), 
-                                       ClientScriptManager.GetScriptLiteral (options.Argument),
-                                       ClientScriptManager.GetScriptLiteral (options.ActionUrl),
-                                       ClientScriptManager.GetScriptLiteral (options.AutoPostBack),
-                                       ClientScriptManager.GetScriptLiteral (options.PerformValidation),
-                                       ClientScriptManager.GetScriptLiteral (options.TrackFocus),
-                                       ClientScriptManager.GetScriptLiteral (options.ClientSubmit),
-                                       ClientScriptManager.GetScriptLiteral (options.ValidationGroup)
-                               );
+
+                       if(options.TrackFocus)
+                               RegisterHiddenField (Page.LastFocusID, String.Empty);
+
+                       string prefix = options.RequiresJavaScriptProtocol ? "javascript:" : String.Empty;
+                       if (page.IsMultiForm)
+                               prefix += page.theForm + ".";
+
+                       return prefix + "WebForm_DoPostback(" +
+                               ClientScriptManager.GetScriptLiteral (options.TargetControl.UniqueID) + "," +
+                               ClientScriptManager.GetScriptLiteral (options.Argument) + "," +
+                               ClientScriptManager.GetScriptLiteral (actionUrl) + "," +
+                               ClientScriptManager.GetScriptLiteral (options.AutoPostBack) + "," +
+                               ClientScriptManager.GetScriptLiteral (options.PerformValidation) + "," +
+                               ClientScriptManager.GetScriptLiteral (options.TrackFocus) + "," +
+                               ClientScriptManager.GetScriptLiteral (options.ClientSubmit) + "," +
+                               ClientScriptManager.GetScriptLiteral (options.ValidationGroup) + ")";
+               }
+
+               internal void RegisterWebFormClientScript ()
+               {
+                       if (_webFormClientScriptRequired)
+                               return;
+
+                       page.RequiresPostBackScript ();
+                       _webFormClientScriptRequired = true;
+               }
+
+               internal void WriteWebFormClientScript (HtmlTextWriter writer) {
+                       if (!_webFormClientScriptRendered && _webFormClientScriptRequired) {
+                               writer.WriteLine ();
+                               WriteClientScriptInclude (writer, GetWebResourceUrl (typeof (Page), "webform.js"), typeof (Page), "webform.js");
+                               WriteBeginScriptBlock (writer);
+                               writer.WriteLine ("WebForm_Initialize({0});", page.IsMultiForm ? page.theForm : "window");
+                               WriteEndScriptBlock (writer);
+                               _webFormClientScriptRendered = true;
+                       }
                }
                
                public string GetCallbackEventReference (Control control, string argument, string clientCallback, string context)
@@ -146,26 +200,27 @@ namespace System.Web.UI
                        if(!(control is ICallbackEventHandler))
                                throw new InvalidOperationException ("The control must implement the ICallbackEventHandler interface and provide a RaiseCallbackEvent method.");
 
-                       return GetCallbackEventReference (control.UniqueID, argument, clientCallback, context, clientErrorCallback, useAsync);
+                       return GetCallbackEventReference ("'" + control.UniqueID + "'", argument, clientCallback, context, clientErrorCallback, useAsync);
                }
 
                public string GetCallbackEventReference (string target, string argument, string clientCallback, string context, string clientErrorCallback, bool useAsync)
                {
-                       page.RequiresPostBackScript ();
+                       RegisterWebFormClientScript ();
 
-                       if (!IsClientScriptIncludeRegistered (typeof(Page), "callback"))
-                               RegisterClientScriptInclude (typeof(Page), "callback", GetWebResourceUrl (typeof(Page), "callback.js"));
-                       
-                       return string.Format ("WebForm_DoCallback('{0}',{1},{2},{3},{4},{5})", target, argument, clientCallback, context, ((clientErrorCallback == null) ? "null" : clientErrorCallback), (useAsync ? "true" : "false"));
+                       if (!_initCallBackRegistered) {
+                               _initCallBackRegistered = true;
+                               RegisterStartupScript (typeof (Page), "WebForm_InitCallback", page.WebFormScriptReference + ".WebForm_InitCallback();", true);
+                       }
+                       return page.WebFormScriptReference + ".WebForm_DoCallback(" +
+                               target + "," +
+                               (argument ?? "null") + "," +
+                               clientCallback + "," +
+                               (context ?? "null") + "," +
+                               (clientErrorCallback ?? "null") + "," +
+                               (useAsync ? "true" : "false") + ")";
                }
-#endif
                
-#if NET_2_0
-               public
-#else
-               internal
-#endif
-               string GetWebResourceUrl(Type type, string resourceName)
+               public string GetWebResourceUrl(Type type, string resourceName)
                {
                        if (type == null)
                                throw new ArgumentNullException ("type");
@@ -209,12 +264,12 @@ namespace System.Web.UI
                
                public bool IsClientScriptIncludeRegistered (string key)
                {
-                       return IsScriptRegistered (scriptIncludes, GetType(), key);
+                       return IsClientScriptIncludeRegistered (GetType (), key);
                }
        
                public bool IsClientScriptIncludeRegistered (Type type, string key)
                {
-                       return IsScriptRegistered (scriptIncludes, type, key);
+                       return IsScriptRegistered (clientScriptBlocks, type, "include-" + key);
                }
                
                bool IsScriptRegistered (ScriptEntry scriptList, Type type, string key)
@@ -236,9 +291,15 @@ namespace System.Web.UI
                                registeredArrayDeclares.Add (arrayName, new ArrayList());
        
                        ((ArrayList) registeredArrayDeclares[arrayName]).Add(arrayValue);
+                       page.RequiresFormScriptDeclaration ();
                }
-       
+
                void RegisterScript (ref ScriptEntry scriptList, Type type, string key, string script, bool addScriptTags)
+               {
+                       RegisterScript (ref scriptList, type, key, script, addScriptTags ? ScriptEntryFormat.AddScriptTag : ScriptEntryFormat.None);
+               }
+
+               void RegisterScript (ref ScriptEntry scriptList, Type type, string key, string script, ScriptEntryFormat format)
                {
                        ScriptEntry last = null;
                        ScriptEntry entry = scriptList;
@@ -249,11 +310,8 @@ namespace System.Web.UI
                                last = entry;
                                entry = entry.Next;
                        }
-                       
-                       if (addScriptTags)
-                               script = "<script language=javascript>\n<!--\n" + script + "\n// -->\n</script>";
 
-                       entry = new ScriptEntry (type, key, script);
+                       entry = new ScriptEntry (type, key, script, format);
                        
                        if (last != null) last.Next = entry;
                        else scriptList = entry;
@@ -329,37 +387,64 @@ namespace System.Web.UI
                        if (url == null || url.Length == 0)
                                throw new ArgumentException ("url");
 
-                       RegisterScript (ref scriptIncludes, type, key, url, false);
+                       RegisterScript (ref clientScriptBlocks, type, "include-" + key, url, ScriptEntryFormat.Include);
                }
 
-#if NET_2_0
                public void RegisterClientScriptResource (Type type, string resourceName)
                {
-                       RegisterScript (ref scriptIncludes, type, "resource-" + resourceName, GetWebResourceUrl (type, resourceName), false);
+                       RegisterScript (ref clientScriptBlocks, type, "resource-" + resourceName, GetWebResourceUrl (type, resourceName), ScriptEntryFormat.Include);
                }
 
-               [MonoTODO]
-               public void RegisterExpandoAttribute (string controlId, 
-                                                     string name, 
-                                                     string value)
+               public void RegisterExpandoAttribute (string controlId, string attributeName, string attributeValue)
                {
-                       throw new NotImplementedException ();
+                       RegisterExpandoAttribute (controlId, attributeName, attributeValue, true);
+               }
+
+               public void RegisterExpandoAttribute (string controlId, string attributeName, string attributeValue, bool encode)
+               {
+                       if (controlId == null)
+                               throw new ArgumentNullException ("controlId");
+
+                       if (attributeName == null)
+                               throw new ArgumentNullException ("attributeName");
+                       
+                       if (expandoAttributes == null)
+                               expandoAttributes = new Hashtable ();
+
+                       ListDictionary list = (ListDictionary)expandoAttributes [controlId];
+                       if (list == null) {
+                               list = new ListDictionary ();
+                               expandoAttributes [controlId] = list;
+                       }
+
+                       list.Add (attributeName, encode ? StrUtils.EscapeQuotesAndBackslashes (attributeValue) : attributeValue);
                }
 
-               [MonoTODO]
-               public void RegisterExpandoAttribute(string controlId, 
-                                                    string attributeName, 
-                                                    string attributeValue, 
-                                                    bool encode)
+               void EnsureEventValidationArray ()
                {
-                       throw new NotImplementedException ();
+                       if (eventValidationValues == null || eventValidationValues.Length == 0)
+                               eventValidationValues = new int [64];
+
+                       int len = eventValidationValues.Length;
+
+                       if (eventValidationPos >= len) {
+                               int [] tmp = new int [len * 2];
+                               Array.Copy (eventValidationValues, tmp, len);
+                               eventValidationValues = tmp;
+                       }
                }
-               
+
+               internal void ResetEventValidationState ()
+               {
+                       _pageInRender = true;
+                       eventValidationPos = 0;
+               }
+
                // Implemented following the description in http://odetocode.com/Blogs/scott/archive/2006/03/20/3145.aspx
-               private int CalculateEventHash (string uniqueId, string argument)
+               int CalculateEventHash (string uniqueId, string argument)
                {
                        int uniqueIdHash = uniqueId.GetHashCode ();
-                       int argumentHash = (argument == null) ? 0 : argument.GetHashCode ();
+                       int argumentHash = String.IsNullOrEmpty (argument) ? 0 : argument.GetHashCode ();
                        return (uniqueIdHash ^ argumentHash);
                }
                
@@ -380,15 +465,18 @@ namespace System.Web.UI
                                return;
                        if (uniqueId == null || uniqueId.Length == 0)
                                return;
-                       if (page.LifeCycle < PageLifeCycle.Render)
+                       if (page.IsCallback)
+                               _hasRegisteredForEventValidationOnCallback = true;
+                       else if (!_pageInRender)
                                throw new InvalidOperationException ("RegisterForEventValidation may only be called from the Render method");
-                       if (eventValidationValues == null)
-                               eventValidationValues = new List <int> ();
 
+                       EnsureEventValidationArray ();
                        
                        int hash = CalculateEventHash (uniqueId, argument);
-                       if (eventValidationValues.BinarySearch (hash) < 0)
-                               eventValidationValues.Add (hash);
+                       for (int i = 0; i < eventValidationPos; i++)
+                               if (eventValidationValues [i] == hash)
+                                       return;
+                       eventValidationValues [eventValidationPos++] = hash;
                }
 
                public void ValidateEvent (string uniqueId)
@@ -396,6 +484,11 @@ namespace System.Web.UI
                        ValidateEvent (uniqueId, null);
                }
 
+               ArgumentException InvalidPostBackException ()
+               {
+                       return new ArgumentException ("Invalid postback or callback argument. Event validation is enabled using <pages enableEventValidation=\"true\"/> in configuration or <%@ Page EnableEventValidation=\"true\" %> in a page. For security purposes, this feature verifies that arguments to postback or callback events originate from the server control that originally rendered them. If the data is valid and expected, use the ClientScriptManager.RegisterForEventValidation method in order to register the postback or callback data for validation.");
+               }
+               
                public void ValidateEvent (string uniqueId, string argument)
                {
                        if (uniqueId == null || uniqueId.Length == 0)
@@ -403,69 +496,177 @@ namespace System.Web.UI
                        if (!page.EnableEventValidation)
                                return;
                        if (eventValidationValues == null)
-                               goto bad;
+                               throw InvalidPostBackException ();
                        
                        int hash = CalculateEventHash (uniqueId, argument);
-                       if (eventValidationValues.BinarySearch (hash) < 0)
-                               goto bad;
-                       return;
+                       for (int i = 0; i < eventValidationValues.Length; i++)
+                               if (eventValidationValues [i] == hash)
+                                       return;
                        
-                       bad:
-                       throw new ArgumentException ("Invalid postback or callback argument. Event validation is enabled using <pages enableEventValidation=\"true\"/> in configuration or <%@ Page EnableEventValidation=\"true\" %> in a page. For security purposes, this feature verifies that arguments to postback or callback events originate from the server control that originally rendered them. If the data is valid and expected, use the ClientScriptManager.RegisterForEventValidation method in order to register the postback or callback data for validation.");
+                       throw InvalidPostBackException ();
                }
-#endif
+
                void WriteScripts (HtmlTextWriter writer, ScriptEntry scriptList)
                {
+                       if (scriptList == null)
+                               return;
+
+                       writer.WriteLine ();
+
                        while (scriptList != null) {
-                               writer.WriteLine (scriptList.Script);
+                               switch (scriptList.Format) {
+                               case ScriptEntryFormat.AddScriptTag:
+                                       EnsureBeginScriptBlock (writer);
+                                       writer.Write (scriptList.Script);
+                                       break;
+                               case ScriptEntryFormat.Include:
+                                       EnsureEndScriptBlock (writer);
+                                       WriteClientScriptInclude (writer, scriptList.Script, scriptList.Type, scriptList.Key);
+                                       break;
+                               default:
+                                       EnsureEndScriptBlock (writer);
+                                       writer.WriteLine (scriptList.Script);
+                                       break;
+                               }
                                scriptList = scriptList.Next;
                        }
+                       EnsureEndScriptBlock (writer);
+               }
+
+               bool _scriptTagOpened;
+
+               void EnsureBeginScriptBlock (HtmlTextWriter writer) {
+                       if (!_scriptTagOpened) {
+                               WriteBeginScriptBlock (writer);
+                               _scriptTagOpened = true;
+                       }
+               }
+
+               void EnsureEndScriptBlock (HtmlTextWriter writer) {
+                       if (_scriptTagOpened) {
+                               WriteEndScriptBlock (writer);
+                               _scriptTagOpened = false;
+                       }
                }
 
-#if NET_2_0
                internal void RestoreEventValidationState (string fieldValue)
                {
                        if (!page.EnableEventValidation || fieldValue == null || fieldValue.Length == 0)
                                return;
-                       LosFormatter fmt = page.GetFormatter ();
-                       eventValidationValues = (List <int>)fmt.Deserialize (fieldValue);
+                       IStateFormatter fmt = page.GetFormatter ();
+                       eventValidationValues = (int []) fmt.Deserialize (fieldValue);
+                       eventValidationPos = eventValidationValues.Length;
                }
                
                internal void SaveEventValidationState ()
                {
-                       if (!page.EnableEventValidation || eventValidationValues == null || eventValidationValues.Count == 0)
+                       if (!page.EnableEventValidation)
                                return;
-                       eventValidationValues.Sort ();
-                       LosFormatter fmt = page.GetFormatter ();
-                       RegisterHiddenField (EventStateFieldName, fmt.SerializeToBase64 (eventValidationValues));
+
+                       string eventValidation = GetEventValidationStateFormatted ();
+                       if (eventValidation == null)
+                               return;
+
+                       RegisterHiddenField (EventStateFieldName, eventValidation);
+               }
+
+               internal string GetEventValidationStateFormatted ()
+               {
+                       if (eventValidationValues == null || eventValidationValues.Length == 0)
+                               return null;
+
+                       if(page.IsCallback && !_hasRegisteredForEventValidationOnCallback)
+                               return null;
+
+                       IStateFormatter fmt = page.GetFormatter ();
+                       int [] array = new int [eventValidationPos];
+                       Array.Copy (eventValidationValues, array, eventValidationPos);
+                       return fmt.Serialize (array);
                }
 
-               internal string EventStateFieldName
+               internal void WriteExpandoAttributes (HtmlTextWriter writer)
                {
-                       get { return "__EVENTVALIDATION"; }
+                       if (expandoAttributes == null)
+                               return;
+
+                       writer.WriteLine ();
+                       WriteBeginScriptBlock (writer);
+
+                       foreach (string controlId in expandoAttributes.Keys) {
+                               writer.WriteLine ("var {0} = document.all ? document.all [\"{0}\"] : document.getElementById (\"{0}\");", controlId);
+                               ListDictionary attrs = (ListDictionary) expandoAttributes [controlId];
+                               foreach (string attributeName in attrs.Keys) {
+                                       writer.WriteLine ("{0}.{1} = \"{2}\";", controlId, attributeName, attrs [attributeName]);
+                               }
+                       }
+                       WriteEndScriptBlock (writer);
+                       writer.WriteLine ();
+               }
+
+               internal const string SCRIPT_BLOCK_START = "//<![CDATA[";
+               internal const string SCRIPT_BLOCK_END = "//]]>";
+               internal const string SCRIPT_ELEMENT_START = @"<script type=""text/javascript"">" + SCRIPT_BLOCK_START;
+               internal const string SCRIPT_ELEMENT_END = SCRIPT_BLOCK_END + "</script>";
+               
+               internal static void WriteBeginScriptBlock (HtmlTextWriter writer)
+               {
+                       writer.WriteLine (SCRIPT_ELEMENT_START);
+               }
+
+               internal static void WriteEndScriptBlock (HtmlTextWriter writer)
+               {
+                       writer.WriteLine (SCRIPT_ELEMENT_END);
                }
-#endif
                
                internal void WriteHiddenFields (HtmlTextWriter writer)
                {
                        if (hiddenFields == null)
                                return;
-       
+
+                       writer.WriteLine ();
+#if NET_4_0
+                       writer.AddAttribute (HtmlTextWriterAttribute.Class, "aspNetHidden");
+#endif
+                       writer.RenderBeginTag (HtmlTextWriterTag.Div);
+                       int oldIndent = writer.Indent;
+                       writer.Indent = 0;
+                       bool first = true;
+                       var sb = new StringBuilder ();
+                       
                        foreach (string key in hiddenFields.Keys) {
                                string value = hiddenFields [key] as string;
-                               writer.WriteLine ("\n<input type=\"hidden\" name=\"{0}\" id=\"{0}\" value=\"{1}\" />", key, value);
+                               if (first)
+                                       first = false;
+                               else
+                                       writer.WriteLine ();
+                               sb.Append ("<input type=\"hidden\" name=\"");
+                               sb.Append (key);
+                               sb.Append ("\" id=\"");
+                               sb.Append (key);
+                               sb.Append ("\" value=\"");
+                               sb.Append (HttpUtility.HtmlAttributeEncode (value));
+                               sb.Append ("\" />");
                        }
-       
+                       writer.Write (sb.ToString ());
+                       writer.Indent = oldIndent;
+                       writer.RenderEndTag (); // DIV
+                       writer.WriteLine ();
                        hiddenFields = null;
                }
                
-               internal void WriteClientScriptIncludes (HtmlTextWriter writer)
-               {
-                       ScriptEntry entry = scriptIncludes;
-                       while (entry != null) {
-                               writer.WriteLine ("\n<script src=\"{0}\" type=\"text/javascript\"></script>", entry.Script);
-                               entry = entry.Next;
-                       }
+               internal void WriteClientScriptInclude (HtmlTextWriter writer, string path, Type type, string key) {
+                                       if (!page.IsMultiForm)
+                                               writer.WriteLine ("<script src=\"{0}\" type=\"text/javascript\"></script>", path);
+                                       else {
+                                               string scriptKey = "inc_" + (type.FullName + key).GetHashCode ().ToString ("X");
+                                               writer.WriteLine ("<script type=\"text/javascript\">");
+                                               writer.WriteLine (SCRIPT_BLOCK_START);
+                                               writer.WriteLine ("if (!window.{0}) {{", scriptKey);
+                                               writer.WriteLine ("\twindow.{0} = true", scriptKey);
+                                               writer.WriteLine ("\tdocument.write('<script src=\"{0}\" type=\"text/javascript\"><\\/script>'); }}", path);
+                                               writer.WriteLine (SCRIPT_BLOCK_END);
+                                               writer.WriteLine ("</script>");
+                                       }
                }
                
                internal void WriteClientScriptBlocks (HtmlTextWriter writer)
@@ -482,11 +683,13 @@ namespace System.Web.UI
                {
                        if (registeredArrayDeclares != null) {
                                writer.WriteLine();
-                               writer.WriteLine("<script language=\"javascript\">");
-                               writer.WriteLine("<!--");
+                               WriteBeginScriptBlock (writer);
                                IDictionaryEnumerator arrayEnum = registeredArrayDeclares.GetEnumerator();
                                while (arrayEnum.MoveNext()) {
-                                       writer.Write("\tvar ");
+                                       if (page.IsMultiForm)
+                                               writer.Write ("\t" + page.theForm + ".");
+                                       else
+                                               writer.Write ("\tvar ");
                                        writer.Write(arrayEnum.Key);
                                        writer.Write(" =  new Array(");
                                        IEnumerator arrayListEnum = ((ArrayList) arrayEnum.Value).GetEnumerator();
@@ -500,20 +703,21 @@ namespace System.Web.UI
                                        }
                                        writer.WriteLine(");");
                                }
-                               writer.WriteLine("// -->");
-                               writer.WriteLine("</script>");
-                               writer.WriteLine();
+                               WriteEndScriptBlock (writer);
+                               writer.WriteLine ();
                        }
                }
 
-#if NET_2_0
                internal string GetClientValidationEvent (string validationGroup) {
+                       if (page.IsMultiForm)
+                               return "if (typeof(" + page.theForm + ".Page_ClientValidate) == 'function') " + page.theForm + ".Page_ClientValidate('" + validationGroup + "');";
                        return "if (typeof(Page_ClientValidate) == 'function') Page_ClientValidate('" + validationGroup + "');";
                }
-#endif
 
                internal string GetClientValidationEvent ()
                {
+                       if (page.IsMultiForm)
+                               return "if (typeof(" + page.theForm + ".Page_ClientValidate) == 'function') " + page.theForm + ".Page_ClientValidate();";
                        return "if (typeof(Page_ClientValidate) == 'function') Page_ClientValidate();";
                }
 
@@ -525,10 +729,17 @@ namespace System.Web.UI
                        StringBuilder sb = new StringBuilder ();
                        ScriptEntry entry = submitStatements;
                        while (entry != null) {
-                               sb.Append (entry.Script);
+                               sb.Append (EnsureEndsWithSemicolon (entry.Script));
                                entry = entry.Next;
                        }
-                       return sb.ToString ();
+                       RegisterClientScriptBlock (GetType(), "HtmlForm-OnSubmitStatemen",
+@"
+" + page.WebFormScriptReference + @".WebForm_OnSubmit = function () {
+" + sb.ToString () + @"
+return true;
+}
+", true);
+                       return "javascript:return " + page.WebFormScriptReference + ".WebForm_OnSubmit();";
                }
                
                internal static string GetScriptLiteral (object ob)
@@ -537,37 +748,67 @@ namespace System.Web.UI
                                return "null";
                        else if (ob is string) {
                                string s = (string)ob;
-                               s = s.Replace ("\"", "\\\"");
-                               return "\"" + s + "\"";
+                               bool escape = false;
+                               int len = s.Length;
+
+                               for (int i = 0; i < len; i++)
+                                       if (s [i] == '\\' || s [i] == '\"') {
+                                               escape = true;
+                                               break;
+                                       }
+
+                               if (!escape)
+                                       return string.Concat ("\"", s, "\"");
+
+                               StringBuilder sb = new StringBuilder (len + 10);
+
+                               sb.Append ('\"');
+                               for (int si = 0; si < len; si++) {
+                                       if (s [si] == '\"')
+                                               sb.Append ("\\\"");
+                                       else if (s [si] == '\\')
+                                               sb.Append ("\\\\");
+                                       else
+                                               sb.Append (s [si]);
+                               }
+                               sb.Append ('\"');
+
+                               return sb.ToString ();
                        } else if (ob is bool) {
-                               return ob.ToString().ToLower();
+                               return ob.ToString ().ToLower (Helpers.InvariantCulture);
                        } else {
                                return ob.ToString ();
                        }
                }
-               
-               class ScriptEntry
+
+               sealed class ScriptEntry
                {
-                       public Type Type;
-                       public string Key;
-                       public string Script;
+                       public readonly Type Type;
+                       public readonly string Key;
+                       public readonly string Script;
+                       public readonly ScriptEntryFormat Format;
                        public ScriptEntry Next;
-                        
-                       public ScriptEntry (Type type, string key, string script)
-                       {
+
+                       public ScriptEntry (Type type, string key, string script, ScriptEntryFormat format) {
                                Key = key;
                                Type = type;
                                Script = script;
+                               Format = format;
                        }
                }
 
-#if NET_2_0
+               enum ScriptEntryFormat
+               {
+                       None,
+                       AddScriptTag,
+                       Include,
+               }
+
                // helper method
                internal static string EnsureEndsWithSemicolon (string value) {
-                       if (value != null && value.Length > 0 && !value.EndsWith (";"))
+                       if (value != null && value.Length > 0 && value [value.Length - 1] != ';')
                                return value += ";";
                        return value;
                }
-#endif
        }
 }