Bug 687744 and bug 573078 - Make buffer allocations in the HTML parser fallible; deal with allocation failures; reuse the buffers of strings passed to the parser. r=Olli.Pettay.
authorHenri Sivonen <hsivonen@iki.fi>
Wed, 28 Sep 2011 15:45:17 +0300
changeset 78758 daef044609cedf37263d0adb662019bb06c2e84f
parent 78735 fa65bb9a09092e9cffa8cf352ddd4eaf8d3bef00
child 78759 1491a374c1a6c8df3fc56b26f31ae1f77cef9992
push id21335
push userbmo@edmorley.co.uk
push dateSat, 15 Oct 2011 11:21:12 +0000
treeherdermozilla-central@3b58a9df4c8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersOlli.Pettay
bugs687744, 573078
milestone10.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 687744 and bug 573078 - Make buffer allocations in the HTML parser fallible; deal with allocation failures; reuse the buffers of strings passed to the parser. r=Olli.Pettay.
parser/html/Makefile.in
parser/html/nsHtml5DependentUTF16Buffer.cpp
parser/html/nsHtml5DependentUTF16Buffer.h
parser/html/nsHtml5OwningUTF16Buffer.cpp
parser/html/nsHtml5OwningUTF16Buffer.h
parser/html/nsHtml5Parser.cpp
parser/html/nsHtml5Parser.h
parser/html/nsHtml5Speculation.cpp
parser/html/nsHtml5Speculation.h
parser/html/nsHtml5StreamParser.cpp
parser/html/nsHtml5StreamParser.h
parser/html/nsHtml5TreeBuilderCppSupplement.h
parser/html/nsHtml5TreeBuilderHSupplement.h
parser/html/nsHtml5TreeOpExecutor.cpp
parser/html/nsHtml5TreeOpExecutor.h
parser/html/nsHtml5TreeOperation.cpp
parser/html/nsHtml5TreeOperation.h
parser/html/nsHtml5UTF16Buffer.cpp
parser/html/nsHtml5UTF16Buffer.h
parser/html/nsHtml5UTF16BufferCppSupplement.h
parser/html/nsHtml5UTF16BufferHSupplement.h
--- a/parser/html/Makefile.in
+++ b/parser/html/Makefile.in
@@ -69,28 +69,32 @@ EXPORTS		= \
 		nsHtml5StreamParser.h \
 		nsHtml5SVGLoadDispatcher.h \
 		nsHtml5TreeOperation.h \
 		nsHtml5TreeOpExecutor.h \
 		nsAHtml5TreeOpSink.h \
 		nsHtml5TreeOpStage.h \
 		nsHtml5UTF16Buffer.h \
 		nsHtml5UTF16BufferHSupplement.h \
+		nsHtml5DependentUTF16Buffer.h \
+		nsHtml5OwningUTF16Buffer.h \
 		$(NULL)
 
 CPPSRCS		= \
 		nsHtml5Atoms.cpp \
 		nsHtml5Atom.cpp \
 		nsHtml5AtomTable.cpp \
 		nsHtml5Parser.cpp \
 		nsHtml5AttributeName.cpp \
 		nsHtml5ElementName.cpp \
 		nsHtml5HtmlAttributes.cpp \
 		nsHtml5StackNode.cpp \
 		nsHtml5UTF16Buffer.cpp \
+		nsHtml5DependentUTF16Buffer.cpp \
+		nsHtml5OwningUTF16Buffer.cpp \
 		nsHtml5NamedCharacters.cpp \
 		nsHtml5NamedCharactersAccel.cpp \
 		nsHtml5Tokenizer.cpp \
 		nsHtml5TreeBuilder.cpp \
 		nsHtml5Portability.cpp \
 		nsHtml5Module.cpp \
 		nsHtml5ReleasableAttributeName.cpp \
 		nsHtml5ReleasableElementName.cpp \
