Skip to content

Instantly share code, notes, and snippets.

@NightOwl888
Last active August 29, 2015 14:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save NightOwl888/c1f0a558d69f82dedfef to your computer and use it in GitHub Desktop.
Save NightOwl888/c1f0a558d69f82dedfef to your computer and use it in GitHub Desktop.
WhiteSpaceCleaner for ASP.NET
Imports System.Collections
Imports System.Reflection
Imports System.Web.UI
Namespace MyNamespace
' This class relies on the internal implementation details of ASP.NET. Tested for ASP.NET 2.0, 3.5 , 4.0
Public NotInheritable Class ControlBuilderExtensions
Private Sub New()
End Sub
Const InstPubNonpub As BindingFlags = BindingFlags.[Public] Or BindingFlags.NonPublic Or BindingFlags.Instance
Public Shared Function GetParentBuilder(ByVal controlBuilder As ControlBuilder) As ControlBuilder
If controlBuilder Is Nothing Then
Throw New ArgumentNullException("controlBuilder")
End If
Return DirectCast(controlBuilder.[GetType]().GetProperty("ParentBuilder", InstPubNonpub).GetValue(controlBuilder, Nothing), ControlBuilder)
End Function
Public Shared Function GetRootBuilder(ByVal controlBuilder As ControlBuilder) As ControlBuilder
If controlBuilder Is Nothing Then
Throw New ArgumentNullException("controlBuilder")
End If
While GetParentBuilder(controlBuilder) IsNot Nothing
controlBuilder = GetParentBuilder(controlBuilder)
End While
Return controlBuilder
End Function
Public Shared Function GetSubBuilders(ByVal controlBuilder As ControlBuilder) As ArrayList
If controlBuilder Is Nothing Then
Throw New ArgumentNullException("controlBuilder")
End If
Return DirectCast(controlBuilder.[GetType]().GetProperty("SubBuilders", InstPubNonpub).GetValue(controlBuilder, Nothing), ArrayList)
End Function
Public Shared Function GetDefaultPropertyBuilder(ByVal controlBuilder As ControlBuilder) As ControlBuilder
If controlBuilder Is Nothing Then
Throw New ArgumentNullException("controlBuilder")
End If
Dim pi As PropertyInfo = Nothing
Dim type As Type = controlBuilder.[GetType]()
While type IsNot Nothing AndAlso (InlineAssignHelper(pi, type.GetProperty("DefaultPropertyBuilder", InstPubNonpub))) Is Nothing
type = type.BaseType
End While
Return DirectCast(pi.GetValue(controlBuilder, Nothing), ControlBuilder)
End Function
Public Shared Function GetTemplatePropertyEntries(ByVal controlBuilder As ControlBuilder) As ArrayList
If controlBuilder Is Nothing Then
Throw New ArgumentNullException("controlBuilder")
End If
Dim tpes As ICollection = DirectCast(controlBuilder.[GetType]().GetProperty("TemplatePropertyEntries", InstPubNonpub).GetValue(controlBuilder, Nothing), ICollection)
If tpes Is Nothing OrElse tpes.Count = 0 Then
Return New ArrayList(0)
Else
Return DirectCast(tpes, ArrayList)
End If
End Function
Public Shared Function GetComplexPropertyEntries(ByVal controlBuilder As ControlBuilder) As ArrayList
If controlBuilder Is Nothing Then
Throw New ArgumentNullException("controlBuilder")
End If
Dim cpes As ICollection = DirectCast(controlBuilder.[GetType]().GetProperty("ComplexPropertyEntries", InstPubNonpub).GetValue(controlBuilder, Nothing), ICollection)
If cpes Is Nothing OrElse cpes.Count = 0 Then
Return New ArrayList(0)
Else
Return DirectCast(cpes, ArrayList)
End If
End Function
Private Shared Function InlineAssignHelper(Of T)(ByRef target As T, ByVal value As T) As T
target = value
Return value
End Function
End Class
End Namespace
Imports System.Web.UI
Namespace MyNamespace
Public Class NotRestrictingParserFilter
Inherits PageParserFilter
Public Overrides ReadOnly Property AllowCode() As Boolean
Get
Return True
End Get
End Property
Public Overrides ReadOnly Property NumberOfControlsAllowed() As Integer
Get
Return Integer.MaxValue
End Get
End Property
Public Overrides ReadOnly Property NumberOfDirectDependenciesAllowed() As Integer
Get
Return Integer.MaxValue
End Get
End Property
Public Overrides ReadOnly Property TotalNumberOfDependenciesAllowed() As Integer
Get
Return Integer.MaxValue
End Get
End Property
Public Overrides Function AllowBaseType(ByVal baseType As Type) As Boolean
Return True
End Function
Public Overrides Function AllowControl(ByVal controlType As Type, ByVal builder As ControlBuilder) As Boolean
Return True
End Function
Public Overrides Function AllowServerSideInclude(ByVal includeVirtualPath As String) As Boolean
Return True
End Function
Public Overrides Function AllowVirtualReference(ByVal referenceVirtualPath As String, ByVal referenceType As VirtualReferenceType) As Boolean
Return True
End Function
Public Overrides Function GetCompilationMode(ByVal current As CompilationMode) As CompilationMode
Return MyBase.GetCompilationMode(current)
End Function
Public Overrides Function GetNoCompileUserControlType() As Type
Return MyBase.GetNoCompileUserControlType()
End Function
Public Overrides Sub PreprocessDirective(ByVal directiveName As String, ByVal attributes As System.Collections.IDictionary)
MyBase.PreprocessDirective(directiveName, attributes)
End Sub
Public Overrides Function ProcessCodeConstruct(ByVal codeType As CodeConstructType, ByVal code As String) As Boolean
Return MyBase.ProcessCodeConstruct(codeType, code)
End Function
Public Overrides Function ProcessDataBindingAttribute(ByVal controlId As String, ByVal name As String, ByVal value As String) As Boolean
Return MyBase.ProcessDataBindingAttribute(controlId, name, value)
End Function
Public Overrides Function ProcessEventHookup(ByVal controlId As String, ByVal eventName As String, ByVal handlerName As String) As Boolean
Return MyBase.ProcessEventHookup(controlId, eventName, handlerName)
End Function
Public Overrides Sub ParseComplete(ByVal rootBuilder As ControlBuilder)
MyBase.ParseComplete(rootBuilder)
End Sub
Protected Overrides Sub Initialize()
MyBase.Initialize()
End Sub
End Class
End Namespace
Imports System.Collections
Imports System.Text.RegularExpressions
Imports System.Web
Imports System.Web.UI
Imports System.Web.Configuration
Namespace MyNamespace
Public Enum WhiteSpaceCleaning
None
Clean
CleanOnReleaseBuild
End Enum
Public Class WhiteSpaceCleaner
Inherits NotRestrictingParserFilter
Const WhiteSpaceDirectiveName As String = "whitespacecleaning"
Private _whiteSpaceCleaning As System.Nullable(Of WhiteSpaceCleaning)
Private Shared RegexLineBreaks As New Regex("\s*\n\s*", RegexOptions.Compiled)
Private Shared RegexWhiteSpace As New Regex("[\t ]{2,}", RegexOptions.Compiled)
Public Overrides Sub PreprocessDirective(ByVal directiveName As String, ByVal attributes As System.Collections.IDictionary)
If attributes.Contains(WhiteSpaceDirectiveName) Then
_whiteSpaceCleaning = CType([Enum].Parse(GetType(WhiteSpaceCleaning), DirectCast(attributes(WhiteSpaceDirectiveName), String)), WhiteSpaceCleaning)
attributes.Remove(WhiteSpaceDirectiveName)
End If
MyBase.PreprocessDirective(directiveName, attributes)
End Sub
Public Overrides Sub ParseComplete(ByVal rootBuilder As ControlBuilder)
Dim whiteSpace As WhiteSpaceCleaning = IIf(_whiteSpaceCleaning.HasValue, _whiteSpaceCleaning, WhiteSpaceCleaning.CleanOnReleaseBuild)
If whiteSpace = WhiteSpaceCleaning.Clean OrElse (whiteSpace = WhiteSpaceCleaning.CleanOnReleaseBuild AndAlso Not IsDebuggingEnabled()) Then
TrimStringSubBuilders(rootBuilder)
End If
MyBase.ParseComplete(rootBuilder)
End Sub
Private Shared Sub TrimStringSubBuilders(ByVal controlBuilder As ControlBuilder)
Dim subBuilders As ArrayList = ControlBuilderExtensions.GetSubBuilders(controlBuilder)
For i As Integer = 0 To subBuilders.Count - 1
Dim literal As String = TryCast(subBuilders(i), String)
If String.IsNullOrEmpty(literal) Then
Continue For
End If
subBuilders(i) = CleanWhiteSpace(literal)
Next
For Each subBuilder As Object In subBuilders
If TypeOf subBuilder Is ControlBuilder Then
TrimStringSubBuilders(DirectCast(subBuilder, ControlBuilder))
End If
Next
For Each entry As TemplatePropertyEntry In ControlBuilderExtensions.GetTemplatePropertyEntries(controlBuilder)
TrimStringSubBuilders(entry.Builder)
Next
For Each entry As ComplexPropertyEntry In ControlBuilderExtensions.GetComplexPropertyEntries(controlBuilder)
TrimStringSubBuilders(entry.Builder)
Next
Dim defaultPropertyBuilder As ControlBuilder = ControlBuilderExtensions.GetDefaultPropertyBuilder(controlBuilder)
If defaultPropertyBuilder IsNot Nothing Then
TrimStringSubBuilders(defaultPropertyBuilder)
End If
End Sub
Private Shared Function CleanWhiteSpace(ByVal literal As String) As String
literal = RegexLineBreaks.Replace(literal, vbLf)
literal = RegexWhiteSpace.Replace(literal, " ")
Return literal
End Function
Private Function IsDebuggingEnabled() As Boolean
If HttpContext.Current IsNot Nothing Then
Return HttpContext.Current.IsDebuggingEnabled
End If
Return DirectCast(WebConfigurationManager.GetSection("system.web/compilation", VirtualPath), CompilationSection).Debug
End Function
End Class
End Namespace
@NightOwl888
Copy link
Author

WhiteSpaceCleaner is enabled through the web.config file, like this.

<configuration>
    <system.web>
        <pages pageParserFilterType="MyNamespace.WhiteSpaceCleaner"/>
    </system.web>
</configuration>

Sorry, I don't have any additional info other than this - I put this in place in 2010 and didn't document it. I have also switched to using HTTP compression built into IIS 7 instead of the 3rd party component.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment