2 // System.Web.UI.ClientScriptManager.cs
5 // Duncan Mak (duncan@ximian.com)
6 // Gonzalo Paniagua (gonzalo@ximian.com)
7 // Andreas Nahr (ClassDevelopment@A-SoftTech.com)
8 // Lluis Sanchez (lluis@novell.com)
10 // (C) 2002,2003 Ximian, Inc. (http://www.ximian.com)
11 // (c) 2003 Novell, Inc. (http://www.novell.com)
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 using System.Collections;
38 using System.Collections.Generic;
41 using System.Collections.Specialized;
42 using System.Web.Util;
44 namespace System.Web.UI
51 class ClientScriptManager
53 Hashtable registeredArrayDeclares;
54 ScriptEntry clientScriptBlocks;
55 ScriptEntry startupScriptBlocks;
56 Hashtable hiddenFields;
57 ScriptEntry submitStatements;
58 ScriptEntry scriptIncludes;
59 Hashtable expandoAttributes;
62 List <int> eventValidationValues;
65 internal ClientScriptManager (Page page)
71 public string GetPostBackClientEvent (Control control, string argument)
73 return GetPostBackEventReference (control, argument);
77 public string GetPostBackClientHyperlink (Control control, string argument)
79 return "javascript:" + GetPostBackEventReference (control, argument);
83 public string GetPostBackClientHyperlink (Control control, string argument, bool registerForEventValidation)
85 if (registerForEventValidation)
86 RegisterForEventValidation (control.UniqueID, argument);
87 return "javascript:" + GetPostBackEventReference (control, argument);
96 string GetPostBackEventReference (Control control, string argument)
99 throw new ArgumentNullException ("control");
101 page.RequiresPostBackScript ();
102 return String.Format ("__doPostBack('{0}','{1}')", control.UniqueID, argument);
106 public string GetPostBackEventReference (Control control, string argument, bool registerForEventValidation)
109 throw new ArgumentNullException ("control");
111 if (registerForEventValidation)
112 RegisterForEventValidation (control.UniqueID, argument);
113 return GetPostBackEventReference (control, argument);
116 public string GetPostBackEventReference (PostBackOptions options, bool registerForEventValidation)
119 throw new ArgumentNullException ("options");
120 if (registerForEventValidation)
121 RegisterForEventValidation (options);
122 return GetPostBackEventReference (options);
125 public string GetPostBackEventReference (PostBackOptions options)
128 throw new ArgumentNullException ("options");
130 if (options.ActionUrl == null && options.ValidationGroup == null && !options.TrackFocus &&
131 !options.AutoPostBack && !options.PerformValidation)
133 if (!options.ClientSubmit)
136 if (options.RequiresJavaScriptProtocol)
137 return GetPostBackClientHyperlink (options.TargetControl, options.Argument);
139 return GetPostBackEventReference (options.TargetControl, options.Argument);
142 RegisterWebFormClientScript ();
144 if (options.ActionUrl != null)
145 RegisterHiddenField (Page.PreviousPageID, page.Request.FilePath);
147 if (options.ClientSubmit || options.ActionUrl != null)
148 page.RequiresPostBackScript ();
150 return String.Format ("{0}WebForm_DoPostback({1},{2},{3},{4},{5},{6},{7},{8})",
151 options.RequiresJavaScriptProtocol ? "javascript:" : "",
152 ClientScriptManager.GetScriptLiteral (options.TargetControl.UniqueID),
153 ClientScriptManager.GetScriptLiteral (options.Argument),
154 ClientScriptManager.GetScriptLiteral (options.ActionUrl),
155 ClientScriptManager.GetScriptLiteral (options.AutoPostBack),
156 ClientScriptManager.GetScriptLiteral (options.PerformValidation),
157 ClientScriptManager.GetScriptLiteral (options.TrackFocus),
158 ClientScriptManager.GetScriptLiteral (options.ClientSubmit),
159 ClientScriptManager.GetScriptLiteral (options.ValidationGroup)
163 internal void RegisterWebFormClientScript ()
165 if (!IsClientScriptIncludeRegistered (typeof (Page), "webform")) {
166 RegisterClientScriptInclude (typeof (Page), "webform", GetWebResourceUrl (typeof (Page), "webform.js"));
170 public string GetCallbackEventReference (Control control, string argument, string clientCallback, string context)
172 return GetCallbackEventReference (control, argument, clientCallback, context, null, false);
175 public string GetCallbackEventReference (Control control, string argument, string clientCallback, string context, bool useAsync)
177 return GetCallbackEventReference (control, argument, clientCallback, context, null, useAsync);
180 public string GetCallbackEventReference (Control control, string argument, string clientCallback, string context, string clientErrorCallback, bool useAsync)
183 throw new ArgumentNullException ("control");
184 if(!(control is ICallbackEventHandler))
185 throw new InvalidOperationException ("The control must implement the ICallbackEventHandler interface and provide a RaiseCallbackEvent method.");
187 return GetCallbackEventReference (control.UniqueID, argument, clientCallback, context, clientErrorCallback, useAsync);
190 public string GetCallbackEventReference (string target, string argument, string clientCallback, string context, string clientErrorCallback, bool useAsync)
192 page.RequiresPostBackScript ();
194 if (!IsClientScriptIncludeRegistered (typeof(Page), "callback"))
195 RegisterClientScriptInclude (typeof(Page), "callback", GetWebResourceUrl (typeof(Page), "callback.js"));
197 return string.Format ("WebForm_DoCallback('{0}',{1},{2},{3},{4},{5})", target, argument, clientCallback, context, ((clientErrorCallback == null) ? "null" : clientErrorCallback), (useAsync ? "true" : "false"));
206 string GetWebResourceUrl(Type type, string resourceName)
209 throw new ArgumentNullException ("type");
211 if (resourceName == null || resourceName.Length == 0)
212 throw new ArgumentNullException ("type");
214 return System.Web.Handlers.AssemblyResourceLoader.GetResourceUrl (type, resourceName);
218 public bool IsClientScriptBlockRegistered (string key)
220 return IsScriptRegistered (clientScriptBlocks, GetType(), key);
223 public bool IsClientScriptBlockRegistered (Type type, string key)
225 return IsScriptRegistered (clientScriptBlocks, type, key);
228 public bool IsStartupScriptRegistered (string key)
230 return IsScriptRegistered (startupScriptBlocks, GetType(), key);
233 public bool IsStartupScriptRegistered (Type type, string key)
235 return IsScriptRegistered (startupScriptBlocks, type, key);
238 public bool IsOnSubmitStatementRegistered (string key)
240 return IsScriptRegistered (submitStatements, GetType(), key);
243 public bool IsOnSubmitStatementRegistered (Type type, string key)
245 return IsScriptRegistered (submitStatements, type, key);
248 public bool IsClientScriptIncludeRegistered (string key)
250 return IsScriptRegistered (scriptIncludes, GetType(), key);
253 public bool IsClientScriptIncludeRegistered (Type type, string key)
255 return IsScriptRegistered (scriptIncludes, type, key);
258 bool IsScriptRegistered (ScriptEntry scriptList, Type type, string key)
260 while (scriptList != null) {
261 if (scriptList.Type == type && scriptList.Key == key)
263 scriptList = scriptList.Next;
268 public void RegisterArrayDeclaration (string arrayName, string arrayValue)
270 if (registeredArrayDeclares == null)
271 registeredArrayDeclares = new Hashtable();
273 if (!registeredArrayDeclares.ContainsKey (arrayName))
274 registeredArrayDeclares.Add (arrayName, new ArrayList());
276 ((ArrayList) registeredArrayDeclares[arrayName]).Add(arrayValue);
279 void RegisterScript (ref ScriptEntry scriptList, Type type, string key, string script, bool addScriptTags)
281 ScriptEntry last = null;
282 ScriptEntry entry = scriptList;
284 while (entry != null) {
285 if (entry.Type == type && entry.Key == key)
292 script = "<script language=javascript>\n<!--\n" + script + "\n// -->\n</script>";
294 entry = new ScriptEntry (type, key, script);
296 if (last != null) last.Next = entry;
297 else scriptList = entry;
300 internal void RegisterClientScriptBlock (string key, string script)
302 RegisterScript (ref clientScriptBlocks, GetType(), key, script, false);
305 public void RegisterClientScriptBlock (Type type, string key, string script)
307 RegisterClientScriptBlock (type, key, script, false);
310 public void RegisterClientScriptBlock (Type type, string key, string script, bool addScriptTags)
313 throw new ArgumentNullException ("type");
315 RegisterScript (ref clientScriptBlocks, type, key, script, addScriptTags);
318 public void RegisterHiddenField (string hiddenFieldName, string hiddenFieldInitialValue)
320 if (hiddenFields == null)
321 hiddenFields = new Hashtable ();
323 if (!hiddenFields.ContainsKey (hiddenFieldName))
324 hiddenFields.Add (hiddenFieldName, hiddenFieldInitialValue);
327 internal void RegisterOnSubmitStatement (string key, string script)
329 RegisterScript (ref submitStatements, GetType (), key, script, false);
332 public void RegisterOnSubmitStatement (Type type, string key, string script)
335 throw new ArgumentNullException ("type");
337 RegisterScript (ref submitStatements, type, key, script, false);
340 internal void RegisterStartupScript (string key, string script)
342 RegisterScript (ref startupScriptBlocks, GetType(), key, script, false);
345 public void RegisterStartupScript (Type type, string key, string script)
347 RegisterStartupScript (type, key, script, false);
350 public void RegisterStartupScript (Type type, string key, string script, bool addScriptTags)
353 throw new ArgumentNullException ("type");
355 RegisterScript (ref startupScriptBlocks, type, key, script, addScriptTags);
358 public void RegisterClientScriptInclude (string key, string url)
360 RegisterClientScriptInclude (GetType (), key, url);
363 public void RegisterClientScriptInclude (Type type, string key, string url)
366 throw new ArgumentNullException ("type");
367 if (url == null || url.Length == 0)
368 throw new ArgumentException ("url");
370 RegisterScript (ref scriptIncludes, type, key, url, false);
374 public void RegisterClientScriptResource (Type type, string resourceName)
376 RegisterScript (ref scriptIncludes, type, "resource-" + resourceName, GetWebResourceUrl (type, resourceName), false);
379 public void RegisterExpandoAttribute (string controlId, string attributeName, string attributeValue)
381 RegisterExpandoAttribute (controlId, attributeName, attributeValue, true);
384 public void RegisterExpandoAttribute (string controlId, string attributeName, string attributeValue, bool encode)
386 if (controlId == null)
387 throw new ArgumentNullException ("controlId");
389 if (attributeName == null)
390 throw new ArgumentNullException ("attributeName");
392 if (expandoAttributes == null)
393 expandoAttributes = new Hashtable ();
395 ListDictionary list = (ListDictionary)expandoAttributes [controlId];
397 list = new ListDictionary ();
398 expandoAttributes [controlId] = list;
401 list.Add (attributeName, encode ? StrUtils.EscapeQuotesAndBackslashes (attributeValue) : attributeValue);
404 // Implemented following the description in http://odetocode.com/Blogs/scott/archive/2006/03/20/3145.aspx
405 private int CalculateEventHash (string uniqueId, string argument)
407 int uniqueIdHash = uniqueId.GetHashCode ();
408 int argumentHash = String.IsNullOrEmpty (argument) ? 0 : argument.GetHashCode ();
409 return (uniqueIdHash ^ argumentHash);
412 public void RegisterForEventValidation (PostBackOptions options)
414 // MS.NET does not check for options == null, so we won't too...
415 RegisterForEventValidation (options.TargetControl.UniqueID, options.Argument);
418 public void RegisterForEventValidation (string uniqueId)
420 RegisterForEventValidation (uniqueId, null);
423 public void RegisterForEventValidation (string uniqueId, string argument)
425 if (!page.EnableEventValidation)
427 if (uniqueId == null || uniqueId.Length == 0)
429 if (page.LifeCycle < PageLifeCycle.Render)
430 throw new InvalidOperationException ("RegisterForEventValidation may only be called from the Render method");
431 if (eventValidationValues == null)
432 eventValidationValues = new List <int> ();
435 int hash = CalculateEventHash (uniqueId, argument);
436 if (eventValidationValues.BinarySearch (hash) < 0)
437 eventValidationValues.Add (hash);
440 public void ValidateEvent (string uniqueId)
442 ValidateEvent (uniqueId, null);
445 public void ValidateEvent (string uniqueId, string argument)
447 if (uniqueId == null || uniqueId.Length == 0)
448 throw new ArgumentException ("must not be null or empty", "uniqueId");
449 if (!page.EnableEventValidation)
451 if (eventValidationValues == null)
454 int hash = CalculateEventHash (uniqueId, argument);
455 if (eventValidationValues.BinarySearch (hash) < 0)
460 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.");
463 void WriteScripts (HtmlTextWriter writer, ScriptEntry scriptList)
465 while (scriptList != null) {
466 writer.WriteLine (scriptList.Script);
467 scriptList = scriptList.Next;
472 internal void RestoreEventValidationState (string fieldValue)
474 if (!page.EnableEventValidation || fieldValue == null || fieldValue.Length == 0)
476 LosFormatter fmt = page.GetFormatter ();
477 eventValidationValues = (List <int>)fmt.Deserialize (fieldValue);
480 internal void SaveEventValidationState ()
482 if (!page.EnableEventValidation || eventValidationValues == null || eventValidationValues.Count == 0)
484 eventValidationValues.Sort ();
485 LosFormatter fmt = page.GetFormatter ();
486 RegisterHiddenField (EventStateFieldName, fmt.SerializeToBase64 (eventValidationValues));
489 internal string EventStateFieldName
491 get { return "__EVENTVALIDATION"; }
494 internal void WriteExpandoAttributes (HtmlTextWriter writer)
496 if (expandoAttributes == null)
500 writer.WriteLine ("<script type=\"text/javascript\">");
501 writer.WriteLine ("<!--");
503 foreach (string controlId in expandoAttributes.Keys) {
504 writer.WriteLine ("var {0} = document.all ? document.all [\"{0}\"] : document.getElementById (\"{0}\");", controlId);
505 ListDictionary attrs = (ListDictionary) expandoAttributes [controlId];
506 foreach (string attributeName in attrs.Keys) {
507 writer.WriteLine ("{0}.{1} = \"{2}\";", controlId, attributeName, attrs [attributeName]);
510 writer.WriteLine ("// -->");
511 writer.WriteLine ("</script>");
516 internal void WriteHiddenFields (HtmlTextWriter writer)
518 if (hiddenFields == null)
521 foreach (string key in hiddenFields.Keys) {
522 string value = hiddenFields [key] as string;
523 writer.WriteLine ("\n<input type=\"hidden\" name=\"{0}\" id=\"{0}\" value=\"{1}\" />", key, value);
529 internal void WriteClientScriptIncludes (HtmlTextWriter writer)
531 ScriptEntry entry = scriptIncludes;
532 while (entry != null) {
533 writer.WriteLine ("\n<script src=\"{0}\" type=\"text/javascript\"></script>", entry.Script);
538 internal void WriteClientScriptBlocks (HtmlTextWriter writer)
540 WriteScripts (writer, clientScriptBlocks);
543 internal void WriteStartupScriptBlocks (HtmlTextWriter writer)
545 WriteScripts (writer, startupScriptBlocks);
548 internal void WriteArrayDeclares (HtmlTextWriter writer)
550 if (registeredArrayDeclares != null) {
552 writer.WriteLine("<script language=\"javascript\">");
553 writer.WriteLine("<!--");
554 IDictionaryEnumerator arrayEnum = registeredArrayDeclares.GetEnumerator();
555 while (arrayEnum.MoveNext()) {
556 writer.Write("\tvar ");
557 writer.Write(arrayEnum.Key);
558 writer.Write(" = new Array(");
559 IEnumerator arrayListEnum = ((ArrayList) arrayEnum.Value).GetEnumerator();
561 while (arrayListEnum.MoveNext()) {
566 writer.Write(arrayListEnum.Current);
568 writer.WriteLine(");");
570 writer.WriteLine("// -->");
571 writer.WriteLine("</script>");
577 internal string GetClientValidationEvent (string validationGroup) {
578 return "if (typeof(Page_ClientValidate) == 'function') Page_ClientValidate('" + validationGroup + "');";
582 internal string GetClientValidationEvent ()
584 return "if (typeof(Page_ClientValidate) == 'function') Page_ClientValidate();";
588 internal string WriteSubmitStatements ()
590 if (submitStatements == null) return null;
592 StringBuilder sb = new StringBuilder ();
593 ScriptEntry entry = submitStatements;
594 while (entry != null) {
595 sb.Append (entry.Script);
598 return sb.ToString ();
601 internal static string GetScriptLiteral (object ob)
605 else if (ob is string) {
606 string s = (string)ob;
607 s = s.Replace ("\"", "\\\"");
608 return "\"" + s + "\"";
609 } else if (ob is bool) {
610 return ob.ToString().ToLower();
612 return ob.ToString ();
620 public string Script;
621 public ScriptEntry Next;
623 public ScriptEntry (Type type, string key, string script)
633 internal static string EnsureEndsWithSemicolon (string value) {
634 if (value != null && value.Length > 0 && !value.EndsWith (";"))