e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
591 lines
21 KiB
C#
591 lines
21 KiB
C#
//------------------------------------------------------------------------------
|
|
// <copyright file="xml.cs" company="Microsoft">
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// </copyright>
|
|
//------------------------------------------------------------------------------
|
|
|
|
namespace System.Web.UI.WebControls {
|
|
|
|
using System;
|
|
using System.IO;
|
|
using System.Collections;
|
|
using System.Collections.Specialized;
|
|
using System.ComponentModel;
|
|
using System.ComponentModel.Design;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Drawing.Design;
|
|
using System.Globalization;
|
|
using System.Web;
|
|
using System.Web.Util;
|
|
using System.Web.UI;
|
|
using System.Web.Caching;
|
|
using System.Web.Hosting;
|
|
using System.Security.Policy;
|
|
using System.Xml;
|
|
using System.Xml.Xsl;
|
|
using System.Xml.XPath;
|
|
using System.Security.Permissions;
|
|
|
|
public class XmlBuilder : ControlBuilder {
|
|
|
|
|
|
public override void AppendLiteralString(string s) {}
|
|
|
|
|
|
public override Type GetChildControlType(string tagName, IDictionary attribs) {
|
|
return null;
|
|
}
|
|
|
|
|
|
public override bool NeedsTagInnerText() { return true; }
|
|
|
|
|
|
[SuppressMessage("Microsoft.Security", "MSEC1220:ReviewDtdProcessingAssignment", Justification = "Dtd processing is needed for back-compat, but is being done as safely as possible.")]
|
|
public override void SetTagInnerText(string text) {
|
|
if (!Util.IsWhiteSpaceString(text)) {
|
|
|
|
// Trim the initial whitespaces since XML is very picky (ASURT 58100)
|
|
int iFirstNonWhiteSpace = Util.FirstNonWhiteSpaceIndex(text);
|
|
string textNoWS = text.Substring(iFirstNonWhiteSpace);
|
|
|
|
// Update the line number to point to the correct line in the xml
|
|
// block (ASURT 58233).
|
|
Line += Util.LineCount(text, 0, iFirstNonWhiteSpace);
|
|
|
|
// Parse the XML here just to cause a parse error in case it is
|
|
// malformed. It will be parsed again at runtime.
|
|
XmlDocument document = new XmlDocument();
|
|
XmlReaderSettings readerSettings = XmlUtils.CreateXmlReaderSettings();
|
|
readerSettings.LineNumberOffset = Line - 1;
|
|
|
|
// VSWhidbey 546662: XmlReader has different default settings than XmlTextReader which was used in Everett
|
|
readerSettings.DtdProcessing = DtdProcessing.Parse;
|
|
readerSettings.CheckCharacters = false;
|
|
|
|
XmlReader dataReader = XmlUtils.CreateXmlReader(new StringReader(textNoWS), string.Empty, readerSettings);
|
|
|
|
try {
|
|
document.Load(dataReader);
|
|
}
|
|
catch (XmlException e) {
|
|
// Xml exception sometimes returns -1 for the line, in which case we ignore it.
|
|
if (e.LineNumber >= 0) {
|
|
Line = e.LineNumber;
|
|
}
|
|
|
|
throw;
|
|
}
|
|
|
|
base.AppendLiteralString(text);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// <para>[To be supplied.]</para>
|
|
/// </devdoc>
|
|
[
|
|
DefaultProperty("DocumentSource"),
|
|
PersistChildren(false, true),
|
|
ControlBuilderAttribute(typeof(XmlBuilder)),
|
|
Designer("System.Web.UI.Design.WebControls.XmlDesigner, " + AssemblyRef.SystemDesign)
|
|
]
|
|
public class Xml : Control {
|
|
|
|
private XPathNavigator _xpathNavigator;
|
|
private XmlDocument _document;
|
|
private XPathDocument _xpathDocument;
|
|
#pragma warning disable 0618 // To avoid deprecation warning
|
|
private XslTransform _transform;
|
|
#pragma warning restore 0618
|
|
private XslCompiledTransform _compiledTransform;
|
|
private XsltArgumentList _transformArgumentList;
|
|
private string _documentContent;
|
|
private string _documentSource;
|
|
private string _transformSource;
|
|
|
|
const string identityXslStr =
|
|
"<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>" +
|
|
"<xsl:template match=\"/\"> <xsl:copy-of select=\".\"/> </xsl:template> </xsl:stylesheet>";
|
|
|
|
#pragma warning disable 0618 // To avoid deprecation warning
|
|
static XslTransform _identityTransform;
|
|
#pragma warning restore 0618
|
|
|
|
[SuppressMessage("Microsoft.Security", "MSEC1201:DoNotUseXslTransform", Justification = "_identityTransform contents are trusted hard-coded string.")]
|
|
[SuppressMessage("Microsoft.Security", "MSEC1205:DoNotAllowDtdOnXmlTextReader", Justification = "_identityTransform contents are trusted hard-coded string.")]
|
|
[PermissionSet(SecurityAction.Assert, Unrestricted = true)]
|
|
static Xml() {
|
|
|
|
// Instantiate an identity transform, to be used whenever we need to output XML
|
|
XmlTextReader reader = new XmlTextReader(new StringReader(identityXslStr));
|
|
#pragma warning disable 0618 // To avoid deprecation warning
|
|
_identityTransform = new XslTransform();
|
|
#pragma warning restore 0618
|
|
|
|
_identityTransform.Load(reader, null /*resolver*/, null /*evidence*/);
|
|
}
|
|
|
|
[
|
|
EditorBrowsable(EditorBrowsableState.Never),
|
|
]
|
|
public override string ClientID {
|
|
get {
|
|
return base.ClientID;
|
|
}
|
|
}
|
|
|
|
[
|
|
EditorBrowsable(EditorBrowsableState.Never),
|
|
]
|
|
public override ControlCollection Controls {
|
|
get {
|
|
return base.Controls;
|
|
}
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// <para>[To be supplied.]</para>
|
|
/// </devdoc>
|
|
[
|
|
Browsable(false),
|
|
WebSysDescription(SR.Xml_Document),
|
|
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
|
|
Obsolete("The recommended alternative is the XPathNavigator property. Create a System.Xml.XPath.XPathDocument and call CreateNavigator() to create an XPathNavigator. http://go.microsoft.com/fwlink/?linkid=14202"),
|
|
]
|
|
public XmlDocument Document {
|
|
get {
|
|
if (_document == null) {
|
|
LoadXmlDocument();
|
|
}
|
|
return _document;
|
|
}
|
|
set {
|
|
DocumentSource = null;
|
|
_xpathDocument = null;
|
|
_documentContent = null;
|
|
_document = value;
|
|
}
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// <para>[To be supplied.]</para>
|
|
/// </devdoc>
|
|
[
|
|
Browsable(false),
|
|
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
|
|
WebSysDescription(SR.Xml_DocumentContent)
|
|
]
|
|
public String DocumentContent {
|
|
get {
|
|
return _documentContent != null ? _documentContent : String.Empty;
|
|
}
|
|
set {
|
|
_document = null;
|
|
_xpathDocument = null;
|
|
_xpathNavigator = null;
|
|
_documentContent = value;
|
|
|
|
if (DesignMode) {
|
|
ViewState["OriginalContent"] = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// <para>[To be supplied.]</para>
|
|
/// </devdoc>
|
|
[
|
|
WebCategory("Behavior"),
|
|
DefaultValue(""),
|
|
Editor("System.Web.UI.Design.XmlUrlEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)),
|
|
UrlProperty(),
|
|
WebSysDescription(SR.Xml_DocumentSource)
|
|
]
|
|
public String DocumentSource {
|
|
get {
|
|
return (_documentSource == null) ? String.Empty : _documentSource;
|
|
}
|
|
set {
|
|
_document = null;
|
|
_xpathDocument = null;
|
|
_documentContent = null;
|
|
_xpathNavigator = null;
|
|
_documentSource = value;
|
|
}
|
|
}
|
|
|
|
[
|
|
Browsable(false),
|
|
DefaultValue(false),
|
|
EditorBrowsable(EditorBrowsableState.Never),
|
|
]
|
|
public override bool EnableTheming {
|
|
get {
|
|
return false;
|
|
}
|
|
set {
|
|
throw new NotSupportedException(SR.GetString(SR.NoThemingSupport, this.GetType().Name));
|
|
}
|
|
}
|
|
|
|
[
|
|
Browsable(false),
|
|
DefaultValue(""),
|
|
EditorBrowsable(EditorBrowsableState.Never),
|
|
]
|
|
public override string SkinID {
|
|
get {
|
|
return String.Empty;
|
|
}
|
|
set {
|
|
throw new NotSupportedException(SR.GetString(SR.NoThemingSupport, this.GetType().Name));
|
|
}
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// <para>[To be supplied.]</para>
|
|
/// </devdoc>
|
|
[
|
|
Browsable(false),
|
|
WebSysDescription(SR.Xml_Transform),
|
|
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
|
|
]
|
|
#pragma warning disable 0618 // To avoid deprecation warning
|
|
public XslTransform Transform {
|
|
#pragma warning restore 0618
|
|
get {
|
|
return XmlUtils.GetXslTransform(_transform);
|
|
}
|
|
set {
|
|
if (XmlUtils.GetXslTransform(value) != null) {
|
|
// Setting this property will nullify _transform, so do it first.
|
|
TransformSource = null;
|
|
_transform = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// <para>[To be supplied.]</para>
|
|
/// </devdoc>
|
|
[
|
|
Browsable(false),
|
|
WebSysDescription(SR.Xml_TransformArgumentList),
|
|
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
|
|
]
|
|
public XsltArgumentList TransformArgumentList {
|
|
get {
|
|
return _transformArgumentList;
|
|
}
|
|
set {
|
|
_transformArgumentList = value;
|
|
}
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// <para>[To be supplied.]</para>
|
|
/// </devdoc>
|
|
[
|
|
WebCategory("Behavior"),
|
|
DefaultValue(""),
|
|
Editor("System.Web.UI.Design.XslUrlEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)),
|
|
WebSysDescription(SR.Xml_TransformSource),
|
|
]
|
|
public String TransformSource {
|
|
get {
|
|
return (_transformSource == null) ? String.Empty : _transformSource;
|
|
}
|
|
set {
|
|
_transform = null;
|
|
_transformSource = value;
|
|
}
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// </devdoc>
|
|
[
|
|
Browsable(false),
|
|
WebSysDescription(SR.Xml_XPathNavigator),
|
|
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
|
|
]
|
|
public XPathNavigator XPathNavigator {
|
|
get {
|
|
return _xpathNavigator;
|
|
}
|
|
set {
|
|
DocumentSource = null;
|
|
_xpathDocument = null;
|
|
_documentContent = null;
|
|
_document = null;
|
|
_xpathNavigator = value;
|
|
}
|
|
}
|
|
|
|
|
|
[SuppressMessage("Microsoft.Security", "MSEC1218:ReviewWebControlForSet_DocumentContent", Justification = "Legacy code that trusts our developer input. Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")]
|
|
protected override void AddParsedSubObject(object obj) {
|
|
if (obj is LiteralControl) {
|
|
// Trim the initial whitespaces since XML is very picky (related to ASURT 58100)
|
|
string text = ((LiteralControl)obj).Text;
|
|
|
|
int iFirstNonWhiteSpace = Util.FirstNonWhiteSpaceIndex(text);
|
|
DocumentContent = text.Substring(iFirstNonWhiteSpace);
|
|
|
|
if (DesignMode) {
|
|
ViewState["OriginalContent"] = text;
|
|
}
|
|
}
|
|
else {
|
|
throw new HttpException(SR.GetString(SR.Cannot_Have_Children_Of_Type, "Xml", obj.GetType().Name.ToString(CultureInfo.InvariantCulture)));
|
|
}
|
|
}
|
|
|
|
protected override ControlCollection CreateControlCollection() {
|
|
return new EmptyControlCollection(this);
|
|
}
|
|
|
|
[
|
|
EditorBrowsable(EditorBrowsableState.Never),
|
|
]
|
|
public override Control FindControl(string id) {
|
|
return base.FindControl(id);
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// </devdoc>
|
|
[
|
|
EditorBrowsable(EditorBrowsableState.Never),
|
|
]
|
|
public override void Focus() {
|
|
throw new NotSupportedException(SR.GetString(SR.NoFocusSupport, this.GetType().Name));
|
|
}
|
|
|
|
[SecurityPermission(SecurityAction.Demand, Unrestricted = true)]
|
|
protected override IDictionary GetDesignModeState() {
|
|
IDictionary designModeState = new HybridDictionary();
|
|
designModeState["OriginalContent"] = ViewState["OriginalContent"];
|
|
|
|
return designModeState;
|
|
}
|
|
|
|
[
|
|
EditorBrowsable(EditorBrowsableState.Never),
|
|
]
|
|
public override bool HasControls() {
|
|
return base.HasControls();
|
|
}
|
|
|
|
private void LoadTransformFromSource() {
|
|
|
|
// We're done if we already have a transform
|
|
if (_transform != null)
|
|
return;
|
|
|
|
if (String.IsNullOrEmpty(_transformSource) || _transformSource.Trim().Length == 0)
|
|
return;
|
|
|
|
// First, figure out if it's a physical or virtual path
|
|
VirtualPath virtualPath;
|
|
string physicalPath;
|
|
ResolvePhysicalOrVirtualPath(_transformSource, out virtualPath, out physicalPath);
|
|
|
|
CacheInternal cacheInternal = HttpRuntime.CacheInternal;
|
|
string key = CacheInternal.PrefixLoadXPath + ((physicalPath != null) ?
|
|
physicalPath : virtualPath.VirtualPathString);
|
|
|
|
object xform = cacheInternal.Get(key);
|
|
|
|
if (xform == null) {
|
|
Debug.Trace("XmlControl", "Xsl Transform not found in cache (" + _transformSource + ")");
|
|
|
|
// Get the stream, and open the doc
|
|
CacheDependency dependency;
|
|
using (Stream stream = OpenFileAndGetDependency(virtualPath, physicalPath, out dependency)) {
|
|
|
|
// If we don't have a physical path, call MapPath to get one, in order to pass it as
|
|
// the baseUri to XmlTextReader. In pure VirtualPathProvider scenarios, it won't
|
|
// help much, but it allows the default case to have relative references (VSWhidbey 545322)
|
|
if (physicalPath == null)
|
|
physicalPath = virtualPath.MapPath();
|
|
|
|
// XslCompiledTransform for some reason wants to completely re-create an internal XmlReader
|
|
// from scratch. In doing so, it does not respect all the settings of XmlTextReader. So we want
|
|
// to be sure to specifically use XmlReader here so our settings on it will be respected by the
|
|
// XslCompiledTransform.
|
|
XmlReader xmlReader = XmlUtils.CreateXmlReader(stream, physicalPath);
|
|
_transform = XmlUtils.CreateXslTransform(xmlReader);
|
|
if (_transform == null) {
|
|
_compiledTransform = XmlUtils.CreateXslCompiledTransform(xmlReader);
|
|
}
|
|
}
|
|
|
|
// Cache it, but only if we got a dependency
|
|
if (dependency != null) {
|
|
using (dependency) {
|
|
cacheInternal.UtcInsert(key, ((_compiledTransform == null) ? (object)_transform : (object)_compiledTransform), dependency);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
Debug.Trace("XmlControl", "XslTransform found in cache (" + _transformSource + ")");
|
|
|
|
_compiledTransform = xform as XslCompiledTransform;
|
|
if (_compiledTransform == null) {
|
|
#pragma warning disable 0618 // To avoid deprecation warning
|
|
_transform = (XslTransform)xform;
|
|
#pragma warning restore 0618
|
|
}
|
|
}
|
|
}
|
|
|
|
private void LoadXmlDocument() {
|
|
|
|
Debug.Assert(_xpathDocument == null && _document == null && _xpathNavigator == null);
|
|
|
|
if (!String.IsNullOrEmpty(_documentContent)) {
|
|
_document = XmlUtils.CreateXmlDocumentFromContent(_documentContent);
|
|
return;
|
|
}
|
|
|
|
if (String.IsNullOrEmpty(_documentSource))
|
|
return;
|
|
|
|
// Make it absolute and check security
|
|
string physicalPath = MapPathSecure(_documentSource);
|
|
|
|
CacheInternal cacheInternal = System.Web.HttpRuntime.CacheInternal;
|
|
string key = CacheInternal.PrefixLoadXml + physicalPath;
|
|
|
|
_document = (XmlDocument) cacheInternal.Get(key);
|
|
|
|
if (_document == null) {
|
|
Debug.Trace("XmlControl", "XmlDocument not found in cache (" + _documentSource + ")");
|
|
|
|
CacheDependency dependency;
|
|
using (Stream stream = OpenFileAndGetDependency(null, physicalPath, out dependency)) {
|
|
|
|
_document = new XmlDocument();
|
|
_document.Load(XmlUtils.CreateXmlReader(stream, physicalPath));
|
|
cacheInternal.UtcInsert(key, _document, dependency);
|
|
}
|
|
}
|
|
else {
|
|
Debug.Trace("XmlControl", "XmlDocument found in cache (" + _documentSource + ")");
|
|
}
|
|
|
|
//
|
|
lock (_document) {
|
|
// Always return a clone of the cached copy
|
|
_document = (XmlDocument)_document.CloneNode(true/*deep*/);
|
|
}
|
|
}
|
|
|
|
private void LoadXPathDocument() {
|
|
|
|
Debug.Assert(_xpathDocument == null && _document == null && _xpathNavigator == null);
|
|
|
|
if (!String.IsNullOrEmpty(_documentContent)) {
|
|
_xpathDocument = XmlUtils.CreateXPathDocumentFromContent(_documentContent);
|
|
return;
|
|
}
|
|
|
|
if (String.IsNullOrEmpty(_documentSource))
|
|
return;
|
|
|
|
// First, figure out if it's a physical or virtual path
|
|
VirtualPath virtualPath;
|
|
string physicalPath;
|
|
ResolvePhysicalOrVirtualPath(_documentSource, out virtualPath, out physicalPath);
|
|
|
|
CacheInternal cacheInternal = HttpRuntime.CacheInternal;
|
|
string key = CacheInternal.PrefixLoadXPath + ((physicalPath != null) ?
|
|
physicalPath : virtualPath.VirtualPathString);
|
|
|
|
_xpathDocument = (XPathDocument)cacheInternal.Get(key);
|
|
if (_xpathDocument == null) {
|
|
Debug.Trace("XmlControl", "XPathDocument not found in cache (" + _documentSource + ")");
|
|
|
|
// Get the stream, and open the doc
|
|
CacheDependency dependency;
|
|
using (Stream stream = OpenFileAndGetDependency(virtualPath, physicalPath, out dependency)) {
|
|
// The same comments as in LoadTransformFromSource() (VSWhidbey 545322, 546662)
|
|
if (physicalPath == null) {
|
|
physicalPath = virtualPath.MapPath();
|
|
}
|
|
|
|
_xpathDocument = new XPathDocument(XmlUtils.CreateXmlReader(stream, physicalPath));
|
|
}
|
|
|
|
// Cache it, but only if we got a dependency
|
|
if (dependency != null) {
|
|
using (dependency) {
|
|
cacheInternal.UtcInsert(key, _xpathDocument, dependency);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
Debug.Trace("XmlControl", "XPathDocument found in cache (" + _documentSource + ")");
|
|
}
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// <para>[To be supplied.]</para>
|
|
/// </devdoc>
|
|
[SuppressMessage("Microsoft.Security", "MSEC1204:UseSecureXmlResolver", Justification = "Legacy code that trusts our developer input. Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")]
|
|
protected internal override void Render(HtmlTextWriter output)
|
|
{
|
|
|
|
// If we don't already have an XmlDocument or an XPathNavigator, load am XPathDocument (which is faster)
|
|
if ((_document == null) && (_xpathNavigator == null)) {
|
|
LoadXPathDocument();
|
|
}
|
|
|
|
LoadTransformFromSource();
|
|
|
|
// Abort if nothing has been loaded
|
|
if (_document == null && _xpathDocument == null && _xpathNavigator == null) {
|
|
return;
|
|
}
|
|
|
|
// If we don't have a transform, use the identity transform, which
|
|
// simply renders the XML.
|
|
if (_transform == null)
|
|
_transform = _identityTransform;
|
|
|
|
// Pass a resolver in full trust, to support certain XSL scenarios (ASURT 141427)
|
|
XmlUrlResolver xr = null;
|
|
if (HttpRuntime.HasUnmanagedPermission()) {
|
|
xr = new XmlUrlResolver();
|
|
}
|
|
|
|
IXPathNavigable doc = null;
|
|
if (_document != null) {
|
|
doc = _document;
|
|
} else if (_xpathNavigator != null) {
|
|
doc = _xpathNavigator;
|
|
} else {
|
|
doc = _xpathDocument;
|
|
}
|
|
|
|
if (_compiledTransform != null) {
|
|
XmlWriter writer = XmlWriter.Create(output);
|
|
_compiledTransform.Transform(doc, _transformArgumentList, writer, null);
|
|
} else {
|
|
_transform.Transform(doc, _transformArgumentList, output, xr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|