Code:
/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / ndp / fx / src / xsp / System / Web / Extensions / ui / PageRequestManager.cs / 2 / 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 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 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;
}
}
[SecurityCritical()]
[SecurityTreatAsSafe()]
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, true);
}
private string GetAsyncPostBackControlIDs(bool includeQuotes) {
return GetControlIDsFromList(_asyncPostBackControls, includeQuotes);
}
private string GetChildUpdatePanelIDs() {
return GetUpdatePanelIDsFromList(_childUpdatePanelsToRefresh, false);
}
private static string GetControlIDsFromList(List list, bool includeQuotes) {
if (list != null && list.Count > 0) {
StringBuilder asyncPostBackControlIDs = new StringBuilder();
bool first = true;
for (int i = 0; i < list.Count; i++) {
if (!list[i].Visible) {
// If the panel isn't visible, the client doesn't need to know about it
continue;
}
if (!first) {
asyncPostBackControlIDs.Append(',');
}
first = false;
if (includeQuotes) {
asyncPostBackControlIDs.Append('\'');
}
asyncPostBackControlIDs.Append(list[i].UniqueID);
if (includeQuotes) {
asyncPostBackControlIDs.Append('\'');
}
}
return asyncPostBackControlIDs.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 string GetPostBackControlIDs(bool includeQuotes) {
return GetControlIDsFromList(_postBackControls, includeQuotes);
}
private string GetRefreshingUpdatePanelIDs() {
return GetUpdatePanelIDsFromList(_updatePanelsToRefresh, false);
}
private static string GetUpdatePanelIDsFromList(List list, bool includeChildrenAsTriggersPrefix) {
if (list != null && list.Count > 0) {
StringBuilder updatePanelIDs = new StringBuilder();
bool first = true;
for (int i = 0; i < list.Count; i++) {
if (!list[i].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.
// We also send down a bool indicating whether the children of
// the panel count as triggers or not.
if (includeChildrenAsTriggersPrefix) {
updatePanelIDs.Append(list[i].ChildrenAsTriggers ? 't' : 'f');
}
updatePanelIDs.Append(list[i].UniqueID);
}
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) {
_updatePanelRequiresUpdate = postBackTarget;
}
}
// 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
// - Explicit call to panel.Update()
// - Panel UpdateMode set to Always
// - Trigger fired (not yet implemented)
bool panelUpdatedByClient =
_updatePanelRequiresUpdate != null &&
String.Equals(panel.UniqueID, _updatePanelRequiresUpdate, StringComparison.Ordinal);
bool requiresUpdate = panelUpdatedByClient || panel.RequiresUpdate;
// 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();
}
}
internal void Render(HtmlTextWriter writer) {
if (!((IControl)_owner).DesignMode && !_owner.IsInAsyncPostBack && _owner.SupportsPartialRendering) {
_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();
// 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++) {
if (!list[i].Visible) {
// If the panel isn't visible, the client doesn't need to know about it
continue;
}
if (!first) {
writer.Write(',');
}
first = false;
writer.Write('\'');
// 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.
// We also send down a bool indicating whether the children of
// the panel count as triggers or not.
writer.Write(list[i].ChildrenAsTriggers ? 't' : 'f');
writer.Write(list[i].UniqueID);
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;
}
}
}
}
// 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 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 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;
}
}
[SecurityCritical()]
[SecurityTreatAsSafe()]
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, true);
}
private string GetAsyncPostBackControlIDs(bool includeQuotes) {
return GetControlIDsFromList(_asyncPostBackControls, includeQuotes);
}
private string GetChildUpdatePanelIDs() {
return GetUpdatePanelIDsFromList(_childUpdatePanelsToRefresh, false);
}
private static string GetControlIDsFromList(List list, bool includeQuotes) {
if (list != null && list.Count > 0) {
StringBuilder asyncPostBackControlIDs = new StringBuilder();
bool first = true;
for (int i = 0; i < list.Count; i++) {
if (!list[i].Visible) {
// If the panel isn't visible, the client doesn't need to know about it
continue;
}
if (!first) {
asyncPostBackControlIDs.Append(',');
}
first = false;
if (includeQuotes) {
asyncPostBackControlIDs.Append('\'');
}
asyncPostBackControlIDs.Append(list[i].UniqueID);
if (includeQuotes) {
asyncPostBackControlIDs.Append('\'');
}
}
return asyncPostBackControlIDs.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 string GetPostBackControlIDs(bool includeQuotes) {
return GetControlIDsFromList(_postBackControls, includeQuotes);
}
private string GetRefreshingUpdatePanelIDs() {
return GetUpdatePanelIDsFromList(_updatePanelsToRefresh, false);
}
private static string GetUpdatePanelIDsFromList(List list, bool includeChildrenAsTriggersPrefix) {
if (list != null && list.Count > 0) {
StringBuilder updatePanelIDs = new StringBuilder();
bool first = true;
for (int i = 0; i < list.Count; i++) {
if (!list[i].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.
// We also send down a bool indicating whether the children of
// the panel count as triggers or not.
if (includeChildrenAsTriggersPrefix) {
updatePanelIDs.Append(list[i].ChildrenAsTriggers ? 't' : 'f');
}
updatePanelIDs.Append(list[i].UniqueID);
}
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) {
_updatePanelRequiresUpdate = postBackTarget;
}
}
// 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
// - Explicit call to panel.Update()
// - Panel UpdateMode set to Always
// - Trigger fired (not yet implemented)
bool panelUpdatedByClient =
_updatePanelRequiresUpdate != null &&
String.Equals(panel.UniqueID, _updatePanelRequiresUpdate, StringComparison.Ordinal);
bool requiresUpdate = panelUpdatedByClient || panel.RequiresUpdate;
// 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();
}
}
internal void Render(HtmlTextWriter writer) {
if (!((IControl)_owner).DesignMode && !_owner.IsInAsyncPostBack && _owner.SupportsPartialRendering) {
_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();
// 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++) {
if (!list[i].Visible) {
// If the panel isn't visible, the client doesn't need to know about it
continue;
}
if (!first) {
writer.Write(',');
}
first = false;
writer.Write('\'');
// 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.
// We also send down a bool indicating whether the children of
// the panel count as triggers or not.
writer.Write(list[i].ChildrenAsTriggers ? 't' : 'f');
writer.Write(list[i].UniqueID);
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;
}
}
}
}
// 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
- WindowsRebar.cs
- XmlSchemaIdentityConstraint.cs
- SafeProcessHandle.cs
- AudioException.cs
- HostProtectionPermission.cs
- FacetValueContainer.cs
- HeaderCollection.cs
- InvalidCastException.cs
- FileNotFoundException.cs
- XamlParser.cs
- XmlSecureResolver.cs
- MemoryStream.cs
- DateTimeConstantAttribute.cs
- DataGridTableCollection.cs
- TypefaceCollection.cs
- ApplicationSettingsBase.cs
- TraceSection.cs
- InfoCardRSAOAEPKeyExchangeFormatter.cs
- DebugView.cs
- DefaultBinder.cs
- ModelChangedEventArgsImpl.cs
- ZipIOExtraFieldZip64Element.cs
- ResourceDescriptionAttribute.cs
- AuthStoreRoleProvider.cs
- LabelAutomationPeer.cs
- XmlProcessingInstruction.cs
- Metafile.cs
- XmlAggregates.cs
- InkCanvasSelection.cs
- PhysicalOps.cs
- ObjectListCommandsPage.cs
- X509UI.cs
- CopyAction.cs
- NativeObjectSecurity.cs
- XmlSchemaChoice.cs
- ExcludeFromCodeCoverageAttribute.cs
- EdmRelationshipNavigationPropertyAttribute.cs
- CacheOutputQuery.cs
- Bind.cs
- Rectangle.cs
- oledbconnectionstring.cs
- DateTimeValueSerializer.cs
- MouseBinding.cs
- ReversePositionQuery.cs
- ParentUndoUnit.cs
- Image.cs
- PathFigureCollectionConverter.cs
- BinaryFormatterWriter.cs
- ExpressionVisitor.cs
- ProtocolException.cs
- ReferentialConstraint.cs
- ObjectView.cs
- ToolStripContainer.cs
- CodeMemberField.cs
- TemplateDefinition.cs
- RouteItem.cs
- TabItem.cs
- ArrayElementGridEntry.cs
- XmlEnumAttribute.cs
- DocumentXPathNavigator.cs
- ThreadStaticAttribute.cs
- ProcessInputEventArgs.cs
- Char.cs
- Validator.cs
- ListViewContainer.cs
- RequestStatusBarUpdateEventArgs.cs
- Preprocessor.cs
- PersonalizationDictionary.cs
- HtmlEmptyTagControlBuilder.cs
- SessionStateContainer.cs
- WorkflowServiceHost.cs
- IntSecurity.cs
- SkinBuilder.cs
- XPathNodeList.cs
- InvocationExpression.cs
- ApplicationDirectory.cs
- SystemIPGlobalStatistics.cs
- MatrixTransform3D.cs
- CLRBindingWorker.cs
- DesignerExtenders.cs
- UrlRoutingHandler.cs
- UniqueIdentifierService.cs
- RequiredAttributeAttribute.cs
- BlobPersonalizationState.cs
- CalendarAutoFormat.cs
- TableLayout.cs
- NamespaceMapping.cs
- AuthenticateEventArgs.cs
- StrokeNodeData.cs
- Monitor.cs
- Pair.cs
- DesignerToolStripControlHost.cs
- WebHttpBindingCollectionElement.cs
- SystemIPv4InterfaceProperties.cs
- precedingquery.cs
- NativeMethodsCLR.cs
- ComponentManagerBroker.cs
- SystemNetworkInterface.cs
- DefaultEvaluationContext.cs
- ReplacementText.cs