//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // //------------------------------------------------------------------------------ using System; using System.Diagnostics; using System.Collections; using System.Security.Permissions; using System.Text; using System.Web; using System.Web.Mobile; using System.Web.UI; using System.Web.UI.MobileControls; using System.Web.UI.MobileControls.Adapters; using System.Configuration; using System.Globalization; using System.Web.Security; #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 XhtmlControlAdapter : ControlAdapter { private bool _physicalCssClassPushed = false; private bool IsRooted(String basepath) { return(basepath == null || basepath.Length == 0 || basepath[0] == '/' || basepath[0] == '\\'); } private bool IsRelativeUrl(string url) { // If it has a protocol, it's not relative if (url.IndexOf(":", StringComparison.Ordinal) != -1) return false; return !IsRooted(url); } /// protected XhtmlPageAdapter PageAdapter { get { return Page.Adapter as XhtmlPageAdapter; } } /// public override void Render(HtmlTextWriter writer) { Render((XhtmlMobileTextWriter)writer); } /// public virtual void Render(XhtmlMobileTextWriter writer) { RenderChildren(writer); } /// protected virtual void RenderPostBackEventAsAnchor ( XhtmlMobileTextWriter writer, String argument, String linkText) { RenderPostBackEventAsAnchor(writer, argument, linkText, null /* accessKey */, null /* style */, null /*cssClass */); } // For convenience in extensibility -not used internally. The overload with style and cssClass args is // to be preferred. See ASURT 144034. /// protected virtual void RenderPostBackEventAsAnchor ( XhtmlMobileTextWriter writer, String argument, String linkText, String accessKey) { RenderPostBackEventAsAnchor(writer, argument, linkText, accessKey, null /* style */, null /* cssClass */); } // For Style/CssClass args, see ASURT 144034 /// protected virtual void RenderPostBackEventAsAnchor ( XhtmlMobileTextWriter writer, String argument, String linkText, String accessKey, Style style, String cssClass) { writer.WriteBeginTag("a"); writer.Write(" href=\""); PageAdapter.RenderUrlPostBackEvent(writer, Control.UniqueID /* target */, argument); writer.Write("\" "); if (accessKey != null && accessKey.Length > 0) { writer.WriteAttribute("accesskey", accessKey); } if ((String)Device[XhtmlConstants.RequiresXhtmlCssSuppression] != "true") { if (CssLocation != StyleSheetLocation.PhysicalFile) { String className = writer.GetCssFormatClassName(style); if (className != null) { writer.WriteAttribute ("class", className); } } else if (cssClass != null && cssClass.Length > 0) { writer.WriteAttribute ("class", cssClass, true /* encode */); } } writer.Write(">"); writer.WriteEncodedText(linkText); writer.WriteEndTag("a"); } /// protected virtual void ConditionalSetPendingBreakAfterInline (XhtmlMobileTextWriter writer) { if ((String)Device[XhtmlConstants.BreaksOnInlineElements] == "true") { return; } ConditionalSetPendingBreak(writer); } /// protected virtual void ConditionalSetPendingBreak (XhtmlMobileTextWriter writer) { MobileControl mobileControl = Control as MobileControl; if (mobileControl != null && mobileControl.BreakAfter) { writer.SetPendingBreak (); } } // Overloads for complex controls like list that compose list items. For these controls, the accessKey attribute // for each link may be different from the accessKey attribute for the control. /// protected virtual void RenderBeginLink(XhtmlMobileTextWriter writer, String target, String accessKey, Style style, String cssClass) { RenderBeginLink(writer, target, accessKey, style, cssClass, null /* title */); } /// protected virtual void RenderBeginLink(XhtmlMobileTextWriter writer, String target, String accessKey, Style style, String cssClass, String title) { writer.WriteBeginTag("a"); writer.Write(" href=\""); RenderHrefValue (writer, target); writer.Write("\""); if (accessKey != null && accessKey.Length > 0) { writer.WriteAttribute("accesskey", accessKey, true); } if (CssLocation != StyleSheetLocation.PhysicalFile) { String className = writer.GetCssFormatClassName(style); if (className != null) { writer.WriteAttribute ("class", className); } } else if (cssClass != null && cssClass.Length > 0) { writer.WriteAttribute ("class", cssClass, true /* encode */); } if (title != null && title.Length > 0) { writer.WriteAttribute("title", title, true /* encode */); } writer.WriteLine(">"); } /// protected virtual void RenderBeginLink(XhtmlMobileTextWriter writer, String target) { String attributeValue = ((IAttributeAccessor)Control).GetAttribute(XhtmlConstants.AccessKeyCustomAttribute); RenderBeginLink(writer, target, attributeValue, null, null); } // Writes the href value for RenderBeginLink, depending on whether the target is a new form on the // current page or a standard url (e.g., a new page). private void RenderHrefValue (XhtmlMobileTextWriter writer, String target) { bool appendCookielessDataDictionary = PageAdapter.PersistCookielessData && !target.StartsWith("http:", StringComparison.Ordinal) && !target.StartsWith("https:", StringComparison.Ordinal); bool queryStringWritten = false; // ASURT 144021 if (target == null || target.Length == 0) { target = Page.Response.ApplyAppPathModifier(Control.TemplateSourceDirectory); } if (target.StartsWith(Constants.FormIDPrefix, StringComparison.Ordinal)) { RenderFormNavigationHrefValue (writer, target); appendCookielessDataDictionary = false; } else { // For page adapter Control = null. if (Control != null) { target = Control.ResolveUrl(target); } // ASURT 147179 if ((String)Device["requiresAbsolutePostbackUrl"] == "true" && IsRelativeUrl(target)) { String templateSourceDirectory = Page.TemplateSourceDirectory; String prefix = writer.EncodeUrlInternal(Page.Response.ApplyAppPathModifier(Page.TemplateSourceDirectory)); if (prefix[prefix.Length - 1] != '/') { prefix = prefix + '/'; } target = prefix + target; } if ((String)Device[XhtmlConstants.SupportsUrlAttributeEncoding] != "false") { writer.WriteEncodedText (target); } else { writer.Write (target); } queryStringWritten = target.IndexOf ('?') != -1; } if (appendCookielessDataDictionary) { RenderCookielessDataDictionaryInQueryString (writer, queryStringWritten); } } // Writes an href postback event with semantics of form navigation (activation). private void RenderFormNavigationHrefValue (XhtmlMobileTextWriter writer, String target) { String prefix = Constants.FormIDPrefix; Debug.Assert (target.StartsWith (prefix, StringComparison.Ordinal)); String name = target.Substring(prefix.Length); Form form = Control.ResolveFormReference(name); // EventTarget = Control, EventArg = Form has semantics navigate to (activate) the form. PageAdapter.RenderUrlPostBackEvent (writer, Control.UniqueID /* target */, form.UniqueID /* argument */); } private void RenderCookielessDataDictionaryInQueryString (XhtmlMobileTextWriter writer, bool queryStringWritten) { IDictionary dictionary = PageAdapter.CookielessDataDictionary; if (dictionary != null) { foreach (String name in dictionary.Keys) { if (queryStringWritten) { String amp = (String)Device[XhtmlConstants.SupportsUrlAttributeEncoding] != "false" ? "&" : "&"; writer.Write(amp); } else { writer.Write ('?'); queryStringWritten = true; } writer.Write (name); writer.Write ('='); writer.Write (dictionary[name]); } } } /// protected virtual void RenderEndLink(XhtmlMobileTextWriter writer) { writer.WriteEndTag("a"); } ///////////////////////////////////////////////////////////////////////// // SECONDARY UI SUPPORT ///////////////////////////////////////////////////////////////////////// internal const int NotSecondaryUIInit = -1; // For initialization of private consts in derived classes. /// protected static readonly int NotSecondaryUI = NotSecondaryUIInit; /// protected virtual int SecondaryUIMode { get { if (Control == null || Control.Form == null) { return NotSecondaryUI; } else { return((XhtmlFormAdapter)Control.Form.Adapter).GetSecondaryUIMode(Control); } } set { ((XhtmlFormAdapter)Control.Form.Adapter).SetSecondaryUIMode(Control, value); } } /// protected virtual void ExitSecondaryUIMode() { SecondaryUIMode = NotSecondaryUI; } /// public override void LoadAdapterState(Object state) { if (state != null) { SecondaryUIMode = (int)state; } } /// public override Object SaveAdapterState() { int mode = SecondaryUIMode; if (mode != NotSecondaryUI) { return mode; } else { return null; } } ///////////////////////////////////////////////////////////////////////// // ENTER STYLE SUPPORT: These methods should, in general, be used in place // of writer.EnterStyle, ExitStyle, etc. They check whether there is // a cssLocation attribute on the active form, and enter style only if // not. ///////////////////////////////////////////////////////////////////////// /// protected virtual void ConditionalEnterStyle(XhtmlMobileTextWriter writer, Style style) { ConditionalEnterStyle(writer, style, String.Empty); } /// protected virtual void ConditionalEnterStyle(XhtmlMobileTextWriter writer, Style style, String tag) { if ((String)Device[XhtmlConstants.RequiresXhtmlCssSuppression] == "true") { return; } if (CssLocation == StyleSheetLocation.PhysicalFile) { // Do nothing. Styles should be handled by CssClass custom attribute. return; } if (tag == null || tag.Length == 0) { writer.EnterStyle(style); } else { writer.EnterStyle(style, tag); } } /// protected virtual void ConditionalExitStyle(XhtmlMobileTextWriter writer, Style style) { if ((String)Device[XhtmlConstants.RequiresXhtmlCssSuppression] == "true") { return; } if (CssLocation == StyleSheetLocation.PhysicalFile) { // Do nothing. Styles should be handled by CssClass custom attribute. return; } writer.ExitStyle(style); } /// protected virtual void ConditionalEnterFormat(XhtmlMobileTextWriter writer, Style style) { if ((String)Device[XhtmlConstants.RequiresXhtmlCssSuppression] == "true") { return; } if (CssLocation == StyleSheetLocation.PhysicalFile) { // Do nothing. Styles should be handled by CssClass custom attribute. return; } writer.EnterFormat(style); } /// protected virtual void ConditionalExitFormat(XhtmlMobileTextWriter writer, Style style) { if ((String)Device[XhtmlConstants.RequiresXhtmlCssSuppression] == "true") { return; } if (CssLocation == StyleSheetLocation.PhysicalFile) { // Do nothing. Styles should be handled by CssClass custom attribute. return; } writer.ExitFormat(style); } /// protected virtual void ConditionalEnterLayout(XhtmlMobileTextWriter writer, Style style) { if ((String)Device[XhtmlConstants.RequiresXhtmlCssSuppression] == "true") { return; } if (CssLocation == StyleSheetLocation.PhysicalFile) { // Do nothing. Styles should be handled by CssClass custom attribute. return; } writer.EnterLayout(style); } /// protected virtual void ConditionalExitLayout(XhtmlMobileTextWriter writer, Style style) { if ((String)Device[XhtmlConstants.RequiresXhtmlCssSuppression] == "true") { return; } if (CssLocation == StyleSheetLocation.PhysicalFile) { // Do nothing. Styles should be handled by CssClass custom attribute. return; } writer.ExitLayout(style); } ///////////////////////////////////////////////////////////////////////// // STYLESHEET LOCATION SUPPORT // Use for determining whether stylesheet is physical or virtual, and // where it is located (application cache, session state, or a physical // directory). ///////////////////////////////////////////////////////////////////////// // For intelligibility at point of call. /// protected virtual String StyleSheetLocationAttributeValue { get{ return(String) Page.ActiveForm.CustomAttributes[XhtmlConstants.StyleSheetLocationCustomAttribute]; } } /// protected virtual String StyleSheetStorageApplicationSetting { get { return(String) ConfigurationManager.AppSettings[XhtmlConstants.CssStateLocationAppSettingKey]; } } // Add new supported markups here. private Doctype _documentType = Doctype.NotSet; /// protected virtual Doctype DocumentType { get{ if (_documentType != Doctype.NotSet) { return _documentType; } if ((String)Device[XhtmlConstants.RequiresOnEnterForward] == "true") { return Doctype.Wml20; } // Use capability rather than preferred rendering type header for accuracy. String browserCap = Device[XhtmlConstants.InternalStyleConfigSetting]; // Send internal styles by default. if (browserCap == null || !String.Equals(browserCap, "false", StringComparison.OrdinalIgnoreCase)) return _documentType = Doctype.XhtmlMobileProfile; else return _documentType = Doctype.XhtmlBasic; } } private StyleSheetLocation _cssLocation = StyleSheetLocation.NotSet; /// protected virtual StyleSheetLocation CssLocation { get { if (_cssLocation != StyleSheetLocation.NotSet) { return _cssLocation; } if (StyleSheetLocationAttributeValue != null && StyleSheetLocationAttributeValue.Length > 0) { return _cssLocation = StyleSheetLocation.PhysicalFile; } if (DocumentType == Doctype.XhtmlMobileProfile || DocumentType == Doctype.Wml20) { return _cssLocation = StyleSheetLocation.Internal; } // if (String.Compare(StyleSheetStorageApplicationSetting, XhtmlConstants.CacheStyleSheetValue, ((true /* ignore case */) ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal)) == 0) { if (String.Compare(StyleSheetStorageApplicationSetting, XhtmlConstants.CacheStyleSheetValue, StringComparison.OrdinalIgnoreCase) == 0) { return _cssLocation = StyleSheetLocation.ApplicationCache; } return _cssLocation = StyleSheetLocation.SessionState; } } ///////////////////////////////////////////////////////////////////////// // CSSCLASS CUSTOM ATTRIBUTE / PHYSICAL STYLESHEET SUPPORT // The cssClass custom attribute should only be honored if we are using a physical // stylesheet. ///////////////////////////////////////////////////////////////////////// // The use of _physicalCssClassPushed precludes nesting calls to ConditionalRenderClassAttribute, ConditionalRenderOpeningSpanElement, // ConditionalRenderClosingSpanElement, etc. The ConditionalRenderOpening call and ConditionalRenderClosing call must be paired without // nesting. ConditionalRenderClassAttribute is to be paired with ConditionalPopPhysicalCssClass (without nesting). /// protected virtual void ConditionalRenderClassAttribute(XhtmlMobileTextWriter writer) { if ((String)Device[XhtmlConstants.RequiresXhtmlCssSuppression] == "true") { return; } String classAttribute = (String) Control.CustomAttributes[XhtmlConstants.CssClassCustomAttribute]; if (CssLocation == StyleSheetLocation.PhysicalFile && classAttribute != null && classAttribute.Length > 0 && writer.DiffersFromCurrentPhysicalCssClass(classAttribute)) { writer.WriteAttribute("class", classAttribute); writer.PushPhysicalCssClass(classAttribute); Debug.Assert(!_physicalCssClassPushed, "These calls should not be nested."); _physicalCssClassPushed = true; } } private bool _physicalSpanClassOpen = false; // Render opening in case the stylesheet location has been specified as a physical file. /// protected virtual void ConditionalRenderOpeningSpanElement(XhtmlMobileTextWriter writer) { if ((String)Device[XhtmlConstants.RequiresXhtmlCssSuppression] == "true") { return; } String classAttribute = (String) Control.CustomAttributes[XhtmlConstants.CssClassCustomAttribute]; if (CssLocation == StyleSheetLocation.PhysicalFile && classAttribute != null && classAttribute.Length > 0 && writer.DiffersFromCurrentPhysicalCssClass(classAttribute)) { writer.WriteLine(); writer.WriteBeginTag("span"); writer.WriteAttribute("class", classAttribute, true /*encode*/); writer.Write(">"); writer.PushPhysicalCssClass(classAttribute); _physicalSpanClassOpen = true; Debug.Assert(!_physicalCssClassPushed, "These calls should not be nested."); _physicalCssClassPushed = true; } } // Render closing in case the stylesheet location has been specified as a physical file. /// protected virtual void ConditionalRenderClosingSpanElement(XhtmlMobileTextWriter writer) { String classAttribute = (String) Control.CustomAttributes[XhtmlConstants.CssClassCustomAttribute]; if (_physicalSpanClassOpen) { Debug.Assert(_physicalCssClassPushed, "_physicalSpanClassOpen implies _physicalCssClassPushed"); writer.WriteEndTag("span"); writer.WriteLine(); ConditionalPopPhysicalCssClass(writer); // resets _physicalCssClassPushed _physicalSpanClassOpen = false; } } // Render opening
in case the stylesheet location has been specified as a physical file. /// protected virtual void ConditionalRenderOpeningDivElement(XhtmlMobileTextWriter writer) { if ((String)Device[XhtmlConstants.RequiresXhtmlCssSuppression] == "true") { return; } String classAttribute = (String) Control.CustomAttributes[XhtmlConstants.CssClassCustomAttribute]; if (CssLocation == StyleSheetLocation.PhysicalFile) { writer.WriteLine(); if ((String)Device["usePOverDiv"] == "true") { writer.WriteBeginTag("p"); } else { writer.WriteBeginTag("div"); } if (classAttribute != null && classAttribute.Length > 0 && writer.DiffersFromCurrentPhysicalCssClass(classAttribute)) { writer.WriteAttribute("class", classAttribute, true); writer.PushPhysicalCssClass(classAttribute); Debug.Assert(!_physicalCssClassPushed, "These calls should not be nested."); _physicalCssClassPushed = true; } writer.Write(">"); } } // Render closing
in case the stylesheet location has been specified as a physical file. /// protected virtual void ConditionalRenderClosingDivElement(XhtmlMobileTextWriter writer) { if ((String)Device[XhtmlConstants.RequiresXhtmlCssSuppression] == "true") { return; } String classAttribute = (String) Control.CustomAttributes[XhtmlConstants.CssClassCustomAttribute]; if (CssLocation == StyleSheetLocation.PhysicalFile) { writer.WriteLine(); if ((String)Device["usePOverDiv"] == "true") { writer.WriteEndTag("p"); } else { writer.WriteEndTag("div"); } writer.WriteLine(); ConditionalPopPhysicalCssClass(writer); } } /// protected virtual void ConditionalPopPhysicalCssClass(XhtmlMobileTextWriter writer) { if (_physicalCssClassPushed) { writer.PopPhysicalCssClass(); _physicalCssClassPushed = false; } } ///////////////////////////////////////////////////////////////////////// // GENERAL CUSTOM ATTRIBUTE SUPPORT ///////////////////////////////////////////////////////////////////////// // Plays same role as HtmlAdapter.AddCustomAttribute. Named for consistency // within Xhtml adapter set. /// protected virtual void ConditionalRenderCustomAttribute(XhtmlMobileTextWriter writer, String attributeName) { ConditionalRenderCustomAttribute(writer, attributeName, attributeName); } /// protected virtual void ConditionalRenderCustomAttribute(XhtmlMobileTextWriter writer, String attributeName, String markupAttributeName) { String attributeValue = ((IAttributeAccessor)Control).GetAttribute(attributeName); if (attributeValue != null && attributeValue.Length > 0) { writer.WriteAttribute(markupAttributeName, attributeValue, true); } } // Utilities to increase intelligibility /// protected virtual String GetCustomAttributeValue(String attributeName) { return((IAttributeAccessor)Control).GetAttribute(attributeName); } /// protected virtual String GetCustomAttributeValue(MobileControl control, String attributeName) { return((IAttributeAccessor)control).GetAttribute(attributeName); } ///////////////////////////////////////////////////////////////////////// // SPECIALIZED UTILITY METHODS FOR LIST SELECTIONLIST OBJECTLIST ///////////////////////////////////////////////////////////////////////// // tagname can be any of table, ul, ol. See the list adapters for examples. /// protected virtual void RenderOpeningListTag(XhtmlMobileTextWriter writer, String tagName) { String classAttribute = (String) Control.CustomAttributes[XhtmlConstants.CssClassCustomAttribute]; if (CssLocation == StyleSheetLocation.PhysicalFile && (String)Device[XhtmlConstants.RequiresXhtmlCssSuppression] != "true") { writer.WritePendingBreak(); writer.WriteBeginTag(tagName); if (classAttribute != null && classAttribute.Length > 0 && writer.DiffersFromCurrentPhysicalCssClass(classAttribute)) { writer.WriteAttribute("class", classAttribute, true); writer.PushPhysicalCssClass(classAttribute); Debug.Assert(!_physicalCssClassPushed, "These calls should not be nested."); _physicalCssClassPushed = true; } writer.Write(">"); } else if ((String)Device[XhtmlConstants.RequiresXhtmlCssSuppression] != "true") { writer.WritePendingBreak(); StyleFilter filter = writer.CurrentStyleClass.GetFilter(Style); writer.EnterStyle(new XhtmlFormatStyleClass(Style, filter), tagName); } else { writer.WritePendingBreak(); writer.WriteFullBeginTag(tagName); } } /// protected virtual void RenderClosingListTag(XhtmlMobileTextWriter writer, String tagName) { if (CssLocation == StyleSheetLocation.PhysicalFile && (String)Device[XhtmlConstants.RequiresXhtmlCssSuppression] != "true") { writer.WriteEndTag(tagName); ConditionalPopPhysicalCssClass(writer); } else if ((String)Device[XhtmlConstants.RequiresXhtmlCssSuppression] != "true") { writer.ExitStyle(Style); } else { writer.WriteEndTag(tagName); } } /// protected virtual void ClearPendingBreakIfDeviceBreaksOnBlockLevel(XhtmlMobileTextWriter writer) { if ((String)Device[XhtmlConstants.BreaksOnBlockElements] != "false") { writer.ClearPendingBreak(); } } /// protected virtual void ConditionalClearPendingBreak(XhtmlMobileTextWriter writer) { if ((String)Device[XhtmlConstants.BreaksOnInlineElements] == "true") { writer.ClearPendingBreak(); } } // Required for a very rare device case where