public override void Write(byte[] buffer, int offset, int count)
{
// If we are not capturing script blocks anymore, just redirect to response stream
if (!_captureScripts)
{
_responseStream.Write(buffer, offset, count);
return;
}
// Script and HTML can be in one of the following combinations in the specified buffer:
// .....<script ....>.....</script>.....
// <script ....>.....</script>.....
// <script ....>.....</script>
// <script ....>.....</script> .....
// ....<script ....>.....
// <script ....>.....
// .....</script>.....
// .....</script>
// <script>.....
// .... </script>
// ......
// Here, "...." means html content between and outside script tags
char[] content;
char[] charBuffer = _encoding.GetChars(buffer, offset, count);
// If some bytes were left for processing during last Write call
// then consider those into the current buffer
if (null != _pendingBuffer)
{
content = new char[charBuffer.Length + _pendingBuffer.Length];
Array.Copy(_pendingBuffer, 0, content, 0, _pendingBuffer.Length);
Array.Copy(charBuffer, 0, content, _pendingBuffer.Length, charBuffer.Length);
_pendingBuffer = null;
}
else
{
content = charBuffer;
}
int scriptTagStart = 0;
int lastScriptTagEnd = 0;
bool scriptTagStarted = false;
int pos;
for (pos = 0; pos < content.Length; pos++)
{
// See if tag start
char c = content[pos];
if (c != '<') continue;
// Make sure there are enough characters available in the buffer to finish
// tag start. This will happen when a tag partially starts but does not end
// For example, a partial script tag
// <script
// Or it's the ending html tag or some tag closing that ends the whole response
// </html>
if (pos + TAG_SCRIPT.Length >= content.Length)
{
// a tag started but there are less than 10 characters available. So, let's
// store the remaining content in a buffer and wait for another Write(...) or
// flush call.
_pendingBuffer = new char[content.Length - pos];
Array.Copy(content, pos, _pendingBuffer, 0, content.Length - pos);
break;
}
int tagStart = pos;
// Check if it's a tag ending
if (content[pos + 1] == '/')
{
pos += 2; // go past the </
// See if script tag is ending
if (CompareChars(TAG_SCRIPT, content, pos))
{
// Script tag just ended. Get the whole script
// and store in buffer
pos += TAG_SCRIPT.Length + 1;
_scriptBlocks.Append(content, scriptTagStart, pos - scriptTagStart);
_scriptBlocks.Append(Environment.NewLine);
lastScriptTagEnd = pos;
scriptTagStarted = false;
pos--; // continue will increase pos by one again
continue;
}
if (_isInServerForm && CompareChars(TAG_FORM, content, pos))
{
// Server form tag has just end. Time for rendering all the script
// blocks we have suppressed so far and stop capturing script blocks.
_isInServerForm = false;
if (_scriptBlocks.Length > 0)
{
// Render all pending html output till now
WriteOutput(content, lastScriptTagEnd, tagStart - lastScriptTagEnd);
// Render the script blocks
RenderAllScriptBlocks();
// Stop capturing for script blocks
_captureScripts = false;
// Write from the body tag start to the end of the inut buffer and return
// from the function. We are done.
WriteOutput(content, tagStart, content.Length - tagStart);
return;
}
}
else
{
// some other tag's closing. safely skip one character as smallest
// html tag is one character e.g. <b>. just an optimization to save one loop
pos++;
}
}
else
{
if (CompareChars(TAG_SCRIPT, content, pos + 1))
{
// Script tag started. Record the position as we will
// capture the whole script tag including its content
// and store in an internal buffer.
scriptTagStart = pos;
// Write html content since last script tag closing upto this script tag
WriteOutput(content, lastScriptTagEnd, scriptTagStart - lastScriptTagEnd);
// Skip the tag start to save some loops
pos += TAG_SCRIPT.Length + 1;
scriptTagStarted = true;
}
else if (!_isInServerForm && CompareChars(TAG_INPUT, content, pos + 1))
{
// <input> tag started, so we need to check if it is __VIEWSTATE field
// to make sure that the form that contains it is a server form.
// First we need to sure if there is enough bytes in the buffer.
if (pos + TAG_INPUT_VIEWSTATE.Length >= content.Length)
{
_pendingBuffer = new char[content.Length - pos];
Array.Copy(content, pos, _pendingBuffer, 0, content.Length - pos);
break;
}
// Check if input tag contains viewstate information.
if (CompareChars(TAG_INPUT_VIEWSTATE, content, pos + 1))
{
// Yep! We are in the server form.
_isInServerForm = true;
pos += TAG_INPUT_VIEWSTATE.Length + 1;
}
else
{
// Skip the input tag.
pos += TAG_INPUT.Length + 1;
}
}
else
{
// Some other tag started.
// We can safely skip 2 character because the smallest tag is one
// character e.g. <b>. It's just an optimization to eliminate one loop.
pos++;
}
}
}
// If a script tag is partially sent to buffer, then the remaining content
// is part of the last script block
if (scriptTagStarted)
{
_scriptBlocks.Append(content, scriptTagStart, pos - scriptTagStart);
}
else
{
// Render the characters since the last script tag ending
WriteOutput(content, lastScriptTagEnd, pos - lastScriptTagEnd);
}
}