michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsHtml5Highlighter.h" michael@0: #include "nsDebug.h" michael@0: #include "nsHtml5Tokenizer.h" michael@0: #include "nsHtml5AttributeName.h" michael@0: #include "nsString.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsHtml5ViewSourceUtils.h" michael@0: #include "mozilla/Preferences.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: // The old code had a limit of 16 tokens. 1300 is a number picked my measuring michael@0: // the size of 16 tokens on cnn.com. michael@0: #define NS_HTML5_HIGHLIGHTER_PRE_BREAK_THRESHOLD 1300 michael@0: michael@0: char16_t nsHtml5Highlighter::sComment[] = michael@0: { 'c', 'o', 'm', 'm', 'e', 'n', 't', 0 }; michael@0: michael@0: char16_t nsHtml5Highlighter::sCdata[] = michael@0: { 'c', 'd', 'a', 't', 'a', 0 }; michael@0: michael@0: char16_t nsHtml5Highlighter::sEntity[] = michael@0: { 'e', 'n', 't', 'i', 't', 'y', 0 }; michael@0: michael@0: char16_t nsHtml5Highlighter::sEndTag[] = michael@0: { 'e', 'n', 'd', '-', 't', 'a', 'g', 0 }; michael@0: michael@0: char16_t nsHtml5Highlighter::sStartTag[] = michael@0: { 's', 't', 'a', 'r', 't', '-', 't', 'a', 'g', 0 }; michael@0: michael@0: char16_t nsHtml5Highlighter::sAttributeName[] = michael@0: { 'a', 't', 't', 'r', 'i', 'b', 'u', 't', 'e', '-', 'n', 'a', 'm', 'e', 0 }; michael@0: michael@0: char16_t nsHtml5Highlighter::sAttributeValue[] = michael@0: { 'a', 't', 't', 'r', 'i', 'b', 'u', 't', 'e', '-', michael@0: 'v', 'a', 'l', 'u', 'e', 0 }; michael@0: michael@0: char16_t nsHtml5Highlighter::sDoctype[] = michael@0: { 'd', 'o', 'c', 't', 'y', 'p', 'e', 0 }; michael@0: michael@0: char16_t nsHtml5Highlighter::sPi[] = michael@0: { 'p', 'i', 0 }; michael@0: michael@0: nsHtml5Highlighter::nsHtml5Highlighter(nsAHtml5TreeOpSink* aOpSink) michael@0: : mState(NS_HTML5TOKENIZER_DATA) michael@0: , mCStart(INT32_MAX) michael@0: , mPos(0) michael@0: , mLineNumber(1) michael@0: , mInlinesOpen(0) michael@0: , mInCharacters(false) michael@0: , mBuffer(nullptr) michael@0: , mSyntaxHighlight(Preferences::GetBool("view_source.syntax_highlight", michael@0: true)) michael@0: , mOpSink(aOpSink) michael@0: , mCurrentRun(nullptr) michael@0: , mAmpersand(nullptr) michael@0: , mSlash(nullptr) michael@0: , mHandles(new nsIContent*[NS_HTML5_HIGHLIGHTER_HANDLE_ARRAY_LENGTH]) michael@0: , mHandlesUsed(0) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: } michael@0: michael@0: nsHtml5Highlighter::~nsHtml5Highlighter() michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: } michael@0: michael@0: void michael@0: nsHtml5Highlighter::Start(const nsAutoString& aTitle) michael@0: { michael@0: // Doctype michael@0: mOpQueue.AppendElement()->Init(nsGkAtoms::html, EmptyString(), EmptyString()); michael@0: michael@0: mOpQueue.AppendElement()->Init(STANDARDS_MODE); michael@0: michael@0: nsIContent** root = CreateElement(nsHtml5Atoms::html, nullptr); michael@0: mOpQueue.AppendElement()->Init(eTreeOpAppendToDocument, root); michael@0: mStack.AppendElement(root); michael@0: michael@0: Push(nsGkAtoms::head, nullptr); michael@0: michael@0: Push(nsGkAtoms::title, nullptr); michael@0: // XUL will add the "Source of: " prefix. michael@0: uint32_t length = aTitle.Length(); michael@0: if (length > INT32_MAX) { michael@0: length = INT32_MAX; michael@0: } michael@0: AppendCharacters(aTitle.get(), 0, (int32_t)length); michael@0: Pop(); // title michael@0: michael@0: Push(nsGkAtoms::link, nsHtml5ViewSourceUtils::NewLinkAttributes()); michael@0: michael@0: mOpQueue.AppendElement()->Init(eTreeOpUpdateStyleSheet, CurrentNode()); michael@0: michael@0: Pop(); // link michael@0: michael@0: Pop(); // head michael@0: michael@0: Push(nsGkAtoms::body, nsHtml5ViewSourceUtils::NewBodyAttributes()); michael@0: michael@0: nsHtml5HtmlAttributes* preAttrs = new nsHtml5HtmlAttributes(0); michael@0: nsString* preId = new nsString(NS_LITERAL_STRING("line1")); michael@0: preAttrs->addAttribute(nsHtml5AttributeName::ATTR_ID, preId); michael@0: Push(nsGkAtoms::pre, preAttrs); michael@0: michael@0: StartCharacters(); michael@0: michael@0: mOpQueue.AppendElement()->Init(eTreeOpStartLayout); michael@0: } michael@0: michael@0: int32_t michael@0: nsHtml5Highlighter::Transition(int32_t aState, bool aReconsume, int32_t aPos) michael@0: { michael@0: mPos = aPos; michael@0: switch (mState) { michael@0: case NS_HTML5TOKENIZER_SCRIPT_DATA: michael@0: case NS_HTML5TOKENIZER_RAWTEXT: michael@0: case NS_HTML5TOKENIZER_RCDATA: michael@0: case NS_HTML5TOKENIZER_DATA: michael@0: // We can transition on < and on &. Either way, we don't yet know the michael@0: // role of the token, so open a span without class. michael@0: if (aState == NS_HTML5TOKENIZER_CONSUME_CHARACTER_REFERENCE) { michael@0: StartSpan(); michael@0: // Start another span for highlighting the ampersand michael@0: StartSpan(); michael@0: mAmpersand = CurrentNode(); michael@0: } else { michael@0: EndCharactersAndStartMarkupRun(); michael@0: } michael@0: break; michael@0: case NS_HTML5TOKENIZER_TAG_OPEN: michael@0: switch (aState) { michael@0: case NS_HTML5TOKENIZER_TAG_NAME: michael@0: StartSpan(sStartTag); michael@0: break; michael@0: case NS_HTML5TOKENIZER_DATA: michael@0: FinishTag(); // DATA michael@0: break; michael@0: case NS_HTML5TOKENIZER_PROCESSING_INSTRUCTION: michael@0: AddClass(sPi); michael@0: break; michael@0: } michael@0: break; michael@0: case NS_HTML5TOKENIZER_TAG_NAME: michael@0: switch (aState) { michael@0: case NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME: michael@0: EndSpanOrA(); // NS_HTML5TOKENIZER_TAG_NAME michael@0: break; michael@0: case NS_HTML5TOKENIZER_SELF_CLOSING_START_TAG: michael@0: EndSpanOrA(); // NS_HTML5TOKENIZER_TAG_NAME michael@0: StartSpan(); // for highlighting the slash michael@0: mSlash = CurrentNode(); michael@0: break; michael@0: default: michael@0: FinishTag(); michael@0: break; michael@0: } michael@0: break; michael@0: case NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME: michael@0: switch (aState) { michael@0: case NS_HTML5TOKENIZER_ATTRIBUTE_NAME: michael@0: StartSpan(sAttributeName); michael@0: break; michael@0: case NS_HTML5TOKENIZER_SELF_CLOSING_START_TAG: michael@0: StartSpan(); // for highlighting the slash michael@0: mSlash = CurrentNode(); michael@0: break; michael@0: default: michael@0: FinishTag(); michael@0: break; michael@0: } michael@0: break; michael@0: case NS_HTML5TOKENIZER_ATTRIBUTE_NAME: michael@0: switch (aState) { michael@0: case NS_HTML5TOKENIZER_AFTER_ATTRIBUTE_NAME: michael@0: case NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_VALUE: michael@0: EndSpanOrA(); // NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME michael@0: break; michael@0: case NS_HTML5TOKENIZER_SELF_CLOSING_START_TAG: michael@0: EndSpanOrA(); // NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME michael@0: StartSpan(); // for highlighting the slash michael@0: mSlash = CurrentNode(); michael@0: break; michael@0: default: michael@0: FinishTag(); michael@0: break; michael@0: } michael@0: break; michael@0: case NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_VALUE: michael@0: switch (aState) { michael@0: case NS_HTML5TOKENIZER_ATTRIBUTE_VALUE_DOUBLE_QUOTED: michael@0: case NS_HTML5TOKENIZER_ATTRIBUTE_VALUE_SINGLE_QUOTED: michael@0: FlushCurrent(); michael@0: StartA(); michael@0: break; michael@0: case NS_HTML5TOKENIZER_ATTRIBUTE_VALUE_UNQUOTED: michael@0: StartA(); michael@0: break; michael@0: default: michael@0: FinishTag(); michael@0: break; michael@0: } michael@0: break; michael@0: case NS_HTML5TOKENIZER_ATTRIBUTE_VALUE_DOUBLE_QUOTED: michael@0: case NS_HTML5TOKENIZER_ATTRIBUTE_VALUE_SINGLE_QUOTED: michael@0: switch (aState) { michael@0: case NS_HTML5TOKENIZER_AFTER_ATTRIBUTE_VALUE_QUOTED: michael@0: EndSpanOrA(); michael@0: break; michael@0: case NS_HTML5TOKENIZER_CONSUME_CHARACTER_REFERENCE: michael@0: StartSpan(); michael@0: StartSpan(); // for ampersand itself michael@0: mAmpersand = CurrentNode(); michael@0: break; michael@0: default: michael@0: NS_NOTREACHED("Impossible transition."); michael@0: break; michael@0: } michael@0: break; michael@0: case NS_HTML5TOKENIZER_AFTER_ATTRIBUTE_VALUE_QUOTED: michael@0: switch (aState) { michael@0: case NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME: michael@0: break; michael@0: case NS_HTML5TOKENIZER_SELF_CLOSING_START_TAG: michael@0: StartSpan(); // for highlighting the slash michael@0: mSlash = CurrentNode(); michael@0: break; michael@0: default: michael@0: FinishTag(); michael@0: break; michael@0: } michael@0: break; michael@0: case NS_HTML5TOKENIZER_SELF_CLOSING_START_TAG: michael@0: EndSpanOrA(); // end the slash highlight michael@0: switch (aState) { michael@0: case NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME: michael@0: break; michael@0: default: michael@0: FinishTag(); michael@0: break; michael@0: } michael@0: break; michael@0: case NS_HTML5TOKENIZER_ATTRIBUTE_VALUE_UNQUOTED: michael@0: switch (aState) { michael@0: case NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME: michael@0: EndSpanOrA(); michael@0: break; michael@0: case NS_HTML5TOKENIZER_CONSUME_CHARACTER_REFERENCE: michael@0: StartSpan(); michael@0: StartSpan(); // for ampersand itself michael@0: mAmpersand = CurrentNode(); michael@0: break; michael@0: default: michael@0: FinishTag(); michael@0: break; michael@0: } michael@0: break; michael@0: case NS_HTML5TOKENIZER_AFTER_ATTRIBUTE_NAME: michael@0: switch (aState) { michael@0: case NS_HTML5TOKENIZER_SELF_CLOSING_START_TAG: michael@0: StartSpan(); // for highlighting the slash michael@0: mSlash = CurrentNode(); michael@0: break; michael@0: case NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_VALUE: michael@0: break; michael@0: case NS_HTML5TOKENIZER_ATTRIBUTE_NAME: michael@0: StartSpan(sAttributeName); michael@0: break; michael@0: default: michael@0: FinishTag(); michael@0: break; michael@0: } michael@0: break; michael@0: // most comment states are omitted, because they don't matter to michael@0: // highlighting michael@0: case NS_HTML5TOKENIZER_COMMENT_START: michael@0: case NS_HTML5TOKENIZER_COMMENT_END: michael@0: case NS_HTML5TOKENIZER_COMMENT_END_BANG: michael@0: case NS_HTML5TOKENIZER_COMMENT_START_DASH: michael@0: case NS_HTML5TOKENIZER_BOGUS_COMMENT: michael@0: case NS_HTML5TOKENIZER_BOGUS_COMMENT_HYPHEN: michael@0: if (aState == NS_HTML5TOKENIZER_DATA) { michael@0: AddClass(sComment); michael@0: FinishTag(); michael@0: } michael@0: break; michael@0: // most cdata states are omitted, because they don't matter to michael@0: // highlighting michael@0: case NS_HTML5TOKENIZER_CDATA_RSQB_RSQB: michael@0: if (aState == NS_HTML5TOKENIZER_DATA) { michael@0: AddClass(sCdata); michael@0: FinishTag(); michael@0: } michael@0: break; michael@0: case NS_HTML5TOKENIZER_CONSUME_CHARACTER_REFERENCE: michael@0: EndSpanOrA(); // the span for the ampersand michael@0: switch (aState) { michael@0: case NS_HTML5TOKENIZER_CONSUME_NCR: michael@0: case NS_HTML5TOKENIZER_CHARACTER_REFERENCE_HILO_LOOKUP: michael@0: break; michael@0: default: michael@0: // not actually a character reference michael@0: EndSpanOrA(); michael@0: break; michael@0: } michael@0: break; michael@0: case NS_HTML5TOKENIZER_CHARACTER_REFERENCE_HILO_LOOKUP: michael@0: if (aState == NS_HTML5TOKENIZER_CHARACTER_REFERENCE_TAIL) { michael@0: break; michael@0: } michael@0: // not actually a character reference michael@0: EndSpanOrA(); michael@0: break; michael@0: case NS_HTML5TOKENIZER_CHARACTER_REFERENCE_TAIL: michael@0: if (!aReconsume) { michael@0: FlushCurrent(); michael@0: } michael@0: EndSpanOrA(); michael@0: break; michael@0: case NS_HTML5TOKENIZER_DECIMAL_NRC_LOOP: michael@0: case NS_HTML5TOKENIZER_HEX_NCR_LOOP: michael@0: switch (aState) { michael@0: case NS_HTML5TOKENIZER_HANDLE_NCR_VALUE: michael@0: AddClass(sEntity); michael@0: FlushCurrent(); michael@0: break; michael@0: case NS_HTML5TOKENIZER_HANDLE_NCR_VALUE_RECONSUME: michael@0: AddClass(sEntity); michael@0: break; michael@0: } michael@0: EndSpanOrA(); michael@0: break; michael@0: case NS_HTML5TOKENIZER_CLOSE_TAG_OPEN: michael@0: switch (aState) { michael@0: case NS_HTML5TOKENIZER_DATA: michael@0: FinishTag(); michael@0: break; michael@0: case NS_HTML5TOKENIZER_TAG_NAME: michael@0: StartSpan(sEndTag); michael@0: break; michael@0: } michael@0: break; michael@0: case NS_HTML5TOKENIZER_RAWTEXT_RCDATA_LESS_THAN_SIGN: michael@0: if (aState == NS_HTML5TOKENIZER_NON_DATA_END_TAG_NAME) { michael@0: FlushCurrent(); michael@0: StartSpan(); // don't know if it is "end-tag" yet :-( michael@0: break; michael@0: } michael@0: EndSpanOrA(); michael@0: StartCharacters(); michael@0: break; michael@0: case NS_HTML5TOKENIZER_NON_DATA_END_TAG_NAME: michael@0: switch (aState) { michael@0: case NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME: michael@0: AddClass(sEndTag); michael@0: EndSpanOrA(); michael@0: break; michael@0: case NS_HTML5TOKENIZER_SELF_CLOSING_START_TAG: michael@0: AddClass(sEndTag); michael@0: EndSpanOrA(); michael@0: StartSpan(); // for highlighting the slash michael@0: mSlash = CurrentNode(); michael@0: break; michael@0: case NS_HTML5TOKENIZER_DATA: // yes, as a result of emitting the token michael@0: AddClass(sEndTag); michael@0: FinishTag(); michael@0: break; michael@0: default: michael@0: FinishTag(); michael@0: break; michael@0: } michael@0: break; michael@0: case NS_HTML5TOKENIZER_SCRIPT_DATA_LESS_THAN_SIGN: michael@0: case NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN: michael@0: if (aState == NS_HTML5TOKENIZER_NON_DATA_END_TAG_NAME) { michael@0: FlushCurrent(); michael@0: StartSpan(); // don't know if it is "end-tag" yet :-( michael@0: break; michael@0: } michael@0: FinishTag(); michael@0: break; michael@0: case NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED_DASH_DASH: michael@0: case NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED: michael@0: case NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED_DASH: michael@0: if (aState == NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN) { michael@0: EndCharactersAndStartMarkupRun(); michael@0: } michael@0: break; michael@0: // Lots of double escape states omitted, because they don't highlight. michael@0: // Likewise, only doctype states that can emit the doctype are of michael@0: // interest. Otherwise, the transition out of bogus comment deals. michael@0: case NS_HTML5TOKENIZER_BEFORE_DOCTYPE_NAME: michael@0: case NS_HTML5TOKENIZER_DOCTYPE_NAME: michael@0: case NS_HTML5TOKENIZER_AFTER_DOCTYPE_NAME: michael@0: case NS_HTML5TOKENIZER_AFTER_DOCTYPE_PUBLIC_KEYWORD: michael@0: case NS_HTML5TOKENIZER_BEFORE_DOCTYPE_PUBLIC_IDENTIFIER: michael@0: case NS_HTML5TOKENIZER_DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED: michael@0: case NS_HTML5TOKENIZER_AFTER_DOCTYPE_PUBLIC_IDENTIFIER: michael@0: case NS_HTML5TOKENIZER_BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS: michael@0: case NS_HTML5TOKENIZER_DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED: michael@0: case NS_HTML5TOKENIZER_AFTER_DOCTYPE_SYSTEM_IDENTIFIER: michael@0: case NS_HTML5TOKENIZER_BOGUS_DOCTYPE: michael@0: case NS_HTML5TOKENIZER_AFTER_DOCTYPE_SYSTEM_KEYWORD: michael@0: case NS_HTML5TOKENIZER_BEFORE_DOCTYPE_SYSTEM_IDENTIFIER: michael@0: case NS_HTML5TOKENIZER_DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED: michael@0: case NS_HTML5TOKENIZER_DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED: michael@0: if (aState == NS_HTML5TOKENIZER_DATA) { michael@0: AddClass(sDoctype); michael@0: FinishTag(); michael@0: } michael@0: break; michael@0: case NS_HTML5TOKENIZER_PROCESSING_INSTRUCTION_QUESTION_MARK: michael@0: if (aState == NS_HTML5TOKENIZER_DATA) { michael@0: FinishTag(); michael@0: } michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: mState = aState; michael@0: return aState; michael@0: } michael@0: michael@0: void michael@0: nsHtml5Highlighter::End() michael@0: { michael@0: switch (mState) { michael@0: case NS_HTML5TOKENIZER_COMMENT_END: michael@0: case NS_HTML5TOKENIZER_COMMENT_END_BANG: michael@0: case NS_HTML5TOKENIZER_COMMENT_START_DASH: michael@0: case NS_HTML5TOKENIZER_BOGUS_COMMENT: michael@0: case NS_HTML5TOKENIZER_BOGUS_COMMENT_HYPHEN: michael@0: AddClass(sComment); michael@0: break; michael@0: case NS_HTML5TOKENIZER_CDATA_RSQB_RSQB: michael@0: AddClass(sCdata); michael@0: break; michael@0: case NS_HTML5TOKENIZER_DECIMAL_NRC_LOOP: michael@0: case NS_HTML5TOKENIZER_HEX_NCR_LOOP: michael@0: // XXX need tokenizer help here michael@0: break; michael@0: case NS_HTML5TOKENIZER_BEFORE_DOCTYPE_NAME: michael@0: case NS_HTML5TOKENIZER_DOCTYPE_NAME: michael@0: case NS_HTML5TOKENIZER_AFTER_DOCTYPE_NAME: michael@0: case NS_HTML5TOKENIZER_AFTER_DOCTYPE_PUBLIC_KEYWORD: michael@0: case NS_HTML5TOKENIZER_BEFORE_DOCTYPE_PUBLIC_IDENTIFIER: michael@0: case NS_HTML5TOKENIZER_DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED: michael@0: case NS_HTML5TOKENIZER_AFTER_DOCTYPE_PUBLIC_IDENTIFIER: michael@0: case NS_HTML5TOKENIZER_BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS: michael@0: case NS_HTML5TOKENIZER_DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED: michael@0: case NS_HTML5TOKENIZER_AFTER_DOCTYPE_SYSTEM_IDENTIFIER: michael@0: case NS_HTML5TOKENIZER_BOGUS_DOCTYPE: michael@0: case NS_HTML5TOKENIZER_AFTER_DOCTYPE_SYSTEM_KEYWORD: michael@0: case NS_HTML5TOKENIZER_BEFORE_DOCTYPE_SYSTEM_IDENTIFIER: michael@0: case NS_HTML5TOKENIZER_DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED: michael@0: case NS_HTML5TOKENIZER_DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED: michael@0: AddClass(sDoctype); michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: treeOp->Init(eTreeOpStreamEnded); michael@0: FlushOps(); michael@0: } michael@0: michael@0: void michael@0: nsHtml5Highlighter::SetBuffer(nsHtml5UTF16Buffer* aBuffer) michael@0: { michael@0: NS_PRECONDITION(!mBuffer, "Old buffer still here!"); michael@0: mBuffer = aBuffer; michael@0: mCStart = aBuffer->getStart(); michael@0: } michael@0: michael@0: void michael@0: nsHtml5Highlighter::DropBuffer(int32_t aPos) michael@0: { michael@0: NS_PRECONDITION(mBuffer, "No buffer to drop!"); michael@0: mPos = aPos; michael@0: FlushChars(); michael@0: mBuffer = nullptr; michael@0: } michael@0: michael@0: void michael@0: nsHtml5Highlighter::StartSpan() michael@0: { michael@0: FlushChars(); michael@0: Push(nsGkAtoms::span, nullptr); michael@0: ++mInlinesOpen; michael@0: } michael@0: michael@0: void michael@0: nsHtml5Highlighter::StartSpan(const char16_t* aClass) michael@0: { michael@0: StartSpan(); michael@0: AddClass(aClass); michael@0: } michael@0: michael@0: void michael@0: nsHtml5Highlighter::EndSpanOrA() michael@0: { michael@0: FlushChars(); michael@0: Pop(); michael@0: --mInlinesOpen; michael@0: } michael@0: michael@0: void michael@0: nsHtml5Highlighter::StartCharacters() michael@0: { michael@0: NS_PRECONDITION(!mInCharacters, "Already in characters!"); michael@0: FlushChars(); michael@0: Push(nsGkAtoms::span, nullptr); michael@0: mCurrentRun = CurrentNode(); michael@0: mInCharacters = true; michael@0: } michael@0: michael@0: void michael@0: nsHtml5Highlighter::EndCharactersAndStartMarkupRun() michael@0: { michael@0: NS_PRECONDITION(mInCharacters, "Not in characters!"); michael@0: FlushChars(); michael@0: Pop(); michael@0: mInCharacters = false; michael@0: // Now start markup run michael@0: StartSpan(); michael@0: mCurrentRun = CurrentNode(); michael@0: } michael@0: michael@0: void michael@0: nsHtml5Highlighter::StartA() michael@0: { michael@0: FlushChars(); michael@0: Push(nsGkAtoms::a, nullptr); michael@0: AddClass(sAttributeValue); michael@0: ++mInlinesOpen; michael@0: } michael@0: michael@0: void michael@0: nsHtml5Highlighter::FinishTag() michael@0: { michael@0: while (mInlinesOpen > 1) { michael@0: EndSpanOrA(); michael@0: } michael@0: FlushCurrent(); // > michael@0: EndSpanOrA(); // DATA michael@0: NS_ASSERTION(!mInlinesOpen, "mInlinesOpen got out of sync!"); michael@0: StartCharacters(); michael@0: } michael@0: michael@0: void michael@0: nsHtml5Highlighter::FlushChars() michael@0: { michael@0: if (mCStart < mPos) { michael@0: char16_t* buf = mBuffer->getBuffer(); michael@0: int32_t i = mCStart; michael@0: while (i < mPos) { michael@0: char16_t c = buf[i]; michael@0: switch (c) { michael@0: case '\r': michael@0: // The input this code sees has been normalized so that there are michael@0: // CR breaks and LF breaks but no CRLF breaks. Overwrite CR with LF michael@0: // to show consistent LF line breaks to layout. It is OK to mutate michael@0: // the input data, because there are no reparses in the View Source michael@0: // case, so we won't need the original data in the buffer anymore. michael@0: buf[i] = '\n'; michael@0: // fall through michael@0: case '\n': { michael@0: ++i; michael@0: if (mCStart < i) { michael@0: int32_t len = i - mCStart; michael@0: AppendCharacters(buf, mCStart, len); michael@0: mCStart = i; michael@0: } michael@0: ++mLineNumber; michael@0: Push(nsGkAtoms::span, nullptr); michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: treeOp->InitAddLineNumberId(CurrentNode(), mLineNumber); michael@0: Pop(); michael@0: break; michael@0: } michael@0: default: michael@0: ++i; michael@0: break; michael@0: } michael@0: } michael@0: if (mCStart < mPos) { michael@0: int32_t len = mPos - mCStart; michael@0: AppendCharacters(buf, mCStart, len); michael@0: mCStart = mPos; michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5Highlighter::FlushCurrent() michael@0: { michael@0: mPos++; michael@0: FlushChars(); michael@0: } michael@0: michael@0: bool michael@0: nsHtml5Highlighter::FlushOps() michael@0: { michael@0: bool hasOps = !mOpQueue.IsEmpty(); michael@0: if (hasOps) { michael@0: mOpSink->MoveOpsFrom(mOpQueue); michael@0: } michael@0: return hasOps; michael@0: } michael@0: michael@0: void michael@0: nsHtml5Highlighter::MaybeLinkifyAttributeValue(nsHtml5AttributeName* aName, michael@0: nsString* aValue) michael@0: { michael@0: if (!(nsHtml5AttributeName::ATTR_HREF == aName || michael@0: nsHtml5AttributeName::ATTR_SRC == aName || michael@0: nsHtml5AttributeName::ATTR_ACTION == aName || michael@0: nsHtml5AttributeName::ATTR_CITE == aName || michael@0: nsHtml5AttributeName::ATTR_BACKGROUND == aName || michael@0: nsHtml5AttributeName::ATTR_LONGDESC == aName || michael@0: nsHtml5AttributeName::ATTR_XLINK_HREF == aName || michael@0: nsHtml5AttributeName::ATTR_DEFINITIONURL == aName)) { michael@0: return; michael@0: } michael@0: AddViewSourceHref(*aValue); michael@0: } michael@0: michael@0: void michael@0: nsHtml5Highlighter::CompletedNamedCharacterReference() michael@0: { michael@0: AddClass(sEntity); michael@0: } michael@0: michael@0: nsIContent** michael@0: nsHtml5Highlighter::AllocateContentHandle() michael@0: { michael@0: if (mHandlesUsed == NS_HTML5_HIGHLIGHTER_HANDLE_ARRAY_LENGTH) { michael@0: mOldHandles.AppendElement(mHandles.forget()); michael@0: mHandles = new nsIContent*[NS_HTML5_HIGHLIGHTER_HANDLE_ARRAY_LENGTH]; michael@0: mHandlesUsed = 0; michael@0: } michael@0: #ifdef DEBUG michael@0: mHandles[mHandlesUsed] = (nsIContent*)0xC0DEDBAD; michael@0: #endif michael@0: return &mHandles[mHandlesUsed++]; michael@0: } michael@0: michael@0: nsIContent** michael@0: nsHtml5Highlighter::CreateElement(nsIAtom* aName, michael@0: nsHtml5HtmlAttributes* aAttributes) michael@0: { michael@0: NS_PRECONDITION(aName, "Got null name."); michael@0: nsIContent** content = AllocateContentHandle(); michael@0: mOpQueue.AppendElement()->Init(kNameSpaceID_XHTML, michael@0: aName, michael@0: aAttributes, michael@0: content, michael@0: true); michael@0: return content; michael@0: } michael@0: michael@0: nsIContent** michael@0: nsHtml5Highlighter::CurrentNode() michael@0: { michael@0: NS_PRECONDITION(mStack.Length() >= 1, "Must have something on stack."); michael@0: return mStack[mStack.Length() - 1]; michael@0: } michael@0: michael@0: void michael@0: nsHtml5Highlighter::Push(nsIAtom* aName, michael@0: nsHtml5HtmlAttributes* aAttributes) michael@0: { michael@0: NS_PRECONDITION(mStack.Length() >= 1, "Pushing without root."); michael@0: nsIContent** elt = CreateElement(aName, aAttributes); // Don't inline below! michael@0: mOpQueue.AppendElement()->Init(eTreeOpAppend, elt, CurrentNode()); michael@0: mStack.AppendElement(elt); michael@0: } michael@0: michael@0: void michael@0: nsHtml5Highlighter::Pop() michael@0: { michael@0: NS_PRECONDITION(mStack.Length() >= 2, "Popping when stack too short."); michael@0: mStack.RemoveElementAt(mStack.Length() - 1); michael@0: } michael@0: michael@0: void michael@0: nsHtml5Highlighter::AppendCharacters(const char16_t* aBuffer, michael@0: int32_t aStart, michael@0: int32_t aLength) michael@0: { michael@0: NS_PRECONDITION(aBuffer, "Null buffer"); michael@0: michael@0: char16_t* bufferCopy = new char16_t[aLength]; michael@0: memcpy(bufferCopy, aBuffer + aStart, aLength * sizeof(char16_t)); michael@0: michael@0: mOpQueue.AppendElement()->Init(eTreeOpAppendText, michael@0: bufferCopy, michael@0: aLength, michael@0: CurrentNode()); michael@0: } michael@0: michael@0: michael@0: void michael@0: nsHtml5Highlighter::AddClass(const char16_t* aClass) michael@0: { michael@0: if (!mSyntaxHighlight) { michael@0: return; michael@0: } michael@0: mOpQueue.AppendElement()->InitAddClass(CurrentNode(), aClass); michael@0: } michael@0: michael@0: void michael@0: nsHtml5Highlighter::AddViewSourceHref(const nsString& aValue) michael@0: { michael@0: char16_t* bufferCopy = new char16_t[aValue.Length() + 1]; michael@0: memcpy(bufferCopy, aValue.get(), aValue.Length() * sizeof(char16_t)); michael@0: bufferCopy[aValue.Length()] = 0; michael@0: michael@0: mOpQueue.AppendElement()->Init(eTreeOpAddViewSourceHref, michael@0: bufferCopy, michael@0: aValue.Length(), michael@0: CurrentNode()); michael@0: } michael@0: michael@0: void michael@0: nsHtml5Highlighter::AddErrorToCurrentNode(const char* aMsgId) michael@0: { michael@0: if (!mSyntaxHighlight) { michael@0: return; michael@0: } michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: treeOp->Init(CurrentNode(), aMsgId); michael@0: } michael@0: michael@0: void michael@0: nsHtml5Highlighter::AddErrorToCurrentRun(const char* aMsgId) michael@0: { michael@0: if (!mSyntaxHighlight) { michael@0: return; michael@0: } michael@0: NS_PRECONDITION(mCurrentRun, "Adding error to run without one!"); michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: treeOp->Init(mCurrentRun, aMsgId); michael@0: } michael@0: michael@0: void michael@0: nsHtml5Highlighter::AddErrorToCurrentRun(const char* aMsgId, michael@0: nsIAtom* aName) michael@0: { michael@0: if (!mSyntaxHighlight) { michael@0: return; michael@0: } michael@0: NS_PRECONDITION(mCurrentRun, "Adding error to run without one!"); michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: treeOp->Init(mCurrentRun, aMsgId, aName); michael@0: } michael@0: michael@0: void michael@0: nsHtml5Highlighter::AddErrorToCurrentRun(const char* aMsgId, michael@0: nsIAtom* aName, michael@0: nsIAtom* aOther) michael@0: { michael@0: if (!mSyntaxHighlight) { michael@0: return; michael@0: } michael@0: NS_PRECONDITION(mCurrentRun, "Adding error to run without one!"); michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: treeOp->Init(mCurrentRun, aMsgId, aName, aOther); michael@0: } michael@0: michael@0: void michael@0: nsHtml5Highlighter::AddErrorToCurrentAmpersand(const char* aMsgId) michael@0: { michael@0: if (!mSyntaxHighlight) { michael@0: return; michael@0: } michael@0: NS_PRECONDITION(mAmpersand, "Adding error to ampersand without one!"); michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: treeOp->Init(mAmpersand, aMsgId); michael@0: } michael@0: michael@0: void michael@0: nsHtml5Highlighter::AddErrorToCurrentSlash(const char* aMsgId) michael@0: { michael@0: if (!mSyntaxHighlight) { michael@0: return; michael@0: } michael@0: NS_PRECONDITION(mSlash, "Adding error to slash without one!"); michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: treeOp->Init(mSlash, aMsgId); michael@0: }