Skip to content
This repository has been archived by the owner on Dec 14, 2018. It is now read-only.

Commit

Permalink
React to aspnet/Razor#705
Browse files Browse the repository at this point in the history
- Updated `ScriptTagHelper` and `LinkTagHelper` to maintain their quotes on generated script tags.
- Removed `TagHelperOutputExtensions`. It's no longer needed.
- We no longer string replace quotes directly. We rely on encoding and the initial representation of an attribute to ensure quotes don't break generated attributes.
- Updated tests.
  • Loading branch information
NTaylorMullen committed May 27, 2016
1 parent 5eed7b9 commit cfd58f1
Show file tree
Hide file tree
Showing 19 changed files with 155 additions and 254 deletions.
2 changes: 0 additions & 2 deletions src/Microsoft.AspNetCore.Mvc.Razor.Host/MvcRazorHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,6 @@ internal MvcRazorHost(IChunkTreeCache chunkTreeCache, RazorPathNormalizer pathNo
ExecutionContextAddTagHelperAttributeMethodName =
nameof(TagHelperExecutionContext.AddTagHelperAttribute),
ExecutionContextAddHtmlAttributeMethodName = nameof(TagHelperExecutionContext.AddHtmlAttribute),
ExecutionContextAddMinimizedHtmlAttributeMethodName =
nameof(TagHelperExecutionContext.AddMinimizedHtmlAttribute),
ExecutionContextOutputPropertyName = nameof(TagHelperExecutionContext.Output),

RunnerTypeName = typeof(TagHelperRunner).FullName,
Expand Down
27 changes: 19 additions & 8 deletions src/Microsoft.AspNetCore.Mvc.Razor/RazorPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ public TagHelperContent EndTagHelperWritingScope()
/// <remarks>
/// All writes to the <see cref="Output"/> or <see cref="ViewContext.Writer"/> after calling this method will
/// be buffered until <see cref="EndWriteTagHelperAttribute"/> is called.
/// The content will be buffered using a shared <see cref="StringWriter"/> within this <see cref="RazorPage"/>
/// The content will be buffered using a shared <see cref="StringWriter"/> within this <see cref="RazorPage"/>
/// Nesting of <see cref="BeginWriteTagHelperAttribute"/> and <see cref="EndWriteTagHelperAttribute"/> method calls
/// is not supported.
/// </remarks>
Expand Down Expand Up @@ -267,7 +267,7 @@ public void BeginWriteTagHelperAttribute()
/// </summary>
/// <returns>The content buffered by the shared <see cref="StringWriter"/> of this <see cref="RazorPage"/>.</returns>
/// <remarks>
/// This method assumes that there will be no nesting of <see cref="BeginWriteTagHelperAttribute"/>
/// This method assumes that there will be no nesting of <see cref="BeginWriteTagHelperAttribute"/>
/// and <see cref="EndWriteTagHelperAttribute"/> method calls.
/// </remarks>
public string EndWriteTagHelperAttribute()
Expand Down Expand Up @@ -572,9 +572,14 @@ public virtual void EndWriteAttributeTo(TextWriter writer)
public void BeginAddHtmlAttributeValues(
TagHelperExecutionContext executionContext,
string attributeName,
int attributeValuesCount)
int attributeValuesCount,
HtmlAttributeValueStyle attributeValueStyle)
{
_tagHelperAttributeInfo = new TagHelperAttributeInfo(executionContext, attributeName, attributeValuesCount);
_tagHelperAttributeInfo = new TagHelperAttributeInfo(
executionContext,
attributeName,
attributeValuesCount,
attributeValueStyle);
}

public void AddHtmlAttributeValue(
Expand All @@ -596,15 +601,17 @@ public void AddHtmlAttributeValue(
// attribute was removed from TagHelperOutput.Attributes).
_tagHelperAttributeInfo.ExecutionContext.AddTagHelperAttribute(
_tagHelperAttributeInfo.Name,
value?.ToString() ?? string.Empty);
value?.ToString() ?? string.Empty,
_tagHelperAttributeInfo.AttributeValueStyle);
_tagHelperAttributeInfo.Suppressed = true;
return;
}
else if (IsBoolTrueWithEmptyPrefixValue(prefix, value))
{
_tagHelperAttributeInfo.ExecutionContext.AddHtmlAttribute(
_tagHelperAttributeInfo.Name,
_tagHelperAttributeInfo.Name);
_tagHelperAttributeInfo.Name,
_tagHelperAttributeInfo.AttributeValueStyle);
_tagHelperAttributeInfo.Suppressed = true;
return;
}
Expand Down Expand Up @@ -637,7 +644,7 @@ public void EndAddHtmlAttributeValues(TagHelperExecutionContext executionContext
var content = _valueBuffer == null ? HtmlString.Empty : new HtmlString(_valueBuffer.ToString());
_valueBuffer?.GetStringBuilder().Clear();

executionContext.AddHtmlAttribute(_tagHelperAttributeInfo.Name, content);
executionContext.AddHtmlAttribute(_tagHelperAttributeInfo.Name, content, _tagHelperAttributeInfo.AttributeValueStyle);
}
}

