1 //------------------------------------------------------------------------------
2 // <copyright file="ClientScriptManager.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6 
7 namespace System.Web.UI {
8     using System;
9     using System.ComponentModel;
10     using System.ComponentModel.Design;
11     using System.Collections;
12     using System.Collections.Specialized;
13     using System.Globalization;
14     using System.Text;
15     using System.Web.Compilation;
16     using System.Web.Handlers;
17     using System.Web.UI.WebControls;
18     using System.Web.Util;
19     using ExceptionUtil=System.Web.Util.ExceptionUtil;
20     using WebUtil = System.Web.Util;
21     using System.Security.Permissions;
22     using System.Reflection;
23     using System.Runtime.Serialization;
24     using System.Collections.Generic;
25     using System.Web.Security.Cryptography;
26 
27     // The various types of client API's that can be registered
28     internal enum ClientAPIRegisterType {
29         WebFormsScript,
30         PostBackScript,
31         FocusScript,
32         ClientScriptBlocks,
33         ClientScriptBlocksWithoutTags,
34         ClientStartupScripts,
35         ClientStartupScriptsWithoutTags,
36         OnSubmitStatement,
37         ArrayDeclaration,
38         HiddenField,
39         ExpandoAttribute,
40         EventValidation,
41     }
42 
43     public sealed class ClientScriptManager {
44         private const string IncludeScriptBegin = @"
45 <script src=""";
46         private const string IncludeScriptEnd = @""" type=""text/javascript""></script>";
47         internal const string ClientScriptStart = "\r\n<script type=\"text/javascript\">\r\n//<![CDATA[\r\n";
48         internal const string ClientScriptStartLegacy = "\r\n<script type=\"text/javascript\">\r\n<!--\r\n";
49         internal const string ClientScriptEnd = "//]]>\r\n</script>\r\n";
50         internal const string ClientScriptEndLegacy = "// -->\r\n</script>\r\n";
51         internal const string JscriptPrefix = "javascript:";
52 
53         private const string _callbackFunctionName = "WebForm_DoCallback";
54         private const string _postbackOptionsFunctionName = "WebForm_DoPostBackWithOptions";
55         private const string _postBackFunctionName = "__doPostBack";
56         private const string PageCallbackScriptKey = "PageCallbackScript";
57 
58         internal static IScriptResourceMapping _scriptResourceMapping;
59 
60         private ListDictionary _registeredClientScriptBlocks;
61         private ArrayList _clientScriptBlocks;
62         private ListDictionary _registeredClientStartupScripts;
63         private ArrayList _clientStartupScripts;
64         private Dictionary<Assembly, Dictionary<String, Object>> _registeredResourcesToSuppress;
65         private bool _eventValidationFieldLoaded;
66 
67         private ListDictionary _registeredOnSubmitStatements;
68 
69         private IDictionary _registeredArrayDeclares;
70         private ListDictionary _registeredHiddenFields;
71         private ListDictionary _registeredControlsWithExpandoAttributes;
72 
73         private IEventValidationProvider _eventValidationProvider;
74 
75         private Page _owner;
76 
ClientScriptManager(Page owner)77         internal ClientScriptManager(Page owner) {
78             _owner = owner;
79         }
80 
81         internal bool HasRegisteredHiddenFields {
82             get {
83                 return (_registeredHiddenFields != null && _registeredHiddenFields.Count > 0);
84             }
85         }
86 
87         internal bool HasSubmitStatements {
88             get {
89                 return (_registeredOnSubmitStatements != null && _registeredOnSubmitStatements.Count > 0);
90             }
91         }
92 
93         internal Dictionary<Assembly, Dictionary<String, Object>> RegisteredResourcesToSuppress {
94             get {
95                 if (_registeredResourcesToSuppress == null) {
96                     _registeredResourcesToSuppress = new Dictionary<Assembly, Dictionary<String, Object>>();
97                 }
98                 return _registeredResourcesToSuppress;
99             }
100         }
101 
102         private IEventValidationProvider EventValidationProvider {
103             get {
104                 if (_eventValidationProvider == null) {
105                     if (AppSettings.UseLegacyEventValidationCompatibility) {
106                         _eventValidationProvider = new LegacyEventValidationProvider(this);
107                     }
108                     else {
109                         _eventValidationProvider = new DefaultEventValidationProvider(this);
110                     }
111                 }
112                 return _eventValidationProvider;
113             }
114         }
115 
GetEventValidationFieldValue()116         internal string GetEventValidationFieldValue() {
117             // Access the _eventValidationProvider field instead of the EventValidationProvider property so that we
118             // don't end up instantiating objects if not necessary.
119             if (_eventValidationProvider != null) {
120                 object eventValidationStoreObject = _eventValidationProvider.GetEventValidationStoreObject();
121                 if (eventValidationStoreObject != null) {
122                     // Make cryptographically secure
123                     IStateFormatter2 formatter = _owner.CreateStateFormatter();
124                     return formatter.Serialize(eventValidationStoreObject, Purpose.WebForms_ClientScriptManager_EventValidation);
125                 }
126             }
127 
128             // If we got here, there was no store data.
129             return String.Empty;
130         }
131 
RegisterForEventValidation(PostBackOptions options)132         public void RegisterForEventValidation(PostBackOptions options) {
133             RegisterForEventValidation(options.TargetControl.UniqueID, options.Argument);
134         }
135 
RegisterForEventValidation(string uniqueId)136         public void RegisterForEventValidation(string uniqueId) {
137             RegisterForEventValidation(uniqueId, String.Empty);
138         }
139 
RegisterForEventValidation(string uniqueId, string argument)140         public void RegisterForEventValidation(string uniqueId, string argument) {
141             // Step 1: argument and precondition checks
142             if (!_owner.EnableEventValidation || _owner.DesignMode) {
143                 return;
144             }
145 
146             // VSWhidbey 497632. Ignore if uniqueID is empty since the postback won't be valid anyway.
147             if (String.IsNullOrEmpty(uniqueId)) {
148                 return;
149             }
150 
151             if ((_owner.ControlState < ControlState.PreRendered) && (!_owner.IsCallback)) {
152                 throw new InvalidOperationException(
153                     SR.GetString(SR.ClientScriptManager_RegisterForEventValidation_Too_Early));
154             }
155 
156             // Step 2: Add this tuple to the list
157             EventValidationProvider.RegisterForEventValidation(uniqueId, argument);
158 
159             // Step 3: If there are any partial caching controls on the stack, forward the call to them
160             if (_owner.PartialCachingControlStack != null) {
161                 foreach (BasePartialCachingControl c in _owner.PartialCachingControlStack) {
162                     c.RegisterForEventValidation(uniqueId, argument);
163                 }
164             }
165         }
166 
SaveEventValidationField()167         internal void SaveEventValidationField() {
168             string fieldValue = GetEventValidationFieldValue();
169             if (!String.IsNullOrEmpty(fieldValue)) {
170                 RegisterHiddenField(Page.EventValidationPrefixID, fieldValue);
171             }
172         }
173 
174         // Used by unobtrusive javascript validators to verify that a scriptresource mapping for jquery is registered
EnsureJqueryRegistered()175         internal static void EnsureJqueryRegistered() {
176             if (_scriptResourceMapping != null) {
177                 if (_scriptResourceMapping.GetDefinition("jquery", typeof(Page).Assembly) == null &&
178                     _scriptResourceMapping.GetDefinition("jquery") == null) {
179                     throw new InvalidOperationException(SR.GetString(SR.ClientScriptManager_JqueryNotRegistered));
180                 }
181             }
182         }
183 
EnsureEventValidationFieldLoaded()184         private void EnsureEventValidationFieldLoaded() {
185             if (_eventValidationFieldLoaded) {
186                 return;
187             }
188 
189             _eventValidationFieldLoaded = true;
190 
191             // Step 1: Read the event validation field
192             string unsafeField = null;
193             if (_owner.RequestValueCollection != null) {
194                 unsafeField = _owner.RequestValueCollection[Page.EventValidationPrefixID];
195             }
196 
197             if (String.IsNullOrEmpty(unsafeField)) {
198                 return;
199             }
200 
201             // Step 2: Decrypt the event validation field
202             IStateFormatter2 formatter = _owner.CreateStateFormatter();
203             object eventValidationField = null;
204             try {
205                 eventValidationField = formatter.Deserialize(unsafeField, Purpose.WebForms_ClientScriptManager_EventValidation);
206             }
207             catch (Exception ex) {
208                 // DevDiv #461378: Ignore validation errors for cross-page postbacks. Since the ValidateEvent method
209                 // is most likely on the call stack right now, this will result in an event validation failure rather
210                 // than a MAC validation failure.
211                 if (!_owner.ShouldSuppressMacValidationException(ex)) {
212                     ViewStateException.ThrowViewStateError(ex, unsafeField);
213                 }
214             }
215 
216             // Step 3: Load the event validation field into the appropriate provider
217             if (!EventValidationProvider.TryLoadEventValidationField(eventValidationField)) {
218                 // Something went wrong while loading the incoming event validation object; the
219                 // most likely cause is that it wasn't submitted with the correct ViewState.
220                 ViewStateException.ThrowViewStateError(null, unsafeField);
221             }
222         }
223 
ValidateEvent(string uniqueId)224         public void ValidateEvent(string uniqueId) {
225             ValidateEvent(uniqueId, String.Empty);
226         }
227 
ValidateEvent(string uniqueId, string argument)228         public void ValidateEvent(string uniqueId, string argument) {
229             if (!_owner.EnableEventValidation) {
230                 return;
231             }
232 
233             if (String.IsNullOrEmpty(uniqueId)) {
234                 throw new ArgumentException(SR.GetString(SR.Parameter_NullOrEmpty, "uniqueId"), "uniqueId");
235             }
236 
237             EnsureEventValidationFieldLoaded();
238 
239             // Go against the _eventValidationProvider field instead of the EventValidationProvider
240             // property to avoid the lazy instantiation code if not necessary.
241             if (_eventValidationProvider == null || !_eventValidationProvider.IsValid(uniqueId, argument)) {
242                 throw new ArgumentException(SR.GetString(SR.ClientScriptManager_InvalidPostBackArgument));
243             }
244         }
245 
ClearHiddenFields()246         internal void ClearHiddenFields() {
247             _registeredHiddenFields = null;
248         }
249 
CreateScriptKey(Type type, string key)250         internal static ScriptKey CreateScriptKey(Type type, string key) {
251             return new ScriptKey(type, key);
252         }
253 
CreateScriptIncludeKey(Type type, string key, bool isResource)254         internal static ScriptKey CreateScriptIncludeKey(Type type, string key, bool isResource) {
255             return new ScriptKey(type, key, true, isResource);
256         }
257 
258         /// <devdoc>
259         ///   Enables controls to obtain client-side script function that will cause
260         ///   (when invoked) an out-of-band callback to the server
261         /// </devdoc>
GetCallbackEventReference(Control control, string argument, string clientCallback, string context)262         public string GetCallbackEventReference(Control control, string argument, string clientCallback, string context) {
263             return GetCallbackEventReference(control, argument, clientCallback, context, false);
264         }
265 
GetCallbackEventReference(Control control, string argument, string clientCallback, string context, bool useAsync)266         public string GetCallbackEventReference(Control control, string argument, string clientCallback, string context, bool useAsync) {
267             return GetCallbackEventReference(control, argument, clientCallback, context, null, useAsync);
268         }
269 
270         /// <devdoc>
271         ///   Enables controls to obtain client-side script function that will cause
272         ///   (when invoked) an out-of-band callback to the server and allows the user to specify a client-side error callback
273         /// </devdoc>
GetCallbackEventReference(Control control, string argument, string clientCallback, string context, string clientErrorCallback, bool useAsync)274         public string GetCallbackEventReference(Control control, string argument, string clientCallback, string context, string clientErrorCallback, bool useAsync) {
275             if (control == null) {
276                 throw new ArgumentNullException("control");
277             }
278             if (!(control is ICallbackEventHandler)) {
279                 throw new InvalidOperationException(SR.GetString(SR.Page_CallBackTargetInvalid, control.UniqueID));
280             }
281             return GetCallbackEventReference("'" + control.UniqueID + "'", argument, clientCallback, context, clientErrorCallback, useAsync);
282         }
283 
284         /// <devdoc>
285         ///   Enables controls to obtain client-side script function that will cause
286         ///   (when invoked) an out-of-band callback to the server and allows the user to specify a client-side error callback
287         /// </devdoc>
GetCallbackEventReference(string target, string argument, string clientCallback, string context, string clientErrorCallback, bool useAsync)288         public string GetCallbackEventReference(string target, string argument, string clientCallback, string context, string clientErrorCallback, bool useAsync) {
289             _owner.RegisterWebFormsScript();
290             if (_owner.ClientSupportsJavaScript && (_owner.RequestInternal != null) && _owner.RequestInternal.Browser.SupportsCallback) {
291                 RegisterStartupScript(typeof(Page), PageCallbackScriptKey, (((_owner.RequestInternal != null) &&
292                         (String.Equals(_owner.RequestInternal.Url.Scheme, "https", StringComparison.OrdinalIgnoreCase))) ?
293                             @"
294 var callBackFrameUrl='" + Util.QuoteJScriptString(GetWebResourceUrl(typeof(Page), "SmartNav.htm"), false) + @"';
295 WebForm_InitCallback();" :
296                             @"
297 WebForm_InitCallback();"), true);
298             }
299             if (argument == null) {
300                 argument = "null";
301             }
302             else if (argument.Length == 0) {
303                 argument = "\"\"";
304             }
305             if (context == null) {
306                 context = "null";
307             }
308             else if (context.Length == 0) {
309                 context = "\"\"";
310             }
311             return _callbackFunctionName +
312                    "(" +
313                    target +
314                    "," +
315                    argument +
316                    "," +
317                    clientCallback +
318                    "," +
319                    context +
320                    "," +
321                    ((clientErrorCallback == null) ? "null" : clientErrorCallback) +
322                    "," +
323                    (useAsync ? "true" : "false") +
324                    ")";
325         }
326 
GetPostBackClientHyperlink(Control control, string argument)327         public string GetPostBackClientHyperlink(Control control, string argument) {
328             // We're using escapePercent=true here and false in Page
329             // because true in Page would be a breaking change:
330             // People may already be encoding percent characters before calling this,
331             // and we may double encode it.
332             // Our own classes and new code should almost always use the override with escapePercent=true.
333             return GetPostBackClientHyperlink(control, argument, true, false);
334         }
335 
GetPostBackClientHyperlink(Control control, string argument, bool registerForEventValidation)336         public string GetPostBackClientHyperlink(Control control, string argument, bool registerForEventValidation) {
337             // We're using escapePercent=true here and false in Page
338             // because true in Page would be a breaking change:
339             // People may already be encoding percent characters before calling this,
340             // and we may double encode it.
341             // Our own classes and new code should almost always use the override with escapePercent=true.
342             return GetPostBackClientHyperlink(control, argument, true, registerForEventValidation);
343         }
344 
345         /// <devdoc>
346         ///    <para>This returs a string that can be put in client event to post back to the named control</para>
347         /// </devdoc>
GetPostBackClientHyperlink(Control control, string argument, bool escapePercent, bool registerForEventValidation)348         internal string GetPostBackClientHyperlink(Control control, string argument, bool escapePercent, bool registerForEventValidation) {
349             // Hyperlinks always need the language prefix
350             // If used in a hyperlink, the event argument needs to be escaped for % characters
351             // which will otherwise be interpreted as escape sequences (VSWhidbey 421874)
352             return JscriptPrefix + GetPostBackEventReference(control, argument, escapePercent, registerForEventValidation);
353         }
354 
GetPostBackEventReference(Control control, string argument)355         public string GetPostBackEventReference(Control control, string argument) {
356             return GetPostBackEventReference(control, argument, false, false);
357         }
358 
GetPostBackEventReference(Control control, string argument, bool registerForEventValidation)359         public string GetPostBackEventReference(Control control, string argument, bool registerForEventValidation) {
360             return GetPostBackEventReference(control, argument, false, registerForEventValidation);
361         }
362 
363         /*
364          * Enables controls to obtain client-side script function that will cause
365          * (when invoked) a server post-back to the form.
366          * argument: Parameter that will be passed to control on server
367          */
368         /// <devdoc>
369         ///    <para>Passes a parameter to the control that will do the postback processing on the
370         ///       server.</para>
371         /// </devdoc>
GetPostBackEventReference(Control control, string argument, bool forUrl, bool registerForEventValidation)372         private string GetPostBackEventReference(Control control, string argument, bool forUrl, bool registerForEventValidation) {
373             if (control == null) {
374                 throw new ArgumentNullException("control");
375             }
376 
377             _owner.RegisterPostBackScript();
378 
379             string controlID = control.UniqueID;
380 
381             if (registerForEventValidation) {
382                 RegisterForEventValidation(controlID, argument);
383             }
384 
385             // VSWhidbey 475945
386             if (control.EnableLegacyRendering && _owner.IsInOnFormRender &&
387                 controlID != null && controlID.IndexOf(Control.LEGACY_ID_SEPARATOR) >= 0) {
388 
389                 controlID = controlID.Replace(Control.LEGACY_ID_SEPARATOR, Control.ID_SEPARATOR);
390             }
391 
392             // Split into 2 calls to String.Concat to improve performance.
393             // CLR is investigating whether this should be fixed at a lower level.
394             string postBackEventReference = _postBackFunctionName + "('" + controlID + "','";
395             // The argument needs to be quoted, in case in contains characters that
396             // can't be used in JScript strings (ASURT 71818).
397             postBackEventReference += Util.QuoteJScriptString(argument, forUrl) + "')";
398 
399             return postBackEventReference;
400         }
401 
402         /// <devdoc>
403         ///    <para>Passes a parameter to the control that will do the postback processing on the
404         ///       server.</para>
405         /// </devdoc>
GetPostBackEventReference(PostBackOptions options)406         public string GetPostBackEventReference(PostBackOptions options) {
407             return GetPostBackEventReference(options, false);
408         }
409 
GetPostBackEventReference(PostBackOptions options, bool registerForEventValidation)410         public string GetPostBackEventReference(PostBackOptions options, bool registerForEventValidation) {
411             if (options == null) {
412                 throw new ArgumentNullException("options");
413             }
414 
415             if (registerForEventValidation) {
416                 RegisterForEventValidation(options);
417             }
418 
419             StringBuilder builder = new StringBuilder();
420             bool shouldRenderPostBackReferenceString = false;
421 
422             if (options.RequiresJavaScriptProtocol) {
423                 builder.Append(JscriptPrefix);
424             }
425 
426             if (options.AutoPostBack) {
427                 builder.Append("setTimeout('");
428             }
429 
430             // Use the old __doPostBack method if not using other postback features.
431             if (!options.PerformValidation && !options.TrackFocus && options.ClientSubmit &&
432                 string.IsNullOrEmpty(options.ActionUrl)) {
433                 string postbackRef = GetPostBackEventReference(options.TargetControl, options.Argument);
434 
435                 // Need to quote the string if auto posting back
436                 if (options.AutoPostBack) {
437                     builder.Append(Util.QuoteJScriptString(postbackRef));
438                     builder.Append("', 0)");
439                 }
440                 else {
441                     builder.Append(postbackRef);
442                 }
443 
444                 return builder.ToString();
445             }
446 
447             builder.Append(_postbackOptionsFunctionName);
448             builder.Append("(new WebForm_PostBackOptions(\"");
449             builder.Append(options.TargetControl.UniqueID);
450             builder.Append("\", ");
451 
452             if (String.IsNullOrEmpty(options.Argument)) {
453                 builder.Append("\"\", ");
454             }
455             else {
456                 builder.Append("\"");
457                 builder.Append(Util.QuoteJScriptString(options.Argument));
458                 builder.Append("\", ");
459             }
460 
461             if (options.PerformValidation) {
462                 shouldRenderPostBackReferenceString = true;
463                 builder.Append("true, ");
464             }
465             else {
466                 builder.Append("false, ");
467             }
468 
469             if (options.ValidationGroup != null && options.ValidationGroup.Length > 0) {
470                 shouldRenderPostBackReferenceString = true;
471 
472                 builder.Append("\"");
473                 builder.Append(options.ValidationGroup);
474                 builder.Append("\", ");
475             }
476             else {
477                 builder.Append("\"\", ");
478             }
479 
480             if (options.ActionUrl != null && options.ActionUrl.Length > 0) {
481                 shouldRenderPostBackReferenceString = true;
482                 _owner.ContainsCrossPagePost = true;
483 
484                 builder.Append("\"");
485                 builder.Append(Util.QuoteJScriptString(options.ActionUrl));
486                 builder.Append("\", ");
487             }
488             else {
489                 builder.Append("\"\", ");
490             }
491 
492             if (options.TrackFocus) {
493                 _owner.RegisterFocusScript();
494                 shouldRenderPostBackReferenceString = true;
495                 builder.Append("true, ");
496             }
497             else {
498                 builder.Append("false, ");
499             }
500 
501             if (options.ClientSubmit) {
502                 shouldRenderPostBackReferenceString = true;
503                 _owner.RegisterPostBackScript();
504 
505                 builder.Append("true))");
506             }
507             else {
508                 builder.Append("false))");
509             }
510 
511             if (options.AutoPostBack) {
512                 builder.Append("', 0)");
513             }
514 
515             string reference = null;
516             if (shouldRenderPostBackReferenceString) {
517                 reference = builder.ToString();
518                 _owner.RegisterWebFormsScript();
519             }
520 
521             return reference;
522         }
523 
524         /// <devdoc>
525         /// Gets a URL resource reference to a client-side resource
526         /// </devdoc>
GetWebResourceUrl(Type type, string resourceName)527         public string GetWebResourceUrl(Type type, string resourceName) {
528             return GetWebResourceUrl(_owner, type, resourceName, false,
529                 (_owner == null ? null : _owner.ScriptManager));
530         }
531 
GetWebResourceUrl(Page owner, Type type, string resourceName, bool htmlEncoded, IScriptManager scriptManager)532         internal static string GetWebResourceUrl(Page owner, Type type, string resourceName, bool htmlEncoded, IScriptManager scriptManager) {
533             bool enableCdn = scriptManager != null && scriptManager.EnableCdn;
534             return GetWebResourceUrl(owner, type, resourceName, htmlEncoded, scriptManager, enableCdn);
535         }
536 
GetWebResourceUrl(Page owner, Type type, string resourceName, bool htmlEncoded, IScriptManager scriptManager, bool enableCdn)537         internal static string GetWebResourceUrl(Page owner, Type type, string resourceName, bool htmlEncoded, IScriptManager scriptManager, bool enableCdn) {
538             if (type == null) {
539                 throw new ArgumentNullException("type");
540             }
541 
542             if (String.IsNullOrEmpty(resourceName)) {
543                 throw new ArgumentNullException("resourceName");
544             }
545 
546             if (owner != null && owner.DesignMode) {
547                 ISite site = ((IComponent)owner).Site;
548                 if (site != null) {
549                     IResourceUrlGenerator urlGenerator = site.GetService(typeof(IResourceUrlGenerator)) as IResourceUrlGenerator;
550                     if (urlGenerator != null) {
551                         return urlGenerator.GetResourceUrl(type, resourceName);
552                     }
553                 }
554 
555                 return resourceName;
556             }
557             else {
558                 return AssemblyResourceLoader.GetWebResourceUrl(type, resourceName, htmlEncoded, scriptManager, enableCdn: enableCdn);
559             }
560         }
561 
562         /// <devdoc>
563         ///    <para>Determines if the client script block is registered with the page.</para>
564         /// </devdoc>
IsClientScriptBlockRegistered(string key)565         public bool IsClientScriptBlockRegistered(string key) {
566             return IsClientScriptBlockRegistered(typeof(Page), key);
567         }
568 
569         /// <devdoc>
570         ///    <para>Determines if the client script block is registered with the page.</para>
571         /// </devdoc>
IsClientScriptBlockRegistered(Type type, string key)572         public bool IsClientScriptBlockRegistered(Type type, string key) {
573             if (type == null) {
574                 throw new ArgumentNullException("type");
575             }
576 
577             return (_registeredClientScriptBlocks != null
578                    && (_registeredClientScriptBlocks.Contains(CreateScriptKey(type, key))));
579         }
580 
581         /// <devdoc>
582         ///    <para>Determines if the onsubmit script is registered with the page.</para>
583         /// </devdoc>
IsClientScriptIncludeRegistered(string key)584         public bool IsClientScriptIncludeRegistered(string key) {
585             return IsClientScriptIncludeRegistered(typeof(Page), key);
586         }
587 
588         /// <devdoc>
589         ///    <para>Determines if the onsubmit script  is registered with the page.</para>
590         /// </devdoc>
IsClientScriptIncludeRegistered(Type type, string key)591         public bool IsClientScriptIncludeRegistered(Type type, string key) {
592             if (type == null) {
593                 throw new ArgumentNullException("type");
594             }
595 
596             return (_registeredClientScriptBlocks != null
597                    && (_registeredClientScriptBlocks.Contains(CreateScriptIncludeKey(type, key, false))));
598         }
599 
600         /// <devdoc>
601         ///    <para>Determines if the client startup script is registered with the
602         ///       page.</para>
603         /// </devdoc>
IsStartupScriptRegistered(string key)604         public bool IsStartupScriptRegistered(string key) {
605             return IsStartupScriptRegistered(typeof(Page), key);
606         }
607 
608         /// <devdoc>
609         ///    <para>Determines if the client startup script is registered with the
610         ///       page.</para>
611         /// </devdoc>
IsStartupScriptRegistered(Type type, string key)612         public bool IsStartupScriptRegistered(Type type, string key) {
613             if (type == null) {
614                 throw new ArgumentNullException("type");
615             }
616 
617             return (_registeredClientStartupScripts != null
618                    && (_registeredClientStartupScripts.Contains(CreateScriptKey(type, key))));
619         }
620 
621         /// <devdoc>
622         ///    <para>Determines if the onsubmit script is registered with the page.</para>
623         /// </devdoc>
IsOnSubmitStatementRegistered(string key)624         public bool IsOnSubmitStatementRegistered(string key) {
625             return IsOnSubmitStatementRegistered(typeof(Page), key);
626         }
627 
628         /// <devdoc>
629         ///    <para>Determines if the onsubmit script  is registered with the page.</para>
630         /// </devdoc>
IsOnSubmitStatementRegistered(Type type, string key)631         public bool IsOnSubmitStatementRegistered(Type type, string key) {
632             if (type == null) {
633                 throw new ArgumentNullException("type");
634             }
635 
636             return (_registeredOnSubmitStatements != null
637                    && (_registeredOnSubmitStatements.Contains(CreateScriptKey(type, key))));
638         }
639 
640         /// <devdoc>
641         ///    <para>Declares a value that will be declared as a JavaScript array declaration
642         ///       when the page renders. This can be used by script-based controls to declare
643         ///       themselves within an array so that a client script library can work with
644         ///       all the controls of the same type.</para>
645         /// </devdoc>
RegisterArrayDeclaration(string arrayName, string arrayValue)646         public void RegisterArrayDeclaration(string arrayName, string arrayValue) {
647             if (arrayName == null) {
648                 throw new ArgumentNullException("arrayName");
649             }
650             if (_registeredArrayDeclares == null) {
651                 _registeredArrayDeclares = new ListDictionary();
652             }
653             if (!_registeredArrayDeclares.Contains(arrayName)) {
654                 _registeredArrayDeclares[arrayName] = new ArrayList();
655             }
656 
657             ArrayList elements = (ArrayList)_registeredArrayDeclares[arrayName];
658             elements.Add(arrayValue);
659 
660             // If there are any partial caching controls on the stack, forward the call to them
661             if (_owner.PartialCachingControlStack != null) {
662                 foreach (BasePartialCachingControl c in _owner.PartialCachingControlStack) {
663                     c.RegisterArrayDeclaration(arrayName, arrayValue);
664                 }
665             }
666         }
667 
668         // RegisterArrayDeclaration implementation that supports partial rendering.
RegisterArrayDeclaration(Control control, string arrayName, string arrayValue)669         internal void RegisterArrayDeclaration(Control control, string arrayName, string arrayValue) {
670             IScriptManager scriptManager = _owner.ScriptManager;
671             if ((scriptManager != null) && scriptManager.SupportsPartialRendering) {
672                 scriptManager.RegisterArrayDeclaration(control, arrayName, arrayValue);
673             }
674             else {
675                 RegisterArrayDeclaration(arrayName, arrayValue);
676             }
677         }
678 
RegisterExpandoAttribute(string controlId, string attributeName, string attributeValue)679         public void RegisterExpandoAttribute(string controlId, string attributeName, string attributeValue) {
680             RegisterExpandoAttribute(controlId, attributeName, attributeValue, true);
681         }
682 
RegisterExpandoAttribute(string controlId, string attributeName, string attributeValue, bool encode)683         public void RegisterExpandoAttribute(string controlId, string attributeName, string attributeValue, bool encode) {
684             // check paramters
685             WebUtil.StringUtil.CheckAndTrimString(controlId, "controlId");
686             WebUtil.StringUtil.CheckAndTrimString(attributeName, "attributeName");
687 
688             ListDictionary expandoAttributes = null;
689             if (_registeredControlsWithExpandoAttributes == null) {
690                 _registeredControlsWithExpandoAttributes = new ListDictionary(StringComparer.Ordinal);
691             }
692             else {
693                 expandoAttributes = (ListDictionary)_registeredControlsWithExpandoAttributes[controlId];
694             }
695 
696             if (expandoAttributes == null) {
697                 expandoAttributes = new ListDictionary(StringComparer.Ordinal);
698                 _registeredControlsWithExpandoAttributes.Add(controlId, expandoAttributes);
699             }
700 
701             if (encode) {
702                 attributeValue = Util.QuoteJScriptString(attributeValue);
703             }
704 
705             expandoAttributes.Add(attributeName, attributeValue);
706 
707             // If there are any partial caching controls on the stack, forward the call to them
708             if (_owner.PartialCachingControlStack != null) {
709                 foreach (BasePartialCachingControl c in _owner.PartialCachingControlStack) {
710                     c.RegisterExpandoAttribute(controlId, attributeName, attributeValue);
711                 }
712             }
713         }
714 
715         // RegisterExpandoAttribute implementation that supports partial rendering.
RegisterExpandoAttribute(Control control, string controlId, string attributeName, string attributeValue, bool encode)716         internal void RegisterExpandoAttribute(Control control, string controlId, string attributeName, string attributeValue, bool encode) {
717             IScriptManager scriptManager = _owner.ScriptManager;
718             if ((scriptManager != null) && scriptManager.SupportsPartialRendering) {
719                 scriptManager.RegisterExpandoAttribute(control, controlId, attributeName, attributeValue, encode);
720             }
721             else {
722                 RegisterExpandoAttribute(controlId, attributeName, attributeValue, encode);
723             }
724         }
725 
726         /// <devdoc>
727         ///    <para>
728         ///       Allows controls to automatically register a hidden field on the form. The
729         ///       field will be emitted when the form control renders itself.
730         ///    </para>
731         /// </devdoc>
RegisterHiddenField(string hiddenFieldName, string hiddenFieldInitialValue)732         public void RegisterHiddenField(string hiddenFieldName,
733                                         string hiddenFieldInitialValue) {
734             if (hiddenFieldName == null) {
735                 throw new ArgumentNullException("hiddenFieldName");
736             }
737             if (_registeredHiddenFields == null)
738                 _registeredHiddenFields = new ListDictionary();
739 
740             if (!_registeredHiddenFields.Contains(hiddenFieldName))
741                 _registeredHiddenFields.Add(hiddenFieldName, hiddenFieldInitialValue);
742             if (_owner._hiddenFieldsToRender == null) {
743                 _owner._hiddenFieldsToRender = new Dictionary<String, String>();
744             }
745             _owner._hiddenFieldsToRender[hiddenFieldName] = hiddenFieldInitialValue;
746 
747             // If there are any partial caching controls on the stack, forward the call to them
748             if (_owner.PartialCachingControlStack != null) {
749                 foreach (BasePartialCachingControl c in _owner.PartialCachingControlStack) {
750                     c.RegisterHiddenField(hiddenFieldName, hiddenFieldInitialValue);
751                 }
752             }
753         }
754 
755         // RegisterHiddenField implementation that supports partial rendering.
RegisterHiddenField(Control control, string hiddenFieldName, string hiddenFieldValue)756         internal void RegisterHiddenField(Control control, string hiddenFieldName, string hiddenFieldValue) {
757             IScriptManager scriptManager = _owner.ScriptManager;
758             if ((scriptManager != null) && scriptManager.SupportsPartialRendering) {
759                 scriptManager.RegisterHiddenField(control, hiddenFieldName, hiddenFieldValue);
760             }
761             else {
762                 RegisterHiddenField(hiddenFieldName, hiddenFieldValue);
763             }
764         }
765 
766 
767         /// <devdoc>
768         ///    Prevents controls from sending duplicate blocks of
769         ///    client-side script to the client. Any script blocks with the same type and key
770         ///    values are considered duplicates.</para>
771         /// </devdoc>
RegisterClientScriptBlock(Type type, string key, string script)772         public void RegisterClientScriptBlock(Type type, string key, string script) {
773             RegisterClientScriptBlock(type, key, script, false);
774         }
775 
776         /// <devdoc>
777         ///    Prevents controls from sending duplicate blocks of
778         ///    client-side script to the client. Any script blocks with the same type and key
779         ///    values are considered duplicates.</para>
780         /// </devdoc>
RegisterClientScriptBlock(Type type, string key, string script, bool addScriptTags)781         public void RegisterClientScriptBlock(Type type, string key, string script, bool addScriptTags) {
782             if (type == null) {
783                 throw new ArgumentNullException("type");
784             }
785 
786             if (addScriptTags) {
787                 RegisterScriptBlock(CreateScriptKey(type, key), script, ClientAPIRegisterType.ClientScriptBlocksWithoutTags);
788             }
789             else {
790                 RegisterScriptBlock(CreateScriptKey(type, key), script, ClientAPIRegisterType.ClientScriptBlocks);
791             }
792         }
793 
794         // RegisterClientScriptBlock implementation that supports partial rendering.
RegisterClientScriptBlock(Control control, Type type, string key, string script, bool addScriptTags)795         internal void RegisterClientScriptBlock(Control control, Type type, string key, string script, bool addScriptTags) {
796             IScriptManager scriptManager = _owner.ScriptManager;
797             if ((scriptManager != null) && scriptManager.SupportsPartialRendering) {
798                 scriptManager.RegisterClientScriptBlock(control, type, key, script, addScriptTags);
799             }
800             else {
801                 RegisterClientScriptBlock(type, key, script, addScriptTags);
802             }
803         }
804 
805 
806         /// <devdoc>
807         ///    <para> Prevents controls from sending duplicate blocks of
808         ///       client-side script to the client. Any script blocks with the same <paramref name="key"/> parameter
809         ///       values are considered duplicates.</para>
810         /// </devdoc>
RegisterClientScriptInclude(string key, string url)811         public void RegisterClientScriptInclude(string key, string url) {
812             RegisterClientScriptInclude(typeof(Page), key, url);
813         }
814 
815         /// <devdoc>
816         ///    Prevents controls from sending duplicate blocks of
817         ///    client-side script to the client. Any script blocks with the same type and key
818         ///    values are considered duplicates.</para>
819         /// </devdoc>
RegisterClientScriptInclude(Type type, string key, string url)820         public void RegisterClientScriptInclude(Type type, string key, string url) {
821             RegisterClientScriptInclude(type, key, url, false);
822         }
823 
RegisterClientScriptInclude(Type type, string key, string url, bool isResource)824         internal void RegisterClientScriptInclude(Type type, string key, string url, bool isResource) {
825             if (type == null) {
826                 throw new ArgumentNullException("type");
827             }
828             if (String.IsNullOrEmpty(url)) {
829                 throw ExceptionUtil.ParameterNullOrEmpty("url");
830             }
831 
832             // VSWhidbey 499036: encode the url
833             string script = IncludeScriptBegin + HttpUtility.HtmlAttributeEncode(url) + IncludeScriptEnd;
834             RegisterScriptBlock(CreateScriptIncludeKey(type, key, isResource), script, ClientAPIRegisterType.ClientScriptBlocks);
835         }
836 
837         // RegisterClientScriptInclude implementation that supports partial rendering.
RegisterClientScriptInclude(Control control, Type type, string key, string url)838         internal void RegisterClientScriptInclude(Control control, Type type, string key, string url) {
839             IScriptManager scriptManager = _owner.ScriptManager;
840             if ((scriptManager != null) && scriptManager.SupportsPartialRendering) {
841                 scriptManager.RegisterClientScriptInclude(control, type, key, url);
842             }
843             else {
844                 RegisterClientScriptInclude(type, key, url);
845             }
846         }
847 
848 
849         /// <devdoc>
850         /// </devdoc>
RegisterClientScriptResource(Type type, string resourceName)851         public void RegisterClientScriptResource(Type type, string resourceName) {
852             if (type == null) {
853                 throw new ArgumentNullException("type");
854             }
855 
856             RegisterClientScriptInclude(type, resourceName, GetWebResourceUrl(type, resourceName), true);
857         }
858 
859         // RegisterClientScriptResource implementation that supports partial rendering.
RegisterClientScriptResource(Control control, Type type, string resourceName)860         internal void RegisterClientScriptResource(Control control, Type type, string resourceName) {
861             IScriptManager scriptManager = _owner.ScriptManager;
862             if ((scriptManager != null) && scriptManager.SupportsPartialRendering) {
863                 scriptManager.RegisterClientScriptResource(control, type, resourceName);
864             }
865             else {
866                 RegisterClientScriptResource(type, resourceName);
867             }
868         }
869 
870 
RegisterDefaultButtonScript(Control button, HtmlTextWriter writer, bool useAddAttribute)871         internal void RegisterDefaultButtonScript(Control button, HtmlTextWriter writer, bool useAddAttribute) {
872             _owner.RegisterWebFormsScript();
873             if (_owner.EnableLegacyRendering) {
874                 if (useAddAttribute) {
875                     writer.AddAttribute("language", "javascript", false);
876                 }
877                 else {
878                     writer.WriteAttribute("language", "javascript", false);
879                 }
880             }
881             string keyPress = "javascript:return WebForm_FireDefaultButton(event, '" + button.ClientID + "')";
882             if (useAddAttribute) {
883                 writer.AddAttribute("onkeypress", keyPress);
884             }
885             else {
886                 writer.WriteAttribute("onkeypress", keyPress);
887             }
888         }
889 
890         /// <devdoc>
891         ///    <para>Allows a control to access a the client
892         ///    <see langword='onsubmit'/> event.
893         ///       The script should be a function call to client code registered elsewhere.</para>
894         /// </devdoc>
RegisterOnSubmitStatement(Type type, string key, string script)895         public void RegisterOnSubmitStatement(Type type, string key, string script) {
896             if (type == null) {
897                 throw new ArgumentNullException("type");
898             }
899 
900             RegisterOnSubmitStatementInternal(CreateScriptKey(type, key), script);
901         }
902 
903 
904         // RegisterOnSubmitStatement implementation that supports partial rendering.
RegisterOnSubmitStatement(Control control, Type type, string key, string script)905         internal void RegisterOnSubmitStatement(Control control, Type type, string key, string script) {
906             IScriptManager scriptManager = _owner.ScriptManager;
907             if ((scriptManager != null) && scriptManager.SupportsPartialRendering) {
908                 scriptManager.RegisterOnSubmitStatement(control, type, key, script);
909             }
910             else {
911                 RegisterOnSubmitStatement(type, key, script);
912             }
913         }
914 
915 
RegisterOnSubmitStatementInternal(ScriptKey key, string script)916         internal void RegisterOnSubmitStatementInternal(ScriptKey key, string script) {
917             if (String.IsNullOrEmpty(script)) {
918                 throw ExceptionUtil.ParameterNullOrEmpty("script");
919             }
920             if (_registeredOnSubmitStatements == null)
921                 _registeredOnSubmitStatements = new ListDictionary();
922 
923             // Make sure the script block ends in a semicolon
924             int index = script.Length - 1;
925             while ((index >= 0) && Char.IsWhiteSpace(script, index)) {
926                 index--;
927             }
928 
929             if ((index >= 0) && (script[index] != ';')) {
930                 script = script.Substring(0, index + 1) + ";" + script.Substring(index + 1);
931             }
932 
933             if (!_registeredOnSubmitStatements.Contains(key))
934                 _registeredOnSubmitStatements.Add(key, script);
935 
936             // If there are any partial caching controls on the stack, forward the call to them
937             if (_owner.PartialCachingControlStack != null) {
938                 foreach (BasePartialCachingControl c in _owner.PartialCachingControlStack) {
939                     c.RegisterOnSubmitStatement(key, script);
940                 }
941             }
942         }
943 
RegisterScriptBlock(ScriptKey key, string script, ClientAPIRegisterType type)944         internal void RegisterScriptBlock(ScriptKey key, string script, ClientAPIRegisterType type) {
945 
946             // Call RegisterScriptBlock with the correct collection based on the blockType
947             switch (type) {
948                 case ClientAPIRegisterType.ClientScriptBlocks:
949                     RegisterScriptBlock(key, script, ref _registeredClientScriptBlocks, ref _clientScriptBlocks, false);
950                     break;
951                 case ClientAPIRegisterType.ClientScriptBlocksWithoutTags:
952                     RegisterScriptBlock(key, script, ref _registeredClientScriptBlocks, ref _clientScriptBlocks, true);
953                     break;
954                 case ClientAPIRegisterType.ClientStartupScripts:
955                     RegisterScriptBlock(key, script, ref _registeredClientStartupScripts, ref _clientStartupScripts, false);
956                     break;
957                 case ClientAPIRegisterType.ClientStartupScriptsWithoutTags:
958                     RegisterScriptBlock(key, script, ref _registeredClientStartupScripts, ref _clientStartupScripts, true);
959                     break;
960                 default:
961                     Debug.Assert(false);
962                     break;
963             }
964 
965             // If there are any partial caching controls on the stack, forward the call to them
966             if (_owner.PartialCachingControlStack != null) {
967                 foreach (BasePartialCachingControl c in _owner.PartialCachingControlStack) {
968                     c.RegisterScriptBlock(type, key, script);
969                 }
970             }
971         }
972 
RegisterScriptBlock(ScriptKey key, string script, ref ListDictionary scriptBlocks, ref ArrayList scriptList, bool needsScriptTags)973         private void RegisterScriptBlock(ScriptKey key, string script, ref ListDictionary scriptBlocks, ref ArrayList scriptList, bool needsScriptTags) {
974             if (scriptBlocks == null) {
975                 scriptBlocks = new ListDictionary();
976                 scriptList = new ArrayList();
977             }
978 
979             if (!scriptBlocks.Contains(key)) {
980                 Tuple<ScriptKey, String, Boolean> entry = new Tuple<ScriptKey, String, Boolean>(key, script, needsScriptTags);
981                 scriptBlocks.Add(key, null);
982                 scriptList.Add(entry);
983             }
984         }
985 
986         /// <devdoc>
987         ///    <para>
988         ///       Allows controls to keep duplicate blocks of client-side script code from
989         ///       being sent to the client. Any script blocks with the same type and key
990         ///       value are considered duplicates.
991         ///    </para>
992         /// </devdoc>
RegisterStartupScript(Type type, string key, string script)993         public void RegisterStartupScript(Type type, string key, string script) {
994             RegisterStartupScript(type, key, script, false);
995         }
996 
997         /// <devdoc>
998         ///    <para>
999         ///       Allows controls to keep duplicate blocks of client-side script code from
1000         ///       being sent to the client. Any script blocks with the same type and key
1001         ///       value are considered duplicates.
1002         ///    </para>
1003         /// </devdoc>
RegisterStartupScript(Type type, string key, string script, bool addScriptTags)1004         public void RegisterStartupScript(Type type, string key, string script, bool addScriptTags) {
1005             if (type == null) {
1006                 throw new ArgumentNullException("type");
1007             }
1008 
1009             if (addScriptTags) {
1010                 RegisterScriptBlock(CreateScriptKey(type, key), script, ClientAPIRegisterType.ClientStartupScriptsWithoutTags);
1011             }
1012             else {
1013                 RegisterScriptBlock(CreateScriptKey(type, key), script, ClientAPIRegisterType.ClientStartupScripts);
1014             }
1015         }
1016 
1017         // RegisterStartupScript implementation that supports partial rendering.
RegisterStartupScript(Control control, Type type, string key, string script, bool addScriptTags)1018         internal void RegisterStartupScript(Control control, Type type, string key, string script, bool addScriptTags) {
1019             IScriptManager scriptManager = _owner.ScriptManager;
1020             if ((scriptManager != null) && scriptManager.SupportsPartialRendering) {
1021                 scriptManager.RegisterStartupScript(control, type, key, script, addScriptTags);
1022             }
1023             else {
1024                 RegisterStartupScript(type, key, script, addScriptTags);
1025             }
1026         }
1027 
1028 
RenderArrayDeclares(HtmlTextWriter writer)1029         internal void RenderArrayDeclares(HtmlTextWriter writer) {
1030             if (_registeredArrayDeclares == null || _registeredArrayDeclares.Count == 0) {
1031                 return;
1032             }
1033 
1034             writer.Write(_owner.EnableLegacyRendering ? ClientScriptStartLegacy : ClientScriptStart);
1035 
1036             // Write out each array
1037             IDictionaryEnumerator arrays = _registeredArrayDeclares.GetEnumerator();
1038             while (arrays.MoveNext()) {
1039                 // Write the declaration
1040                 writer.Write("var ");
1041                 writer.Write(arrays.Key);
1042                 writer.Write(" =  new Array(");
1043 
1044                 // Write each element
1045                 IEnumerator elements = ((ArrayList)arrays.Value).GetEnumerator();
1046                 bool first = true;
1047                 while (elements.MoveNext()) {
1048                     if (first) {
1049                         first = false;
1050                     }
1051                     else {
1052                         writer.Write(", ");
1053                     }
1054                     writer.Write(elements.Current);
1055                 }
1056 
1057                 // Close the declaration
1058                 writer.WriteLine(");");
1059             }
1060 
1061             writer.Write(_owner.EnableLegacyRendering ? ClientScriptEndLegacy : ClientScriptEnd);
1062         }
1063 
RenderExpandoAttribute(HtmlTextWriter writer)1064         internal void RenderExpandoAttribute(HtmlTextWriter writer) {
1065             if (_registeredControlsWithExpandoAttributes == null ||
1066                 _registeredControlsWithExpandoAttributes.Count == 0) {
1067                 return;
1068             }
1069 
1070             writer.Write(_owner.EnableLegacyRendering ? ClientScriptStartLegacy : ClientScriptStart);
1071 
1072             foreach (DictionaryEntry controlEntry in _registeredControlsWithExpandoAttributes) {
1073                 string controlId = (string)controlEntry.Key;
1074                 writer.Write("var ");
1075                 writer.Write(controlId);
1076                 writer.Write(" = document.all ? document.all[\"");
1077                 writer.Write(controlId);
1078                 writer.Write("\"] : document.getElementById(\"");
1079                 writer.Write(controlId);
1080                 writer.WriteLine("\");");
1081 
1082                 ListDictionary expandoAttributes = (ListDictionary)controlEntry.Value;
1083                 Debug.Assert(expandoAttributes != null && expandoAttributes.Count > 0);
1084                 foreach (DictionaryEntry expandoAttribute in expandoAttributes) {
1085                     writer.Write(controlId);
1086                     writer.Write(".");
1087                     writer.Write(expandoAttribute.Key);
1088                     if (expandoAttribute.Value == null) {
1089                         // VSWhidbey 382151 Render out null string for nulls
1090                         writer.WriteLine(" = null;");
1091                     }
1092                     else {
1093                         writer.Write(" = \"");
1094                         writer.Write(expandoAttribute.Value);
1095                         writer.WriteLine("\";");
1096                     }
1097                 }
1098             }
1099 
1100             writer.Write(_owner.EnableLegacyRendering ? ClientScriptEndLegacy : ClientScriptEnd);
1101         }
1102 
RenderHiddenFields(HtmlTextWriter writer)1103         internal void RenderHiddenFields(HtmlTextWriter writer) {
1104             if (_registeredHiddenFields == null || _registeredHiddenFields.Count == 0) {
1105                 return;
1106             }
1107 
1108             foreach (DictionaryEntry entry in _registeredHiddenFields) {
1109                 string entryKey = (string)entry.Key;
1110                 if (entryKey == null) {
1111                     entryKey = String.Empty;
1112                 }
1113                 writer.WriteLine();
1114                 writer.Write("<input type=\"hidden\" name=\"");
1115                 writer.Write(entryKey);
1116                 writer.Write("\" id=\"");
1117                 writer.Write(entryKey);
1118                 writer.Write("\" value=\"");
1119                 HttpUtility.HtmlEncode((string)entry.Value, writer);
1120                 writer.Write("\" />");
1121             }
1122 
1123             ClearHiddenFields();
1124         }
1125 
RenderClientScriptBlocks(HtmlTextWriter writer)1126         internal void RenderClientScriptBlocks(HtmlTextWriter writer) {
1127             bool inScriptBlock = false;
1128             if (_clientScriptBlocks != null) {
1129                 inScriptBlock = RenderRegisteredScripts(writer, _clientScriptBlocks, true);
1130             }
1131 
1132             // Emit the onSubmit function, in necessary
1133             if (!String.IsNullOrEmpty(_owner.ClientOnSubmitEvent) && _owner.ClientSupportsJavaScript) {
1134                 // If we were already inside a script tag, don't emit a new open script tag
1135                 if (!inScriptBlock) {
1136                     writer.Write(_owner.EnableLegacyRendering ? ClientScriptStartLegacy : ClientScriptStart);
1137                 }
1138 
1139                 writer.Write(@"function WebForm_OnSubmit() {
1140 ");
1141                 if (_registeredOnSubmitStatements != null) {
1142                     foreach (string s in _registeredOnSubmitStatements.Values) {
1143                         writer.Write(s);
1144                     }
1145                 }
1146                 writer.WriteLine(@"
1147 return true;
1148 }");
1149                 // We always need to close the script tag
1150                 writer.Write(_owner.EnableLegacyRendering ? ClientScriptEndLegacy : ClientScriptEnd);
1151             }
1152             // If there was no onSubmit function, close the script tag if needed
1153             else if (inScriptBlock) {
1154                 writer.Write(_owner.EnableLegacyRendering ? ClientScriptEndLegacy : ClientScriptEnd);
1155             }
1156         }
1157 
RenderClientStartupScripts(HtmlTextWriter writer)1158         internal void RenderClientStartupScripts(HtmlTextWriter writer) {
1159             if (_clientStartupScripts != null) {
1160                 bool inScriptBlock = RenderRegisteredScripts(writer, _clientStartupScripts, false);
1161                 // Close the script tag if needed
1162                 if (inScriptBlock) {
1163                     writer.Write(_owner.EnableLegacyRendering ? ClientScriptEndLegacy : ClientScriptEnd);
1164                 }
1165             }
1166         }
1167 
RenderRegisteredScripts(HtmlTextWriter writer, ArrayList scripts, bool checkForScriptManagerRegistrations)1168         private bool RenderRegisteredScripts(HtmlTextWriter writer, ArrayList scripts, bool checkForScriptManagerRegistrations) {
1169             writer.WriteLine();
1170             bool inScriptBlock = false;
1171             checkForScriptManagerRegistrations &= (_registeredResourcesToSuppress != null);
1172             // Write out each registered script block
1173             foreach (Tuple<ScriptKey, String, Boolean> entry in scripts) {
1174                 if (checkForScriptManagerRegistrations) {
1175                     ScriptKey scriptKey = entry.Item1;
1176                     if (scriptKey.IsResource) {
1177                         Dictionary<String, Object> resources;
1178                         if (_registeredResourcesToSuppress.TryGetValue(scriptKey.Assembly, out resources)
1179                             && resources.ContainsKey(scriptKey.Key)) {
1180                             // this is a suppressed resource
1181                             continue;
1182                         }
1183                     }
1184                 }
1185                 if (entry.Item3) {
1186                     if (!inScriptBlock) {
1187                         // If we need script tags and we're not in a script tag, emit a start script tag
1188                         writer.Write(_owner.EnableLegacyRendering ? ClientScriptStartLegacy : ClientScriptStart);
1189                         inScriptBlock = true;
1190                     }
1191                 }
1192                 else if (inScriptBlock) {
1193                     // If we don't need script tags, and we're in a script tag, emit an end script tag
1194                     writer.Write(_owner.EnableLegacyRendering ? ClientScriptEndLegacy : ClientScriptEnd);
1195                     inScriptBlock = false;
1196                 }
1197                 writer.Write(entry.Item2);
1198             }
1199             return inScriptBlock;
1200         }
1201 
RenderWebFormsScript(HtmlTextWriter writer)1202         internal void RenderWebFormsScript(HtmlTextWriter writer) {
1203             const string webFormScript = "WebForms.js";
1204             if (_registeredResourcesToSuppress != null) {
1205                 Dictionary<String, Object> systemWebResources;
1206                 if (_registeredResourcesToSuppress.TryGetValue(AssemblyResourceLoader.GetAssemblyFromType(typeof(Page)),
1207                         out systemWebResources) &&
1208                     systemWebResources.ContainsKey("WebForms.js")) {
1209                     return;
1210                 }
1211             }
1212             writer.Write(IncludeScriptBegin);
1213             writer.Write(GetWebResourceUrl(_owner, typeof(Page), webFormScript, htmlEncoded: true, scriptManager: _owner.ScriptManager));
1214             writer.Write(IncludeScriptEnd);
1215 
1216             // Render the fallback script for WebForm.js
1217             if (_owner.ScriptManager != null && _owner.ScriptManager.EnableCdn && _owner.ScriptManager.EnableCdnFallback) {
1218                 var localPath = GetWebResourceUrl(_owner, typeof(Page), webFormScript, htmlEncoded: true, scriptManager: _owner.ScriptManager, enableCdn: false);
1219                 if (!String.IsNullOrEmpty(localPath)) {
1220                     writer.Write(ClientScriptStart);
1221                     writer.Write(@"window.WebForm_PostBackOptions||document.write('<script type=""text/javascript"" src=""" + localPath + @"""><\/script>');");
1222                     writer.Write(ClientScriptEnd);
1223                 }
1224             }
1225             writer.WriteLine();
1226         }
1227 
1228         private interface IEventValidationProvider {
1229             // Gets an object that - when serialized and encrypted - is the outbound __EVENTVALIDATION field value.
GetEventValidationStoreObject()1230             object GetEventValidationStoreObject();
1231 
1232             // Returns a value denoting whether this (uniqueId, argument) tuple is valid for the current postback.
IsValid(string uniqueId, string argument)1233             bool IsValid(string uniqueId, string argument);
1234 
1235             // Registers the tuple (uniqueId, argument) as a valid event for the next postback.
RegisterForEventValidation(string uniqueId, string argument)1236             void RegisterForEventValidation(string uniqueId, string argument);
1237 
1238             // Given the deserialized form of an incoming __EVENTVALIDATION field, tries to load the valid
1239             // event references for this postback. Returns true on success, false on failure.
TryLoadEventValidationField(object eventValidationField)1240             bool TryLoadEventValidationField(object eventValidationField);
1241         }
1242 
1243         // provides a more secure implementation of event validation (fix for DevDiv #233564)
1244         private sealed class DefaultEventValidationProvider : IEventValidationProvider {
1245             private readonly ClientScriptManager _clientScriptManager;
1246             private EventValidationStore _inboundEvents; // events which are valid for the current postback
1247             private EventValidationStore _outboundEvents; // events which will be valid on the next postback
1248 
DefaultEventValidationProvider(ClientScriptManager clientScriptManager)1249             internal DefaultEventValidationProvider(ClientScriptManager clientScriptManager) {
1250                 _clientScriptManager = clientScriptManager;
1251             }
1252 
GetEventValidationStoreObject()1253             public object GetEventValidationStoreObject() {
1254                 // We only produce the object to be serialized if there is data in the store
1255                 if (_outboundEvents != null && _outboundEvents.Count > 0) {
1256                     return _outboundEvents;
1257                 }
1258                 else {
1259                     return null;
1260                 }
1261             }
1262 
IsValid(string uniqueId, string argument)1263             public bool IsValid(string uniqueId, string argument) {
1264                 return _inboundEvents != null && _inboundEvents.Contains(uniqueId, argument);
1265             }
1266 
RegisterForEventValidation(string uniqueId, string argument)1267             public void RegisterForEventValidation(string uniqueId, string argument) {
1268                 if (_outboundEvents == null) {
1269                     if (_clientScriptManager._owner.IsCallback) {
1270                         _clientScriptManager.EnsureEventValidationFieldLoaded();
1271 
1272                         // _outboundEvents could have been initialized by the call to EnsureEventValidationFieldLoaded.
1273                         if (_outboundEvents == null) {
1274                             _outboundEvents = new EventValidationStore();
1275                         }
1276                     }
1277                     else {
1278                         // Make a new store object and tie it to the outbound __VIEWSTATE field.
1279                         // (This is the only field which can have a null/empty 'target'.)
1280                         _outboundEvents = new EventValidationStore();
1281                         _outboundEvents.Add(null, _clientScriptManager._owner.ClientState);
1282                     }
1283                 }
1284 
1285                 _outboundEvents.Add(uniqueId, argument);
1286             }
1287 
TryLoadEventValidationField(object eventValidationField)1288             public bool TryLoadEventValidationField(object eventValidationField) {
1289                 EventValidationStore validatedIncomingEvents = eventValidationField as EventValidationStore;
1290                 if (validatedIncomingEvents == null || validatedIncomingEvents.Count < 1) {
1291                     return true; // empty collection is not an error condition
1292                 }
1293 
1294                 Debug.Assert(_outboundEvents == null);
1295 
1296                 string viewStateString = _clientScriptManager._owner.RequestViewStateString;
1297                 if (!validatedIncomingEvents.Contains(null, viewStateString)) {
1298                     return false; // error: this event validation store isn't associated with the incoming __VIEWSTATE
1299                 }
1300                 _inboundEvents = validatedIncomingEvents;
1301 
1302                 if (_clientScriptManager._owner.IsCallback) {
1303                     // Seed the outbound provider with the valid inbound values; clone so that any outbound values
1304                     // added during page processing aren't accidentally treated as valid inbound values.
1305                     EventValidationStore clonedEventValidationStore = validatedIncomingEvents.Clone();
1306                     _outboundEvents = clonedEventValidationStore;
1307                 }
1308                 return true;
1309             }
1310         }
1311 
1312         // provides the legacy implementation of event validation (before DevDiv #233564)
1313         private sealed class LegacyEventValidationProvider : IEventValidationProvider {
1314             private readonly ClientScriptManager _clientScriptManager;
1315             private ArrayList _validEventReferences;
1316             private HybridDictionary _clientPostBackValidatedEventTable;
1317 
LegacyEventValidationProvider(ClientScriptManager clientScriptManager)1318             internal LegacyEventValidationProvider(ClientScriptManager clientScriptManager) {
1319                 _clientScriptManager = clientScriptManager;
1320             }
1321 
ComputeHashKey(String uniqueId, String argument)1322             private static int ComputeHashKey(String uniqueId, String argument) {
1323                 if (String.IsNullOrEmpty(argument)) {
1324                     return StringUtil.GetStringHashCode(uniqueId);
1325                 }
1326 
1327                 return StringUtil.GetStringHashCode(uniqueId) ^ StringUtil.GetStringHashCode(argument);
1328             }
1329 
GetEventValidationStoreObject()1330             public object GetEventValidationStoreObject() {
1331                 // We only produce the object to be serialized if there is data in the store
1332                 if (_validEventReferences != null && _validEventReferences.Count > 0) {
1333                     return _validEventReferences;
1334                 }
1335                 else {
1336                     return null;
1337                 }
1338             }
1339 
IsValid(string uniqueId, string argument)1340             public bool IsValid(string uniqueId, string argument) {
1341                 if (_clientPostBackValidatedEventTable == null) {
1342                     return false;
1343                 }
1344 
1345 #if DEBUGEVENTVALIDATION
1346             String hashCode = uniqueId + "@" + argument;
1347 #else
1348                 int hashCode = ComputeHashKey(uniqueId, argument);
1349 #endif //DEBUGEVENTVALIDATION
1350 
1351                 return _clientPostBackValidatedEventTable.Contains(hashCode);
1352             }
1353 
RegisterForEventValidation(string uniqueId, string argument)1354             public void RegisterForEventValidation(string uniqueId, string argument) {
1355 #if DEBUGEVENTVALIDATION
1356             string key = uniqueId + "@" + argument;
1357 #else
1358                 int key = ComputeHashKey(uniqueId, argument);
1359 #endif //DEBUGEVENTVALIDATION
1360 
1361                 string stateString = _clientScriptManager._owner.ClientState;
1362                 if (stateString == null) {
1363                     stateString = String.Empty;
1364                 }
1365 
1366                 if (_validEventReferences == null) {
1367                     if (_clientScriptManager._owner.IsCallback) {
1368                         _clientScriptManager.EnsureEventValidationFieldLoaded();
1369                         if (_validEventReferences == null) {
1370                             _validEventReferences = new ArrayList();
1371                         }
1372                     }
1373                     else {
1374                         _validEventReferences = new ArrayList();
1375                         _validEventReferences.Add(
1376                             StringUtil.GetStringHashCode(stateString));
1377                     }
1378                 }
1379 
1380 #if DEBUGEVENTVALIDATION
1381             Debug.Assert(!_validEventReferences.Contains(key));
1382 #endif //DEBUGEVENTVALIDATION
1383 
1384                 _validEventReferences.Add(key);
1385             }
1386 
TryLoadEventValidationField(object eventValidationField)1387             public bool TryLoadEventValidationField(object eventValidationField) {
1388                 ArrayList validatedClientEvents = eventValidationField as ArrayList;
1389                 if (validatedClientEvents == null || validatedClientEvents.Count < 1) {
1390                     return true; // empty collection is not an error condition
1391                 }
1392 
1393                 Debug.Assert(_clientPostBackValidatedEventTable == null);
1394                 int viewStateHashCode = (int)validatedClientEvents[0];
1395 
1396                 string viewStateString = _clientScriptManager._owner.RequestViewStateString;
1397 
1398                 if (viewStateHashCode != StringUtil.GetStringHashCode(viewStateString)) {
1399                     return false; // hash mismatch is an error condition
1400                 }
1401 
1402                 _clientPostBackValidatedEventTable = new HybridDictionary(validatedClientEvents.Count - 1, true);
1403 
1404                 // Ignore the first item in the arrayList, which is the controlstate
1405                 for (int index = 1; index < validatedClientEvents.Count; index++) {
1406 #if DEBUGEVENTVALIDATION
1407                 string hashKey = (string)validatedClientEvents[index];
1408 #else
1409                     int hashKey = (int)validatedClientEvents[index];
1410 #endif //DEBUGEVENTVALIDATION
1411                     _clientPostBackValidatedEventTable[hashKey] = null;
1412                 }
1413 
1414                 if (_clientScriptManager._owner.IsCallback) {
1415                     _validEventReferences = validatedClientEvents;
1416                 }
1417                 return true;
1418             }
1419         }
1420     }
1421 
1422     [Serializable]
1423     internal class ScriptKey {
1424         [NonSerialized]
1425         private Type _type;
1426         private string _typeNameForSerialization;
1427         private string _key;
1428         private bool _isInclude;
1429         private bool _isResource;
1430 
ScriptKey(Type type, string key)1431         internal ScriptKey(Type type, string key) : this(type, key, false, false) {
1432         }
1433 
ScriptKey(Type type, string key, bool isInclude, bool isResource)1434         internal ScriptKey(Type type, string key, bool isInclude, bool isResource) {
1435             Debug.Assert(type != null);
1436             _type = type;
1437 
1438             // To treat nulls the same as empty strings, make them empty string.
1439             if (key == null) {
1440                 key = String.Empty;
1441             }
1442             _key = key;
1443             _isInclude = isInclude;
1444             _isResource = isResource;
1445         }
1446 
1447         public Assembly Assembly {
1448             get {
1449                 return _type == null ? null : AssemblyResourceLoader.GetAssemblyFromType(_type);
1450             }
1451         }
1452 
1453         public bool IsResource {
1454             get {
1455                 return _isResource;
1456             }
1457         }
1458 
1459         public string Key {
1460             get {
1461                 return _key;
1462             }
1463         }
1464 
GetHashCode()1465         public override int GetHashCode() {
1466             return WebUtil.HashCodeCombiner.CombineHashCodes(_type.GetHashCode(), _key.GetHashCode(),
1467                                                              _isInclude.GetHashCode());
1468         }
1469 
Equals(object o)1470         public override bool Equals(object o) {
1471             ScriptKey key = (ScriptKey)o;
1472             return (key._type == _type) && (key._key == _key) && (key._isInclude == _isInclude);
1473         }
1474 
1475         [OnSerializing()]
OnSerializingMethod(StreamingContext context)1476         private void OnSerializingMethod(StreamingContext context) {
1477             // create a string representation of _type
1478             _typeNameForSerialization = System.Web.UI.Util.GetAssemblyQualifiedTypeName(_type);
1479         }
1480 
1481         [OnDeserialized()]
OnDeserializedMethod(StreamingContext context)1482         private void OnDeserializedMethod(StreamingContext context) {
1483             // re-create _type from its string representation
1484             _type = BuildManager.GetType(_typeNameForSerialization, true /*throwOnFail*/, false /*ignoreCase*/);
1485         }
1486     }
1487 }
1488