Add support for multiple fomrs on the client side in case of TARGET_J2EE portal support.
[mono.git] / mcs / class / System.Web / System.Web.UI / ClientScriptManager.cs
1 //
2 // System.Web.UI.ClientScriptManager.cs
3 //
4 // Authors:
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)
9 //
10 // (C) 2002,2003 Ximian, Inc. (http://www.ximian.com)
11 // (c) 2003 Novell, Inc. (http://www.novell.com)
12 //
13
14 //
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:
22 // 
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
25 // 
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.
33 //
34
35 using System;
36 using System.Collections;
37 #if NET_2_0
38 using System.Collections.Generic;
39 #endif
40 using System.Text;
41 using System.Collections.Specialized;
42 using System.Web.Util;
43
44 namespace System.Web.UI
45 {
46         #if NET_2_0
47         public sealed
48         #else
49         internal
50         #endif
51                 class ClientScriptManager
52         {
53                 Hashtable registeredArrayDeclares;
54                 ScriptEntry clientScriptBlocks;
55                 ScriptEntry startupScriptBlocks;
56                 internal Hashtable hiddenFields;
57                 ScriptEntry submitStatements;
58                 ScriptEntry scriptIncludes;
59                 Page page;
60 #if NET_2_0
61                 List <int> eventValidationValues;
62                 Hashtable expandoAttributes;
63 #endif
64                 
65                 internal ClientScriptManager (Page page)
66                 {
67                         this.page = page;
68                 }
69
70 #if !NET_2_0
71                 public string GetPostBackClientEvent (Control control, string argument)
72                 {
73                         return GetPostBackEventReference (control, argument);
74                 }
75 #endif
76
77                 public string GetPostBackClientHyperlink (Control control, string argument)
78                 {
79                         return "javascript:" + GetPostBackEventReference (control, argument);
80                 }
81         
82 #if NET_2_0
83                 public string GetPostBackClientHyperlink (Control control, string argument, bool registerForEventValidation)
84                 {
85                         if (registerForEventValidation)
86                                 RegisterForEventValidation (control.UniqueID, argument);
87                         return "javascript:" + GetPostBackEventReference (control, argument);
88                 }
89 #endif          
90
91 #if !NET_2_0
92                 internal
93 #else
94                 public
95 #endif
96                 string GetPostBackEventReference (Control control, string argument)
97                 {
98                         if (control == null)
99                                 throw new ArgumentNullException ("control");
100                         
101                         page.RequiresPostBackScript ();
102                         return String.Format ("__doPostBack('{0}','{1}')", control.UniqueID, argument);
103                 }
104
105 #if NET_2_0
106                 public string GetPostBackEventReference (Control control, string argument, bool registerForEventValidation)
107                 {
108                         if (control == null)
109                                 throw new ArgumentNullException ("control");
110                         
111                         if (registerForEventValidation)
112                                 RegisterForEventValidation (control.UniqueID, argument);
113                         return GetPostBackEventReference (control, argument);
114                 }
115                 
116                 public string GetPostBackEventReference (PostBackOptions options, bool registerForEventValidation)
117                 {
118                         if (options == null)
119                                 throw new ArgumentNullException ("options");
120                         if (registerForEventValidation)
121                                 RegisterForEventValidation (options);
122                         return GetPostBackEventReference (options);
123                 }
124                 
125                 public string GetPostBackEventReference (PostBackOptions options)
126                 {
127                         if (options == null)
128                                 throw new ArgumentNullException ("options");
129                         
130                         if (options.ActionUrl == null && options.ValidationGroup == null && !options.TrackFocus && 
131                                 !options.AutoPostBack && !options.PerformValidation)
132                         {
133                                 if (!options.ClientSubmit)
134                                         return null;
135
136                                 if (options.RequiresJavaScriptProtocol)
137                                         return GetPostBackClientHyperlink (options.TargetControl, options.Argument);
138                                 else
139                                         return GetPostBackEventReference (options.TargetControl, options.Argument);
140                         }
141                         
142                         if (options.ActionUrl != null)
143                                 RegisterHiddenField (Page.PreviousPageID, page.Request.FilePath);
144                         
145                         if (options.ClientSubmit || options.ActionUrl != null)
146                                 page.RequiresPostBackScript ();
147                         
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)
158                                 );
159                 }
160
161                 internal void RegisterWebFormClientScript ()
162                 {
163                         if (!IsClientScriptIncludeRegistered (typeof (Page), "webform"))
164                                 RegisterClientScriptInclude (typeof (Page), "webform", GetWebResourceUrl (typeof (Page), "webform.js"));
165
166                         if (!IsClientScriptIncludeRegistered (typeof (Page), "callback"))
167                                 RegisterClientScriptInclude (typeof (Page), "callback", GetWebResourceUrl (typeof (Page), "callback.js"));
168                 }
169                 
170                 public string GetCallbackEventReference (Control control, string argument, string clientCallback, string context)
171                 {
172                         return GetCallbackEventReference (control, argument, clientCallback, context, null, false);
173                 }
174
175                 public string GetCallbackEventReference (Control control, string argument, string clientCallback, string context, bool useAsync)
176                 {
177                         return GetCallbackEventReference (control, argument, clientCallback, context, null, useAsync);
178                 }
179
180                 public string GetCallbackEventReference (Control control, string argument, string clientCallback, string context, string clientErrorCallback, bool useAsync)
181                 {
182                         if (control == null)
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.");
186
187                         return GetCallbackEventReference (control.UniqueID, argument, clientCallback, context, clientErrorCallback, useAsync);
188                 }
189
190                 public string GetCallbackEventReference (string target, string argument, string clientCallback, string context, string clientErrorCallback, bool useAsync)
191                 {
192                         page.RequiresPostBackScript ();
193                         
194                         return string.Format ("WebForm_DoCallback('{0}',{1},{2},{3},{4},{5})", target, argument, clientCallback, context, ((clientErrorCallback == null) ? "null" : clientErrorCallback), (useAsync ? "true" : "false"));
195                 }
196 #endif
197                 
198 #if NET_2_0
199                 public
200 #else
201                 internal
202 #endif
203                 string GetWebResourceUrl(Type type, string resourceName)
204                 {
205                         if (type == null)
206                                 throw new ArgumentNullException ("type");
207                 
208                         if (resourceName == null || resourceName.Length == 0)
209                                 throw new ArgumentNullException ("type");
210                 
211                         return System.Web.Handlers.AssemblyResourceLoader.GetResourceUrl (type, resourceName); 
212                 }
213                 
214
215                 public bool IsClientScriptBlockRegistered (string key)
216                 {
217                         return IsScriptRegistered (clientScriptBlocks, GetType(), key);
218                 }
219         
220                 public bool IsClientScriptBlockRegistered (Type type, string key)
221                 {
222                         return IsScriptRegistered (clientScriptBlocks, type, key);
223                 }
224         
225                 public bool IsStartupScriptRegistered (string key)
226                 {
227                         return IsScriptRegistered (startupScriptBlocks, GetType(), key);
228                 }
229         
230                 public bool IsStartupScriptRegistered (Type type, string key)
231                 {
232                         return IsScriptRegistered (startupScriptBlocks, type, key);
233                 }
234                 
235                 public bool IsOnSubmitStatementRegistered (string key)
236                 {
237                         return IsScriptRegistered (submitStatements, GetType(), key);
238                 }
239         
240                 public bool IsOnSubmitStatementRegistered (Type type, string key)
241                 {
242                         return IsScriptRegistered (submitStatements, type, key);
243                 }
244                 
245                 public bool IsClientScriptIncludeRegistered (string key)
246                 {
247                         return IsScriptRegistered (scriptIncludes, GetType(), key);
248                 }
249         
250                 public bool IsClientScriptIncludeRegistered (Type type, string key)
251                 {
252                         return IsScriptRegistered (scriptIncludes, type, key);
253                 }
254                 
255                 bool IsScriptRegistered (ScriptEntry scriptList, Type type, string key)
256                 {
257                         while (scriptList != null) {
258                                 if (scriptList.Type == type && scriptList.Key == key)
259                                         return true;
260                                 scriptList = scriptList.Next;
261                         }
262                         return false;
263                 }
264                 
265                 public void RegisterArrayDeclaration (string arrayName, string arrayValue)
266                 {
267                         if (registeredArrayDeclares == null)
268                                 registeredArrayDeclares = new Hashtable();
269         
270                         if (!registeredArrayDeclares.ContainsKey (arrayName))
271                                 registeredArrayDeclares.Add (arrayName, new ArrayList());
272         
273                         ((ArrayList) registeredArrayDeclares[arrayName]).Add(arrayValue);
274                 }
275         
276                 void RegisterScript (ref ScriptEntry scriptList, Type type, string key, string script, bool addScriptTags)
277                 {
278                         ScriptEntry last = null;
279                         ScriptEntry entry = scriptList;
280
281                         while (entry != null) {
282                                 if (entry.Type == type && entry.Key == key)
283                                         return;
284                                 last = entry;
285                                 entry = entry.Next;
286                         }
287                         
288                         if (addScriptTags)
289                                 script = "<script language=javascript>\n<!--\n" + script + "\n// -->\n</script>";
290
291                         entry = new ScriptEntry (type, key, script);
292                         
293                         if (last != null) last.Next = entry;
294                         else scriptList = entry;
295                 }
296         
297                 internal void RegisterClientScriptBlock (string key, string script)
298                 {
299                         RegisterScript (ref clientScriptBlocks, GetType(), key, script, false);
300                 }
301         
302                 public void RegisterClientScriptBlock (Type type, string key, string script)
303                 {
304                         RegisterClientScriptBlock (type, key, script, false);
305                 }
306         
307                 public void RegisterClientScriptBlock (Type type, string key, string script, bool addScriptTags)
308                 {
309                         if (type == null)
310                                 throw new ArgumentNullException ("type");
311
312                         RegisterScript (ref clientScriptBlocks, type, key, script, addScriptTags);
313                 }
314         
315                 public void RegisterHiddenField (string hiddenFieldName, string hiddenFieldInitialValue)
316                 {
317                         if (hiddenFields == null)
318                                 hiddenFields = new Hashtable ();
319
320                         if (!hiddenFields.ContainsKey (hiddenFieldName))
321                                 hiddenFields.Add (hiddenFieldName, hiddenFieldInitialValue);
322                 }
323         
324                 internal void RegisterOnSubmitStatement (string key, string script)
325                 {
326                         RegisterScript (ref submitStatements, GetType (), key, script, false);
327                 }
328         
329                 public void RegisterOnSubmitStatement (Type type, string key, string script)
330                 {
331                         if (type == null)
332                                 throw new ArgumentNullException ("type");
333                         
334                         RegisterScript (ref submitStatements, type, key, script, false);
335                 }
336         
337                 internal void RegisterStartupScript (string key, string script)
338                 {
339                         RegisterScript (ref startupScriptBlocks, GetType(), key, script, false);
340                 }
341                 
342                 public void RegisterStartupScript (Type type, string key, string script)
343                 {
344                         RegisterStartupScript (type, key, script, false);
345                 }
346                 
347                 public void RegisterStartupScript (Type type, string key, string script, bool addScriptTags)
348                 {
349                         if (type == null)
350                                 throw new ArgumentNullException ("type");
351
352                         RegisterScript (ref startupScriptBlocks, type, key, script, addScriptTags);
353                 }
354
355                 public void RegisterClientScriptInclude (string key, string url)
356                 {
357                         RegisterClientScriptInclude (GetType (), key, url);
358                 }
359                 
360                 public void RegisterClientScriptInclude (Type type, string key, string url)
361                 {
362                         if (type == null)
363                                 throw new ArgumentNullException ("type");
364                         if (url == null || url.Length == 0)
365                                 throw new ArgumentException ("url");
366
367                         RegisterScript (ref scriptIncludes, type, key, url, false);
368                 }
369
370 #if NET_2_0
371                 public void RegisterClientScriptResource (Type type, string resourceName)
372                 {
373                         RegisterScript (ref scriptIncludes, type, "resource-" + resourceName, GetWebResourceUrl (type, resourceName), false);
374                 }
375
376                 public void RegisterExpandoAttribute (string controlId, string attributeName, string attributeValue)
377                 {
378                         RegisterExpandoAttribute (controlId, attributeName, attributeValue, true);
379                 }
380
381                 public void RegisterExpandoAttribute (string controlId, string attributeName, string attributeValue, bool encode)
382                 {
383                         if (controlId == null)
384                                 throw new ArgumentNullException ("controlId");
385
386                         if (attributeName == null)
387                                 throw new ArgumentNullException ("attributeName");
388                         
389                         if (expandoAttributes == null)
390                                 expandoAttributes = new Hashtable ();
391
392                         ListDictionary list = (ListDictionary)expandoAttributes [controlId];
393                         if (list == null) {
394                                 list = new ListDictionary ();
395                                 expandoAttributes [controlId] = list;
396                         }
397
398                         list.Add (attributeName, encode ? StrUtils.EscapeQuotesAndBackslashes (attributeValue) : attributeValue);
399                 }
400                 
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)
403                 {
404                         int uniqueIdHash = uniqueId.GetHashCode ();
405                         int argumentHash = String.IsNullOrEmpty (argument) ? 0 : argument.GetHashCode ();
406                         return (uniqueIdHash ^ argumentHash);
407                 }
408                 
409                 public void RegisterForEventValidation (PostBackOptions options)
410                 {
411                         // MS.NET does not check for options == null, so we won't too...
412                         RegisterForEventValidation (options.TargetControl.UniqueID, options.Argument);
413                 }
414                 
415                 public void RegisterForEventValidation (string uniqueId)
416                 {
417                         RegisterForEventValidation (uniqueId, null);
418                 }
419                 
420                 public void RegisterForEventValidation (string uniqueId, string argument)
421                 {
422                         if (!page.EnableEventValidation)
423                                 return;
424                         if (uniqueId == null || uniqueId.Length == 0)
425                                 return;
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> ();
430
431                         
432                         int hash = CalculateEventHash (uniqueId, argument);
433                         if (eventValidationValues.BinarySearch (hash) < 0)
434                                 eventValidationValues.Add (hash);
435                 }
436
437                 public void ValidateEvent (string uniqueId)
438                 {
439                         ValidateEvent (uniqueId, null);
440                 }
441
442                 public void ValidateEvent (string uniqueId, string argument)
443                 {
444                         if (uniqueId == null || uniqueId.Length == 0)
445                                 throw new ArgumentException ("must not be null or empty", "uniqueId");
446                         if (!page.EnableEventValidation)
447                                 return;
448                         if (eventValidationValues == null)
449                                 goto bad;
450                         
451                         int hash = CalculateEventHash (uniqueId, argument);
452                         if (eventValidationValues.BinarySearch (hash) < 0)
453                                 goto bad;
454                         return;
455                         
456                         bad:
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.");
458                 }
459 #endif
460                 void WriteScripts (HtmlTextWriter writer, ScriptEntry scriptList)
461                 {
462                         while (scriptList != null) {
463                                 writer.WriteLine (scriptList.Script);
464                                 scriptList = scriptList.Next;
465                         }
466                 }
467
468 #if NET_2_0
469                 internal void RestoreEventValidationState (string fieldValue)
470                 {
471                         if (!page.EnableEventValidation || fieldValue == null || fieldValue.Length == 0)
472                                 return;
473                         IStateFormatter fmt = page.GetFormatter ();
474                         eventValidationValues = (List <int>)fmt.Deserialize (fieldValue);
475                 }
476                 
477                 internal void SaveEventValidationState ()
478                 {
479                         if (!page.EnableEventValidation || eventValidationValues == null || eventValidationValues.Count == 0)
480                                 return;
481                         eventValidationValues.Sort ();
482                         IStateFormatter fmt = page.GetFormatter ();
483                         RegisterHiddenField (EventStateFieldName, fmt.Serialize (eventValidationValues));
484                 }
485
486                 internal string EventStateFieldName
487                 {
488                         get { return "__EVENTVALIDATION"; }
489                 }
490
491                 internal void WriteExpandoAttributes (HtmlTextWriter writer)
492                 {
493                         if (expandoAttributes == null)
494                                 return;
495
496                         writer.WriteLine ();
497                         writer.WriteLine ("<script type=\"text/javascript\">");
498                         writer.WriteLine ("<!--");
499
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]);
505                                 }
506                         }
507                         writer.WriteLine ("// -->");
508                         writer.WriteLine ("</script>");
509                         writer.WriteLine ();
510                 }
511 #endif
512                 
513                 internal void WriteHiddenFields (HtmlTextWriter writer)
514                 {
515                         if (hiddenFields == null)
516                                 return;
517         
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);
521                         }
522         
523                         hiddenFields = null;
524                 }
525                 
526                 internal void WriteClientScriptIncludes (HtmlTextWriter writer)
527                 {
528                         ScriptEntry entry = scriptIncludes;
529                         while (entry != null) {
530                                 writer.WriteLine ("\n<script src=\"{0}\" type=\"text/javascript\"></script>", entry.Script);
531                                 entry = entry.Next;
532                         }
533                 }
534                 
535                 internal void WriteClientScriptBlocks (HtmlTextWriter writer)
536                 {
537                         WriteScripts (writer, clientScriptBlocks);
538                 }
539         
540                 internal void WriteStartupScriptBlocks (HtmlTextWriter writer)
541                 {
542                         WriteScripts (writer, startupScriptBlocks);
543                 }
544         
545                 internal void WriteArrayDeclares (HtmlTextWriter writer)
546                 {
547                         if (registeredArrayDeclares != null) {
548                                 writer.WriteLine();
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();
557                                         bool isFirst = true;
558                                         while (arrayListEnum.MoveNext()) {
559                                                 if (isFirst)
560                                                         isFirst = false;
561                                                 else
562                                                         writer.Write(", ");
563                                                 writer.Write(arrayListEnum.Current);
564                                         }
565                                         writer.WriteLine(");");
566                                 }
567                                 writer.WriteLine("// -->");
568                                 writer.WriteLine("</script>");
569                                 writer.WriteLine();
570                         }
571                 }
572
573 #if NET_2_0
574                 internal string GetClientValidationEvent (string validationGroup) {
575                         return "if (typeof(Page_ClientValidate) == 'function') Page_ClientValidate('" + validationGroup + "');";
576                 }
577 #endif
578
579                 internal string GetClientValidationEvent ()
580                 {
581                         return "if (typeof(Page_ClientValidate) == 'function') Page_ClientValidate();";
582                 }
583
584
585                 internal string WriteSubmitStatements ()
586                 {
587                         if (submitStatements == null) return null;
588                         
589                         StringBuilder sb = new StringBuilder ();
590                         ScriptEntry entry = submitStatements;
591                         while (entry != null) {
592                                 sb.Append (entry.Script);
593                                 entry = entry.Next;
594                         }
595                         return sb.ToString ();
596                 }
597                 
598                 internal static string GetScriptLiteral (object ob)
599                 {
600                         if (ob == null)
601                                 return "null";
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();
608                         } else {
609                                 return ob.ToString ();
610                         }
611                 }
612                 
613                 class ScriptEntry
614                 {
615                         public Type Type;
616                         public string Key;
617                         public string Script;
618                         public ScriptEntry Next;
619                          
620                         public ScriptEntry (Type type, string key, string script)
621                         {
622                                 Key = key;
623                                 Type = type;
624                                 Script = script;
625                         }
626                 }
627
628 #if NET_2_0
629                 // helper method
630                 internal static string EnsureEndsWithSemicolon (string value) {
631                         if (value != null && value.Length > 0 && !value.EndsWith (";"))
632                                 return value += ";";
633                         return value;
634                 }
635 #endif
636         }
637 }