Expand Down Expand Up @@ -1081,11 +1088,13 @@ private struct TagHelperAttributeInfo
public TagHelperAttributeInfo(
TagHelperExecutionContext tagHelperExecutionContext,
string name,
int attributeValuesCount)
int attributeValuesCount,
HtmlAttributeValueStyle attributeValueStyle)
{
ExecutionContext = tagHelperExecutionContext;
Name = name;
AttributeValuesCount = attributeValuesCount;
AttributeValueStyle = attributeValueStyle;

Suppressed = false;
}
Expand All @@ -1096,6 +1105,8 @@ public TagHelperAttributeInfo(

public int AttributeValuesCount { get; }

public HtmlAttributeValueStyle AttributeValueStyle { get; }

public bool Suppressed { get; set; }
}

Expand Down
12 changes: 4 additions & 8 deletions src/Microsoft.AspNetCore.Mvc.TagHelpers/LinkTagHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,8 @@ private void BuildLinkTag(string href, TagHelperAttributeList attributes, TagHel
}
else
{
AppendAttribute(attribute.Name, attribute.Value, builder);
attribute.CopyTo(builder);
builder.AppendHtml(" ");
}
}

Expand All @@ -441,15 +442,10 @@ private void AppendVersionedHref(string hrefName, string hrefValue, TagHelperCon
hrefValue = _fileVersionProvider.AddFileVersionToPath(hrefValue);
}

AppendAttribute(hrefName, hrefValue, builder);
}

private void AppendAttribute(string key, object value, TagHelperContent builder)
{
builder
.AppendHtml(key)
.AppendHtml(hrefName)
.AppendHtml("=\"")
.Append(HtmlEncoder, value)
.Append(hrefValue)
.AppendHtml("\" ");
}

Expand Down
114 changes: 41 additions & 73 deletions src/Microsoft.AspNetCore.Mvc.TagHelpers/ScriptTagHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Html;
Expand Down Expand Up @@ -235,7 +236,8 @@ public override void Process(TagHelperContext context, TagHelperOutput output)
var index = output.Attributes.IndexOfName(SrcAttributeName);
output.Attributes[index] = new TagHelperAttribute(
SrcAttributeName,
_fileVersionProvider.AddFileVersionToPath(Src));
_fileVersionProvider.AddFileVersionToPath(Src),
output.Attributes[index].ValueStyle);
}
}

Expand Down Expand Up @@ -306,7 +308,7 @@ private void BuildFallbackBlock(TagHelperAttributeList attributes, TagHelperCont
// Fallback "src" values come from bound attributes and globbing. Must always be non-null.
Debug.Assert(src != null);

builder.AppendHtml("<script");
StringWriter.Write("<script");

var addSrc = true;

Expand All @@ -316,80 +318,68 @@ private void BuildFallbackBlock(TagHelperAttributeList attributes, TagHelperCont
var attribute = attributes[i];
if (!attribute.Name.Equals(SrcAttributeName, StringComparison.OrdinalIgnoreCase))
{
var encodedKey = JavaScriptEncoder.Encode(attribute.Name);
var attributeValue = GetAttributeValue(attribute.Value);
var encodedValue = JavaScriptEncoder.Encode(attributeValue);

AppendAttribute(builder, encodedKey, encodedValue, escapeQuotes: true);
StringWriter.Write(' ');
attribute.WriteTo(StringWriter, HtmlEncoder);
}
else
{
addSrc = false;
AppendEncodedVersionedSrc(attribute.Name, src, builder, generateForDocumentWrite: true);
WriteVersionedSrc(attribute.Name, src, attribute.ValueStyle, StringWriter);
}
}

if (addSrc)
{
AppendEncodedVersionedSrc(SrcAttributeName, src, builder, generateForDocumentWrite: true);
WriteVersionedSrc(SrcAttributeName, src, HtmlAttributeValueStyle.DoubleQuotes, StringWriter);
}

builder.AppendHtml("><\\/script>");
StringWriter.Write("></script>");
}

