mirror of
https://github.com/roytam1/UXP.git
synced 2026-05-26 22:46:03 +00:00
48844fe691
This is all the manual work for Bug 483155, minus the added functionality to disable SVG and MathML which can be done at any time and are out of scope. Tag UXP Issue #1344
817 lines
24 KiB
C++
817 lines
24 KiB
C++
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "nsHtml5Highlighter.h"
|
|
#include "nsDebug.h"
|
|
#include "nsHtml5Tokenizer.h"
|
|
#include "nsHtml5AttributeName.h"
|
|
#include "nsString.h"
|
|
#include "nsThreadUtils.h"
|
|
#include "nsHtml5ViewSourceUtils.h"
|
|
|
|
#include "mozilla/Attributes.h"
|
|
#include "mozilla/Preferences.h"
|
|
|
|
using namespace mozilla;
|
|
|
|
// The old code had a limit of 16 tokens. 1300 is a number picked my measuring
|
|
// the size of 16 tokens on cnn.com.
|
|
#define NS_HTML5_HIGHLIGHTER_PRE_BREAK_THRESHOLD 1300
|
|
|
|
char16_t nsHtml5Highlighter::sComment[] =
|
|
{ 'c', 'o', 'm', 'm', 'e', 'n', 't', 0 };
|
|
|
|
char16_t nsHtml5Highlighter::sCdata[] =
|
|
{ 'c', 'd', 'a', 't', 'a', 0 };
|
|
|
|
char16_t nsHtml5Highlighter::sEntity[] =
|
|
{ 'e', 'n', 't', 'i', 't', 'y', 0 };
|
|
|
|
char16_t nsHtml5Highlighter::sEndTag[] =
|
|
{ 'e', 'n', 'd', '-', 't', 'a', 'g', 0 };
|
|
|
|
char16_t nsHtml5Highlighter::sStartTag[] =
|
|
{ 's', 't', 'a', 'r', 't', '-', 't', 'a', 'g', 0 };
|
|
|
|
char16_t nsHtml5Highlighter::sAttributeName[] =
|
|
{ 'a', 't', 't', 'r', 'i', 'b', 'u', 't', 'e', '-', 'n', 'a', 'm', 'e', 0 };
|
|
|
|
char16_t nsHtml5Highlighter::sAttributeValue[] =
|
|
{ 'a', 't', 't', 'r', 'i', 'b', 'u', 't', 'e', '-',
|
|
'v', 'a', 'l', 'u', 'e', 0 };
|
|
|
|
char16_t nsHtml5Highlighter::sDoctype[] =
|
|
{ 'd', 'o', 'c', 't', 'y', 'p', 'e', 0 };
|
|
|
|
char16_t nsHtml5Highlighter::sPi[] =
|
|
{ 'p', 'i', 0 };
|
|
|
|
nsHtml5Highlighter::nsHtml5Highlighter(nsAHtml5TreeOpSink* aOpSink)
|
|
: mState(nsHtml5Tokenizer::DATA)
|
|
, mCStart(INT32_MAX)
|
|
, mPos(0)
|
|
, mLineNumber(1)
|
|
, mInlinesOpen(0)
|
|
, mInCharacters(false)
|
|
, mBuffer(nullptr)
|
|
, mOpSink(aOpSink)
|
|
, mCurrentRun(nullptr)
|
|
, mAmpersand(nullptr)
|
|
, mSlash(nullptr)
|
|
, mHandles(MakeUnique<nsIContent*[]>(NS_HTML5_HIGHLIGHTER_HANDLE_ARRAY_LENGTH))
|
|
, mHandlesUsed(0)
|
|
, mSeenBase(false)
|
|
{
|
|
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
|
}
|
|
|
|
nsHtml5Highlighter::~nsHtml5Highlighter()
|
|
{
|
|
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
|
}
|
|
|
|
void
|
|
nsHtml5Highlighter::Start(const nsAutoString& aTitle)
|
|
{
|
|
// Doctype
|
|
mOpQueue.AppendElement()->Init(nsGkAtoms::html, EmptyString(), EmptyString());
|
|
|
|
mOpQueue.AppendElement()->Init(STANDARDS_MODE);
|
|
|
|
// <html> uses NS_NewHTMLSharedElement creator
|
|
nsIContent** root =
|
|
CreateElement(nsHtml5Atoms::html, nullptr, nullptr, NS_NewHTMLSharedElement);
|
|
mOpQueue.AppendElement()->Init(eTreeOpAppendToDocument, root);
|
|
mStack.AppendElement(root);
|
|
|
|
// <head> uses NS_NewHTMLSharedElement creator
|
|
Push(nsGkAtoms::head, nullptr, NS_NewHTMLSharedElement);
|
|
|
|
Push(nsGkAtoms::title, nullptr, NS_NewHTMLTitleElement);
|
|
// XUL will add the "Source of: " prefix.
|
|
uint32_t length = aTitle.Length();
|
|
if (length > INT32_MAX) {
|
|
length = INT32_MAX;
|
|
}
|
|
AppendCharacters(aTitle.BeginReading(), 0, (int32_t)length);
|
|
Pop(); // title
|
|
|
|
Push(nsGkAtoms::link,
|
|
nsHtml5ViewSourceUtils::NewLinkAttributes(),
|
|
NS_NewHTMLLinkElement);
|
|
|
|
mOpQueue.AppendElement()->Init(eTreeOpUpdateStyleSheet, CurrentNode());
|
|
|
|
Pop(); // link
|
|
|
|
Pop(); // head
|
|
|
|
Push(nsGkAtoms::body,
|
|
nsHtml5ViewSourceUtils::NewBodyAttributes(),
|
|
NS_NewHTMLBodyElement);
|
|
|
|
nsHtml5HtmlAttributes* preAttrs = new nsHtml5HtmlAttributes(0);
|
|
nsHtml5String preId = nsHtml5Portability::newStringFromLiteral("line1");
|
|
preAttrs->addAttribute(nsHtml5AttributeName::ATTR_ID, preId, -1);
|
|
Push(nsGkAtoms::pre, preAttrs, NS_NewHTMLPreElement);
|
|
|
|
StartCharacters();
|
|
|
|
mOpQueue.AppendElement()->Init(eTreeOpStartLayout);
|
|
}
|
|
|
|
int32_t
|
|
nsHtml5Highlighter::Transition(int32_t aState, bool aReconsume, int32_t aPos)
|
|
{
|
|
mPos = aPos;
|
|
switch (mState) {
|
|
case nsHtml5Tokenizer::SCRIPT_DATA:
|
|
case nsHtml5Tokenizer::RAWTEXT:
|
|
case nsHtml5Tokenizer::RCDATA:
|
|
case nsHtml5Tokenizer::DATA:
|
|
// We can transition on < and on &. Either way, we don't yet know the
|
|
// role of the token, so open a span without class.
|
|
if (aState == nsHtml5Tokenizer::CONSUME_CHARACTER_REFERENCE) {
|
|
StartSpan();
|
|
// Start another span for highlighting the ampersand
|
|
StartSpan();
|
|
mAmpersand = CurrentNode();
|
|
} else {
|
|
EndCharactersAndStartMarkupRun();
|
|
}
|
|
break;
|
|
case nsHtml5Tokenizer::TAG_OPEN:
|
|
switch (aState) {
|
|
case nsHtml5Tokenizer::TAG_NAME:
|
|
StartSpan(sStartTag);
|
|
break;
|
|
case nsHtml5Tokenizer::DATA:
|
|
FinishTag(); // DATA
|
|
break;
|
|
case nsHtml5Tokenizer::PROCESSING_INSTRUCTION:
|
|
AddClass(sPi);
|
|
break;
|
|
}
|
|
break;
|
|
case nsHtml5Tokenizer::TAG_NAME:
|
|
switch (aState) {
|
|
case nsHtml5Tokenizer::BEFORE_ATTRIBUTE_NAME:
|
|
EndSpanOrA(); // nsHtml5Tokenizer::TAG_NAME
|
|
break;
|
|
case nsHtml5Tokenizer::SELF_CLOSING_START_TAG:
|
|
EndSpanOrA(); // nsHtml5Tokenizer::TAG_NAME
|
|
StartSpan(); // for highlighting the slash
|
|
mSlash = CurrentNode();
|
|
break;
|
|
default:
|
|
FinishTag();
|
|
break;
|
|
}
|
|
break;
|
|
case nsHtml5Tokenizer::BEFORE_ATTRIBUTE_NAME:
|
|
switch (aState) {
|
|
case nsHtml5Tokenizer::ATTRIBUTE_NAME:
|
|
StartSpan(sAttributeName);
|
|
break;
|
|
case nsHtml5Tokenizer::SELF_CLOSING_START_TAG:
|
|
StartSpan(); // for highlighting the slash
|
|
mSlash = CurrentNode();
|
|
break;
|
|
default:
|
|
FinishTag();
|
|
break;
|
|
}
|
|
break;
|
|
case nsHtml5Tokenizer::ATTRIBUTE_NAME:
|
|
switch (aState) {
|
|
case nsHtml5Tokenizer::AFTER_ATTRIBUTE_NAME:
|
|
case nsHtml5Tokenizer::BEFORE_ATTRIBUTE_VALUE:
|
|
EndSpanOrA(); // nsHtml5Tokenizer::BEFORE_ATTRIBUTE_NAME
|
|
break;
|
|
case nsHtml5Tokenizer::SELF_CLOSING_START_TAG:
|
|
EndSpanOrA(); // nsHtml5Tokenizer::BEFORE_ATTRIBUTE_NAME
|
|
StartSpan(); // for highlighting the slash
|
|
mSlash = CurrentNode();
|
|
break;
|
|
default:
|
|
FinishTag();
|
|
break;
|
|
}
|
|
break;
|
|
case nsHtml5Tokenizer::BEFORE_ATTRIBUTE_VALUE:
|
|
switch (aState) {
|
|
case nsHtml5Tokenizer::ATTRIBUTE_VALUE_DOUBLE_QUOTED:
|
|
case nsHtml5Tokenizer::ATTRIBUTE_VALUE_SINGLE_QUOTED:
|
|
FlushCurrent();
|
|
StartA();
|
|
break;
|
|
case nsHtml5Tokenizer::ATTRIBUTE_VALUE_UNQUOTED:
|
|
StartA();
|
|
break;
|
|
default:
|
|
FinishTag();
|
|
break;
|
|
}
|
|
break;
|
|
case nsHtml5Tokenizer::ATTRIBUTE_VALUE_DOUBLE_QUOTED:
|
|
case nsHtml5Tokenizer::ATTRIBUTE_VALUE_SINGLE_QUOTED:
|
|
switch (aState) {
|
|
case nsHtml5Tokenizer::AFTER_ATTRIBUTE_VALUE_QUOTED:
|
|
EndSpanOrA();
|
|
break;
|
|
case nsHtml5Tokenizer::CONSUME_CHARACTER_REFERENCE:
|
|
StartSpan();
|
|
StartSpan(); // for ampersand itself
|
|
mAmpersand = CurrentNode();
|
|
break;
|
|
default:
|
|
NS_NOTREACHED("Impossible transition.");
|
|
break;
|
|
}
|
|
break;
|
|
case nsHtml5Tokenizer::AFTER_ATTRIBUTE_VALUE_QUOTED:
|
|
switch (aState) {
|
|
case nsHtml5Tokenizer::BEFORE_ATTRIBUTE_NAME:
|
|
break;
|
|
case nsHtml5Tokenizer::SELF_CLOSING_START_TAG:
|
|
StartSpan(); // for highlighting the slash
|
|
mSlash = CurrentNode();
|
|
break;
|
|
default:
|
|
FinishTag();
|
|
break;
|
|
}
|
|
break;
|
|
case nsHtml5Tokenizer::SELF_CLOSING_START_TAG:
|
|
EndSpanOrA(); // end the slash highlight
|
|
switch (aState) {
|
|
case nsHtml5Tokenizer::BEFORE_ATTRIBUTE_NAME:
|
|
break;
|
|
default:
|
|
FinishTag();
|
|
break;
|
|
}
|
|
break;
|
|
case nsHtml5Tokenizer::ATTRIBUTE_VALUE_UNQUOTED:
|
|
switch (aState) {
|
|
case nsHtml5Tokenizer::BEFORE_ATTRIBUTE_NAME:
|
|
EndSpanOrA();
|
|
break;
|
|
case nsHtml5Tokenizer::CONSUME_CHARACTER_REFERENCE:
|
|
StartSpan();
|
|
StartSpan(); // for ampersand itself
|
|
mAmpersand = CurrentNode();
|
|
break;
|
|
default:
|
|
FinishTag();
|
|
break;
|
|
}
|
|
break;
|
|
case nsHtml5Tokenizer::AFTER_ATTRIBUTE_NAME:
|
|
switch (aState) {
|
|
case nsHtml5Tokenizer::SELF_CLOSING_START_TAG:
|
|
StartSpan(); // for highlighting the slash
|
|
mSlash = CurrentNode();
|
|
break;
|
|
case nsHtml5Tokenizer::BEFORE_ATTRIBUTE_VALUE:
|
|
break;
|
|
case nsHtml5Tokenizer::ATTRIBUTE_NAME:
|
|
StartSpan(sAttributeName);
|
|
break;
|
|
default:
|
|
FinishTag();
|
|
break;
|
|
}
|
|
break;
|
|
// most comment states are omitted, because they don't matter to
|
|
// highlighting
|
|
case nsHtml5Tokenizer::COMMENT_START:
|
|
case nsHtml5Tokenizer::COMMENT_END:
|
|
case nsHtml5Tokenizer::COMMENT_END_BANG:
|
|
case nsHtml5Tokenizer::COMMENT_START_DASH:
|
|
case nsHtml5Tokenizer::BOGUS_COMMENT:
|
|
case nsHtml5Tokenizer::BOGUS_COMMENT_HYPHEN:
|
|
if (aState == nsHtml5Tokenizer::DATA) {
|
|
AddClass(sComment);
|
|
FinishTag();
|
|
}
|
|
break;
|
|
// most cdata states are omitted, because they don't matter to
|
|
// highlighting
|
|
case nsHtml5Tokenizer::CDATA_RSQB_RSQB:
|
|
if (aState == nsHtml5Tokenizer::DATA) {
|
|
AddClass(sCdata);
|
|
FinishTag();
|
|
}
|
|
break;
|
|
case nsHtml5Tokenizer::CONSUME_CHARACTER_REFERENCE:
|
|
EndSpanOrA(); // the span for the ampersand
|
|
switch (aState) {
|
|
case nsHtml5Tokenizer::CONSUME_NCR:
|
|
case nsHtml5Tokenizer::CHARACTER_REFERENCE_HILO_LOOKUP:
|
|
break;
|
|
default:
|
|
// not actually a character reference
|
|
EndSpanOrA();
|
|
break;
|
|
}
|
|
break;
|
|
case nsHtml5Tokenizer::CHARACTER_REFERENCE_HILO_LOOKUP:
|
|
if (aState == nsHtml5Tokenizer::CHARACTER_REFERENCE_TAIL) {
|
|
break;
|
|
}
|
|
// not actually a character reference
|
|
EndSpanOrA();
|
|
break;
|
|
case nsHtml5Tokenizer::CHARACTER_REFERENCE_TAIL:
|
|
if (!aReconsume) {
|
|
FlushCurrent();
|
|
}
|
|
EndSpanOrA();
|
|
break;
|
|
case nsHtml5Tokenizer::DECIMAL_NRC_LOOP:
|
|
case nsHtml5Tokenizer::HEX_NCR_LOOP:
|
|
switch (aState) {
|
|
case nsHtml5Tokenizer::HANDLE_NCR_VALUE:
|
|
AddClass(sEntity);
|
|
FlushCurrent();
|
|
break;
|
|
case nsHtml5Tokenizer::HANDLE_NCR_VALUE_RECONSUME:
|
|
AddClass(sEntity);
|
|
break;
|
|
}
|
|
EndSpanOrA();
|
|
break;
|
|
case nsHtml5Tokenizer::CLOSE_TAG_OPEN:
|
|
switch (aState) {
|
|
case nsHtml5Tokenizer::DATA:
|
|
FinishTag();
|
|
break;
|
|
case nsHtml5Tokenizer::TAG_NAME:
|
|
StartSpan(sEndTag);
|
|
break;
|
|
}
|
|
break;
|
|
case nsHtml5Tokenizer::RAWTEXT_RCDATA_LESS_THAN_SIGN:
|
|
if (aState == nsHtml5Tokenizer::NON_DATA_END_TAG_NAME) {
|
|
FlushCurrent();
|
|
StartSpan(); // don't know if it is "end-tag" yet :-(
|
|
break;
|
|
}
|
|
EndSpanOrA();
|
|
StartCharacters();
|
|
break;
|
|
case nsHtml5Tokenizer::NON_DATA_END_TAG_NAME:
|
|
switch (aState) {
|
|
case nsHtml5Tokenizer::BEFORE_ATTRIBUTE_NAME:
|
|
AddClass(sEndTag);
|
|
EndSpanOrA();
|
|
break;
|
|
case nsHtml5Tokenizer::SELF_CLOSING_START_TAG:
|
|
AddClass(sEndTag);
|
|
EndSpanOrA();
|
|
StartSpan(); // for highlighting the slash
|
|
mSlash = CurrentNode();
|
|
break;
|
|
case nsHtml5Tokenizer::DATA: // yes, as a result of emitting the token
|
|
AddClass(sEndTag);
|
|
FinishTag();
|
|
break;
|
|
default:
|
|
FinishTag();
|
|
break;
|
|
}
|
|
break;
|
|
case nsHtml5Tokenizer::SCRIPT_DATA_LESS_THAN_SIGN:
|
|
case nsHtml5Tokenizer::SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN:
|
|
if (aState == nsHtml5Tokenizer::NON_DATA_END_TAG_NAME) {
|
|
FlushCurrent();
|
|
StartSpan(); // don't know if it is "end-tag" yet :-(
|
|
break;
|
|
}
|
|
FinishTag();
|
|
break;
|
|
case nsHtml5Tokenizer::SCRIPT_DATA_ESCAPED_DASH_DASH:
|
|
case nsHtml5Tokenizer::SCRIPT_DATA_ESCAPED:
|
|
case nsHtml5Tokenizer::SCRIPT_DATA_ESCAPED_DASH:
|
|
if (aState == nsHtml5Tokenizer::SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN) {
|
|
EndCharactersAndStartMarkupRun();
|
|
}
|
|
break;
|
|
// Lots of double escape states omitted, because they don't highlight.
|
|
// Likewise, only doctype states that can emit the doctype are of
|
|
// interest. Otherwise, the transition out of bogus comment deals.
|
|
case nsHtml5Tokenizer::BEFORE_DOCTYPE_NAME:
|
|
case nsHtml5Tokenizer::DOCTYPE_NAME:
|
|
case nsHtml5Tokenizer::AFTER_DOCTYPE_NAME:
|
|
case nsHtml5Tokenizer::AFTER_DOCTYPE_PUBLIC_KEYWORD:
|
|
case nsHtml5Tokenizer::BEFORE_DOCTYPE_PUBLIC_IDENTIFIER:
|
|
case nsHtml5Tokenizer::DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED:
|
|
case nsHtml5Tokenizer::AFTER_DOCTYPE_PUBLIC_IDENTIFIER:
|
|
case nsHtml5Tokenizer::BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS:
|
|
case nsHtml5Tokenizer::DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED:
|
|
case nsHtml5Tokenizer::AFTER_DOCTYPE_SYSTEM_IDENTIFIER:
|
|
case nsHtml5Tokenizer::BOGUS_DOCTYPE:
|
|
case nsHtml5Tokenizer::AFTER_DOCTYPE_SYSTEM_KEYWORD:
|
|
case nsHtml5Tokenizer::BEFORE_DOCTYPE_SYSTEM_IDENTIFIER:
|
|
case nsHtml5Tokenizer::DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED:
|
|
case nsHtml5Tokenizer::DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED:
|
|
if (aState == nsHtml5Tokenizer::DATA) {
|
|
AddClass(sDoctype);
|
|
FinishTag();
|
|
}
|
|
break;
|
|
case nsHtml5Tokenizer::PROCESSING_INSTRUCTION_QUESTION_MARK:
|
|
if (aState == nsHtml5Tokenizer::DATA) {
|
|
FinishTag();
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
mState = aState;
|
|
return aState;
|
|
}
|
|
|
|
void
|
|
nsHtml5Highlighter::End()
|
|
{
|
|
switch (mState) {
|
|
case nsHtml5Tokenizer::COMMENT_END:
|
|
case nsHtml5Tokenizer::COMMENT_END_BANG:
|
|
case nsHtml5Tokenizer::COMMENT_START_DASH:
|
|
case nsHtml5Tokenizer::BOGUS_COMMENT:
|
|
case nsHtml5Tokenizer::BOGUS_COMMENT_HYPHEN:
|
|
AddClass(sComment);
|
|
break;
|
|
case nsHtml5Tokenizer::CDATA_RSQB_RSQB:
|
|
AddClass(sCdata);
|
|
break;
|
|
case nsHtml5Tokenizer::DECIMAL_NRC_LOOP:
|
|
case nsHtml5Tokenizer::HEX_NCR_LOOP:
|
|
// XXX need tokenizer help here
|
|
break;
|
|
case nsHtml5Tokenizer::BEFORE_DOCTYPE_NAME:
|
|
case nsHtml5Tokenizer::DOCTYPE_NAME:
|
|
case nsHtml5Tokenizer::AFTER_DOCTYPE_NAME:
|
|
case nsHtml5Tokenizer::AFTER_DOCTYPE_PUBLIC_KEYWORD:
|
|
case nsHtml5Tokenizer::BEFORE_DOCTYPE_PUBLIC_IDENTIFIER:
|
|
case nsHtml5Tokenizer::DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED:
|
|
case nsHtml5Tokenizer::AFTER_DOCTYPE_PUBLIC_IDENTIFIER:
|
|
case nsHtml5Tokenizer::BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS:
|
|
case nsHtml5Tokenizer::DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED:
|
|
case nsHtml5Tokenizer::AFTER_DOCTYPE_SYSTEM_IDENTIFIER:
|
|
case nsHtml5Tokenizer::BOGUS_DOCTYPE:
|
|
case nsHtml5Tokenizer::AFTER_DOCTYPE_SYSTEM_KEYWORD:
|
|
case nsHtml5Tokenizer::BEFORE_DOCTYPE_SYSTEM_IDENTIFIER:
|
|
case nsHtml5Tokenizer::DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED:
|
|
case nsHtml5Tokenizer::DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED:
|
|
AddClass(sDoctype);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
|
|
NS_ASSERTION(treeOp, "Tree op allocation failed.");
|
|
treeOp->Init(eTreeOpStreamEnded);
|
|
FlushOps();
|
|
}
|
|
|
|
void
|
|
nsHtml5Highlighter::SetBuffer(nsHtml5UTF16Buffer* aBuffer)
|
|
{
|
|
NS_PRECONDITION(!mBuffer, "Old buffer still here!");
|
|
mBuffer = aBuffer;
|
|
mCStart = aBuffer->getStart();
|
|
}
|
|
|
|
void
|
|
nsHtml5Highlighter::DropBuffer(int32_t aPos)
|
|
{
|
|
NS_PRECONDITION(mBuffer, "No buffer to drop!");
|
|
mPos = aPos;
|
|
FlushChars();
|
|
mBuffer = nullptr;
|
|
}
|
|
|
|
void
|
|
nsHtml5Highlighter::StartSpan()
|
|
{
|
|
FlushChars();
|
|
Push(nsGkAtoms::span, nullptr, NS_NewHTMLSpanElement);
|
|
++mInlinesOpen;
|
|
}
|
|
|
|
void
|
|
nsHtml5Highlighter::StartSpan(const char16_t* aClass)
|
|
{
|
|
StartSpan();
|
|
AddClass(aClass);
|
|
}
|
|
|
|
void
|
|
nsHtml5Highlighter::EndSpanOrA()
|
|
{
|
|
FlushChars();
|
|
Pop();
|
|
--mInlinesOpen;
|
|
}
|
|
|
|
void
|
|
nsHtml5Highlighter::StartCharacters()
|
|
{
|
|
NS_PRECONDITION(!mInCharacters, "Already in characters!");
|
|
FlushChars();
|
|
Push(nsGkAtoms::span, nullptr, NS_NewHTMLSpanElement);
|
|
mCurrentRun = CurrentNode();
|
|
mInCharacters = true;
|
|
}
|
|
|
|
void
|
|
nsHtml5Highlighter::EndCharactersAndStartMarkupRun()
|
|
{
|
|
NS_PRECONDITION(mInCharacters, "Not in characters!");
|
|
FlushChars();
|
|
Pop();
|
|
mInCharacters = false;
|
|
// Now start markup run
|
|
StartSpan();
|
|
mCurrentRun = CurrentNode();
|
|
}
|
|
|
|
void
|
|
nsHtml5Highlighter::StartA()
|
|
{
|
|
FlushChars();
|
|
Push(nsGkAtoms::a, nullptr, NS_NewHTMLAnchorElement);
|
|
AddClass(sAttributeValue);
|
|
++mInlinesOpen;
|
|
}
|
|
|
|
void
|
|
nsHtml5Highlighter::FinishTag()
|
|
{
|
|
while (mInlinesOpen > 1) {
|
|
EndSpanOrA();
|
|
}
|
|
FlushCurrent(); // >
|
|
EndSpanOrA(); // DATA
|
|
NS_ASSERTION(!mInlinesOpen, "mInlinesOpen got out of sync!");
|
|
StartCharacters();
|
|
}
|
|
|
|
void
|
|
nsHtml5Highlighter::FlushChars()
|
|
{
|
|
if (mCStart < mPos) {
|
|
char16_t* buf = mBuffer->getBuffer();
|
|
int32_t i = mCStart;
|
|
while (i < mPos) {
|
|
char16_t c = buf[i];
|
|
switch (c) {
|
|
case '\r':
|
|
// The input this code sees has been normalized so that there are
|
|
// CR breaks and LF breaks but no CRLF breaks. Overwrite CR with LF
|
|
// to show consistent LF line breaks to layout. It is OK to mutate
|
|
// the input data, because there are no reparses in the View Source
|
|
// case, so we won't need the original data in the buffer anymore.
|
|
buf[i] = '\n';
|
|
MOZ_FALLTHROUGH;
|
|
case '\n': {
|
|
++i;
|
|
if (mCStart < i) {
|
|
int32_t len = i - mCStart;
|
|
AppendCharacters(buf, mCStart, len);
|
|
mCStart = i;
|
|
}
|
|
++mLineNumber;
|
|
Push(nsGkAtoms::span, nullptr, NS_NewHTMLSpanElement);
|
|
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
|
|
NS_ASSERTION(treeOp, "Tree op allocation failed.");
|
|
treeOp->InitAddLineNumberId(CurrentNode(), mLineNumber);
|
|
Pop();
|
|
break;
|
|
}
|
|
default:
|
|
++i;
|
|
break;
|
|
}
|
|
}
|
|
if (mCStart < mPos) {
|
|
int32_t len = mPos - mCStart;
|
|
AppendCharacters(buf, mCStart, len);
|
|
mCStart = mPos;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
nsHtml5Highlighter::FlushCurrent()
|
|
{
|
|
mPos++;
|
|
FlushChars();
|
|
}
|
|
|
|
bool
|
|
nsHtml5Highlighter::FlushOps()
|
|
{
|
|
bool hasOps = !mOpQueue.IsEmpty();
|
|
if (hasOps) {
|
|
mOpSink->MoveOpsFrom(mOpQueue);
|
|
}
|
|
return hasOps;
|
|
}
|
|
|
|
void
|
|
nsHtml5Highlighter::MaybeLinkifyAttributeValue(nsHtml5AttributeName* aName,
|
|
nsHtml5String aValue)
|
|
{
|
|
if (!(nsHtml5AttributeName::ATTR_HREF == aName ||
|
|
nsHtml5AttributeName::ATTR_SRC == aName ||
|
|
nsHtml5AttributeName::ATTR_ACTION == aName ||
|
|
nsHtml5AttributeName::ATTR_CITE == aName ||
|
|
nsHtml5AttributeName::ATTR_BACKGROUND == aName ||
|
|
nsHtml5AttributeName::ATTR_LONGDESC == aName ||
|
|
nsHtml5AttributeName::ATTR_XLINK_HREF == aName ||
|
|
nsHtml5AttributeName::ATTR_DEFINITIONURL == aName)) {
|
|
return;
|
|
}
|
|
AddViewSourceHref(aValue);
|
|
}
|
|
|
|
void
|
|
nsHtml5Highlighter::CompletedNamedCharacterReference()
|
|
{
|
|
AddClass(sEntity);
|
|
}
|
|
|
|
nsIContent**
|
|
nsHtml5Highlighter::AllocateContentHandle()
|
|
{
|
|
if (mHandlesUsed == NS_HTML5_HIGHLIGHTER_HANDLE_ARRAY_LENGTH) {
|
|
mOldHandles.AppendElement(Move(mHandles));
|
|
mHandles = MakeUnique<nsIContent*[]>(NS_HTML5_HIGHLIGHTER_HANDLE_ARRAY_LENGTH);
|
|
mHandlesUsed = 0;
|
|
}
|
|
#ifdef DEBUG
|
|
mHandles[mHandlesUsed] = reinterpret_cast<nsIContent*>(uintptr_t(0xC0DEDBAD));
|
|
#endif
|
|
return &mHandles[mHandlesUsed++];
|
|
}
|
|
|
|
nsIContent**
|
|
nsHtml5Highlighter::CreateElement(nsIAtom* aName,
|
|
nsHtml5HtmlAttributes* aAttributes,
|
|
nsIContent** aIntendedParent,
|
|
mozilla::dom::HTMLContentCreatorFunction aCreator)
|
|
{
|
|
NS_PRECONDITION(aName, "Got null name.");
|
|
nsHtml5ContentCreatorFunction creator;
|
|
creator.html = aCreator;
|
|
nsIContent** content = AllocateContentHandle();
|
|
mOpQueue.AppendElement()->Init(kNameSpaceID_XHTML,
|
|
aName,
|
|
aAttributes,
|
|
content,
|
|
aIntendedParent,
|
|
true,
|
|
creator);
|
|
return content;
|
|
}
|
|
|
|
nsIContent**
|
|
nsHtml5Highlighter::CurrentNode()
|
|
{
|
|
NS_PRECONDITION(mStack.Length() >= 1, "Must have something on stack.");
|
|
return mStack[mStack.Length() - 1];
|
|
}
|
|
|
|
void
|
|
nsHtml5Highlighter::Push(nsIAtom* aName,
|
|
nsHtml5HtmlAttributes* aAttributes,
|
|
mozilla::dom::HTMLContentCreatorFunction aCreator)
|
|
{
|
|
NS_PRECONDITION(mStack.Length() >= 1, "Pushing without root.");
|
|
nsIContent** elt = CreateElement(aName, aAttributes,
|
|
CurrentNode(),
|
|
aCreator); // Don't inline below!
|
|
mOpQueue.AppendElement()->Init(eTreeOpAppend, elt, CurrentNode());
|
|
mStack.AppendElement(elt);
|
|
}
|
|
|
|
void
|
|
nsHtml5Highlighter::Pop()
|
|
{
|
|
NS_PRECONDITION(mStack.Length() >= 2, "Popping when stack too short.");
|
|
mStack.RemoveElementAt(mStack.Length() - 1);
|
|
}
|
|
|
|
void
|
|
nsHtml5Highlighter::AppendCharacters(const char16_t* aBuffer,
|
|
int32_t aStart,
|
|
int32_t aLength)
|
|
{
|
|
NS_PRECONDITION(aBuffer, "Null buffer");
|
|
|
|
char16_t* bufferCopy = new char16_t[aLength];
|
|
memcpy(bufferCopy, aBuffer + aStart, aLength * sizeof(char16_t));
|
|
|
|
mOpQueue.AppendElement()->Init(eTreeOpAppendText,
|
|
bufferCopy,
|
|
aLength,
|
|
CurrentNode());
|
|
}
|
|
|
|
|
|
void
|
|
nsHtml5Highlighter::AddClass(const char16_t* aClass)
|
|
{
|
|
mOpQueue.AppendElement()->InitAddClass(CurrentNode(), aClass);
|
|
}
|
|
|
|
void
|
|
nsHtml5Highlighter::AddViewSourceHref(nsHtml5String aValue)
|
|
{
|
|
char16_t* bufferCopy = new char16_t[aValue.Length() + 1];
|
|
aValue.CopyToBuffer(bufferCopy);
|
|
bufferCopy[aValue.Length()] = 0;
|
|
|
|
mOpQueue.AppendElement()->Init(eTreeOpAddViewSourceHref,
|
|
bufferCopy,
|
|
aValue.Length(),
|
|
CurrentNode());
|
|
}
|
|
|
|
void
|
|
nsHtml5Highlighter::AddBase(nsHtml5String aValue)
|
|
{
|
|
if(mSeenBase) {
|
|
return;
|
|
}
|
|
mSeenBase = true;
|
|
char16_t* bufferCopy = new char16_t[aValue.Length() + 1];
|
|
aValue.CopyToBuffer(bufferCopy);
|
|
bufferCopy[aValue.Length()] = 0;
|
|
|
|
mOpQueue.AppendElement()->Init(eTreeOpAddViewSourceBase,
|
|
bufferCopy,
|
|
aValue.Length());
|
|
}
|
|
|
|
void
|
|
nsHtml5Highlighter::AddErrorToCurrentNode(const char* aMsgId)
|
|
{
|
|
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
|
|
NS_ASSERTION(treeOp, "Tree op allocation failed.");
|
|
treeOp->Init(CurrentNode(), aMsgId);
|
|
}
|
|
|
|
void
|
|
nsHtml5Highlighter::AddErrorToCurrentRun(const char* aMsgId)
|
|
{
|
|
NS_PRECONDITION(mCurrentRun, "Adding error to run without one!");
|
|
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
|
|
NS_ASSERTION(treeOp, "Tree op allocation failed.");
|
|
treeOp->Init(mCurrentRun, aMsgId);
|
|
}
|
|
|
|
void
|
|
nsHtml5Highlighter::AddErrorToCurrentRun(const char* aMsgId,
|
|
nsIAtom* aName)
|
|
{
|
|
NS_PRECONDITION(mCurrentRun, "Adding error to run without one!");
|
|
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
|
|
NS_ASSERTION(treeOp, "Tree op allocation failed.");
|
|
treeOp->Init(mCurrentRun, aMsgId, aName);
|
|
}
|
|
|
|
void
|
|
nsHtml5Highlighter::AddErrorToCurrentRun(const char* aMsgId,
|
|
nsIAtom* aName,
|
|
nsIAtom* aOther)
|
|
{
|
|
NS_PRECONDITION(mCurrentRun, "Adding error to run without one!");
|
|
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
|
|
NS_ASSERTION(treeOp, "Tree op allocation failed.");
|
|
treeOp->Init(mCurrentRun, aMsgId, aName, aOther);
|
|
}
|
|
|
|
void
|
|
nsHtml5Highlighter::AddErrorToCurrentAmpersand(const char* aMsgId)
|
|
{
|
|
NS_PRECONDITION(mAmpersand, "Adding error to ampersand without one!");
|
|
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
|
|
NS_ASSERTION(treeOp, "Tree op allocation failed.");
|
|
treeOp->Init(mAmpersand, aMsgId);
|
|
}
|
|
|
|
void
|
|
nsHtml5Highlighter::AddErrorToCurrentSlash(const char* aMsgId)
|
|
{
|
|
NS_PRECONDITION(mSlash, "Adding error to slash without one!");
|
|
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
|
|
NS_ASSERTION(treeOp, "Tree op allocation failed.");
|
|
treeOp->Init(mSlash, aMsgId);
|
|
}
|