diff options
Diffstat (limited to 'mcs/class/referencesource/System.Web/UI')
65 files changed, 859 insertions, 244 deletions
diff --git a/mcs/class/referencesource/System.Web/UI/BaseParser.cs b/mcs/class/referencesource/System.Web/UI/BaseParser.cs index b5eea9d3ddc..1ecb550dadd 100644 --- a/mcs/class/referencesource/System.Web/UI/BaseParser.cs +++ b/mcs/class/referencesource/System.Web/UI/BaseParser.cs @@ -79,7 +79,7 @@ public class BaseParser { } private Regex _tagRegex; - // The 3.5 regex is used only when targeting 2.0/3.5 for backward compatibility (Dev10 bug 830783). + // The 3.5 regex is used only when targeting 2.0/3.5 for backward compatibility (Dev10 private readonly static Regex tagRegex35 = new TagRegex35(); // The 4.0 regex is used for web sites targeting 4.0 and above. private readonly static Regex tagRegex40 = new TagRegex(); diff --git a/mcs/class/referencesource/System.Web/UI/ConflictOptions.cs b/mcs/class/referencesource/System.Web/UI/ConflictOptions.cs index 6fce0c1620d..916a363e5a6 100644 --- a/mcs/class/referencesource/System.Web/UI/ConflictOptions.cs +++ b/mcs/class/referencesource/System.Web/UI/ConflictOptions.cs @@ -23,4 +23,4 @@ namespace System.Web.UI { /// </devdoc> CompareAllValues = 1 } -} +}
\ No newline at end of file diff --git a/mcs/class/referencesource/System.Web/UI/Control.cs b/mcs/class/referencesource/System.Web/UI/Control.cs index 1a9686a27e5..b698ff6f310 100644 --- a/mcs/class/referencesource/System.Web/UI/Control.cs +++ b/mcs/class/referencesource/System.Web/UI/Control.cs @@ -146,7 +146,6 @@ namespace System.Web.UI { /// <devdoc> /// <para>Initializes a new instance of the <see cref='System.Web.UI.Control'/> class.</para> /// </devdoc> - [System.Runtime.TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] public Control() { if (this is INamingContainer) flags.Set(isNamingContainer); @@ -169,7 +168,6 @@ namespace System.Web.UI { WebSysDescription(SR.Control_ClientIDMode) ] public virtual ClientIDMode ClientIDMode { - [System.Runtime.TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] get { return ClientIDModeValue; } @@ -396,7 +394,6 @@ namespace System.Web.UI { ] protected internal virtual HttpContext Context { // Request context containing the intrinsics - [System.Runtime.TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] get { Page page = Page; if(page != null) { @@ -509,7 +506,6 @@ namespace System.Web.UI { /// is read-only.</para> /// </devdoc> protected EventHandlerList Events { - [System.Runtime.TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] get { if (_events == null) { _events = new EventHandlerList(); @@ -518,7 +514,6 @@ namespace System.Web.UI { } } - [System.Runtime.TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] protected bool HasEvents() { return (_events != null); } @@ -862,7 +857,6 @@ namespace System.Web.UI { /// VSWhidbey 80467: Need to adapt id separator. /// </devdoc> protected char IdSeparator { - [System.Runtime.TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] get { if (Page != null) { return Page.IdSeparator; @@ -942,7 +936,6 @@ namespace System.Web.UI { // VSWhidbey 244999 internal virtual bool IsReloadable { - [System.Runtime.TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] get { return false; } @@ -1118,7 +1111,6 @@ namespace System.Web.UI { WebSysDescription(SR.Control_Parent) ] public virtual Control Parent { - [System.Runtime.TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] get { return _parent; } @@ -1590,7 +1582,6 @@ namespace System.Web.UI { } } - [System.Runtime.TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] internal void PreventAutoID() { // controls that are also naming containers must always get an id if (flags[isNamingContainer] == false) { @@ -1603,7 +1594,6 @@ namespace System.Web.UI { /// <para>Notifies the control that an element, XML or HTML, was parsed, and adds it to /// the control.</para> /// </devdoc> - [System.Runtime.TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] protected virtual void AddParsedSubObject(object obj) { Control control = obj as Control; if (control != null) { @@ -2361,7 +2351,6 @@ namespace System.Web.UI { _controlState = ControlState.PreRendered; } - // Same as PreRenderRecursive, but has an async point after the call to this.OnPreRender. internal async Task PreRenderRecursiveInternalAsync(Page page) { // Call Visible property and cache value in !flags[invisible] to allow Visible to be overridden. @@ -2372,7 +2361,15 @@ namespace System.Web.UI { } else { flags.Clear(invisible); - EnsureChildControls(); + if (AppSettings.EnableAsyncModelBinding) { + using (page.Context.SyncContext.AllowVoidAsyncOperationsBlock()) { + EnsureChildControls(); + await page.GetWaitForPreviousStepCompletionAwaitable(); + } + } + else { + EnsureChildControls(); + } using (page.Context.SyncContext.AllowVoidAsyncOperationsBlock()) { if (AdapterInternal != null) { @@ -2389,8 +2386,17 @@ namespace System.Web.UI { int controlCount = _controls.Count; for (int i = 0; i < controlCount; i++) { - _controls[i].PreRenderRecursiveInternal(); + if (AppSettings.EnableAsyncModelBinding) { + // To make sure every OnPreRender is awaited so that _controlState + // would not be set to ControlState.PreRendered until the control is + // really PreRendered + await _controls[i].PreRenderRecursiveInternalAsync(page); + } + else { + _controls[i].PreRenderRecursiveInternal(); + } } + _controls.SetCollectionReadOnly(oldmsg); } } @@ -2587,7 +2593,6 @@ namespace System.Web.UI { /// <devdoc> /// <para>[To be supplied.]</para> /// </devdoc> - [System.Runtime.TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] public virtual void RenderControl(HtmlTextWriter writer) { //use the Adapter property to ensure it is resolved RenderControl(writer, Adapter); @@ -2714,7 +2719,7 @@ namespace System.Web.UI { // But for control which requires its OnInit method to be called again // to properly initialize when the control is removed and added back // to Page's control tree, the control can override IsReloadable - // to true so the control state is reset. e.g. Validator, see bug + // to true so the control state is reset. e.g. Validator, see if (IsReloadable) { _controlState = ControlState.Constructed; } @@ -2842,7 +2847,6 @@ namespace System.Web.UI { DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ] protected virtual bool ViewStateIgnoresCase { - [System.Runtime.TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] get { return false; } @@ -2949,7 +2953,6 @@ namespace System.Web.UI { /// child controls they contain in preperation for postback or rendering. /// </para> /// </devdoc> - [System.Runtime.TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] protected internal virtual void CreateChildControls() { } @@ -3176,7 +3179,7 @@ namespace System.Web.UI { internal Control FindControlFromPageIfNecessary(string id) { Control c = FindControl(id); // Find control from the page if it's a hierarchical ID. - // Dev11 bug 19915 + // Dev11 if (c == null && Page != null) { char[] findControlSeparators = { ID_SEPARATOR, LEGACY_ID_SEPARATOR }; if (id.IndexOfAny(findControlSeparators) != -1) { @@ -3275,7 +3278,6 @@ namespace System.Web.UI { /// so that they can be stored in the <see langword='StateBag'/> /// object.</para> /// </devdoc> - [System.Runtime.TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] protected virtual void TrackViewState() { if (_viewState != null) _viewState.TrackViewState(); @@ -3420,7 +3422,6 @@ namespace System.Web.UI { /// <para>Notifies the control that an element, XML or HTML, was parsed, and adds it to /// the control.</para> /// </devdoc> - [System.Runtime.TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] void IParserAccessor.AddParsedSubObject(object obj) { AddParsedSubObject(obj); } @@ -3436,7 +3437,6 @@ namespace System.Web.UI { } private Control OwnerControl { - [System.Runtime.TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] get { if (RareFields == null) { return null; diff --git a/mcs/class/referencesource/System.Web/UI/ControlBuilder.cs b/mcs/class/referencesource/System.Web/UI/ControlBuilder.cs index 6a5c0117707..91b38689c56 100644 --- a/mcs/class/referencesource/System.Web/UI/ControlBuilder.cs +++ b/mcs/class/referencesource/System.Web/UI/ControlBuilder.cs @@ -2220,8 +2220,8 @@ namespace System.Web.UI { try { // We must push the theme flag to child complex objects so they are init'd properly - // DevDiv Bug 59351 - // Set applytheme only when necessary. + // DevDiv + if (entry.Builder.flags[applyTheme] != flags[applyTheme]) { entry.Builder.flags[applyTheme] = flags[applyTheme]; } diff --git a/mcs/class/referencesource/System.Web/UI/ControlCollection.cs b/mcs/class/referencesource/System.Web/UI/ControlCollection.cs index 29e193397dc..61aacfdc5b0 100644 --- a/mcs/class/referencesource/System.Web/UI/ControlCollection.cs +++ b/mcs/class/referencesource/System.Web/UI/ControlCollection.cs @@ -192,7 +192,6 @@ namespace System.Web.UI { /// </para> /// </devdoc> public virtual int Count { - [System.Runtime.TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] get { return _size; } @@ -363,14 +362,12 @@ namespace System.Web.UI { } object IEnumerator.Current { - [System.Runtime.TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] get { return Current; } } public Control Current { - [System.Runtime.TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] get { if (index == -1) throw new InvalidOperationException(SR.GetString(SR.ListEnumCurrentOutOfRange)); diff --git a/mcs/class/referencesource/System.Web/UI/EmptyTextWriter.cs b/mcs/class/referencesource/System.Web/UI/EmptyTextWriter.cs index bab0f2435ba..eddd0967fe0 100644 --- a/mcs/class/referencesource/System.Web/UI/EmptyTextWriter.cs +++ b/mcs/class/referencesource/System.Web/UI/EmptyTextWriter.cs @@ -138,4 +138,4 @@ namespace System.Web.UI } } } -#endif +#endif
\ No newline at end of file diff --git a/mcs/class/referencesource/System.Web/UI/HTMLTextWriter.cs b/mcs/class/referencesource/System.Web/UI/HTMLTextWriter.cs index e8eafdfb8c8..b5c395a74f8 100644 --- a/mcs/class/referencesource/System.Web/UI/HTMLTextWriter.cs +++ b/mcs/class/referencesource/System.Web/UI/HTMLTextWriter.cs @@ -98,7 +98,7 @@ namespace System.Web.UI { public const char StyleEqualsChar = ':'; public const string DefaultTabString = "\t"; - // The DesignerRegion attribute name must be kept in [....] with + // The DesignerRegion attribute name must be kept in sync with // System.Web.UI.Design.DesignerRegion.DesignerRegionNameAttribute internal const string DesignerRegionAttributeName = "_designerRegion"; @@ -139,7 +139,9 @@ namespace System.Web.UI { RegisterTag("big", HtmlTextWriterTag.Big, TagType.Inline); RegisterTag("blockquote", HtmlTextWriterTag.Blockquote, TagType.Other); RegisterTag("body", HtmlTextWriterTag.Body, TagType.Other); - RegisterTag("br", HtmlTextWriterTag.Br, TagType.Other); + // Devdiv 852940, BR is a self-closing tag + RegisterTag("br", HtmlTextWriterTag.Br, + BinaryCompatibility.Current.TargetsAtLeastFramework46 ? TagType.NonClosing : TagType.Other); RegisterTag("button", HtmlTextWriterTag.Button, TagType.Inline); RegisterTag("caption", HtmlTextWriterTag.Caption, TagType.Other); RegisterTag("center", HtmlTextWriterTag.Center, TagType.Other); @@ -373,7 +375,6 @@ namespace System.Web.UI { } //Writes a string to the text stream. - [System.Runtime.TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] public override void Write(string s) { if (tabsPending) { OutputTabs(); @@ -390,7 +391,6 @@ namespace System.Web.UI { } //Writes a character to the text stream. - [System.Runtime.TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] public override void Write(char value) { if (tabsPending) { OutputTabs(); @@ -1128,22 +1128,18 @@ namespace System.Web.UI { } } - [System.Runtime.TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] protected virtual string RenderBeforeTag() { return null; } - [System.Runtime.TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] protected virtual string RenderBeforeContent() { return null; } - [System.Runtime.TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] protected virtual string RenderAfterContent() { return null; } - [System.Runtime.TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] protected virtual string RenderAfterTag() { return null; } diff --git a/mcs/class/referencesource/System.Web/UI/HtmlControls/HtmlControl.cs b/mcs/class/referencesource/System.Web/UI/HtmlControls/HtmlControl.cs index 157049e6404..7061b39e0d0 100644 --- a/mcs/class/referencesource/System.Web/UI/HtmlControls/HtmlControl.cs +++ b/mcs/class/referencesource/System.Web/UI/HtmlControls/HtmlControl.cs @@ -73,7 +73,6 @@ namespace System.Web.UI.HtmlControls { DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), ] public AttributeCollection Attributes { - [System.Runtime.TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] get { if (_attributes == null) _attributes = new AttributeCollection(ViewState); diff --git a/mcs/class/referencesource/System.Web/UI/HtmlForm.cs b/mcs/class/referencesource/System.Web/UI/HtmlForm.cs index ce4088eaf66..5dbcc9f16bd 100644 --- a/mcs/class/referencesource/System.Web/UI/HtmlForm.cs +++ b/mcs/class/referencesource/System.Web/UI/HtmlForm.cs @@ -219,8 +219,8 @@ namespace System.Web.UI.HtmlControls { /// <devdoc> /// Overridden to return a constant value or tack the ID onto the same constant value. - /// This fixes a bug in PocketPC which doesn't allow the name and ID of a form to be different - /// </devdoc> + /// This fixes a + public override string UniqueID { get { if (NamingContainer == Page) { @@ -279,7 +279,7 @@ namespace System.Web.UI.HtmlControls { // scenarios need the postback action to be the original URL. Note however, if Server.Transfer/Execute // is used, the action will be set to the transferred/executed page, that is, the value of // CurrentExecutionFilePathObject. This is because of ASURT 59970 and the document attached to - // that bug, which indirectly states that things should behave this way when Transfer/Execute is used. + // that if (Context.ServerExecuteDepth == 0) { // There hasn't been any Server.Transfer or RewritePath. // ASURT 15979: need to use a relative path, not absolute @@ -423,7 +423,7 @@ namespace System.Web.UI.HtmlControls { (page.RequestInternal.Browser.W3CDomVersion.Major > 0)) { if (DefaultButton.Length > 0) { // Find control from the page if it's a hierarchical ID. - // Dev11 bug 19915 + // Dev11 Control c = FindControlFromPageIfNecessary(DefaultButton); if (c is IButtonControl) { diff --git a/mcs/class/referencesource/System.Web/UI/IUpdatePanel.cs b/mcs/class/referencesource/System.Web/UI/IUpdatePanel.cs index 1bafbf1e45e..37bffe52a7a 100644 --- a/mcs/class/referencesource/System.Web/UI/IUpdatePanel.cs +++ b/mcs/class/referencesource/System.Web/UI/IUpdatePanel.cs @@ -7,4 +7,4 @@ namespace System.Web.UI { internal interface IUpdatePanel { } -} +}
\ No newline at end of file diff --git a/mcs/class/referencesource/System.Web/UI/LiteralControl.cs b/mcs/class/referencesource/System.Web/UI/LiteralControl.cs index 99e8f1616ff..f7e9fe26c72 100644 --- a/mcs/class/referencesource/System.Web/UI/LiteralControl.cs +++ b/mcs/class/referencesource/System.Web/UI/LiteralControl.cs @@ -74,7 +74,6 @@ namespace System.Web.UI { /// <devdoc> /// <para>Saves any state that was modified after mark.</para> /// </devdoc> - [System.Runtime.TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] protected internal override void Render(HtmlTextWriter output) { output.Write(_text); } diff --git a/mcs/class/referencesource/System.Web/UI/ObjectStateFormatter.cs b/mcs/class/referencesource/System.Web/UI/ObjectStateFormatter.cs index 5eb66eae4cd..7e60b164e4a 100644 --- a/mcs/class/referencesource/System.Web/UI/ObjectStateFormatter.cs +++ b/mcs/class/referencesource/System.Web/UI/ObjectStateFormatter.cs @@ -189,7 +189,7 @@ namespace System.Web.UI { return null; } - // Note: duplicated (somewhat) in GetMacKeyModifier, keep in [....] + // Note: duplicated (somewhat) in GetMacKeyModifier, keep in sync // See that method for comments on why these modifiers are in place List<string> specificPurposes = new List<string>() { @@ -216,7 +216,7 @@ namespace System.Web.UI { return null; } - // Note: duplicated (somewhat) in GetSpecificPurposes, keep in [....] + // Note: duplicated (somewhat) in GetSpecificPurposes, keep in sync // Use the page's directory and class name as part of the key (ASURT 64044) uint pageHashCode = _page.GetClientStateIdentifier(); diff --git a/mcs/class/referencesource/System.Web/UI/Page.cs b/mcs/class/referencesource/System.Web/UI/Page.cs index 9f9f36cfd55..d8ee546df17 100644 --- a/mcs/class/referencesource/System.Web/UI/Page.cs +++ b/mcs/class/referencesource/System.Web/UI/Page.cs @@ -17,6 +17,7 @@ namespace System.Web.UI { using System; using System.Collections; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; @@ -194,7 +195,7 @@ public class Page: TemplateControl, IHttpHandler { private const string PageSubmitScriptKey = "PageSubmitScript"; private const string PageReEnableControlsScriptKey = "PageReEnableControlsScript"; - // NOTE: Make sure this stays in [....] with MobilePage.PageRegisteredControlsThatRequirePostBackKey + // NOTE: Make sure this stays in sync with MobilePage.PageRegisteredControlsThatRequirePostBackKey // private const string PageRegisteredControlsThatRequirePostBackKey = "__ControlsRequirePostBackKey__"; @@ -420,6 +421,8 @@ public class Page: TemplateControl, IHttpHandler { private UnobtrusiveValidationMode? _unobtrusiveValidationMode; + private bool _executingAsyncTasks = false; + private static StringSet s_systemPostFields; static Page() { // Create a static hashtable with all the names that should be @@ -475,6 +478,15 @@ public class Page: TemplateControl, IHttpHandler { set; } + internal bool IsExecutingAsyncTasks { + get { + return _executingAsyncTasks; + } + set { + _executingAsyncTasks = value; + } + } + public ModelBindingExecutionContext ModelBindingExecutionContext { get { if (_modelBindingExecutionContext == null) { @@ -606,7 +618,6 @@ public class Page: TemplateControl, IHttpHandler { /// <para>Gets the HttpContext for the Page.</para> /// </devdoc> protected internal override HttpContext Context { - [System.Runtime.TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] get { if (_context == null) { _context = HttpContext.Current; @@ -1111,7 +1122,6 @@ public class Page: TemplateControl, IHttpHandler { DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ] public HttpRequest Request { - [System.Runtime.TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] get { if (_request == null) throw new HttpException(SR.GetString(SR.Request_not_available)); @@ -1520,14 +1530,14 @@ public class Page: TemplateControl, IHttpHandler { // a __VIEWSTATE field. It shouldn't be considered sensitive information since its inputs // are assumed to be known by all parties. internal uint GetClientStateIdentifier() { - // + // Use non-randomized hash code algorithms instead of String.GetHashCode. // Use the page's directory and class name as part of the key (ASURT 64044) // We need to make sure that the hash is case insensitive, since the file system // is, and strange view state errors could otherwise happen (ASURT 128657) - int pageHashCode = StringComparer.InvariantCultureIgnoreCase.GetHashCode( - TemplateSourceDirectory); - pageHashCode += StringComparer.InvariantCultureIgnoreCase.GetHashCode(GetType().Name); + int pageHashCode = StringUtil.GetNonRandomizedHashCode(TemplateSourceDirectory, ignoreCase:true); + pageHashCode += StringUtil.GetNonRandomizedHashCode(GetType().Name, ignoreCase:true); + return (uint)pageHashCode; } @@ -1807,7 +1817,6 @@ public class Page: TemplateControl, IHttpHandler { Browsable(false) ] public override bool Visible { - [System.Runtime.TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] get { return base.Visible; } @@ -2264,8 +2273,8 @@ public class Page: TemplateControl, IHttpHandler { // Need to always render out the viewstate field so alternate viewstate persistence will get called writer.Write("\r\n<input type=\"hidden\" name=\""); writer.Write(ViewStateFieldPrefixID); - // Dev10 Bug 486494 - // Remove previously rendered NewLine + // Dev10 + writer.Write("\" id=\""); writer.Write(ViewStateFieldPrefixID); writer.WriteLine("\" value=\"\" />"); @@ -2684,7 +2693,6 @@ window.onload = WebForm_RestoreScrollPosition; } } - [System.Runtime.TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] internal bool ApplyControlStyleSheet(Control ctrl) { if (_styleSheet != null) { _styleSheet.ApplyControlSkin(ctrl); @@ -3120,6 +3128,130 @@ window.onload = WebForm_RestoreScrollPosition; } + // Operations like FindControl and LoadPostData call EnsureDataBound, which may fire up + // async model binding methods. Therefore we make ProcessPostData method to be async so that we can await + // async data bindings. + // The differences between ProcessPostData and ProcessPostDataAsync are: + // 1. ProcessPostDataAsync awaits GetWaitForPreviousStepCompletionAwaitable after FindControl(); + // 2. ProcessPostDataAsync calls LoadPostDataAsync() instead of LoadPostData(). + private async Task ProcessPostDataAsync(NameValueCollection postData, bool fBeforeLoad) { + if (_changedPostDataConsumers == null) + _changedPostDataConsumers = new ArrayList(); + + // identify controls that have postback data + if (postData != null) { + foreach (string postKey in postData) { + if (postKey != null) { + // Ignore system post fields + if (IsSystemPostField(postKey)) + continue; + + Control ctrl = null; + using (Context.SyncContext.AllowVoidAsyncOperationsBlock()) { + ctrl = FindControl(postKey); + await GetWaitForPreviousStepCompletionAwaitable(); + } + + if (ctrl == null) { + if (fBeforeLoad) { + // It was not found, so keep track of it for the post load attempt + if (_leftoverPostData == null) + _leftoverPostData = new NameValueCollection(); + _leftoverPostData.Add(postKey, null); + } + continue; + } + + IPostBackDataHandler consumer = ctrl.PostBackDataHandler; + + // Ignore controls that are not IPostBackDataHandler (see ASURT 13581) + if (consumer == null) { + + // If it's a IPostBackEventHandler (which doesn't implement IPostBackDataHandler), + // register it (ASURT 39040) + if (ctrl.PostBackEventHandler != null) + RegisterRequiresRaiseEvent(ctrl.PostBackEventHandler); + + continue; + } + + if (consumer != null) { + NameValueCollection postCollection = ctrl.CalculateEffectiveValidateRequest() ? _requestValueCollection : _unvalidatedRequestValueCollection; + bool changed = await LoadPostDataAsync(consumer, postKey, postCollection); + + if (changed) + _changedPostDataConsumers.Add(ctrl); + } + + // ensure controls are only notified of postback once + if (_controlsRequiringPostBack != null) + _controlsRequiringPostBack.Remove(postKey); + } + } + } + + // Keep track of the leftover for the post-load attempt + ArrayList leftOverControlsRequiringPostBack = null; + + // process controls that explicitly registered to be notified of postback + if (_controlsRequiringPostBack != null) { + foreach (string controlID in _controlsRequiringPostBack) { + Control c = null; + using (Context.SyncContext.AllowVoidAsyncOperationsBlock()) { + c = FindControl(controlID); + await GetWaitForPreviousStepCompletionAwaitable(); + } + + if (c != null) { + IPostBackDataHandler consumer = c.AdapterInternal as IPostBackDataHandler; + if (consumer == null) { + consumer = c as IPostBackDataHandler; + } + + // Give a helpful error if the control is not a IPostBackDataHandler (ASURT 128532) + if (consumer == null) { + throw new HttpException(SR.GetString(SR.Postback_ctrl_not_found, controlID)); + } + + NameValueCollection postCollection = c.CalculateEffectiveValidateRequest() ? _requestValueCollection : _unvalidatedRequestValueCollection; + bool changed = await LoadPostDataAsync(consumer, controlID, postCollection); + if (changed) + _changedPostDataConsumers.Add(c); + } + else { + if (fBeforeLoad) { + if (leftOverControlsRequiringPostBack == null) + leftOverControlsRequiringPostBack = new ArrayList(); + leftOverControlsRequiringPostBack.Add(controlID); + } + } + } + + _controlsRequiringPostBack = leftOverControlsRequiringPostBack; + } + + } + + private async Task<bool> LoadPostDataAsync(IPostBackDataHandler consumer, string postKey, NameValueCollection postCollection) { + bool changed; + + // ListControl family controls call EnsureDataBound in consumer.LoadPostData, which could be an async call in 4.6. + // LoadPostData, however, is a sync method, which means we cannot await EnsureDataBound in the method. + // To workaround this, for ListControl family controls, we call EnsureDataBound before we call into LoadPostData. + if (AppSettings.EnableAsyncModelBinding && consumer is ListControl) { + var listControl = consumer as ListControl; + listControl.SkipEnsureDataBoundInLoadPostData = true; + using (Context.SyncContext.AllowVoidAsyncOperationsBlock()) { + listControl.InternalEnsureDataBound(); + await GetWaitForPreviousStepCompletionAwaitable(); + } + } + + changed = consumer.LoadPostData(postKey, postCollection); + + return changed; + } + /* * This method will raise change events for those controls that indicated * during PostProcessData that their data has changed. @@ -4928,7 +5060,13 @@ window.onload = WebForm_RestoreScrollPosition; } if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Verbose, EtwTraceFlags.Page)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_PAGE_LOAD_POSTDATA_ENTER, _context.WorkerRequest); - ProcessPostData(_requestValueCollection, true /* fBeforeLoad */); + if (AppSettings.EnableAsyncModelBinding) { + await ProcessPostDataAsync(_requestValueCollection, true /* fBeforeLoad */).WithinCancellableCallback(con); + } + else { + ProcessPostData(_requestValueCollection, true /* fBeforeLoad */); + } + if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Verbose, EtwTraceFlags.Page)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_PAGE_LOAD_POSTDATA_LEAVE, _context.WorkerRequest); if (con.TraceIsEnabled) Trace.Write("aspx.page", "End ProcessPostData"); } @@ -4949,7 +5087,13 @@ window.onload = WebForm_RestoreScrollPosition; if (IsPostBack) { // Try process the post data again (ASURT 29045) if (con.TraceIsEnabled) Trace.Write("aspx.page", "Begin ProcessPostData Second Try"); - ProcessPostData(_leftoverPostData, false /* !fBeforeLoad */); + if (AppSettings.EnableAsyncModelBinding) { + await ProcessPostDataAsync(_leftoverPostData, false /* !fBeforeLoad */).WithinCancellableCallback(con); + } + else { + ProcessPostData(_leftoverPostData, false /* !fBeforeLoad */); + } + if (con.TraceIsEnabled) { Trace.Write("aspx.page", "End ProcessPostData Second Try"); Trace.Write("aspx.page", "Begin Raise ChangedEvents"); @@ -5437,8 +5581,8 @@ window.onload = WebForm_RestoreScrollPosition; HttpCapabilitiesBase caps = _request.Browser; if(caps != null) { - // Dev10 440476: Page.SetIntrinsics method has a bug causing throwing NullReferenceException - // in certain circumstances. This edge case was regressed by the VSWhidbey fix below. + // Dev10 440476: Page.SetIntrinsics method has a + // VSWhidbey 109162: Set content type at the very beginning so it can be // overwritten within the user code of the page if needed. @@ -5850,7 +5994,7 @@ window.onload = WebForm_RestoreScrollPosition; } } - private CancellationTokenSource CreateCancellationTokenFromAsyncTimeout() { + internal CancellationTokenSource CreateCancellationTokenFromAsyncTimeout() { TimeSpan timeout = AsyncTimeout; // CancellationTokenSource can only create timers within a specific range (<= _maxAsyncTimeout) diff --git a/mcs/class/referencesource/System.Web/UI/PartialCachingControl.cs b/mcs/class/referencesource/System.Web/UI/PartialCachingControl.cs index c7eb347bfb9..5e61537f1e5 100644 --- a/mcs/class/referencesource/System.Web/UI/PartialCachingControl.cs +++ b/mcs/class/referencesource/System.Web/UI/PartialCachingControl.cs @@ -468,9 +468,9 @@ public abstract class BasePartialCachingControl : Control { NameValueCollection reqValCollection; HttpRequest request = Page.Request; if (request != null && request.HttpVerb == HttpVerb.POST) { - // Bug 6129: Partial cache key should include posted form values in postbacks. - // Include both QueryString and Form values (but not Cookies or Server Variables like Request.Params does). - // Per Request.Params behavior, add QueryString values before Form values + // + + reqValCollection = new NameValueCollection(request.QueryString); reqValCollection.Add(request.Form); } @@ -844,10 +844,8 @@ internal class ControlCachedVary { public override int GetHashCode () { HashCodeCombiner hashCodeCombiner = new HashCodeCombiner(); - // Cast _varyByCustom to an object, since the HashCodeCombiner.AddObject(string) - // overload uses StringUtil.GetStringHashCode(). We want to use String.GetHashCode() - // in this method, since we do not require a stable hash code across architectures. - hashCodeCombiner.AddObject((object)_varyByCustom); + // We need non-randomized hash code for _varyByCustom + hashCodeCombiner.AddInt(StringUtil.GetNonRandomizedHashCode(_varyByCustom)); hashCodeCombiner.AddArray(_varyByParams); hashCodeCombiner.AddArray(_varyByControls); diff --git a/mcs/class/referencesource/System.Web/UI/TargetFrameworkUtil.cs b/mcs/class/referencesource/System.Web/UI/TargetFrameworkUtil.cs index e8b736cd82a..e4030f7da89 100644 --- a/mcs/class/referencesource/System.Web/UI/TargetFrameworkUtil.cs +++ b/mcs/class/referencesource/System.Web/UI/TargetFrameworkUtil.cs @@ -103,7 +103,7 @@ namespace System.Web.UI { // The provider needs not be cached because the TFP service // returns light-weight providers that delegate to the same - // underlying TFP instance. (Dev10 bug 795001) + // underlying TFP instance. (Dev10 private static TypeDescriptionProvider GetTargetFrameworkProvider(object obj) { TypeDescriptionProviderService service = TargetFrameworkUtil.TypeDescriptionProviderService; if (service != null) { @@ -532,7 +532,7 @@ namespace System.Web.UI { // updated). // - Otherwise, we are either already using standard reflection, or we are using the // TFP in the primary appdomain, and should not be caching statically. - // Dev10 bug 805134 + // Dev10 return s_cbmTdpBridge == null; } } diff --git a/mcs/class/referencesource/System.Web/UI/TemplateParser.cs b/mcs/class/referencesource/System.Web/UI/TemplateParser.cs index dea1ef4c948..13ba31d98c5 100644 --- a/mcs/class/referencesource/System.Web/UI/TemplateParser.cs +++ b/mcs/class/referencesource/System.Web/UI/TemplateParser.cs @@ -1567,7 +1567,7 @@ public abstract class TemplateParser : BaseParser, IAssemblyDependencyParser { TypeDescriptor.GetAttributes(childType)[typeof(PartialCachingAttribute)]; // If we are parsing a theme file, the controls do not have an ID, - // and we should not be adding one. (Dev10 bug 660310) + // and we should not be adding one. (Dev10 if (!(subBuilder.Parser is PageThemeParser) && cacheAttrib != null) { _id = "_ctrl_" + _controlCount.ToString(NumberFormatInfo.InvariantInfo); subBuilder.ID = _id; diff --git a/mcs/class/referencesource/System.Web/UI/TraceContext.cs b/mcs/class/referencesource/System.Web/UI/TraceContext.cs index b1c853675b1..b0354a0dcc1 100644 --- a/mcs/class/referencesource/System.Web/UI/TraceContext.cs +++ b/mcs/class/referencesource/System.Web/UI/TraceContext.cs @@ -759,7 +759,7 @@ namespace System.Web { private void InitRequest() { // Master request is assumed to be initialized first System.Web.Util.Debug.Assert(_masterRequest != null); - + DataSet requestData = _masterRequest.Clone(); // request info @@ -786,19 +786,19 @@ namespace System.Web { AddRow(requestData, SR.Trace_Request, row); // header info - int i; - String[] keys = _context.Request.Headers.AllKeys; - for (i=0; i<keys.Length; i++) { - row = NewRow(requestData, SR.Trace_Headers_Collection); - row[SR.Trace_Name] = keys[i]; - row[SR.Trace_Value] = _context.Request.Headers[keys[i]]; - AddRow(requestData, SR.Trace_Headers_Collection, row); + try { + // + + AddCollectionToRequestData(requestData, SR.Trace_Headers_Collection, _context.Request.Unvalidated.Headers); + } + catch { + // ---- exceptions when we fail to get the unvalidated collection } // response header info ArrayList headers = _context.Response.GenerateResponseHeaders(false); int n = (headers != null) ? headers.Count : 0; - for (i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { HttpResponseHeader h = (HttpResponseHeader)headers[i]; row = NewRow(requestData, SR.Trace_Response_Headers_Collection); row[SR.Trace_Name] = h.Name; @@ -807,34 +807,26 @@ namespace System.Web { } //form info - keys = _context.Request.Form.AllKeys; - for (i=0; i<keys.Length; i++) { - row = NewRow(requestData, SR.Trace_Form_Collection); - row[SR.Trace_Name] = keys[i]; - row[SR.Trace_Value] = _context.Request.Form[keys[i]]; - AddRow(requestData, SR.Trace_Form_Collection, row); + try { + AddCollectionToRequestData(requestData, SR.Trace_Form_Collection, _context.Request.Unvalidated.Form); + } + catch { + // ---- exceptions when we fail to get the unvalidated collection } //QueryString info - keys = _context.Request.QueryString.AllKeys; - for (i=0; i<keys.Length; i++) { - row = NewRow(requestData, SR.Trace_Querystring_Collection); - row[SR.Trace_Name] = keys[i]; - row[SR.Trace_Value] = _context.Request.QueryString[keys[i]]; - AddRow(requestData, SR.Trace_Querystring_Collection, row); + try { + AddCollectionToRequestData(requestData, SR.Trace_Querystring_Collection, _context.Request.Unvalidated.QueryString); + } + catch { + // ---- exceptions when we fail to get the unvalidated collection } //Server Variable info if (HttpRuntime.HasAppPathDiscoveryPermission()) { - keys = _context.Request.ServerVariables.AllKeys; - for (i=0; i<keys.Length; i++) { - row = NewRow(requestData, SR.Trace_Server_Variables); - row[SR.Trace_Name] = keys[i]; - row[SR.Trace_Value] = _context.Request.ServerVariables.Get(keys[i]); - AddRow(requestData, SR.Trace_Server_Variables, row); - } + AddCollectionToRequestData(requestData, SR.Trace_Server_Variables, _context.Request.ServerVariables); } - + _requestData = requestData; if (HttpRuntime.UseIntegratedPipeline) { @@ -847,6 +839,18 @@ namespace System.Web { _context.Request.InsertEntityBody(); } } + + private void AddCollectionToRequestData(DataSet requestData, string traceCollectionTitle, NameValueCollection collection) { + if (null != collection) { + var keys = collection.AllKeys; + for (int i = 0; i < keys.Length; i++) { + var row = NewRow(requestData, traceCollectionTitle); + row[SR.Trace_Name] = keys[i]; + row[SR.Trace_Value] = collection[keys[i]]; + AddRow(requestData, traceCollectionTitle, row); + } + } + } } } diff --git a/mcs/class/referencesource/System.Web/UI/Util.cs b/mcs/class/referencesource/System.Web/UI/Util.cs index 66a3bacdeb4..e6461ae8b16 100644 --- a/mcs/class/referencesource/System.Web/UI/Util.cs +++ b/mcs/class/referencesource/System.Web/UI/Util.cs @@ -329,7 +329,7 @@ internal static class Util { string assembly = "system_web"; // QFE number is not included in client path - string version = VersionInfo.EngineVersion.Substring(0, VersionInfo.EngineVersion.LastIndexOf('.')).Replace('.', '_'); + string version = VersionInfo.SystemWebVersion.Substring(0, VersionInfo.SystemWebVersion.LastIndexOf('.')).Replace('.', '_'); location = String.Format(CultureInfo.InvariantCulture, location, assembly, version); } diff --git a/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/MenuAdapter.cs b/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/MenuAdapter.cs index 83c46ef3715..2fc4fce2058 100644 --- a/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/MenuAdapter.cs +++ b/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/MenuAdapter.cs @@ -44,8 +44,8 @@ namespace System.Web.UI.WebControls.Adapters { private string Escape(string path) { // This function escapes \\ so that they don't get replaced because of - // a Netscape 4 bug. Other escapable characters will be escaped by . - // _ becomes __ and \\ becomes \_\ + // a Netscape 4 + StringBuilder b = null; if (String.IsNullOrEmpty(path)) { diff --git a/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/WmlBulletedListAdapter.cs b/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/WmlBulletedListAdapter.cs index c30735b2563..711bedb6551 100644 --- a/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/WmlBulletedListAdapter.cs +++ b/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/WmlBulletedListAdapter.cs @@ -32,7 +32,7 @@ namespace System.Web.UI.WebControls.Adapters { writer.WriteBreak(); break; case BulletedListDisplayMode.HyperLink: - // + // TODO: if index == 0, set accesskey. Needs a new RenderBeginHyperlink method. string targetURL = Control.ResolveClientUrl(items[index].Value); if (items[index].Enabled) { PageAdapter.RenderBeginHyperlink(writer, targetURL, true /* encode */, items[index].Text); @@ -45,7 +45,7 @@ namespace System.Web.UI.WebControls.Adapters { break; case BulletedListDisplayMode.LinkButton: if (items[index].Enabled) { - // + // TODO: if index == 0, set accesskey. Needs a new RenderPostBackEvent method. PageAdapter.RenderPostBackEvent(writer, Control.UniqueID, index.ToString(CultureInfo.InvariantCulture), items[index].Text, items[index].Text); } else { diff --git a/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/WmlDataBoundLiteralControlAdapter.cs b/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/WmlDataBoundLiteralControlAdapter.cs index f1f5c99a3ca..4a4fd4232b7 100644 --- a/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/WmlDataBoundLiteralControlAdapter.cs +++ b/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/WmlDataBoundLiteralControlAdapter.cs @@ -21,8 +21,8 @@ namespace System.Web.UI.Adapters { protected internal override void EndRender(HtmlTextWriter writer) { } - // - + // BUGBUG: This override is for compatibility with MMIT. + // MMIT legacy pages also use this adapter -UNDONE: Review once MMIT legacy plan is complete. protected internal override void Render(HtmlTextWriter writer) { WmlTextWriter wmlWriter = writer as WmlTextWriter; if (wmlWriter == null) { diff --git a/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/WmlHyperLinkAdapter.cs b/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/WmlHyperLinkAdapter.cs index 9c919c17b52..b80aa4f9de2 100644 --- a/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/WmlHyperLinkAdapter.cs +++ b/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/WmlHyperLinkAdapter.cs @@ -20,8 +20,8 @@ namespace System.Web.UI.WebControls.Adapters { String text = Control.Text; if (text.Length == 0) { - // Whidbey 18195 - + // Whidbey 18195 UNDONE: This solution is somewhat ad hoc, awaiting spec resolution on IStaticTextControl + // in M2. For now, take text from first IStaticTextControl or DataboundLiteralControl. foreach(Control child in Control.Controls) { if (child is IStaticTextControl) { text = ((IStaticTextControl)child).Text; diff --git a/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/WmlImageButtonAdapter.cs b/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/WmlImageButtonAdapter.cs index 31d463449da..f98b86743e9 100644 --- a/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/WmlImageButtonAdapter.cs +++ b/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/WmlImageButtonAdapter.cs @@ -10,9 +10,9 @@ namespace System.Web.UI.WebControls.Adapters { using System.Web.UI.Adapters; using System.Web.UI.WebControls; - // - - + // REVIEW: Inheritance. If this inherits from ImageButtonAdapter, there is no way to create a + // WmlImageAdapter and set the Control property to delegate rendering (base.Render, below). Control is read-only. + // Maybe Control should be get/set for this situation. public class WmlImageButtonAdapter : WmlImageAdapter { protected new ImageButton Control { @@ -30,7 +30,7 @@ namespace System.Web.UI.WebControls.Adapters { postUrl = Control.ResolveClientUrl (Control.PostBackUrl); } - // + // UNDONE: Replace hard coded string indexer with strongly typed capability. if (Page != null && Page.Request != null && (String)Page.Request.Browser["supportsImageSubmit"] == "false") { writer.EnterStyle(Control.ControlStyle); diff --git a/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/WmlLiteralAdapter.cs b/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/WmlLiteralAdapter.cs index f5f05650e59..01845963870 100644 --- a/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/WmlLiteralAdapter.cs +++ b/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/WmlLiteralAdapter.cs @@ -21,7 +21,7 @@ namespace System.Web.UI.WebControls.Adapters { protected internal override void EndRender(HtmlTextWriter writer) { } - // + // BUGBUG: This override is for compatibility with MMIT only. -UNDONE: Review once MMIT legacy plan is complete. protected internal override void Render(HtmlTextWriter writer) { WmlTextWriter wmlWriter = writer as WmlTextWriter; if (wmlWriter == null) { diff --git a/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/WmlLiteralControlAdapter.cs b/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/WmlLiteralControlAdapter.cs index 7cf14fc7538..052a41fe32a 100644 --- a/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/WmlLiteralControlAdapter.cs +++ b/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/WmlLiteralControlAdapter.cs @@ -20,8 +20,8 @@ namespace System.Web.UI.Adapters { protected internal override void EndRender(HtmlTextWriter writer) { } - // - + // BUGBUG: This override is for compatibility with MMIT. + // MMIT legacy pages also use this adapter -UNDONE: Review once MMIT legacy plan is complete. protected internal override void Render(HtmlTextWriter writer) { WmlTextWriter wmlWriter = writer as WmlTextWriter; if (wmlWriter == null) { diff --git a/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/WmlPageAdapter.cs b/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/WmlPageAdapter.cs index 87fcb974b51..08636d4e370 100644 --- a/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/WmlPageAdapter.cs +++ b/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/WmlPageAdapter.cs @@ -67,8 +67,8 @@ namespace System.Web.UI.Adapters { } } - // - + // UNDONE: Internal because needed by WmlTextWriter. Consider removing this prop somehow. + // Returns true if form variables have been written. internal bool WrittenFormVariables { get { return _writtenFormVariables; @@ -131,8 +131,8 @@ namespace System.Web.UI.Adapters { return collection; } - // - + // UNDONE: For M1, we only have Wml browsers which do not support accesskey. For later milestones, make this + // dependent on a capability or replace with a capability. private bool DoesBrowserSupportAccessKey() { return false; } @@ -235,16 +235,16 @@ namespace System.Web.UI.Adapters { } // Renders the beginning of the form. - // + // UNDONE: Remove internal modifier when method is completely removed from writer. protected internal virtual void RenderBeginForm(WmlTextWriter writer) { RenderBeginCardTag(writer); // Write form variables. - // - - + // UNDONE: Move writer._provideBackButton to this adapter. + // Review: In V1 we had a writer.ProvideBackButton property, is there any need for this with (more advanced) + // whidbey devices? _writtenFormVariables = true; if (_formVariables == null) { _formVariables = new ListDictionary(); @@ -259,7 +259,7 @@ namespace System.Web.UI.Adapters { RenderSetFormVariables(writer); RenderPostUrlFormVariable(writer); writer.WriteLine("</refresh></onevent>"); - // + // UNDONE: formAdapter.RenderExtraCardElements(this); writer.BeginFormOrPanel(); } @@ -432,7 +432,7 @@ namespace System.Web.UI.Adapters { RenderTargetAndArgumentPostFields(writer, target, argument, postFieldType); RenderPostFieldVariableDictionary(writer, _dynamicPostFields); RenderPostFieldDictionary(writer, _staticPostFields); - // + // UNDONE: Add postbacks for variables which are not on the current page. writer.WriteEndTag("go"); } @@ -442,8 +442,8 @@ namespace System.Web.UI.Adapters { return; } writer.Write("?"); - // - + // UNDONE: MMIT IPageAdapter.PersistCookielessData NYI + // if(Page.Adapter.PersistCookielessData && Browser["canRenderOneventAndPrevElementsTogether"] != "false") if (!StringUtil.EqualsIgnoreCase((string)Browser["canRenderOneventAndPrevElementsTogether"], "false")) { queryString = writer.ReplaceFormsCookieWithVariable(queryString); } @@ -504,7 +504,7 @@ namespace System.Web.UI.Adapters { writer.Write("<onevent type=\"onenterforward\">"); RenderFormPostInGoAction(writer, null, _postBackEventArgumentVarName, WmlPostFieldType.Variable, String.Empty); - // + // REVIEW: Should we always include page hidden variables. writer.WriteLine("</onevent>"); writer.WriteLine("<onevent type=\"onenterbackward\"><prev /></onevent>"); diff --git a/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/WmlPhoneLinkAdapter.cs b/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/WmlPhoneLinkAdapter.cs index c4dfaa39a26..efccabe639b 100644 --- a/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/WmlPhoneLinkAdapter.cs +++ b/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/WmlPhoneLinkAdapter.cs @@ -11,7 +11,7 @@ namespace System.Web.UI.WebControls.Adapters { public class WmlPhoneLinkAdapter : PhoneLinkAdapter { - // + // UNDONE: Add style. protected internal override void Render(HtmlTextWriter markupWriter) { WmlTextWriter writer = (WmlTextWriter)markupWriter; String text, url, phoneNumber; @@ -42,7 +42,7 @@ namespace System.Web.UI.WebControls.Adapters { if (Page != null && Page.Request != null) { browser = Page.Request.Browser; } - // + // TODO: Replace hard coded string key. if (browser != null && (String)browser["canInitiateVoiceCall"] != "true") { text = String.Format(controlText, originalNumber); @@ -54,7 +54,7 @@ namespace System.Web.UI.WebControls.Adapters { // showing as text so it can be selected. If it is not // formatted in the text yet, append it to the end of the // text. - // + // TODO: Replace hard coded string key. if (browser != null && browser["requiresPhoneNumbersAsPlainText"] == "true") { text = controlText + " " + phoneNumber; url = String.Empty; diff --git a/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/WmlRadioButtonAdapter.cs b/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/WmlRadioButtonAdapter.cs index 397b021374f..7923294a176 100644 --- a/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/WmlRadioButtonAdapter.cs +++ b/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/WmlRadioButtonAdapter.cs @@ -141,14 +141,14 @@ namespace System.Web.UI.WebControls.Adapters { // RenderAsGroup returns a RadioButtonGroup object if the group should be // rendered in a single <select> statement, or null if autopostback should // be enabled. - // + // UNDONE: Check more general case when radiobuttons do not all share the same parent private RadioButtonGroup RenderAsGroup(RadioButton r) { bool startedSequence = false; bool finishedSequence = false; RadioButtonGroup group = new RadioButtonGroup(); - // + //TODO : Check all controls on page foreach (Control c in r.Parent.Controls) { RadioButton radioSibling = c as RadioButton; LiteralControl literalSibling = c as LiteralControl; diff --git a/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/WmlTextBoxAdapter.cs b/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/WmlTextBoxAdapter.cs index a3875b40a56..51124c6afd0 100644 --- a/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/WmlTextBoxAdapter.cs +++ b/mcs/class/referencesource/System.Web/UI/WebControls/Adapters/WmlTextBoxAdapter.cs @@ -33,7 +33,7 @@ namespace System.Web.UI.WebControls.Adapters { if (Control.TextMode == TextBoxMode.Password) { value = String.Empty; - // + // UNDONE: Consider adding property or wml adapter property to set requiresRandomID to true. requiresRandomID = true; } @@ -53,8 +53,8 @@ namespace System.Web.UI.WebControls.Adapters { writer.MapClientIDToShortName(Control.ClientID, requiresRandomID); } - // - + // UNDONE: There are some wml-specific properties (format, title, size, maxLength) which + // we should consider for Whidbey. RenderTextBox((WmlTextWriter)writer, Control.ClientID, value, null /* format */, @@ -69,7 +69,7 @@ namespace System.Web.UI.WebControls.Adapters { // Renders the TextBox. public virtual void RenderTextBox(WmlTextWriter writer, String id, String value, String format, String title, bool password, int size, int maxLength, bool generateRandomID) { if (!writer.AnalyzeMode) { - // + // UNDONE: Handle rendersBreakBeforeWmlSelectAndInput, if this capability is still needed for Whidbey devices // VSWhidbey 147458. Close any style tags. writer.CloseCurrentStyleTags(); diff --git a/mcs/class/referencesource/System.Web/UI/WebControls/BaseDataBoundControl.cs b/mcs/class/referencesource/System.Web/UI/WebControls/BaseDataBoundControl.cs index ef6fda2fd99..ef21e2f0ca6 100644 --- a/mcs/class/referencesource/System.Web/UI/WebControls/BaseDataBoundControl.cs +++ b/mcs/class/referencesource/System.Web/UI/WebControls/BaseDataBoundControl.cs @@ -223,6 +223,10 @@ namespace System.Web.UI.WebControls { } } + internal void InternalEnsureDataBound() { + EnsureDataBound(); + } + protected virtual void OnDataBound(EventArgs e) { EventHandler handler = Events[EventDataBound] as EventHandler; diff --git a/mcs/class/referencesource/System.Web/UI/WebControls/ChangePassword.cs b/mcs/class/referencesource/System.Web/UI/WebControls/ChangePassword.cs index 4588444664e..cd906e11df0 100644 --- a/mcs/class/referencesource/System.Web/UI/WebControls/ChangePassword.cs +++ b/mcs/class/referencesource/System.Web/UI/WebControls/ChangePassword.cs @@ -1939,9 +1939,9 @@ namespace System.Web.UI.WebControls { string cancelPageUrl = CancelDestinationPageUrl; if (!String.IsNullOrEmpty(cancelPageUrl)) { - // [....] suggested that we should not terminate execution of current page, to give + // Microsoft suggested that we should not terminate execution of current page, to give // page a chance to cleanup its resources. This may be less performant though. - // [....] suggested that we need to call ResolveClientUrl before redirecting. + // Microsoft suggested that we need to call ResolveClientUrl before redirecting. // Example is this control inside user control, want redirect relative to user control dir. Page.Response.Redirect(ResolveClientUrl(cancelPageUrl), false); } @@ -1993,9 +1993,9 @@ namespace System.Web.UI.WebControls { string continuePageUrl = ContinueDestinationPageUrl; if (!String.IsNullOrEmpty(continuePageUrl)) { - // [....] suggested that we should not terminate execution of current page, to give + // Microsoft suggested that we should not terminate execution of current page, to give // page a chance to cleanup its resources. This may be less performant though. - // [....] suggested that we need to call ResolveClientUrl before redirecting. + // Microsoft suggested that we need to call ResolveClientUrl before redirecting. // Example is this control inside user control, want redirect relative to user control dir. Page.Response.Redirect(ResolveClientUrl(continuePageUrl), false); } @@ -2066,9 +2066,9 @@ namespace System.Web.UI.WebControls { string successPageUrl = SuccessPageUrl; if (!String.IsNullOrEmpty(successPageUrl)) { - // [....] suggested that we should not terminate execution of current page, to give + // Microsoft suggested that we should not terminate execution of current page, to give // page a chance to cleanup its resources. This may be less performant though. - // [....] suggested that we need to call ResolveClientUrl before redirecting. + // Microsoft suggested that we need to call ResolveClientUrl before redirecting. // Example is this control inside user control, want redirect relative to user control dir. Page.Response.Redirect(ResolveClientUrl(successPageUrl), false); } diff --git a/mcs/class/referencesource/System.Web/UI/WebControls/CheckBoxList.cs b/mcs/class/referencesource/System.Web/UI/WebControls/CheckBoxList.cs index d01dc81dc0c..24763e8b166 100644 --- a/mcs/class/referencesource/System.Web/UI/WebControls/CheckBoxList.cs +++ b/mcs/class/referencesource/System.Web/UI/WebControls/CheckBoxList.cs @@ -361,7 +361,7 @@ namespace System.Web.UI.WebControls { // strIndex is now definitely the index as a string, regardless of which case postDataKey was in. int index = Int32.Parse(strIndex, CultureInfo.InvariantCulture); - EnsureDataBound(); + EnsureDataBoundInLoadPostData(); // Maintain state from the form if (index >= 0 && index < Items.Count) { diff --git a/mcs/class/referencesource/System.Web/UI/WebControls/DataBoundControl.cs b/mcs/class/referencesource/System.Web/UI/WebControls/DataBoundControl.cs index 0a433fda772..39801f9c17e 100644 --- a/mcs/class/referencesource/System.Web/UI/WebControls/DataBoundControl.cs +++ b/mcs/class/referencesource/System.Web/UI/WebControls/DataBoundControl.cs @@ -445,11 +445,14 @@ namespace System.Web.UI.WebControls { OnDataBinding(EventArgs.Empty); } DataSourceView view = GetData(); - _arguments = CreateDataSourceSelectArguments(); _ignoreDataSourceViewChanged = true; RequiresDataBinding = false; MarkAsDataBound(); - view.Select(_arguments, OnDataSourceViewSelectCallback); + + // when PerformSelect is called in async method, setting _arguments + // to a new instance causes an exception deep in ListView. + // Instead, we should use SelectArguments. + view.Select(SelectArguments, OnDataSourceViewSelectCallback); } @@ -490,10 +493,10 @@ namespace System.Web.UI.WebControls { /// Saves view state. /// </devdoc> protected override object SaveViewState() { - // Bug 322689: In the web farms scenario, if a web site is hosted in 4.0 and 4.5 servers - // (though this is not a really supported scenario, we are fixing this instance), - // the View state created by 4.0 should be able to be understood by 4.5 controls. - // So, we create a Pair only if we are using model binding and otherwise fallback to 4.0 behavior. + // + + + object baseViewState = base.SaveViewState(); diff --git a/mcs/class/referencesource/System.Web/UI/WebControls/DataGridItem.cs b/mcs/class/referencesource/System.Web/UI/WebControls/DataGridItem.cs index 9f399bb9652..480db6455aa 100644 --- a/mcs/class/referencesource/System.Web/UI/WebControls/DataGridItem.cs +++ b/mcs/class/referencesource/System.Web/UI/WebControls/DataGridItem.cs @@ -71,7 +71,6 @@ namespace System.Web.UI.WebControls { /// <para>Indicates the type of the item in the <see cref='System.Web.UI.WebControls.DataGrid'/>.</para> /// </devdoc> public virtual ListItemType ItemType { - [System.Runtime.TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] get { return itemType; } diff --git a/mcs/class/referencesource/System.Web/UI/WebControls/DetailsView.cs b/mcs/class/referencesource/System.Web/UI/WebControls/DetailsView.cs index c51c96272d6..268143d0896 100644 --- a/mcs/class/referencesource/System.Web/UI/WebControls/DetailsView.cs +++ b/mcs/class/referencesource/System.Web/UI/WebControls/DetailsView.cs @@ -2867,7 +2867,7 @@ namespace System.Web.UI.WebControls { propertyChanged = true; // since we can't go into insert mode in a callback, oldPageIndex should never be -1 and different from PageIndex - Debug.Assert(oldPageIndex >= 0, "Page indeces are out of [....] from callback hidden field state"); + Debug.Assert(oldPageIndex >= 0, "Page indeces are out of sync from callback hidden field state"); _pageIndex = oldPageIndex; string oldDataKeyString = dataKey; diff --git a/mcs/class/referencesource/System.Web/UI/WebControls/DropDownList.cs b/mcs/class/referencesource/System.Web/UI/WebControls/DropDownList.cs index f1ec34e88b6..214af08d61f 100644 --- a/mcs/class/referencesource/System.Web/UI/WebControls/DropDownList.cs +++ b/mcs/class/referencesource/System.Web/UI/WebControls/DropDownList.cs @@ -154,7 +154,7 @@ namespace System.Web.UI.WebControls { string [] selectedItems = postCollection.GetValues(postDataKey); - EnsureDataBound(); + EnsureDataBoundInLoadPostData(); if (selectedItems != null) { ValidateEvent(postDataKey, selectedItems[0]); diff --git a/mcs/class/referencesource/System.Web/UI/WebControls/GridView.cs b/mcs/class/referencesource/System.Web/UI/WebControls/GridView.cs index 3d78a8ec912..722176466a9 100644 --- a/mcs/class/referencesource/System.Web/UI/WebControls/GridView.cs +++ b/mcs/class/referencesource/System.Web/UI/WebControls/GridView.cs @@ -2279,7 +2279,15 @@ namespace System.Web.UI.WebControls { if (pagedDataSourceEnumerator != null) { if (pagedDataSource.IsPagingEnabled) { _pageCount = pagedDataSource.PageCount; - createdRowsCount = pagedDataSource.DataSourceCount; + if (pagedDataSource.IsCustomPagingEnabled) { + // DevDiv 782891: We didn't well handle GridView paging in the scenario + // in which custom paging was enabled. The root cause was that we mixed up + // createdRowsCount with pagedDataSource.DataSourceCount. + createdRowsCount = count; + } + else { + createdRowsCount = pagedDataSource.DataSourceCount; + } } else { _pageCount = 1; @@ -2896,7 +2904,19 @@ namespace System.Web.UI.WebControls { if (affectedRows > 0) { // Patch up the current page. We might have deleted the last records on the last page, so move // to the right page. - int rowCount = (int)ViewState[ItemCountViewStateKey]; + // DevDiv 782891: We didn't handle GridView paging well in the scenario + // in which custom paging was enabled. The root cause was that we mixed up + // createdRowsCount with pagedDataSource.DataSourceCount. + int rowCount; + if (this.AllowPaging && this.AllowCustomPaging) { + // Under this condition, the value of ViewState[ItemCountViewStateKey] indicates + // created rows count other than total row count. + rowCount = (int)VirtualItemCount; + } + else { + rowCount = (int)ViewState[ItemCountViewStateKey]; + } + // Can't have negative rowCount int expectedRowCount = Math.Max(0, rowCount - affectedRows); diff --git a/mcs/class/referencesource/System.Web/UI/WebControls/ICompositeControlDesignerAccessor.cs b/mcs/class/referencesource/System.Web/UI/WebControls/ICompositeControlDesignerAccessor.cs index f2467f3d366..cfb9515b423 100644 --- a/mcs/class/referencesource/System.Web/UI/WebControls/ICompositeControlDesignerAccessor.cs +++ b/mcs/class/referencesource/System.Web/UI/WebControls/ICompositeControlDesignerAccessor.cs @@ -6,7 +6,7 @@ namespace System.Web.UI.WebControls { - // + // TODO, nikhilko: Change namespace to System.Web.UI /// <devdoc> diff --git a/mcs/class/referencesource/System.Web/UI/WebControls/IFieldControl.cs b/mcs/class/referencesource/System.Web/UI/WebControls/IFieldControl.cs index 2a7f357e5c9..0dacfe9fdad 100644 --- a/mcs/class/referencesource/System.Web/UI/WebControls/IFieldControl.cs +++ b/mcs/class/referencesource/System.Web/UI/WebControls/IFieldControl.cs @@ -8,4 +8,4 @@ set; } } -} +}
\ No newline at end of file diff --git a/mcs/class/referencesource/System.Web/UI/WebControls/ListControl.cs b/mcs/class/referencesource/System.Web/UI/WebControls/ListControl.cs index ee8eb18a219..012b960ae30 100644 --- a/mcs/class/referencesource/System.Web/UI/WebControls/ListControl.cs +++ b/mcs/class/referencesource/System.Web/UI/WebControls/ListControl.cs @@ -39,6 +39,7 @@ namespace System.Web.UI.WebControls { private string cachedSelectedValue; private ArrayList cachedSelectedIndices; private bool _stateLoaded; + private bool _asyncSelectPending; /// <devdoc> @@ -582,10 +583,48 @@ namespace System.Web.UI.WebControls { _stateLoaded = true; } + + private void OnDataSourceViewSelectCallback(IEnumerable data) { + _asyncSelectPending = false; + PerformDataBinding(data); + PostPerformDataBindingAction(); + } + protected override void OnDataBinding(EventArgs e) { base.OnDataBinding(e); - IEnumerable data = GetData().ExecuteSelect(DataSourceSelectArguments.Empty); - PerformDataBinding(data); + DataSourceView view = GetData(); + + // view could be null when a user implements his own GetData(). + if (null == view) { + throw new InvalidOperationException(SR.GetString(SR.DataControl_ViewNotFound, ID)); + } + + // DevDiv 1036362: enable async model binding for ListControl + bool useAsyncSelect = false; + if (AppSettings.EnableAsyncModelBinding) { + var modelDataView = view as ModelDataSourceView; + useAsyncSelect = modelDataView != null && modelDataView.IsSelectMethodAsync; + } + + if (useAsyncSelect) { + _asyncSelectPending = true; // disable post data binding action until the callback is invoked + view.Select(SelectArguments, OnDataSourceViewSelectCallback); + } + else { + IEnumerable data = view.ExecuteSelect(DataSourceSelectArguments.Empty); + PerformDataBinding(data); + } + } + + internal void EnsureDataBoundInLoadPostData() { + if (!SkipEnsureDataBoundInLoadPostData) { + EnsureDataBound(); + } + } + + internal bool SkipEnsureDataBoundInLoadPostData { + get; + set; } @@ -715,6 +754,13 @@ namespace System.Web.UI.WebControls { // performed the databind, and we need to maintain backward compat. OnDataBinding will retrieve the // data from the view synchronously and call PerformDataBinding with the data, preserving the OM. OnDataBinding(EventArgs.Empty); + PostPerformDataBindingAction(); + } + + private void PostPerformDataBindingAction() { + if (_asyncSelectPending) + return; + RequiresDataBinding = false; MarkAsDataBound(); OnDataBound(EventArgs.Empty); diff --git a/mcs/class/referencesource/System.Web/UI/WebControls/Listbox.cs b/mcs/class/referencesource/System.Web/UI/WebControls/Listbox.cs index aadb02d1f5b..2ce71922ea3 100644 --- a/mcs/class/referencesource/System.Web/UI/WebControls/Listbox.cs +++ b/mcs/class/referencesource/System.Web/UI/WebControls/Listbox.cs @@ -191,7 +191,7 @@ namespace System.Web.UI.WebControls { string[] selectedItems = postCollection.GetValues(postDataKey); bool selectionChanged = false; - EnsureDataBound(); + EnsureDataBoundInLoadPostData(); if (selectedItems != null) { if (SelectionMode == ListSelectionMode.Single) { diff --git a/mcs/class/referencesource/System.Web/UI/WebControls/LoginView.cs b/mcs/class/referencesource/System.Web/UI/WebControls/LoginView.cs index 3ede63ea959..820d536afec 100644 --- a/mcs/class/referencesource/System.Web/UI/WebControls/LoginView.cs +++ b/mcs/class/referencesource/System.Web/UI/WebControls/LoginView.cs @@ -350,7 +350,7 @@ namespace System.Web.UI.WebControls { // Note: we always recreate the child controls in the designer to correctly handle the case of // the currently selected role group being deleted. This is necessary because the // setter for TemplateIndex won't recreate the controls if the TemplateIndex is unchanged, - // which is the case when deleting all but the last role group. [Fix for Bug 148406] + // which is the case when deleting all but the last role group. [Fix for ChildControlsCreated = false; } } diff --git a/mcs/class/referencesource/System.Web/UI/WebControls/MenuItem.cs b/mcs/class/referencesource/System.Web/UI/WebControls/MenuItem.cs index f78c6b43c0c..0eeaa9fc94a 100644 --- a/mcs/class/referencesource/System.Web/UI/WebControls/MenuItem.cs +++ b/mcs/class/referencesource/System.Web/UI/WebControls/MenuItem.cs @@ -966,7 +966,7 @@ namespace System.Web.UI.WebControls { // In 4.0, the default value of Menu.StaticSubMenuIndent was changed from 16px to Unit.Empty, // since the table and list rendering modes need to have different effective default values. // To maintain back compat, the effective default value for table rendering is 16px. - // Dev10 Bug 741543 + // Dev10 if (indent.IsEmpty) { indent = Unit.Pixel(16); } diff --git a/mcs/class/referencesource/System.Web/UI/WebControls/MenuRenderingMode.cs b/mcs/class/referencesource/System.Web/UI/WebControls/MenuRenderingMode.cs index ed11ee68d01..f6c81bd7a86 100644 --- a/mcs/class/referencesource/System.Web/UI/WebControls/MenuRenderingMode.cs +++ b/mcs/class/referencesource/System.Web/UI/WebControls/MenuRenderingMode.cs @@ -13,4 +13,4 @@ namespace System.Web.UI.WebControls { Table, List, } -} +}
\ No newline at end of file diff --git a/mcs/class/referencesource/System.Web/UI/WebControls/ModelDataSourceView.cs b/mcs/class/referencesource/System.Web/UI/WebControls/ModelDataSourceView.cs index 6c028feb191..8f762058eb2 100644 --- a/mcs/class/referencesource/System.Web/UI/WebControls/ModelDataSourceView.cs +++ b/mcs/class/referencesource/System.Web/UI/WebControls/ModelDataSourceView.cs @@ -14,6 +14,9 @@ namespace System.Web.UI.WebControls { using System.Linq; using System.Linq.Expressions; using System.Reflection; + using System.Runtime.CompilerServices; + using System.Threading; + using System.Threading.Tasks; using System.Web.Compilation; using System.Web.ModelBinding; using System.Web.UI; @@ -41,6 +44,8 @@ namespace System.Web.UI.WebControls { private string _updateMethod; private string _dataKeyName; + private Task _viewOperationTask = null; + private const string TotalRowCountParameterName = "totalRowCount"; private const string MaximumRowsParameterName = "maximumRows"; private const string StartRowIndexParameterName = "startRowIndex"; @@ -236,7 +241,7 @@ namespace System.Web.UI.WebControls { EvaluateSelectParameters(); } - private static bool IsAutoPagingRequired(MethodInfo selectMethod, bool isReturningQueryable) { + private static bool IsAutoPagingRequired(MethodInfo selectMethod, bool isReturningQueryable, bool isAsyncSelect) { bool maximumRowsFound = false; bool totalRowCountFound = false; @@ -264,10 +269,22 @@ namespace System.Web.UI.WebControls { } } - bool pagingParamsFound = maximumRowsFound && startRowIndexFound && totalRowCountFound; + bool pagingParamsFound; + if (isAsyncSelect) { + pagingParamsFound = maximumRowsFound && startRowIndexFound; + } + else { + pagingParamsFound = maximumRowsFound && startRowIndexFound && totalRowCountFound; + } - if (!isReturningQueryable && !pagingParamsFound) { - throw new InvalidOperationException(SR.GetString(SR.ModelDataSourceView_InvalidPagingParameters)); + bool canDoPaging = isReturningQueryable || pagingParamsFound; + if (!canDoPaging) { + if (isAsyncSelect) { + throw new InvalidOperationException(SR.GetString(SR.ModelDataSourceView_InvalidAsyncPagingParameters)); + } + else { + throw new InvalidOperationException(SR.GetString(SR.ModelDataSourceView_InvalidPagingParameters)); + } } return !pagingParamsFound; @@ -286,11 +303,40 @@ namespace System.Web.UI.WebControls { } } - if ((!isReturningQueryable && !sortExpressionFound)) { + if (!isReturningQueryable && !sortExpressionFound) { throw new InvalidOperationException(SR.GetString(SR.ModelDataSourceView_InvalidSortingParameters)); } return !sortExpressionFound; + } + + private object GetPropertyValueByName(object o, string name) { + var propInfo = o.GetType().GetProperty(name); + object value = propInfo.GetValue(o, null); + return value; + } + + private void ValidateAsyncModelBindingRequirements() { + if (!(_owner.DataControl.Page.IsAsync && SynchronizationContextUtil.CurrentMode != SynchronizationContextMode.Legacy)) { + throw new InvalidOperationException(SR.GetString(SR.ModelDataSourceView_UseAsyncMethodMustBeUsingAsyncPage)); + } + } + + private bool RequireAsyncModelBinding(string methodName, out ModelDataSourceMethod method) { + if (!AppSettings.EnableAsyncModelBinding) { + method = null; + return false; + } + + method = FindMethod(methodName); + if (null == method) { + return false; + } + + MethodInfo methodInfo = method.MethodInfo; + bool returnTypeIsTask = typeof(Task).IsAssignableFrom(methodInfo.ReturnType); + + return returnTypeIsTask; } /// <summary> @@ -322,12 +368,19 @@ namespace System.Web.UI.WebControls { /// </param> /// <returns>A <see cref="System.Web.UI.WebControls.ModelDataSourceMethod"/> with the information required to invoke the select method.</returns> protected virtual ModelDataSourceMethod EvaluateSelectMethodParameters(DataSourceSelectArguments arguments, out DataSourceSelectResultProcessingOptions selectResultProcessingOptions) { - ModelDataSourceMethod method; + return EvaluateSelectMethodParameters(arguments, null/*method*/, false /*isAsyncSelect*/, out selectResultProcessingOptions); + } + + private ModelDataSourceMethod EvaluateSelectMethodParameters(DataSourceSelectArguments arguments, ModelDataSourceMethod method, bool isAsyncSelect, out DataSourceSelectResultProcessingOptions selectResultProcessingOptions) { IOrderedDictionary mergedParameters = MergeSelectParameters(arguments); // Resolve the method - method = FindMethod(SelectMethod); + method = method ?? FindMethod(SelectMethod); Type selectMethodReturnType = method.MethodInfo.ReturnType; + if (isAsyncSelect) { + selectMethodReturnType = ExtractAsyncSelectReturnType(selectMethodReturnType); + } + Type modelType = ModelType; if (modelType == null) { //When ModelType is not specified but SelectMethod returns IQueryable<T>, we treat T as model type for auto paging and sorting. @@ -344,11 +397,25 @@ namespace System.Web.UI.WebControls { //We do auto paging or auto sorting when the select method is returning an IQueryable and does not have parameters for paging or sorting. bool isReturningQueryable = queryableModelType != null && queryableModelType.IsAssignableFrom(selectMethodReturnType); + if (isAsyncSelect && isReturningQueryable) { + // async select method does not support returning IQueryable<>. + throw new InvalidOperationException(SR.GetString(SR.ModelDataSourceView_InvalidAsyncSelectReturnType, modelType)); + } + bool autoPage = false; bool autoSort = false; if (arguments.StartRowIndex >= 0 && arguments.MaximumRows > 0) { - autoPage = IsAutoPagingRequired(method.MethodInfo, isReturningQueryable); + autoPage = IsAutoPagingRequired(method.MethodInfo, isReturningQueryable, isAsyncSelect); + + if (isAsyncSelect) { + Debug.Assert(!autoPage, "auto-paging should not be true when using async select method"); + + // custom paging is not supported if the return type is not SelectResult + if (typeof(SelectResult) != selectMethodReturnType) { + throw new InvalidOperationException(SR.GetString(SR.ModelDataSourceView_MustUseSelectResultAsReturnType)); + } + } } if (!String.IsNullOrEmpty(arguments.SortExpression)) { @@ -360,6 +427,19 @@ namespace System.Web.UI.WebControls { return method; } + private Type ExtractAsyncSelectReturnType(Type t) { + // Async select return type is expected to be Task<T>. + // This method is trying to return type T. + if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Task<>)) { + Type[] typeArguments = t.GetGenericArguments(); + if (typeArguments.Length == 1) { + return typeArguments[0]; + } + } + + throw new InvalidOperationException(SR.GetString(SR.ModelDataSourceView_InvalidAsyncSelectReturnType, ModelType)); + } + /// <summary> /// This method performs operations on the select method result like auto paging and sorting if applicable. /// </summary> @@ -384,7 +464,7 @@ namespace System.Web.UI.WebControls { MethodInfo countHelperMethod = typeof(QueryableHelpers).GetMethod("CountHelper").MakeGenericMethod(modelType); arguments.TotalRowCount = (int)countHelperMethod.Invoke(null, new object[] { result.ReturnValue }); - //Bug 180907: We would like to auto sort on DataKeyName when paging is enabled and result is not already sorted by user to overcome a limitation in EF. + // MethodInfo isOrderingMethodFoundMethod = typeof(QueryableHelpers).GetMethod("IsOrderingMethodFound").MakeGenericMethod(modelType); bool isOrderingMethodFound = (bool)isOrderingMethodFoundMethod.Invoke(null, new object[] { result.ReturnValue }); if (!isOrderingMethodFound) { @@ -445,6 +525,10 @@ namespace System.Web.UI.WebControls { /// If ItemType is set and the return value is not of proper type, throws an InvalidOperationException. /// </returns> protected virtual IEnumerable CreateSelectResult(object result) { + return CreateSelectResult(result, false/*isAsyncSelect*/); + } + + private IEnumerable CreateSelectResult(object result, bool isAsyncSelect) { if (result == null) { return null; } @@ -463,8 +547,36 @@ namespace System.Web.UI.WebControls { } else { //Sorry only the above return types are allowed!!! - throw new InvalidOperationException(SR.GetString(SR.ModelDataSourceView_InvalidSelectReturnType, modelType)); + if (isAsyncSelect) { + throw new InvalidOperationException(SR.GetString(SR.ModelDataSourceView_InvalidAsyncSelectReturnType, modelType)); + } + else { + throw new InvalidOperationException(SR.GetString(SR.ModelDataSourceView_InvalidSelectReturnType, modelType)); + } + } + } + } + + private static bool IsCancellationRequired(MethodInfo method, out string parameterName) { + parameterName = null; + bool cancellationTokenFound = false; + var lastParameter = method.GetParameters().LastOrDefault(); + if (lastParameter != null && lastParameter.ParameterType == typeof(CancellationToken)) { + cancellationTokenFound = true; + parameterName = lastParameter.Name; + } + + return cancellationTokenFound; + } + + private void SetCancellationTokenIfRequired(ModelDataSourceMethod method, bool isAsyncMethod, CancellationToken? cancellationToken) { + string cancellationTokenParameterName; + if (isAsyncMethod && IsCancellationRequired(method.MethodInfo, out cancellationTokenParameterName)) { + if (null == cancellationToken) { + throw new InvalidOperationException(SR.GetString(SR.ModelDataSourceView_CancellationTokenIsNotSupported)); } + + method.Parameters[cancellationTokenParameterName] = cancellationToken; } } @@ -472,13 +584,22 @@ namespace System.Web.UI.WebControls { /// Invokes the Delete method and gets the result. /// </summary> protected virtual object GetDeleteMethodResult(IDictionary keys, IDictionary oldValues) { - ModelDataSourceMethod method = EvaluateDeleteMethodParameters(keys, oldValues); - ModelDataMethodResult result = InvokeMethod(method); + return GetDeleteMethodResult(keys, oldValues, null/*method*/, false/*isAsyncMethod*/, null/*cancellationToken*/); + } + + private object GetDeleteMethodResult(IDictionary keys, IDictionary oldValues, ModelDataSourceMethod method, bool isAsyncMethod, CancellationToken? cancellationToken) { + method = method == null ? EvaluateDeleteMethodParameters(keys, oldValues) : EvaluateDeleteMethodParameters(keys, oldValues, method); + SetCancellationTokenIfRequired(method, isAsyncMethod, cancellationToken); + ModelDataMethodResult result = InvokeMethod(method, isAsyncMethod); return result.ReturnValue; } protected virtual ModelDataSourceMethod EvaluateDeleteMethodParameters(IDictionary keys, IDictionary oldValues) { + return EvaluateDeleteMethodParameters(keys, oldValues, null/*method*/); + } + + private ModelDataSourceMethod EvaluateDeleteMethodParameters(IDictionary keys, IDictionary oldValues, ModelDataSourceMethod method) { if (!CanDelete) { throw new NotSupportedException(SR.GetString(SR.ModelDataSourceView_DeleteNotSupported)); } @@ -488,7 +609,7 @@ namespace System.Web.UI.WebControls { MergeDictionaries(keys, caseInsensitiveOldValues); MergeDictionaries(oldValues, caseInsensitiveOldValues); - ModelDataSourceMethod method = FindMethod(DeleteMethod); + method = method ?? FindMethod(DeleteMethod); EvaluateMethodParameters(DataSourceOperation.Delete, method, caseInsensitiveOldValues); return method; } @@ -497,13 +618,22 @@ namespace System.Web.UI.WebControls { /// Invokes the Insert method and gets the result. /// </summary> protected virtual object GetInsertMethodResult(IDictionary values) { - ModelDataSourceMethod method = EvaluateInsertMethodParameters(values); - ModelDataMethodResult result = InvokeMethod(method); + return GetInsertMethodResult(values, null/*method*/, false/*isAsyncMethod&*/, null/*cancellationToken*/); + } + + private object GetInsertMethodResult(IDictionary values, ModelDataSourceMethod method, bool isAsyncMethod, CancellationToken? cancellationToken) { + method = method == null ? EvaluateInsertMethodParameters(values) : EvaluateInsertMethodParameters(values, method); + SetCancellationTokenIfRequired(method, isAsyncMethod, cancellationToken); + ModelDataMethodResult result = InvokeMethod(method, isAsyncMethod); return result.ReturnValue; } protected virtual ModelDataSourceMethod EvaluateInsertMethodParameters(IDictionary values) { + return EvaluateInsertMethodParameters(values, null/*method*/); + } + + private ModelDataSourceMethod EvaluateInsertMethodParameters(IDictionary values, ModelDataSourceMethod method) { if (!CanInsert) { throw new NotSupportedException(SR.GetString(SR.ModelDataSourceView_InsertNotSupported)); } @@ -512,7 +642,7 @@ namespace System.Web.UI.WebControls { MergeDictionaries(values, caseInsensitiveNewValues); - ModelDataSourceMethod method = FindMethod(InsertMethod); + method = method ?? FindMethod(InsertMethod); EvaluateMethodParameters(DataSourceOperation.Insert, method, caseInsensitiveNewValues); return method; } @@ -521,13 +651,22 @@ namespace System.Web.UI.WebControls { /// Invokes the Update method and gets the result. /// </summary> protected virtual object GetUpdateMethodResult(IDictionary keys, IDictionary values, IDictionary oldValues) { - ModelDataSourceMethod method = EvaluateUpdateMethodParameters(keys, values, oldValues); - ModelDataMethodResult result = InvokeMethod(method); + return GetUpdateMethodResult(keys, values, oldValues, null/*method*/, false/*isAsyncMethod*/, null/*cancellationToken*/); + } + + private object GetUpdateMethodResult(IDictionary keys, IDictionary values, IDictionary oldValues, ModelDataSourceMethod method, bool isAsyncMethod, CancellationToken? cancellationToken) { + method = method == null ? EvaluateUpdateMethodParameters(keys, values, oldValues) : EvaluateUpdateMethodParameters(keys, values, oldValues, method); + SetCancellationTokenIfRequired(method, isAsyncMethod, cancellationToken); + ModelDataMethodResult result = InvokeMethod(method, isAsyncMethod); return result.ReturnValue; } protected virtual ModelDataSourceMethod EvaluateUpdateMethodParameters(IDictionary keys, IDictionary values, IDictionary oldValues) { + return EvaluateUpdateMethodParameters(keys, values, oldValues, null/*method*/); + } + + private ModelDataSourceMethod EvaluateUpdateMethodParameters(IDictionary keys, IDictionary values, IDictionary oldValues, ModelDataSourceMethod method) { if (!CanUpdate) { throw new NotSupportedException(SR.GetString(SR.ModelDataSourceView_UpdateNotSupported)); } @@ -542,7 +681,7 @@ namespace System.Web.UI.WebControls { MergeDictionaries(keys, caseInsensitiveNewValues); MergeDictionaries(values, caseInsensitiveNewValues); - ModelDataSourceMethod method = FindMethod(UpdateMethod); + method = method ?? FindMethod(UpdateMethod); EvaluateMethodParameters(DataSourceOperation.Update, method, caseInsensitiveNewValues); return method; } @@ -556,6 +695,190 @@ namespace System.Web.UI.WebControls { return (result is int) ? (int)result : -1 ; } + // We cannot cache this value since users can change SelectMethod by using UpdateProperties + // method. + internal bool IsSelectMethodAsync { + get { + ModelDataSourceMethod method; + return RequireAsyncModelBinding(SelectMethod, out method); + } + } + + public override void Select(DataSourceSelectArguments arguments, DataSourceViewSelectCallback callback) { + ModelDataSourceMethod method; + if (RequireAsyncModelBinding(SelectMethod, out method)) { + // We have to remember the method to make sure the method we later would use in the async function + // is the method we validate here in case the method later might be changed by UpdateProperties. + SelectAsync(arguments, callback, method); + } + else { + base.Select(arguments, callback); + } + } + + private void SelectAsync(DataSourceSelectArguments arguments, DataSourceViewSelectCallback callback, ModelDataSourceMethod method) { + Func<object, Task> func = GetSelectAsyncFunc(arguments, callback, method); + + var syncContext = _owner.DataControl.Page.Context.SyncContext as AspNetSynchronizationContext; + if (null == syncContext) { + throw new InvalidOperationException(SR.GetString(SR.ModelDataSourceView_UseAsyncMethodMustBeUsingAsyncPage)); + } + + // The first edition of the async model binding feature was implemented by registering async binding + // function as PageAsyncTask. We, however, decided not to do that because postponing data binding + // to page async point changed the order of page events and caused many problems. + // See the comment on SynchronizationHelper.QueueAsynchronousAsync for more details regarding to the PostAsync + // function. + syncContext.PostAsync(func, null); + } + + private Func<object, Task> GetSelectAsyncFunc(DataSourceSelectArguments arguments, DataSourceViewSelectCallback callback, ModelDataSourceMethod method) { + Func<object, Task> func = async _ => { + ValidateAsyncModelBindingRequirements(); + CancellationTokenSource cancellationTokenSource = _owner.DataControl.Page.CreateCancellationTokenFromAsyncTimeout(); + CancellationToken cancellationToken = cancellationTokenSource.Token; + DataSourceSelectResultProcessingOptions selectResultProcessingOptions = null; + ModelDataSourceMethod modelMethod = EvaluateSelectMethodParameters(arguments, method, true/*isAsyncSelect*/, out selectResultProcessingOptions); + SetCancellationTokenIfRequired(modelMethod, true/*isAsyncMethod*/, cancellationToken); + ModelDataMethodResult result = InvokeMethod(modelMethod); + IEnumerable finalResult = null; + if (result.ReturnValue != null) { + await (Task)result.ReturnValue; + var returnValue = GetPropertyValueByName(result.ReturnValue, "Result"); + + if (null == returnValue) { + // do nothing + } + // Users needs to use SelectResult as return type to use + // custom paging. + else if (returnValue is SelectResult) { + var viewOperationTask = _viewOperationTask; + if (viewOperationTask != null) { + await viewOperationTask; + } + + var selectResult = (SelectResult)returnValue; + arguments.TotalRowCount = selectResult.TotalRowCount; + finalResult = CreateSelectResult(selectResult.Results, true/*isAsyncSelect*/); + } + else { + // The returnValue does not have to run through ProcessSelectMethodResult() as we + // don't support auto-paging or auto-sorting when using async select. + finalResult = CreateSelectResult(returnValue, true/*isAsyncSelect*/); + } + } + + callback(finalResult); + if (cancellationToken.IsCancellationRequested) { + throw new TimeoutException(SR.GetString(SR.Async_task_timed_out)); + } + }; + + return func; + } + + public override void Insert(IDictionary values, DataSourceViewOperationCallback callback) { + if (callback == null) { + throw new ArgumentNullException("callback"); + } + + ModelDataSourceMethod method; + if (RequireAsyncModelBinding(InsertMethod, out method)) { + ViewOperationAsync((cancellationToken) => (Task)GetInsertMethodResult(values, method, true/*isAsyncMethod*/, cancellationToken), callback); + } + else { + base.Insert(values, callback); + } + } + + public override void Update(IDictionary keys, IDictionary values, IDictionary oldValues, DataSourceViewOperationCallback callback) { + if (callback == null) { + throw new ArgumentNullException("callback"); + } + + ModelDataSourceMethod method; + if (RequireAsyncModelBinding(UpdateMethod, out method)) { + ViewOperationAsync((cancellationToken) => (Task)GetUpdateMethodResult(keys, values, oldValues, method, true/*isAsyncMethod*/, cancellationToken), callback); + } + else { + base.Update(keys, values, oldValues, callback); + } + } + + public override void Delete(IDictionary keys, IDictionary oldValues, DataSourceViewOperationCallback callback) { + if (callback == null) { + throw new ArgumentNullException("callback"); + } + + ModelDataSourceMethod method; + if (RequireAsyncModelBinding(DeleteMethod, out method)) { + ViewOperationAsync((cancellationToken) => (Task)GetDeleteMethodResult(keys, oldValues, method, true/*isAsyncMethod*/, cancellationToken), callback); + } + else { + base.Delete(keys, oldValues, callback); + } + } + + private void ViewOperationAsync(Func<CancellationToken, Task> asyncViewOperation, DataSourceViewOperationCallback callback) { + ValidateAsyncModelBindingRequirements(); + + Func<object, Task> func = async _ => { + CancellationTokenSource cancellationTokenSource = _owner.DataControl.Page.CreateCancellationTokenFromAsyncTimeout(); + CancellationToken cancellationToken = cancellationTokenSource.Token; + var viewOperationTask = _viewOperationTask; + if (viewOperationTask != null) { + await viewOperationTask; + } + + var operationTask = asyncViewOperation(cancellationToken); + _viewOperationTask = operationTask; + var operationTaskInt = operationTask as Task<int>; + var operationThrew = false; + var affectedRecords = -1; + try { + if (null != operationTask) { + await operationTask; + if (operationTaskInt != null) { + affectedRecords = operationTaskInt.Result; + } + } + } + catch (Exception ex) { + operationThrew = true; + if (!callback(affectedRecords, ex)) { + // Nobody handled the operation error so re-throw + throw; + } + } + finally { + _viewOperationTask = null; + + // Data Method is done executing, turn off the TryUpdateModel + _owner.DataControl.Page.SetActiveValueProvider(null); + + if (!operationThrew) { + if (_owner.DataControl.Page.ModelState.IsValid) { + OnDataSourceViewChanged(EventArgs.Empty); + } + + // Success + callback(affectedRecords, null); + } + } + + if (cancellationToken.IsCancellationRequested) { + throw new TimeoutException(SR.GetString(SR.Async_task_timed_out)); + } + }; + + var syncContext = _owner.DataControl.Page.Context.SyncContext as AspNetSynchronizationContext; + if (null == syncContext) { + throw new InvalidOperationException(SR.GetString(SR.ModelDataSourceView_UseAsyncMethodMustBeUsingAsyncPage)); + } + + syncContext.PostAsync(func, null); + } + protected override int ExecuteDelete(IDictionary keys, IDictionary oldValues) { object result = GetDeleteMethodResult(keys, oldValues); OnDataSourceViewChanged(EventArgs.Empty); @@ -601,6 +924,12 @@ namespace System.Web.UI.WebControls { return ExecuteUpdate(keys, values, oldValues); } + // For unit testing. + // Return the select async func that we use in the SelectAsync method. + internal Func<object, Task> SelectAsyncInternal(DataSourceSelectArguments arguments, DataSourceViewSelectCallback callback, ModelDataSourceMethod method) { + return GetSelectAsyncFunc(arguments, callback, method); + } + //Evaluates the select method parameters using the custom value provides. This is done after page load so that //we raise the DataSourceChanged event if the values of parameters change. private void EvaluateSelectParameters() { @@ -641,6 +970,17 @@ namespace System.Web.UI.WebControls { IValueProvider dataBoundControlValueProvider = GetValueProviderFromDictionary(controlValues); ModelBindingExecutionContext modelBindingExecutionContext = _owner.DataControl.Page.ModelBindingExecutionContext; + + Control previousDataControl = null; + if (BinaryCompatibility.Current.TargetsAtLeastFramework46) { + // DevDiv 1087698: a child control overwrites its parent controls's modelBindingExecutionContext, + // which may cause a problem for the parent control to find control by controlId. + Control dataControl = modelBindingExecutionContext.TryGetService<Control>(); + if (dataControl != _owner.DataControl) { + previousDataControl = dataControl; + } + } + //This is used by ControlValueProvider later. modelBindingExecutionContext.PublishService<Control>(_owner.DataControl); @@ -649,7 +989,13 @@ namespace System.Web.UI.WebControls { _owner.DataControl.Page.SetActiveValueProvider(dataBoundControlValueProvider); } - foreach (ParameterInfo parameterInfo in actionMethod.GetParameters()) { + var methodParameters = actionMethod.GetParameters(); + ParameterInfo lastParameter = null; + if (methodParameters.Length > 0) { + lastParameter = methodParameters[methodParameters.Length - 1]; + } + + foreach (ParameterInfo parameterInfo in methodParameters) { object value = null; string modelName = parameterInfo.Name; @@ -705,12 +1051,22 @@ namespace System.Web.UI.WebControls { } } + // We set the CancellationToken after EvaluateMethodParameters(). + // We don't want to set a null value to a CancellationToken variable. + if (parameterInfo == lastParameter && typeof(CancellationToken) == parameterInfo.ParameterType && value == null) { + value = CancellationToken.None; + } + if (!isPageLoadComplete) { ValidateParameterValue(parameterInfo, value, actionMethod); } } modelDataSourceMethod.Parameters.Add(parameterInfo.Name, value); } + + if (previousDataControl != null) { + modelBindingExecutionContext.PublishService<Control>(previousDataControl); + } } private static IValueProvider GetValueProviderFromDictionary(IDictionary controlValues) { @@ -841,6 +1197,10 @@ namespace System.Web.UI.WebControls { /// A ModelDataSouceResult object containing the ReturnValue of the method and any out parameters. /// </returns> protected virtual ModelDataMethodResult InvokeMethod(ModelDataSourceMethod method) { + return InvokeMethod(method, false/*isAsyncMethod*/); + } + + private ModelDataMethodResult InvokeMethod(ModelDataSourceMethod method, bool isAsyncMethod) { object returnValue = null; object[] parameterValues = null; @@ -855,8 +1215,11 @@ namespace System.Web.UI.WebControls { OrderedDictionary outputParameters = GetOutputParameters(method.MethodInfo.GetParameters(), parameterValues); method.Instance = null; - //Data Method is done executing, turn off the TryUpdateModel - _owner.DataControl.Page.SetActiveValueProvider(null); + // Data Method is done executing, turn off the TryUpdateModel + // Do not turn off the TryUpdateModel at this point when the method is async + if (!isAsyncMethod) { + _owner.DataControl.Page.SetActiveValueProvider(null); + } return new ModelDataMethodResult(returnValue, outputParameters); } diff --git a/mcs/class/referencesource/System.Web/UI/WebControls/PagerTable.cs b/mcs/class/referencesource/System.Web/UI/WebControls/PagerTable.cs index 25f4d0ed826..f55e26d655b 100644 --- a/mcs/class/referencesource/System.Web/UI/WebControls/PagerTable.cs +++ b/mcs/class/referencesource/System.Web/UI/WebControls/PagerTable.cs @@ -18,4 +18,4 @@ namespace System.Web.UI.WebControls { /// </devdoc> internal sealed class PagerTable : Table { } -} +}
\ No newline at end of file diff --git a/mcs/class/referencesource/System.Web/UI/WebControls/Panel.cs b/mcs/class/referencesource/System.Web/UI/WebControls/Panel.cs index dc6bdc050eb..cef8bb9e4a2 100644 --- a/mcs/class/referencesource/System.Web/UI/WebControls/Panel.cs +++ b/mcs/class/referencesource/System.Web/UI/WebControls/Panel.cs @@ -290,7 +290,7 @@ namespace System.Web.UI.WebControls { (Page.Request.Browser.W3CDomVersion.Major > 0)) { if (DefaultButton.Length > 0) { // Find control from the page if it's a hierarchical ID. - // Dev11 bug 19915 + // Dev11 Control c = FindControlFromPageIfNecessary(DefaultButton); if (c is IButtonControl) { diff --git a/mcs/class/referencesource/System.Web/UI/WebControls/PasswordRecovery.cs b/mcs/class/referencesource/System.Web/UI/WebControls/PasswordRecovery.cs index f75ce6aa4cd..a236ddde527 100644 --- a/mcs/class/referencesource/System.Web/UI/WebControls/PasswordRecovery.cs +++ b/mcs/class/referencesource/System.Web/UI/WebControls/PasswordRecovery.cs @@ -1569,9 +1569,9 @@ namespace System.Web.UI.WebControls { private void PerformSuccessAction() { string successPageUrl = SuccessPageUrl; if (!String.IsNullOrEmpty(successPageUrl)) { - // [....] suggested that we should not terminate execution of current page, to give + // Microsoft suggested that we should not terminate execution of current page, to give // page a chance to cleanup its resources. This may be less performant though. - // [....] suggested that we need to call ResolveClientUrl before redirecting. + // Microsoft suggested that we need to call ResolveClientUrl before redirecting. // Example is this control inside user control, want redirect relative to user control dir. Page.Response.Redirect(ResolveClientUrl(successPageUrl), false); } diff --git a/mcs/class/referencesource/System.Web/UI/WebControls/RadioButtonList.cs b/mcs/class/referencesource/System.Web/UI/WebControls/RadioButtonList.cs index 9a5c20e6ea0..9bbe64825f2 100644 --- a/mcs/class/referencesource/System.Web/UI/WebControls/RadioButtonList.cs +++ b/mcs/class/referencesource/System.Web/UI/WebControls/RadioButtonList.cs @@ -249,7 +249,8 @@ namespace System.Web.UI.WebControls { string post = postCollection[postDataKey]; int currentSelectedIndex = SelectedIndex; - EnsureDataBound(); + EnsureDataBoundInLoadPostData(); + int n = Items.Count; for (int i=0; i < n; i++) { if (post == Items[i].Value && Items[i].Enabled) { diff --git a/mcs/class/referencesource/System.Web/UI/WebControls/RepeatLayout.cs b/mcs/class/referencesource/System.Web/UI/WebControls/RepeatLayout.cs index a7980bcbbd5..81714e34e97 100644 --- a/mcs/class/referencesource/System.Web/UI/WebControls/RepeatLayout.cs +++ b/mcs/class/referencesource/System.Web/UI/WebControls/RepeatLayout.cs @@ -14,4 +14,4 @@ namespace System.Web.UI.WebControls { UnorderedList = 2, OrderedList = 3, } -} +}
\ No newline at end of file diff --git a/mcs/class/referencesource/System.Web/UI/WebControls/Repeater.cs b/mcs/class/referencesource/System.Web/UI/WebControls/Repeater.cs index bb14215ae01..bb4bbfdc14c 100644 --- a/mcs/class/referencesource/System.Web/UI/WebControls/Repeater.cs +++ b/mcs/class/referencesource/System.Web/UI/WebControls/Repeater.cs @@ -56,6 +56,7 @@ namespace System.Web.UI.WebControls { private string _itemType; private string _selectMethod; private ModelDataSource _modelDataSource; + private bool _asyncSelectPending; /// <devdoc> @@ -603,7 +604,6 @@ namespace System.Web.UI.WebControls { /// </devdoc> protected virtual void CreateControlHierarchy(bool useDataSource) { IEnumerable dataSource = null; - int count = -1; if (itemsArray != null) { itemsArray.Clear(); @@ -612,31 +612,50 @@ namespace System.Web.UI.WebControls { itemsArray = new ArrayList(); } - if (useDataSource == false) { + if (!useDataSource) { // ViewState must have a non-null value for ItemCount because we check for // this in CreateChildControls - count = (int)ViewState[ItemCountViewStateKey]; + int count = (int)ViewState[ItemCountViewStateKey]; if (count != -1) { dataSource = new DummyDataSource(count); itemsArray.Capacity = count; } + + AddDataItemsIntoItemsArray(dataSource, useDataSource); } else { dataSource = GetData(); + PostGetDataAction(dataSource); + } + } - ICollection collection = dataSource as ICollection; - if (collection != null) { - itemsArray.Capacity = collection.Count; - } + private void OnDataSourceViewSelectCallback(IEnumerable data) { + _asyncSelectPending = false; + PostGetDataAction(data); + } + + private void PostGetDataAction(IEnumerable dataSource) { + if (_asyncSelectPending) + return; + + ICollection collection = dataSource as ICollection; + if (collection != null) { + itemsArray.Capacity = collection.Count; } + int addedItemCount = AddDataItemsIntoItemsArray(dataSource, true/*useDataSource*/); + ViewState[ItemCountViewStateKey] = addedItemCount; + } + + private int AddDataItemsIntoItemsArray(IEnumerable dataSource, bool useDataSource) { + int dataItemCount = -1; if (dataSource != null) { RepeaterItem item; ListItemType itemType; int index = 0; bool hasSeparators = (separatorTemplate != null); - count = 0; + dataItemCount = 0; if (headerTemplate != null) { CreateItem(-1, ListItemType.Header, useDataSource, null); @@ -646,7 +665,7 @@ namespace System.Web.UI.WebControls { // rather than creating separators after the item, we create the separator // for the previous item in all iterations of this loop. // this is so that we don't create a separator for the last item - if (hasSeparators && (count > 0)) { + if (hasSeparators && (dataItemCount > 0)) { CreateItem(index - 1, ListItemType.Separator, useDataSource, null); } @@ -654,7 +673,7 @@ namespace System.Web.UI.WebControls { item = CreateItem(index, itemType, useDataSource, dataItem); itemsArray.Add(item); - count++; + dataItemCount++; index++; } @@ -663,10 +682,7 @@ namespace System.Web.UI.WebControls { } } - if (useDataSource) { - // save the number of items contained in the repeater for use in round-trips - ViewState[ItemCountViewStateKey] = ((dataSource != null) ? count : -1); - } + return dataItemCount; } protected virtual DataSourceSelectArguments CreateDataSourceSelectArguments() { @@ -749,8 +765,22 @@ namespace System.Web.UI.WebControls { Debug.Assert(_currentViewValid); if (view != null) { - return view.ExecuteSelect(SelectArguments); + // DevDiv 1070535: enable async model binding for Repeater + bool useAsyncSelect = false; + if (AppSettings.EnableAsyncModelBinding) { + var modelDataView = view as ModelDataSourceView; + useAsyncSelect = modelDataView != null && modelDataView.IsSelectMethodAsync; + } + + if (useAsyncSelect) { + _asyncSelectPending = true; // disable post data binding action until the callback is invoked + view.Select(SelectArguments, OnDataSourceViewSelectCallback); + } + else { + return view.ExecuteSelect(SelectArguments); + } } + return null; } @@ -969,10 +999,10 @@ namespace System.Web.UI.WebControls { /// Saves view state. /// </devdoc> protected override object SaveViewState() { - // Bug 322689: In the web farms scenario, if a web site is hosted in 4.0 and 4.5 servers - // (though this is not a really supported scenario, we are fixing this instance), - // the View state created by 4.0 should be able to be understood by 4.5 controls. - // So, we create a Pair only if we are using model binding and otherwise fallback to 4.0 behavior. + // + + + object baseViewState = base.SaveViewState(); diff --git a/mcs/class/referencesource/System.Web/UI/WebControls/SelectResult.cs b/mcs/class/referencesource/System.Web/UI/WebControls/SelectResult.cs new file mode 100644 index 00000000000..a1d26bfee1c --- /dev/null +++ b/mcs/class/referencesource/System.Web/UI/WebControls/SelectResult.cs @@ -0,0 +1,17 @@ +using System.Collections; + +namespace System.Web.UI.WebControls { + public sealed class SelectResult { + public SelectResult(int totalRowCount, IEnumerable results) { + if (totalRowCount < 0) { + throw new ArgumentOutOfRangeException("totalRowCount"); + } + + TotalRowCount = totalRowCount; + Results = results; + } + + public int TotalRowCount { get; private set; } + public IEnumerable Results { get; private set; } + } +} diff --git a/mcs/class/referencesource/System.Web/UI/WebControls/Style.cs b/mcs/class/referencesource/System.Web/UI/WebControls/Style.cs index 3ab5ad6ac01..274490550b3 100644 --- a/mcs/class/referencesource/System.Web/UI/WebControls/Style.cs +++ b/mcs/class/referencesource/System.Web/UI/WebControls/Style.cs @@ -304,7 +304,6 @@ namespace System.Web.UI.WebControls { DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ] public virtual bool IsEmpty { - [System.Runtime.TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] get { return ((setBits == 0) && (RegisteredCssClass.Length == 0)); } @@ -672,7 +671,6 @@ namespace System.Web.UI.WebControls { /// Returns a value indicating whether the specified style /// property has been defined in the state bag. /// </devdoc> - [System.Runtime.TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] internal bool IsSet(int propKey) { return (setBits & propKey) != 0; } @@ -704,7 +702,6 @@ namespace System.Web.UI.WebControls { /// state changes on the control. Any changes made after "mark" will be tracked and /// saved as part of the control viewstate. /// </devdoc> - [System.Runtime.TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] protected internal virtual void TrackViewState() { if (ownStateBag) { ViewState.TrackViewState(); diff --git a/mcs/class/referencesource/System.Web/UI/WebControls/TextBox.cs b/mcs/class/referencesource/System.Web/UI/WebControls/TextBox.cs index 35e7c2844fc..89eede656b0 100644 --- a/mcs/class/referencesource/System.Web/UI/WebControls/TextBox.cs +++ b/mcs/class/referencesource/System.Web/UI/WebControls/TextBox.cs @@ -696,12 +696,12 @@ namespace System.Web.UI.WebControls { /// </devdoc> protected internal override void Render(HtmlTextWriter writer) { RenderBeginTag(writer); - //Dev10 Bug 483896: Original TextBox rendering in MultiLine mode suffers from the - //problem of losing the first newline. We fixed this bug by always rendering a newline - //before rendering the value of the Text property. + //Dev10 + + if (TextMode == TextBoxMode.MultiLine) { - //Dev11 Bug 437709 fix: We do not want to encode the extra new line that we are - //rendering. However we are doing this only for 4.5 or later frameworks for back-compat. + //Dev11 + if (RenderingCompatibility >= VersionUtil.Framework45) { writer.Write(System.Environment.NewLine); HttpUtility.HtmlEncode(Text, writer); diff --git a/mcs/class/referencesource/System.Web/UI/WebControls/WebControl.cs b/mcs/class/referencesource/System.Web/UI/WebControls/WebControl.cs index 5bc22540155..58454583e6b 100644 --- a/mcs/class/referencesource/System.Web/UI/WebControls/WebControl.cs +++ b/mcs/class/referencesource/System.Web/UI/WebControls/WebControl.cs @@ -317,7 +317,6 @@ namespace System.Web.UI.WebControls { WebSysDescription(SR.WebControl_Enabled) ] public virtual bool Enabled { - [System.Runtime.TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] get { return !flags[isWebControlDisabled]; } @@ -511,7 +510,6 @@ namespace System.Web.UI.WebControls { DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ] protected virtual HtmlTextWriterTag TagKey { - [System.Runtime.TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] get { return tagKey; } diff --git a/mcs/class/referencesource/System.Web/UI/WebControls/Wizard.cs b/mcs/class/referencesource/System.Web/UI/WebControls/Wizard.cs index 4aa0a6f25b3..882c5adcec8 100644 --- a/mcs/class/referencesource/System.Web/UI/WebControls/Wizard.cs +++ b/mcs/class/referencesource/System.Web/UI/WebControls/Wizard.cs @@ -1876,9 +1876,9 @@ namespace System.Web.UI.WebControls { string finishPageUrl = FinishDestinationPageUrl; if (!String.IsNullOrEmpty(finishPageUrl)) { - // [....] suggested that we should not terminate execution of current page, to give + // Microsoft suggested that we should not terminate execution of current page, to give // page a chance to cleanup its resources. This may be less performant though. - // [....] suggested that we need to call ResolveClientUrl before redirecting. + // Microsoft suggested that we need to call ResolveClientUrl before redirecting. // Example is this control inside user control, want redirect relative to user control dir. Page.Response.Redirect(ResolveClientUrl(finishPageUrl), false); } diff --git a/mcs/class/referencesource/System.Web/UI/WebControls/basecomparevalidator.cs b/mcs/class/referencesource/System.Web/UI/WebControls/basecomparevalidator.cs index 04de8b3b4a1..84070930161 100644 --- a/mcs/class/referencesource/System.Web/UI/WebControls/basecomparevalidator.cs +++ b/mcs/class/referencesource/System.Web/UI/WebControls/basecomparevalidator.cs @@ -110,9 +110,9 @@ namespace System.Web.UI.WebControls { AddExpandoAttribute(expandoAttributeWriter, id, "dateorder", GetDateElementOrder(), false); AddExpandoAttribute(expandoAttributeWriter, id, "cutoffyear", CutoffYear.ToString(NumberFormatInfo.InvariantInfo), false); - // VSWhidbey 504553: The changes of this bug make client-side script not - // using the century attribute anymore, but still generating it for - // backward compatibility with Everett pages. + // VSWhidbey 504553: The changes of this + + int currentYear = DateTime.Today.Year; int century = currentYear - (currentYear % 100); AddExpandoAttribute(expandoAttributeWriter, id, "century", century.ToString(NumberFormatInfo.InvariantInfo), false); diff --git a/mcs/class/referencesource/System.Web/UI/WebParts/CatalogZoneBase.cs b/mcs/class/referencesource/System.Web/UI/WebParts/CatalogZoneBase.cs index 597b93fd564..46968b54da7 100644 --- a/mcs/class/referencesource/System.Web/UI/WebParts/CatalogZoneBase.cs +++ b/mcs/class/referencesource/System.Web/UI/WebParts/CatalogZoneBase.cs @@ -549,7 +549,7 @@ namespace System.Web.UI.WebControls.WebParts { // Mozilla renders padding on an empty TD without this attribute writer.AddStyleAttribute(HtmlTextWriterStyle.Padding, "0"); - // Add an extra row with height of 100%, to [....] up any extra space + // Add an extra row with height of 100%, to Microsoft up any extra space // if the height of the zone is larger than its contents // Mac IE needs height=100% set on <td> instead of <tr> writer.AddStyleAttribute(HtmlTextWriterStyle.Height, "100%"); diff --git a/mcs/class/referencesource/System.Web/UI/WebParts/EditorPart.cs b/mcs/class/referencesource/System.Web/UI/WebParts/EditorPart.cs index 66e4961126b..051143d5629 100644 --- a/mcs/class/referencesource/System.Web/UI/WebParts/EditorPart.cs +++ b/mcs/class/referencesource/System.Web/UI/WebParts/EditorPart.cs @@ -246,7 +246,7 @@ namespace System.Web.UI.WebControls.WebParts { } /// <devdoc> - /// Called by the Zone when the EditorPart should [....] its values because other EditorParts + /// Called by the Zone when the EditorPart should sync its values because other EditorParts /// may have changed control properties. This is only called after all the ApplyChanges have returned. /// </devdoc> public abstract void SyncChanges(); diff --git a/mcs/class/referencesource/System.Web/UI/WebParts/EditorZoneBase.cs b/mcs/class/referencesource/System.Web/UI/WebParts/EditorZoneBase.cs index de13bd30076..ab7ee071b22 100644 --- a/mcs/class/referencesource/System.Web/UI/WebParts/EditorZoneBase.cs +++ b/mcs/class/referencesource/System.Web/UI/WebParts/EditorZoneBase.cs @@ -412,7 +412,7 @@ namespace System.Web.UI.WebControls.WebParts { // Mozilla renders padding on an empty TD without this attribute writer.AddStyleAttribute(HtmlTextWriterStyle.Padding, "0"); - // Add an extra row with height of 100%, to [....] up any extra space + // Add an extra row with height of 100%, to Microsoft up any extra space // if the height of the zone is larger than its contents // Mac IE needs height=100% set on <td> instead of <tr> writer.AddStyleAttribute(HtmlTextWriterStyle.Height, "100%"); diff --git a/mcs/class/referencesource/System.Web/UI/WebParts/ProxyWebPartConnectionCollection.cs b/mcs/class/referencesource/System.Web/UI/WebParts/ProxyWebPartConnectionCollection.cs index 4791f4a76fe..5ace8f9d8da 100644 --- a/mcs/class/referencesource/System.Web/UI/WebParts/ProxyWebPartConnectionCollection.cs +++ b/mcs/class/referencesource/System.Web/UI/WebParts/ProxyWebPartConnectionCollection.cs @@ -113,7 +113,7 @@ namespace System.Web.UI.WebControls.WebParts { CheckReadOnly(); if (_webPartManager != null) { int webPartManagerIndex = _webPartManager.StaticConnections.IndexOf((WebPartConnection)oldValue); - // It is a bug if the main WebPartManager does not contain the oldValue + // It is a Debug.Assert(webPartManagerIndex >= 0); _webPartManager.StaticConnections[webPartManagerIndex] = (WebPartConnection)newValue; } diff --git a/mcs/class/referencesource/System.Web/UI/WebParts/WebPartManager.cs b/mcs/class/referencesource/System.Web/UI/WebParts/WebPartManager.cs index c5b3495230e..25d9bb1c410 100644 --- a/mcs/class/referencesource/System.Web/UI/WebParts/WebPartManager.cs +++ b/mcs/class/referencesource/System.Web/UI/WebParts/WebPartManager.cs @@ -571,7 +571,7 @@ if (zoneElement != null) {{ } // Only call PermitOnly() in legacy CAS mode. In the v4 CAS model, calling PermitOnly() would prevent us from calling - // Activator.CreateInstance() on types in App_Code (assuming it is non-APTCA). (Dev10 Bug 807117) + // Activator.CreateInstance() on types in App_Code (assuming it is non-APTCA). (Dev10 private bool UsePermitOnly { get { if (!_usePermitOnly.HasValue) { @@ -1584,8 +1584,8 @@ if (zoneElement != null) {{ StaticConnections.SetReadOnly(SR.WebPartManager_StaticConnectionsReadOnly); // The user can't directly change the DynamicConnections property since it is internal. - // Make it read-only in case we have a bug and try to change it after activation. - // We check the read-only status of this collection in ConnectWebParts() and DisconnectWebParts(). + // Make it read-only in case we have a + DynamicConnections.SetReadOnly(SR.WebPartManager_DynamicConnectionsReadOnly); return (WebPartConnection[])finalConnectionsToActivate.ToArray(typeof(WebPartConnection)); @@ -2691,7 +2691,7 @@ if (zoneElement != null) {{ /// <devdoc> /// Never throws except for null arguments. Returns an error message in the out parameter instead. - /// [[....]] I investigated whether this could be refactored to share common code with + /// [Microsoft] I investigated whether this could be refactored to share common code with /// LoadDynamicWebPart(), but it seems the methods are too different. /// </devdoc> public virtual WebPart ImportWebPart(XmlReader reader, out string errorMessage) { diff --git a/mcs/class/referencesource/System.Web/UI/WebParts/WebPartZoneBase.cs b/mcs/class/referencesource/System.Web/UI/WebParts/WebPartZoneBase.cs index 4fd1f72fd88..7dc790dc485 100644 --- a/mcs/class/referencesource/System.Web/UI/WebParts/WebPartZoneBase.cs +++ b/mcs/class/referencesource/System.Web/UI/WebParts/WebPartZoneBase.cs @@ -1261,7 +1261,7 @@ namespace System.Web.UI.WebControls.WebParts { } if (orientation == Orientation.Vertical) { - // Add an extra row with height of 100%, to [....] up any extra space + // Add an extra row with height of 100%, to Microsoft up any extra space // if the height of the zone is larger than its contents writer.RenderBeginTag(HtmlTextWriterTag.Tr); @@ -1276,7 +1276,7 @@ namespace System.Web.UI.WebControls.WebParts { writer.RenderEndTag(); // Tr } else { - // Add an extra cell with width of 100%, to [....] up any extra space + // Add an extra cell with width of 100%, to Microsoft up any extra space // if the width of the zone is larger than its contents. writer.AddStyleAttribute(HtmlTextWriterStyle.Width, "100%"); diff --git a/mcs/class/referencesource/System.Web/UI/WebResourceAttribute.cs b/mcs/class/referencesource/System.Web/UI/WebResourceAttribute.cs index ed43883c7de..fa2df352bdd 100644 --- a/mcs/class/referencesource/System.Web/UI/WebResourceAttribute.cs +++ b/mcs/class/referencesource/System.Web/UI/WebResourceAttribute.cs @@ -22,7 +22,7 @@ namespace System.Web.UI { private string _cdnPathSecureConnection; private bool _cdnSupportsSecureConnection; - internal const string _microsoftCdnBasePath = "http://ajax.aspnetcdn.com/ajax/4.5.1/1/"; // "/4.5/6/" = 4.5 RTM + internal const string _microsoftCdnBasePath = "http://ajax.aspnetcdn.com/ajax/4.6/1/"; public WebResourceAttribute(string webResource, string contentType) { if (String.IsNullOrEmpty(webResource)) { diff --git a/mcs/class/referencesource/System.Web/UI/WmlTextWriter.cs b/mcs/class/referencesource/System.Web/UI/WmlTextWriter.cs index 702369f5ba5..f2ed95782b9 100644 --- a/mcs/class/referencesource/System.Web/UI/WmlTextWriter.cs +++ b/mcs/class/referencesource/System.Web/UI/WmlTextWriter.cs @@ -32,7 +32,7 @@ namespace System.Web.UI { private bool _analyzeMode = false; private const String _boldTag = "b"; private bool _boldTagOpen = false; - // + // UNDONE: This HttpBrowserCapabilities instance couples the writer with the HttpContext and can be removed. private HttpBrowserCapabilities _browser = null; private IDictionary _controlShortNames = null; private HtmlForm _currentForm = null; @@ -85,7 +85,7 @@ namespace System.Web.UI { } } - // + // UNDONE: This property couples the writer to the HttpContext and can be removed. private HttpBrowserCapabilities Browser { get { if (_browser == null && HttpContext.Current != null) { @@ -186,7 +186,7 @@ namespace System.Web.UI { /// <devdoc> /// <para>[To be supplied.]</para> /// </devdoc> - // + // UNDONE: See whether this method can be replaced by calls to RenderBeginTag. public override void BeginRender() { if (AnalyzeMode) { return; @@ -684,7 +684,7 @@ namespace System.Web.UI { /// <para>[To be supplied.]</para> /// </devdoc> internal String ReplaceFormsCookieWithVariable(String queryString) { - // + // UNDONE: MMIT FormsAuthentication not integrated yet return queryString; } @@ -779,8 +779,8 @@ namespace System.Web.UI { // MapClientIDToShortName for details. WriteAttribute("iname", MapClientIDToShortName(iname, false)); } - // - + // UNDONE: The FormAdapter WrittenFormVariables property couples this writer with the form adapter. Consider + // removing the property somehow. if (!((WmlPageAdapter)CurrentForm.Page.Adapter).WrittenFormVariables && ivalue != null && ivalue.Length > 0) { WriteTextEncodedAttribute("ivalue", ivalue); } |