536cd135cc
Former-commit-id: 5624ac747d633e885131e8349322922b6a59baaa
1592 lines
58 KiB
C#
1592 lines
58 KiB
C#
//------------------------------------------------------------------------------
|
|
// <copyright file="MobilePage.cs" company="Microsoft">
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// </copyright>
|
|
//------------------------------------------------------------------------------
|
|
|
|
using System.Collections;
|
|
using System.Collections.Specialized;
|
|
using System.ComponentModel;
|
|
using System.ComponentModel.Design;
|
|
using System.Diagnostics;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using System.Text;
|
|
using System.Web.SessionState;
|
|
using System.Web.Mobile;
|
|
using System.Web.Security;
|
|
using System.Web.Util;
|
|
using System.Security.Permissions;
|
|
|
|
namespace System.Web.UI.MobileControls
|
|
{
|
|
|
|
/*
|
|
* Mobile page class.
|
|
* The page will use device id to create the appropriate DeviceAdapter,
|
|
* and then delegate all major functions to the adapter.
|
|
*
|
|
* THE MOBILE PAGE CLASS DOES NOT CONTAIN DEVICE-SPECIFIC CODE.
|
|
*
|
|
* All mobile aspx pages MUST extend from this using page inherit directive:
|
|
* <%@ Page Inherits="System.Web.UI.MobileControls.MobilePage" Language="cs" %>
|
|
*
|
|
* Copyright (c) 2000 Microsoft Corporation
|
|
*/
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage"]/*' />
|
|
[
|
|
Designer("Microsoft.VisualStudio.Web.WebForms.MobileWebFormDesigner, " + AssemblyRef.MicrosoftVisualStudioWeb, typeof(IRootDesigner)),
|
|
ToolboxItem(false)
|
|
]
|
|
[AspNetHostingPermission(SecurityAction.LinkDemand, Level=AspNetHostingPermissionLevel.Minimal)]
|
|
[AspNetHostingPermission(SecurityAction.InheritanceDemand, Level=AspNetHostingPermissionLevel.Minimal)]
|
|
[Obsolete("The System.Web.Mobile.dll assembly has been deprecated and should no longer be used. For information about how to develop ASP.NET mobile applications, see http://go.microsoft.com/fwlink/?LinkId=157231.")]
|
|
public class MobilePage : Page
|
|
{
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.HiddenPostEventSourceId"]/*' />
|
|
public static readonly String HiddenPostEventSourceId = postEventSourceID;
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.HiddenPostEventArgumentId"]/*' />
|
|
public static readonly String HiddenPostEventArgumentId = postEventArgumentID;
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.ViewStateID"]/*' />
|
|
public static readonly String ViewStateID = "__VIEWSTATE";
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.HiddenVariablePrefix"]/*' />
|
|
public static readonly String HiddenVariablePrefix = "__V_";
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.PageClientViewStateKey"]/*' />
|
|
public static readonly String PageClientViewStateKey = "__P";
|
|
|
|
private const String DesignerAdapter = "System.Web.UI.MobileControls.Adapters.HtmlPageAdapter";
|
|
private IPageAdapter _pageAdapter;
|
|
private bool _debugMode = false;
|
|
private StyleSheet _styleSheet = null;
|
|
private IDictionary _hiddenVariables;
|
|
private Hashtable _clientViewState;
|
|
private String _eventSource;
|
|
private Hashtable _privateViewState = new Hashtable();
|
|
bool _privateViewStateLoaded = false;
|
|
private NameValueCollection _requestValueCollection;
|
|
private bool _isRenderingInForm = false;
|
|
private bool _afterPreInit;
|
|
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.AddParsedSubObject"]/*' />
|
|
protected override void AddParsedSubObject(Object o)
|
|
{
|
|
// Note : AddParsedSubObject is never called at DesignTime
|
|
if (o is StyleSheet)
|
|
{
|
|
if (_styleSheet != null)
|
|
{
|
|
throw new
|
|
Exception(SR.GetString(SR.StyleSheet_DuplicateWarningMessage));
|
|
}
|
|
else
|
|
{
|
|
_styleSheet = (StyleSheet)o;
|
|
}
|
|
}
|
|
|
|
base.AddParsedSubObject(o);
|
|
}
|
|
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.Device"]/*' />
|
|
[
|
|
Browsable(false),
|
|
Bindable(false),
|
|
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
|
|
]
|
|
public virtual MobileCapabilities Device
|
|
{
|
|
get
|
|
{
|
|
if (DesignMode)
|
|
{
|
|
return new
|
|
System.Web.UI.Design.MobileControls.DesignerCapabilities();
|
|
}
|
|
return (MobileCapabilities)Request.Browser;
|
|
}
|
|
}
|
|
|
|
[
|
|
Browsable(false),
|
|
Bindable(false),
|
|
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
|
|
EditorBrowsable(EditorBrowsableState.Advanced)
|
|
]
|
|
public override sealed string MasterPageFile {
|
|
get {
|
|
return null;
|
|
}
|
|
set {
|
|
if (_afterPreInit) {
|
|
throw new NotSupportedException(SR.GetString(SR.Feature_Not_Supported_On_MobilePage, "MasterPage"));
|
|
}
|
|
}
|
|
}
|
|
|
|
// EventValidation is not supported on a mobile page.
|
|
public override bool EnableEventValidation {
|
|
get {
|
|
return false;
|
|
}
|
|
set {
|
|
if (_afterPreInit && value) {
|
|
throw new NotSupportedException(SR.GetString(SR.Feature_Not_Supported_On_MobilePage, "EventValidation"));
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.StyleSheet"]/*' />
|
|
[
|
|
Browsable(false),
|
|
Bindable(false),
|
|
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
|
|
]
|
|
public StyleSheet StyleSheet
|
|
{
|
|
get
|
|
{
|
|
return (_styleSheet != null) ? _styleSheet : StyleSheet.Default;
|
|
}
|
|
|
|
set
|
|
{
|
|
_styleSheet = value;
|
|
}
|
|
}
|
|
|
|
[EditorBrowsable(EditorBrowsableState.Advanced)]
|
|
public override String Theme {
|
|
get {
|
|
return base.Theme;
|
|
}
|
|
set {
|
|
if (_afterPreInit) {
|
|
throw new NotSupportedException(SR.GetString(SR.Feature_Not_Supported_On_MobilePage, "Theme"));
|
|
}
|
|
}
|
|
}
|
|
|
|
[
|
|
Bindable(false),
|
|
Localizable(false),
|
|
EditorBrowsable(EditorBrowsableState.Never),
|
|
]
|
|
public new String Title {
|
|
get {
|
|
return String.Empty;
|
|
}
|
|
set {
|
|
throw new NotSupportedException(SR.GetString(SR.Feature_Not_Supported_On_MobilePage, "Title"));
|
|
}
|
|
}
|
|
|
|
[EditorBrowsable(EditorBrowsableState.Advanced)]
|
|
public override String StyleSheetTheme {
|
|
get {
|
|
return base.StyleSheetTheme;
|
|
}
|
|
set {
|
|
if (_afterPreInit) {
|
|
throw new NotSupportedException(SR.GetString(SR.Feature_Not_Supported_On_MobilePage, "StyleSheetTheme"));
|
|
}
|
|
}
|
|
}
|
|
|
|
[
|
|
Browsable(false),
|
|
EditorBrowsable(EditorBrowsableState.Advanced)
|
|
]
|
|
public override bool EnableTheming {
|
|
get {
|
|
return base.EnableTheming;
|
|
}
|
|
set {
|
|
throw new NotSupportedException(SR.GetString(SR.Feature_Not_Supported_On_MobilePage, "Theme"));
|
|
}
|
|
}
|
|
|
|
private IList _forms;
|
|
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.Forms"]/*' />
|
|
[
|
|
Browsable(false),
|
|
Bindable(false),
|
|
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
|
|
]
|
|
public IList Forms
|
|
{
|
|
get
|
|
{
|
|
if (_forms == null)
|
|
{
|
|
int probableFormCount = Controls.Count / 2; // since there are literal controls between each
|
|
_forms = new ArrayList(probableFormCount);
|
|
AddForms(this);
|
|
}
|
|
return _forms;
|
|
}
|
|
}
|
|
|
|
private void AddForms(Control parent)
|
|
{
|
|
foreach (Control control in parent.Controls)
|
|
{
|
|
if (control is Form)
|
|
{
|
|
_forms.Add(control);
|
|
}
|
|
else if (control is UserControl)
|
|
{
|
|
AddForms(control);
|
|
}
|
|
}
|
|
}
|
|
|
|
private enum RunMode
|
|
{
|
|
Unknown,
|
|
Design,
|
|
Runtime,
|
|
};
|
|
private RunMode _runMode = RunMode.Unknown;
|
|
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.DesignMode"]/*' />
|
|
[
|
|
Browsable(false),
|
|
Bindable(false),
|
|
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
|
|
EditorBrowsable(EditorBrowsableState.Never),
|
|
]
|
|
public new bool DesignMode
|
|
{
|
|
get
|
|
{
|
|
if (_runMode == RunMode.Unknown)
|
|
{
|
|
_runMode = RunMode.Runtime;
|
|
try
|
|
{
|
|
_runMode = (HttpContext.Current == null) ? RunMode.Design : RunMode.Runtime;
|
|
}
|
|
catch
|
|
{
|
|
_runMode = RunMode.Design;
|
|
}
|
|
}
|
|
return _runMode == RunMode.Design;
|
|
}
|
|
}
|
|
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.Adapter"]/*' />
|
|
[
|
|
Browsable(false),
|
|
Bindable(false),
|
|
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
|
|
]
|
|
public new IPageAdapter Adapter
|
|
{
|
|
get
|
|
{
|
|
if (_pageAdapter == null)
|
|
{
|
|
IPageAdapter pageAdapter = RequestingDeviceConfig.NewPageAdapter();
|
|
pageAdapter.Page = this;
|
|
_pageAdapter = pageAdapter;
|
|
if(!DesignMode)
|
|
{
|
|
Type t = ControlsConfig.GetFromContext(HttpContext.Current).CookielessDataDictionaryType;
|
|
if(t != null && typeof(IDictionary).IsAssignableFrom(t))
|
|
{
|
|
pageAdapter.CookielessDataDictionary = Activator.CreateInstance(t) as IDictionary;
|
|
pageAdapter.PersistCookielessData = true;
|
|
}
|
|
}
|
|
}
|
|
return _pageAdapter;
|
|
}
|
|
}
|
|
|
|
|
|
private bool _haveIdSeparator;
|
|
private char _idSeparator;
|
|
public override char IdSeparator {
|
|
get {
|
|
if (_haveIdSeparator) {
|
|
return _idSeparator;
|
|
}
|
|
|
|
_haveIdSeparator = true;
|
|
IPageAdapter pageAdapter = Adapter;
|
|
Debug.Assert(pageAdapter != null);
|
|
|
|
// VSWhidbey 280485
|
|
if (pageAdapter is System.Web.UI.MobileControls.Adapters.WmlPageAdapter ||
|
|
pageAdapter is System.Web.UI.MobileControls.Adapters.XhtmlAdapters.XhtmlPageAdapter) {
|
|
_idSeparator = ':';
|
|
}
|
|
else {
|
|
_idSeparator = base.IdSeparator;
|
|
}
|
|
return _idSeparator;
|
|
}
|
|
}
|
|
|
|
String _clientViewStateString;
|
|
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.ClientViewState"]/*' />
|
|
[
|
|
Browsable(false),
|
|
Bindable(false),
|
|
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
|
|
]
|
|
public String ClientViewState
|
|
{
|
|
get
|
|
{
|
|
if (_clientViewState == null || _clientViewState.Count == 0)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if (_clientViewStateString == null)
|
|
{
|
|
StringWriter writer = new StringWriter(CultureInfo.InvariantCulture);
|
|
StateFormatter.Serialize(writer, _clientViewState);
|
|
_clientViewStateString = writer.ToString();
|
|
}
|
|
|
|
return _clientViewStateString;
|
|
}
|
|
}
|
|
|
|
private BooleanOption _allowCustomAttributes = BooleanOption.NotSet;
|
|
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.AllowCustomAttributes"]/*' />
|
|
[
|
|
Browsable(false),
|
|
Bindable(false),
|
|
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
|
|
]
|
|
public bool AllowCustomAttributes
|
|
{
|
|
get
|
|
{
|
|
if (DesignMode)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (_allowCustomAttributes == BooleanOption.NotSet)
|
|
{
|
|
_allowCustomAttributes =
|
|
ControlsConfig.GetFromContext(Context).AllowCustomAttributes ?
|
|
BooleanOption.True : BooleanOption.False;
|
|
}
|
|
return _allowCustomAttributes == BooleanOption.True;
|
|
}
|
|
|
|
set
|
|
{
|
|
_allowCustomAttributes = value ? BooleanOption.True : BooleanOption.False;
|
|
}
|
|
}
|
|
|
|
private void AddClientViewState(String id, Object viewState)
|
|
{
|
|
if (_clientViewState == null)
|
|
{
|
|
_clientViewState = new Hashtable();
|
|
}
|
|
_clientViewState[id] = viewState;
|
|
_clientViewStateString = null;
|
|
}
|
|
|
|
internal void AddClientViewState(MobileControl control, Object viewState)
|
|
{
|
|
AddClientViewState(control.UniqueID, viewState);
|
|
}
|
|
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.GetControlAdapter"]/*' />
|
|
public virtual IControlAdapter GetControlAdapter(MobileControl control)
|
|
{
|
|
IControlAdapter adapter = RequestingDeviceConfig.NewControlAdapter(control.GetType ());
|
|
adapter.Control = control;
|
|
return adapter;
|
|
}
|
|
|
|
private IndividualDeviceConfig _deviceConfig = null;
|
|
private IndividualDeviceConfig RequestingDeviceConfig
|
|
{
|
|
get
|
|
{
|
|
if (_deviceConfig == null)
|
|
{
|
|
if (DesignMode)
|
|
{
|
|
_deviceConfig = new DesignerDeviceConfig(DesignerAdapter);
|
|
}
|
|
else
|
|
{
|
|
_deviceConfig =
|
|
ControlsConfig.GetFromContext(Context).GetDeviceConfig(Context);
|
|
}
|
|
}
|
|
return _deviceConfig;
|
|
}
|
|
}
|
|
|
|
private String _appPath;
|
|
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.MakePathAbsolute"]/*' />
|
|
public String MakePathAbsolute(String virtualPath)
|
|
{
|
|
if (virtualPath == null || virtualPath.Length == 0)
|
|
{
|
|
return virtualPath;
|
|
}
|
|
|
|
if (!UrlPath.IsRelativeUrl(virtualPath))
|
|
{
|
|
// For consistency with ResolveUrl, do not apply app path modifier to rooted paths.
|
|
//return Response.ApplyAppPathModifier(virtualPath);
|
|
return virtualPath;
|
|
}
|
|
else
|
|
{
|
|
if (_appPath == null)
|
|
{
|
|
String path = Request.CurrentExecutionFilePath;
|
|
path = Response.ApplyAppPathModifier(path);
|
|
int slash = path.LastIndexOf('/');
|
|
if (slash != -1)
|
|
{
|
|
path = path.Substring(0, slash);
|
|
}
|
|
if (path.IndexOf(' ') != -1)
|
|
{
|
|
path = path.Replace(" ", "%20");
|
|
}
|
|
_appPath = path;
|
|
}
|
|
|
|
virtualPath = UrlPath.Combine(_appPath, virtualPath);
|
|
return virtualPath;
|
|
}
|
|
}
|
|
|
|
private String _relativeFilePath;
|
|
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.RelativeFilePath"]/*' />
|
|
[
|
|
Browsable(false),
|
|
]
|
|
public String RelativeFilePath
|
|
{
|
|
get
|
|
{
|
|
// Vs7 Property sig will always try to access public properties with get methods no
|
|
// matter Brosable attribute is off or not. We need to check if is DesignMode in
|
|
// order to prevent the exception from vs7 at design time.
|
|
if (DesignMode)
|
|
{
|
|
return String.Empty;
|
|
}
|
|
|
|
if (_relativeFilePath == null)
|
|
{
|
|
String s = Context.Request.CurrentExecutionFilePath;
|
|
String filePath = Context.Request.FilePath;
|
|
if(filePath.Equals(s))
|
|
{
|
|
int slash = s.LastIndexOf('/');
|
|
if (slash >= 0)
|
|
{
|
|
s = s.Substring(slash+1);
|
|
}
|
|
_relativeFilePath = s;
|
|
}
|
|
else
|
|
{
|
|
_relativeFilePath = Server.UrlDecode(UrlPath.MakeRelative(filePath, s));
|
|
}
|
|
}
|
|
return _relativeFilePath;
|
|
}
|
|
}
|
|
|
|
private String _absoluteFilePath;
|
|
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.AbsoluteFilePath"]/*' />
|
|
[
|
|
Browsable(false),
|
|
]
|
|
public String AbsoluteFilePath
|
|
{
|
|
get
|
|
{
|
|
// Vs7 Property sig will always try to access public properties with get methods no
|
|
// matter Brosable attribute is off or not. We need to check if Context is null in
|
|
// order to prevent the exception from vs7 at design time.
|
|
if (_absoluteFilePath == null && Context != null)
|
|
{
|
|
_absoluteFilePath = Response.ApplyAppPathModifier(Context.Request.CurrentExecutionFilePath);
|
|
}
|
|
return _absoluteFilePath;
|
|
}
|
|
}
|
|
|
|
private String _uniqueFilePathSuffix;
|
|
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.UniqueFilePathSuffix"]/*' />
|
|
[
|
|
Browsable(false),
|
|
]
|
|
public new String UniqueFilePathSuffix
|
|
{
|
|
// Required for browsers that don't properly handle
|
|
// self-referential form posts.
|
|
|
|
get
|
|
{
|
|
if (_uniqueFilePathSuffix == null)
|
|
{
|
|
// Only need a few digits, so save space by modulo'ing by a prime.
|
|
// The chosen prime is the highest of six digits.
|
|
long ticks = DateTime.Now.Ticks % 999983;
|
|
_uniqueFilePathSuffix = String.Concat(
|
|
Constants.UniqueFilePathSuffixVariable,
|
|
ticks.ToString("D6", CultureInfo.InvariantCulture));
|
|
}
|
|
return _uniqueFilePathSuffix;
|
|
}
|
|
}
|
|
|
|
private static String RemoveQueryStringElement(String queryStringText, String elementName)
|
|
{
|
|
int n = elementName.Length;
|
|
int i = 0;
|
|
for (i = 0; i < queryStringText.Length;)
|
|
{
|
|
i = queryStringText.IndexOf(elementName, i, StringComparison.Ordinal);
|
|
if (i < 0)
|
|
{
|
|
break;
|
|
}
|
|
if (i == 0 || queryStringText[i-1] == '&')
|
|
{
|
|
if (i+n < queryStringText.Length && queryStringText[i+n] == '=')
|
|
{
|
|
int j = queryStringText.IndexOf('&', i+n);
|
|
if (j < 0)
|
|
{
|
|
if (i == 0)
|
|
{
|
|
queryStringText = String.Empty;
|
|
}
|
|
else
|
|
{
|
|
queryStringText = queryStringText.Substring(0, i-1);
|
|
}
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
queryStringText = queryStringText.Remove(i, j-i+1);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
i += n;
|
|
}
|
|
return queryStringText;
|
|
}
|
|
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.QueryStringText"]/*' />
|
|
[
|
|
Browsable(false),
|
|
]
|
|
public String QueryStringText
|
|
{
|
|
// Returns the query string text, stripping off a unique file path
|
|
// suffix as required. Also assumes that if the suffix is
|
|
// present, the query string part is the text after it.
|
|
|
|
get
|
|
{
|
|
// Vs7 Property sig will always try to access public properties with get methods no
|
|
// matter Brosable attribute is off or not. We need to check if Context is null in
|
|
// order to prevent the exception from vs7 at design time.
|
|
if(DesignMode)
|
|
{
|
|
return String.Empty;
|
|
}
|
|
|
|
String fullQueryString;
|
|
|
|
if (Request.HttpMethod != "POST")
|
|
{
|
|
fullQueryString = CreateQueryStringTextFromCollection(Request.QueryString);
|
|
}
|
|
else if (Device.SupportsQueryStringInFormAction)
|
|
{
|
|
fullQueryString = Request.ServerVariables["QUERY_STRING"];
|
|
}
|
|
else
|
|
{
|
|
fullQueryString = CreateQueryStringTextFromCollection(_requestValueCollection);
|
|
}
|
|
|
|
if(fullQueryString != null && fullQueryString.Length > 0)
|
|
{
|
|
fullQueryString = RemoveQueryStringElement(fullQueryString, Constants.UniqueFilePathSuffixVariableWithoutEqual);
|
|
fullQueryString = RemoveQueryStringElement(fullQueryString, MobileRedirect.QueryStringVariable);
|
|
if (!Adapter.PersistCookielessData)
|
|
{
|
|
fullQueryString = RemoveQueryStringElement(fullQueryString, FormsAuthentication.FormsCookieName);
|
|
}
|
|
}
|
|
return fullQueryString;
|
|
}
|
|
}
|
|
|
|
private String _activeFormID;
|
|
private Form _activeForm;
|
|
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.ActiveForm"]/*' />
|
|
[
|
|
Browsable(false),
|
|
Bindable(false),
|
|
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
|
|
]
|
|
public Form ActiveForm
|
|
{
|
|
get
|
|
{
|
|
// retrieve form cached in local variable
|
|
if (_activeForm != null)
|
|
{
|
|
return _activeForm;
|
|
}
|
|
|
|
// else get the id from state and retrieve form
|
|
if (_activeFormID != null)
|
|
{
|
|
_activeForm = GetForm(_activeFormID);
|
|
_activeForm.Activated = true;
|
|
return _activeForm;
|
|
}
|
|
|
|
// else first visit to page, so activate first form
|
|
if (_activeForm == null && Forms.Count > 0)
|
|
{
|
|
_activeForm = (Form)Forms[0];
|
|
if(IsPostBack) {
|
|
_activeForm.Activated = true;
|
|
}
|
|
return _activeForm;
|
|
}
|
|
|
|
if (DesignMode)
|
|
{
|
|
return null;
|
|
}
|
|
else
|
|
{
|
|
throw new Exception(
|
|
SR.GetString(SR.MobilePage_AtLeastOneFormInPage));
|
|
}
|
|
}
|
|
set
|
|
{
|
|
Form oldForm = ActiveForm;
|
|
Form newForm = value;
|
|
|
|
_activeForm = newForm;
|
|
_activeFormID = newForm.UniqueID;
|
|
|
|
if (newForm != oldForm)
|
|
{
|
|
oldForm.FireDeactivate(EventArgs.Empty);
|
|
newForm.FireActivate(EventArgs.Empty);
|
|
|
|
// AUI 5577
|
|
newForm.PaginationStateChanged = true;
|
|
}
|
|
else
|
|
{
|
|
newForm.FireActivate(EventArgs.Empty);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.GetForm"]/*' />
|
|
public Form GetForm(String id)
|
|
{
|
|
Form form = FindControl(id) as Form;
|
|
if (form == null)
|
|
{
|
|
throw new ArgumentException(SR.GetString(
|
|
SR.MobilePage_FormNotFound, id));
|
|
}
|
|
return form;
|
|
}
|
|
|
|
|
|
// Perform a "safe" redirect on postback.
|
|
// Abstracts away differences between clients in redirect behavior after a
|
|
// postback. Some clients do a GET to the new URL (treating it as a HTTP 303),
|
|
// others do a POST, with old data. This method ads a query string parameter to
|
|
// the redirection URL, so that the new target page can determine that it's a result
|
|
// of a redirection.
|
|
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.RedirectToMobilePage"]/*' />
|
|
public void RedirectToMobilePage(String url)
|
|
{
|
|
RedirectToMobilePage(url, true);
|
|
}
|
|
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.RedirectToMobilePage1"]/*' />
|
|
public void RedirectToMobilePage(String url, bool endResponse)
|
|
{
|
|
bool queryStringWritten = url.IndexOf("?", StringComparison.Ordinal) != -1 ? true : false;
|
|
if(Adapter.PersistCookielessData)
|
|
{
|
|
IDictionary dictionary = Adapter.CookielessDataDictionary;
|
|
if(dictionary != null)
|
|
{
|
|
foreach(String name in dictionary.Keys)
|
|
{
|
|
if(queryStringWritten)
|
|
{
|
|
url = String.Concat(url, "&");
|
|
}
|
|
else
|
|
{
|
|
url = String.Concat(url, "?");
|
|
queryStringWritten = true;
|
|
}
|
|
url = String.Concat(url, name + "=" + dictionary[name]);
|
|
}
|
|
}
|
|
}
|
|
Response.Redirect(url, endResponse);
|
|
// MobileRedirect.RedirectToUrl(Context, url, endResponse);
|
|
}
|
|
|
|
// Override Page.Validate to do the validation only for mobile
|
|
// validators that are in the current active form. Other validators in
|
|
// Page.Validators collection like aggregated web validators and mobile
|
|
// validators in other forms shouldn't be checked.
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.Validate"]/*' />
|
|
public override void Validate()
|
|
{
|
|
// We can safely remove other validators from the validator list
|
|
// since they shouldn't be checked.
|
|
for (int i = Validators.Count - 1; i >= 0; i--)
|
|
{
|
|
IValidator validator = Validators[i];
|
|
if (!(validator is BaseValidator) ||
|
|
((BaseValidator) validator).Form != ActiveForm)
|
|
{
|
|
Validators.Remove(validator);
|
|
}
|
|
}
|
|
|
|
base.Validate();
|
|
}
|
|
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.VerifyRenderingInServerForm"]/*' />
|
|
[
|
|
EditorBrowsable(EditorBrowsableState.Never),
|
|
]
|
|
public override void VerifyRenderingInServerForm(Control control)
|
|
{
|
|
if (!_isRenderingInForm && !DesignMode)
|
|
{
|
|
throw new Exception(SR.GetString(SR.MobileControl_MustBeInForm,
|
|
control.UniqueID,
|
|
control.GetType().Name));
|
|
}
|
|
}
|
|
|
|
internal void EnterFormRender(Form form)
|
|
{
|
|
_isRenderingInForm = true;
|
|
}
|
|
|
|
internal void ExitFormRender()
|
|
{
|
|
_isRenderingInForm = false;
|
|
}
|
|
|
|
// Override Page.InitOutputCache to add additional VaryByHeader
|
|
// keywords to provide correct caching of page outputs since by
|
|
// default ASP.NET only keys on URL for caching. In the case that
|
|
// different markup devices browse to the same URL, caching key on
|
|
// the URL is not good enough. So in addition to URL, User-Agent
|
|
// header is also added for the key. Also any additional headers can
|
|
// be added by the associated page adapter.
|
|
|
|
private const String UserAgentHeader = "User-Agent";
|
|
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.InitOutputCache"]/*' />
|
|
protected override void InitOutputCache(int duration,
|
|
String varyByHeader,
|
|
String varyByCustom,
|
|
OutputCacheLocation location,
|
|
String varyByParam)
|
|
{
|
|
InitOutputCache(duration, null, varyByHeader, varyByCustom, location, varyByParam);
|
|
}
|
|
|
|
protected override void InitOutputCache(int duration,
|
|
String varyByContentEncoding,
|
|
String varyByHeader,
|
|
String varyByCustom,
|
|
OutputCacheLocation location,
|
|
String varyByParam)
|
|
{
|
|
base.InitOutputCache(duration, varyByContentEncoding, varyByHeader, varyByCustom,
|
|
location, varyByParam);
|
|
Response.Cache.SetCacheability(HttpCacheability.ServerAndPrivate);
|
|
Response.Cache.VaryByHeaders[UserAgentHeader] = true;
|
|
|
|
IList headerList = Adapter.CacheVaryByHeaders;
|
|
if (headerList != null)
|
|
{
|
|
foreach (String header in headerList)
|
|
{
|
|
Response.Cache.VaryByHeaders[header] = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
// HIDDEN FORM VARIABLES
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.HasHiddenVariables"]/*' />
|
|
public bool HasHiddenVariables()
|
|
{
|
|
return _hiddenVariables != null;
|
|
}
|
|
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.HiddenVariables"]/*' />
|
|
[
|
|
Browsable(false),
|
|
Bindable(false),
|
|
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
|
|
]
|
|
public IDictionary HiddenVariables
|
|
{
|
|
get
|
|
{
|
|
if (_hiddenVariables == null)
|
|
{
|
|
_hiddenVariables = new Hashtable();
|
|
}
|
|
return _hiddenVariables;
|
|
}
|
|
}
|
|
|
|
protected override void OnPreInit(EventArgs e) {
|
|
_afterPreInit = true;
|
|
base.OnPreInit(e);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
// DEVICE-INDEPENDENT POSTBACK
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
// The functionality required here is to trap and handle
|
|
// postback events at the page level (delegating to the adapter),
|
|
// rather than expecting a control to handle it.
|
|
// This has to be done in DeterminePostBackMode, because there isn't
|
|
// anything else overrideable.
|
|
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.DeterminePostBackMode"]/*' />
|
|
protected override NameValueCollection DeterminePostBackMode()
|
|
{
|
|
// Ignore the transfer case.
|
|
if (Context.Handler != this)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
// Let the specific adapter to manipulate the base collection if
|
|
// necessary.
|
|
NameValueCollection collection =
|
|
Adapter.DeterminePostBackMode(Context.Request,
|
|
postEventSourceID,
|
|
postEventArgumentID,
|
|
base.DeterminePostBackMode());
|
|
|
|
// Get hidden variables out of the collection.
|
|
if (collection != null)
|
|
{
|
|
// If the page was posted due to a redirect started by calling
|
|
// RedirectToMobilePage, then ignore the postback. For details,
|
|
// see RedirectToMobilePage method elsewhere in this class.
|
|
|
|
if (Page.Request.QueryString[MobileRedirect.QueryStringVariable] == MobileRedirect.QueryStringValue)
|
|
{
|
|
collection = null;
|
|
}
|
|
else
|
|
{
|
|
int count = collection.Count;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
String key = collection.GetKey(i);
|
|
if (key.StartsWith(HiddenVariablePrefix, StringComparison.Ordinal))
|
|
{
|
|
HiddenVariables[key.Substring(HiddenVariablePrefix.Length)] = collection[i];
|
|
}
|
|
}
|
|
|
|
String eventSource = collection[postEventSourceID];
|
|
if (eventSource != null)
|
|
{
|
|
// Page level event
|
|
RaisePagePostBackEvent(eventSource, collection[postEventArgumentID]);
|
|
_eventSource = eventSource;
|
|
}
|
|
}
|
|
}
|
|
|
|
_requestValueCollection = collection;
|
|
|
|
/* Obsolete.
|
|
// If doing a postback, don't allow redirections.
|
|
|
|
if (collection != null)
|
|
{
|
|
MobileRedirect.DisallowRedirection(Context);
|
|
}
|
|
*/
|
|
|
|
return collection;
|
|
}
|
|
|
|
private void RaisePagePostBackEvent(String eventSource, String eventArgument)
|
|
{
|
|
// Let the adapter handle it.
|
|
Adapter.HandlePagePostBackEvent(eventSource, eventArgument);
|
|
}
|
|
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.RaisePostBackEvent"]/*' />
|
|
protected override void RaisePostBackEvent(IPostBackEventHandler sourceControl, String eventArgument)
|
|
{
|
|
if (eventArgument == null && sourceControl is Form)
|
|
{
|
|
// This is really a default event sent by an HTML browser. Try to find
|
|
// the default event handler from the active form, and call it.
|
|
|
|
Form activeForm = ActiveForm;
|
|
if (activeForm != null)
|
|
{
|
|
IPostBackEventHandler defaultHandler = activeForm.DefaultEventHandler;
|
|
if (defaultHandler != null)
|
|
{
|
|
base.RaisePostBackEvent(defaultHandler, null);
|
|
}
|
|
}
|
|
|
|
// Otherwise, eat the event - there's no one to send it to, and the form
|
|
// can't use it.
|
|
}
|
|
else
|
|
{
|
|
base.RaisePostBackEvent(sourceControl, eventArgument);
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
// BEGIN ADAPTER PLUMBING
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.OnInit"]/*' />
|
|
protected override void OnInit(EventArgs e)
|
|
{
|
|
#if ICECAP
|
|
IceCapAPI.StartProfile(IceCapAPI.PROFILE_THREADLEVEL, IceCapAPI.PROFILE_CURRENTID);
|
|
#endif
|
|
OnDeviceCustomize(new EventArgs());
|
|
|
|
// Accessing Request throws exception at designtime
|
|
if(!DesignMode && Request.Headers["__vs_debug"] != null)
|
|
{
|
|
_debugMode = true;
|
|
}
|
|
|
|
// ASP.NET requires the following method to be called to have
|
|
// ViewState calculated for the page.
|
|
RegisterViewStateHandler();
|
|
|
|
Adapter.OnInit(e);
|
|
base.OnInit(e);
|
|
}
|
|
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.OnLoad"]/*' />
|
|
protected override void OnLoad(EventArgs e)
|
|
{
|
|
// AUI 865
|
|
|
|
if (_eventSource != null && _eventSource.Length > 0)
|
|
{
|
|
MobileControl control = FindControl (_eventSource) as MobileControl;
|
|
if (control != null && (control is IPostBackEventHandler))
|
|
{
|
|
_activeForm = control.Form;
|
|
_activeForm.Activated = true;
|
|
}
|
|
}
|
|
|
|
Adapter.OnLoad(e);
|
|
base.OnLoad(e);
|
|
|
|
if (!IsPostBack)
|
|
{
|
|
ActiveForm.FireActivate(EventArgs.Empty);
|
|
}
|
|
}
|
|
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.OnPreRender"]/*' />
|
|
protected override void OnPreRender(EventArgs e)
|
|
{
|
|
Adapter.OnPreRender(e);
|
|
base.OnPreRender(e);
|
|
}
|
|
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.Render"]/*' />
|
|
protected override void Render(HtmlTextWriter writer)
|
|
{
|
|
#if TRACE
|
|
DumpSessionViewState();
|
|
#endif
|
|
|
|
Adapter.Render(writer);
|
|
}
|
|
|
|
#if TRACE
|
|
void DumpSessionViewState()
|
|
{
|
|
ArrayList arr;
|
|
_sessionViewState.Dump(this, out arr);
|
|
StringBuilder sb = new StringBuilder();
|
|
foreach (String s in arr)
|
|
{
|
|
sb.Append(s);
|
|
sb.Append("\r\n");
|
|
}
|
|
Trace.Write("SessionViewState", sb.ToString());
|
|
}
|
|
#endif
|
|
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.OnUnload"]/*' />
|
|
protected override void OnUnload(EventArgs e)
|
|
{
|
|
base.OnUnload(e);
|
|
Adapter.OnUnload(e);
|
|
#if ICECAP
|
|
IceCapAPI.StopProfile(IceCapAPI.PROFILE_THREADLEVEL, IceCapAPI.PROFILE_CURRENTID);
|
|
#endif
|
|
}
|
|
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.OnDeviceCustomize"]/*' />
|
|
protected virtual void OnDeviceCustomize(EventArgs e)
|
|
{
|
|
}
|
|
|
|
internal bool PrivateViewStateLoaded
|
|
{
|
|
get
|
|
{
|
|
return _privateViewStateLoaded;
|
|
}
|
|
}
|
|
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.GetPrivateViewState"]/*' />
|
|
public Object GetPrivateViewState(MobileControl ctl)
|
|
{
|
|
return _privateViewState == null ?
|
|
null :
|
|
_privateViewState[ctl.UniqueID];
|
|
}
|
|
|
|
private SessionViewState _sessionViewState = new SessionViewState();
|
|
private static readonly String _controlsRequiringPostBackKey = ".PBC";
|
|
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.LoadPageStateFromPersistenceMedium"]/*' />
|
|
protected override Object LoadPageStateFromPersistenceMedium()
|
|
{
|
|
Object state = null;
|
|
|
|
String clientViewStateString = _requestValueCollection[ViewStateID];
|
|
if (clientViewStateString != null)
|
|
{
|
|
try {
|
|
_privateViewState = StateFormatter.Deserialize(clientViewStateString) as Hashtable;
|
|
}
|
|
catch (Exception e) {
|
|
if (IsViewStateException(e)) {
|
|
_privateViewState = null;
|
|
|
|
// DevDiv #461378: Suppress validation errors for cross-page postbacks.
|
|
// This is a much simplified form of the check in Page.LoadPageStateFromPersistenceMedium.
|
|
if (Context != null && TraceEnabled) {
|
|
Trace.Write("aspx.page", "Ignoring page state", e);
|
|
}
|
|
}
|
|
else {
|
|
// we shouldn't ---- this exception; let the app error handler take care of it
|
|
throw;
|
|
}
|
|
}
|
|
|
|
if (_privateViewState != null)
|
|
{
|
|
Pair pair = _privateViewState[PageClientViewStateKey] as Pair;
|
|
if (pair != null)
|
|
{
|
|
_activeFormID = (String) pair.First;
|
|
|
|
Pair id = (Pair) pair.Second;
|
|
if (id != null)
|
|
{
|
|
_sessionViewState.Load(this, id);
|
|
state = _sessionViewState.ViewState;
|
|
if(state == null)
|
|
{
|
|
OnViewStateExpire(EventArgs.Empty);
|
|
}
|
|
else
|
|
{
|
|
Object[] arrState = state as Object[];
|
|
if (arrState != null)
|
|
{
|
|
_privateViewState = (Hashtable) arrState[1];
|
|
state = arrState[0];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
_privateViewState.Remove(PageClientViewStateKey);
|
|
|
|
// If the page had no view state, but had controls requiring postback,
|
|
// this information was saved in client view state.
|
|
|
|
Object controlsRequiringPostBack =
|
|
_privateViewState[_controlsRequiringPostBackKey];
|
|
if (controlsRequiringPostBack != null)
|
|
{
|
|
state = new Pair(null,
|
|
new Triplet(GetTypeHashCode().ToString(CultureInfo.InvariantCulture),
|
|
null,
|
|
controlsRequiringPostBack));
|
|
_privateViewState.Remove(_controlsRequiringPostBackKey);
|
|
}
|
|
|
|
// Apply whatever private view state can be applied now.
|
|
|
|
foreach (DictionaryEntry entry in _privateViewState)
|
|
{
|
|
if (entry.Value != null)
|
|
{
|
|
MobileControl ctl = FindControl((String)entry.Key) as MobileControl;
|
|
if (ctl != null)
|
|
{
|
|
ctl.LoadPrivateViewStateInternal(entry.Value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
_privateViewStateLoaded = true;
|
|
|
|
if (state == null)
|
|
{
|
|
// Give framework back an empty page view state
|
|
state = new Pair(null, new Triplet(GetTypeHashCode().ToString(CultureInfo.InvariantCulture), null, null));
|
|
}
|
|
return state;
|
|
}
|
|
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.OnViewStateExpire"]/*' />
|
|
protected virtual void OnViewStateExpire(EventArgs e)
|
|
{
|
|
throw new Exception(SR.GetString(SR.SessionViewState_ExpiredOrCookieless));
|
|
}
|
|
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.SavePageStateToPersistenceMedium"]/*' />
|
|
protected override void SavePageStateToPersistenceMedium(Object view)
|
|
{
|
|
Object viewState = null;
|
|
Object privateViewState = null;
|
|
Pair serverViewStateID;
|
|
|
|
SavePrivateViewStateRecursive(this);
|
|
|
|
if (!CheckEmptyViewState(view))
|
|
{
|
|
viewState = view;
|
|
}
|
|
|
|
if (Device.RequiresOutputOptimization &&
|
|
_clientViewState != null &&
|
|
_clientViewState.Count > 0 &&
|
|
EnableViewState)
|
|
{
|
|
// Here we take over the content in _clientViewState. It
|
|
// should be reset to null. Then subsequently any info added
|
|
// will be set to the client accordingly.
|
|
privateViewState = _clientViewState;
|
|
_clientViewState = null;
|
|
_clientViewStateString = null;
|
|
}
|
|
|
|
// Are we being asked to save an empty view state?
|
|
|
|
if (viewState == null && privateViewState == null)
|
|
{
|
|
serverViewStateID = null;
|
|
}
|
|
else
|
|
{
|
|
// Our view state is dependent on session state. So, make sure session
|
|
// state is available.
|
|
|
|
if (!(this is IRequiresSessionState) || (this is IReadOnlySessionState))
|
|
{
|
|
throw new Exception(SR.GetString(SR.MobilePage_RequiresSessionState));
|
|
}
|
|
|
|
_sessionViewState.ViewState = (privateViewState == null) ?
|
|
viewState : new Object[2] { viewState, privateViewState };
|
|
|
|
serverViewStateID = _sessionViewState.Save(this);
|
|
if (Device.PreferredRenderingMime != "text/vnd.wap.wml" && Device["cachesAllResponsesWithExpires"] != "true")
|
|
{
|
|
if (String.Compare(Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase) == 0)
|
|
{
|
|
Response.Expires = 0;
|
|
}
|
|
else
|
|
{
|
|
Response.Expires = HttpContext.Current.Session.Timeout;
|
|
}
|
|
}
|
|
}
|
|
|
|
String activeFormID = ActiveForm == Forms[0] ? null : ActiveForm.UniqueID;
|
|
|
|
// Optimize what is written out.
|
|
|
|
if (activeFormID != null || serverViewStateID != null)
|
|
{
|
|
AddClientViewState(PageClientViewStateKey,
|
|
new Pair(activeFormID, serverViewStateID));
|
|
}
|
|
}
|
|
|
|
// NOTE: Make sure this stays in sync with Page.PageRegisteredControlsThatRequirePostBackKey
|
|
private const string PageRegisteredControlsThatRequirePostBackKey = "__ControlsRequirePostBackKey__";
|
|
private bool CheckEmptyViewState(Object viewState)
|
|
{
|
|
Pair pair = viewState as Pair;
|
|
if (pair == null) {
|
|
return false;
|
|
}
|
|
|
|
Pair allViewState = pair.Second as Pair;
|
|
if (allViewState == null || allViewState.Second != null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
IDictionary controlStates = pair.First as IDictionary;
|
|
if (controlStates != null)
|
|
{
|
|
// If the only thing in control is the set of controls
|
|
// requiring postback, then save the information in client-side
|
|
// state instead.
|
|
if (controlStates.Count == 1 &&
|
|
controlStates[PageRegisteredControlsThatRequirePostBackKey] != null) {
|
|
AddClientViewState(_controlsRequiringPostBackKey, controlStates[PageRegisteredControlsThatRequirePostBackKey]);
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private void SavePrivateViewStateRecursive(Control control)
|
|
{
|
|
if (control.HasControls())
|
|
{
|
|
IEnumerator e = control.Controls.GetEnumerator();
|
|
while (e.MoveNext())
|
|
{
|
|
MobileControl c = e.Current as MobileControl;
|
|
if (c != null)
|
|
{
|
|
c.SavePrivateViewStateInternal();
|
|
SavePrivateViewStateRecursive(c);
|
|
}
|
|
else
|
|
{
|
|
SavePrivateViewStateRecursive((Control)e.Current);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.LoadViewState"]/*' />
|
|
protected override void LoadViewState(Object savedState)
|
|
{
|
|
if (savedState != null)
|
|
{
|
|
Object[] state = (Object[])savedState;
|
|
if (state.Length > 0)
|
|
{
|
|
base.LoadViewState(state[0]);
|
|
if (state.Length > 1)
|
|
{
|
|
Adapter.LoadAdapterState(state[1]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.SaveViewState"]/*' />
|
|
protected override Object SaveViewState()
|
|
{
|
|
Object baseState = base.SaveViewState();
|
|
Object adapterState = Adapter.SaveAdapterState();
|
|
if (adapterState == null)
|
|
{
|
|
return (baseState == null) ? null : new Object[1] { baseState };
|
|
}
|
|
else
|
|
{
|
|
return new Object[2] { baseState, adapterState };
|
|
}
|
|
}
|
|
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.OnError"]/*' />
|
|
protected override void OnError(EventArgs e)
|
|
{
|
|
// Let the base class deal with it. A user-written error handler
|
|
// may catch and handle it.
|
|
|
|
base.OnError(e);
|
|
|
|
Exception error = Server.GetLastError();
|
|
if (error == null || error is System.Threading.ThreadAbortException)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!_debugMode)
|
|
{
|
|
if(!HttpContext.Current.IsCustomErrorEnabled)
|
|
{
|
|
Response.Clear();
|
|
if (Adapter.HandleError(error, (HtmlTextWriter)CreateHtmlTextWriter(Response.Output)))
|
|
{
|
|
Server.ClearError();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.CreateMarkupTextWriter"]/*' />
|
|
protected override HtmlTextWriter CreateHtmlTextWriter(TextWriter writer)
|
|
{
|
|
HtmlTextWriter htmlwriter = Adapter.CreateTextWriter(writer);
|
|
if (htmlwriter == null)
|
|
{
|
|
htmlwriter = base.CreateHtmlTextWriter(writer);
|
|
}
|
|
return htmlwriter;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
// END ADAPTER PLUMBING
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.AddedControl"]/*' />
|
|
protected override void AddedControl(Control control, int index)
|
|
{
|
|
if (control is Form || control is UserControl)
|
|
{
|
|
_forms = null;
|
|
}
|
|
base.AddedControl(control, index);
|
|
}
|
|
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.RemovedControl"]/*' />
|
|
protected override void RemovedControl(Control control)
|
|
{
|
|
if (control is Form || control is UserControl)
|
|
{
|
|
_forms = null;
|
|
}
|
|
base.RemovedControl(control);
|
|
}
|
|
|
|
private byte[] GetMacKeyModifier()
|
|
{
|
|
//NOTE: duplicate of the version in objectstateformatter.cs, keep in sync
|
|
|
|
// Use the page's directory and class name as part of the key (ASURT 64044)
|
|
// We need to make sure that the hash is case insensitive, since the file system
|
|
// is, and strange view state errors could otherwise happen (ASURT 128657)
|
|
|
|
int pageHashCode = StringComparer.InvariantCultureIgnoreCase.GetHashCode(
|
|
TemplateSourceDirectory);
|
|
pageHashCode += StringComparer.InvariantCultureIgnoreCase.GetHashCode(GetType().Name);
|
|
|
|
|
|
byte[] macKeyModifier;
|
|
|
|
if (ViewStateUserKey != null) {
|
|
// Modify the key with the ViewStateUserKey, if any (ASURT 126375)
|
|
int count = Encoding.Unicode.GetByteCount(ViewStateUserKey);
|
|
macKeyModifier = new byte[count + 4];
|
|
Encoding.Unicode.GetBytes(ViewStateUserKey,0, ViewStateUserKey.Length, macKeyModifier, 4);
|
|
}
|
|
else {
|
|
macKeyModifier = new byte[4];
|
|
}
|
|
|
|
macKeyModifier[0] = (byte) pageHashCode;
|
|
macKeyModifier[1] = (byte) (pageHashCode >> 8);
|
|
macKeyModifier[2] = (byte) (pageHashCode >> 16);
|
|
macKeyModifier[3] = (byte) (pageHashCode >> 24);
|
|
|
|
return macKeyModifier;
|
|
}
|
|
|
|
private LosFormatter _stateFormatter;
|
|
private LosFormatter StateFormatter
|
|
{
|
|
get
|
|
{
|
|
if (_stateFormatter == null)
|
|
{
|
|
if(!EnableViewStateMac)
|
|
{
|
|
_stateFormatter = new LosFormatter();
|
|
}
|
|
else
|
|
{
|
|
_stateFormatter = new LosFormatter(true, GetMacKeyModifier());
|
|
}
|
|
}
|
|
return _stateFormatter;
|
|
}
|
|
}
|
|
|
|
private String CreateQueryStringTextFromCollection(
|
|
NameValueCollection collection)
|
|
{
|
|
const String systemPostFieldPrefix = "__";
|
|
StringBuilder stringBuilder = new StringBuilder();
|
|
|
|
if (collection == null)
|
|
{
|
|
return String.Empty;
|
|
}
|
|
|
|
for (int i = 0; i < collection.Count; i++)
|
|
{
|
|
String name = collection.GetKey(i);
|
|
|
|
if (name != null)
|
|
{
|
|
if (name.StartsWith(systemPostFieldPrefix, StringComparison.Ordinal))
|
|
{
|
|
// Remove well-known postback elements
|
|
if (name == ViewStateID ||
|
|
name == postEventSourceID ||
|
|
name == postEventArgumentID ||
|
|
name == Constants.EventSourceID ||
|
|
name == Constants.EventArgumentID ||
|
|
name.StartsWith(HiddenVariablePrefix, StringComparison.Ordinal))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
String controlId = name;
|
|
if (controlId.EndsWith(".x", StringComparison.Ordinal) ||
|
|
controlId.EndsWith(".y", StringComparison.Ordinal))
|
|
{
|
|
// Remove the .x and .y coordinates if the control is
|
|
// an image button
|
|
controlId = controlId.Substring(0, name.Length - 2);
|
|
}
|
|
|
|
if (FindControl(controlId) != null)
|
|
{
|
|
// Remove control id/value pairs if present
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
AppendParameters(collection, name, stringBuilder);
|
|
}
|
|
|
|
return stringBuilder.ToString();
|
|
}
|
|
|
|
private void AppendParameters(NameValueCollection sourceCollection,
|
|
String sourceKey,
|
|
StringBuilder stringBuilder)
|
|
{
|
|
String [] values = sourceCollection.GetValues(sourceKey);
|
|
foreach (String value in values)
|
|
{
|
|
if (stringBuilder.Length != 0)
|
|
{
|
|
stringBuilder.Append('&');
|
|
}
|
|
|
|
if (sourceKey == null)
|
|
{
|
|
// name can be null if there is a query name without equal
|
|
// sign appended
|
|
stringBuilder.Append(Server.UrlEncode(value));
|
|
}
|
|
else
|
|
{
|
|
stringBuilder.Append(Server.UrlEncode(sourceKey));
|
|
stringBuilder.Append('=');
|
|
stringBuilder.Append(Server.UrlEncode(value));
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <include file='doc\MobilePage.uex' path='docs/doc[@for="MobilePage.RenderControl"]/*' />
|
|
public override void RenderControl(HtmlTextWriter writer) {
|
|
RenderControl(writer, null); // Use legacy adapter, not V2 adapter.
|
|
}
|
|
|
|
// Similar to the logic in ViewStateException.cs, but we can only check for
|
|
// ViewState exceptions in general, not MAC-specific exceptions.
|
|
private static bool IsViewStateException(Exception e) {
|
|
for (; e != null; e = e.InnerException) {
|
|
if (e is ViewStateException) { return true; }
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
}
|