new file mode 100644
--- /dev/null
+++ b/parser/html/nsHtml5DependentUTF16Buffer.cpp
@@ -0,0 +1,66 @@
+/* ***** 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 Dependent UTF-16 Buffer code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Henri Sivonen <hsivonen@iki.fi>
+ *
+ * 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 "nsHtml5DependentUTF16Buffer.h"
+
+nsHtml5DependentUTF16Buffer::nsHtml5DependentUTF16Buffer(const nsAString& aToWrap)
+  : nsHtml5UTF16Buffer(const_cast<PRUnichar*> (aToWrap.BeginReading()),
+                       aToWrap.Length())
+{
+  MOZ_COUNT_CTOR(nsHtml5DependentUTF16Buffer);
+}
+
+nsHtml5DependentUTF16Buffer::~nsHtml5DependentUTF16Buffer()
+{
+  MOZ_COUNT_DTOR(nsHtml5DependentUTF16Buffer);
+}
+
+already_AddRefed<nsHtml5OwningUTF16Buffer>
+nsHtml5DependentUTF16Buffer::FalliblyCopyAsOwningBuffer()
+{
+  PRInt32 newLength = getEnd() - getStart();
+  nsRefPtr<nsHtml5OwningUTF16Buffer> newObj =
+    nsHtml5OwningUTF16Buffer::FalliblyCreate(newLength);
+  if (!newObj) {
+    return nsnull;
+  }
+  newObj->setEnd(newLength);
+  memcpy(newObj->getBuffer(),
+         getBuffer() + getStart(),
+         newLength * sizeof(PRUnichar));
+  return newObj.forget();
+}
new file mode 100644
--- /dev/null
+++ b/parser/html/nsHtml5DependentUTF16Buffer.h
@@ -0,0 +1,64 @@
+/* ***** 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 Dependent UTF-16 Buffer code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Henri Sivonen <hsivonen@iki.fi>
+ *
+ * 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 nsHtml5DependentUTF16Buffer_h_
+#define nsHtml5DependentUTF16Buffer_h_
+
+#include "nscore.h"
+#include "nsHtml5OwningUTF16Buffer.h"
+
+class NS_STACK_CLASS nsHtml5DependentUTF16Buffer : public nsHtml5UTF16Buffer
+{
+  public:
+    /**
+     * Wraps a string without taking ownership of the buffer. aToWrap MUST NOT
+     * go away or be shortened while nsHtml5DependentUTF16Buffer is in use.
+     */
+    nsHtml5DependentUTF16Buffer(const nsAString& aToWrap);
+
+    ~nsHtml5DependentUTF16Buffer();
+
+    /**
+     * Copies the currently unconsumed part of this buffer into a new
+     * heap-allocated nsHtml5OwningUTF16Buffer. The new object is allocated
+     * with a fallible allocator. If the allocation fails, nsnull is returned.
+     * @return heap-allocated copy or nsnull if memory allocation failed
+     */
+    already_AddRefed<nsHtml5OwningUTF16Buffer> FalliblyCopyAsOwningBuffer();
+};
+
+#endif // nsHtml5DependentUTF16Buffer_h_
new file mode 100644
--- /dev/null
+++ b/parser/html/nsHtml5OwningUTF16Buffer.cpp
@@ -0,0 +1,104 @@
+/* ***** 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) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Henri Sivonen <hsivonen@iki.fi>
+ *
+ * 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 "nsHtml5OwningUTF16Buffer.h"
+
+nsHtml5OwningUTF16Buffer::nsHtml5OwningUTF16Buffer(PRUnichar* aBuffer)
+  : nsHtml5UTF16Buffer(aBuffer, 0),
+    next(nsnull),
+    key(nsnull)
+{
+  MOZ_COUNT_CTOR(nsHtml5OwningUTF16Buffer);
+}
+
+nsHtml5OwningUTF16Buffer::nsHtml5OwningUTF16Buffer(void* aKey)
+  : nsHtml5UTF16Buffer(nsnull, 0),
+    next(nsnull),
+    key(aKey)
+{
+  MOZ_COUNT_CTOR(nsHtml5OwningUTF16Buffer);
+}
+
+nsHtml5OwningUTF16Buffer::~nsHtml5OwningUTF16Buffer()
+{
+  MOZ_COUNT_DTOR(nsHtml5OwningUTF16Buffer);
+  DeleteBuffer();
+}
+
+// static
+already_AddRefed<nsHtml5OwningUTF16Buffer>
+nsHtml5OwningUTF16Buffer::FalliblyCreate(PRInt32 aLength)
+{
+  const mozilla::fallible_t fallible = mozilla::fallible_t();
+  PRUnichar* newBuf = new (fallible) PRUnichar[aLength];
+  if (!newBuf) {
+    return nsnull;
+  }
+  nsRefPtr<nsHtml5OwningUTF16Buffer> newObj =
+    new (fallible) nsHtml5OwningUTF16Buffer(newBuf);
+  if (!newObj) {
+    delete[] newBuf;
+    return nsnull;
+  }
+  return newObj.forget();
+}
+
+// Not using macros for AddRef and Release in order to be able to refcount on
+// and create on different threads.
+
+nsrefcnt
+nsHtml5OwningUTF16Buffer::AddRef()
+{
+  NS_PRECONDITION(PRInt32(mRefCnt) >= 0, "Illegal refcount.");
+  ++mRefCnt;
+  NS_LOG_ADDREF(this, mRefCnt, "nsHtml5OwningUTF16Buffer", sizeof(*this));
+  return mRefCnt;
+}
+
+nsrefcnt
+nsHtml5OwningUTF16Buffer::Release()
+{
+  NS_PRECONDITION(0 != mRefCnt, "Release without AddRef.");
+  --mRefCnt;
+  NS_LOG_RELEASE(this, mRefCnt, "nsHtml5OwningUTF16Buffer");
+  if (mRefCnt == 0) {
+    mRefCnt = 1; /* stabilize */
+    delete this;
+    return 0;
+  }
+  return mRefCnt;
+}
new file mode 100644
--- /dev/null
+++ b/parser/html/nsHtml5OwningUTF16Buffer.h
@@ -0,0 +1,84 @@
+/* ***** 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) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Henri Sivonen <hsivonen@iki.fi>
+ *
+ * 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 nsHtml5OwningUTF16Buffer_h_
+#define nsHtml5OwningUTF16Buffer_h_
+
+#include "nsHtml5UTF16Buffer.h"
+
+class nsHtml5OwningUTF16Buffer : public nsHtml5UTF16Buffer
+{
+  private:
+
+    /**
+     * Passes a buffer and its length to the superclass constructor.
+     */
+    nsHtml5OwningUTF16Buffer(PRUnichar* aBuffer);
+
+  public:
+
+    /**
+     * Constructor for a parser key placeholder. (No actual buffer.)
+     * @param aKey a parser key
+     */
+    nsHtml5OwningUTF16Buffer(void* aKey);
+
+    /**
+     * Takes care of releasing the owned buffer.
+     */
+    ~nsHtml5OwningUTF16Buffer();
+
+    /**
+     * The next buffer in a queue.
+     */
+    nsRefPtr<nsHtml5OwningUTF16Buffer> next;
+
+    /**
+     * A parser key.
+     */
+    void* key;
+
+    static already_AddRefed<nsHtml5OwningUTF16Buffer>
+    FalliblyCreate(PRInt32 aLength);
+
+    nsrefcnt AddRef();
+    nsrefcnt Release();
+  private:
+    nsAutoRefCnt mRefCnt;
+};
+
+#endif // nsHtml5OwningUTF16Buffer_h_
--- a/parser/html/nsHtml5Parser.cpp
+++ b/parser/html/nsHtml5Parser.cpp
@@ -56,16 +56,17 @@
 #include "nsIScriptSecurityManager.h"
 #include "nsHtml5DocumentMode.h"
 #include "nsHtml5Tokenizer.h"
 #include "nsHtml5UTF16Buffer.h"
 #include "nsHtml5TreeBuilder.h"
 #include "nsHtml5Parser.h"
 #include "nsHtml5AtomTable.h"
 #include "nsIDOMDocumentFragment.h"
+#include "nsHtml5DependentUTF16Buffer.h"
 
 NS_INTERFACE_TABLE_HEAD(nsHtml5Parser)
   NS_INTERFACE_TABLE2(nsHtml5Parser, nsIParser, nsISupportsWeakReference)
   NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsHtml5Parser)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsHtml5Parser)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsHtml5Parser)
@@ -80,17 +81,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsHtml5Parser)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mExecutor)
   tmp->DropStreamParser();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 nsHtml5Parser::nsHtml5Parser()
