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 internal Hashtable hiddenFields;
57 ScriptEntry submitStatements;
58 ScriptEntry scriptIncludes;
61 List <int> eventValidationValues;
62 Hashtable expandoAttributes;
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 if (options.ActionUrl != null)
143 RegisterHiddenField (Page.PreviousPageID, page.Request.FilePath);
145 if (options.ClientSubmit || options.ActionUrl != null)
146 page.RequiresPostBackScript ();
148 return String.Format ("{0}WebForm_DoPostback({1},{2},{3},{4},{5},{6},{7},{8})",
149 options.RequiresJavaScriptProtocol ? "javascript:" : "",
150 ClientScriptManager.GetScriptLiteral (options.TargetControl.UniqueID),
151 ClientScriptManager.GetScriptLiteral (options.Argument),
152 ClientScriptManager.GetScriptLiteral (options.ActionUrl),
153 ClientScriptManager.GetScriptLiteral (options.AutoPostBack),
154 ClientScriptManager.GetScriptLiteral (options.PerformValidation),
155 ClientScriptManager.GetScriptLiteral (options.TrackFocus),
156 ClientScriptManager.GetScriptLiteral (options.ClientSubmit),
157 ClientScriptManager.GetScriptLiteral (options.ValidationGroup)
161 internal void RegisterWebFormClientScript ()
163 if (!IsClientScriptIncludeRegistered (typeof (Page), "webform"))
164 RegisterClientScriptInclude (typeof (Page), "webform", GetWebResourceUrl (typeof (Page), "webform.js"));
166 if (!IsClientScriptIncludeRegistered (typeof (Page), "callback"))
167 RegisterClientScriptInclude (typeof (Page), "callback", GetWebResourceUrl (typeof (Page), "callback.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 return string.Format ("WebForm_DoCallback('{0}',{1},{2},{3},{4},{5})", target, argument, clientCallback, context, ((clientErrorCallback == null) ? "null" : clientErrorCallback), (useAsync ? "true" : "false"));
203 string GetWebResourceUrl(Type type, string resourceName)
206 throw new ArgumentNullException ("type");
208 if (resourceName == null || resourceName.Length == 0)
209 throw new ArgumentNullException ("type");
211 return System.Web.Handlers.AssemblyResourceLoader.GetResourceUrl (type, resourceName);
215 public bool IsClientScriptBlockRegistered (string key)
217 return IsScriptRegistered (clientScriptBlocks, GetType(), key);
220 public bool IsClientScriptBlockRegistered (Type type, string key)
222 return IsScriptRegistered (clientScriptBlocks, type, key);
225 public bool IsStartupScriptRegistered (string key)
227 return IsScriptRegistered (startupScriptBlocks, GetType(), key);
230 public bool IsStartupScriptRegistered (Type type, string key)
232 return IsScriptRegistered (startupScriptBlocks, type, key);
235 public bool IsOnSubmitStatementRegistered (string key)
237 return IsScriptRegistered (submitStatements, GetType(), key);
240 public bool IsOnSubmitStatementRegistered (Type type, string key)
242 return IsScriptRegistered (submitStatements, type, key);
245 public bool IsClientScriptIncludeRegistered (string key)
247 return IsScriptRegistered (scriptIncludes, GetType(), key);
250 public bool IsClientScriptIncludeRegistered (Type type, string key)
252 return IsScriptRegistered (scriptIncludes, type, key);
255 bool IsScriptRegistered (ScriptEntry scriptList, Type type, string key)
257 while (scriptList != null) {
258 if (scriptList.Type == type && scriptList.Key == key)
260 scriptList = scriptList.Next;
265 public void RegisterArrayDeclaration (string arrayName, string arrayValue)
267 if (registeredArrayDeclares == null)
268 registeredArrayDeclares = new Hashtable();
270 if (!registeredArrayDeclares.ContainsKey (arrayName))
271 registeredArrayDeclares.Add (arrayName, new ArrayList());
273 ((ArrayList) registeredArrayDeclares[arrayName]).Add(arrayValue);
276 void RegisterScript (ref ScriptEntry scriptList, Type type, string key, string script, bool addScriptTags)
278 ScriptEntry last = null;
279 ScriptEntry entry = scriptList;
281 while (entry != null) {
282 if (entry.Type == type && entry.Key == key)
289 script = "<script language=javascript>\n<!--\n" + script + "\n// -->\n</script>";
291 entry = new ScriptEntry (type, key, script);
293 if (last != null) last.Next = entry;
294 else scriptList = entry;
297 internal void RegisterClientScriptBlock (string key, string script)
299 RegisterScript (ref clientScriptBlocks, GetType(), key, script, false);
302 public void RegisterClientScriptBlock (Type type, string key, string script)
304 RegisterClientScriptBlock (type, key, script, false);
307 public void RegisterClientScriptBlock (Type type, string key, string script, bool addScriptTags)
310 throw new ArgumentNullException ("type");
312 RegisterScript (ref clientScriptBlocks, type, key, script, addScriptTags);
315 public void RegisterHiddenField (string hiddenFieldName, string hiddenFieldInitialValue)
317 if (hiddenFields == null)
318 hiddenFields = new Hashtable ();
320 if (!hiddenFields.ContainsKey (hiddenFieldName))
321 hiddenFields.Add (hiddenFieldName, hiddenFieldInitialValue);
324 internal void RegisterOnSubmitStatement (string key, string script)
326 RegisterScript (ref submitStatements, GetType (), key, script, false);
329 public void RegisterOnSubmitStatement (Type type, string key, string script)
332 throw new ArgumentNullException ("type");
334 RegisterScript (ref submitStatements, type, key, script, false);
337 internal void RegisterStartupScript (string key, string script)
339 RegisterScript (ref startupScriptBlocks, GetType(), key, script, false);
342 public void RegisterStartupScript (Type type, string key, string script)
344 RegisterStartupScript (type, key, script, false);
347 public void RegisterStartupScript (Type type, string key, string script, bool addScriptTags)
350 throw new ArgumentNullException ("type");
352 RegisterScript (ref startupScriptBlocks, type, key, script, addScriptTags);
355 public void RegisterClientScriptInclude (string key, string url)
357 RegisterClientScriptInclude (GetType (), key, url);
360 public void RegisterClientScriptInclude (Type type, string key, string url)
363 throw new ArgumentNullException ("type");
364 if (url == null || url.Length == 0)
365 throw new ArgumentException ("url");
367 RegisterScript (ref scriptIncludes, type, key, url, false);
371 public void RegisterClientScriptResource (Type type, string resourceName)
373 RegisterScript (ref scriptIncludes, type, "resource-" + resourceName, GetWebResourceUrl (type, resourceName), false);
376 public void RegisterExpandoAttribute (string controlId, string attributeName, string attributeValue)
378 RegisterExpandoAttribute (controlId, attributeName, attributeValue, true);
381 public void RegisterExpandoAttribute (string controlId, string attributeName, string attributeValue, bool encode)
383 if (controlId == null)
384 throw new ArgumentNullException ("controlId");
386 if (attributeName == null)
387 throw new ArgumentNullException ("attributeName");
389 if (expandoAttributes == null)
390 expandoAttributes = new Hashtable ();
392 ListDictionary list = (ListDictionary)expandoAttributes [controlId];
394 list = new ListDictionary ();
395 expandoAttributes [controlId] = list;
398 list.Add (attributeName, encode ? StrUtils.EscapeQuotesAndBackslashes (attributeValue) : attributeValue);
401 // Implemented following the description in http://odetocode.com/Blogs/scott/archive/2006/03/20/3145.aspx
402 private int CalculateEventHash (string uniqueId, string argument)
404 int uniqueIdHash = uniqueId.GetHashCode ();
405 int argumentHash = String.IsNullOrEmpty (argument) ? 0 : argument.GetHashCode ();
406 return (uniqueIdHash ^ argumentHash);
409 public void RegisterForEventValidation (PostBackOptions options)
411 // MS.NET does not check for options == null, so we won't too...
412 RegisterForEventValidation (options.TargetControl.UniqueID, options.Argument);
415 public void RegisterForEventValidation (string uniqueId)
417 RegisterForEventValidation (uniqueId, null);
420 public void RegisterForEventValidation (string uniqueId, string argument)
422 if (!page.EnableEventValidation)
424 if (uniqueId == null || uniqueId.Length == 0)
426 if (page.LifeCycle < PageLifeCycle.Render)
427 throw new InvalidOperationException ("RegisterForEventValidation may only be called from the Render method");
428 if (eventValidationValues == null)
429 eventValidationValues = new List <int> ();
432 int hash = CalculateEventHash (uniqueId, argument);
433 if (eventValidationValues.BinarySearch (hash) < 0)
434 eventValidationValues.Add (hash);
437 public void ValidateEvent (string uniqueId)
439 ValidateEvent (uniqueId, null);
442 public void ValidateEvent (string uniqueId, string argument)
444 if (uniqueId == null || uniqueId.Length == 0)
445 throw new ArgumentException ("must not be null or empty", "uniqueId");
446 if (!page.EnableEventValidation)
448 if (eventValidationValues == null)
451 int hash = CalculateEventHash (uniqueId, argument);
452 if (eventValidationValues.BinarySearch (hash) < 0)
457 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.");
460 void WriteScripts (HtmlTextWriter writer, ScriptEntry scriptList)
462 while (scriptList != null) {
463 writer.WriteLine (scriptList.Script);
464 scriptList = scriptList.Next;
469 internal void RestoreEventValidationState (string fieldValue)
471 if (!page.EnableEventValidation || fieldValue == null || fieldValue.Length == 0)
473 IStateFormatter fmt = page.GetFormatter ();
474 eventValidationValues = (List <int>)fmt.Deserialize (fieldValue);
477 internal void SaveEventValidationState ()
479 if (!page.EnableEventValidation || eventValidationValues == null || eventValidationValues.Count == 0)
481 eventValidationValues.Sort ();
482 IStateFormatter fmt = page.GetFormatter ();
483 RegisterHiddenField (EventStateFieldName, fmt.Serialize (eventValidationValues));
486 internal string EventStateFieldName
488 get { return "__EVENTVALIDATION"; }
491 internal void WriteExpandoAttributes (HtmlTextWriter writer)
493 if (expandoAttributes == null)
497 writer.WriteLine ("<script type=\"text/javascript\">");
498 writer.WriteLine ("<!--");
500 foreach (string controlId in expandoAttributes.Keys) {
501 writer.WriteLine ("var {0} = document.all ? document.all [\"{0}\"] : document.getElementById (\"{0}\");", controlId);
502 ListDictionary attrs = (ListDictionary) expandoAttributes [controlId];
503 foreach (string attributeName in attrs.Keys) {
504 writer.WriteLine ("{0}.{1} = \"{2}\";", controlId, attributeName, attrs [attributeName]);
507 writer.WriteLine ("// -->");
508 writer.WriteLine ("</script>");
513 internal void WriteHiddenFields (HtmlTextWriter writer)
515 if (hiddenFields == null)
518 foreach (string key in hiddenFields.Keys) {
519 string value = hiddenFields [key] as string;
520 writer.WriteLine ("\n<input type=\"hidden\" name=\"{0}\" id=\"{0}\" value=\"{1}\" />", key, value);
526 internal void WriteClientScriptIncludes (HtmlTextWriter writer)
528 ScriptEntry entry = scriptIncludes;
529 while (entry != null) {
530 writer.WriteLine ("\n<script src=\"{0}\" type=\"text/javascript\"></script>", entry.Script);
535 internal void WriteClientScriptBlocks (HtmlTextWriter writer)
537 WriteScripts (writer, clientScriptBlocks);
540 internal void WriteStartupScriptBlocks (HtmlTextWriter writer)
542 WriteScripts (writer, startupScriptBlocks);
545 internal void WriteArrayDeclares (HtmlTextWriter writer)
547 if (registeredArrayDeclares != null) {
549 writer.WriteLine("<script language=\"javascript\">");
550 writer.WriteLine("<!--");
551 IDictionaryEnumerator arrayEnum = registeredArrayDeclares.GetEnumerator();
552 while (arrayEnum.MoveNext()) {
553 writer.Write("\tvar ");
554 writer.Write(arrayEnum.Key);
555 writer.Write(" = new Array(");
556 IEnumerator arrayListEnum = ((ArrayList) arrayEnum.Value).GetEnumerator();
558 while (arrayListEnum.MoveNext()) {
563 writer.Write(arrayListEnum.Current);
565 writer.WriteLine(");");
567 writer.WriteLine("// -->");
568 writer.WriteLine("</script>");
574 internal string GetClientValidationEvent (string validationGroup) {
575 return "if (typeof(Page_ClientValidate) == 'function') Page_ClientValidate('" + validationGroup + "');";
579 internal string GetClientValidationEvent ()
581 return "if (typeof(Page_ClientValidate) == 'function') Page_ClientValidate();";
585 internal string WriteSubmitStatements ()
587 if (submitStatements == null) return null;
589 StringBuilder sb = new StringBuilder ();
590 ScriptEntry entry = submitStatements;
591 while (entry != null) {
592 sb.Append (entry.Script);
595 return sb.ToString ();
598 internal static string GetScriptLiteral (object ob)
602 else if (ob is string) {
603 string s = (string)ob;
604 s = s.Replace ("\"", "\\\"");
605 return "\"" + s + "\"";
606 } else if (ob is bool) {
607 return ob.ToString().ToLower();
609 return ob.ToString ();
617 public string Script;
618 public ScriptEntry Next;
620 public ScriptEntry (Type type, string key, string script)
630 internal static string EnsureEndsWithSemicolon (string value) {
631 if (value != null && value.Length > 0 && !value.EndsWith (";"))