//------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//------------------------------------------------------------------------------
using System;
using System.Security.Permissions;
using System.Web;
using System.Web.Caching;
using System.Web.Mobile;
using System.Web.UI.MobileControls;
using System.Web.UI.MobileControls.Adapters;
using System.IO;
using System.Text;
using System.Drawing;
using System.Collections;
using System.Diagnostics;
using System.Collections.Specialized;
using System.Globalization;
using System.Web.SessionState;
#if COMPILING_FOR_SHIPPED_SOURCE
namespace System.Web.UI.MobileControls.ShippedAdapterSource.XhtmlAdapters
#else
namespace System.Web.UI.MobileControls.Adapters.XhtmlAdapters
#endif
{
///
[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 XhtmlMobileTextWriter : MobileTextWriter {
private int _styleCount = 0;
private static readonly Style _defaultStyle = new Style ();
private static readonly XhtmlStyleClass _defaultStyleClass = new XhtmlStyleClass(_defaultStyle, XhtmlConstants.All);
// For certain WML 2.0 devices, we have to render a WML onevent element.
// Therefore we need two markup builders -a preWmlOnEventLocation and a postWmlOnEventLocation.
// Calling MarkWmlOnEventLocation switches the markup string builder from the pre to the post
// builder. Everything is concatenated in order in EndCachedRendering / WriteCachedMarkup.
private XhtmlStyleClass _bodyStyle = null;
private TextWriter _cachedInnerWriter = null;
private String _cacheKey;
private String _cachedMarkup;
private string _cachedEndTag = null;
private bool _cacheKeyValid = false;
private bool _cachingRendering;
private string _customBodyStyles = null;
private IDictionary _doctypeDeclarations = new Hashtable ();
private bool _isStyleSheetEmpty = true;
private ArrayList _orderedStyleClassKeys = new ArrayList ();
private bool _pendingBreak = false;
private Stack _physicalCssClasses = new Stack();
private StringBuilder _preWmlOnEventMarkupBuilder = new StringBuilder();
private StringBuilder _postWmlOnEventMarkupBuilder = new StringBuilder();
private string _sessionKey;
private bool _sessionKeyValid = false;
private IDictionary _styleHash = new Hashtable ();
private bool _supportsNoWrapStyle = true;
private bool _suppressNewLines = false;
private ArrayList _wmlOnEnterForwardVarNames = new ArrayList();
private bool _useDivsForBreaks = false;
///
public XhtmlMobileTextWriter (TextWriter writer, MobileCapabilities device) : base(writer, device) {
_doctypeDeclarations[Doctype.XhtmlBasic] = "";
_doctypeDeclarations[Doctype.XhtmlMobileProfile] = "";
_doctypeDeclarations[Doctype.Wml20] = "";
}
internal String CachedEndTag {
get {
return _cachedEndTag;
}
}
///
public virtual String CacheKey {
get {
// Exception is for extensibility scenarios.
if (!_cacheKeyValid) {
throw new Exception (SR.GetString(
SR.XhtmlMobileTextWriter_CacheKeyNotSet));
}
return _cacheKey;
}
}
#if UNUSED_CODE
internal bool CssClassOnStack {
get {
return _physicalCssClasses.Count > 0;
}
}
#endif
// Saves a couple of lines of code in most cases.
internal XhtmlStyleClass CurrentStyleClass {
get {
if (_styleStack.Count > 0) {
StylePair pair = (StylePair) _styleStack.Peek ();
return pair.Class;
}
else {
// If the style stack is empty, the current style is default.
return _defaultStyleClass;
}
}
}
///
public String CustomBodyStyles {
get {
return _customBodyStyles;
}
set {
_isStyleSheetEmpty = false;
_customBodyStyles = value;
}
}
///
public virtual String SessionKey {
// Exception is for extensibility scenarios.
get {
if (!_sessionKeyValid) {
throw new Exception (SR.GetString(
SR.XhtmlMobileTextWriter_SessionKeyNotSet));
}
return _sessionKey;
}
}
public virtual bool SupportsNoWrapStyle {
get {
return _supportsNoWrapStyle;
}
set {
_supportsNoWrapStyle = value;
}
}
///
public bool SuppressNewLine {
get {
return _suppressNewLines;
}
set {
_suppressNewLines = value;
}
}
///
public bool UseDivsForBreaks {
get {
return _useDivsForBreaks;
}
set {
_useDivsForBreaks = value;
}
}
// Add a variable name to clear.
///
public virtual void AddOnEnterForwardSetVar(String var) {
AddOnEnterForwardSetVar(var, String.Empty);
}
///
public virtual void AddOnEnterForwardSetVar(String var, String value) {
_wmlOnEnterForwardVarNames.Add(new String[2]{var, value});
}
///
public virtual void BeginCachedRendering () {
_cachedInnerWriter = InnerWriter;
InnerWriter = new StringWriter (_preWmlOnEventMarkupBuilder, CultureInfo.InvariantCulture);
_cachingRendering = true;
}
internal void ClearCachedEndTag() {
_cachedEndTag = null;
}
///
public virtual void ClearPendingBreak() {
_pendingBreak = false;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Optimization for physical stylesheets. We only write the cssClass attribute if it differs
// (case-sensitive) from the current cssClass. The current cssClass is kept in a stack.
////////////////////////////////////////////////////////////////////////////////////////////////////
internal bool DiffersFromCurrentPhysicalCssClass(String cssClass) {
// Case sensitive comparison.
return _physicalCssClasses.Count == 0 ||
String.Compare(cssClass, (String)_physicalCssClasses.Peek(), StringComparison.Ordinal) != 0;
}
private void EncodeAttributeValue(String value, StringBuilder encodedValue) {
StringWriter writer = new StringWriter(encodedValue, CultureInfo.InvariantCulture);
HttpUtility.HtmlEncode(value, writer);
}
// Expose base method to xhtml control adapter.
internal string EncodeUrlInternal(String url) {
return base.EncodeUrl(url);
}
///
public virtual void EndCachedRendering () {
StringBuilder cachedMarkupBuilder = new StringBuilder();
cachedMarkupBuilder.Append(_preWmlOnEventMarkupBuilder.ToString());
cachedMarkupBuilder.Append(GetWmlOnEventSubtree());
cachedMarkupBuilder.Append(_postWmlOnEventMarkupBuilder.ToString());
_cachedMarkup = cachedMarkupBuilder.ToString ();
InnerWriter = _cachedInnerWriter;
_cachingRendering = false;
}
private XhtmlStyleClass EnsureXhtmlStyleClassInHashtable (XhtmlStyleClass styleClass) {
if (styleClass.Filter == StyleFilter.None) {
return CurrentStyleClass;
}
// We hash the style classes by the class definition.
String classKey = styleClass.GetClassDefinition ();
XhtmlStyleClass existingClass = (XhtmlStyleClass) _styleHash [classKey];
string className = existingClass == null ?
"s" + _styleCount++ : existingClass.StyleClassName;
if (existingClass == null) {
// Used to retrieve style classes in order from the hash table.
_orderedStyleClassKeys.Add (classKey);
styleClass.StyleClassName = className;
_styleHash [classKey] = styleClass;
_isStyleSheetEmpty = false;
}
return existingClass == null ? styleClass : existingClass;
}
///
public override void EnterFormat(Style style) {
StyleFilter filter = CurrentStyleClass.GetFilter(style);
EnterStyleInternal(new XhtmlFormatStyleClass(style, filter), "span", null /* no additional attributes */);
}
///
public override void EnterLayout (Style style) {
StyleFilter filter = CurrentStyleClass.GetFilter(style);
if (!SupportsNoWrapStyle) {
filter &= ~StyleFilter.Wrapping;
}
EnterStyleInternal (new XhtmlLayoutStyleClass(style, filter), "div", null /* no additional attributes */, true /* force tag to be written */);
}
// Hiding inherited member works because dynamic type is same as static type.
///
public new void EnterStyle (Style style) {
StyleFilter filter = CurrentStyleClass.GetFilter(style);
if (!SupportsNoWrapStyle) {
filter &= ~StyleFilter.Wrapping;
}
// We prefer span to div because span is inline.
if ((filter & XhtmlConstants.Layout) == 0) {
EnterStyleInternal (style, "span", filter);
}
else {
EnterStyleInternal (new XhtmlStyleClass(style, filter), "div", null /*additional attributes */, true /* force tag to be written */);
}
}
// This internal overload can be used to enter style by setting the class element
// on a caller-specified tag, such as
or
.
internal void EnterStyle(Style style, String styleTag) {
StyleFilter filter = CurrentStyleClass.GetFilter(style);
if (filter == StyleFilter.None) {
WriteFullBeginTag(styleTag);
_styleStack.Push(new StylePair(styleTag, style, StyleFilter.None));
return;
}
EnterStyleInternal (style, styleTag, filter);
}
// This internal overload can be used to enter style by setting the class element
// on a caller-specified tag, such as or