Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / xsp / System / Extensions / UI / PageRequestManager.cs / 1438166 / PageRequestManager.cs
//------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- namespace System.Web.UI { using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics; using System.Globalization; using System.IO; using System.Security; using System.Text; using System.Web; using System.Web.Configuration; using System.Web.UI; using System.Web.UI.HtmlControls; using System.Web.Resources; using System.Web.Script.Serialization; using System.Reflection; using System.Security.Permissions; internal sealed class PageRequestManager { // Type tokens for partial rendering format internal const string UpdatePanelVersionToken = "#"; internal const string UpdatePanelVersionNumber = "4"; internal const string PageRedirectToken = "pageRedirect"; internal const string HiddenFieldToken = "hiddenField"; private const string AsyncPostBackControlIDsToken = "asyncPostBackControlIDs"; private const string PostBackControlIDsToken = "postBackControlIDs"; private const string UpdatePanelIDsToken = "updatePanelIDs"; private const string AsyncPostBackTimeoutToken = "asyncPostBackTimeout"; private const string ChildUpdatePanelIDsToken = "childUpdatePanelIDs"; private const string UpdatePanelsToRefreshToken = "panelsToRefreshIDs"; private const string FormActionToken = "formAction"; private const string DataItemToken = "dataItem"; private const string DataItemJsonToken = "dataItemJson"; internal const string ArrayDeclarationToken = "arrayDeclaration"; internal const string ExpandoToken = "expando"; internal const string OnSubmitToken = "onSubmit"; internal const string ScriptBlockToken = "scriptBlock"; internal const string ScriptStartupBlockToken = "scriptStartupBlock"; internal const string ScriptDisposeToken = "scriptDispose"; internal const string ErrorToken = "error"; internal const string AsyncPostBackErrorKey = "System.Web.UI.PageRequestManager:AsyncPostBackError"; internal const string AsyncPostBackErrorMessageKey = "System.Web.UI.PageRequestManager:AsyncPostBackErrorMessage"; internal const string AsyncPostBackErrorHttpCodeKey = "System.Web.UI.PageRequestManager:AsyncPostBackErrorHttpCode"; private const string PageTitleToken = "pageTitle"; private const string FocusToken = "focus"; private const string AsyncPostFormField = "__ASYNCPOST"; private const char LengthEncodeDelimiter = '|'; private static readonly Version MinimumW3CDomVersion = new Version(1, 0); private static readonly Version MinimumEcmaScriptVersion = new Version(1, 0); private ScriptManager _owner; private List_allUpdatePanels; private List _updatePanelsToRefresh; private List _childUpdatePanelsToRefresh; private List _asyncPostBackControls; private List _postBackControls; private ScriptDataItemCollection _scriptDataItems; private string _updatePanelRequiresUpdate; private string[] _updatePanelsRequireUpdate; private HtmlTextWriter _updatePanelWriter; private bool _panelsInitialized; private string _asyncPostBackSourceElementID; // Stolen from Whidbey Page.cs for focus support private static readonly Version FocusMinimumEcmaVersion = new Version("1.4"); private static readonly Version FocusMinimumJScriptVersion = new Version("3.0"); private string _focusedControlID; private Control _focusedControl; private bool _requireFocusScript; public PageRequestManager(ScriptManager owner) { Debug.Assert(owner != null); _owner = owner; } public string AsyncPostBackSourceElementID { get { if (_asyncPostBackSourceElementID == null) { return String.Empty; } return _asyncPostBackSourceElementID; } } // Stolen from Whidbey Page.cs private bool ClientSupportsFocus { get { HttpBrowserCapabilitiesBase browser = _owner.IPage.Request.Browser; return (browser.EcmaScriptVersion >= FocusMinimumEcmaVersion) || (browser.JScriptVersion >= FocusMinimumJScriptVersion); } } private bool EnableLegacyRendering { get { return _owner.EnableLegacyRendering; } } [SecuritySafeCritical()] private bool CustomErrorsSectionHasRedirect(int httpCode) { bool hasRedirect = (_owner.CustomErrorsSection.DefaultRedirect != null); if (!hasRedirect) { if (_owner.CustomErrorsSection.Errors != null) { foreach (CustomError error in _owner.CustomErrorsSection.Errors) { if (error.StatusCode == httpCode) { hasRedirect = true; break; } } } } return hasRedirect; } // Optimized version of EncodeString that writes directly to a writer. This // eliminates the need to create several copies of the same string as well // as a StringBuilder. internal static void EncodeString(TextWriter writer, string type, string id, string content) { Debug.Assert(!String.IsNullOrEmpty(type), "Type should always be specified"); if (id == null) { id = String.Empty; } if (content == null) { content = String.Empty; } Debug.Assert(type.IndexOf(LengthEncodeDelimiter) == -1, "Should not be a " + LengthEncodeDelimiter + " in type"); Debug.Assert(id.IndexOf(LengthEncodeDelimiter) == -1, "Should not be a " + LengthEncodeDelimiter + " in id"); // len|type|id|content| // ------- len writer.Write(content.Length.ToString(CultureInfo.InvariantCulture)); writer.Write(LengthEncodeDelimiter); writer.Write(type); writer.Write(LengthEncodeDelimiter); writer.Write(id); writer.Write(LengthEncodeDelimiter); // DevDiv 75383: We used to escape null characters from the content, but this had a non trivial hit on perf // They were escaped because XMLHttpRequest in IE truncates content after a null character. // However, when HTML contains a null character, subsequent content is truncated anyway, so the value of escaping nulls // in the first place is not clear and it was decided it is not worth the perf hit. writer.Write(content); writer.Write(LengthEncodeDelimiter); } private string GetAllUpdatePanelIDs() { return GetUpdatePanelIDsFromList(_allUpdatePanels, IDType.Both, true); } private string GetAsyncPostBackControlIDs(bool includeQuotes) { return GetControlIDsFromList(_asyncPostBackControls, includeQuotes); } private string GetChildUpdatePanelIDs() { return GetUpdatePanelIDsFromList(_childUpdatePanelsToRefresh, IDType.UniqueID, false); } private static string GetControlIDsFromList(List list, bool includeQuotes) { if (list != null && list.Count > 0) { StringBuilder idList = new StringBuilder(); bool first = true; for (int i = 0; i < list.Count; i++) { var control = list[i]; if (!control.Visible) { // If the panel isn't visible, the client doesn't need to know about it continue; } if (!first) { idList.Append(','); } first = false; if (includeQuotes) { idList.Append('\''); } idList.Append(control.UniqueID); if (includeQuotes) { idList.Append('\''); } if (control.EffectiveClientIDMode == ClientIDMode.AutoID) { if (includeQuotes) { idList.Append(",''"); } else { idList.Append(','); } } else { if (includeQuotes) { idList.Append(",'"); idList.Append(control.ClientID); idList.Append('\''); } else { idList.Append(','); idList.Append(control.ClientID); } } } return idList.ToString(); } return String.Empty; } private static Exception GetControlRegistrationException(Control control) { // DevDiv Bugs 145573: It is ok to register the Page as an async/postback control if (control == null) { return new ArgumentNullException("control"); } if (!(control is INamingContainer) && !(control is IPostBackDataHandler) && !(control is IPostBackEventHandler)) { return new ArgumentException(String.Format(CultureInfo.InvariantCulture, AtlasWeb.ScriptManager_InvalidControlRegistration, control.ID)); } return null; } // This code is roughly stolen from HttpException.GetHttpCodeForException() private static int GetHttpCodeForException(Exception e) { HttpException he = e as HttpException; if (he != null) { return he.GetHttpCode(); } else if (e is UnauthorizedAccessException) { return 401; } else if (e is PathTooLongException) { return 414; } // If there is an inner exception, try to get the code from it if (e.InnerException != null) return GetHttpCodeForException(e.InnerException); // If all else fails, use 500 return 500; } private static string GetMasterPageUniqueID(Page page) { // return the UniqueID of the root master page, if any. // The root master page has the full UniqueID prefix that // all controls will have at the start of their 'UniqueID', // counter intuitively it is not the last Master Page with this // full uniqueID. MasterPage m = page.Master; if (m != null) { while (m.Master != null) { m = m.Master; } return m.UniqueID; } return String.Empty; } private string GetPostBackControlIDs(bool includeQuotes) { return GetControlIDsFromList(_postBackControls, includeQuotes); } private string GetRefreshingUpdatePanelIDs() { return GetUpdatePanelIDsFromList(_updatePanelsToRefresh, IDType.Both, false); } private static string GetUpdatePanelIDsFromList(List list, IDType idType, bool includeChildrenAsTriggersPrefix) { if (list != null && list.Count > 0) { StringBuilder updatePanelIDs = new StringBuilder(); bool first = true; for (int i = 0; i < list.Count; i++) { var up = list[i]; if (!up.Visible) { // If the panel isn't visible, the client doesn't need to know about it continue; } if (!first) { updatePanelIDs.Append(','); } first = false; // We send down the UniqueID instead of the ClientID because // we need both versions on the client. You can convert from // UniqueID to ClientID, but not back. // If the UpdatePanel has its ClientID set, we cannot convert // it to UniqueID, so we send both. // We also send down a bool indicating whether the children of // the panel count as triggers or not. if (includeChildrenAsTriggersPrefix) { updatePanelIDs.Append(up.ChildrenAsTriggers ? 't' : 'f'); } updatePanelIDs.Append(up.UniqueID); if (idType == IDType.Both) { updatePanelIDs.Append(','); if (up.EffectiveClientIDMode != ClientIDMode.AutoID) { updatePanelIDs.Append(up.ClientID); } } } return updatePanelIDs.ToString(); } return String.Empty; } internal static bool IsAsyncPostBackRequest(HttpRequestBase request) { // Detect the header for async postbacks. A header can appear // multiple times, and each header entry can contain a comma-separated // list of values. ASP.NET doesn't split the comma-separated values for // us so we have to do it. // We used to use the Pragma header but some browsers, such as Opera, // do not support sending it through XMLHttpRequest. Instead we use a // custom header, X-MicrosoftAjax. string[] headerValues = request.Headers.GetValues("X-MicrosoftAjax"); if (headerValues != null) { for (int i = 0; i < headerValues.Length; i++) { string[] headerContents = headerValues[i].Split(','); for (int j = 0; j < headerContents.Length; j++) { if (headerContents[j].Trim() == "Delta=true") { return true; } } } } // DevDiv Bugs 188713: X-MicrosoftAjax header is stripped by some firewalls string asyncPost = request.Form[AsyncPostFormField]; return !String.IsNullOrEmpty(asyncPost) && (asyncPost.Trim() == "true"); } internal void LoadPostData(string postDataKey, NameValueCollection postCollection) { // Check if the async postback was caused by a specific panel, and if so, force // that panel to update, regardless of whether it had any explicit triggers, etc. // If the post back data starts with the ScriptManager's UniqueID that means the // async postback was caused by a control outside of an UpdatePanel, and the rest // of the string is the UniqueID of that control. string postBackSourceInfo = postCollection[postDataKey]; if (postBackSourceInfo != null) { string postBackTarget; // The target of the postback - either the ScriptManager or an UpdatePanel int indexOfPipe = postBackSourceInfo.IndexOf('|'); if (indexOfPipe != -1) { // We have a target and source element postBackTarget = postBackSourceInfo.Substring(0, indexOfPipe); _asyncPostBackSourceElementID = postBackSourceInfo.Substring(indexOfPipe + 1); } else { // We only have a target postBackTarget = postBackSourceInfo; _asyncPostBackSourceElementID = String.Empty; } if (postBackTarget != _owner.UniqueID) { if (postBackTarget.IndexOf(',') != -1) { _updatePanelRequiresUpdate = null; _updatePanelsRequireUpdate = postBackTarget.Split(','); } else { _updatePanelRequiresUpdate = postBackTarget; _updatePanelsRequireUpdate = null; } } } // Initialize all UpdatePanels (and their triggers, specifically) so that // they can hook events, etc. before other controls can process their // own post data. // LoadPostData on ScriptManager only gets called during async posts, and // is guaranteed to be called before any other controls have a chance to // process their post data. // During regular posts the UpdatePanel initializes itself in OnLoad. if ((_allUpdatePanels != null) && (_allUpdatePanels.Count != 0)) { foreach (UpdatePanel panel in _allUpdatePanels) { panel.Initialize(); } } _panelsInitialized = true; } internal void OnInit() { // Check if the browser supports partial rendering. We only do the check // if the user hasn't already forced the feature to be on or off explicitly. if (_owner.EnablePartialRendering && !_owner._supportsPartialRenderingSetByUser) { HttpBrowserCapabilitiesBase browser = _owner.IPage.Request.Browser; // There is no browser cap that directly tells us whether the browser // supports XmlHttpRequest so we use the next best capability, which is // the SupportsCallback property. // Checking the other properties helps exclude agents such as crawlers. bool supportsPartialRendering = (browser.W3CDomVersion >= MinimumW3CDomVersion) && (browser.EcmaScriptVersion >= MinimumEcmaScriptVersion) && browser.SupportsCallback; if (supportsPartialRendering) { // If we still think we want to support it, now do a more expensive // check for XHTML legacy rendering support. supportsPartialRendering = !EnableLegacyRendering; } _owner.SupportsPartialRendering = supportsPartialRendering; } if (_owner.IsInAsyncPostBack) { _owner.IPage.Error += OnPageError; } } private void OnPageError(object sender, EventArgs e) { Exception ex = _owner.IPage.Server.GetLastError(); _owner.OnAsyncPostBackError(new AsyncPostBackErrorEventArgs(ex)); string errorMessage = _owner.AsyncPostBackErrorMessage; if (String.IsNullOrEmpty(errorMessage) && !_owner.Control.Context.IsCustomErrorEnabled) { // Only use the exception's message if we're not doing custom errors errorMessage = ex.Message; } int httpCode = GetHttpCodeForException(ex); bool showAsyncErrorMessage = false; if (_owner.AllowCustomErrorsRedirect && _owner.Control.Context.IsCustomErrorEnabled) { // Figure out if there's going to be a redirect for this error bool hasRedirect = CustomErrorsSectionHasRedirect(httpCode); if (!hasRedirect) { // If there's no redirect, we need to send back the error message showAsyncErrorMessage = true; } // If there was a redirect we do nothing since ASP.NET will automatically // redirect the user to the error page anyway. This way we don't have to // worry about how to resolve the paths from config. } else { // If we're not going to use custom errors, just send back the error message showAsyncErrorMessage = true; } if (showAsyncErrorMessage) { IDictionary items = _owner.Control.Context.Items; items[AsyncPostBackErrorKey] = true; items[AsyncPostBackErrorMessageKey] = errorMessage; items[AsyncPostBackErrorHttpCodeKey] = httpCode; } } internal void OnPreRender() { _owner.IPage.SetRenderMethodDelegate(RenderPageCallback); } private void ProcessFocus(HtmlTextWriter writer) { // Roughly stolen from Whidbey Page.cs if (_requireFocusScript) { Debug.Assert(ClientSupportsFocus, "If ClientSupportsFocus is false then we never should have set _requireFocusScript to true."); string focusedControlId = String.Empty; // Someone calling SetFocus(controlId) has the most precedent if (!String.IsNullOrEmpty(_focusedControlID)) { focusedControlId = _focusedControlID; } else { if (_focusedControl != null && _focusedControl.Visible) { focusedControlId = _focusedControl.ClientID; } } if (focusedControlId.Length > 0) { // Register focus script library string focusResourceUrl = _owner.GetScriptResourceUrl("Focus.js", typeof(HtmlForm).Assembly); EncodeString(writer, ScriptBlockToken, "ScriptPath", focusResourceUrl); // Send the target control ID to the client EncodeString(writer, FocusToken, String.Empty, focusedControlId); } } } private void ProcessScriptRegistration(HtmlTextWriter writer) { _owner.ScriptRegistration.RenderActiveArrayDeclarations(_updatePanelsToRefresh, writer); _owner.ScriptRegistration.RenderActiveScripts(_updatePanelsToRefresh, writer); _owner.ScriptRegistration.RenderActiveSubmitStatements(_updatePanelsToRefresh, writer); _owner.ScriptRegistration.RenderActiveExpandos(_updatePanelsToRefresh, writer); _owner.ScriptRegistration.RenderActiveHiddenFields(_updatePanelsToRefresh, writer); _owner.ScriptRegistration.RenderActiveScriptDisposes(_updatePanelsToRefresh, writer); } private void ProcessUpdatePanels() { Debug.Assert(_owner.IsInAsyncPostBack); Debug.Assert(_updatePanelsToRefresh == null); if (_allUpdatePanels != null) { _updatePanelsToRefresh = new List (_allUpdatePanels.Count); _childUpdatePanelsToRefresh = new List (_allUpdatePanels.Count); // Process the UpdatePanels to determine which are to be set in // partial rendering mode. // We need to process the list such that parent UpdatePanels are // evaluated first. A child UpdatePanel inside a parent that is being // updated should not be considered in partial rendering mode. // Ordinarily child controls get initialized first before their parents // so you'd expect the list to be in reverse order, but this isn't the case. // UpdatePanels instantiate their templates in their OnInit, so a child // UpdatePanel only exists in the control tree after the parent has been // initialized. HtmlForm form = _owner.Page.Form; for (int i = 0; i < _allUpdatePanels.Count; i++) { UpdatePanel panel = _allUpdatePanels[i]; // Check whether the panel thinks it wants to update. Possible reasons // a panel might be updating: // - Postback data indicates the postback came from within the panel // - Postback data indicates the postbacks was caused by PageRequestManager.beginAsyncPost // and the update panel was explicitly requested to update // - Explicit call to panel.Update() // - Panel UpdateMode set to Always // - Trigger fired (not yet implemented) bool requiresUpdate = panel.RequiresUpdate || (_updatePanelRequiresUpdate != null && String.Equals(panel.UniqueID, _updatePanelRequiresUpdate, StringComparison.Ordinal)) || (_updatePanelsRequireUpdate != null && Array.IndexOf(_updatePanelsRequireUpdate, panel.UniqueID) != -1); // Check and see if a parent panel will take update this panel, whether // this panel wants to update or not. If so, then this panel doesn't need // to be in update mode since it will get included in the rendering // by its parent anyway. // If this parent doesn't want to update then we don't need to do any // additional checks because whether it renders depends entirely on // whether the parent wants to render. Control parent = panel.Parent; while (parent != form) { UpdatePanel parentUpdatePanel = parent as UpdatePanel; if ((parentUpdatePanel != null) && (_updatePanelsToRefresh.Contains(parentUpdatePanel) || _childUpdatePanelsToRefresh.Contains(parentUpdatePanel))) { // This panel is inside another UpdatePanel that is being // rendered, so it should render in normal mode. requiresUpdate = false; _childUpdatePanelsToRefresh.Add(panel); break; } parent = parent.Parent; if (parent == null) { // This UpdatePanel was not inside an HtmlForm // This really shouldn't happen, because the UpdatePanel would have thrown // an exception on the initial GET request that it should be inside a form, // so we'll just ignore it now... requiresUpdate = false; break; } } if (requiresUpdate) { panel.SetAsyncPostBackMode(true); _updatePanelsToRefresh.Add(panel); } else { panel.SetAsyncPostBackMode(false); } } } } public void RegisterAsyncPostBackControl(Control control) { Exception ex = GetControlRegistrationException(control); if (ex != null) { throw ex; } if (_postBackControls != null && _postBackControls.Contains(control)) { throw new ArgumentException(String.Format(CultureInfo.InvariantCulture, AtlasWeb.ScriptManager_CannotRegisterBothPostBacks, control.ID)); } if (_asyncPostBackControls == null) { _asyncPostBackControls = new List (); } // It is acceptable to register the same control twice since the same // control might be referred to by more than one trigger. if (!_asyncPostBackControls.Contains(control)) { _asyncPostBackControls.Add(control); } } public void RegisterDataItem(Control control, string dataItem, bool isJsonSerialized) { if (control == null) { throw new ArgumentNullException("control"); } if (!_owner.IsInAsyncPostBack) { throw new InvalidOperationException(AtlasWeb.PageRequestManager_RegisterDataItemInNonAsyncRequest); } if (_scriptDataItems == null) { _scriptDataItems = new ScriptDataItemCollection(); } else { if (_scriptDataItems.ContainsControl(control)) { throw new ArgumentException( String.Format( CultureInfo.InvariantCulture, AtlasWeb.PageRequestManager_RegisterDataItemTwice, control.ID), "control"); } } _scriptDataItems.Add(new ScriptDataItem(control, dataItem, isJsonSerialized)); } private void RegisterFocusScript() { if (ClientSupportsFocus && !_requireFocusScript) { _requireFocusScript = true; } } public void RegisterPostBackControl(Control control) { Exception ex = GetControlRegistrationException(control); if (ex != null) { throw ex; } if (_asyncPostBackControls != null && _asyncPostBackControls.Contains(control)) { throw new ArgumentException(String.Format(CultureInfo.InvariantCulture, AtlasWeb.ScriptManager_CannotRegisterBothPostBacks, control.ID)); } if (_postBackControls == null) { _postBackControls = new List (); } // It is acceptable to register the same control twice since the same // control might be referred to by more than one trigger. if (!_postBackControls.Contains(control)) { _postBackControls.Add(control); } } internal void RegisterUpdatePanel(UpdatePanel updatePanel) { Debug.Assert(updatePanel != null); if (_allUpdatePanels == null) { _allUpdatePanels = new List (); } Debug.Assert(!_allUpdatePanels.Contains(updatePanel), String.Format(CultureInfo.InvariantCulture, "The UpdatePanel with ID '{0}' has already been registered with the ScriptManager.", updatePanel.ID)); _allUpdatePanels.Add(updatePanel); if (_panelsInitialized) { // Do catch-up for panels that may have been added later in // the lifecycle during an async post. Debug.Assert(_owner.IsInAsyncPostBack, "Catch-up initialization should only be done in async posts."); updatePanel.Initialize(); } } // Only call this method when these condictions are met: // if (!((IControl)_owner).DesignMode && !_owner.IsInAsyncPostBack && _owner.SupportsPartialRendering // && (_owner.MicrosoftAjaxMode != MicrosoftAjaxMode.Disabled)) internal void Render(HtmlTextWriter writer) { _owner.IPage.VerifyRenderingInServerForm(_owner); RenderPageRequestManagerScript(writer); } private void RenderFormCallback(HtmlTextWriter writer, Control containerControl) { Debug.Assert(_updatePanelWriter != null, "_updatePanelWriter should be set by RenderPageCallback before RenderFormCallback is called."); ParserStringWriter parserWriter = writer.InnerWriter as ParserStringWriter; // Disable parsing while rendering UpdatePanels parserWriter.ParseWrites = false; // Suppress rendering of default content; instead just render out // update panels if (_updatePanelsToRefresh != null) { foreach (UpdatePanel panel in _updatePanelsToRefresh) { if (panel.Visible) { // Write UpdatePanels to the response's writer; the writer passed in is a // dummy parserWriter. It will contain hidden fields that are written to // the response's writer later in RenderPageCallback. panel.RenderControl(_updatePanelWriter); } } } IPage page = _owner.IPage; if (page.EnableEventValidation) { // If the page has event validation turned on, we need to run through // the render logic for the rest of the page as well. However, the // rendering is essentially ignored. // UpdatePanels that were already rendered above do not re-render by checking // a flag whether they already rendered. // // DevDiv 55447: Do not use Response.Flush and a NullStream to prevent Response.Writes // from being written to the output stream, as calling Response.Flush causes headers to // be written. This prevents cookies from being issued after this, for example. // Instead, use a NullWriter that ignores Writes. We can change the writer used by HttpResponse // using the internal SwitchWriter method. // We must do this since data written via Response.Write will make the partial update // response invalid. TextWriter oldWriter = null; bool writerSwitched = false; try { // beginning of possible direct Response.Writes oldWriter = page.Response.SwitchWriter(TextWriter.Null); // if we cant switch the writer for some reason we need to know not to switch it back again in the finally block // writerSwitched will be false writerSwitched = true; // nullHtmlWriter captures any writes by controls to the textwriter they are passed. // Note that we could possibly just let null TextWriter we switched catch this data, but this // is more efficient. HtmlTextWriter nullHtmlWriter = new HtmlTextWriter(TextWriter.Null); foreach (Control control in containerControl.Controls) { control.RenderControl(nullHtmlWriter); } } finally { // end of possible direct Response.Writes if (writerSwitched) { page.Response.SwitchWriter(oldWriter); } } } // Re-enable parsing now that the form is done rendering its content parserWriter.ParseWrites = true; } private void RenderPageCallback(HtmlTextWriter writer, Control pageControl) { ProcessUpdatePanels(); // Although we could use the pageControl parameter it's hard to write // unit tests for it. Instead we just use our own page, which is the // same instance anyway (but easier to test with). HttpResponseBase response = _owner.IPage.Response; response.ContentType = "text/plain"; response.Cache.SetNoServerCaching(); // Write out the version identifier, which helps the client-side deal with the response // in a back-compatible way when there are changes made server-side. EncodeString(writer, UpdatePanelVersionToken, String.Empty, UpdatePanelVersionNumber); // Render the form. It will render its tag, hidden fields, etc. // and then call our render method delegate, which will in turn render // all the UpdatePanels IHtmlForm formControl = _owner.IPage.Form; formControl.SetRenderMethodDelegate(RenderFormCallback); // Let updatePanels write directly to Response _updatePanelWriter = writer; // Let form header/footer write to a parser ParserStringWriter parserWriter = new ParserStringWriter(); ParserHtmlTextWriter formWriter = new ParserHtmlTextWriter(parserWriter); parserWriter.ParseWrites = true; formControl.RenderControl(formWriter); parserWriter.ParseWrites = false; // Write out built-in ASP.NET hidden fields // Hidden fields registered by partial-rendering compatible controls will be // rendered by ProcessScriptRegistration(). foreach (KeyValuePair hiddenField in parserWriter.HiddenFields) { if (ControlUtil.IsBuiltInHiddenField(hiddenField.Key)) { EncodeString(writer, HiddenFieldToken, hiddenField.Key, hiddenField.Value); } } // Write out PageRequestManager settings that can change during an async postback. // This is required for dynamic UpdatePanels since the list of panels could // change. EncodeString(writer, AsyncPostBackControlIDsToken, String.Empty, GetAsyncPostBackControlIDs(false)); EncodeString(writer, PostBackControlIDsToken, String.Empty, GetPostBackControlIDs(false)); EncodeString(writer, UpdatePanelIDsToken, String.Empty, GetAllUpdatePanelIDs()); EncodeString(writer, ChildUpdatePanelIDsToken, String.Empty, GetChildUpdatePanelIDs()); EncodeString(writer, UpdatePanelsToRefreshToken, String.Empty, GetRefreshingUpdatePanelIDs()); EncodeString(writer, AsyncPostBackTimeoutToken, String.Empty, _owner.AsyncPostBackTimeout.ToString(CultureInfo.InvariantCulture)); if (formWriter.FormAction != null) { EncodeString(writer, FormActionToken, String.Empty, formWriter.FormAction); } if (_owner.IPage.Header != null) { string pageTitle = _owner.IPage.Title; if (!String.IsNullOrEmpty(pageTitle)) { EncodeString(writer, PageTitleToken, String.Empty, pageTitle); } } RenderDataItems(writer); ProcessScriptRegistration(writer); // We process the focus after regular script registrations to // make sure that if it ends up including some script that it // executes last. ProcessFocus(writer); } private void RenderDataItems(HtmlTextWriter writer) { if (_scriptDataItems != null) { foreach (ScriptDataItem dataItem in _scriptDataItems) { EncodeString( writer, dataItem.IsJsonSerialized ? DataItemJsonToken : DataItemToken, dataItem.Control.ClientID, dataItem.DataItem); } } } internal void RenderPageRequestManagerScript(HtmlTextWriter writer) { // // Script format: // // Writing directly to the writer is more performant than building // up a big string with formatting and then writing it out later. writer.Write(@" "); } private static void RenderUpdatePanelIDsFromList(HtmlTextWriter writer, List list) { // Writing directly to the writer is more performant than building // up a big string with formatting and then writing it out later. if (list != null && list.Count > 0) { bool first = true; for (int i = 0; i < list.Count; i++) { UpdatePanel up = list[i]; if (!up.Visible) { // If the panel isn't visible, the client doesn't need to know about it continue; } if (!first) { writer.Write(','); } first = false; // Due to the settable ClientID feature, UpdatePanel // needs both the clientID and uniqueID // We also send down a bool indicating whether the children of // the panel count as triggers or not. // ['[t|f]uniqueid1','clientid1','[t|f]uniqueid2','clientid2',...] writer.Write("'"); writer.Write(up.ChildrenAsTriggers ? 't' : 'f'); writer.Write(up.UniqueID); writer.Write("',"); if (up.EffectiveClientIDMode == ClientIDMode.AutoID) { writer.Write("''"); } else { writer.Write("'"); writer.Write(up.ClientID); writer.Write("'"); } } } } public void SetFocus(Control control) { // We always call the real Page's method at least to do parameter validation _owner.IPage.SetFocus(control); // If it's not async, just let the page do whatever it wants. If we are // in an async post, we need to keep track of what to focus later on. if (_owner.IsInAsyncPostBack) { _focusedControl = control; _focusedControlID = null; RegisterFocusScript(); } } public void SetFocus(string clientID) { // We always call the real Page's method at least to do parameter validation _owner.IPage.SetFocus(clientID); SetFocusInternal(clientID); } internal void SetFocusInternal(string clientID) { // If it's not async, just let the page do whatever it wants. If we are // in an async post, we need to keep track of what to focus later on. if (_owner.IsInAsyncPostBack) { _focusedControlID = clientID.Trim(); _focusedControl = null; RegisterFocusScript(); } } internal void UnregisterUpdatePanel(UpdatePanel updatePanel) { Debug.Assert(updatePanel != null); if ((_allUpdatePanels == null) || !_allUpdatePanels.Contains(updatePanel)) { throw new ArgumentException(String.Format(CultureInfo.InvariantCulture, AtlasWeb.ScriptManager_UpdatePanelNotRegistered, updatePanel.ID), "updatePanel"); } _allUpdatePanels.Remove(updatePanel); } // Specialized parser StringWriter that can detect when ASP.NET renders // hidden field tags. It strips them from the rendering, and // they can later be retrieved via properties. It only parses while // ParseWrites is set to true. private sealed class ParserStringWriter : StringWriter { private enum ParserState { Ready = 0, ReadHiddenFieldNameValue = 1, ReadHiddenFieldIdAttribute = 2, ReadHiddenFieldIdValue = 3, ReadHiddenFieldValueAttribute = 4, ReadHiddenFieldValueValue = 5, } private bool _parseWrites; private List _pendingWrites = new List (); private ParserState _parserState = ParserState.Ready; private bool _secondTry; // Indicates that we might be accepting a 2nd write for the same parser state private string _proposedHiddenFieldName = String.Empty; private string _matchingHiddenFieldName = String.Empty; private StringBuilder _proposedHiddenFieldValue = new StringBuilder(); private Dictionary _hiddenFields = new Dictionary (); public ParserStringWriter() : base(CultureInfo.CurrentCulture) { } // Gets the name/value pairs for hidden fields parsed from the content. public IDictionary HiddenFields { get { return _hiddenFields; } } // Indicates whether calls to Write(string) and WriteLine(string) // should be parsed for specific content. public bool ParseWrites { get { return _parseWrites; } set { _parseWrites = value; } } private void FlushPendingWrites() { if (_pendingWrites.Count > 0) { foreach (string pendingWrite in _pendingWrites) { base.Write(pendingWrite); } _pendingWrites.Clear(); } } private void ParseString(string s) { switch (_parserState) { case ParserState.Ready: if (s == "") { _proposedHiddenFieldValue.Append(s); } else { // If we get here that means we have a complete hidden field _pendingWrites.Clear(); _hiddenFields.Add(_proposedHiddenFieldName, _proposedHiddenFieldValue.ToString()); _proposedHiddenFieldName = String.Empty; _matchingHiddenFieldName = String.Empty; _proposedHiddenFieldValue = new StringBuilder(); _parserState = ParserState.Ready; } break; default: _secondTry = false; FlushPendingWrites(); base.Write(s); break; } } public override void Write(string s) { if (!ParseWrites) { base.Write(s); return; } ParseString(s); } public override void WriteLine(string value) { if (!ParseWrites) { base.WriteLine(value); return; } // For simplicity's sake, we turn parsed WriteLines into two // calls to our parser. ParseString(value); ParseString(NewLine); } } private sealed class ParserHtmlTextWriter : HtmlTextWriter { private bool _writingForm; private string _formAction; public string FormAction { get { return _formAction; } } public ParserHtmlTextWriter(TextWriter writer) : base(writer) { } public override void WriteBeginTag(string tagName) { base.WriteBeginTag(tagName); // _writingForm = (tagName == "form"); } public override void WriteAttribute(string name, string value, bool fEncode) { base.WriteAttribute(name, value, fEncode); // if (_writingForm) { if (name == "action") { _formAction = value; } } } } private sealed class ScriptDataItem { private Control _control; private string _dataItem; private bool _isJsonSerialized; public ScriptDataItem(Control control, string dataItem, bool isJsonSerialized) { _control = control; _dataItem = (dataItem == null) ? String.Empty : dataItem; _isJsonSerialized = isJsonSerialized; } public Control Control { get { return _control; } } public string DataItem { get { return _dataItem; } } public bool IsJsonSerialized { get { return _isJsonSerialized; } } } private sealed class ScriptDataItemCollection : List { public bool ContainsControl(Control control) { foreach (ScriptDataItem dataItem in this) { if (dataItem.Control == control) { return true; } } return false; } } private enum IDType { UniqueID, Both } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- namespace System.Web.UI { using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics; using System.Globalization; using System.IO; using System.Security; using System.Text; using System.Web; using System.Web.Configuration; using System.Web.UI; using System.Web.UI.HtmlControls; using System.Web.Resources; using System.Web.Script.Serialization; using System.Reflection; using System.Security.Permissions; internal sealed class PageRequestManager { // Type tokens for partial rendering format internal const string UpdatePanelVersionToken = "#"; internal const string UpdatePanelVersionNumber = "4"; internal const string PageRedirectToken = "pageRedirect"; internal const string HiddenFieldToken = "hiddenField"; private const string AsyncPostBackControlIDsToken = "asyncPostBackControlIDs"; private const string PostBackControlIDsToken = "postBackControlIDs"; private const string UpdatePanelIDsToken = "updatePanelIDs"; private const string AsyncPostBackTimeoutToken = "asyncPostBackTimeout"; private const string ChildUpdatePanelIDsToken = "childUpdatePanelIDs"; private const string UpdatePanelsToRefreshToken = "panelsToRefreshIDs"; private const string FormActionToken = "formAction"; private const string DataItemToken = "dataItem"; private const string DataItemJsonToken = "dataItemJson"; internal const string ArrayDeclarationToken = "arrayDeclaration"; internal const string ExpandoToken = "expando"; internal const string OnSubmitToken = "onSubmit"; internal const string ScriptBlockToken = "scriptBlock"; internal const string ScriptStartupBlockToken = "scriptStartupBlock"; internal const string ScriptDisposeToken = "scriptDispose"; internal const string ErrorToken = "error"; internal const string AsyncPostBackErrorKey = "System.Web.UI.PageRequestManager:AsyncPostBackError"; internal const string AsyncPostBackErrorMessageKey = "System.Web.UI.PageRequestManager:AsyncPostBackErrorMessage"; internal const string AsyncPostBackErrorHttpCodeKey = "System.Web.UI.PageRequestManager:AsyncPostBackErrorHttpCode"; private const string PageTitleToken = "pageTitle"; private const string FocusToken = "focus"; private const string AsyncPostFormField = "__ASYNCPOST"; private const char LengthEncodeDelimiter = '|'; private static readonly Version MinimumW3CDomVersion = new Version(1, 0); private static readonly Version MinimumEcmaScriptVersion = new Version(1, 0); private ScriptManager _owner; private List_allUpdatePanels; private List _updatePanelsToRefresh; private List _childUpdatePanelsToRefresh; private List _asyncPostBackControls; private List _postBackControls; private ScriptDataItemCollection _scriptDataItems; private string _updatePanelRequiresUpdate; private string[] _updatePanelsRequireUpdate; private HtmlTextWriter _updatePanelWriter; private bool _panelsInitialized; private string _asyncPostBackSourceElementID; // Stolen from Whidbey Page.cs for focus support private static readonly Version FocusMinimumEcmaVersion = new Version("1.4"); private static readonly Version FocusMinimumJScriptVersion = new Version("3.0"); private string _focusedControlID; private Control _focusedControl; private bool _requireFocusScript; public PageRequestManager(ScriptManager owner) { Debug.Assert(owner != null); _owner = owner; } public string AsyncPostBackSourceElementID { get { if (_asyncPostBackSourceElementID == null) { return String.Empty; } return _asyncPostBackSourceElementID; } } // Stolen from Whidbey Page.cs private bool ClientSupportsFocus { get { HttpBrowserCapabilitiesBase browser = _owner.IPage.Request.Browser; return (browser.EcmaScriptVersion >= FocusMinimumEcmaVersion) || (browser.JScriptVersion >= FocusMinimumJScriptVersion); } } private bool EnableLegacyRendering { get { return _owner.EnableLegacyRendering; } } [SecuritySafeCritical()] private bool CustomErrorsSectionHasRedirect(int httpCode) { bool hasRedirect = (_owner.CustomErrorsSection.DefaultRedirect != null); if (!hasRedirect) { if (_owner.CustomErrorsSection.Errors != null) { foreach (CustomError error in _owner.CustomErrorsSection.Errors) { if (error.StatusCode == httpCode) { hasRedirect = true; break; } } } } return hasRedirect; } // Optimized version of EncodeString that writes directly to a writer. This // eliminates the need to create several copies of the same string as well // as a StringBuilder. internal static void EncodeString(TextWriter writer, string type, string id, string content) { Debug.Assert(!String.IsNullOrEmpty(type), "Type should always be specified"); if (id == null) { id = String.Empty; } if (content == null) { content = String.Empty; } Debug.Assert(type.IndexOf(LengthEncodeDelimiter) == -1, "Should not be a " + LengthEncodeDelimiter + " in type"); Debug.Assert(id.IndexOf(LengthEncodeDelimiter) == -1, "Should not be a " + LengthEncodeDelimiter + " in id"); // len|type|id|content| // ------- len writer.Write(content.Length.ToString(CultureInfo.InvariantCulture)); writer.Write(LengthEncodeDelimiter); writer.Write(type); writer.Write(LengthEncodeDelimiter); writer.Write(id); writer.Write(LengthEncodeDelimiter); // DevDiv 75383: We used to escape null characters from the content, but this had a non trivial hit on perf // They were escaped because XMLHttpRequest in IE truncates content after a null character. // However, when HTML contains a null character, subsequent content is truncated anyway, so the value of escaping nulls // in the first place is not clear and it was decided it is not worth the perf hit. writer.Write(content); writer.Write(LengthEncodeDelimiter); } private string GetAllUpdatePanelIDs() { return GetUpdatePanelIDsFromList(_allUpdatePanels, IDType.Both, true); } private string GetAsyncPostBackControlIDs(bool includeQuotes) { return GetControlIDsFromList(_asyncPostBackControls, includeQuotes); } private string GetChildUpdatePanelIDs() { return GetUpdatePanelIDsFromList(_childUpdatePanelsToRefresh, IDType.UniqueID, false); } private static string GetControlIDsFromList(List list, bool includeQuotes) { if (list != null && list.Count > 0) { StringBuilder idList = new StringBuilder(); bool first = true; for (int i = 0; i < list.Count; i++) { var control = list[i]; if (!control.Visible) { // If the panel isn't visible, the client doesn't need to know about it continue; } if (!first) { idList.Append(','); } first = false; if (includeQuotes) { idList.Append('\''); } idList.Append(control.UniqueID); if (includeQuotes) { idList.Append('\''); } if (control.EffectiveClientIDMode == ClientIDMode.AutoID) { if (includeQuotes) { idList.Append(",''"); } else { idList.Append(','); } } else { if (includeQuotes) { idList.Append(",'"); idList.Append(control.ClientID); idList.Append('\''); } else { idList.Append(','); idList.Append(control.ClientID); } } } return idList.ToString(); } return String.Empty; } private static Exception GetControlRegistrationException(Control control) { // DevDiv Bugs 145573: It is ok to register the Page as an async/postback control if (control == null) { return new ArgumentNullException("control"); } if (!(control is INamingContainer) && !(control is IPostBackDataHandler) && !(control is IPostBackEventHandler)) { return new ArgumentException(String.Format(CultureInfo.InvariantCulture, AtlasWeb.ScriptManager_InvalidControlRegistration, control.ID)); } return null; } // This code is roughly stolen from HttpException.GetHttpCodeForException() private static int GetHttpCodeForException(Exception e) { HttpException he = e as HttpException; if (he != null) { return he.GetHttpCode(); } else if (e is UnauthorizedAccessException) { return 401; } else if (e is PathTooLongException) { return 414; } // If there is an inner exception, try to get the code from it if (e.InnerException != null) return GetHttpCodeForException(e.InnerException); // If all else fails, use 500 return 500; } private static string GetMasterPageUniqueID(Page page) { // return the UniqueID of the root master page, if any. // The root master page has the full UniqueID prefix that // all controls will have at the start of their 'UniqueID', // counter intuitively it is not the last Master Page with this // full uniqueID. MasterPage m = page.Master; if (m != null) { while (m.Master != null) { m = m.Master; } return m.UniqueID; } return String.Empty; } private string GetPostBackControlIDs(bool includeQuotes) { return GetControlIDsFromList(_postBackControls, includeQuotes); } private string GetRefreshingUpdatePanelIDs() { return GetUpdatePanelIDsFromList(_updatePanelsToRefresh, IDType.Both, false); } private static string GetUpdatePanelIDsFromList(List list, IDType idType, bool includeChildrenAsTriggersPrefix) { if (list != null && list.Count > 0) { StringBuilder updatePanelIDs = new StringBuilder(); bool first = true; for (int i = 0; i < list.Count; i++) { var up = list[i]; if (!up.Visible) { // If the panel isn't visible, the client doesn't need to know about it continue; } if (!first) { updatePanelIDs.Append(','); } first = false; // We send down the UniqueID instead of the ClientID because // we need both versions on the client. You can convert from // UniqueID to ClientID, but not back. // If the UpdatePanel has its ClientID set, we cannot convert // it to UniqueID, so we send both. // We also send down a bool indicating whether the children of // the panel count as triggers or not. if (includeChildrenAsTriggersPrefix) { updatePanelIDs.Append(up.ChildrenAsTriggers ? 't' : 'f'); } updatePanelIDs.Append(up.UniqueID); if (idType == IDType.Both) { updatePanelIDs.Append(','); if (up.EffectiveClientIDMode != ClientIDMode.AutoID) { updatePanelIDs.Append(up.ClientID); } } } return updatePanelIDs.ToString(); } return String.Empty; } internal static bool IsAsyncPostBackRequest(HttpRequestBase request) { // Detect the header for async postbacks. A header can appear // multiple times, and each header entry can contain a comma-separated // list of values. ASP.NET doesn't split the comma-separated values for // us so we have to do it. // We used to use the Pragma header but some browsers, such as Opera, // do not support sending it through XMLHttpRequest. Instead we use a // custom header, X-MicrosoftAjax. string[] headerValues = request.Headers.GetValues("X-MicrosoftAjax"); if (headerValues != null) { for (int i = 0; i < headerValues.Length; i++) { string[] headerContents = headerValues[i].Split(','); for (int j = 0; j < headerContents.Length; j++) { if (headerContents[j].Trim() == "Delta=true") { return true; } } } } // DevDiv Bugs 188713: X-MicrosoftAjax header is stripped by some firewalls string asyncPost = request.Form[AsyncPostFormField]; return !String.IsNullOrEmpty(asyncPost) && (asyncPost.Trim() == "true"); } internal void LoadPostData(string postDataKey, NameValueCollection postCollection) { // Check if the async postback was caused by a specific panel, and if so, force // that panel to update, regardless of whether it had any explicit triggers, etc. // If the post back data starts with the ScriptManager's UniqueID that means the // async postback was caused by a control outside of an UpdatePanel, and the rest // of the string is the UniqueID of that control. string postBackSourceInfo = postCollection[postDataKey]; if (postBackSourceInfo != null) { string postBackTarget; // The target of the postback - either the ScriptManager or an UpdatePanel int indexOfPipe = postBackSourceInfo.IndexOf('|'); if (indexOfPipe != -1) { // We have a target and source element postBackTarget = postBackSourceInfo.Substring(0, indexOfPipe); _asyncPostBackSourceElementID = postBackSourceInfo.Substring(indexOfPipe + 1); } else { // We only have a target postBackTarget = postBackSourceInfo; _asyncPostBackSourceElementID = String.Empty; } if (postBackTarget != _owner.UniqueID) { if (postBackTarget.IndexOf(',') != -1) { _updatePanelRequiresUpdate = null; _updatePanelsRequireUpdate = postBackTarget.Split(','); } else { _updatePanelRequiresUpdate = postBackTarget; _updatePanelsRequireUpdate = null; } } } // Initialize all UpdatePanels (and their triggers, specifically) so that // they can hook events, etc. before other controls can process their // own post data. // LoadPostData on ScriptManager only gets called during async posts, and // is guaranteed to be called before any other controls have a chance to // process their post data. // During regular posts the UpdatePanel initializes itself in OnLoad. if ((_allUpdatePanels != null) && (_allUpdatePanels.Count != 0)) { foreach (UpdatePanel panel in _allUpdatePanels) { panel.Initialize(); } } _panelsInitialized = true; } internal void OnInit() { // Check if the browser supports partial rendering. We only do the check // if the user hasn't already forced the feature to be on or off explicitly. if (_owner.EnablePartialRendering && !_owner._supportsPartialRenderingSetByUser) { HttpBrowserCapabilitiesBase browser = _owner.IPage.Request.Browser; // There is no browser cap that directly tells us whether the browser // supports XmlHttpRequest so we use the next best capability, which is // the SupportsCallback property. // Checking the other properties helps exclude agents such as crawlers. bool supportsPartialRendering = (browser.W3CDomVersion >= MinimumW3CDomVersion) && (browser.EcmaScriptVersion >= MinimumEcmaScriptVersion) && browser.SupportsCallback; if (supportsPartialRendering) { // If we still think we want to support it, now do a more expensive // check for XHTML legacy rendering support. supportsPartialRendering = !EnableLegacyRendering; } _owner.SupportsPartialRendering = supportsPartialRendering; } if (_owner.IsInAsyncPostBack) { _owner.IPage.Error += OnPageError; } } private void OnPageError(object sender, EventArgs e) { Exception ex = _owner.IPage.Server.GetLastError(); _owner.OnAsyncPostBackError(new AsyncPostBackErrorEventArgs(ex)); string errorMessage = _owner.AsyncPostBackErrorMessage; if (String.IsNullOrEmpty(errorMessage) && !_owner.Control.Context.IsCustomErrorEnabled) { // Only use the exception's message if we're not doing custom errors errorMessage = ex.Message; } int httpCode = GetHttpCodeForException(ex); bool showAsyncErrorMessage = false; if (_owner.AllowCustomErrorsRedirect && _owner.Control.Context.IsCustomErrorEnabled) { // Figure out if there's going to be a redirect for this error bool hasRedirect = CustomErrorsSectionHasRedirect(httpCode); if (!hasRedirect) { // If there's no redirect, we need to send back the error message showAsyncErrorMessage = true; } // If there was a redirect we do nothing since ASP.NET will automatically // redirect the user to the error page anyway. This way we don't have to // worry about how to resolve the paths from config. } else { // If we're not going to use custom errors, just send back the error message showAsyncErrorMessage = true; } if (showAsyncErrorMessage) { IDictionary items = _owner.Control.Context.Items; items[AsyncPostBackErrorKey] = true; items[AsyncPostBackErrorMessageKey] = errorMessage; items[AsyncPostBackErrorHttpCodeKey] = httpCode; } } internal void OnPreRender() { _owner.IPage.SetRenderMethodDelegate(RenderPageCallback); } private void ProcessFocus(HtmlTextWriter writer) { // Roughly stolen from Whidbey Page.cs if (_requireFocusScript) { Debug.Assert(ClientSupportsFocus, "If ClientSupportsFocus is false then we never should have set _requireFocusScript to true."); string focusedControlId = String.Empty; // Someone calling SetFocus(controlId) has the most precedent if (!String.IsNullOrEmpty(_focusedControlID)) { focusedControlId = _focusedControlID; } else { if (_focusedControl != null && _focusedControl.Visible) { focusedControlId = _focusedControl.ClientID; } } if (focusedControlId.Length > 0) { // Register focus script library string focusResourceUrl = _owner.GetScriptResourceUrl("Focus.js", typeof(HtmlForm).Assembly); EncodeString(writer, ScriptBlockToken, "ScriptPath", focusResourceUrl); // Send the target control ID to the client EncodeString(writer, FocusToken, String.Empty, focusedControlId); } } } private void ProcessScriptRegistration(HtmlTextWriter writer) { _owner.ScriptRegistration.RenderActiveArrayDeclarations(_updatePanelsToRefresh, writer); _owner.ScriptRegistration.RenderActiveScripts(_updatePanelsToRefresh, writer); _owner.ScriptRegistration.RenderActiveSubmitStatements(_updatePanelsToRefresh, writer); _owner.ScriptRegistration.RenderActiveExpandos(_updatePanelsToRefresh, writer); _owner.ScriptRegistration.RenderActiveHiddenFields(_updatePanelsToRefresh, writer); _owner.ScriptRegistration.RenderActiveScriptDisposes(_updatePanelsToRefresh, writer); } private void ProcessUpdatePanels() { Debug.Assert(_owner.IsInAsyncPostBack); Debug.Assert(_updatePanelsToRefresh == null); if (_allUpdatePanels != null) { _updatePanelsToRefresh = new List (_allUpdatePanels.Count); _childUpdatePanelsToRefresh = new List (_allUpdatePanels.Count); // Process the UpdatePanels to determine which are to be set in // partial rendering mode. // We need to process the list such that parent UpdatePanels are // evaluated first. A child UpdatePanel inside a parent that is being // updated should not be considered in partial rendering mode. // Ordinarily child controls get initialized first before their parents // so you'd expect the list to be in reverse order, but this isn't the case. // UpdatePanels instantiate their templates in their OnInit, so a child // UpdatePanel only exists in the control tree after the parent has been // initialized. HtmlForm form = _owner.Page.Form; for (int i = 0; i < _allUpdatePanels.Count; i++) { UpdatePanel panel = _allUpdatePanels[i]; // Check whether the panel thinks it wants to update. Possible reasons // a panel might be updating: // - Postback data indicates the postback came from within the panel // - Postback data indicates the postbacks was caused by PageRequestManager.beginAsyncPost // and the update panel was explicitly requested to update // - Explicit call to panel.Update() // - Panel UpdateMode set to Always // - Trigger fired (not yet implemented) bool requiresUpdate = panel.RequiresUpdate || (_updatePanelRequiresUpdate != null && String.Equals(panel.UniqueID, _updatePanelRequiresUpdate, StringComparison.Ordinal)) || (_updatePanelsRequireUpdate != null && Array.IndexOf(_updatePanelsRequireUpdate, panel.UniqueID) != -1); // Check and see if a parent panel will take update this panel, whether // this panel wants to update or not. If so, then this panel doesn't need // to be in update mode since it will get included in the rendering // by its parent anyway. // If this parent doesn't want to update then we don't need to do any // additional checks because whether it renders depends entirely on // whether the parent wants to render. Control parent = panel.Parent; while (parent != form) { UpdatePanel parentUpdatePanel = parent as UpdatePanel; if ((parentUpdatePanel != null) && (_updatePanelsToRefresh.Contains(parentUpdatePanel) || _childUpdatePanelsToRefresh.Contains(parentUpdatePanel))) { // This panel is inside another UpdatePanel that is being // rendered, so it should render in normal mode. requiresUpdate = false; _childUpdatePanelsToRefresh.Add(panel); break; } parent = parent.Parent; if (parent == null) { // This UpdatePanel was not inside an HtmlForm // This really shouldn't happen, because the UpdatePanel would have thrown // an exception on the initial GET request that it should be inside a form, // so we'll just ignore it now... requiresUpdate = false; break; } } if (requiresUpdate) { panel.SetAsyncPostBackMode(true); _updatePanelsToRefresh.Add(panel); } else { panel.SetAsyncPostBackMode(false); } } } } public void RegisterAsyncPostBackControl(Control control) { Exception ex = GetControlRegistrationException(control); if (ex != null) { throw ex; } if (_postBackControls != null && _postBackControls.Contains(control)) { throw new ArgumentException(String.Format(CultureInfo.InvariantCulture, AtlasWeb.ScriptManager_CannotRegisterBothPostBacks, control.ID)); } if (_asyncPostBackControls == null) { _asyncPostBackControls = new List (); } // It is acceptable to register the same control twice since the same // control might be referred to by more than one trigger. if (!_asyncPostBackControls.Contains(control)) { _asyncPostBackControls.Add(control); } } public void RegisterDataItem(Control control, string dataItem, bool isJsonSerialized) { if (control == null) { throw new ArgumentNullException("control"); } if (!_owner.IsInAsyncPostBack) { throw new InvalidOperationException(AtlasWeb.PageRequestManager_RegisterDataItemInNonAsyncRequest); } if (_scriptDataItems == null) { _scriptDataItems = new ScriptDataItemCollection(); } else { if (_scriptDataItems.ContainsControl(control)) { throw new ArgumentException( String.Format( CultureInfo.InvariantCulture, AtlasWeb.PageRequestManager_RegisterDataItemTwice, control.ID), "control"); } } _scriptDataItems.Add(new ScriptDataItem(control, dataItem, isJsonSerialized)); } private void RegisterFocusScript() { if (ClientSupportsFocus && !_requireFocusScript) { _requireFocusScript = true; } } public void RegisterPostBackControl(Control control) { Exception ex = GetControlRegistrationException(control); if (ex != null) { throw ex; } if (_asyncPostBackControls != null && _asyncPostBackControls.Contains(control)) { throw new ArgumentException(String.Format(CultureInfo.InvariantCulture, AtlasWeb.ScriptManager_CannotRegisterBothPostBacks, control.ID)); } if (_postBackControls == null) { _postBackControls = new List (); } // It is acceptable to register the same control twice since the same // control might be referred to by more than one trigger. if (!_postBackControls.Contains(control)) { _postBackControls.Add(control); } } internal void RegisterUpdatePanel(UpdatePanel updatePanel) { Debug.Assert(updatePanel != null); if (_allUpdatePanels == null) { _allUpdatePanels = new List (); } Debug.Assert(!_allUpdatePanels.Contains(updatePanel), String.Format(CultureInfo.InvariantCulture, "The UpdatePanel with ID '{0}' has already been registered with the ScriptManager.", updatePanel.ID)); _allUpdatePanels.Add(updatePanel); if (_panelsInitialized) { // Do catch-up for panels that may have been added later in // the lifecycle during an async post. Debug.Assert(_owner.IsInAsyncPostBack, "Catch-up initialization should only be done in async posts."); updatePanel.Initialize(); } } // Only call this method when these condictions are met: // if (!((IControl)_owner).DesignMode && !_owner.IsInAsyncPostBack && _owner.SupportsPartialRendering // && (_owner.MicrosoftAjaxMode != MicrosoftAjaxMode.Disabled)) internal void Render(HtmlTextWriter writer) { _owner.IPage.VerifyRenderingInServerForm(_owner); RenderPageRequestManagerScript(writer); } private void RenderFormCallback(HtmlTextWriter writer, Control containerControl) { Debug.Assert(_updatePanelWriter != null, "_updatePanelWriter should be set by RenderPageCallback before RenderFormCallback is called."); ParserStringWriter parserWriter = writer.InnerWriter as ParserStringWriter; // Disable parsing while rendering UpdatePanels parserWriter.ParseWrites = false; // Suppress rendering of default content; instead just render out // update panels if (_updatePanelsToRefresh != null) { foreach (UpdatePanel panel in _updatePanelsToRefresh) { if (panel.Visible) { // Write UpdatePanels to the response's writer; the writer passed in is a // dummy parserWriter. It will contain hidden fields that are written to // the response's writer later in RenderPageCallback. panel.RenderControl(_updatePanelWriter); } } } IPage page = _owner.IPage; if (page.EnableEventValidation) { // If the page has event validation turned on, we need to run through // the render logic for the rest of the page as well. However, the // rendering is essentially ignored. // UpdatePanels that were already rendered above do not re-render by checking // a flag whether they already rendered. // // DevDiv 55447: Do not use Response.Flush and a NullStream to prevent Response.Writes // from being written to the output stream, as calling Response.Flush causes headers to // be written. This prevents cookies from being issued after this, for example. // Instead, use a NullWriter that ignores Writes. We can change the writer used by HttpResponse // using the internal SwitchWriter method. // We must do this since data written via Response.Write will make the partial update // response invalid. TextWriter oldWriter = null; bool writerSwitched = false; try { // beginning of possible direct Response.Writes oldWriter = page.Response.SwitchWriter(TextWriter.Null); // if we cant switch the writer for some reason we need to know not to switch it back again in the finally block // writerSwitched will be false writerSwitched = true; // nullHtmlWriter captures any writes by controls to the textwriter they are passed. // Note that we could possibly just let null TextWriter we switched catch this data, but this // is more efficient. HtmlTextWriter nullHtmlWriter = new HtmlTextWriter(TextWriter.Null); foreach (Control control in containerControl.Controls) { control.RenderControl(nullHtmlWriter); } } finally { // end of possible direct Response.Writes if (writerSwitched) { page.Response.SwitchWriter(oldWriter); } } } // Re-enable parsing now that the form is done rendering its content parserWriter.ParseWrites = true; } private void RenderPageCallback(HtmlTextWriter writer, Control pageControl) { ProcessUpdatePanels(); // Although we could use the pageControl parameter it's hard to write // unit tests for it. Instead we just use our own page, which is the // same instance anyway (but easier to test with). HttpResponseBase response = _owner.IPage.Response; response.ContentType = "text/plain"; response.Cache.SetNoServerCaching(); // Write out the version identifier, which helps the client-side deal with the response // in a back-compatible way when there are changes made server-side. EncodeString(writer, UpdatePanelVersionToken, String.Empty, UpdatePanelVersionNumber); // Render the form. It will render its tag, hidden fields, etc. // and then call our render method delegate, which will in turn render // all the UpdatePanels IHtmlForm formControl = _owner.IPage.Form; formControl.SetRenderMethodDelegate(RenderFormCallback); // Let updatePanels write directly to Response _updatePanelWriter = writer; // Let form header/footer write to a parser ParserStringWriter parserWriter = new ParserStringWriter(); ParserHtmlTextWriter formWriter = new ParserHtmlTextWriter(parserWriter); parserWriter.ParseWrites = true; formControl.RenderControl(formWriter); parserWriter.ParseWrites = false; // Write out built-in ASP.NET hidden fields // Hidden fields registered by partial-rendering compatible controls will be // rendered by ProcessScriptRegistration(). foreach (KeyValuePair hiddenField in parserWriter.HiddenFields) { if (ControlUtil.IsBuiltInHiddenField(hiddenField.Key)) { EncodeString(writer, HiddenFieldToken, hiddenField.Key, hiddenField.Value); } } // Write out PageRequestManager settings that can change during an async postback. // This is required for dynamic UpdatePanels since the list of panels could // change. EncodeString(writer, AsyncPostBackControlIDsToken, String.Empty, GetAsyncPostBackControlIDs(false)); EncodeString(writer, PostBackControlIDsToken, String.Empty, GetPostBackControlIDs(false)); EncodeString(writer, UpdatePanelIDsToken, String.Empty, GetAllUpdatePanelIDs()); EncodeString(writer, ChildUpdatePanelIDsToken, String.Empty, GetChildUpdatePanelIDs()); EncodeString(writer, UpdatePanelsToRefreshToken, String.Empty, GetRefreshingUpdatePanelIDs()); EncodeString(writer, AsyncPostBackTimeoutToken, String.Empty, _owner.AsyncPostBackTimeout.ToString(CultureInfo.InvariantCulture)); if (formWriter.FormAction != null) { EncodeString(writer, FormActionToken, String.Empty, formWriter.FormAction); } if (_owner.IPage.Header != null) { string pageTitle = _owner.IPage.Title; if (!String.IsNullOrEmpty(pageTitle)) { EncodeString(writer, PageTitleToken, String.Empty, pageTitle); } } RenderDataItems(writer); ProcessScriptRegistration(writer); // We process the focus after regular script registrations to // make sure that if it ends up including some script that it // executes last. ProcessFocus(writer); } private void RenderDataItems(HtmlTextWriter writer) { if (_scriptDataItems != null) { foreach (ScriptDataItem dataItem in _scriptDataItems) { EncodeString( writer, dataItem.IsJsonSerialized ? DataItemJsonToken : DataItemToken, dataItem.Control.ClientID, dataItem.DataItem); } } } internal void RenderPageRequestManagerScript(HtmlTextWriter writer) { // // Script format: // // Writing directly to the writer is more performant than building // up a big string with formatting and then writing it out later. writer.Write(@" "); } private static void RenderUpdatePanelIDsFromList(HtmlTextWriter writer, List list) { // Writing directly to the writer is more performant than building // up a big string with formatting and then writing it out later. if (list != null && list.Count > 0) { bool first = true; for (int i = 0; i < list.Count; i++) { UpdatePanel up = list[i]; if (!up.Visible) { // If the panel isn't visible, the client doesn't need to know about it continue; } if (!first) { writer.Write(','); } first = false; // Due to the settable ClientID feature, UpdatePanel // needs both the clientID and uniqueID // We also send down a bool indicating whether the children of // the panel count as triggers or not. // ['[t|f]uniqueid1','clientid1','[t|f]uniqueid2','clientid2',...] writer.Write("'"); writer.Write(up.ChildrenAsTriggers ? 't' : 'f'); writer.Write(up.UniqueID); writer.Write("',"); if (up.EffectiveClientIDMode == ClientIDMode.AutoID) { writer.Write("''"); } else { writer.Write("'"); writer.Write(up.ClientID); writer.Write("'"); } } } } public void SetFocus(Control control) { // We always call the real Page's method at least to do parameter validation _owner.IPage.SetFocus(control); // If it's not async, just let the page do whatever it wants. If we are // in an async post, we need to keep track of what to focus later on. if (_owner.IsInAsyncPostBack) { _focusedControl = control; _focusedControlID = null; RegisterFocusScript(); } } public void SetFocus(string clientID) { // We always call the real Page's method at least to do parameter validation _owner.IPage.SetFocus(clientID); SetFocusInternal(clientID); } internal void SetFocusInternal(string clientID) { // If it's not async, just let the page do whatever it wants. If we are // in an async post, we need to keep track of what to focus later on. if (_owner.IsInAsyncPostBack) { _focusedControlID = clientID.Trim(); _focusedControl = null; RegisterFocusScript(); } } internal void UnregisterUpdatePanel(UpdatePanel updatePanel) { Debug.Assert(updatePanel != null); if ((_allUpdatePanels == null) || !_allUpdatePanels.Contains(updatePanel)) { throw new ArgumentException(String.Format(CultureInfo.InvariantCulture, AtlasWeb.ScriptManager_UpdatePanelNotRegistered, updatePanel.ID), "updatePanel"); } _allUpdatePanels.Remove(updatePanel); } // Specialized parser StringWriter that can detect when ASP.NET renders // hidden field tags. It strips them from the rendering, and // they can later be retrieved via properties. It only parses while // ParseWrites is set to true. private sealed class ParserStringWriter : StringWriter { private enum ParserState { Ready = 0, ReadHiddenFieldNameValue = 1, ReadHiddenFieldIdAttribute = 2, ReadHiddenFieldIdValue = 3, ReadHiddenFieldValueAttribute = 4, ReadHiddenFieldValueValue = 5, } private bool _parseWrites; private List _pendingWrites = new List (); private ParserState _parserState = ParserState.Ready; private bool _secondTry; // Indicates that we might be accepting a 2nd write for the same parser state private string _proposedHiddenFieldName = String.Empty; private string _matchingHiddenFieldName = String.Empty; private StringBuilder _proposedHiddenFieldValue = new StringBuilder(); private Dictionary _hiddenFields = new Dictionary (); public ParserStringWriter() : base(CultureInfo.CurrentCulture) { } // Gets the name/value pairs for hidden fields parsed from the content. public IDictionary HiddenFields { get { return _hiddenFields; } } // Indicates whether calls to Write(string) and WriteLine(string) // should be parsed for specific content. public bool ParseWrites { get { return _parseWrites; } set { _parseWrites = value; } } private void FlushPendingWrites() { if (_pendingWrites.Count > 0) { foreach (string pendingWrite in _pendingWrites) { base.Write(pendingWrite); } _pendingWrites.Clear(); } } private void ParseString(string s) { switch (_parserState) { case ParserState.Ready: if (s == "") { _proposedHiddenFieldValue.Append(s); } else { // If we get here that means we have a complete hidden field _pendingWrites.Clear(); _hiddenFields.Add(_proposedHiddenFieldName, _proposedHiddenFieldValue.ToString()); _proposedHiddenFieldName = String.Empty; _matchingHiddenFieldName = String.Empty; _proposedHiddenFieldValue = new StringBuilder(); _parserState = ParserState.Ready; } break; default: _secondTry = false; FlushPendingWrites(); base.Write(s); break; } } public override void Write(string s) { if (!ParseWrites) { base.Write(s); return; } ParseString(s); } public override void WriteLine(string value) { if (!ParseWrites) { base.WriteLine(value); return; } // For simplicity's sake, we turn parsed WriteLines into two // calls to our parser. ParseString(value); ParseString(NewLine); } } private sealed class ParserHtmlTextWriter : HtmlTextWriter { private bool _writingForm; private string _formAction; public string FormAction { get { return _formAction; } } public ParserHtmlTextWriter(TextWriter writer) : base(writer) { } public override void WriteBeginTag(string tagName) { base.WriteBeginTag(tagName); // _writingForm = (tagName == "form"); } public override void WriteAttribute(string name, string value, bool fEncode) { base.WriteAttribute(name, value, fEncode); // if (_writingForm) { if (name == "action") { _formAction = value; } } } } private sealed class ScriptDataItem { private Control _control; private string _dataItem; private bool _isJsonSerialized; public ScriptDataItem(Control control, string dataItem, bool isJsonSerialized) { _control = control; _dataItem = (dataItem == null) ? String.Empty : dataItem; _isJsonSerialized = isJsonSerialized; } public Control Control { get { return _control; } } public string DataItem { get { return _dataItem; } } public bool IsJsonSerialized { get { return _isJsonSerialized; } } } private sealed class ScriptDataItemCollection : List { public bool ContainsControl(Control control) { foreach (ScriptDataItem dataItem in this) { if (dataItem.Control == control) { return true; } } return false; } } private enum IDType { UniqueID, Both } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- ELinqQueryState.cs
- TableLayoutCellPaintEventArgs.cs
- DesignerDataView.cs
- TextPattern.cs
- FirstMatchCodeGroup.cs
- SystemEvents.cs
- ContentFilePart.cs
- RevocationPoint.cs
- __FastResourceComparer.cs
- MediaScriptCommandRoutedEventArgs.cs
- AxisAngleRotation3D.cs
- RuleElement.cs
- PathFigure.cs
- UnhandledExceptionEventArgs.cs
- ListBindableAttribute.cs
- TransformationRules.cs
- NotFiniteNumberException.cs
- BorderGapMaskConverter.cs
- _NTAuthentication.cs
- DetailsViewUpdatedEventArgs.cs
- RequestBringIntoViewEventArgs.cs
- ActivityExecutionFilter.cs
- ContentDefinition.cs
- PageCache.cs
- UnsafeNativeMethods.cs
- SchemaImporter.cs
- ProfilePropertySettings.cs
- SliderAutomationPeer.cs
- ContentElement.cs
- ProtocolReflector.cs
- ImportContext.cs
- UniqueIdentifierService.cs
- TypeInfo.cs
- TrueReadOnlyCollection.cs
- localization.cs
- XmlSiteMapProvider.cs
- ToolboxItem.cs
- LocalizableAttribute.cs
- VariantWrapper.cs
- EventLogPermissionAttribute.cs
- NetworkAddressChange.cs
- ZipIOZip64EndOfCentralDirectoryLocatorBlock.cs
- ValueOfAction.cs
- Cursor.cs
- BoolLiteral.cs
- SoapMessage.cs
- DataControlReferenceCollection.cs
- BypassElementCollection.cs
- ExecutedRoutedEventArgs.cs
- XmlAnyElementAttribute.cs
- TrackingServices.cs
- CustomErrorsSection.cs
- ParentQuery.cs
- Function.cs
- XamlReader.cs
- Roles.cs
- IsolatedStorageFileStream.cs
- PrimitiveList.cs
- PageStatePersister.cs
- ModelUIElement3D.cs
- SmtpNetworkElement.cs
- GridViewHeaderRowPresenterAutomationPeer.cs
- DiffuseMaterial.cs
- WebEvents.cs
- ActivitySurrogate.cs
- ServicePoint.cs
- LoginCancelEventArgs.cs
- FeatureAttribute.cs
- RoleGroupCollection.cs
- ExpandableObjectConverter.cs
- WindowsSspiNegotiation.cs
- XhtmlBasicCommandAdapter.cs
- SiteMapNodeItem.cs
- IgnoreSectionHandler.cs
- ParameterElementCollection.cs
- DesignerMetadata.cs
- IpcClientManager.cs
- __FastResourceComparer.cs
- ErrorView.xaml.cs
- DbConnectionStringCommon.cs
- mediaeventshelper.cs
- BigInt.cs
- BamlLocalizationDictionary.cs
- UpDownBase.cs
- CharacterString.cs
- DocumentCollection.cs
- SplineKeyFrames.cs
- listitem.cs
- VarRemapper.cs
- MimeMapping.cs
- UIElementParaClient.cs
- NativeMethods.cs
- OneOf.cs
- ReceiveErrorHandling.cs
- Adorner.cs
- ObjectConverter.cs
- WindowInteractionStateTracker.cs
- IndexedGlyphRun.cs
- RequestCacheValidator.cs
- OLEDB_Util.cs