a749832d715273bab7dbaa00e31ed453a2d04b5e
[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                 Hashtable hiddenFields;
57                 ScriptEntry submitStatements;
58                 ScriptEntry scriptIncludes;
59                 Hashtable expandoAttributes;
60                 Page page;
61 #if NET_2_0
62                 List <int> eventValidationValues;
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                         RegisterWebFormClientScript ();
143                         
144                         if (options.ActionUrl != null)
145                                 RegisterHiddenField (Page.PreviousPageID, page.Request.FilePath);
146                         
147                         if (options.ClientSubmit || options.ActionUrl != null)
148                                 page.RequiresPostBackScript ();
149                         
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)
160                                 );
161                 }
162
163                 internal void RegisterWebFormClientScript ()
164                 {
165                         if (!IsClientScriptIncludeRegistered (typeof (Page), "webform")) {
166                                 RegisterClientScriptInclude (typeof (Page), "webform", GetWebResourceUrl (typeof (Page), "webform.js"));
167                         }
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                         if (!IsClientScriptIncludeRegistered (typeof(Page), "callback"))
195                                 RegisterClientScriptInclude (typeof(Page), "callback", GetWebResourceUrl (typeof(Page), "callback.js"));
196                         
197                         return string.Format ("WebForm_DoCallback('{0}',{1},{2},{3},{4},{5})", target, argument, clientCallback, context, ((clientErrorCallback == null) ? "null" : clientErrorCallback), (useAsync ? "true" : "false"));
198                 }
199 #endif
200                 
201 #if NET_2_0
202                 public
203 #else
204                 internal
205 #endif
206                 string GetWebResourceUrl(Type type, string resourceName)
207                 {
208                         if (type == null)
209                                 throw new ArgumentNullException ("type");
210                 
211                         if (resourceName == null || resourceName.Length == 0)
212                                 throw new ArgumentNullException ("type");
213                 
214                         return System.Web.Handlers.AssemblyResourceLoader.GetResourceUrl (type, resourceName); 
215                 }
216                 
217
218                 public bool IsClientScriptBlockRegistered (string key)
219                 {
220                         return IsScriptRegistered (clientScriptBlocks, GetType(), key);
221                 }
222         
223                 public bool IsClientScriptBlockRegistered (Type type, string key)
224                 {
225                         return IsScriptRegistered (clientScriptBlocks, type, key);
226                 }
227         
228                 public bool IsStartupScriptRegistered (string key)
229                 {
230                         return IsScriptRegistered (startupScriptBlocks, GetType(), key);
231                 }
232         
233                 public bool IsStartupScriptRegistered (Type type, string key)
234                 {
235                         return IsScriptRegistered (startupScriptBlocks, type, key);
236                 }
237                 
238                 public bool IsOnSubmitStatementRegistered (string key)
239                 {
240                         return IsScriptRegistered (submitStatements, GetType(), key);
241                 }
242         
243                 public bool IsOnSubmitStatementRegistered (Type type, string key)
244                 {
245                         return IsScriptRegistered (submitStatements, type, key);
246                 }
247                 
248                 public bool IsClientScriptIncludeRegistered (string key)
249                 {
250                         return IsScriptRegistered (scriptIncludes, GetType(), key);
251                 }
252         
253                 public bool IsClientScriptIncludeRegistered (Type type, string key)
254                 {
255                         return IsScriptRegistered (scriptIncludes, type, key);
256                 }
257                 
258                 bool IsScriptRegistered (ScriptEntry scriptList, Type type, string key)
259                 {
260                         while (scriptList != null) {
261                                 if (scriptList.Type == type && scriptList.Key == key)
262                                         return true;
263                                 scriptList = scriptList.Next;
264                         }
265                         return false;
266                 }
267                 
268                 public void RegisterArrayDeclaration (string arrayName, string arrayValue)
269                 {
270                         if (registeredArrayDeclares == null)
271                                 registeredArrayDeclares = new Hashtable();
272         
273                         if (!registeredArrayDeclares.ContainsKey (arrayName))
274                                 registeredArrayDeclares.Add (arrayName, new ArrayList());
275         
276                         ((ArrayList) registeredArrayDeclares[arrayName]).Add(arrayValue);
277                 }
278         
279                 void RegisterScript (ref ScriptEntry scriptList, Type type, string key, string script, bool addScriptTags)
280                 {
281                         ScriptEntry last = null;
282                         ScriptEntry entry = scriptList;
283
284                         while (entry != null) {
285                                 if (entry.Type == type && entry.Key == key)
286                                         return;
287                                 last = entry;
288                                 entry = entry.Next;
289                         }
290                         
291                         if (addScriptTags)
292                                 script = "<script language=javascript>\n<!--\n" + script + "\n// -->\n</script>";
293
294                         entry = new ScriptEntry (type, key, script);
295                         
296                         if (last != null) last.Next = entry;
297                         else scriptList = entry;
298                 }
299         
300                 internal void RegisterClientScriptBlock (string key, string script)
301                 {
302                         RegisterScript (ref clientScriptBlocks, GetType(), key, script, false);
303                 }
304         
305                 public void RegisterClientScriptBlock (Type type, string key, string script)
306                 {
307                         RegisterClientScriptBlock (type, key, script, false);
308                 }
309         
310                 public void RegisterClientScriptBlock (Type type, string key, string script, bool addScriptTags)
311                 {
312                         if (type == null)
313                                 throw new ArgumentNullException ("type");
314
315                         RegisterScript (ref clientScriptBlocks, type, key, script, addScriptTags);
316                 }
317         
318                 public void RegisterHiddenField (string hiddenFieldName, string hiddenFieldInitialValue)
319                 {
320                         if (hiddenFields == null)
321                                 hiddenFields = new Hashtable ();
322
323                         if (!hiddenFields.ContainsKey (hiddenFieldName))
324                                 hiddenFields.Add (hiddenFieldName, hiddenFieldInitialValue);
325                 }
326         
327                 internal void RegisterOnSubmitStatement (string key, string script)
328                 {
329                         RegisterScript (ref submitStatements, GetType (), key, script, false);
330                 }
331         
332                 public void RegisterOnSubmitStatement (Type type, string key, string script)
333                 {
334                         if (type == null)
335                                 throw new ArgumentNullException ("type");
336                         
337                         RegisterScript (ref submitStatements, type, key, script, false);
338                 }
339         
340                 internal void RegisterStartupScript (string key, string script)
341                 {
342                         RegisterScript (ref startupScriptBlocks, GetType(), key, script, false);
343                 }
344                 
345                 public void RegisterStartupScript (Type type, string key, string script)
346                 {
347                         RegisterStartupScript (type, key, script, false);
348                 }
349                 
350                 public void RegisterStartupScript (Type type, string key, string script, bool addScriptTags)
351                 {
352                         if (type == null)
353                                 throw new ArgumentNullException ("type");
354
355                         RegisterScript (ref startupScriptBlocks, type, key, script, addScriptTags);
356                 }
357
358                 public void RegisterClientScriptInclude (string key, string url)
359                 {
360                         RegisterClientScriptInclude (GetType (), key, url);
361                 }
362                 
363                 public void RegisterClientScriptInclude (Type type, string key, string url)
364                 {
365                         if (type == null)
366                                 throw new ArgumentNullException ("type");
367                         if (url == null || url.Length == 0)
368                                 throw new ArgumentException ("url");
369
370                         RegisterScript (ref scriptIncludes, type, key, url, false);
371                 }
372
373 #if NET_2_0
374                 public void RegisterClientScriptResource (Type type, string resourceName)
375                 {
376                         RegisterScript (ref scriptIncludes, type, "resource-" + resourceName, GetWebResourceUrl (type, resourceName), false);
377                 }
378
379                 public void RegisterExpandoAttribute (string controlId, string attributeName, string attributeValue)
380                 {
381                         RegisterExpandoAttribute (controlId, attributeName, attributeValue, true);
382                 }
383
384                 public void RegisterExpandoAttribute (string controlId, string attributeName, string attributeValue, bool encode)
385                 {
386                         if (controlId == null)
387                                 throw new ArgumentNullException ("controlId");
388
389                         if (attributeName == null)
390                                 throw new ArgumentNullException ("attributeName");
391                         
392                         if (expandoAttributes == null)
393                                 expandoAttributes = new Hashtable ();
394
395                         ListDictionary list = (ListDictionary)expandoAttributes [controlId];
396                         if (list == null) {
397                                 list = new ListDictionary ();
398                                 expandoAttributes [controlId] = list;
399                         }
400
401                         list.Add (attributeName, encode ? StrUtils.EscapeQuotesAndBackslashes (attributeValue) : attributeValue);
402                 }
403                 
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)
406                 {
407                         int uniqueIdHash = uniqueId.GetHashCode ();
408                         int argumentHash = String.IsNullOrEmpty (argument) ? 0 : argument.GetHashCode ();
409                         return (uniqueIdHash ^ argumentHash);
410                 }
411                 
412                 public void RegisterForEventValidation (PostBackOptions options)
413                 {
414                         // MS.NET does not check for options == null, so we won't too...
415                         RegisterForEventValidation (options.TargetControl.UniqueID, options.Argument);
416                 }
417                 
418                 public void RegisterForEventValidation (string uniqueId)
419                 {
420                         RegisterForEventValidation (uniqueId, null);
421                 }
422                 
423                 public void RegisterForEventValidation (string uniqueId, string argument)
424                 {
425                         if (!page.EnableEventValidation)
426                                 return;
427                         if (uniqueId == null || uniqueId.Length == 0)
428                                 return;
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> ();
433
434                         
435                         int hash = CalculateEventHash (uniqueId, argument);
436                         if (eventValidationValues.BinarySearch (hash) < 0)
437                                 eventValidationValues.Add (hash);
438                 }
439
440                 public void ValidateEvent (string uniqueId)
441                 {
442                         ValidateEvent (uniqueId, null);
443                 }
444
445                 public void ValidateEvent (string uniqueId, string argument)
446                 {
447                         if (uniqueId == null || uniqueId.Length == 0)
448                                 throw new ArgumentException ("must not be null or empty", "uniqueId");
449                         if (!page.EnableEventValidation)
450                                 return;
451                         if (eventValidationValues == null)
452                                 goto bad;
453                         
454                         int hash = CalculateEventHash (uniqueId, argument);
455                         if (eventValidationValues.BinarySearch (hash) < 0)
456                                 goto bad;
457                         return;
458                         
459                         bad:
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.");
461                 }
462 #endif
463                 void WriteScripts (HtmlTextWriter writer, ScriptEntry scriptList)
464                 {
465                         while (scriptList != null) {
466                                 writer.WriteLine (scriptList.Script);
467                                 scriptList = scriptList.Next;
468                         }
469                 }
470
471 #if NET_2_0
472                 internal void RestoreEventValidationState (string fieldValue)
473                 {
474                         if (!page.EnableEventValidation || fieldValue == null || fieldValue.Length == 0)
475                                 return;
476                         LosFormatter fmt = page.GetFormatter ();
477                         eventValidationValues = (List <int>)fmt.Deserialize (fieldValue);
478                 }
479                 
480                 internal void SaveEventValidationState ()
481                 {
482                         if (!page.EnableEventValidation || eventValidationValues == null || eventValidationValues.Count == 0)
483                                 return;
484                         eventValidationValues.Sort ();
485                         LosFormatter fmt = page.GetFormatter ();
486                         RegisterHiddenField (EventStateFieldName, fmt.SerializeToBase64 (eventValidationValues));
487                 }
488
489                 internal string EventStateFieldName
490                 {
491                         get { return "__EVENTVALIDATION"; }
492                 }
493
494                 internal void WriteExpandoAttributes (HtmlTextWriter writer)
495                 {
496                         if (expandoAttributes == null)
497                                 return;
498
499                         writer.WriteLine ();
500                         writer.WriteLine ("<script type=\"text/javascript\">");
501                         writer.WriteLine ("<!--");
502
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]);
508                                 }
509                         }
510                         writer.WriteLine ("// -->");
511                         writer.WriteLine ("</script>");
512                         writer.WriteLine ();
513                 }
514 #endif
515                 
516                 internal void WriteHiddenFields (HtmlTextWriter writer)
517                 {
518                         if (hiddenFields == null)
519                                 return;
520         
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);
524                         }
525         
526                         hiddenFields = null;
527                 }
528                 
529                 internal void WriteClientScriptIncludes (HtmlTextWriter writer)
530                 {
531                         ScriptEntry entry = scriptIncludes;
532                         while (entry != null) {
533                                 writer.WriteLine ("\n<script src=\"{0}\" type=\"text/javascript\"></script>", entry.Script);
534                                 entry = entry.Next;
535                         }
536                 }
537                 
538                 internal void WriteClientScriptBlocks (HtmlTextWriter writer)
539                 {
540                         WriteScripts (writer, clientScriptBlocks);
541                 }
542         
543                 internal void WriteStartupScriptBlocks (HtmlTextWriter writer)
544                 {
545                         WriteScripts (writer, startupScriptBlocks);
546                 }
547         
548                 internal void WriteArrayDeclares (HtmlTextWriter writer)
549                 {
550                         if (registeredArrayDeclares != null) {
551                                 writer.WriteLine();
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();
560                                         bool isFirst = true;
561                                         while (arrayListEnum.MoveNext()) {
562                                                 if (isFirst)
563                                                         isFirst = false;
564                                                 else
565                                                         writer.Write(", ");
566                                                 writer.Write(arrayListEnum.Current);
567                                         }
568                                         writer.WriteLine(");");
569                                 }
570                                 writer.WriteLine("// -->");
571                                 writer.WriteLine("</script>");
572                                 writer.WriteLine();
573                         }
574                 }
575
576 #if NET_2_0
577                 internal string GetClientValidationEvent (string validationGroup) {
578                         return "if (typeof(Page_ClientValidate) == 'function') Page_ClientValidate('" + validationGroup + "');";
579                 }
580 #endif
581
582                 internal string GetClientValidationEvent ()
583                 {
584                         return "if (typeof(Page_ClientValidate) == 'function') Page_ClientValidate();";
585                 }
586
587
588                 internal string WriteSubmitStatements ()
589                 {
590                         if (submitStatements == null) return null;
591                         
592                         StringBuilder sb = new StringBuilder ();
593                         ScriptEntry entry = submitStatements;
594                         while (entry != null) {
595                                 sb.Append (entry.Script);
596                                 entry = entry.Next;
597                         }
598                         return sb.ToString ();
599                 }
600                 
601                 internal static string GetScriptLiteral (object ob)
602                 {
603                         if (ob == null)
604                                 return "null";
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();
611                         } else {
612                                 return ob.ToString ();
613                         }
614                 }
615                 
616                 class ScriptEntry
617                 {
618                         public Type Type;
619                         public string Key;
620                         public string Script;
621                         public ScriptEntry Next;
622                          
623                         public ScriptEntry (Type type, string key, string script)
624                         {
625                                 Key = key;
626                                 Type = type;
627                                 Script = script;
628                         }
629                 }
630
631 #if NET_2_0
632                 // helper method
633                 internal static string EnsureEndsWithSemicolon (string value) {
634                         if (value != null && value.Length > 0 && !value.EndsWith (";"))
635                                 return value += ";";
636                         return value;
637                 }
638 #endif
639         }
640 }