-  : mFirstBuffer(new nsHtml5UTF16Buffer(0))
+  : mFirstBuffer(new nsHtml5OwningUTF16Buffer((void*)nsnull))
   , mLastBuffer(mFirstBuffer)
   , mExecutor(new nsHtml5TreeOpExecutor())
   , mTreeBuilder(new nsHtml5TreeBuilder(mExecutor, nsnull))
   , mTokenizer(new nsHtml5Tokenizer(mTreeBuilder))
   , mRootContextLineNumber(1)
 {
   mAtomTable.Init(); // we aren't checking for OOM anyway...
   mTokenizer->setInterner(&mAtomTable);
@@ -238,16 +239,23 @@ NS_IMETHODIMP
 nsHtml5Parser::Parse(const nsAString& aSourceBuffer,
                      void* aKey,
                      const nsACString& aContentType, // ignored
                      bool aLastCall,
                      nsDTDMode aMode) // ignored
 {
   NS_PRECONDITION(!mExecutor->IsFragmentMode(),
                   "Document.write called in fragment mode!");
+  if (mExecutor->IsBroken()) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+  if (aSourceBuffer.Length() > PR_INT32_MAX) {
+    mExecutor->MarkAsBroken();
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
 
   // Maintain a reference to ourselves so we don't go away
   // till we're completely done. The old parser grips itself in this method.
   nsCOMPtr<nsIParser> kungFuDeathGrip(this);
   
   // Gripping the other objects just in case, since the other old grip
   // required grips to these, too.
   nsRefPtr<nsHtml5StreamParser> streamKungFuDeathGrip(mStreamParser);
@@ -290,110 +298,124 @@ nsHtml5Parser::Parse(const nsAString& aS
 
   NS_ASSERTION(!(mStreamParser && !aKey),
                "Got a null key in a non-script-created parser");
 
   if (aSourceBuffer.IsEmpty()) {
     return NS_OK;
   }
 
-  nsRefPtr<nsHtml5UTF16Buffer> buffer =
-    new nsHtml5UTF16Buffer(aSourceBuffer.Length());
-  memcpy(buffer->getBuffer(),
-         aSourceBuffer.BeginReading(),
-         aSourceBuffer.Length() * sizeof(PRUnichar));
-  buffer->setEnd(aSourceBuffer.Length());
-
-  // The buffer is inserted to the stream here in case it won't be parsed
-  // to completion.
-  // The script is identified by aKey. If there's nothing in the buffer
-  // chain for that key, we'll insert at the head of the queue.
-  // When the script leaves something in the queue, a zero-length
-  // key-holder "buffer" is inserted in the queue. If the same script
-  // leaves something in the chain again, it will be inserted immediately
-  // before the old key holder belonging to the same script.
-  nsHtml5UTF16Buffer* prevSearchBuf = nsnull;
-  nsHtml5UTF16Buffer* searchBuf = mFirstBuffer;
+  nsHtml5DependentUTF16Buffer stackBuffer(aSourceBuffer);
 
-  // after document.open, the first level of document.write has null key
-  if (aKey) {
-    while (searchBuf != mLastBuffer) {
-      if (searchBuf->key == aKey) {
-        // found a key holder
-        // now insert the new buffer between the previous buffer
-        // and the key holder.
-        buffer->next = searchBuf;
-        if (prevSearchBuf) {
-          prevSearchBuf->next = buffer;
-        } else {
-          mFirstBuffer = buffer;
-        }
-        break;
-      }
-      prevSearchBuf = searchBuf;
-      searchBuf = searchBuf->next;
-    }
-    if (searchBuf == mLastBuffer) {
-      // key was not found
-      nsHtml5UTF16Buffer* keyHolder = new nsHtml5UTF16Buffer(aKey);
-      keyHolder->next = mFirstBuffer;
-      buffer->next = keyHolder;
-      mFirstBuffer = buffer;
-    }
-  } else {
-    // we have a first level document.write after document.open()
-    // insert immediately before mLastBuffer
-    while (searchBuf != mLastBuffer) {
-      prevSearchBuf = searchBuf;
-      searchBuf = searchBuf->next;
-    }
-    buffer->next = mLastBuffer;
-    if (prevSearchBuf) {
-      prevSearchBuf->next = buffer;
-    } else {
-      mFirstBuffer = buffer;
-    }
-  }
-
-  while (!mBlocked && buffer->hasMore()) {
-    buffer->adjust(mLastWasCR);
+  while (!mBlocked && stackBuffer.hasMore()) {
+    stackBuffer.adjust(mLastWasCR);
     mLastWasCR = PR_FALSE;
-    if (buffer->hasMore()) {
+    if (stackBuffer.hasMore()) {
       PRInt32 lineNumberSave;
       bool inRootContext = (!mStreamParser && (aKey == mRootContextKey));
       if (inRootContext) {
         mTokenizer->setLineNumber(mRootContextLineNumber);
       } else {
         // we aren't the root context, so save the line number on the
         // *stack* so that we can restore it.
         lineNumberSave = mTokenizer->getLineNumber();
       }
 
-      mLastWasCR = mTokenizer->tokenizeBuffer(buffer);
+      mLastWasCR = mTokenizer->tokenizeBuffer(&stackBuffer);
 
       if (inRootContext) {
         mRootContextLineNumber = mTokenizer->getLineNumber();
       } else {
         mTokenizer->setLineNumber(lineNumberSave);
       }
 
       if (mTreeBuilder->HasScript()) {
         mTreeBuilder->Flush(); // Move ops to the executor
         mExecutor->FlushDocumentWrite(); // run the ops
       }
       // Ignore suspension requests
     }
   }
 
+  nsRefPtr<nsHtml5OwningUTF16Buffer> heapBuffer;
+  if (stackBuffer.hasMore()) {
+    // The buffer wasn't tokenized to completion. Create a copy of the tail
+    // on the heap.
+    heapBuffer = stackBuffer.FalliblyCopyAsOwningBuffer();
+    if (!heapBuffer) {
+      // Allocation failed. The parser is now broken.
+      mExecutor->MarkAsBroken();
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+  }
+
+  // The buffer is inserted to the stream here in case it won't be parsed
+  // to completion.
+  // The script is identified by aKey. If there's nothing in the buffer
+  // chain for that key, we'll insert at the head of the queue.
+  // When the script leaves something in the queue, a zero-length
+  // key-holder "buffer" is inserted in the queue. If the same script
+  // leaves something in the chain again, it will be inserted immediately
+  // before the old key holder belonging to the same script.
+  nsHtml5OwningUTF16Buffer* prevSearchBuf = nsnull;
+  nsHtml5OwningUTF16Buffer* searchBuf = mFirstBuffer;
+
+  // after document.open, the first level of document.write has null key
+  if (aKey) {
+    while (searchBuf != mLastBuffer) {
+      if (searchBuf->key == aKey) {
+        // found a key holder
+        // now insert the new buffer between the previous buffer
+        // and the key holder if we have a buffer left.
+        if (heapBuffer) {
+          heapBuffer->next = searchBuf;
+          if (prevSearchBuf) {
+            prevSearchBuf->next = heapBuffer;
+          } else {
+            mFirstBuffer = heapBuffer;
+          }
+        }
+        break;
+      }
+      prevSearchBuf = searchBuf;
+      searchBuf = searchBuf->next;
+    }
+    if (searchBuf == mLastBuffer) {
+      // key was not found
+      nsHtml5OwningUTF16Buffer* keyHolder = new nsHtml5OwningUTF16Buffer(aKey);
+      keyHolder->next = mFirstBuffer;
+      if (heapBuffer) {
+        heapBuffer->next = keyHolder;
+        mFirstBuffer = heapBuffer;
+      } else {
+        mFirstBuffer = keyHolder;
+      }
+    }
+  } else if (heapBuffer) {
+    // we have a first level document.write after document.open()
+    // insert immediately before mLastBuffer
+    while (searchBuf != mLastBuffer) {
+      prevSearchBuf = searchBuf;
+      searchBuf = searchBuf->next;
+    }
+    heapBuffer->next = mLastBuffer;
+    if (prevSearchBuf) {
+      prevSearchBuf->next = heapBuffer;
+    } else {
+      mFirstBuffer = heapBuffer;
+    }
+  }
+
   if (!mBlocked) { // buffer was tokenized to completion
-    NS_ASSERTION(!buffer->hasMore(), "Buffer wasn't tokenized to completion?");
+    NS_ASSERTION(!stackBuffer.hasMore(),
+      "Buffer wasn't tokenized to completion?");
     // Scripting semantics require a forced tree builder flush here
     mTreeBuilder->Flush(); // Move ops to the executor
     mExecutor->FlushDocumentWrite(); // run the ops
-  } else if (buffer->hasMore()) {
+  } else if (stackBuffer.hasMore()) {
     // The buffer wasn't tokenized to completion. Tokenize the untokenized
     // content in order to preload stuff. This content will be retokenized
     // later for normal parsing.
     if (!mDocWriteSpeculatorActive) {
       mDocWriteSpeculatorActive = PR_TRUE;
       if (!mDocWriteSpeculativeTreeBuilder) {
         // Lazily initialize if uninitialized
         mDocWriteSpeculativeTreeBuilder =
@@ -411,25 +433,26 @@ nsHtml5Parser::Parse(const nsAString& aS
     }
 
     // Note that with multilevel document.write if we didn't just activate the
     // speculator, it's possible that the speculator is now in the wrong state.
     // That's OK for the sake of simplicity. The worst that can happen is
     // that the speculative loads aren't exactly right. The content will be
     // reparsed anyway for non-preload purposes.
 
-    PRInt32 originalStart = buffer->getStart();
-    while (buffer->hasMore()) {
-      buffer->adjust(mDocWriteSpeculativeLastWasCR);
-      if (buffer->hasMore()) {
+    // The buffer position for subsequent non-speculative parsing now lives
+    // in heapBuffer, so it's ok to let the buffer position of stackBuffer
+    // to be overwritten and not restored below.
+    while (stackBuffer.hasMore()) {
+      stackBuffer.adjust(mDocWriteSpeculativeLastWasCR);
+      if (stackBuffer.hasMore()) {
         mDocWriteSpeculativeLastWasCR =
-            mDocWriteSpeculativeTokenizer->tokenizeBuffer(buffer);
+            mDocWriteSpeculativeTokenizer->tokenizeBuffer(&stackBuffer);
       }
     }
-    buffer->setStart(originalStart);
 
     mDocWriteSpeculativeTreeBuilder->Flush();
     mDocWriteSpeculativeTreeBuilder->DropHandles();
     mExecutor->FlushSpeculativeLoads();
   }
 
   return NS_OK;
 }
@@ -472,16 +495,18 @@ nsHtml5Parser::ParseFragment(const nsASt
 nsresult
 nsHtml5Parser::ParseHtml5Fragment(const nsAString& aSourceBuffer,
                                   nsIContent* aTargetNode,
                                   nsIAtom* aContextLocalName,
                                   PRInt32 aContextNamespace,
                                   bool aQuirks,
                                   bool aPreventScriptExecution)
 {
+  NS_ENSURE_TRUE(aSourceBuffer.Length() <= PR_INT32_MAX,
+      NS_ERROR_OUT_OF_MEMORY);
   nsIDocument* doc = aTargetNode->GetOwnerDoc();
   NS_ENSURE_TRUE(doc, NS_ERROR_NOT_AVAILABLE);
   
   nsIURI* uri = doc->GetDocumentURI();
   NS_ENSURE_TRUE(uri, NS_ERROR_NOT_AVAILABLE);
 
   mExecutor->EnableFragmentMode(aPreventScriptExecution);
 
@@ -509,21 +534,17 @@ nsHtml5Parser::ParseHtml5Fragment(const 
 
   NS_PRECONDITION(!mExecutor->HasStarted(),
                   "Tried to start parse without initializing the parser.");
   mTreeBuilder->setScriptingEnabled(mExecutor->IsScriptEnabled());
   mTokenizer->start();
   mExecutor->Start(); // Don't call WillBuildModel in fragment case
   if (!aSourceBuffer.IsEmpty()) {
     bool lastWasCR = false;
-    nsHtml5UTF16Buffer buffer(aSourceBuffer.Length());
-    memcpy(buffer.getBuffer(),
-           aSourceBuffer.BeginReading(),
-           aSourceBuffer.Length() * sizeof(PRUnichar));
-    buffer.setEnd(aSourceBuffer.Length());
+    nsHtml5DependentUTF16Buffer buffer(aSourceBuffer);    
     while (buffer.hasMore()) {
       buffer.adjust(lastWasCR);
       lastWasCR = PR_FALSE;
       if (buffer.hasMore()) {
         lastWasCR = mTokenizer->tokenizeBuffer(&buffer);
         if (mTreeBuilder->HasScript()) {
           // Flush on each script, because the execution prevention code
           // can handle at most one script per flush.
@@ -624,21 +645,17 @@ nsHtml5Parser::IsScriptCreated()
 
 // not from interface
 void
 nsHtml5Parser::ParseUntilBlocked()
 {
   NS_PRECONDITION(!mExecutor->IsFragmentMode(),
                   "ParseUntilBlocked called in fragment mode.");
 
-  if (mBlocked) {
-    return;
-  }
-
-  if (mExecutor->IsComplete()) {
+  if (mBlocked || mExecutor->IsComplete() || mExecutor->IsBroken()) {
     return;
   }
   NS_ASSERTION(mExecutor->HasStarted(), "Bad life cycle.");
 
   mDocWriteSpeculatorActive = PR_FALSE;
 
   for (;;) {
     if (!mFirstBuffer->hasMore()) {
--- a/parser/html/nsHtml5Parser.h
+++ b/parser/html/nsHtml5Parser.h
@@ -51,17 +51,17 @@
 #include "nsIRequest.h"
 #include "nsIChannel.h"
 #include "nsCOMArray.h"
 #include "nsContentSink.h"
 #include "nsIHTMLDocument.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIInputStream.h"
 #include "nsDetectionConfident.h"
-#include "nsHtml5UTF16Buffer.h"
+#include "nsHtml5OwningUTF16Buffer.h"
 #include "nsHtml5TreeOpExecutor.h"
 #include "nsHtml5StreamParser.h"
 #include "nsHtml5AtomTable.h"
 #include "nsWeakReference.h"
 
 class nsHtml5Parser : public nsIParser,
                       public nsSupportsWeakReference
 {
@@ -356,23 +356,23 @@ class nsHtml5Parser : public nsIParser,
 
     // Gecko integration
     void*                         mRootContextKey;
 
     // Portable parser objects
     /**
      * The first buffer in the pending UTF-16 buffer queue
      */
-    nsRefPtr<nsHtml5UTF16Buffer>  mFirstBuffer;
+    nsRefPtr<nsHtml5OwningUTF16Buffer>  mFirstBuffer;
 
     /**
-     * The last buffer in the pending UTF-16 buffer queue
+     * The last buffer in the pending UTF-16 buffer queue. Always points
+     * to a sentinel object with nsnull as its parser key.
      */
-    nsHtml5UTF16Buffer*           mLastBuffer; // weak ref; always points to
-                      // a buffer of the size NS_HTML5_PARSER_READ_BUFFER_SIZE
+    nsHtml5OwningUTF16Buffer* mLastBuffer; // weak ref;
 
     /**
      * The tree operation executor
      */
     nsRefPtr<nsHtml5TreeOpExecutor>     mExecutor;
 
     /**
      * The HTML5 tree builder
--- a/parser/html/nsHtml5Speculation.cpp
+++ b/parser/html/nsHtml5Speculation.cpp
@@ -32,17 +32,17 @@
  * 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 "nsHtml5Speculation.h"
 
-nsHtml5Speculation::nsHtml5Speculation(nsHtml5UTF16Buffer* aBuffer, 
+nsHtml5Speculation::nsHtml5Speculation(nsHtml5OwningUTF16Buffer* aBuffer,
                                        PRInt32 aStart, 
                                        PRInt32 aStartLineNumber, 
                                        nsAHtml5TreeBuilderState* aSnapshot)
   : mBuffer(aBuffer)
   , mStart(aStart)
   , mStartLineNumber(aStartLineNumber)
   , mSnapshot(aSnapshot)
 {
--- a/parser/html/nsHtml5Speculation.h
+++ b/parser/html/nsHtml5Speculation.h
@@ -33,34 +33,34 @@
  * 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 nsHtml5Speculation_h__
 #define nsHtml5Speculation_h__
 
-#include "nsHtml5UTF16Buffer.h"
+#include "nsHtml5OwningUTF16Buffer.h"
 #include "nsAHtml5TreeBuilderState.h"
 #include "nsHtml5TreeOperation.h"
 #include "nsAHtml5TreeOpSink.h"
 #include "nsTArray.h"
 #include "nsAutoPtr.h"
 
 class nsHtml5Speculation : public nsAHtml5TreeOpSink
 {
   public:
-    nsHtml5Speculation(nsHtml5UTF16Buffer* aBuffer, 
+    nsHtml5Speculation(nsHtml5OwningUTF16Buffer* aBuffer,
                        PRInt32 aStart, 
                        PRInt32 aStartLineNumber, 
                        nsAHtml5TreeBuilderState* aSnapshot);
     
     ~nsHtml5Speculation();
 
-    nsHtml5UTF16Buffer* GetBuffer() {
+    nsHtml5OwningUTF16Buffer* GetBuffer() {
       return mBuffer;
     }
     
     PRInt32 GetStart() {
       return mStart;
     }
 
     PRInt32 GetStartLineNumber() {
@@ -78,17 +78,17 @@ class nsHtml5Speculation : public nsAHtm
     virtual void MoveOpsFrom(nsTArray<nsHtml5TreeOperation>& aOpQueue);
     
     void FlushToSink(nsAHtml5TreeOpSink* aSink);
 
   private:
     /**
      * The first buffer in the pending UTF-16 buffer queue
      */
-    nsRefPtr<nsHtml5UTF16Buffer>        mBuffer;
+    nsRefPtr<nsHtml5OwningUTF16Buffer>  mBuffer;
     
     /**
      * The start index of this speculation in the first buffer
      */
     PRInt32                             mStart;
 
     /**
      * The current line number at the start of the speculation
--- a/parser/html/nsHtml5StreamParser.cpp
+++ b/parser/html/nsHtml5StreamParser.cpp
@@ -170,18 +170,18 @@ class nsHtml5LoadFlusher : public nsRunn
     {
       mExecutor->FlushSpeculativeLoads();
       return NS_OK;
     }
 };
 
 nsHtml5StreamParser::nsHtml5StreamParser(nsHtml5TreeOpExecutor* aExecutor,
                                          nsHtml5Parser* aOwner)
-  : mFirstBuffer(new nsHtml5UTF16Buffer(NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE))
-  , mLastBuffer(mFirstBuffer)
+  : mFirstBuffer(nsnull) // Will be filled when starting
+  , mLastBuffer(nsnull) // Will be filled when starting
   , mExecutor(aExecutor)
   , mTreeBuilder(new nsHtml5TreeBuilder(mExecutor->GetStage(),
                                         mExecutor->GetStage()))
   , mTokenizer(new nsHtml5Tokenizer(mTreeBuilder))
   , mTokenizerMutex("nsHtml5StreamParser mTokenizerMutex")
   , mOwner(aOwner)
   , mSpeculationMutex("nsHtml5StreamParser mSpeculationMutex")
   , mTerminatedMutex("nsHtml5StreamParser mTerminatedMutex")
@@ -553,33 +553,45 @@ nsHtml5StreamParser::SniffStreamBytes(co
     mUnicodeDecoder->SetInputErrorBehavior(nsIUnicodeDecoder::kOnError_Recover);
     mCharsetSource = kCharsetFromMetaPrescan;
     mFeedChardet = PR_FALSE;
     mTreeBuilder->SetDocumentCharset(mCharset, mCharsetSource);
     mMetaScanner = nsnull;
     return WriteSniffingBufferAndCurrentSegment(aFromSegment, aCount, aWriteCount);
   }
   if (!mSniffingBuffer) {
-    mSniffingBuffer = new PRUint8[NS_HTML5_STREAM_PARSER_SNIFFING_BUFFER_SIZE];
+    const mozilla::fallible_t fallible = mozilla::fallible_t();
+    mSniffingBuffer = new (fallible)
+      PRUint8[NS_HTML5_STREAM_PARSER_SNIFFING_BUFFER_SIZE];
+    if (!mSniffingBuffer) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
   }
   memcpy(mSniffingBuffer + mSniffingLength, aFromSegment, aCount);
   mSniffingLength += aCount;
   *aWriteCount = aCount;
   return NS_OK;
 }
 
 nsresult
 nsHtml5StreamParser::WriteStreamBytes(const PRUint8* aFromSegment,
                                       PRUint32 aCount,
                                       PRUint32* aWriteCount)
 {
   NS_ASSERTION(IsParserThread(), "Wrong thread!");
-  // mLastBuffer always points to a buffer of the size NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE.
+  // mLastBuffer always points to a buffer of the size
+  // NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE.
   if (mLastBuffer->getEnd() == NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE) {
-    mLastBuffer = (mLastBuffer->next = new nsHtml5UTF16Buffer(NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE));
+    nsRefPtr<nsHtml5OwningUTF16Buffer> newBuf =
+      nsHtml5OwningUTF16Buffer::FalliblyCreate(
+        NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE);
+    if (!newBuf) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+    mLastBuffer = (mLastBuffer->next = newBuf.forget());
   }
   PRInt32 totalByteCount = 0;
   for (;;) {
     PRInt32 end = mLastBuffer->getEnd();
     PRInt32 byteCount = aCount - totalByteCount;
     PRInt32 utf16Count = NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE - end;
 
     NS_ASSERTION(utf16Count, "Trying to convert into a buffer with no free space!");
@@ -618,26 +630,38 @@ nsHtml5StreamParser::WriteStreamBytes(co
         totalByteCount = (PRInt32)aCount;
       }
 
       // Emit the REPLACEMENT CHARACTER
       mLastBuffer->getBuffer()[end] = 0xFFFD;
       ++end;
       mLastBuffer->setEnd(end);
       if (end == NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE) {
-          mLastBuffer = mLastBuffer->next = new nsHtml5UTF16Buffer(NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE);
+        nsRefPtr<nsHtml5OwningUTF16Buffer> newBuf =
+          nsHtml5OwningUTF16Buffer::FalliblyCreate(
+            NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE);
+        if (!newBuf) {
+          return NS_ERROR_OUT_OF_MEMORY;
+        }
+        mLastBuffer = (mLastBuffer->next = newBuf.forget());
       }
 
       mUnicodeDecoder->Reset();
       if (totalByteCount == (PRInt32)aCount) {
         *aWriteCount = (PRUint32)totalByteCount;
         return NS_OK;
       }
     } else if (convResult == NS_PARTIAL_MORE_OUTPUT) {
-      mLastBuffer = mLastBuffer->next = new nsHtml5UTF16Buffer(NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE);
+      nsRefPtr<nsHtml5OwningUTF16Buffer> newBuf =
+        nsHtml5OwningUTF16Buffer::FalliblyCreate(
+          NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE);
+      if (!newBuf) {
+        return NS_ERROR_OUT_OF_MEMORY;
+      }
+      mLastBuffer = (mLastBuffer->next = newBuf.forget());
       // All input may have been consumed if there is a pending surrogate pair
       // that doesn't fit in the output buffer. Loop back to push a zero-length
       // input to the decoder in that case.
     } else {
       NS_ASSERTION(totalByteCount == (PRInt32)aCount,
           "The Unicode decoder consumed the wrong number of bytes.");
       *aWriteCount = (PRUint32)totalByteCount;
       return NS_OK;
@@ -669,16 +693,29 @@ nsHtml5StreamParser::OnStartRequest(nsIR
   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.
    */
   mExecutor->WillBuildModel(eDTDMode_unknown);
   
+  nsRefPtr<nsHtml5OwningUTF16Buffer> newBuf =
+    nsHtml5OwningUTF16Buffer::FalliblyCreate(
+      NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE);
+  if (!newBuf) {
+    mExecutor->MarkAsBroken(); // marks this stream parser as terminated,
+                               // which prevents entry to code paths that
+                               // would use mFirstBuffer or mLastBuffer.
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+  NS_ASSERTION(!mFirstBuffer, "How come we have the first buffer set?");
+  NS_ASSERTION(!mLastBuffer, "How come we have the last buffer set?");
+  mFirstBuffer = mLastBuffer = newBuf;
+
   nsresult rv = NS_OK;
 
   mReparseForbidden = PR_FALSE;
   nsCOMPtr<nsIHttpChannel> 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
@@ -721,26 +758,28 @@ nsHtml5StreamParser::DoStopRequest()
   NS_PRECONDITION(STREAM_BEING_READ == mStreamState,
                   "Stream ended without being open.");
   mTokenizerMutex.AssertCurrentThreadOwns();
 
   if (IsTerminated()) {
     return;
   }
 
+  mStreamState = STREAM_ENDED;
+
   if (!mUnicodeDecoder) {
     PRUint32 writeCount;
-    FinalizeSniffing(nsnull, 0, &writeCount, 0);
-    // dropped nsresult here
+    if (NS_FAILED(FinalizeSniffing(nsnull, 0, &writeCount, 0))) {
+      MarkAsBroken();
+      return;
+    }
   } else if (mFeedChardet) {
     mChardet->Done();
   }
 
-  mStreamState = STREAM_ENDED;
-
   if (IsTerminatedOrInterrupted()) {
     return;
   }
 
   ParseAvailableData(); 
 }
 
 class nsHtml5RequestStopper : public nsRunnable
@@ -784,27 +823,31 @@ nsHtml5StreamParser::DoDataAvailable(PRU
                   "DoDataAvailable called when stream not open.");
   mTokenizerMutex.AssertCurrentThreadOwns();
 
   if (IsTerminated()) {
     return;
   }
 
   PRUint32 writeCount;
+  nsresult rv;
   if (HasDecoder()) {
     if (mFeedChardet) {
       bool dontFeed;
       mChardet->DoIt((const char*)aBuffer, aLength, &dontFeed);
       mFeedChardet = !dontFeed;
     }
-    WriteStreamBytes(aBuffer, aLength, &writeCount);
+    rv = WriteStreamBytes(aBuffer, aLength, &writeCount);
   } else {
-    SniffStreamBytes(aBuffer, aLength, &writeCount);
+    rv = SniffStreamBytes(aBuffer, aLength, &writeCount);
   }
-  // dropping nsresult here
+  if (NS_FAILED(rv)) {
+    MarkAsBroken();
+    return;
+  }
   NS_ASSERTION(writeCount == aLength, "Wrong number of stream bytes written/sniffed.");
 
   if (IsTerminatedOrInterrupted()) {
     return;
   }
 
   ParseAvailableData();
 
@@ -846,25 +889,34 @@ class nsHtml5DataAvailable : public nsRu
 // nsIStreamListener method:
 nsresult
 nsHtml5StreamParser::OnDataAvailable(nsIRequest* aRequest,
                                nsISupports* aContext,
                                nsIInputStream* aInStream,
                                PRUint32 aSourceOffset,
                                PRUint32 aLength)
 {
+  if (mExecutor->IsBroken()) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
   NS_ASSERTION(mRequest == aRequest, "Got data on wrong stream.");
   PRUint32 totalRead;
-  nsAutoArrayPtr<PRUint8> data(new PRUint8[aLength]);
+  const mozilla::fallible_t fallible = mozilla::fallible_t();
+  nsAutoArrayPtr<PRUint8> data(new (fallible) PRUint8[aLength]);
+  if (!data) {
+    mExecutor->MarkAsBroken();
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
   nsresult rv = aInStream->Read(reinterpret_cast<char*>(data.get()),
   aLength, &totalRead);
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ASSERTION(totalRead <= aLength, "Read more bytes than were available?");
   nsCOMPtr<nsIRunnable> dataAvailable = new nsHtml5DataAvailable(this,
-                                                                data.forget(),
+                                                                 data.forget(),
                                                                 totalRead);
   if (NS_FAILED(mThread->Dispatch(dataAvailable, nsIThread::DISPATCH_NORMAL))) {
     NS_WARNING("Dispatching DataAvailable event failed.");
   }
   return rv;
 }
 
 bool
@@ -1057,16 +1109,19 @@ public:
 };
 
 void
 nsHtml5StreamParser::ContinueAfterScripts(nsHtml5Tokenizer* aTokenizer, 
                                           nsHtml5TreeBuilder* aTreeBuilder,
                                           bool aLastWasCR)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  if (mExecutor->IsBroken()) {
+    return;
+  }
   #ifdef DEBUG
     mExecutor->AssertStageEmpty();
   #endif
   bool speculationFailed = false;
   {
     mozilla::MutexAutoLock speculationAutoLock(mSpeculationMutex);
     if (mSpeculations.IsEmpty()) {
       NS_NOTREACHED("ContinueAfterScripts called without speculations.");
@@ -1131,17 +1186,17 @@ nsHtml5StreamParser::ContinueAfterScript
                                       nsnull,
                                       EmptyString(),
                                       speculation->GetStartLineNumber(),
                                       0,
                                       nsIScriptError::warningFlag,
                                       "DOM Events",
                                       mExecutor->GetDocument());
 
-      nsHtml5UTF16Buffer* buffer = mFirstBuffer->next;
+      nsHtml5OwningUTF16Buffer* buffer = mFirstBuffer->next;
       while (buffer) {
         buffer->setStart(0);
         buffer = buffer->next;
       }
       
       mSpeculations.Clear(); // potentially a huge number of destructors 
                              // run here synchronously on the main thread...
 
@@ -1277,8 +1332,23 @@ nsHtml5StreamParser::TimerFlush()
   // we aren't speculating and we don't know when new data is
   // going to arrive. Send data to the main thread.
   if (mTreeBuilder->Flush(PR_TRUE)) {
     if (NS_FAILED(NS_DispatchToMainThread(mExecutorFlusher))) {
       NS_WARNING("failed to dispatch executor flush event");
     }
   }
 }
+
+void
+nsHtml5StreamParser::MarkAsBroken()
+{
+  NS_ASSERTION(IsParserThread(), "Wrong thread!");
+  mTokenizerMutex.AssertCurrentThreadOwns();
+
+  Terminate();
+  mTreeBuilder->MarkAsBroken();
+  mozilla::DebugOnly<bool> hadOps = mTreeBuilder->Flush(PR_FALSE);
+  NS_ASSERTION(hadOps, "Should have had the markAsBroken op!");
+  if (NS_FAILED(NS_DispatchToMainThread(mExecutorFlusher))) {
+    NS_WARNING("failed to dispatch executor flush event");
+  }
+}
--- a/parser/html/nsHtml5StreamParser.h
+++ b/parser/html/nsHtml5StreamParser.h
@@ -41,17 +41,17 @@
 
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsIStreamListener.h"
 #include "nsICharsetDetectionObserver.h"
 #include "nsHtml5MetaScanner.h"
 #include "nsIUnicodeDecoder.h"
 #include "nsHtml5TreeOpExecutor.h"
-#include "nsHtml5UTF16Buffer.h"
+#include "nsHtml5OwningUTF16Buffer.h"
 #include "nsIInputStream.h"
 #include "nsICharsetAlias.h"
 #include "mozilla/Mutex.h"
 #include "nsHtml5AtomTable.h"
 #include "nsHtml5Speculation.h"
 #include "nsITimer.h"
 #include "nsICharsetDetector.h"
 
@@ -190,16 +190,18 @@ class nsHtml5StreamParser : public nsISt
 #ifdef DEBUG
     bool IsParserThread() {
       bool ret;
       mThread->IsOnCurrentThread(&ret);
       return ret;
     }
 #endif
 
+    void MarkAsBroken();
+
     /**
      * Marks the stream parser as interrupted. If you ever add calls to this
      * method, be sure to review Uninterrupt usage very, very carefully to
      * avoid having a previous in-flight runnable cancel your Interrupt()
      * call on the other thread too soon.
      */
     void Interrupt() {
       mozilla::MutexAutoLock autoLock(mTerminatedMutex);
@@ -378,22 +380,22 @@ class nsHtml5StreamParser : public nsISt
      * Whether reparse is forbidden
      */
     bool                          mReparseForbidden;
 
     // Portable parser objects
     /**
      * The first buffer in the pending UTF-16 buffer queue
      */
-    nsRefPtr<nsHtml5UTF16Buffer>  mFirstBuffer;
+    nsRefPtr<nsHtml5OwningUTF16Buffer> mFirstBuffer;
 
     /**
      * The last buffer in the pending UTF-16 buffer queue
      */
-    nsHtml5UTF16Buffer*           mLastBuffer; // weak ref; always points to
+    nsHtml5OwningUTF16Buffer*     mLastBuffer; // weak ref; always points to
                       // a buffer of the size NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE
 
     /**
      * The tree operation executor
      */
     nsHtml5TreeOpExecutor*        mExecutor;
 
     /**
--- a/parser/html/nsHtml5TreeBuilderCppSupplement.h
+++ b/parser/html/nsHtml5TreeBuilderCppSupplement.h
@@ -682,16 +682,23 @@ nsHtml5TreeBuilder::AddSnapshotToScript(
 
 void
 nsHtml5TreeBuilder::DropHandles()
 {
   mOldHandles.Clear();
   mHandlesUsed = 0;
 }
 
+void
+nsHtml5TreeBuilder::MarkAsBroken()
+{
+  mOpQueue.Clear(); // Previous ops don't matter anymore
+  mOpQueue.AppendElement()->Init(eTreeOpMarkAsBroken);
+}
+
 // DocumentModeHandler
 void
 nsHtml5TreeBuilder::documentMode(nsHtml5DocumentMode m)
 {
   nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
   NS_ASSERTION(treeOp, "Tree op allocation failed.");
   treeOp->Init(m);
 }
--- a/parser/html/nsHtml5TreeBuilderHSupplement.h
+++ b/parser/html/nsHtml5TreeBuilderHSupplement.h
@@ -90,8 +90,10 @@
 
     void StreamEnded();
 
     void NeedsCharsetSwitchTo(const nsACString& aEncoding, PRInt32 aSource);
 
     void AddSnapshotToScript(nsAHtml5TreeBuilderState* aSnapshot, PRInt32 aLine);
 
     void DropHandles();
+
+    void MarkAsBroken();
--- a/parser/html/nsHtml5TreeOpExecutor.cpp
+++ b/parser/html/nsHtml5TreeOpExecutor.cpp
@@ -264,16 +264,37 @@ nsHtml5TreeOpExecutor::GetTarget()
 // nsContentSink overrides
 
 void
 nsHtml5TreeOpExecutor::UpdateChildCounts()
 {
   // No-op
 }
 
+void
+nsHtml5TreeOpExecutor::MarkAsBroken()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(!mFragmentMode, "Fragment parsers can't be broken!");
+  mBroken = true;
+  if (mStreamParser) {
+    mStreamParser->Terminate();
+  }
+  // We are under memory pressure, but let's hope the following allocation
+  // works out so that we get to terminate and clean up the parser from
+  // a safer point.
+  if (mParser) { // can mParser ever be null here?
+    nsCOMPtr<nsIRunnable> terminator =
+      NS_NewRunnableMethod(GetParser(), &nsHtml5Parser::Terminate);
+    if (NS_FAILED(NS_DispatchToMainThread(terminator))) {
+      NS_WARNING("failed to dispatch executor flush event");
+    }
+  }
+}
+
 nsresult
 nsHtml5TreeOpExecutor::FlushTags()
 {
   return NS_OK;
 }
 
 void
 nsHtml5TreeOpExecutor::PostEvaluateScript(nsIScriptElement *aElement)
@@ -421,16 +442,20 @@ nsHtml5TreeOpExecutor::RunFlushLoop()
 
   for (;;) {
     if (!mParser) {
       // Parse has terminated.
       mOpQueue.Clear(); // clear in order to be able to assert in destructor
       return;
     }
 
+    if (IsBroken()) {
+      return;
+    }
+
     if (!mParser->IsParserEnabled()) {
       // The parser is blocked.
       return;
     }
   
     if (mFlushState != eNotFlushing) {
       // XXX Can this happen? In case it can, let's avoid crashing.
       return;
@@ -831,16 +856,17 @@ void
 nsHtml5TreeOpExecutor::Reset()
 {
   DropHeldElements();
   mReadingFromStage = PR_FALSE;
   mOpQueue.Clear();
   mStarted = PR_FALSE;
   mFlushState = eNotFlushing;
   mRunFlushLoopOnStack = PR_FALSE;
+  NS_ASSERTION(!mBroken, "Fragment parser got broken.");
 }
 
 void
 nsHtml5TreeOpExecutor::DropHeldElements()
 {
   mScriptLoader = nsnull;
   mDocument = nsnull;
   mNodeInfoManager = nsnull;
--- a/parser/html/nsHtml5TreeOpExecutor.h
+++ b/parser/html/nsHtml5TreeOpExecutor.h
@@ -120,16 +120,26 @@ class nsHtml5TreeOpExecutor : public nsC
     nsHtml5TreeOpStage            mStage;
 
     eHtml5FlushState              mFlushState;
 
     bool                          mRunFlushLoopOnStack;
 
     bool                          mCallContinueInterruptedParsingIfEnabled;
 
+    /**
+     * True if this parser should refuse to process any more input.
+     * Currently, the only way a parser can break is if it drops some input
+     * due to a memory allocation failure. In such a case, the whole parser
+     * needs to be marked as broken, because some input has been lost and
+     * parsing more input could lead to a DOM where pieces of HTML source
+     * that weren't supposed to become scripts become scripts.
+     */
+    bool                          mBroken;
+
   public:
   
     nsHtml5TreeOpExecutor();
     virtual ~nsHtml5TreeOpExecutor();
   
     // nsIContentSink
 
     /**
@@ -240,16 +250,30 @@ class nsHtml5TreeOpExecutor : public nsC
       mFragmentMode = PR_TRUE;
       mPreventScriptExecution = aPreventScriptExecution;
     }
     
     bool IsFragmentMode() {
       return mFragmentMode;
     }
 
+    /**
+     * Marks this parser as broken and tells the stream parser (if any) to
+     * terminate.
+     */
+    void MarkAsBroken();
+
+    /**
+     * Checks if this parser is broken.
+     */
+    inline bool IsBroken() {
+      NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+      return mBroken;
+    }
+
     inline void BeginDocUpdate() {
       NS_PRECONDITION(mFlushState == eInFlush, "Tried to double-open update.");
       NS_PRECONDITION(mParser, "Started update without parser.");
       mFlushState = eInDocUpdate;
       mDocument->BeginUpdate(UPDATE_CONTENT_MODEL);
     }
 
     inline void EndDocUpdate() {
--- a/parser/html/nsHtml5TreeOperation.cpp
+++ b/parser/html/nsHtml5TreeOperation.cpp
@@ -595,16 +595,20 @@ nsHtml5TreeOperation::Perform(nsHtml5Tre
                             name,
                             publicId,
                             systemId,
                             voidString);
       NS_ASSERTION(docType, "Doctype creation failed.");
       nsCOMPtr<nsIContent> asContent = do_QueryInterface(docType);
       return AppendToDocument(asContent, aBuilder);
     }
+    case eTreeOpMarkAsBroken: {
+      aBuilder->MarkAsBroken();
+      return rv;
+    }
     case eTreeOpRunScript: {
       nsIContent* node = *(mOne.node);
       nsAHtml5TreeBuilderState* snapshot = mTwo.state;
       if (snapshot) {
         aBuilder->InitializeDocWriteParserState(snapshot, mInt);
       }
       *aScriptElement = node;
       return rv;
--- a/parser/html/nsHtml5TreeOperation.h
+++ b/parser/html/nsHtml5TreeOperation.h
@@ -63,16 +63,17 @@ enum eHtml5TreeOperation {
   eTreeOpSetFormElement,
   eTreeOpAppendText,
   eTreeOpAppendIsindexPrompt,
   eTreeOpFosterParentText,
   eTreeOpAppendComment,
   eTreeOpAppendCommentToDocument,
   eTreeOpAppendDoctypeToDocument,
   // Gecko-specific on-pop ops
+  eTreeOpMarkAsBroken,
   eTreeOpRunScript,
   eTreeOpRunScriptAsyncDefer,
   eTreeOpDoneAddingChildren,
   eTreeOpDoneCreatingElement,
   eTreeOpFlushPendingAppendNotifications,
   eTreeOpNeedsCharsetSwitchTo,
   eTreeOpUpdateStyleSheet,
   eTreeOpProcessMeta,
--- a/parser/html/nsHtml5UTF16Buffer.cpp
+++ b/parser/html/nsHtml5UTF16Buffer.cpp
@@ -53,25 +53,16 @@
 #include "nsHtml5ElementName.h"
 #include "nsHtml5HtmlAttributes.h"
 #include "nsHtml5StackNode.h"
 #include "nsHtml5StateSnapshot.h"
 #include "nsHtml5Portability.h"
 
 #include "nsHtml5UTF16Buffer.h"
 
-
-nsHtml5UTF16Buffer::nsHtml5UTF16Buffer(PRUnichar* buffer, PRInt32 start, PRInt32 end)
-  : buffer(buffer),
-    start(start),
-    end(end)
-{
-  MOZ_COUNT_CTOR(nsHtml5UTF16Buffer);
-}
-
 PRInt32 
 nsHtml5UTF16Buffer::getStart()
 {
   return start;
 }
 
 void 
 nsHtml5UTF16Buffer::setStart(PRInt32 start)
--- a/parser/html/nsHtml5UTF16Buffer.h
+++ b/parser/html/nsHtml5UTF16Buffer.h
@@ -61,17 +61,16 @@ class nsHtml5Portability;
 
 class nsHtml5UTF16Buffer
 {
   private:
     PRUnichar* buffer;
     PRInt32 start;
     PRInt32 end;
   public:
-    nsHtml5UTF16Buffer(PRUnichar* buffer, PRInt32 start, PRInt32 end);
     PRInt32 getStart();
     void setStart(PRInt32 start);
     PRUnichar* getBuffer();
     PRInt32 getEnd();
     bool hasMore();
     void adjust(bool lastWasCR);
     void setEnd(PRInt32 end);
     static void initializeStatics();
--- a/parser/html/nsHtml5UTF16BufferCppSupplement.h
+++ b/parser/html/nsHtml5UTF16BufferCppSupplement.h
@@ -30,61 +30,26 @@
  * 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 "nsTraceRefcnt.h"
-
-nsHtml5UTF16Buffer::nsHtml5UTF16Buffer(PRInt32 size)
-  : buffer(new PRUnichar[size]),
-    start(0),
-    end(0),
-    next(nsnull),
-    key(nsnull)
-{
-  MOZ_COUNT_CTOR(nsHtml5UTF16Buffer);
-}
-
-nsHtml5UTF16Buffer::nsHtml5UTF16Buffer(void* key)
-  : buffer(nsnull),
-    start(0),
-    end(0),
-    next(nsnull),
-    key(key)
+nsHtml5UTF16Buffer::nsHtml5UTF16Buffer(PRUnichar* aBuffer, PRInt32 aEnd)
+  : buffer(aBuffer)
+  , start(0)
+  , end(aEnd)
 {
   MOZ_COUNT_CTOR(nsHtml5UTF16Buffer);
 }
 
 nsHtml5UTF16Buffer::~nsHtml5UTF16Buffer()
 {
   MOZ_COUNT_DTOR(nsHtml5UTF16Buffer);
-  delete[] buffer;
 }
 
-// Not using macros for AddRef and Release in order to be able to refcount on
-// and create on different threads.
-
-nsrefcnt
-nsHtml5UTF16Buffer::AddRef()
+void
+nsHtml5UTF16Buffer::DeleteBuffer()
 {
-  NS_PRECONDITION(PRInt32(mRefCnt) >= 0, "Illegal refcount.");
-  ++mRefCnt;
-  NS_LOG_ADDREF(this, mRefCnt, "nsHtml5UTF16Buffer", sizeof(*this));
-  return mRefCnt;
+  delete[] buffer;
 }
-
-nsrefcnt
-nsHtml5UTF16Buffer::Release()
-{
-  NS_PRECONDITION(0 != mRefCnt, "Release without AddRef.");
-  --mRefCnt;
-  NS_LOG_RELEASE(this, mRefCnt, "nsHtml5UTF16Buffer");
-  if (mRefCnt == 0) {
-    mRefCnt = 1; /* stabilize */
-    delete this;
-    return 0;
-  }
-  return mRefCnt;                              
-}
--- a/parser/html/nsHtml5UTF16BufferHSupplement.h
+++ b/parser/html/nsHtml5UTF16BufferHSupplement.h
@@ -30,19 +30,16 @@
  * 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 ***** */
 
-  public:
-    nsHtml5UTF16Buffer(PRInt32 size);
-    nsHtml5UTF16Buffer(void* key);
-    ~nsHtml5UTF16Buffer();
-    nsRefPtr<nsHtml5UTF16Buffer> next;
-    void* key;
-    nsrefcnt AddRef();
-    nsrefcnt Release();
-  private:
-    nsAutoRefCnt mRefCnt;
+protected:
+  nsHtml5UTF16Buffer(PRUnichar* aBuffer, PRInt32 aEnd);
+  ~nsHtml5UTF16Buffer();
 
+  /**
+   * For working around the privacy of |buffer| in the generated code.
+   */
+  void DeleteBuffer();