|
//------------------------------------------------------------------------------
// <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;
}
}
}
|