var stringBuilder = StringWriter.GetStringBuilder();
var scriptTags = stringBuilder.ToString();
stringBuilder.Clear();
var encodedScriptTags = JavaScriptEncoder.Encode(scriptTags);
builder.AppendHtml(encodedScriptTags);

builder.AppendHtml("\"));</script>");
}
}

private string GetAttributeValue(object value)
private string GetVersionedSrc(string srcValue)
{
string stringValue;
var htmlString = value as HtmlString;
if (htmlString != null)
if (AppendVersion == true)
{
// Value likely came from an HTML context in the .cshtml file but may still contain double quotes
// since attribute could have been enclosed in single quotes.
stringValue = htmlString.Value;
stringValue = stringValue.Replace("\"", "&quot;");
srcValue = _fileVersionProvider.AddFileVersionToPath(srcValue);
}
else
{
var writer = StringWriter;
RazorPage.WriteTo(writer, HtmlEncoder, value);

// Value is now correctly HTML-encoded but may still contain double quotes since attribute could
// have been enclosed in single quotes and portions that were HtmlStrings are not re-encoded.
var builder = writer.GetStringBuilder();
builder.Replace("\"", "&quot;");
return srcValue;
}

stringValue = builder.ToString();
builder.Clear();
}
private void AppendVersionedSrc(
string srcName,
string srcValue,
HtmlAttributeValueStyle valueStyle,
IHtmlContentBuilder builder)
{
srcValue = GetVersionedSrc(srcValue);

return stringValue;
builder.AppendHtml(" ");
var attribute = new TagHelperAttribute(srcName, srcValue, valueStyle);
attribute.CopyTo(builder);
}

private void AppendEncodedVersionedSrc(
private void WriteVersionedSrc(
string srcName,
string srcValue,
TagHelperContent builder,
bool generateForDocumentWrite)
HtmlAttributeValueStyle valueStyle,
TextWriter writer)
{
if (AppendVersion == true)
{
srcValue = _fileVersionProvider.AddFileVersionToPath(srcValue);
}

if (generateForDocumentWrite)
{
// srcValue comes from a C# context and globbing. Must HTML-encode it to ensure the
// written <script/> element is valid. Must also JavaScript-encode that value to ensure
// the document.write() statement is valid.
srcValue = HtmlEncoder.Encode(srcValue);
srcValue = JavaScriptEncoder.Encode(srcValue);
}
srcValue = GetVersionedSrc(srcValue);

AppendAttribute(builder, srcName, srcValue, escapeQuotes: generateForDocumentWrite);
writer.Write(' ');
var attribute = new TagHelperAttribute(srcName, srcValue, valueStyle);
attribute.WriteTo(writer, HtmlEncoder);
}

private void EnsureGlobbingUrlBuilder()
Expand Down Expand Up @@ -429,46 +419,24 @@ private void BuildScriptTag(
var attribute = attributes[i];
if (!attribute.Name.Equals(SrcAttributeName, StringComparison.OrdinalIgnoreCase))
{
AppendAttribute(builder, attribute.Name, attribute.Value, escapeQuotes: false);
builder.AppendHtml(" ");
attribute.CopyTo(builder);
}
else
{
addSrc = false;
AppendEncodedVersionedSrc(attribute.Name, src, builder, generateForDocumentWrite: false);
AppendVersionedSrc(attribute.Name, src, attribute.ValueStyle, builder);
}
}

if (addSrc)
{
AppendEncodedVersionedSrc(SrcAttributeName, src, builder, generateForDocumentWrite: false);
AppendVersionedSrc(SrcAttributeName, src, HtmlAttributeValueStyle.DoubleQuotes, builder);
}

builder.AppendHtml("></script>");
}

private void AppendAttribute(TagHelperContent content, string key, object value, bool escapeQuotes)
{
content
.AppendHtml(" ")
.AppendHtml(key);
if (escapeQuotes)
{
// Passed only JavaScript-encoded strings in this case. Do not perform HTML-encoding as well.
content
.AppendHtml("=\\\"")
.AppendHtml((string)value)
.AppendHtml("\\\"");
}
else
{
// HTML-encode the given value if necessary.
content
.AppendHtml("=\"")
.Append(HtmlEncoder, value)
.AppendHtml("\"");
}
}

private enum Mode
{
/// <summary>
Expand Down

This file was deleted.

Loading

0 comments on commit cfd58f1

Please sign in to comment.