diff --git a/parser/html/Makefile.in b/parser/html/Makefile.in
index a8f8cc112f5..aea2502c485 100644
--- a/parser/html/Makefile.in
+++ b/parser/html/Makefile.in
@@ -69,6 +69,7 @@ CPPSRCS = \
nsHtml5ReleasableElementName.cpp \
nsHtml5MetaScanner.cpp \
nsHtml5TreeOperation.cpp \
+ nsHtml5TreeOpStage.cpp \
nsHtml5StateSnapshot.cpp \
nsHtml5TreeOpExecutor.cpp \
nsHtml5StreamParser.cpp \
diff --git a/parser/html/nsAHtml5TreeBuilderState.h b/parser/html/nsAHtml5TreeBuilderState.h
new file mode 100644
index 00000000000..5a7171be430
--- /dev/null
+++ b/parser/html/nsAHtml5TreeBuilderState.h
@@ -0,0 +1,70 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is HTML Parser C++ Translator code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Henri Sivonen
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nsAHtml5TreeBuilderState_h___
+#define nsAHtml5TreeBuilderState_h___
+
+class nsAHtml5TreeBuilderState {
+ public:
+
+ virtual jArray getStack() = 0;
+
+ virtual jArray getListOfActiveFormattingElements() = 0;
+
+ virtual PRInt32 getStackLength() = 0;
+
+ virtual PRInt32 getListLength() = 0;
+
+ virtual nsIContent** getFormPointer() = 0;
+
+ virtual nsIContent** getHeadPointer() = 0;
+
+ virtual PRInt32 getMode() = 0;
+
+ virtual PRInt32 getOriginalMode() = 0;
+
+ virtual PRInt32 getForeignFlag() = 0;
+
+ virtual PRBool isNeedToDropLF() = 0;
+
+ virtual PRBool isQuirks() = 0;
+
+ virtual ~nsAHtml5TreeBuilderState() {
+ }
+};
+
+#endif /* nsAHtml5TreeBuilderState_h___ */
diff --git a/parser/html/nsAHtml5TreeOpSink.h b/parser/html/nsAHtml5TreeOpSink.h
new file mode 100644
index 00000000000..a4040f1cad6
--- /dev/null
+++ b/parser/html/nsAHtml5TreeOpSink.h
@@ -0,0 +1,63 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is HTML Parser C++ Translator code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Henri Sivonen
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nsAHtml5TreeOpSink_h___
+#define nsAHtml5TreeOpSink_h___
+
+/**
+ * The purpose of this interface is to connect a tree op executor
+ * (main-thread case), a tree op stage (non-speculative off-the-main-thread
+ * case) or a speculation (speculative case).
+ */
+class nsAHtml5TreeOpSink {
+ public:
+
+ /**
+ * Flush the operations from the tree operations from the argument
+ * queue if flushing is not expensive.
+ */
+ virtual void MaybeFlush(nsTArray& aOpQueue) = 0;
+
+ /**
+ * Flush the operations from the tree operations from the argument
+ * queue unconditionally.
+ */
+ virtual void ForcedFlush(nsTArray& aOpQueue) = 0;
+
+};
+
+#endif /* nsAHtml5TreeOpSink_h___ */
diff --git a/parser/html/nsHtml5AttributeName.cpp b/parser/html/nsHtml5AttributeName.cpp
index a7d40b4adc5..75a8912570c 100644
--- a/parser/html/nsHtml5AttributeName.cpp
+++ b/parser/html/nsHtml5AttributeName.cpp
@@ -42,6 +42,7 @@
#include "nsHtml5Atoms.h"
#include "nsHtml5ByteReadable.h"
#include "nsIUnicodeDecoder.h"
+#include "nsAHtml5TreeBuilderState.h"
#include "nsHtml5Tokenizer.h"
#include "nsHtml5TreeBuilder.h"
@@ -160,7 +161,7 @@ nsHtml5AttributeName::~nsHtml5AttributeName()
}
nsHtml5AttributeName*
-nsHtml5AttributeName::cloneAttributeName()
+nsHtml5AttributeName::cloneAttributeName(nsHtml5AtomTable* interner)
{
return this;
}
diff --git a/parser/html/nsHtml5AttributeName.h b/parser/html/nsHtml5AttributeName.h
index ca6e538ad8a..ed4f80f4f6c 100644
--- a/parser/html/nsHtml5AttributeName.h
+++ b/parser/html/nsHtml5AttributeName.h
@@ -43,8 +43,10 @@
#include "nsHtml5Atoms.h"
#include "nsHtml5ByteReadable.h"
#include "nsIUnicodeDecoder.h"
+#include "nsAHtml5TreeBuilderState.h"
class nsHtml5StreamParser;
+class nsHtml5SpeculativeLoader;
class nsHtml5Tokenizer;
class nsHtml5TreeBuilder;
@@ -88,7 +90,7 @@ class nsHtml5AttributeName
public:
virtual void release();
~nsHtml5AttributeName();
- virtual nsHtml5AttributeName* cloneAttributeName();
+ virtual nsHtml5AttributeName* cloneAttributeName(nsHtml5AtomTable* interner);
PRInt32 getUri(PRInt32 mode);
nsIAtom* getLocal(PRInt32 mode);
nsIAtom* getPrefix(PRInt32 mode);
diff --git a/parser/html/nsHtml5ElementName.cpp b/parser/html/nsHtml5ElementName.cpp
index d9cff19209a..04a20a34ade 100644
--- a/parser/html/nsHtml5ElementName.cpp
+++ b/parser/html/nsHtml5ElementName.cpp
@@ -42,6 +42,7 @@
#include "nsHtml5Atoms.h"
#include "nsHtml5ByteReadable.h"
#include "nsIUnicodeDecoder.h"
+#include "nsAHtml5TreeBuilderState.h"
#include "nsHtml5Tokenizer.h"
#include "nsHtml5TreeBuilder.h"
@@ -124,6 +125,12 @@ nsHtml5ElementName::~nsHtml5ElementName()
nsHtml5Portability::releaseLocal(name);
}
+nsHtml5ElementName*
+nsHtml5ElementName::cloneElementName(nsHtml5AtomTable* interner)
+{
+ return this;
+}
+
static PRInt32 const ELEMENT_HASHES_DATA[] = { 1057, 1090, 1255, 1321, 1552, 1585, 1651, 1717, 68162, 68899, 69059, 69764, 70020, 70276, 71077, 71205, 72134, 72232, 72264, 72296, 72328, 72360, 72392, 73351, 74312, 75209, 78124, 78284, 78476, 79149, 79309, 79341, 79469, 81295, 81487, 82224, 84498, 84626, 86164, 86292, 86612, 86676, 87445, 3183041, 3186241, 3198017, 3218722, 3226754, 3247715, 3256803, 3263971, 3264995, 3289252, 3291332, 3295524, 3299620, 3326725, 3379303, 3392679, 3448233, 3460553, 3461577, 3510347, 3546604, 3552364, 3556524, 3576461, 3586349, 3588141, 3590797, 3596333, 3622062, 3625454, 3627054, 3675728, 3749042, 3771059, 3771571, 3776211, 3782323, 3782963, 3784883, 3785395, 3788979, 3815476, 3839605, 3885110, 3917911, 3948984, 3951096, 135304769, 135858241, 136498210, 136906434, 137138658, 137512995, 137531875, 137548067, 137629283, 137645539, 137646563, 137775779, 138529956, 138615076, 139040932, 140954086, 141179366, 141690439, 142738600, 143013512, 146979116, 147175724, 147475756, 147902637, 147936877, 148017645, 148131885, 148228141, 148229165, 148309165, 148395629, 148551853, 148618829, 149076462, 149490158, 149572782, 151277616, 151639440, 153268914, 153486514, 153563314, 153750706, 153763314, 153914034, 154406067, 154417459, 154600979, 154678323, 154680979, 154866835, 155366708, 155375188, 155391572, 155465780, 155869364, 158045494, 168988979, 169321621, 169652752, 173151309, 174240818, 174247297, 174669292, 175391532, 176638123, 177380397, 177879204, 177886734, 180753473, 181020073, 181503558, 181686320, 181999237, 181999311, 182048201, 182074866, 182078003, 182083764, 182920847, 184716457, 184976961, 185145071, 187281445, 187872052, 188100653, 188875944, 188919873, 188920457, 189203987, 189371817, 189414886, 189567458, 190266670, 191318187, 191337609, 202479203, 202493027, 202835587, 202843747, 203013219, 203036048, 203045987, 203177552, 203898516, 204648562, 205067918, 205078130, 205096654, 205689142, 205690439, 205766017, 205988909, 207213161, 207794484, 207800999, 208023602, 208213644, 208213647, 210310273, 210940978, 213325049, 213946445, 214055079, 215125040, 215134273, 215135028, 215237420, 215418148, 215553166, 215553394, 215563858, 215627949, 215754324, 217529652, 217713834, 217732628, 218731945, 221417045, 221424946, 221493746, 221515401, 221658189, 221844577, 221908140, 221910626, 221921586, 222659762, 225001091, 236105833, 236113965, 236194995, 236195427, 236206132, 236206387, 236211683, 236212707, 236381647, 236571826, 237124271, 238172205, 238210544, 238270764, 238435405, 238501172, 239224867, 239257644, 239710497, 240307721, 241208789, 241241557, 241318060, 241319404, 241343533, 241344069, 241405397, 241765845, 243864964, 244502085, 244946220, 245109902, 247647266, 247707956, 248648814, 248648836, 248682161, 248986932, 249058914, 249697357, 252132601, 252135604, 252317348, 255007012, 255278388, 256365156, 257566121, 269763372, 271202790, 271863856, 272049197, 272127474, 272770631, 274339449, 274939471, 275388004, 275388005, 275388006, 275977800, 278267602, 278513831, 278712622, 281613765, 281683369, 282120228, 282250732, 282508942, 283743649, 283787570, 284710386, 285391148, 285478533, 285854898, 285873762, 286931113, 288964227, 289445441, 289689648, 291671489, 303512884, 305319975, 305610036, 305764101, 308448294, 308675890, 312085683, 312264750, 315032867, 316391000, 317331042, 317902135, 318950711, 319447220, 321499182, 322538804, 323145200, 337067316, 337826293, 339905989, 340833697, 341457068, 345302593, 349554733, 349771471, 349786245, 350819405, 356072847, 370349192, 373962798, 374509141, 375558638, 375574835, 376053993, 383276530, 383373833, 383407586, 384439906, 386079012, 404133513, 404307343, 407031852, 408072233, 409112005, 409608425, 409771500, 419040932, 437730612, 439529766, 442616365, 442813037, 443157674, 443295316, 450118444, 450482697, 456789668, 459935396, 471217869, 474073645, 476230702, 476665218, 476717289, 483014825, 485083298, 489306281, 538364390, 540675748, 543819186, 543958612, 576960820, 577242548, 610515252, 642202932, 644420819 };
void
nsHtml5ElementName::initializeStatics()
diff --git a/parser/html/nsHtml5ElementName.h b/parser/html/nsHtml5ElementName.h
index 74fba554b61..08865a87717 100644
--- a/parser/html/nsHtml5ElementName.h
+++ b/parser/html/nsHtml5ElementName.h
@@ -43,8 +43,10 @@
#include "nsHtml5Atoms.h"
#include "nsHtml5ByteReadable.h"
#include "nsIUnicodeDecoder.h"
+#include "nsAHtml5TreeBuilderState.h"
class nsHtml5StreamParser;
+class nsHtml5SpeculativeLoader;
class nsHtml5Tokenizer;
class nsHtml5TreeBuilder;
@@ -75,6 +77,7 @@ class nsHtml5ElementName
public:
virtual void release();
~nsHtml5ElementName();
+ virtual nsHtml5ElementName* cloneElementName(nsHtml5AtomTable* interner);
static nsHtml5ElementName* ELT_A;
static nsHtml5ElementName* ELT_B;
static nsHtml5ElementName* ELT_G;
diff --git a/parser/html/nsHtml5HtmlAttributes.cpp b/parser/html/nsHtml5HtmlAttributes.cpp
index 45800c6c91e..f9ae80b0fda 100644
--- a/parser/html/nsHtml5HtmlAttributes.cpp
+++ b/parser/html/nsHtml5HtmlAttributes.cpp
@@ -43,6 +43,7 @@
#include "nsHtml5Atoms.h"
#include "nsHtml5ByteReadable.h"
#include "nsIUnicodeDecoder.h"
+#include "nsAHtml5TreeBuilderState.h"
#include "nsHtml5Tokenizer.h"
#include "nsHtml5TreeBuilder.h"
@@ -225,12 +226,12 @@ nsHtml5HtmlAttributes::adjustForSvg()
}
nsHtml5HtmlAttributes*
-nsHtml5HtmlAttributes::cloneAttributes()
+nsHtml5HtmlAttributes::cloneAttributes(nsHtml5AtomTable* interner)
{
nsHtml5HtmlAttributes* clone = new nsHtml5HtmlAttributes(0);
for (PRInt32 i = 0; i < length; i++) {
- clone->addAttribute(names[i]->cloneAttributeName(), nsHtml5Portability::newStringFromString(values[i]));
+ clone->addAttribute(names[i]->cloneAttributeName(interner), nsHtml5Portability::newStringFromString(values[i]));
}
return clone;
}
diff --git a/parser/html/nsHtml5HtmlAttributes.h b/parser/html/nsHtml5HtmlAttributes.h
index 9ab6b0f441c..03f1eb4e71a 100644
--- a/parser/html/nsHtml5HtmlAttributes.h
+++ b/parser/html/nsHtml5HtmlAttributes.h
@@ -44,8 +44,10 @@
#include "nsHtml5Atoms.h"
#include "nsHtml5ByteReadable.h"
#include "nsIUnicodeDecoder.h"
+#include "nsAHtml5TreeBuilderState.h"
class nsHtml5StreamParser;
+class nsHtml5SpeculativeLoader;
class nsHtml5Tokenizer;
class nsHtml5TreeBuilder;
@@ -84,7 +86,7 @@ class nsHtml5HtmlAttributes
PRBool contains(nsHtml5AttributeName* name);
void adjustForMath();
void adjustForSvg();
- nsHtml5HtmlAttributes* cloneAttributes();
+ nsHtml5HtmlAttributes* cloneAttributes(nsHtml5AtomTable* interner);
static void initializeStatics();
static void releaseStatics();
};
diff --git a/parser/html/nsHtml5MetaScanner.cpp b/parser/html/nsHtml5MetaScanner.cpp
index 3ef50c396e6..7fab7b7a80c 100644
--- a/parser/html/nsHtml5MetaScanner.cpp
+++ b/parser/html/nsHtml5MetaScanner.cpp
@@ -43,6 +43,7 @@
#include "nsHtml5Atoms.h"
#include "nsHtml5ByteReadable.h"
#include "nsIUnicodeDecoder.h"
+#include "nsAHtml5TreeBuilderState.h"
#include "nsHtml5Tokenizer.h"
#include "nsHtml5TreeBuilder.h"
diff --git a/parser/html/nsHtml5MetaScanner.h b/parser/html/nsHtml5MetaScanner.h
index 765e34b3be5..79f69eb8ec0 100644
--- a/parser/html/nsHtml5MetaScanner.h
+++ b/parser/html/nsHtml5MetaScanner.h
@@ -44,8 +44,10 @@
#include "nsHtml5Atoms.h"
#include "nsHtml5ByteReadable.h"
#include "nsIUnicodeDecoder.h"
+#include "nsAHtml5TreeBuilderState.h"
class nsHtml5StreamParser;
+class nsHtml5SpeculativeLoader;
class nsHtml5Tokenizer;
class nsHtml5TreeBuilder;
diff --git a/parser/html/nsHtml5Parser.cpp b/parser/html/nsHtml5Parser.cpp
index 9bff4e12ed8..80b24a16615 100644
--- a/parser/html/nsHtml5Parser.cpp
+++ b/parser/html/nsHtml5Parser.cpp
@@ -110,16 +110,15 @@ nsHtml5Parser::nsHtml5Parser()
, mExecutor(new nsHtml5TreeOpExecutor())
, mTreeBuilder(new nsHtml5TreeBuilder(mExecutor))
, mTokenizer(new nsHtml5Tokenizer(mTreeBuilder))
- , mAtomTable(new nsHtml5AtomTable())
{
- mExecutor->SetTreeBuilder(mTreeBuilder);
- mAtomTable->Init(); // we aren't checking for OOM anyway...
- mTokenizer->setInterner(mAtomTable);
+ mAtomTable.Init(); // we aren't checking for OOM anyway...
+ mTokenizer->setInterner(&mAtomTable);
// There's a zeroing operator new for everything else
}
nsHtml5Parser::~nsHtml5Parser()
{
+ mTokenizer->end();
while (mFirstBuffer) {
nsHtml5UTF16Buffer* old = mFirstBuffer;
mFirstBuffer = mFirstBuffer->next;
@@ -196,7 +195,7 @@ NS_IMETHODIMP
nsHtml5Parser::GetStreamListener(nsIStreamListener** aListener)
{
if (!mStreamParser) {
- mStreamParser = new nsHtml5StreamParser(mTokenizer, mExecutor, this);
+ mStreamParser = new nsHtml5StreamParser(mExecutor, this);
}
NS_ADDREF(*aListener = mStreamParser);
return NS_OK;
@@ -225,9 +224,7 @@ nsHtml5Parser::ContinueInterruptedParsing()
nsCOMPtr kungFuDeathGrip(this);
nsRefPtr streamKungFuDeathGrip(mStreamParser);
nsRefPtr treeOpKungFuDeathGrip(mExecutor);
- // XXX Stop speculative script thread but why?
- mExecutor->MaybeFlush();
- ParseUntilSuspend();
+ ParseUntilScript();
return NS_OK;
}
@@ -235,18 +232,12 @@ NS_IMETHODIMP_(void)
nsHtml5Parser::BlockParser()
{
mBlocked = PR_TRUE;
- if (mStreamParser) {
- mStreamParser->Block();
- }
}
NS_IMETHODIMP_(void)
nsHtml5Parser::UnblockParser()
{
mBlocked = PR_FALSE;
- if (mStreamParser) {
- mStreamParser->Unblock();
- }
}
NS_IMETHODIMP_(PRBool)
@@ -274,16 +265,12 @@ nsHtml5Parser::Parse(nsIURI* aURL, // legacy parameter; ignored
NS_PRECONDITION(mExecutor->GetLifeCycle() == NOT_STARTED,
"Tried to start parse without initializing the parser properly.");
if (!mStreamParser) {
- mStreamParser = new nsHtml5StreamParser(mTokenizer, mExecutor, this);
+ mStreamParser = new nsHtml5StreamParser(mExecutor, this);
}
mStreamParser->SetObserver(aObserver);
- mTokenizer->setEncodingDeclarationHandler(mStreamParser);
mExecutor->SetStreamParser(mStreamParser);
mExecutor->SetParser(this);
- mTreeBuilder->setScriptingEnabled(mExecutor->IsScriptEnabled());
- mExecutor->AllowInterrupts();
mRootContextKey = aKey;
- mExecutor->SetParser(this);
return NS_OK;
}
@@ -310,10 +297,18 @@ nsHtml5Parser::Parse(const nsAString& aSourceBuffer,
case TERMINATED:
return NS_OK;
case NOT_STARTED:
+ NS_ASSERTION(!mStreamParser,
+ "Had stream parser but document.write started life cycle.");
mExecutor->SetParser(this);
mTreeBuilder->setScriptingEnabled(mExecutor->IsScriptEnabled());
mTokenizer->start();
- mExecutor->SetLifeCycle(PARSING);
+ mExecutor->Start();
+ /*
+ * If you move the following line, be very careful not to cause
+ * WillBuildModel to be called before the document has had its
+ * script global object set.
+ */
+ mExecutor->WillBuildModel(eDTDMode_unknown);
break;
default:
break;
@@ -321,13 +316,13 @@ nsHtml5Parser::Parse(const nsAString& aSourceBuffer,
if (aLastCall && aSourceBuffer.IsEmpty() && aKey == GetRootContextKey()) {
// document.close()
- mExecutor->SetLifeCycle(STREAM_ENDING);
+ NS_ASSERTION(!mStreamParser,
+ "Had stream parser but got document.close().");
+ mDocumentClosed = PR_TRUE;
MaybePostContinueEvent();
return NS_OK;
}
- // XXX stop speculative script thread here
-
PRInt32 lineNumberSave = mTokenizer->getLineNumber();
if (!aSourceBuffer.IsEmpty()) {
@@ -335,16 +330,20 @@ nsHtml5Parser::Parse(const nsAString& aSourceBuffer,
memcpy(buffer->getBuffer(), aSourceBuffer.BeginReading(), aSourceBuffer.Length() * sizeof(PRUnichar));
buffer->setEnd(aSourceBuffer.Length());
if (!mBlocked) {
- mExecutor->WillResume();
+ // mExecutor->WillResume();
while (buffer->hasMore()) {
buffer->adjust(mLastWasCR);
mLastWasCR = PR_FALSE;
if (buffer->hasMore()) {
mLastWasCR = mTokenizer->tokenizeBuffer(buffer);
- mExecutor->MaybeExecuteScript();
+ if (mTreeBuilder->HasScript()) {
+ mTreeBuilder->Flush(); // moves ops to executor queue
+ mExecutor->Flush(); // executes the queue
+ // Is mBlocked always true here?
+ }
if (mBlocked) {
// XXX is the tail insertion and script exec in the wrong order?
- mExecutor->WillInterrupt();
+ // mExecutor->WillInterrupt();
break;
}
// Ignore suspension requests
@@ -396,6 +395,8 @@ nsHtml5Parser::Parse(const nsAString& aSourceBuffer,
}
// Scripting semantics require a forced tree builder flush here
+ // TODO: Also flush the pending text node from tree builder
+ mTreeBuilder->Flush();
mExecutor->Flush();
mTokenizer->setLineNumber(lineNumberSave);
return NS_OK;
@@ -426,7 +427,9 @@ nsHtml5Parser::Terminate(void)
// CancelParsingEvents must be called to avoid leaking the nsParser object
// @see bug 108049
CancelParsingEvents();
-
+ if (mStreamParser) {
+ mStreamParser->Terminate();
+ }
return mExecutor->DidBuildModel(PR_TRUE);
}
@@ -464,7 +467,6 @@ nsHtml5Parser::ParseFragment(const nsAString& aSourceBuffer,
// Initialize() doesn't deal with base URI
mExecutor->SetBaseUriFromDocument();
- mExecutor->ProhibitInterrupts();
mExecutor->SetParser(this);
mExecutor->SetNodeInfoManager(target->GetOwnerDoc()->NodeInfoManager());
@@ -475,7 +477,7 @@ nsHtml5Parser::ParseFragment(const nsAString& aSourceBuffer,
NS_PRECONDITION(mExecutor->GetLifeCycle() == NOT_STARTED, "Tried to start parse without initializing the parser properly.");
mTreeBuilder->setScriptingEnabled(mExecutor->IsScriptEnabled());
mTokenizer->start();
- mExecutor->SetLifeCycle(PARSING);
+ mExecutor->Start(); // Don't call WillBuildModel in fragment case
if (!aSourceBuffer.IsEmpty()) {
PRBool lastWasCR = PR_FALSE;
nsHtml5UTF16Buffer buffer(aSourceBuffer.Length());
@@ -490,21 +492,22 @@ nsHtml5Parser::ParseFragment(const nsAString& aSourceBuffer,
}
}
}
- mExecutor->SetLifeCycle(TERMINATED);
mTokenizer->eof();
+ mTreeBuilder->StreamEnded();
+ mTreeBuilder->Flush();
mExecutor->Flush();
mTokenizer->end();
+ mExecutor->SetLifeCycle(TERMINATED);
mExecutor->DropParserAndPerfHint();
- mAtomTable->Clear();
+ mAtomTable.Clear();
return NS_OK;
}
NS_IMETHODIMP
nsHtml5Parser::BuildModel(void)
{
- // XXX who calls this? Should this be a no-op?
- ParseUntilSuspend();
- return NS_OK;
+ NS_NOTREACHED("Don't call this!");
+ return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
@@ -521,11 +524,11 @@ nsHtml5Parser::Reset()
mLastWasCR = PR_FALSE;
mFragmentMode = PR_FALSE;
UnblockParser();
- mSuspending = PR_FALSE;
+ mDocumentClosed = PR_FALSE;
mStreamParser = nsnull;
mRootContextKey = nsnull;
mContinueEvent = nsnull; // weak ref
- mAtomTable->Clear(); // should be already cleared in the fragment case anyway
+ mAtomTable.Clear(); // should be already cleared in the fragment case anyway
// Portable parser objects
while (mFirstBuffer->next) {
nsHtml5UTF16Buffer* oldBuf = mFirstBuffer;
@@ -557,10 +560,9 @@ nsHtml5Parser::HandleParserContinueEvent(nsHtml5ParserContinueEvent* ev)
}
void
-nsHtml5Parser::ParseUntilSuspend()
+nsHtml5Parser::ParseUntilScript()
{
- NS_PRECONDITION(!mFragmentMode, "ParseUntilSuspend called in fragment mode.");
- NS_PRECONDITION(!mExecutor->NeedsCharsetSwitch(), "ParseUntilSuspend called when charset switch needed.");
+ NS_PRECONDITION(!mFragmentMode, "ParseUntilScript called in fragment mode.");
if (mBlocked) {
return;
@@ -577,7 +579,6 @@ nsHtml5Parser::ParseUntilSuspend()
}
mExecutor->WillResume();
- mSuspending = PR_FALSE;
for (;;) {
if (!mFirstBuffer->hasMore()) {
if (mFirstBuffer == mLastBuffer) {
@@ -586,21 +587,26 @@ nsHtml5Parser::ParseUntilSuspend()
// something like cache manisfests stopped the parse in mid-flight
return;
case PARSING:
- // never release the last buffer. instead just zero its indeces for refill
- mFirstBuffer->setStart(0);
- mFirstBuffer->setEnd(0);
- if (mStreamParser) {
- mStreamParser->ParseUntilSuspend();
- }
- return; // no more data for now but expecting more
- case STREAM_ENDING:
- if (mStreamParser && !mStreamParser->IsDone()) { // may still have stream data left
- mStreamParser->ParseUntilSuspend();
+ if (mDocumentClosed) {
+ NS_ASSERTION(!mStreamParser,
+ "This should only happen with script-created parser.");
+ mTokenizer->eof();
+ mTreeBuilder->StreamEnded();
+ mTreeBuilder->Flush();
+ mExecutor->Flush();
+ mTokenizer->end();
+ return;
} else {
- // no more data and not expecting more
- mExecutor->DidBuildModel(PR_FALSE);
+ // never release the last buffer. instead just zero its indeces for refill
+ mFirstBuffer->setStart(0);
+ mFirstBuffer->setEnd(0);
+ if (mStreamParser) {
+ mStreamParser->ContinueAfterScripts(mTokenizer,
+ mTreeBuilder,
+ mLastWasCR);
+ }
+ return; // no more data for now but expecting more
}
- return;
default:
NS_NOTREACHED("It should be impossible to reach this.");
return;
@@ -622,16 +628,12 @@ nsHtml5Parser::ParseUntilSuspend()
mLastWasCR = PR_FALSE;
if (mFirstBuffer->hasMore()) {
mLastWasCR = mTokenizer->tokenizeBuffer(mFirstBuffer);
- NS_ASSERTION(!(mExecutor->HasScriptElement() && mExecutor->NeedsCharsetSwitch()), "Can't have both script and charset switch.");
- mExecutor->IgnoreCharsetSwitch();
- mExecutor->MaybeExecuteScript();
- if (mBlocked) {
- mExecutor->WillInterrupt();
- return;
+ if (mTreeBuilder->HasScript()) {
+ mTreeBuilder->Flush();
+ mExecutor->Flush();
}
- if (mSuspending) {
- MaybePostContinueEvent();
- mExecutor->WillInterrupt();
+ if (mBlocked) {
+ // mExecutor->WillInterrupt();
return;
}
}
@@ -657,15 +659,6 @@ nsHtml5Parser::MaybePostContinueEvent()
}
}
-void
-nsHtml5Parser::Suspend()
-{
- mSuspending = PR_TRUE;
- if (mStreamParser) {
- mStreamParser->Suspend();
- }
-}
-
nsresult
nsHtml5Parser::Initialize(nsIDocument* aDoc,
nsIURI* aURI,
@@ -675,3 +668,16 @@ nsHtml5Parser::Initialize(nsIDocument* aDoc,
return mExecutor->Init(aDoc, aURI, aContainer, aChannel);
}
+void
+nsHtml5Parser::StartTokenizer(PRBool aScriptingEnabled) {
+ mTreeBuilder->setScriptingEnabled(aScriptingEnabled);
+ mTokenizer->start();
+}
+
+void
+nsHtml5Parser::InitializeDocWriteParserState(nsAHtml5TreeBuilderState* aState)
+{
+ mTokenizer->resetToDataState();
+ mTreeBuilder->loadState(aState, &mAtomTable);
+ mLastWasCR = PR_FALSE;
+}
diff --git a/parser/html/nsHtml5Parser.h b/parser/html/nsHtml5Parser.h
index 5a7fb4220d4..cac7755f38b 100644
--- a/parser/html/nsHtml5Parser.h
+++ b/parser/html/nsHtml5Parser.h
@@ -232,7 +232,7 @@ class nsHtml5Parser : public nsIParser {
PRBool aQuirks);
/**
- * Calls ParseUntilSuspend()
+ * Don't call. For interface compat only.
*/
NS_IMETHOD BuildModel(void);
@@ -271,15 +271,12 @@ class nsHtml5Parser : public nsIParser {
nsISupports* aContainer,
nsIChannel* aChannel);
- /**
- * Request event loop spin as soon as the tokenizer returns
- */
- void Suspend();
-
inline nsHtml5Tokenizer* GetTokenizer() {
return mTokenizer;
}
+ void InitializeDocWriteParserState(nsAHtml5TreeBuilderState* aState);
+
/**
* Posts a continue event if there isn't one already
*/
@@ -288,13 +285,21 @@ class nsHtml5Parser : public nsIParser {
void DropStreamParser() {
mStreamParser = nsnull;
}
+
+ void StartTokenizer(PRBool aScriptingEnabled);
+
+#ifdef DEBUG
+ PRBool HasStreamParser() {
+ return !!mStreamParser;
+ }
+#endif
private:
/**
- * Parse until pending data is exhausted or tree builder suspends
+ * Parse until pending data is exhausted or a script end tag is seen
*/
- void ParseUntilSuspend();
+ void ParseUntilScript();
// State variables
@@ -312,13 +317,11 @@ class nsHtml5Parser : public nsIParser {
* The parser is blocking on a script
*/
PRBool mBlocked;
-
+
/**
- * The event loop will spin ASAP
+ * True if document.close() has been called.
*/
- PRBool mSuspending;
-
- // script execution
+ PRBool mDocumentClosed;
// Gecko integration
void* mRootContextKey;
@@ -357,9 +360,9 @@ class nsHtml5Parser : public nsIParser {
nsRefPtr mStreamParser;
/**
- * The scoped atom service
+ * The scoped atom table
*/
- const nsAutoPtr mAtomTable;
+ nsHtml5AtomTable mAtomTable;
};
#endif
diff --git a/parser/html/nsHtml5Portability.cpp b/parser/html/nsHtml5Portability.cpp
index dfb85bb1afa..09dd13fd71a 100644
--- a/parser/html/nsHtml5Portability.cpp
+++ b/parser/html/nsHtml5Portability.cpp
@@ -96,6 +96,19 @@ nsHtml5Portability::newCharArrayFromString(nsString* string)
return arr;
}
+nsIAtom*
+nsHtml5Portability::newLocalFromLocal(nsIAtom* local, nsHtml5AtomTable* interner)
+{
+ NS_PRECONDITION(local, "Atom was null.");
+ NS_PRECONDITION(interner, "Atom table was null");
+ if (local->IsStaticAtom()) {
+ nsAutoString str;
+ local->ToString(str);
+ local = interner->GetAtom(str);
+ }
+ return local;
+}
+
void
nsHtml5Portability::releaseString(nsString* str)
{
@@ -114,18 +127,6 @@ nsHtml5Portability::releaseLocal(nsIAtom* local)
{
}
-// XXX Useless code
-void
-nsHtml5Portability::retainElement(nsIContent** element)
-{
-}
-
-// XXX Useless code
-void
-nsHtml5Portability::releaseElement(nsIContent** element)
-{
-}
-
PRBool
nsHtml5Portability::localEqualsBuffer(nsIAtom* local, PRUnichar* buf, PRInt32 offset, PRInt32 length)
{
diff --git a/parser/html/nsHtml5Portability.h b/parser/html/nsHtml5Portability.h
index 09424087056..8022a234723 100644
--- a/parser/html/nsHtml5Portability.h
+++ b/parser/html/nsHtml5Portability.h
@@ -43,8 +43,10 @@
#include "nsHtml5Atoms.h"
#include "nsHtml5ByteReadable.h"
#include "nsIUnicodeDecoder.h"
+#include "nsAHtml5TreeBuilderState.h"
class nsHtml5StreamParser;
+class nsHtml5SpeculativeLoader;
class nsHtml5Tokenizer;
class nsHtml5TreeBuilder;
@@ -66,11 +68,10 @@ class nsHtml5Portability
static nsString* newStringFromString(nsString* string);
static jArray newCharArrayFromLocal(nsIAtom* local);
static jArray newCharArrayFromString(nsString* string);
+ static nsIAtom* newLocalFromLocal(nsIAtom* local, nsHtml5AtomTable* interner);
static void releaseString(nsString* str);
static void retainLocal(nsIAtom* local);
static void releaseLocal(nsIAtom* local);
- static void retainElement(nsIContent** elt);
- static void releaseElement(nsIContent** elt);
static PRBool localEqualsBuffer(nsIAtom* local, PRUnichar* buf, PRInt32 offset, PRInt32 length);
static PRBool lowerCaseLiteralIsPrefixOfIgnoreAsciiCaseString(const char* lowerCaseLiteral, nsString* string);
static PRBool lowerCaseLiteralEqualsIgnoreAsciiCaseString(const char* lowerCaseLiteral, nsString* string);
diff --git a/parser/html/nsHtml5ReleasableAttributeName.cpp b/parser/html/nsHtml5ReleasableAttributeName.cpp
index 6d126415c37..87e2a306576 100644
--- a/parser/html/nsHtml5ReleasableAttributeName.cpp
+++ b/parser/html/nsHtml5ReleasableAttributeName.cpp
@@ -37,6 +37,7 @@
#include "nsHtml5ReleasableAttributeName.h"
#include "nsHtml5Portability.h"
+#include "nsHtml5AtomTable.h"
nsHtml5ReleasableAttributeName::nsHtml5ReleasableAttributeName(PRInt32* uri, nsIAtom** local, nsIAtom** prefix)
: nsHtml5AttributeName(uri, local, prefix)
@@ -44,10 +45,16 @@ nsHtml5ReleasableAttributeName::nsHtml5ReleasableAttributeName(PRInt32* uri, nsI
}
nsHtml5AttributeName*
-nsHtml5ReleasableAttributeName::cloneAttributeName()
+nsHtml5ReleasableAttributeName::cloneAttributeName(nsHtml5AtomTable* aInterner)
{
nsIAtom* l = getLocal(0);
- nsHtml5Portability::retainLocal(l);
+ if (aInterner) {
+ if (l->IsStaticAtom()) {
+ nsAutoString str;
+ l->ToString(str);
+ l = aInterner->GetAtom(str);
+ }
+ }
return new nsHtml5ReleasableAttributeName(nsHtml5AttributeName::ALL_NO_NS,
nsHtml5AttributeName::SAME_LOCAL(l),
nsHtml5AttributeName::ALL_NO_PREFIX);
diff --git a/parser/html/nsHtml5ReleasableAttributeName.h b/parser/html/nsHtml5ReleasableAttributeName.h
index 845643d30aa..467cf4f5d57 100644
--- a/parser/html/nsHtml5ReleasableAttributeName.h
+++ b/parser/html/nsHtml5ReleasableAttributeName.h
@@ -40,11 +40,13 @@
#include "nsHtml5AttributeName.h"
+class nsHtml5AtomTable;
+
class nsHtml5ReleasableAttributeName : public nsHtml5AttributeName
{
public:
nsHtml5ReleasableAttributeName(PRInt32* uri, nsIAtom** local, nsIAtom** prefix);
- virtual nsHtml5AttributeName* cloneAttributeName();
+ virtual nsHtml5AttributeName* cloneAttributeName(nsHtml5AtomTable* aInterner);
virtual void release();
};
diff --git a/parser/html/nsHtml5ReleasableElementName.cpp b/parser/html/nsHtml5ReleasableElementName.cpp
index 18cec0f0c98..df2b41ce4b2 100644
--- a/parser/html/nsHtml5ReleasableElementName.cpp
+++ b/parser/html/nsHtml5ReleasableElementName.cpp
@@ -47,3 +47,17 @@ nsHtml5ReleasableElementName::release()
{
delete this;
}
+
+nsHtml5ElementName*
+nsHtml5ReleasableElementName::cloneElementName(nsHtml5AtomTable* aInterner)
+{
+ nsIAtom* l = name;
+ if (aInterner) {
+ if (l->IsStaticAtom()) {
+ nsAutoString str;
+ l->ToString(str);
+ l = aInterner->GetAtom(str);
+ }
+ }
+ return new nsHtml5ReleasableElementName(l);
+}
diff --git a/parser/html/nsHtml5ReleasableElementName.h b/parser/html/nsHtml5ReleasableElementName.h
index 8ec51c604d7..090c5e62704 100644
--- a/parser/html/nsHtml5ReleasableElementName.h
+++ b/parser/html/nsHtml5ReleasableElementName.h
@@ -45,6 +45,7 @@ class nsHtml5ReleasableElementName : public nsHtml5ElementName
public:
nsHtml5ReleasableElementName(nsIAtom* name);
virtual void release();
+ virtual nsHtml5ElementName* cloneElementName(nsHtml5AtomTable* interner);
};
#endif // nsHtml5ReleasableElementName_h__
diff --git a/parser/html/nsHtml5StackNode.cpp b/parser/html/nsHtml5StackNode.cpp
index 6c29c78e58d..f3ef34eb97c 100644
--- a/parser/html/nsHtml5StackNode.cpp
+++ b/parser/html/nsHtml5StackNode.cpp
@@ -43,6 +43,7 @@
#include "nsHtml5Atoms.h"
#include "nsHtml5ByteReadable.h"
#include "nsIUnicodeDecoder.h"
+#include "nsAHtml5TreeBuilderState.h"
#include "nsHtml5Tokenizer.h"
#include "nsHtml5TreeBuilder.h"
@@ -72,7 +73,7 @@ nsHtml5StackNode::nsHtml5StackNode(PRInt32 group, PRInt32 ns, nsIAtom* name, nsI
MOZ_COUNT_CTOR(nsHtml5StackNode);
nsHtml5Portability::retainLocal(name);
nsHtml5Portability::retainLocal(popName);
- nsHtml5Portability::retainElement(node);
+ ;
}
@@ -91,7 +92,7 @@ nsHtml5StackNode::nsHtml5StackNode(PRInt32 ns, nsHtml5ElementName* elementName,
MOZ_COUNT_CTOR(nsHtml5StackNode);
nsHtml5Portability::retainLocal(name);
nsHtml5Portability::retainLocal(popName);
- nsHtml5Portability::retainElement(node);
+ ;
}
@@ -110,7 +111,7 @@ nsHtml5StackNode::nsHtml5StackNode(PRInt32 ns, nsHtml5ElementName* elementName,
MOZ_COUNT_CTOR(nsHtml5StackNode);
nsHtml5Portability::retainLocal(name);
nsHtml5Portability::retainLocal(popName);
- nsHtml5Portability::retainElement(node);
+ ;
}
@@ -129,7 +130,7 @@ nsHtml5StackNode::nsHtml5StackNode(PRInt32 ns, nsHtml5ElementName* elementName,
MOZ_COUNT_CTOR(nsHtml5StackNode);
nsHtml5Portability::retainLocal(name);
nsHtml5Portability::retainLocal(popName);
- nsHtml5Portability::retainElement(node);
+ ;
}
@@ -148,7 +149,7 @@ nsHtml5StackNode::nsHtml5StackNode(PRInt32 ns, nsHtml5ElementName* elementName,
MOZ_COUNT_CTOR(nsHtml5StackNode);
nsHtml5Portability::retainLocal(name);
nsHtml5Portability::retainLocal(popName);
- nsHtml5Portability::retainElement(node);
+ ;
}
@@ -157,7 +158,7 @@ nsHtml5StackNode::~nsHtml5StackNode()
MOZ_COUNT_DTOR(nsHtml5StackNode);
nsHtml5Portability::releaseLocal(name);
nsHtml5Portability::releaseLocal(popName);
- nsHtml5Portability::releaseElement(node);
+ ;
delete attributes;
}
diff --git a/parser/html/nsHtml5StackNode.h b/parser/html/nsHtml5StackNode.h
index 9be20fd9b13..02dddf5879a 100644
--- a/parser/html/nsHtml5StackNode.h
+++ b/parser/html/nsHtml5StackNode.h
@@ -44,8 +44,10 @@
#include "nsHtml5Atoms.h"
#include "nsHtml5ByteReadable.h"
#include "nsIUnicodeDecoder.h"
+#include "nsAHtml5TreeBuilderState.h"
class nsHtml5StreamParser;
+class nsHtml5SpeculativeLoader;
class nsHtml5Tokenizer;
class nsHtml5TreeBuilder;
diff --git a/parser/html/nsHtml5StateSnapshot.cpp b/parser/html/nsHtml5StateSnapshot.cpp
index 4701738ee34..14dc74e9d6b 100644
--- a/parser/html/nsHtml5StateSnapshot.cpp
+++ b/parser/html/nsHtml5StateSnapshot.cpp
@@ -42,6 +42,7 @@
#include "nsHtml5Atoms.h"
#include "nsHtml5ByteReadable.h"
#include "nsIUnicodeDecoder.h"
+#include "nsAHtml5TreeBuilderState.h"
#include "nsHtml5Tokenizer.h"
#include "nsHtml5TreeBuilder.h"
@@ -56,14 +57,86 @@
#include "nsHtml5StateSnapshot.h"
-nsHtml5StateSnapshot::nsHtml5StateSnapshot(jArray stack, jArray listOfActiveFormattingElements, nsIContent** formPointer)
+nsHtml5StateSnapshot::nsHtml5StateSnapshot(jArray stack, jArray listOfActiveFormattingElements, nsIContent** formPointer, nsIContent** headPointer, PRInt32 mode, PRInt32 originalMode, PRInt32 foreignFlag, PRBool needToDropLF, PRBool quirks)
: stack(stack),
listOfActiveFormattingElements(listOfActiveFormattingElements),
- formPointer(formPointer)
+ formPointer(formPointer),
+ headPointer(headPointer),
+ mode(mode),
+ originalMode(originalMode),
+ foreignFlag(foreignFlag),
+ needToDropLF(needToDropLF),
+ quirks(quirks)
{
MOZ_COUNT_CTOR(nsHtml5StateSnapshot);
}
+jArray
+nsHtml5StateSnapshot::getStack()
+{
+ return stack;
+}
+
+jArray
+nsHtml5StateSnapshot::getListOfActiveFormattingElements()
+{
+ return listOfActiveFormattingElements;
+}
+
+nsIContent**
+nsHtml5StateSnapshot::getFormPointer()
+{
+ return formPointer;
+}
+
+nsIContent**
+nsHtml5StateSnapshot::getHeadPointer()
+{
+ return headPointer;
+}
+
+PRInt32
+nsHtml5StateSnapshot::getMode()
+{
+ return mode;
+}
+
+PRInt32
+nsHtml5StateSnapshot::getOriginalMode()
+{
+ return originalMode;
+}
+
+PRInt32
+nsHtml5StateSnapshot::getForeignFlag()
+{
+ return foreignFlag;
+}
+
+PRBool
+nsHtml5StateSnapshot::isNeedToDropLF()
+{
+ return needToDropLF;
+}
+
+PRBool
+nsHtml5StateSnapshot::isQuirks()
+{
+ return quirks;
+}
+
+PRInt32
+nsHtml5StateSnapshot::getListLength()
+{
+ return listOfActiveFormattingElements.length;
+}
+
+PRInt32
+nsHtml5StateSnapshot::getStackLength()
+{
+ return stack.length;
+}
+
nsHtml5StateSnapshot::~nsHtml5StateSnapshot()
{
@@ -78,7 +151,7 @@ nsHtml5StateSnapshot::~nsHtml5StateSnapshot()
}
}
listOfActiveFormattingElements.release();
- nsHtml5Portability::retainElement(formPointer);
+ ;
}
void
diff --git a/parser/html/nsHtml5StateSnapshot.h b/parser/html/nsHtml5StateSnapshot.h
index 1f56487a1cc..545675273f1 100644
--- a/parser/html/nsHtml5StateSnapshot.h
+++ b/parser/html/nsHtml5StateSnapshot.h
@@ -43,8 +43,10 @@
#include "nsHtml5Atoms.h"
#include "nsHtml5ByteReadable.h"
#include "nsIUnicodeDecoder.h"
+#include "nsAHtml5TreeBuilderState.h"
class nsHtml5StreamParser;
+class nsHtml5SpeculativeLoader;
class nsHtml5Tokenizer;
class nsHtml5TreeBuilder;
@@ -56,13 +58,31 @@ class nsHtml5UTF16Buffer;
class nsHtml5Portability;
-class nsHtml5StateSnapshot
+class nsHtml5StateSnapshot : public nsAHtml5TreeBuilderState
{
- public:
- nsHtml5StateSnapshot(jArray stack, jArray listOfActiveFormattingElements, nsIContent** formPointer);
+ private:
jArray stack;
jArray listOfActiveFormattingElements;
nsIContent** formPointer;
+ nsIContent** headPointer;
+ PRInt32 mode;
+ PRInt32 originalMode;
+ PRInt32 foreignFlag;
+ PRBool needToDropLF;
+ PRBool quirks;
+ public:
+ nsHtml5StateSnapshot(jArray stack, jArray listOfActiveFormattingElements, nsIContent** formPointer, nsIContent** headPointer, PRInt32 mode, PRInt32 originalMode, PRInt32 foreignFlag, PRBool needToDropLF, PRBool quirks);
+ jArray getStack();
+ jArray getListOfActiveFormattingElements();
+ nsIContent** getFormPointer();
+ nsIContent** getHeadPointer();
+ PRInt32 getMode();
+ PRInt32 getOriginalMode();
+ PRInt32 getForeignFlag();
+ PRBool isNeedToDropLF();
+ PRBool isQuirks();
+ PRInt32 getListLength();
+ PRInt32 getStackLength();
~nsHtml5StateSnapshot();
static void initializeStatics();
static void releaseStatics();
diff --git a/parser/html/nsHtml5StreamParser.cpp b/parser/html/nsHtml5StreamParser.cpp
index 156e669917d..a4c8361027e 100644
--- a/parser/html/nsHtml5StreamParser.cpp
+++ b/parser/html/nsHtml5StreamParser.cpp
@@ -47,6 +47,8 @@
#include "nsHtml5Tokenizer.h"
#include "nsIHttpChannel.h"
#include "nsHtml5Parser.h"
+#include "nsHtml5TreeBuilder.h"
+#include "nsHtml5AtomTable.h"
static NS_DEFINE_CID(kCharsetAliasCID, NS_CHARSETALIAS_CID);
@@ -62,19 +64,28 @@ NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTION_3(nsHtml5StreamParser, mObserver, mRequest, mOwner)
-nsHtml5StreamParser::nsHtml5StreamParser(nsHtml5Tokenizer* aTokenizer,
- nsHtml5TreeOpExecutor* aExecutor,
+nsHtml5StreamParser::nsHtml5StreamParser(nsHtml5TreeOpExecutor* aExecutor,
nsHtml5Parser* aOwner)
: mFirstBuffer(new nsHtml5UTF16Buffer(NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE))
, mLastBuffer(mFirstBuffer)
, mExecutor(aExecutor)
- , mTokenizer(aTokenizer)
+ , mTreeBuilder(new nsHtml5TreeBuilder(mExecutor->GetStage()))
+ , mTokenizer(new nsHtml5Tokenizer(mTreeBuilder))
+ , mTokenizerMutex("nsHtml5StreamParser mTokenizerMutex")
, mOwner(aOwner)
+ , mTerminatedMutex("nsHtml5StreamParser mTerminatedMutex")
{
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+ mAtomTable.Init(); // we aren't checking for OOM anyway...
+ mTokenizer->setInterner(&mAtomTable);
+ mTokenizer->setEncodingDeclarationHandler(this);
+ // There's a zeroing operator new for everything else
}
nsHtml5StreamParser::~nsHtml5StreamParser()
{
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+ mTokenizer->end();
mRequest = nsnull;
mObserver = nsnull;
mUnicodeDecoder = nsnull;
@@ -94,6 +105,7 @@ nsHtml5StreamParser::~nsHtml5StreamParser()
nsresult
nsHtml5StreamParser::GetChannel(nsIChannel** aChannel)
{
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
return mRequest ? CallQueryInterface(mRequest, aChannel) :
NS_ERROR_NOT_AVAILABLE;
}
@@ -104,7 +116,7 @@ nsHtml5StreamParser::Notify(const char* aCharset, nsDetectionConfident aConf)
if (aConf == eBestAnswer || aConf == eSureAnswer) {
mCharset.Assign(aCharset);
mCharsetSource = kCharsetFromAutoDetection;
- mExecutor->SetDocumentCharset(mCharset);
+ mTreeBuilder->SetDocumentCharset(mCharset);
}
return NS_OK;
}
@@ -122,7 +134,7 @@ nsHtml5StreamParser::SetupDecodingAndWriteSniffingBufferAndCurrentSegment(const
mCharset.Assign("windows-1252"); // lower case is the raw form
mCharsetSource = kCharsetFromWeakDocTypeDefault;
rv = convManager->GetUnicodeDecoderRaw(mCharset.get(), getter_AddRefs(mUnicodeDecoder));
- mExecutor->SetDocumentCharset(mCharset);
+ mTreeBuilder->SetDocumentCharset(mCharset);
}
NS_ENSURE_SUCCESS(rv, rv);
mUnicodeDecoder->SetInputErrorBehavior(nsIUnicodeDecoder::kOnError_Recover);
@@ -158,7 +170,7 @@ nsHtml5StreamParser::SetupDecodingFromBom(const char* aCharsetName, const char*
NS_ENSURE_SUCCESS(rv, rv);
mCharset.Assign(aCharsetName);
mCharsetSource = kCharsetFromByteOrderMark;
- mExecutor->SetDocumentCharset(mCharset);
+ mTreeBuilder->SetDocumentCharset(mCharset);
mSniffingBuffer = nsnull;
mMetaScanner = nsnull;
mBomState = BOM_SNIFFING_OVER;
@@ -205,7 +217,7 @@ nsHtml5StreamParser::FinalizeSniffing(const PRUint8* aFromSegment, // can be nul
// Hopefully this case is never needed, but dealing with it anyway
mCharset.Assign("windows-1252");
mCharsetSource = kCharsetFromWeakDocTypeDefault;
- mExecutor->SetDocumentCharset(mCharset);
+ mTreeBuilder->SetDocumentCharset(mCharset);
}
return SetupDecodingAndWriteSniffingBufferAndCurrentSegment(aFromSegment, aCount, aWriteCount);
}
@@ -299,7 +311,7 @@ nsHtml5StreamParser::SniffStreamBytes(const PRUint8* aFromSegment,
mUnicodeDecoder->SetInputErrorBehavior(nsIUnicodeDecoder::kOnError_Recover);
// meta scan successful
mCharsetSource = kCharsetFromMetaPrescan;
- mExecutor->SetDocumentCharset(mCharset);
+ mTreeBuilder->SetDocumentCharset(mCharset);
mMetaScanner = nsnull;
return WriteSniffingBufferAndCurrentSegment(aFromSegment, aCount, aWriteCount);
}
@@ -312,7 +324,7 @@ nsHtml5StreamParser::SniffStreamBytes(const PRUint8* aFromSegment,
if (mUnicodeDecoder) {
// meta scan successful
mCharsetSource = kCharsetFromMetaPrescan;
- mExecutor->SetDocumentCharset(mCharset);
+ mTreeBuilder->SetDocumentCharset(mCharset);
mMetaScanner = nsnull;
return WriteSniffingBufferAndCurrentSegment(aFromSegment, aCount, aWriteCount);
}
@@ -382,26 +394,46 @@ nsHtml5StreamParser::WriteStreamBytes(const PRUint8* aFromSegment,
nsresult
nsHtml5StreamParser::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
{
- NS_PRECONDITION(eNone == mStreamListenerState,
- "Parser's nsIStreamListener API was not setup "
- "correctly in constructor.");
+ NS_PRECONDITION(STREAM_NOT_STARTED == mStreamState,
+ "Got OnStartRequest when the stream had already started.");
NS_PRECONDITION(mExecutor->GetLifeCycle() == NOT_STARTED,
- "Got OnStartRequest at the wrong stage in the life cycle.");
+ "Got OnStartRequest at the wrong stage in the executor life cycle.");
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (mObserver) {
mObserver->OnStartRequest(aRequest, aContext);
}
-#ifdef DEBUG
- mStreamListenerState = eOnStart;
-#endif
mRequest = aRequest;
+ mStreamState = STREAM_BEING_READ;
+
+ PRBool scriptingEnabled = mExecutor->IsScriptEnabled();
+ mOwner->StartTokenizer(scriptingEnabled);
+ mTreeBuilder->setScriptingEnabled(scriptingEnabled);
+ mTokenizer->start();
+ mExecutor->Start();
+ mExecutor->StartReadingFromStage();
/*
* If you move the following line, be very careful not to cause
* WillBuildModel to be called before the document has had its
* script global object set.
*/
- mTokenizer->start();
- mExecutor->SetLifeCycle(PARSING);
+ mExecutor->WillBuildModel(eDTDMode_unknown);
+
+ nsresult rv = NS_OK;
+
+ mReparseForbidden = PR_FALSE;
+ nsCOMPtr httpChannel(do_QueryInterface(mRequest, &rv));
+ if (NS_SUCCEEDED(rv)) {
+ nsCAutoString method;
+ httpChannel->GetRequestMethod(method);
+ // XXX does Necko have a way to renavigate POST, etc. without hitting
+ // the network?
+ if (!method.EqualsLiteral("GET")) {
+ // This is the old Gecko behavior but the HTML5 spec disagrees.
+ // Don't reparse on POST.
+ mReparseForbidden = PR_TRUE;
+ }
+ }
if (mCharsetSource < kCharsetFromChannel) {
// we aren't ready to commit to an encoding yet
@@ -409,7 +441,6 @@ nsHtml5StreamParser::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
return NS_OK;
}
- nsresult rv = NS_OK;
nsCOMPtr convManager = do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = convManager->GetUnicodeDecoder(mCharset.get(), getter_AddRefs(mUnicodeDecoder));
@@ -423,36 +454,26 @@ nsHtml5StreamParser::OnStopRequest(nsIRequest* aRequest,
nsISupports* aContext,
nsresult status)
{
- mExecutor->MaybeFlush();
+ NS_PRECONDITION(STREAM_BEING_READ == mStreamState,
+ "Stream ended without being open.");
NS_ASSERTION(mRequest == aRequest, "Got Stop on wrong stream.");
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
nsresult rv = NS_OK;
if (!mUnicodeDecoder) {
PRUint32 writeCount;
rv = FinalizeSniffing(nsnull, 0, &writeCount, 0);
NS_ENSURE_SUCCESS(rv, rv);
}
- switch (mExecutor->GetLifeCycle()) {
- case TERMINATED:
- break;
- case NOT_STARTED:
- NS_NOTREACHED("OnStopRequest before calling Parse() on the owner.");
- break;
- case STREAM_ENDING:
- NS_ERROR("OnStopRequest when the stream lifecycle was already ending.");
- break;
- default:
- mExecutor->SetLifeCycle(STREAM_ENDING);
- break;
- }
-#ifdef DEBUG
- mStreamListenerState = eOnStop;
-#endif
- if (!mExecutor->IsScriptExecuting()) {
- ParseUntilSuspend();
- }
+
+ mStreamState = STREAM_ENDED;
+
if (mObserver) {
mObserver->OnStopRequest(aRequest, aContext, status);
}
+ // TODO: proxy this to parser thread
+ if (!mWaitingForScripts) {
+ ParseUntilScript();
+ }
return NS_OK;
}
@@ -486,10 +507,8 @@ nsHtml5StreamParser::OnDataAvailable(nsIRequest* aRequest,
PRUint32 aSourceOffset,
PRUint32 aLength)
{
- mExecutor->MaybeFlush();
- NS_PRECONDITION(eOnStart == mStreamListenerState ||
- eOnDataAvail == mStreamListenerState,
- "Error: OnStartRequest() must be called before OnDataAvailable()");
+ NS_PRECONDITION(STREAM_BEING_READ == mStreamState,
+ "OnDataAvailable called when stream not open.");
NS_ASSERTION(mRequest == aRequest, "Got data on wrong stream.");
PRUint32 totalRead;
nsresult rv = aInStream->ReadSegments(nsHtml5StreamParser::ParserWriteFunc,
@@ -497,8 +516,8 @@ nsHtml5StreamParser::OnDataAvailable(nsIRequest* aRequest,
aLength,
&totalRead);
NS_ASSERTION(totalRead == aLength, "ReadSegments read the wrong number of bytes.");
- if (!mExecutor->IsScriptExecuting()) {
- ParseUntilSuspend();
+ if (!mWaitingForScripts) {
+ ParseUntilScript();
}
return rv;
}
@@ -529,62 +548,44 @@ nsHtml5StreamParser::internalEncodingDeclaration(nsString* aEncoding)
// XXX check HTML5 non-IANA aliases here
// The encodings are different. We want to reparse.
- nsCOMPtr httpChannel(do_QueryInterface(mRequest, &rv));
- if (NS_SUCCEEDED(rv)) {
- nsCAutoString method;
- httpChannel->GetRequestMethod(method);
- // XXX does Necko have a way to renavigate POST, etc. without hitting
- // the network?
- if (!method.EqualsLiteral("GET")) {
- // This is the old Gecko behavior but the spec disagrees.
- // Don't reparse on POST.
- return;
- }
+ if (mReparseForbidden) {
+ return; // not reparsing after all
}
// we still want to reparse
- mExecutor->NeedsCharsetSwitchTo(newEncoding);
+ mTreeBuilder->NeedsCharsetSwitchTo(newEncoding);
+ mTreeBuilder->Flush();
+ // the tree op executor will cause the stream parser to terminate
+ // if the charset switch request is accepted
}
void
-nsHtml5StreamParser::ParseUntilSuspend()
+nsHtml5StreamParser::ParseUntilScript()
{
- NS_PRECONDITION(!mExecutor->NeedsCharsetSwitch(), "ParseUntilSuspend called when charset switch needed.");
-
- if (mBlocked) {
+ if (IsTerminated()) {
return;
}
- switch (mExecutor->GetLifeCycle()) {
- case TERMINATED:
- return;
- case NOT_STARTED:
- NS_NOTREACHED("Bad life cycle!");
- break;
- default:
- break;
- }
+ // TODO: Relax this mutex so that the parser doesn't speculate to
+ // completion when it's already known that the speculation will fail.
+ mozilla::MutexAutoLock autoLock(mTokenizerMutex);
- mExecutor->WillResume();
- mSuspending = PR_FALSE;
for (;;) {
if (!mFirstBuffer->hasMore()) {
if (mFirstBuffer == mLastBuffer) {
- switch (mExecutor->GetLifeCycle()) {
- case TERMINATED:
- // something like cache manisfests stopped the parse in mid-flight
- return;
- case PARSING:
+ switch (mStreamState) {
+ case STREAM_BEING_READ:
// never release the last buffer. instead just zero its indeces for refill
mFirstBuffer->setStart(0);
mFirstBuffer->setEnd(0);
+ mTreeBuilder->Flush();
return; // no more data for now but expecting more
- case STREAM_ENDING:
- mDone = PR_TRUE;
- {
- nsRefPtr kungFuDeathGrip(this);
- mExecutor->DidBuildModel(PR_FALSE);
- }
+ case STREAM_ENDED:
+ Terminate(); // TODO Don't terminate if this is a speculation
+ mTokenizer->eof();
+ mTreeBuilder->StreamEnded();
+ mTreeBuilder->Flush();
+ TellExecutorToFlush();
return; // no more data and not expecting more
default:
NS_NOTREACHED("It should be impossible to reach this.");
@@ -598,32 +599,93 @@ nsHtml5StreamParser::ParseUntilSuspend()
}
}
- if (mBlocked || (mExecutor->GetLifeCycle() == TERMINATED)) {
- return;
- }
-
// now we have a non-empty buffer
mFirstBuffer->adjust(mLastWasCR);
mLastWasCR = PR_FALSE;
if (mFirstBuffer->hasMore()) {
mLastWasCR = mTokenizer->tokenizeBuffer(mFirstBuffer);
- NS_ASSERTION(!(mExecutor->HasScriptElement() && mExecutor->NeedsCharsetSwitch()), "Can't have both script and charset switch.");
- mExecutor->MaybeExecuteScript();
- if (mExecutor->MaybePerformCharsetSwitch() == NS_ERROR_HTMLPARSER_STOPPARSING) {
+ // At this point, internalEncodingDeclaration() may have called
+ // Terminate, but that never happens together with script.
+ // Can't assert that here, though, because it's possible that the main
+ // thread has called Terminate() while this thread was parsing.
+ if (IsTerminated()) {
return;
}
- if (mBlocked) {
- mExecutor->WillInterrupt();
- return;
- }
- // XXX we may now have document.written stuff in the other buffer
- // queue
- if (mSuspending) {
- mOwner->MaybePostContinueEvent();
- mExecutor->WillInterrupt();
- return;
+ if (mTreeBuilder->HasScript()) {
+ mTreeBuilder->AddSnapshotToScript(mTreeBuilder->newSnapshot());
+ mTreeBuilder->Flush();
+ TellExecutorToFlush();
+ // XXX start speculation
+ mWaitingForScripts = PR_TRUE;
+ return; // ContinueAfterScripts() will re-enable this parser
}
+ mTreeBuilder->MaybeFlush();
}
continue;
}
}
+
+void
+nsHtml5StreamParser::ContinueAfterScripts(nsHtml5Tokenizer* aTokenizer,
+ nsHtml5TreeBuilder* aTreeBuilder,
+ PRBool aLastWasCR)
+{
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+ mExecutor->StartReadingFromStage();
+ // TODO:
+ // test if the state of the argument tokenizer and tree builder match
+ // the earliest speculation.
+ // If not, rendez-vous at barrier, zaps all speculations, rewind the stream
+ // and copy over the state.
+ // If yes:
+ // If there are multiple speculations or the stream parser has terminated.
+ // load the tree op queue from the earliest speculation into the tree op
+ // executor and discard the stream data for that speculation. Return.
+ // Otherwise, rendez-vous at barrier, load the tree op queue from the
+ // speculation into the tree op executor, set the tree op executor to read
+ // from the stage, set the stream parser tree builder to write to stage,
+ // discard the stream data for the speculation.
+
+ {
+ mozilla::MutexAutoLock autoLock(mTokenizerMutex);
+
+ // Approximation: Copy state over for now unconditionally.
+ mLastWasCR = aLastWasCR;
+ mTokenizer->loadState(aTokenizer);
+ mTreeBuilder->loadState(aTreeBuilder, &mAtomTable);
+
+ mWaitingForScripts = PR_FALSE;
+ }
+ // TODO: proxy the tail of this method to the parser thread
+ ParseUntilScript();
+}
+
+class nsHtml5StreamParserExecutorFlushEvent : public nsRunnable
+{
+public:
+ nsRefPtr mStreamParser;
+ nsHtml5StreamParserExecutorFlushEvent(nsHtml5StreamParser* aStreamParser)
+ : mStreamParser(aStreamParser)
+ {}
+ NS_IMETHODIMP Run()
+ {
+ mStreamParser->DoExecFlush();
+ return NS_OK;
+ }
+};
+
+void
+nsHtml5StreamParser::DoExecFlush()
+{
+ mExecutor->Flush();
+}
+
+void
+nsHtml5StreamParser::TellExecutorToFlush()
+{
+ // TODO: Make this cross-thread
+ nsCOMPtr event = new nsHtml5StreamParserExecutorFlushEvent(this);
+ if (NS_FAILED(NS_DispatchToMainThread(event))) {
+ NS_WARNING("failed to dispatch executor flush event");
+ }
+}
diff --git a/parser/html/nsHtml5StreamParser.h b/parser/html/nsHtml5StreamParser.h
index c82826b83a0..fbfa1721a58 100644
--- a/parser/html/nsHtml5StreamParser.h
+++ b/parser/html/nsHtml5StreamParser.h
@@ -49,6 +49,8 @@
#include "nsHtml5UTF16Buffer.h"
#include "nsIInputStream.h"
#include "nsICharsetAlias.h"
+#include "mozilla/Mutex.h"
+#include "nsHtml5AtomTable.h"
class nsHtml5Parser;
@@ -91,6 +93,12 @@ enum eBomState {
BOM_SNIFFING_OVER = 5
};
+enum eHtml5StreamState {
+ STREAM_NOT_STARTED = 0,
+ STREAM_BEING_READ = 1,
+ STREAM_ENDED = 2
+};
+
class nsHtml5StreamParser : public nsIStreamListener,
public nsICharsetDetectionObserver {
public:
@@ -98,8 +106,7 @@ class nsHtml5StreamParser : public nsIStreamListener,
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsHtml5StreamParser, nsIStreamListener)
- nsHtml5StreamParser(nsHtml5Tokenizer* aTokenizer,
- nsHtml5TreeOpExecutor* aExecutor,
+ nsHtml5StreamParser(nsHtml5TreeOpExecutor* aExecutor,
nsHtml5Parser* aOwner);
virtual ~nsHtml5StreamParser();
@@ -131,36 +138,49 @@ class nsHtml5StreamParser : public nsIStreamListener,
* @param aCharsetSource the source of the charset
*/
inline void SetDocumentCharset(const nsACString& aCharset, PRInt32 aSource) {
+ NS_PRECONDITION(mStreamState == STREAM_NOT_STARTED,
+ "SetDocumentCharset called too late.");
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
mCharset = aCharset;
mCharsetSource = aSource;
}
inline void SetObserver(nsIRequestObserver* aObserver) {
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
mObserver = aObserver;
}
nsresult GetChannel(nsIChannel** aChannel);
- inline void Block() {
- mBlocked = PR_TRUE;
- }
+ void ParseUntilScript();
- inline void Unblock() {
- mBlocked = PR_FALSE;
- }
+ /**
+ * The owner parser must call this after script execution
+ * when no scripts are executing and the document.written
+ * buffer has been exhausted.
+ */
+ void ContinueAfterScripts(nsHtml5Tokenizer* aTokenizer,
+ nsHtml5TreeBuilder* aTreeBuilder,
+ PRBool aLastWasCR);
- inline void Suspend() {
- mSuspending = PR_TRUE;
+ void Terminate() {
+ mozilla::MutexAutoLock autoLock(mTerminatedMutex);
+ mTerminated = PR_TRUE;
+ // TODO: Make sure this object stays alive as long as there are
+ // in-flight runnables coming this way
}
+
+ void DoExecFlush();
- void ParseUntilSuspend();
-
- PRBool IsDone() {
- return mDone;
- }
-
private:
+ PRBool IsTerminated() {
+ mozilla::MutexAutoLock autoLock(mTerminatedMutex);
+ return mTerminated;
+ }
+
+ void TellExecutorToFlush();
+
static NS_METHOD ParserWriteFunc(nsIInputStream* aInStream,
void* aHtml5StreamParser,
const char* aFromSegment,
@@ -289,6 +309,11 @@ class nsHtml5StreamParser : public nsIStreamListener,
*/
nsCString mCharset;
+ /**
+ * Whether reparse is forbidden
+ */
+ PRBool mReparseForbidden;
+
// Portable parser objects
/**
* The first buffer in the pending UTF-16 buffer queue
@@ -309,13 +334,27 @@ class nsHtml5StreamParser : public nsIStreamListener,
/**
* The HTML5 tree builder
*/
- nsHtml5TreeBuilder* mTreeBuilder;
+ nsAutoPtr mTreeBuilder;
/**
* The HTML5 tokenizer
*/
- nsHtml5Tokenizer* mTokenizer;
+ nsAutoPtr mTokenizer;
+ /**
+ * Makes sure the main thread can't mess the tokenizer state while it's
+ * tokenizing
+ */
+ mozilla::Mutex mTokenizerMutex;
+
+ /**
+ * The scoped atom table
+ */
+ nsHtml5AtomTable mAtomTable;
+
+ /**
+ * The owner parser.
+ */
nsCOMPtr mOwner;
/**
@@ -324,26 +363,20 @@ class nsHtml5StreamParser : public nsIStreamListener,
PRBool mLastWasCR;
/**
- * The parser is blocking on a script
+ * For tracking stream life cycle
*/
- PRBool mBlocked;
+ eHtml5StreamState mStreamState;
+
+ /**
+ * Whether we are waiting for scripts to be done.
+ */
+ PRBool mWaitingForScripts;
/**
- * The event loop will spin ASAP
+ * True to terminate early; protected by mTerminatedMutex
*/
- PRBool mSuspending;
-
- /**
- * Whether the stream parser is done
- */
- PRBool mDone;
-
-#ifdef DEBUG
- /**
- * For asserting stream life cycle
- */
- eStreamState mStreamListenerState;
-#endif
+ PRBool mTerminated;
+ mozilla::Mutex mTerminatedMutex;
};
diff --git a/parser/html/nsHtml5Tokenizer.cpp b/parser/html/nsHtml5Tokenizer.cpp
index 96a655d193d..cd2f3d61462 100644
--- a/parser/html/nsHtml5Tokenizer.cpp
+++ b/parser/html/nsHtml5Tokenizer.cpp
@@ -45,6 +45,7 @@
#include "nsHtml5Atoms.h"
#include "nsHtml5ByteReadable.h"
#include "nsIUnicodeDecoder.h"
+#include "nsAHtml5TreeBuilderState.h"
#include "nsHtml5TreeBuilder.h"
#include "nsHtml5MetaScanner.h"
@@ -74,9 +75,12 @@ nsHtml5Tokenizer::nsHtml5Tokenizer(nsHtml5TreeBuilder* tokenHandler)
encodingDeclarationHandler(nsnull),
bmpChar(jArray(1)),
astralChar(jArray(2)),
- attributes(nsnull),
tagName(nsnull),
- attributeName(nsnull)
+ attributeName(nsnull),
+ doctypeName(nsnull),
+ publicIdentifier(nsnull),
+ systemIdentifier(nsnull),
+ attributes(nsnull)
{
MOZ_COUNT_CTOR(nsHtml5Tokenizer);
}
@@ -400,6 +404,7 @@ nsHtml5Tokenizer::addAttributeWithoutValue()
if (!!attributeName) {
attributes->addAttribute(attributeName, nsHtml5Portability::newEmptyString());
+ attributeName = nsnull;
}
}
@@ -409,6 +414,7 @@ nsHtml5Tokenizer::addAttributeWithValue()
if (!!attributeName) {
nsString* value = longStrBufToString();
attributes->addAttribute(attributeName, value);
+ attributeName = nsnull;
}
}
@@ -420,39 +426,8 @@ nsHtml5Tokenizer::startErrorReporting()
void
nsHtml5Tokenizer::start()
{
- confident = PR_FALSE;
- strBuf = jArray(64);
- strBufLen = 0;
- longStrBuf = jArray(1024);
- longStrBufLen = 0;
- stateSave = NS_HTML5TOKENIZER_DATA;
- line = 1;
- lastCR = PR_FALSE;
+ initializeWithoutStarting();
tokenHandler->startTokenization(this);
- index = 0;
- forceQuirks = PR_FALSE;
- additional = '\0';
- entCol = -1;
- lo = 0;
- hi = (nsHtml5NamedCharacters::NAMES.length - 1);
- candidate = -1;
- strBufMark = 0;
- prevValue = -1;
- value = 0;
- seenDigits = PR_FALSE;
- shouldSuspend = PR_FALSE;
- if (!!tagName) {
- tagName->release();
- tagName = nsnull;
- }
- if (!!attributeName) {
- attributeName->release();
- attributeName = nsnull;
- }
- if (!!attributes) {
- delete attributes;
- attributes = nsnull;
- }
}
PRBool
@@ -948,7 +923,7 @@ nsHtml5Tokenizer::stateLoop(PRInt32 state, PRUnichar c, PRInt32 pos, PRUnichar*
}
case '&': {
clearStrBufAndAppendCurrentC(c);
- rememberAmpersandLocation('\0');
+ rememberAmpersandLocation('>');
returnState = state;
state = NS_HTML5TOKENIZER_CONSUME_CHARACTER_REFERENCE;
goto stateloop;
@@ -2894,6 +2869,22 @@ nsHtml5Tokenizer::stateLoop(PRInt32 state, PRUnichar c, PRInt32 pos, PRUnichar*
return pos;
}
+void
+nsHtml5Tokenizer::initDoctypeFields()
+{
+ nsHtml5Portability::releaseLocal(doctypeName);
+ doctypeName = nsHtml5Atoms::emptystring;
+ if (!!systemIdentifier) {
+ nsHtml5Portability::releaseString(systemIdentifier);
+ systemIdentifier = nsnull;
+ }
+ if (!!publicIdentifier) {
+ nsHtml5Portability::releaseString(publicIdentifier);
+ publicIdentifier = nsnull;
+ }
+ forceQuirks = PR_FALSE;
+}
+
void
nsHtml5Tokenizer::emitCarriageReturn(PRUnichar* buf, PRInt32 pos)
{
@@ -3057,9 +3048,16 @@ nsHtml5Tokenizer::eof()
emitComment(0, 0);
} else {
+ nsHtml5Portability::releaseLocal(doctypeName);
doctypeName = nsHtml5Atoms::emptystring;
- publicIdentifier = nsnull;
- systemIdentifier = nsnull;
+ if (!!systemIdentifier) {
+ nsHtml5Portability::releaseString(systemIdentifier);
+ systemIdentifier = nsnull;
+ }
+ if (!!publicIdentifier) {
+ nsHtml5Portability::releaseString(publicIdentifier);
+ publicIdentifier = nsnull;
+ }
forceQuirks = PR_TRUE;
emitDoctypeToken(0);
goto eofloop_end;
@@ -3268,8 +3266,11 @@ nsHtml5Tokenizer::emitDoctypeToken(PRInt32 pos)
cstart = pos + 1;
tokenHandler->doctype(doctypeName, publicIdentifier, systemIdentifier, forceQuirks);
nsHtml5Portability::releaseLocal(doctypeName);
+ doctypeName = nsnull;
nsHtml5Portability::releaseString(publicIdentifier);
+ publicIdentifier = nsnull;
nsHtml5Portability::releaseString(systemIdentifier);
+ systemIdentifier = nsnull;
}
void
@@ -3303,11 +3304,20 @@ nsHtml5Tokenizer::emitOrAppendOne(PRUnichar* val, PRInt32 returnState)
void
nsHtml5Tokenizer::end()
{
+ strBuf.release();
strBuf = nsnull;
+ longStrBuf.release();
longStrBuf = nsnull;
- systemIdentifier = nsnull;
- publicIdentifier = nsnull;
+ nsHtml5Portability::releaseLocal(doctypeName);
doctypeName = nsnull;
+ if (!!systemIdentifier) {
+ nsHtml5Portability::releaseString(systemIdentifier);
+ systemIdentifier = nsnull;
+ }
+ if (!!publicIdentifier) {
+ nsHtml5Portability::releaseString(publicIdentifier);
+ publicIdentifier = nsnull;
+ }
if (!!tagName) {
tagName->release();
tagName = nsnull;
@@ -3366,6 +3376,122 @@ nsHtml5Tokenizer::isInDataState()
return (stateSave == NS_HTML5TOKENIZER_DATA);
}
+void
+nsHtml5Tokenizer::resetToDataState()
+{
+ strBufLen = 0;
+ longStrBufLen = 0;
+ stateSave = NS_HTML5TOKENIZER_DATA;
+ lastCR = PR_FALSE;
+ index = 0;
+ forceQuirks = PR_FALSE;
+ additional = '\0';
+ entCol = -1;
+ lo = 0;
+ hi = (nsHtml5NamedCharacters::NAMES.length - 1);
+ candidate = -1;
+ strBufMark = 0;
+ prevValue = -1;
+ value = 0;
+ seenDigits = PR_FALSE;
+ shouldSuspend = PR_FALSE;
+ initDoctypeFields();
+ if (!!tagName) {
+ tagName->release();
+ tagName = nsnull;
+ }
+ if (!!attributeName) {
+ attributeName->release();
+ attributeName = nsnull;
+ }
+ if (!!attributes) {
+ delete attributes;
+ attributes = nsnull;
+ }
+}
+
+void
+nsHtml5Tokenizer::loadState(nsHtml5Tokenizer* other)
+{
+ strBufLen = other->strBufLen;
+ if (strBufLen > strBuf.length) {
+ strBuf.release();
+ strBuf = jArray(strBufLen);
+ }
+ nsHtml5ArrayCopy::arraycopy(other->strBuf, strBuf, strBufLen);
+ longStrBufLen = other->longStrBufLen;
+ if (longStrBufLen > longStrBuf.length) {
+ longStrBuf.release();
+ longStrBuf = jArray(longStrBufLen);
+ }
+ nsHtml5ArrayCopy::arraycopy(other->longStrBuf, longStrBuf, longStrBufLen);
+ stateSave = other->stateSave;
+ lastCR = other->lastCR;
+ index = other->index;
+ forceQuirks = other->forceQuirks;
+ additional = other->additional;
+ entCol = other->entCol;
+ lo = other->lo;
+ hi = other->hi;
+ candidate = other->candidate;
+ strBufMark = other->strBufMark;
+ prevValue = other->prevValue;
+ value = other->value;
+ seenDigits = other->seenDigits;
+ shouldSuspend = PR_FALSE;
+ nsHtml5Portability::releaseLocal(doctypeName);
+ if (!other->doctypeName) {
+ doctypeName = nsnull;
+ } else {
+ doctypeName = nsHtml5Portability::newLocalFromLocal(other->doctypeName, interner);
+ }
+ nsHtml5Portability::releaseString(systemIdentifier);
+ if (!other->systemIdentifier) {
+ systemIdentifier = nsnull;
+ } else {
+ systemIdentifier = nsHtml5Portability::newStringFromString(other->systemIdentifier);
+ }
+ nsHtml5Portability::releaseString(publicIdentifier);
+ if (!other->publicIdentifier) {
+ publicIdentifier = nsnull;
+ } else {
+ publicIdentifier = nsHtml5Portability::newStringFromString(other->publicIdentifier);
+ }
+ if (!!tagName) {
+ tagName->release();
+ }
+ if (!other->tagName) {
+ tagName = nsnull;
+ } else {
+ tagName = other->tagName->cloneElementName(interner);
+ }
+ if (!!attributeName) {
+ attributeName->release();
+ }
+ if (!other->attributeName) {
+ attributeName = nsnull;
+ } else {
+ attributeName = other->attributeName->cloneAttributeName(interner);
+ }
+ if (!!attributes) {
+ delete attributes;
+ }
+ if (!other->attributes) {
+ attributes = nsnull;
+ } else {
+ attributes = other->attributes->cloneAttributes(interner);
+ }
+}
+
+void
+nsHtml5Tokenizer::initializeWithoutStarting()
+{
+ confident = PR_FALSE;
+ strBuf = jArray(64);
+ longStrBuf = jArray(1024);
+ resetToDataState();
+}
+
void
nsHtml5Tokenizer::setEncodingDeclarationHandler(nsHtml5StreamParser* encodingDeclarationHandler)
{
diff --git a/parser/html/nsHtml5Tokenizer.h b/parser/html/nsHtml5Tokenizer.h
index 2bacfb2c7d0..d046a1908a2 100644
--- a/parser/html/nsHtml5Tokenizer.h
+++ b/parser/html/nsHtml5Tokenizer.h
@@ -46,8 +46,10 @@
#include "nsHtml5Atoms.h"
#include "nsHtml5ByteReadable.h"
#include "nsIUnicodeDecoder.h"
+#include "nsAHtml5TreeBuilderState.h"
class nsHtml5StreamParser;
+class nsHtml5SpeculativeLoader;
class nsHtml5TreeBuilder;
class nsHtml5MetaScanner;
@@ -112,7 +114,6 @@ class nsHtml5Tokenizer
PRInt32 strBufLen;
jArray longStrBuf;
PRInt32 longStrBufLen;
- nsHtml5HtmlAttributes* attributes;
jArray bmpChar;
jArray astralChar;
protected:
@@ -129,6 +130,7 @@ class nsHtml5Tokenizer
nsIAtom* doctypeName;
nsString* publicIdentifier;
nsString* systemIdentifier;
+ nsHtml5HtmlAttributes* attributes;
PRInt32 mappingLangToXmlLang;
PRBool shouldSuspend;
protected:
@@ -192,14 +194,7 @@ class nsHtml5Tokenizer
PRBool tokenizeBuffer(nsHtml5UTF16Buffer* buffer);
private:
PRInt32 stateLoop(PRInt32 state, PRUnichar c, PRInt32 pos, PRUnichar* buf, PRBool reconsume, PRInt32 returnState, PRInt32 endPos);
- inline void initDoctypeFields()
- {
- doctypeName = nsHtml5Atoms::emptystring;
- systemIdentifier = nsnull;
- publicIdentifier = nsnull;
- forceQuirks = PR_FALSE;
- }
-
+ void initDoctypeFields();
inline void adjustDoubleHyphenAndAppendToLongStrBufCarriageReturn()
{
silentCarriageReturn();
@@ -268,6 +263,9 @@ class nsHtml5Tokenizer
PRInt32 getLine();
PRInt32 getCol();
PRBool isInDataState();
+ void resetToDataState();
+ void loadState(nsHtml5Tokenizer* other);
+ void initializeWithoutStarting();
void setEncodingDeclarationHandler(nsHtml5StreamParser* encodingDeclarationHandler);
static void initializeStatics();
static void releaseStatics();
diff --git a/parser/html/nsHtml5TreeBuilder.cpp b/parser/html/nsHtml5TreeBuilder.cpp
index eeeae554603..d681da78588 100644
--- a/parser/html/nsHtml5TreeBuilder.cpp
+++ b/parser/html/nsHtml5TreeBuilder.cpp
@@ -32,6 +32,7 @@
#include "prtypes.h"
#include "nsIAtom.h"
+#include "nsHtml5AtomTable.h"
#include "nsITimer.h"
#include "nsString.h"
#include "nsINameSpaceManager.h"
@@ -51,6 +52,7 @@
#include "nsHtml5StackNode.h"
#include "nsHtml5TreeOpExecutor.h"
#include "nsHtml5StreamParser.h"
+#include "nsAHtml5TreeBuilderState.h"
#include "nsHtml5Tokenizer.h"
#include "nsHtml5MetaScanner.h"
@@ -74,9 +76,9 @@ nsHtml5TreeBuilder::startTokenization(nsHtml5Tokenizer* self)
originalMode = NS_HTML5TREE_BUILDER_INITIAL;
currentPtr = -1;
listPtr = -1;
- nsHtml5Portability::releaseElement(formPointer);
+ ;
formPointer = nsnull;
- nsHtml5Portability::releaseElement(headPointer);
+ ;
headPointer = nsnull;
start(fragment);
charBufferLen = 0;
@@ -85,7 +87,7 @@ nsHtml5TreeBuilder::startTokenization(nsHtml5Tokenizer* self)
nsIContent** elt;
if (!!contextNode) {
elt = contextNode;
- nsHtml5Portability::retainElement(elt);
+ ;
} else {
elt = createHtmlElementSetAsRoot(tokenizer->emptyAttributes());
}
@@ -104,9 +106,9 @@ nsHtml5TreeBuilder::startTokenization(nsHtml5Tokenizer* self)
}
nsHtml5Portability::releaseLocal(contextName);
contextName = nsnull;
- nsHtml5Portability::releaseElement(contextNode);
+ ;
contextNode = nsnull;
- nsHtml5Portability::releaseElement(elt);
+ ;
} else {
mode = NS_HTML5TREE_BUILDER_INITIAL;
foreignFlag = NS_HTML5TREE_BUILDER_NOT_IN_FOREIGN;
@@ -534,26 +536,32 @@ nsHtml5TreeBuilder::eof()
void
nsHtml5TreeBuilder::endTokenization()
{
- nsHtml5Portability::releaseElement(formPointer);
+ ;
formPointer = nsnull;
- nsHtml5Portability::releaseElement(headPointer);
+ ;
headPointer = nsnull;
- while (currentPtr > -1) {
- stack[currentPtr]->release();
- currentPtr--;
- }
- stack.release();
- stack = nsnull;
- while (listPtr > -1) {
- if (!!listOfActiveFormattingElements[listPtr]) {
- listOfActiveFormattingElements[listPtr]->release();
+ if (!!stack) {
+ while (currentPtr > -1) {
+ stack[currentPtr]->release();
+ currentPtr--;
}
- listPtr--;
+ stack.release();
+ stack = nsnull;
+ }
+ if (!!listOfActiveFormattingElements) {
+ while (listPtr > -1) {
+ if (!!listOfActiveFormattingElements[listPtr]) {
+ listOfActiveFormattingElements[listPtr]->release();
+ }
+ listPtr--;
+ }
+ listOfActiveFormattingElements.release();
+ listOfActiveFormattingElements = nsnull;
+ }
+ if (!!charBuffer) {
+ charBuffer.release();
+ charBuffer = nsnull;
}
- listOfActiveFormattingElements.release();
- listOfActiveFormattingElements = nsnull;
- charBuffer.release();
- charBuffer = nsnull;
end();
}
@@ -2234,7 +2242,7 @@ nsHtml5TreeBuilder::endTag(nsHtml5ElementName* elementName)
goto endtagloop_end;
}
- nsHtml5Portability::releaseElement(formPointer);
+ ;
formPointer = nsnull;
eltPos = findLastInScope(name);
if (eltPos == NS_HTML5TREE_BUILDER_NOT_FOUND_ON_STACK) {
@@ -3136,7 +3144,7 @@ nsHtml5TreeBuilder::adoptionAgencyEndTag(nsIAtom* name)
}
- nsIContent** clone = createElement(kNameSpaceID_XHTML, node->name, node->attributes->cloneAttributes());
+ nsIContent** clone = createElement(kNameSpaceID_XHTML, node->name, node->attributes->cloneAttributes(nsnull));
nsHtml5StackNode* newNode = new nsHtml5StackNode(node->group, node->ns, node->name, clone, node->scoping, node->special, node->fosterParenting, node->popName, node->attributes);
node->dropAttributes();
stack[nodePos] = newNode;
@@ -3145,7 +3153,7 @@ nsHtml5TreeBuilder::adoptionAgencyEndTag(nsIAtom* name)
node->release();
node->release();
node = newNode;
- nsHtml5Portability::releaseElement(clone);
+ ;
detachFromParent(lastNode->node);
appendElement(lastNode->node, node->node);
lastNode = node;
@@ -3158,7 +3166,7 @@ nsHtml5TreeBuilder::adoptionAgencyEndTag(nsIAtom* name)
detachFromParent(lastNode->node);
appendElement(lastNode->node, commonAncestor->node);
}
- nsIContent** clone = createElement(kNameSpaceID_XHTML, formattingElt->name, formattingElt->attributes->cloneAttributes());
+ nsIContent** clone = createElement(kNameSpaceID_XHTML, formattingElt->name, formattingElt->attributes->cloneAttributes(nsnull));
nsHtml5StackNode* formattingClone = new nsHtml5StackNode(formattingElt->group, formattingElt->ns, formattingElt->name, clone, formattingElt->scoping, formattingElt->special, formattingElt->fosterParenting, formattingElt->popName, formattingElt->attributes);
formattingElt->dropAttributes();
appendChildrenToNewParent(furthestBlock->node, clone);
@@ -3168,7 +3176,7 @@ nsHtml5TreeBuilder::adoptionAgencyEndTag(nsIAtom* name)
removeFromStack(formattingEltStackPos);
insertIntoStack(formattingClone, furthestBlockPos);
- nsHtml5Portability::releaseElement(clone);
+ ;
}
}
@@ -3305,7 +3313,7 @@ nsHtml5TreeBuilder::reconstructTheActiveFormattingElements()
while (entryPos < listPtr) {
entryPos++;
nsHtml5StackNode* entry = listOfActiveFormattingElements[entryPos];
- nsIContent** clone = createElement(kNameSpaceID_XHTML, entry->name, entry->attributes->cloneAttributes());
+ nsIContent** clone = createElement(kNameSpaceID_XHTML, entry->name, entry->attributes->cloneAttributes(nsnull));
nsHtml5StackNode* entryClone = new nsHtml5StackNode(entry->group, entry->ns, entry->name, clone, entry->scoping, entry->special, entry->fosterParenting, entry->popName, entry->attributes);
entry->dropAttributes();
nsHtml5StackNode* currentNode = stack[currentPtr];
@@ -3363,8 +3371,8 @@ nsHtml5TreeBuilder::popOnEof()
nsHtml5StackNode* node = stack[currentPtr];
currentPtr--;
- elementPopped(node->ns, node->popName, node->node);
markMalformedIfScript(node->node);
+ elementPopped(node->ns, node->popName, node->node);
node->release();
}
@@ -3374,7 +3382,7 @@ nsHtml5TreeBuilder::appendHtmlElementToDocumentAndPush(nsHtml5HtmlAttributes* at
nsIContent** elt = createHtmlElementSetAsRoot(attributes);
nsHtml5StackNode* node = new nsHtml5StackNode(kNameSpaceID_XHTML, nsHtml5ElementName::ELT_HTML, elt);
push(node);
- nsHtml5Portability::releaseElement(elt);
+ ;
}
void
@@ -3390,10 +3398,10 @@ nsHtml5TreeBuilder::appendToCurrentNodeAndPushHeadElement(nsHtml5HtmlAttributes*
nsIContent** elt = createElement(kNameSpaceID_XHTML, nsHtml5Atoms::head, attributes);
appendElement(elt, stack[currentPtr]->node);
headPointer = elt;
- nsHtml5Portability::retainElement(headPointer);
+ ;
nsHtml5StackNode* node = new nsHtml5StackNode(kNameSpaceID_XHTML, nsHtml5ElementName::ELT_HEAD, elt);
push(node);
- nsHtml5Portability::releaseElement(elt);
+ ;
}
void
@@ -3414,7 +3422,7 @@ nsHtml5TreeBuilder::appendToCurrentNodeAndPushFormElementMayFoster(nsHtml5HtmlAt
flushCharacters();
nsIContent** elt = createElement(kNameSpaceID_XHTML, nsHtml5Atoms::form, attributes);
formPointer = elt;
- nsHtml5Portability::retainElement(formPointer);
+ ;
nsHtml5StackNode* current = stack[currentPtr];
if (current->fosterParenting) {
@@ -3424,7 +3432,7 @@ nsHtml5TreeBuilder::appendToCurrentNodeAndPushFormElementMayFoster(nsHtml5HtmlAt
}
nsHtml5StackNode* node = new nsHtml5StackNode(kNameSpaceID_XHTML, nsHtml5ElementName::ELT_FORM, elt);
push(node);
- nsHtml5Portability::releaseElement(elt);
+ ;
}
void
@@ -3439,11 +3447,11 @@ nsHtml5TreeBuilder::appendToCurrentNodeAndPushFormattingElementMayFoster(PRInt32
} else {
appendElement(elt, current->node);
}
- nsHtml5StackNode* node = new nsHtml5StackNode(ns, elementName, elt, attributes->cloneAttributes());
+ nsHtml5StackNode* node = new nsHtml5StackNode(ns, elementName, elt, attributes->cloneAttributes(nsnull));
push(node);
append(node);
node->retain();
- nsHtml5Portability::releaseElement(elt);
+ ;
}
void
@@ -3454,7 +3462,7 @@ nsHtml5TreeBuilder::appendToCurrentNodeAndPushElement(PRInt32 ns, nsHtml5Element
appendElement(elt, stack[currentPtr]->node);
nsHtml5StackNode* node = new nsHtml5StackNode(ns, elementName, elt);
push(node);
- nsHtml5Portability::releaseElement(elt);
+ ;
}
void
@@ -3472,7 +3480,7 @@ nsHtml5TreeBuilder::appendToCurrentNodeAndPushElementMayFoster(PRInt32 ns, nsHtm
}
nsHtml5StackNode* node = new nsHtml5StackNode(ns, elementName, elt, popName);
push(node);
- nsHtml5Portability::releaseElement(elt);
+ ;
}
void
@@ -3490,7 +3498,7 @@ nsHtml5TreeBuilder::appendToCurrentNodeAndPushElementMayFosterNoScoping(PRInt32
}
nsHtml5StackNode* node = new nsHtml5StackNode(ns, elementName, elt, popName, PR_FALSE);
push(node);
- nsHtml5Portability::releaseElement(elt);
+ ;
}
void
@@ -3508,7 +3516,7 @@ nsHtml5TreeBuilder::appendToCurrentNodeAndPushElementMayFosterCamelCase(PRInt32
}
nsHtml5StackNode* node = new nsHtml5StackNode(ns, elementName, elt, popName, nsHtml5ElementName::ELT_FOREIGNOBJECT == elementName);
push(node);
- nsHtml5Portability::releaseElement(elt);
+ ;
}
void
@@ -3525,7 +3533,7 @@ nsHtml5TreeBuilder::appendToCurrentNodeAndPushElementMayFoster(PRInt32 ns, nsHtm
}
nsHtml5StackNode* node = new nsHtml5StackNode(ns, elementName, elt);
push(node);
- nsHtml5Portability::releaseElement(elt);
+ ;
}
void
@@ -3542,7 +3550,7 @@ nsHtml5TreeBuilder::appendVoidElementToCurrentMayFoster(PRInt32 ns, nsIAtom* nam
}
elementPushed(ns, name, elt);
elementPopped(ns, name, elt);
- nsHtml5Portability::releaseElement(elt);
+ ;
}
void
@@ -3560,7 +3568,7 @@ nsHtml5TreeBuilder::appendVoidElementToCurrentMayFoster(PRInt32 ns, nsHtml5Eleme
}
elementPushed(ns, popName, elt);
elementPopped(ns, popName, elt);
- nsHtml5Portability::releaseElement(elt);
+ ;
}
void
@@ -3578,7 +3586,7 @@ nsHtml5TreeBuilder::appendVoidElementToCurrentMayFosterCamelCase(PRInt32 ns, nsH
}
elementPushed(ns, popName, elt);
elementPopped(ns, popName, elt);
- nsHtml5Portability::releaseElement(elt);
+ ;
}
void
@@ -3590,7 +3598,7 @@ nsHtml5TreeBuilder::appendVoidElementToCurrent(PRInt32 ns, nsIAtom* name, nsHtml
appendElement(elt, current->node);
elementPushed(ns, name, elt);
elementPopped(ns, name, elt);
- nsHtml5Portability::releaseElement(elt);
+ ;
}
void
@@ -3620,7 +3628,7 @@ nsHtml5TreeBuilder::setFragmentContext(nsIAtom* context, PRInt32 ns, nsIContent*
nsHtml5Portability::retainLocal(context);
this->contextNamespace = ns;
this->contextNode = node;
- nsHtml5Portability::retainElement(node);
+ ;
this->fragment = (!!contextName);
this->quirks = quirks;
}
@@ -3692,46 +3700,198 @@ nsHtml5TreeBuilder::charBufferContainsNonWhitespace()
return PR_FALSE;
}
-nsHtml5StateSnapshot*
+nsAHtml5TreeBuilderState*
nsHtml5TreeBuilder::newSnapshot()
{
- jArray stackCopy = jArray(currentPtr + 1);
- for (PRInt32 i = 0; i < stackCopy.length; i++) {
- (stackCopy[i] = stack[i])->retain();
- }
jArray listCopy = jArray(listPtr + 1);
for (PRInt32 i = 0; i < listCopy.length; i++) {
nsHtml5StackNode* node = listOfActiveFormattingElements[i];
if (!!node) {
- node->retain();
+ nsHtml5StackNode* newNode = new nsHtml5StackNode(node->group, node->ns, node->name, node->node, node->scoping, node->special, node->fosterParenting, node->popName, node->attributes->cloneAttributes(nsnull));
+ listCopy[i] = newNode;
+ } else {
+ listCopy[i] = nsnull;
}
- listCopy[i] = node;
}
- nsHtml5Portability::retainElement(formPointer);
- return new nsHtml5StateSnapshot(stackCopy, listCopy, formPointer);
+ jArray stackCopy = jArray(currentPtr + 1);
+ for (PRInt32 i = 0; i < stackCopy.length; i++) {
+ nsHtml5StackNode* node = stack[i];
+ PRInt32 listIndex = findInListOfActiveFormattingElements(node);
+ if (listIndex == -1) {
+ nsHtml5StackNode* newNode = new nsHtml5StackNode(node->group, node->ns, node->name, node->node, node->scoping, node->special, node->fosterParenting, node->popName, nsnull);
+ stackCopy[i] = newNode;
+ } else {
+ stackCopy[i] = listCopy[listIndex];
+ stackCopy[i]->retain();
+ }
+ }
+ ;
+ return new nsHtml5StateSnapshot(stackCopy, listCopy, formPointer, headPointer, mode, originalMode, foreignFlag, needToDropLF, quirks);
}
PRBool
-nsHtml5TreeBuilder::snapshotMatches(nsHtml5StateSnapshot* snapshot)
+nsHtml5TreeBuilder::snapshotMatches(nsAHtml5TreeBuilderState* snapshot)
{
- jArray stackCopy = snapshot->stack;
- jArray listCopy = snapshot->listOfActiveFormattingElements;
- if (stackCopy.length != currentPtr + 1 || listCopy.length != listPtr + 1 || formPointer != snapshot->formPointer) {
+ jArray stackCopy = snapshot->getStack();
+ PRInt32 stackLen = snapshot->getStackLength();
+ jArray listCopy = snapshot->getListOfActiveFormattingElements();
+ PRInt32 listLen = snapshot->getListLength();
+ if (stackLen != currentPtr + 1 || listLen != listPtr + 1 || formPointer != snapshot->getFormPointer() || headPointer != snapshot->getHeadPointer() || mode != snapshot->getMode() || originalMode != snapshot->getOriginalMode() || foreignFlag != snapshot->getForeignFlag() || needToDropLF != snapshot->isNeedToDropLF() || quirks != snapshot->isQuirks()) {
return PR_FALSE;
}
- for (PRInt32 i = listCopy.length - 1; i >= 0; i--) {
- if (listCopy[i] != listOfActiveFormattingElements[i]) {
+ for (PRInt32 i = listLen - 1; i >= 0; i--) {
+ if (!listCopy[i] && !listOfActiveFormattingElements[i]) {
+ continue;
+ } else if (!listCopy[i] || !listOfActiveFormattingElements[i]) {
+ return PR_FALSE;
+ }
+ if (listCopy[i]->node != listOfActiveFormattingElements[i]->node) {
return PR_FALSE;
}
}
- for (PRInt32 i = listCopy.length - 1; i >= 0; i--) {
- if (listCopy[i] != listOfActiveFormattingElements[i]) {
+ for (PRInt32 i = stackLen - 1; i >= 0; i--) {
+ if (stackCopy[i]->node != stack[i]->node) {
return PR_FALSE;
}
}
return PR_TRUE;
}
+void
+nsHtml5TreeBuilder::loadState(nsAHtml5TreeBuilderState* snapshot, nsHtml5AtomTable* interner)
+{
+ jArray stackCopy = snapshot->getStack();
+ PRInt32 stackLen = snapshot->getStackLength();
+ jArray listCopy = snapshot->getListOfActiveFormattingElements();
+ PRInt32 listLen = snapshot->getListLength();
+ for (PRInt32 i = 0; i <= listPtr; i++) {
+ if (!!listOfActiveFormattingElements[i]) {
+ listOfActiveFormattingElements[i]->release();
+ }
+ }
+ if (listOfActiveFormattingElements.length < listLen) {
+ listOfActiveFormattingElements.release();
+ listOfActiveFormattingElements = jArray(listLen);
+ }
+ listPtr = listLen - 1;
+ for (PRInt32 i = 0; i <= currentPtr; i++) {
+ stack[i]->release();
+ }
+ if (stack.length < stackLen) {
+ stack.release();
+ stack = jArray(stackLen);
+ }
+ currentPtr = stackLen - 1;
+ for (PRInt32 i = 0; i < listLen; i++) {
+ nsHtml5StackNode* node = listCopy[i];
+ if (!!node) {
+ nsHtml5StackNode* newNode = new nsHtml5StackNode(node->group, node->ns, nsHtml5Portability::newLocalFromLocal(node->name, interner), node->node, node->scoping, node->special, node->fosterParenting, nsHtml5Portability::newLocalFromLocal(node->popName, interner), node->attributes->cloneAttributes(nsnull));
+ listOfActiveFormattingElements[i] = newNode;
+ } else {
+ listOfActiveFormattingElements[i] = nsnull;
+ }
+ }
+ for (PRInt32 i = 0; i < stackLen; i++) {
+ nsHtml5StackNode* node = stackCopy[i];
+ PRInt32 listIndex = findInArray(node, listCopy);
+ if (listIndex == -1) {
+ nsHtml5StackNode* newNode = new nsHtml5StackNode(node->group, node->ns, nsHtml5Portability::newLocalFromLocal(node->name, interner), node->node, node->scoping, node->special, node->fosterParenting, nsHtml5Portability::newLocalFromLocal(node->popName, interner), nsnull);
+ stack[i] = newNode;
+ } else {
+ stack[i] = listOfActiveFormattingElements[listIndex];
+ stack[i]->retain();
+ }
+ }
+ ;
+ formPointer = snapshot->getFormPointer();
+ ;
+ ;
+ headPointer = snapshot->getHeadPointer();
+ ;
+ mode = snapshot->getMode();
+ originalMode = snapshot->getOriginalMode();
+ foreignFlag = snapshot->getForeignFlag();
+ needToDropLF = snapshot->isNeedToDropLF();
+ quirks = snapshot->isQuirks();
+}
+
+PRInt32
+nsHtml5TreeBuilder::findInArray(nsHtml5StackNode* node, jArray arr)
+{
+ for (PRInt32 i = listPtr; i >= 0; i--) {
+ if (node == arr[i]) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+nsIContent**
+nsHtml5TreeBuilder::getFormPointer()
+{
+ return formPointer;
+}
+
+nsIContent**
+nsHtml5TreeBuilder::getHeadPointer()
+{
+ return headPointer;
+}
+
+jArray
+nsHtml5TreeBuilder::getListOfActiveFormattingElements()
+{
+ return listOfActiveFormattingElements;
+}
+
+jArray
+nsHtml5TreeBuilder::getStack()
+{
+ return stack;
+}
+
+PRInt32
+nsHtml5TreeBuilder::getMode()
+{
+ return mode;
+}
+
+PRInt32
+nsHtml5TreeBuilder::getOriginalMode()
+{
+ return originalMode;
+}
+
+PRInt32
+nsHtml5TreeBuilder::getForeignFlag()
+{
+ return foreignFlag;
+}
+
+PRBool
+nsHtml5TreeBuilder::isNeedToDropLF()
+{
+ return needToDropLF;
+}
+
+PRBool
+nsHtml5TreeBuilder::isQuirks()
+{
+ return quirks;
+}
+
+PRInt32
+nsHtml5TreeBuilder::getListLength()
+{
+ return listPtr + 1;
+}
+
+PRInt32
+nsHtml5TreeBuilder::getStackLength()
+{
+ return currentPtr + 1;
+}
+
void
nsHtml5TreeBuilder::initializeStatics()
{
diff --git a/parser/html/nsHtml5TreeBuilder.h b/parser/html/nsHtml5TreeBuilder.h
index cda5bc99af9..4799d346b20 100644
--- a/parser/html/nsHtml5TreeBuilder.h
+++ b/parser/html/nsHtml5TreeBuilder.h
@@ -33,6 +33,7 @@
#include "prtypes.h"
#include "nsIAtom.h"
+#include "nsHtml5AtomTable.h"
#include "nsITimer.h"
#include "nsString.h"
#include "nsINameSpaceManager.h"
@@ -52,8 +53,10 @@
#include "nsHtml5StackNode.h"
#include "nsHtml5TreeOpExecutor.h"
#include "nsHtml5StreamParser.h"
+#include "nsAHtml5TreeBuilderState.h"
class nsHtml5StreamParser;
+class nsHtml5SpeculativeLoader;
class nsHtml5Tokenizer;
class nsHtml5MetaScanner;
@@ -65,7 +68,7 @@ class nsHtml5StateSnapshot;
class nsHtml5Portability;
-class nsHtml5TreeBuilder
+class nsHtml5TreeBuilder : public nsAHtml5TreeBuilderState
{
private:
static jArray ISINDEX_PROMPT;
@@ -205,12 +208,27 @@ class nsHtml5TreeBuilder
PRBool isScriptingEnabled();
void setScriptingEnabled(PRBool scriptingEnabled);
PRBool inForeign();
- private:
void flushCharacters();
+ private:
PRBool charBufferContainsNonWhitespace();
public:
- nsHtml5StateSnapshot* newSnapshot();
- PRBool snapshotMatches(nsHtml5StateSnapshot* snapshot);
+ nsAHtml5TreeBuilderState* newSnapshot();
+ PRBool snapshotMatches(nsAHtml5TreeBuilderState* snapshot);
+ void loadState(nsAHtml5TreeBuilderState* snapshot, nsHtml5AtomTable* interner);
+ private:
+ PRInt32 findInArray(nsHtml5StackNode* node, jArray arr);
+ public:
+ nsIContent** getFormPointer();
+ nsIContent** getHeadPointer();
+ jArray getListOfActiveFormattingElements();
+ jArray getStack();
+ PRInt32 getMode();
+ PRInt32 getOriginalMode();
+ PRInt32 getForeignFlag();
+ PRBool isNeedToDropLF();
+ PRBool isQuirks();
+ PRInt32 getListLength();
+ PRInt32 getStackLength();
static void initializeStatics();
static void releaseStatics();
diff --git a/parser/html/nsHtml5TreeBuilderCppSupplement.h b/parser/html/nsHtml5TreeBuilderCppSupplement.h
index afae73e6017..1c90d1d9bbe 100644
--- a/parser/html/nsHtml5TreeBuilderCppSupplement.h
+++ b/parser/html/nsHtml5TreeBuilderCppSupplement.h
@@ -49,13 +49,13 @@
// this really should be autogenerated...
jArray nsHtml5TreeBuilder::ISINDEX_PROMPT = jArray();
-nsHtml5TreeBuilder::nsHtml5TreeBuilder(nsHtml5TreeOpExecutor* aExec)
+nsHtml5TreeBuilder::nsHtml5TreeBuilder(nsAHtml5TreeOpSink* aOpSink)
: scriptingEnabled(PR_FALSE)
, fragment(PR_FALSE)
, contextNode(nsnull)
, formPointer(nsnull)
, headPointer(nsnull)
- , mExecutor(aExec)
+ , mOpSink(aOpSink)
, mHandles(new nsIContent*[NS_HTML5_TREE_BUILDER_HANDLE_ARRAY_LENGTH])
, mHandlesUsed(0)
#ifdef DEBUG
@@ -117,7 +117,7 @@ nsHtml5TreeBuilder::appendElement(nsIContent** aChild, nsIContent** aParent)
{
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
// XXX if null, OOM!
- treeOp->Init(aChild, aParent);
+ treeOp->Init(eTreeOpAppend, aChild, aParent);
}
void
@@ -167,7 +167,7 @@ nsHtml5TreeBuilder::appendCharacters(nsIContent** aParent, PRUnichar* aBuffer, P
treeOp = mOpQueue.AppendElement();
// XXX if null, OOM!
- treeOp->Init(text, aParent);
+ treeOp->Init(eTreeOpAppend, text, aParent);
}
void
@@ -184,7 +184,7 @@ nsHtml5TreeBuilder::appendComment(nsIContent** aParent, PRUnichar* aBuffer, PRIn
treeOp = mOpQueue.AppendElement();
// XXX if null, OOM!
- treeOp->Init(comment, aParent);
+ treeOp->Init(eTreeOpAppend, comment, aParent);
}
void
@@ -227,15 +227,6 @@ void
nsHtml5TreeBuilder::start(PRBool fragment)
{
// XXX check that timer creation didn't fail in constructor
- if (!fragment) {
- /*
- * If you move the following line, be very careful not to cause
- * WillBuildModel to be called before the document has had its
- * script global object set.
- */
- mExecutor->WillBuildModel(eDTDMode_unknown);
- }
- mExecutor->Start();
#ifdef DEBUG
mActive = PR_TRUE;
#endif
@@ -244,7 +235,6 @@ nsHtml5TreeBuilder::start(PRBool fragment)
void
nsHtml5TreeBuilder::end()
{
- mExecutor->End();
mOpQueue.Clear();
#ifdef DEBUG
mActive = PR_FALSE;
@@ -286,7 +276,6 @@ nsHtml5TreeBuilder::elementPushed(PRInt32 aNamespace, nsIAtom* aName, nsIContent
return;
}
}
- mExecutor->MaybeSuspend();
}
void
@@ -295,7 +284,6 @@ nsHtml5TreeBuilder::elementPopped(PRInt32 aNamespace, nsIAtom* aName, nsIContent
NS_ASSERTION(aNamespace == kNameSpaceID_XHTML || aNamespace == kNameSpaceID_SVG || aNamespace == kNameSpaceID_MathML, "Element isn't HTML, SVG or MathML!");
NS_ASSERTION(aName, "Element doesn't have local name!");
NS_ASSERTION(aElement, "No element!");
- mExecutor->MaybeSuspend();
if (aNamespace == kNameSpaceID_MathML) {
return;
}
@@ -304,7 +292,7 @@ nsHtml5TreeBuilder::elementPopped(PRInt32 aNamespace, nsIAtom* aName, nsIContent
requestSuspension();
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
// XXX if null, OOM!
- treeOp->Init(eTreeOpRunScript, aElement);
+ treeOp->Init(eTreeOpRunScript, aElement, nsnull);
return;
}
if (aName == nsHtml5Atoms::title) {
@@ -337,6 +325,7 @@ nsHtml5TreeBuilder::elementPopped(PRInt32 aNamespace, nsIAtom* aName, nsIContent
nsEventDispatcher::Dispatch(aElement, ctx, &event);
}
#endif
+ // TODO soft flush the op queue every now and then
return;
}
// we now have only HTML
@@ -414,6 +403,56 @@ nsHtml5TreeBuilder::HasScript()
return mOpQueue.ElementAt(len - 1).IsRunScript();
}
+void
+nsHtml5TreeBuilder::Flush()
+{
+ mOpSink->ForcedFlush(mOpQueue);
+}
+
+void
+nsHtml5TreeBuilder::MaybeFlush()
+{
+ mOpSink->MaybeFlush(mOpQueue);
+}
+
+void
+nsHtml5TreeBuilder::SetDocumentCharset(nsACString& aCharset)
+{
+ nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
+ // XXX if null, OOM!
+ treeOp->Init(eTreeOpSetDocumentCharset, aCharset);
+}
+
+void
+nsHtml5TreeBuilder::StreamEnded()
+{
+ // The fragement mode calls DidBuildModel from nsHtml5Parser.
+ // Letting DidBuildModel be called from the executor in the fragment case
+ // confuses the EndLoad logic of nsHTMLDocument, since nsHTMLDocument
+ // thinks it is dealing with document.written content as opposed to
+ // innerHTML content.
+ if (!fragment) {
+ nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
+ // XXX if null, OOM!
+ treeOp->Init(eTreeOpStreamEnded);
+ }
+}
+
+void
+nsHtml5TreeBuilder::NeedsCharsetSwitchTo(const nsACString& aCharset)
+{
+ nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
+ // XXX if null, OOM!
+ treeOp->Init(eTreeOpNeedsCharsetSwitchTo, aCharset);
+}
+
+void
+nsHtml5TreeBuilder::AddSnapshotToScript(nsAHtml5TreeBuilderState* aSnapshot)
+{
+ NS_PRECONDITION(HasScript(), "No script to add a snapshot to!");
+ mOpQueue.ElementAt(mOpQueue.Length() - 1).SetSnapshot(aSnapshot);
+}
+
// DocumentModeHandler
void
nsHtml5TreeBuilder::documentMode(nsHtml5DocumentMode m)
diff --git a/parser/html/nsHtml5TreeBuilderHSupplement.h b/parser/html/nsHtml5TreeBuilderHSupplement.h
index dba1c85ffc1..8df730fd924 100644
--- a/parser/html/nsHtml5TreeBuilderHSupplement.h
+++ b/parser/html/nsHtml5TreeBuilderHSupplement.h
@@ -40,7 +40,7 @@
private:
nsTArray mOpQueue;
- nsHtml5TreeOpExecutor* mExecutor;
+ nsAHtml5TreeOpSink* mOpSink;
nsAutoArrayPtr mHandles;
PRInt32 mHandlesUsed;
nsTArray > mOldHandles;
@@ -58,21 +58,24 @@
public:
- nsHtml5TreeBuilder(nsHtml5TreeOpExecutor* aExec);
+ nsHtml5TreeBuilder(nsAHtml5TreeOpSink* aOpSink);
~nsHtml5TreeBuilder();
-
- inline PRUint32 GetOpQueueLength() {
- return mOpQueue.Length();
- }
-
- inline void SwapQueue(nsTArray& aOtherQueue) {
- mOpQueue.SwapElements(aOtherQueue);
- }
-
- inline void ReqSuspension() {
- requestSuspension();
- }
PRBool HasScript();
+ void SetOpSink(nsAHtml5TreeOpSink* aOpSink) {
+ mOpSink = aOpSink;
+ }
+
+ void Flush();
+
+ void MaybeFlush();
+
+ void SetDocumentCharset(nsACString& aCharset);
+
+ void StreamEnded();
+
+ void NeedsCharsetSwitchTo(const nsACString& aEncoding);
+
+ void AddSnapshotToScript(nsAHtml5TreeBuilderState* aSnapshot);
diff --git a/parser/html/nsHtml5TreeOpExecutor.cpp b/parser/html/nsHtml5TreeOpExecutor.cpp
index 6eca15a5e96..701b70b1a51 100644
--- a/parser/html/nsHtml5TreeOpExecutor.cpp
+++ b/parser/html/nsHtml5TreeOpExecutor.cpp
@@ -91,13 +91,10 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsHtml5TreeOpExecutor, nsContent
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
nsHtml5TreeOpExecutor::nsHtml5TreeOpExecutor()
- : mSuppressEOF(PR_FALSE)
- , mHasProcessedBase(PR_FALSE)
- , mNeedsFlush(PR_FALSE)
+ : mHasProcessedBase(PR_FALSE)
+ , mReadingFromStage(PR_FALSE)
, mFlushTimer(do_CreateInstance("@mozilla.org/timer;1"))
- , mNeedsCharsetSwitch(PR_FALSE)
{
-
}
nsHtml5TreeOpExecutor::~nsHtml5TreeOpExecutor()
@@ -106,12 +103,13 @@ nsHtml5TreeOpExecutor::~nsHtml5TreeOpExecutor()
if (mFlushTimer) {
mFlushTimer->Cancel(); // XXX why is this even necessary? it is, though.
}
+ mFlushTimer = nsnull;
}
static void
TimerCallbackFunc(nsITimer* aTimer, void* aClosure)
{
- (static_cast (aClosure))->DeferredTimerFlush();
+ (static_cast (aClosure))->Flush();
}
// nsIContentSink
@@ -126,13 +124,9 @@ nsHtml5TreeOpExecutor::WillParse()
NS_IMETHODIMP
nsHtml5TreeOpExecutor::DidBuildModel(PRBool aTerminated)
{
- NS_ASSERTION(mLifeCycle == STREAM_ENDING, "Bad life cycle.");
+ NS_PRECONDITION(mLifeCycle == PARSING,
+ "Bad life cycle.");
mLifeCycle = TERMINATED;
- if (!mSuppressEOF) {
- GetTokenizer()->eof();
- Flush();
- }
- GetTokenizer()->end();
// This is comes from nsXMLContentSink
DidBuildModelImpl(aTerminated);
mDocument->ScriptLoader()->RemoveObserver(this);
@@ -272,42 +266,73 @@ nsHtml5TreeOpExecutor::UpdateStyleSheet(nsIContent* aElement)
void
nsHtml5TreeOpExecutor::Flush()
{
- mNeedsFlush = PR_FALSE;
- FillQueue();
+ nsRefPtr kungFuDeathGrip(this); // avoid crashing near EOF
+ nsCOMPtr parserKungFuDeathGrip(mParser);
+
+ if (mLifeCycle == TERMINATED) {
+ mFlushTimer->Cancel();
+ return;
+ }
+
+ NS_ASSERTION(mParser, "mParser was nulled out but life cycle wasn't terminated.");
+
+ if (mReadingFromStage) {
+ mStage.RetrieveOperations(mOpQueue);
+ }
- MOZ_AUTO_DOC_UPDATE(GetDocument(), UPDATE_CONTENT_MODEL, PR_TRUE);
- PRIntervalTime flushStart = 0;
- PRUint32 opQueueLength = mOpQueue.Length();
- if (opQueueLength > NS_HTML5_TREE_OP_EXECUTOR_MIN_QUEUE_LENGTH) { // avoid computing averages with too few ops
- flushStart = PR_IntervalNow();
- }
- mElementsSeenInThisAppendBatch.SetCapacity(opQueueLength * 2);
- // XXX alloc failure
- const nsHtml5TreeOperation* start = mOpQueue.Elements();
- const nsHtml5TreeOperation* end = start + opQueueLength;
- for (nsHtml5TreeOperation* iter = (nsHtml5TreeOperation*)start; iter < end; ++iter) {
- iter->Perform(this);
- }
- FlushPendingAppendNotifications();
-#ifdef DEBUG_hsivonen
- if (mOpQueue.Length() > sInsertionBatchMaxLength) {
- sInsertionBatchMaxLength = opQueueLength;
- }
-#endif
- mOpQueue.Clear();
- if (flushStart) {
- PRUint32 delta = PR_IntervalToMilliseconds(PR_IntervalNow() - flushStart);
- sTreeOpQueueMaxLength = delta ?
- (PRUint32)((NS_HTML5_TREE_OP_EXECUTOR_MAX_QUEUE_TIME * (PRUint64)opQueueLength) / delta) :
- 0;
- if (sTreeOpQueueMaxLength < NS_HTML5_TREE_OP_EXECUTOR_MIN_QUEUE_LENGTH) {
- sTreeOpQueueMaxLength = NS_HTML5_TREE_OP_EXECUTOR_MIN_QUEUE_LENGTH;
+ { // scope for the auto update so that it ends before we try to run the
+ // script
+ MOZ_AUTO_DOC_UPDATE(GetDocument(), UPDATE_CONTENT_MODEL, PR_TRUE);
+ PRIntervalTime flushStart = 0;
+ PRUint32 opQueueLength = mOpQueue.Length();
+ if (opQueueLength > NS_HTML5_TREE_OP_EXECUTOR_MIN_QUEUE_LENGTH) { // avoid computing averages with too few ops
+ flushStart = PR_IntervalNow();
}
+ mElementsSeenInThisAppendBatch.SetCapacity(opQueueLength * 2);
+ // XXX alloc failure
+ const nsHtml5TreeOperation* start = mOpQueue.Elements();
+ const nsHtml5TreeOperation* end = start + opQueueLength;
+ for (nsHtml5TreeOperation* iter = (nsHtml5TreeOperation*)start; iter < end; ++iter) {
+ iter->Perform(this);
+ }
+ FlushPendingAppendNotifications();
#ifdef DEBUG_hsivonen
- printf("QUEUE MAX LENGTH: %d\n", sTreeOpQueueMaxLength);
+ if (mOpQueue.Length() > sInsertionBatchMaxLength) {
+ sInsertionBatchMaxLength = opQueueLength;
+ }
#endif
+ mOpQueue.Clear();
+ if (flushStart) {
+ PRUint32 delta = PR_IntervalToMilliseconds(PR_IntervalNow() - flushStart);
+ sTreeOpQueueMaxLength = delta ?
+ (PRUint32)((NS_HTML5_TREE_OP_EXECUTOR_MAX_QUEUE_TIME * (PRUint64)opQueueLength) / delta) :
+ 0;
+ if (sTreeOpQueueMaxLength < NS_HTML5_TREE_OP_EXECUTOR_MIN_QUEUE_LENGTH) {
+ sTreeOpQueueMaxLength = NS_HTML5_TREE_OP_EXECUTOR_MIN_QUEUE_LENGTH;
+ }
+#ifdef DEBUG_hsivonen
+ printf("QUEUE MAX LENGTH: %d\n", sTreeOpQueueMaxLength);
+#endif
+ }
}
- mFlushTimer->InitWithFuncCallback(TimerCallbackFunc, static_cast (this), NS_HTML5_TREE_OP_EXECUTOR_MAX_TIME_WITHOUT_FLUSH, nsITimer::TYPE_ONE_SHOT);
+ ScheduleTimer();
+ if (mScriptElement) {
+ NS_ASSERTION(!mCallDidBuildModel, "Had a script element and DidBuildModel call");
+ ExecuteScript();
+ } else if (mCallDidBuildModel) {
+ mCallDidBuildModel = PR_FALSE;
+ DidBuildModel(PR_FALSE);
+ }
+}
+
+void
+nsHtml5TreeOpExecutor::ScheduleTimer()
+{
+ mFlushTimer->Cancel();
+ mFlushTimer->InitWithFuncCallback(TimerCallbackFunc,
+ static_cast (this),
+ NS_HTML5_TREE_OP_EXECUTOR_MAX_TIME_WITHOUT_FLUSH,
+ nsITimer::TYPE_ONE_SHOT);
}
nsresult
@@ -382,55 +407,27 @@ nsHtml5TreeOpExecutor::DocumentMode(nsHtml5DocumentMode m)
htmlDocument->SetCompatibilityMode(mode);
}
-nsresult
-nsHtml5TreeOpExecutor::MaybePerformCharsetSwitch()
-{
- if (!mNeedsCharsetSwitch) {
- return NS_ERROR_HTMLPARSER_CONTINUE;
- }
- // this code comes from nsObserverBase.cpp
- nsresult rv = NS_OK;
- nsCOMPtr wss = do_QueryInterface(mDocShell);
- if (!wss) {
- return NS_ERROR_HTMLPARSER_CONTINUE;
- }
-#ifndef DONT_INFORM_WEBSHELL
- // ask the webshellservice to load the URL
- if (NS_FAILED(rv = wss->SetRendering(PR_FALSE))) {
- // do nothing and fall thru
- } else if (NS_FAILED(rv = wss->StopDocumentLoad())) {
- rv = wss->SetRendering(PR_TRUE); // turn on the rendering so at least we will see something.
- } else if (NS_FAILED(rv = wss->ReloadDocument(mPendingCharset.get(), kCharsetFromMetaTag))) {
- rv = wss->SetRendering(PR_TRUE); // turn on the rendering so at least we will see something.
- } else {
- rv = NS_ERROR_HTMLPARSER_STOPPARSING; // We're reloading a new document...stop loading the current.
- }
-#endif
- // if our reload request is not accepted, we should tell parser to go on
- if (rv != NS_ERROR_HTMLPARSER_STOPPARSING)
- mNeedsCharsetSwitch = PR_FALSE;
- rv = NS_ERROR_HTMLPARSER_CONTINUE;
- return rv;
-}
-
/**
- * This method executes a script element set by nsHtml5TreeBuilder. The reason
- * why this code is here and not in the tree builder is to allow the control
- * to return from the tokenizer before scripts run. This way, the tokenizer
- * is not invoked re-entrantly although the parser is.
+ * The reason why this code is here and not in the tree builder even in the
+ * main-thread case is to allow the control to return from the tokenizer
+ * before scripts run. This way, the tokenizer is not invoked re-entrantly
+ * although the parser is.
*/
void
nsHtml5TreeOpExecutor::ExecuteScript()
{
- NS_PRECONDITION(mScriptElement, "Trying to run a script without having one!");
- Flush();
-#ifdef GATHER_DOCWRITE_STATISTICS
- if (!mSnapshot) {
- mSnapshot = mTreeBuilder->newSnapshot();
- }
-#endif
+ mReadingFromStage = PR_FALSE;
+ NS_ASSERTION(mScriptElement, "No script to run");
+
nsCOMPtr sele = do_QueryInterface(mScriptElement);
- // Notify our document that we're loading this script.
+ if (mLifeCycle == TERMINATED) {
+ NS_ASSERTION(sele->IsMalformed(), "Script wasn't marked as malformed.");
+ // We got here not because of an end tag but because the tree builder
+ // popped an incomplete script element on EOF. Returning here to avoid
+ // calling back into mParser anymore. mParser has been nulled out by now.
+ return;
+ }
+ // Notify our document that we're loading this script.
nsCOMPtr htmlDocument = do_QueryInterface(mDocument);
NS_ASSERTION(htmlDocument, "Document didn't QI into HTML document.");
htmlDocument->ScriptLoading(sele);
@@ -439,17 +436,18 @@ nsHtml5TreeOpExecutor::ExecuteScript()
// or return NS_ERROR_HTMLPARSER_BLOCK. Or neither if the script doesn't
// need executing.
nsresult rv = mScriptElement->DoneAddingChildren(PR_TRUE);
+ mScriptElement = nsnull;
// If the act of insertion evaluated the script, we're fine.
// Else, block the parser till the script has loaded.
if (rv == NS_ERROR_HTMLPARSER_BLOCK) {
mScriptElements.AppendObject(sele);
mParser->BlockParser();
- } else {
+ } else if (mLifeCycle != TERMINATED) {
// This may have already happened if the script executed, but in case
// it didn't then remove the element so that it doesn't get stuck forever.
htmlDocument->ScriptExecuted(sele);
+ static_cast (mParser.get())->MaybePostContinueEvent();
}
- mScriptElement = nsnull;
}
nsresult
@@ -460,30 +458,40 @@ nsHtml5TreeOpExecutor::Init(nsIDocument* aDoc,
{
nsresult rv = nsContentSink::Init(aDoc, aURI, aContainer, aChannel);
NS_ENSURE_SUCCESS(rv, rv);
- aDoc->AddObserver(this);
+ mCanInterruptParser = PR_FALSE; // without this, nsContentSink calls
+ // UnblockOnload from DropParserAndPerfHint
return rv;
}
void
nsHtml5TreeOpExecutor::Start()
{
- mNeedsFlush = PR_FALSE;
- mNeedsCharsetSwitch = PR_FALSE;
- mPendingCharset.Truncate();
+ NS_PRECONDITION(mLifeCycle == NOT_STARTED, "Tried to start when already started.");
+ mLifeCycle = PARSING;
mScriptElement = nsnull;
+ ScheduleTimer();
}
void
-nsHtml5TreeOpExecutor::End()
+nsHtml5TreeOpExecutor::NeedsCharsetSwitchTo(const char* aEncoding)
{
- mFlushTimer->Cancel();
-}
-
-void
-nsHtml5TreeOpExecutor::NeedsCharsetSwitchTo(const nsACString& aEncoding)
-{
- mNeedsCharsetSwitch = PR_TRUE;
- mPendingCharset.Assign(aEncoding);
+ nsresult rv = NS_OK;
+ nsCOMPtr wss = do_QueryInterface(mDocShell);
+ if (!wss) {
+ return;
+ }
+#ifndef DONT_INFORM_WEBSHELL
+ // ask the webshellservice to load the URL
+ if (NS_FAILED(rv = wss->SetRendering(PR_FALSE))) {
+ // do nothing and fall thru
+ } else if (NS_FAILED(rv = wss->StopDocumentLoad())) {
+ rv = wss->SetRendering(PR_TRUE); // turn on the rendering so at least we will see something.
+ } else if (NS_FAILED(rv = wss->ReloadDocument(aEncoding, kCharsetFromMetaTag))) {
+ rv = wss->SetRendering(PR_TRUE); // turn on the rendering so at least we will see something.
+ }
+ // if the charset switch was accepted, wss has called Terminate() on the
+ // parser by now
+#endif
}
nsHtml5Tokenizer*
@@ -492,54 +500,42 @@ nsHtml5TreeOpExecutor::GetTokenizer()
return (static_cast (mParser.get()))->GetTokenizer();
}
-void
-nsHtml5TreeOpExecutor::MaybeSuspend() {
- if (!mNeedsFlush) {
- mNeedsFlush = !!(mTreeBuilder->GetOpQueueLength() >= sTreeOpQueueMaxLength);
- }
- if (DidProcessATokenImpl() == NS_ERROR_HTMLPARSER_INTERRUPTED || mNeedsFlush) {
- // We've been in the parser for too long and/or the op queue is becoming too
- // long to flush in one go it it grows further.
- static_cast(mParser.get())->Suspend();
- mTreeBuilder->ReqSuspension();
- }
-}
-
-void
-nsHtml5TreeOpExecutor::MaybeExecuteScript() {
- if (!mTreeBuilder->HasScript()) {
- return;
- }
- Flush(); // Let the doc update end before we start executing the script
- NS_ASSERTION(mScriptElement, "No script to run");
- ExecuteScript();
- if (mStreamParser) {
- mStreamParser->Suspend();
- }
-}
-
-void
-nsHtml5TreeOpExecutor::DeferredTimerFlush() {
- if (mTreeBuilder->GetOpQueueLength() > 0) {
- mNeedsFlush = PR_TRUE;
- }
-}
-
-void
-nsHtml5TreeOpExecutor::FillQueue() {
- mTreeBuilder->SwapQueue(mOpQueue);
-}
-
void
nsHtml5TreeOpExecutor::Reset() {
- mSuppressEOF = PR_FALSE;
mHasProcessedBase = PR_FALSE;
- mNeedsFlush = PR_FALSE;
+ mReadingFromStage = PR_FALSE;
mOpQueue.Clear();
- mPendingCharset.Truncate();
- mNeedsCharsetSwitch = PR_FALSE;
mLifeCycle = NOT_STARTED;
mScriptElement = nsnull;
+ mCallDidBuildModel = PR_FALSE;
+}
+
+void
+nsHtml5TreeOpExecutor::MaybeFlush(nsTArray& aOpQueue)
+{
+ // no-op
+}
+
+void
+nsHtml5TreeOpExecutor::ForcedFlush(nsTArray& aOpQueue)
+{
+ if (mOpQueue.IsEmpty()) {
+ mOpQueue.SwapElements(aOpQueue);
+ return;
+ }
+ mOpQueue.MoveElementsFrom(aOpQueue);
+}
+
+void
+nsHtml5TreeOpExecutor::InitializeDocWriteParserState(nsAHtml5TreeBuilderState* aState)
+{
+ static_cast (mParser.get())->InitializeDocWriteParserState(aState);
+}
+
+void
+nsHtml5TreeOpExecutor::StreamEnded()
+{
+ mCallDidBuildModel = PR_TRUE;
}
PRUint32 nsHtml5TreeOpExecutor::sTreeOpQueueMaxLength = NS_HTML5_TREE_OP_EXECUTOR_DEFAULT_QUEUE_LENGTH;
diff --git a/parser/html/nsHtml5TreeOpExecutor.h b/parser/html/nsHtml5TreeOpExecutor.h
index 09c08c6d2e5..e03d2a3dc62 100644
--- a/parser/html/nsHtml5TreeOpExecutor.h
+++ b/parser/html/nsHtml5TreeOpExecutor.h
@@ -54,6 +54,8 @@
#include "nsIScriptElement.h"
#include "nsIParser.h"
#include "nsCOMArray.h"
+#include "nsAHtml5TreeOpSink.h"
+#include "nsHtml5TreeOpStage.h"
class nsHtml5TreeBuilder;
class nsHtml5Tokenizer;
@@ -66,26 +68,22 @@ enum eHtml5ParserLifecycle {
NOT_STARTED = 0,
/**
- * The parser has started the tokenizer and the stream hasn't ended yet.
+ * The parser has started the tokenizer and as far as the executor is
+ * aware, the stream hasn't ended.
*/
PARSING = 1,
/**
- * The parser hasn't told the tokenizer to emit EOF yet, but the network
- * stream has been exhausted or document.close() called.
+ * The parse has ended.
*/
- STREAM_ENDING = 2,
-
- /**
- * The parser has told the tokenizer to emit EOF.
- */
- TERMINATED = 3
+ TERMINATED = 2
};
typedef nsIContent* nsIContentPtr;
class nsHtml5TreeOpExecutor : public nsIContentSink,
- public nsContentSink
+ public nsContentSink,
+ public nsAHtml5TreeOpSink
{
public:
NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
@@ -107,7 +105,7 @@ class nsHtml5TreeOpExecutor : public nsIContentSink,
PRBool mSuppressEOF;
PRBool mHasProcessedBase;
- PRBool mNeedsFlush;
+ PRBool mReadingFromStage;
nsCOMPtr mFlushTimer;
nsTArray mOpQueue;
nsTArray mElementsSeenInThisAppendBatch;
@@ -119,16 +117,6 @@ class nsHtml5TreeOpExecutor : public nsIContentSink,
// non-elements wouldn't use the handle setup but the text node / comment
// / doctype operand would be remembered by the tree op executor.
nsCOMArray mOwnedNonElements;
-
- /**
- * The character encoding to which to switch in a late renavigation
- */
- nsCString mPendingCharset;
-
- /**
- * Call to PerformCharsetSwitch() needed
- */
- PRBool mNeedsCharsetSwitch;
/**
* The current point on parser life cycle
@@ -140,7 +128,12 @@ class nsHtml5TreeOpExecutor : public nsIContentSink,
*/
nsCOMPtr mScriptElement;
- nsHtml5TreeBuilder* mTreeBuilder;
+ nsHtml5TreeOpStage mStage;
+
+ /**
+ * Used for deferring DidBuildModel call out of notification batch
+ */
+ PRBool mCallDidBuildModel;
public:
@@ -160,6 +153,7 @@ class nsHtml5TreeOpExecutor : public nsIContentSink,
NS_IMETHOD WillBuildModel(nsDTDMode aDTDMode) {
NS_ASSERTION(GetDocument()->GetScriptGlobalObject(),
"Script global object not ready");
+ mDocument->AddObserver(this);
WillBuildModelImpl();
GetDocument()->BeginLoad();
return NS_OK;
@@ -225,14 +219,6 @@ class nsHtml5TreeOpExecutor : public nsIContentSink,
return IsScriptExecutingImpl();
}
- void AllowInterrupts() {
- mCanInterruptParser = PR_TRUE;
- }
-
- void ProhibitInterrupts() {
- mCanInterruptParser = PR_FALSE;
- }
-
void SetBaseUriFromDocument() {
mDocumentBaseURI = mDocument->GetBaseURI();
mHasProcessedBase = PR_TRUE;
@@ -246,20 +232,12 @@ class nsHtml5TreeOpExecutor : public nsIContentSink,
mStreamParser = aStreamParser;
}
- /**
- * Renavigates to the document with a different charset
- */
- nsresult MaybePerformCharsetSwitch();
-
inline void SetScriptElement(nsIContent* aScript) {
mScriptElement = aScript;
}
-
- /**
- * Runs mScriptElement
- */
- void ExecuteScript();
+ void InitializeDocWriteParserState(nsAHtml5TreeBuilderState* aState);
+
PRBool IsScriptEnabled();
void PostPendingAppendNotification(nsIContent* aParent, nsIContent* aChild) {
@@ -337,29 +315,11 @@ class nsHtml5TreeOpExecutor : public nsIContentSink,
void MaybeSuspend();
- void MaybeFlush() {
- if (mNeedsFlush) {
- Flush();
- }
- }
-
- void DeferredTimerFlush();
-
void Start();
- void End();
-
- void NeedsCharsetSwitchTo(const nsACString& aEncoding);
-
- void IgnoreCharsetSwitch() {
- mNeedsCharsetSwitch = PR_FALSE;
- }
+ void NeedsCharsetSwitchTo(const char* aEncoding);
#ifdef DEBUG
- PRBool NeedsCharsetSwitch() {
- return mNeedsCharsetSwitch;
- }
-
PRBool HasScriptElement() {
return !!mScriptElement;
}
@@ -377,7 +337,7 @@ class nsHtml5TreeOpExecutor : public nsIContentSink,
mLifeCycle = aLifeCycle;
}
- void MaybeExecuteScript();
+ void ExecuteScript();
void MaybePreventExecution() {
if (mScriptElement) {
@@ -387,10 +347,6 @@ class nsHtml5TreeOpExecutor : public nsIContentSink,
mScriptElement = nsnull;
}
}
-
- void SetTreeBuilder(nsHtml5TreeBuilder* aBuilder) {
- mTreeBuilder = aBuilder;
- }
void Reset();
@@ -402,12 +358,35 @@ class nsHtml5TreeOpExecutor : public nsIContentSink,
mOwnedNonElements.AppendObject(aContent);
}
+ // The following two methods are for the main-thread case
+
+ /**
+ * No-op
+ */
+ virtual void MaybeFlush(nsTArray& aOpQueue);
+
+ /**
+ * Flush the operations from the tree operations from the argument
+ * queue unconditionally.
+ */
+ virtual void ForcedFlush(nsTArray& aOpQueue);
+
+ nsAHtml5TreeOpSink* GetStage() {
+ return &mStage;
+ }
+
+ void StartReadingFromStage() {
+ mReadingFromStage = PR_TRUE;
+ }
+
+ void StreamEnded();
+
+ void ScheduleTimer();
+
private:
nsHtml5Tokenizer* GetTokenizer();
-
- void FillQueue();
-
+
};
#endif // nsHtml5TreeOpExecutor_h__
diff --git a/parser/html/nsHtml5TreeOpStage.cpp b/parser/html/nsHtml5TreeOpStage.cpp
new file mode 100644
index 00000000000..15447443130
--- /dev/null
+++ b/parser/html/nsHtml5TreeOpStage.cpp
@@ -0,0 +1,78 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is HTML Parser C++ Translator code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Henri Sivonen
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsHtml5TreeOpStage.h"
+
+nsHtml5TreeOpStage::nsHtml5TreeOpStage()
+ : mMutex("nsHtml5TreeOpStage mutex")
+{
+}
+
+nsHtml5TreeOpStage::~nsHtml5TreeOpStage()
+{
+}
+
+void
+nsHtml5TreeOpStage::MaybeFlush(nsTArray& aOpQueue)
+{
+ mozilla::MutexAutoLock autoLock(mMutex);
+ if (mOpQueue.IsEmpty()) {
+ mOpQueue.SwapElements(aOpQueue);
+ }
+}
+
+void
+nsHtml5TreeOpStage::ForcedFlush(nsTArray& aOpQueue)
+{
+ mozilla::MutexAutoLock autoLock(mMutex);
+ if (mOpQueue.IsEmpty()) {
+ mOpQueue.SwapElements(aOpQueue);
+ return;
+ }
+ mOpQueue.MoveElementsFrom(aOpQueue);
+}
+
+void
+nsHtml5TreeOpStage::RetrieveOperations(nsTArray& aOpQueue)
+{
+ mozilla::MutexAutoLock autoLock(mMutex);
+ if (aOpQueue.IsEmpty()) {
+ mOpQueue.SwapElements(aOpQueue);
+ return;
+ }
+ aOpQueue.MoveElementsFrom(mOpQueue);
+}
diff --git a/parser/html/nsHtml5TreeOpStage.h b/parser/html/nsHtml5TreeOpStage.h
new file mode 100644
index 00000000000..9ee856ff89b
--- /dev/null
+++ b/parser/html/nsHtml5TreeOpStage.h
@@ -0,0 +1,76 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is HTML Parser C++ Translator code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Henri Sivonen
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nsHtml5TreeOpStage_h___
+#define nsHtml5TreeOpStage_h___
+
+#include "mozilla/Mutex.h"
+#include "nsHtml5TreeOperation.h"
+#include "nsTArray.h"
+#include "nsAHtml5TreeOpSink.h"
+
+class nsHtml5TreeOpStage : public nsAHtml5TreeOpSink {
+ public:
+
+ nsHtml5TreeOpStage();
+
+ ~nsHtml5TreeOpStage();
+
+ /**
+ * Flush the operations from the tree operations from the argument
+ * queue if flushing is not expensive.
+ */
+ virtual void MaybeFlush(nsTArray& aOpQueue);
+
+ /**
+ * Flush the operations from the tree operations from the argument
+ * queue unconditionally.
+ */
+ virtual void ForcedFlush(nsTArray& aOpQueue);
+
+ /**
+ * Retrieve the staged operations into the argument.
+ */
+ void RetrieveOperations(nsTArray& aOpQueue);
+
+ private:
+ nsTArray mOpQueue;
+ mozilla::Mutex mMutex;
+
+};
+
+#endif /* nsHtml5TreeOpStage_h___ */
diff --git a/parser/html/nsHtml5TreeOperation.cpp b/parser/html/nsHtml5TreeOperation.cpp
index 5d5476fa7cd..a331724727c 100644
--- a/parser/html/nsHtml5TreeOperation.cpp
+++ b/parser/html/nsHtml5TreeOperation.cpp
@@ -59,7 +59,9 @@
#include "nsIDOMDocumentType.h"
nsHtml5TreeOperation::nsHtml5TreeOperation()
- : mOpCode(eTreeOpAppend)
+#ifdef DEBUG
+ : mOpCode(eTreeOpUninitialized)
+#endif
{
MOZ_COUNT_CTOR(nsHtml5TreeOperation);
}
@@ -67,6 +69,7 @@ nsHtml5TreeOperation::nsHtml5TreeOperation()
nsHtml5TreeOperation::~nsHtml5TreeOperation()
{
MOZ_COUNT_DTOR(nsHtml5TreeOperation);
+ NS_ASSERTION(mOpCode != eTreeOpUninitialized, "Uninitialized tree op.");
switch(mOpCode) {
case eTreeOpAddAttributes:
delete mTwo.attributes;
@@ -81,6 +84,13 @@ nsHtml5TreeOperation::~nsHtml5TreeOperation()
case eTreeOpCreateComment:
delete[] mTwo.unicharPtr;
break;
+ case eTreeOpSetDocumentCharset:
+ case eTreeOpNeedsCharsetSwitchTo:
+ delete[] mOne.charPtr;
+ break;
+ case eTreeOpRunScript:
+ delete mTwo.state;
+ break;
default: // keep the compiler happy
break;
}
@@ -313,6 +323,10 @@ nsHtml5TreeOperation::Perform(nsHtml5TreeOpExecutor* aBuilder)
}
case eTreeOpRunScript: {
nsIContent* node = *(mOne.node);
+ nsAHtml5TreeBuilderState* snapshot = mTwo.state;
+ if (snapshot) {
+ aBuilder->InitializeDocWriteParserState(snapshot);
+ }
aBuilder->SetScriptElement(node);
return rv;
}
@@ -326,6 +340,17 @@ nsHtml5TreeOperation::Perform(nsHtml5TreeOpExecutor* aBuilder)
node->DoneCreatingElement();
return rv;
}
+ case eTreeOpSetDocumentCharset: {
+ char* str = mOne.charPtr;
+ nsDependentCString dependentString(str);
+ aBuilder->SetDocumentCharset(dependentString);
+ return rv;
+ }
+ case eTreeOpNeedsCharsetSwitchTo: {
+ char* str = mOne.charPtr;
+ aBuilder->NeedsCharsetSwitchTo(str);
+ return rv;
+ }
case eTreeOpUpdateStyleSheet: {
nsIContent* node = *(mOne.node);
aBuilder->UpdateStyleSheet(node);
@@ -355,6 +380,10 @@ nsHtml5TreeOperation::Perform(nsHtml5TreeOpExecutor* aBuilder)
}
return rv;
}
+ case eTreeOpStreamEnded: {
+ aBuilder->StreamEnded();
+ return rv;
+ }
case eTreeOpStartLayout: {
aBuilder->StartLayout(); // this causes a flush anyway
return rv;
diff --git a/parser/html/nsHtml5TreeOperation.h b/parser/html/nsHtml5TreeOperation.h
index 01cb2908e76..3fd17f271ea 100644
--- a/parser/html/nsHtml5TreeOperation.h
+++ b/parser/html/nsHtml5TreeOperation.h
@@ -43,8 +43,12 @@
#include "nsHtml5HtmlAttributes.h"
class nsHtml5TreeOpExecutor;
+class nsHtml5StateSnapshot;
enum eHtml5TreeOperation {
+#ifdef DEBUG
+ eTreeOpUninitialized,
+#endif
// main HTML5 ops
eTreeOpAppend,
eTreeOpDetach,
@@ -62,11 +66,14 @@ enum eHtml5TreeOperation {
eTreeOpRunScript,
eTreeOpDoneAddingChildren,
eTreeOpDoneCreatingElement,
+ eTreeOpSetDocumentCharset,
+ eTreeOpNeedsCharsetSwitchTo,
eTreeOpUpdateStyleSheet,
eTreeOpProcessBase,
eTreeOpProcessMeta,
eTreeOpProcessOfflineManifest,
eTreeOpMarkMalformedIfScript,
+ eTreeOpStreamEnded,
eTreeOpStartLayout
};
@@ -99,12 +106,15 @@ class nsHtml5TreeOperation {
~nsHtml5TreeOperation();
- inline void Init(nsIContent** aNode, nsIContent** aParent) {
- mOne.node = aNode;
- mTwo.node = aParent;
+ inline void Init(eHtml5TreeOperation aOpCode) {
+ NS_PRECONDITION(mOpCode == eTreeOpUninitialized,
+ "Op code must be uninitialized when initializing.");
+ mOpCode = aOpCode;
}
inline void Init(eHtml5TreeOperation aOpCode, nsIContent** aNode) {
+ NS_PRECONDITION(mOpCode == eTreeOpUninitialized,
+ "Op code must be uninitialized when initializing.");
mOpCode = aOpCode;
mOne.node = aNode;
}
@@ -112,6 +122,8 @@ class nsHtml5TreeOperation {
inline void Init(eHtml5TreeOperation aOpCode,
nsIContent** aNode,
nsIContent** aParent) {
+ NS_PRECONDITION(mOpCode == eTreeOpUninitialized,
+ "Op code must be uninitialized when initializing.");
mOpCode = aOpCode;
mOne.node = aNode;
mTwo.node = aParent;
@@ -121,6 +133,8 @@ class nsHtml5TreeOperation {
nsIContent** aNode,
nsIContent** aParent,
nsIContent** aTable) {
+ NS_PRECONDITION(mOpCode == eTreeOpUninitialized,
+ "Op code must be uninitialized when initializing.");
mOpCode = aOpCode;
mOne.node = aNode;
mTwo.node = aParent;
@@ -128,6 +142,8 @@ class nsHtml5TreeOperation {
}
inline void Init(nsHtml5DocumentMode aMode) {
+ NS_PRECONDITION(mOpCode == eTreeOpUninitialized,
+ "Op code must be uninitialized when initializing.");
mOpCode = eTreeOpDocumentMode;
mOne.mode = aMode;
}
@@ -136,6 +152,8 @@ class nsHtml5TreeOperation {
nsIAtom* aName,
nsHtml5HtmlAttributes* aAttributes,
nsIContent** aTarget) {
+ NS_PRECONDITION(mOpCode == eTreeOpUninitialized,
+ "Op code must be uninitialized when initializing.");
mOpCode = eTreeOpCreateElement;
mInt = aNamespace;
mOne.node = aTarget;
@@ -151,6 +169,8 @@ class nsHtml5TreeOperation {
PRUnichar* aBuffer,
PRInt32 aLength,
nsIContent** aTarget) {
+ NS_PRECONDITION(mOpCode == eTreeOpUninitialized,
+ "Op code must be uninitialized when initializing.");
mOpCode = aOpCode;
mOne.node = aTarget;
mTwo.unicharPtr = aBuffer;
@@ -159,6 +179,8 @@ class nsHtml5TreeOperation {
inline void Init(nsIContent** aElement,
nsHtml5HtmlAttributes* aAttributes) {
+ NS_PRECONDITION(mOpCode == eTreeOpUninitialized,
+ "Op code must be uninitialized when initializing.");
mOpCode = eTreeOpAddAttributes;
mOne.node = aElement;
mTwo.attributes = aAttributes;
@@ -167,15 +189,39 @@ class nsHtml5TreeOperation {
inline void Init(nsIAtom* aName,
const nsAString& aPublicId,
const nsAString& aSystemId, nsIContent** aTarget) {
+ NS_PRECONDITION(mOpCode == eTreeOpUninitialized,
+ "Op code must be uninitialized when initializing.");
mOpCode = eTreeOpCreateDoctype;
mOne.atom = aName;
mTwo.stringPair = new nsHtml5TreeOperationStringPair(aPublicId, aSystemId);
mThree.node = aTarget;
}
+
+ inline void Init(eHtml5TreeOperation aOpCode, const nsACString& aString) {
+ NS_PRECONDITION(mOpCode == eTreeOpUninitialized,
+ "Op code must be uninitialized when initializing.");
+
+ PRInt32 len = aString.Length();
+ char* str = new char[len + 1];
+ const char* start = aString.BeginReading();
+ for (PRInt32 i = 0; i < len; ++i) {
+ str[i] = start[i];
+ }
+ str[len] = '\0';
+
+ mOpCode = aOpCode;
+ mOne.charPtr = str;
+ }
inline PRBool IsRunScript() {
return mOpCode == eTreeOpRunScript;
}
+
+ inline void SetSnapshot(nsAHtml5TreeBuilderState* aSnapshot) {
+ NS_ASSERTION(IsRunScript(),
+ "Setting a snapshot for a tree operation other than eTreeOpRunScript!");
+ mTwo.state = aSnapshot;
+ }
nsresult Perform(nsHtml5TreeOpExecutor* aBuilder);
@@ -200,7 +246,9 @@ class nsHtml5TreeOperation {
nsHtml5HtmlAttributes* attributes;
nsHtml5DocumentMode mode;
PRUnichar* unicharPtr;
+ char* charPtr;
nsHtml5TreeOperationStringPair* stringPair;
+ nsAHtml5TreeBuilderState* state;
} mOne, mTwo, mThree;
PRInt32 mInt; // optimize this away later by using an end
// pointer instead of string length and distinct
diff --git a/parser/html/nsHtml5UTF16Buffer.cpp b/parser/html/nsHtml5UTF16Buffer.cpp
index 24258f32f22..d0f52945e29 100644
--- a/parser/html/nsHtml5UTF16Buffer.cpp
+++ b/parser/html/nsHtml5UTF16Buffer.cpp
@@ -42,6 +42,7 @@
#include "nsHtml5Atoms.h"
#include "nsHtml5ByteReadable.h"
#include "nsIUnicodeDecoder.h"
+#include "nsAHtml5TreeBuilderState.h"
#include "nsHtml5Tokenizer.h"
#include "nsHtml5TreeBuilder.h"
diff --git a/parser/html/nsHtml5UTF16Buffer.h b/parser/html/nsHtml5UTF16Buffer.h
index 85929a9a24e..6d64348b74e 100644
--- a/parser/html/nsHtml5UTF16Buffer.h
+++ b/parser/html/nsHtml5UTF16Buffer.h
@@ -43,8 +43,10 @@
#include "nsHtml5Atoms.h"
#include "nsHtml5ByteReadable.h"
#include "nsIUnicodeDecoder.h"
+#include "nsAHtml5TreeBuilderState.h"
class nsHtml5StreamParser;
+class nsHtml5SpeculativeLoader;
class nsHtml5Tokenizer;
class nsHtml5TreeBuilder;