Bug 651072 - Support HTML parsing in XMLHttpRequest. r=smaug.
authorHenri Sivonen <hsivonen@iki.fi>
Wed, 16 Nov 2011 09:38:51 +0200
changeset 81355 0c4d3b7be17e12c03984b859c8bffc90dde1a83e
parent 81354 909ff89ee4877499cb859866a3a179458362c89c
child 81356 9276e3274f18d69c840df1fa44f8b5b32d0e567a
child 81368 291faf6a656a14af465058729c5bd0f14e9673b0
push id628
push userclegnitto@mozilla.com
push dateWed, 21 Dec 2011 14:41:57 +0000
treeherdermozilla-aurora@24a61ad789e8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs651072
milestone11.0a1
Bug 651072 - Support HTML parsing in XMLHttpRequest. r=smaug.
content/base/public/nsContentUtils.h
content/base/public/nsIDocument.h
content/base/src/nsContentUtils.cpp
content/base/src/nsDOMParser.cpp
content/base/src/nsDocument.cpp
content/base/src/nsXMLHttpRequest.cpp
content/base/src/nsXMLHttpRequest.h
content/base/test/Makefile.in
content/base/test/file_html_in_xhr.html
content/base/test/file_html_in_xhr.sjs
content/base/test/file_html_in_xhr2.html
content/base/test/file_html_in_xhr3.html
content/base/test/file_html_in_xhr_slow.sjs
content/base/test/test_html_in_xhr.html
content/html/document/src/nsHTMLDocument.cpp
content/xml/document/src/nsXMLDocument.cpp
dom/locales/en-US/chrome/dom/dom.properties
parser/html/nsHtml5Parser.cpp
parser/html/nsHtml5StreamParser.cpp
parser/html/nsHtml5StreamParser.h
parser/html/nsHtml5TreeOpExecutor.cpp
parser/html/nsHtml5TreeOpExecutor.h
parser/htmlparser/public/nsIParser.h
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -1105,27 +1105,27 @@ public:
    *                       insert in the document. If empty no root element will
    *                       be created.
    * @param aDoctype Doctype node to insert in the document.
    * @param aDocumentURI URI of the document. Must not be null.
    * @param aBaseURI Base URI of the document. Must not be null.
    * @param aPrincipal Prinicpal of the document. Must not be null.
    * @param aScriptObject The object from which the context for event handling
    *                      can be got.
-   * @param aSVGDocument Force SVG Document creation.
+   * @param aFlavor Select the kind of document to create.
    * @param aResult [out] The document that was created.
    */
   static nsresult CreateDocument(const nsAString& aNamespaceURI, 
                                  const nsAString& aQualifiedName, 
                                  nsIDOMDocumentType* aDoctype,
                                  nsIURI* aDocumentURI,
                                  nsIURI* aBaseURI,
                                  nsIPrincipal* aPrincipal,
                                  nsIScriptGlobalObject* aScriptObject,
-                                 bool aSVGDocument,
+                                 DocumentFlavor aFlavor,
                                  nsIDOMDocument** aResult);
 
   /**
    * Sets the text contents of a node by replacing all existing children
    * with a single text child.
    *
    * The function always notifies.
    *
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -125,16 +125,23 @@ class Element;
 
 #define NS_IDOCUMENT_IID \
 { 0xc3e40e8e, 0x8b91, 0x424c, \
   { 0xbe, 0x9c, 0x9c, 0xc1, 0x76, 0xa7, 0xf7, 0x24 } }
 
 // Flag for AddStyleSheet().
 #define NS_STYLESHEET_FROM_CATALOG                (1 << 0)
 
+// Enum for requesting a particular type of document when creating a doc
+enum DocumentFlavor {
+  DocumentFlavorLegacyGuess, // compat with old code until made HTML5-compliant
+  DocumentFlavorHTML, // HTMLDocument with HTMLness bit set to true
+  DocumentFlavorSVG // SVGDocument
+};
+
 // Document states
 
 // RTL locale: specific to the XUL localedir attribute
 #define NS_DOCUMENT_STATE_RTL_LOCALE              NS_DEFINE_EVENT_STATE_MACRO(0)
 // Window activation status
 #define NS_DOCUMENT_STATE_WINDOW_INACTIVE         NS_DEFINE_EVENT_STATE_MACRO(1)
 
 //----------------------------------------------------------------------
@@ -1886,17 +1893,17 @@ NS_NewDOMDocument(nsIDOMDocument** aInst
                   const nsAString& aNamespaceURI, 
                   const nsAString& aQualifiedName, 
                   nsIDOMDocumentType* aDoctype,
                   nsIURI* aDocumentURI,
                   nsIURI* aBaseURI,
                   nsIPrincipal* aPrincipal,
                   bool aLoadedAsData,
                   nsIScriptGlobalObject* aEventObject,
-                  bool aSVGDocument);
+                  DocumentFlavor aFlavor);
 
 // This is used only for xbl documents created from the startup cache.
 // Non-cached documents are created in the same manner as xml documents.
 nsresult
 NS_NewXBLDocument(nsIDOMDocument** aInstancePtrResult,
                   nsIURI* aDocumentURI,
                   nsIURI* aBaseURI,
                   nsIPrincipal* aPrincipal);
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -3723,22 +3723,22 @@ nsContentUtils::ParseFragmentXML(const n
 /* static */
 nsresult
 nsContentUtils::CreateDocument(const nsAString& aNamespaceURI, 
                                const nsAString& aQualifiedName, 
                                nsIDOMDocumentType* aDoctype,
                                nsIURI* aDocumentURI, nsIURI* aBaseURI,
                                nsIPrincipal* aPrincipal,
                                nsIScriptGlobalObject* aEventObject,
-                               bool aSVGDocument,
+                               DocumentFlavor aFlavor,
                                nsIDOMDocument** aResult)
 {
   nsresult rv = NS_NewDOMDocument(aResult, aNamespaceURI, aQualifiedName,
                                   aDoctype, aDocumentURI, aBaseURI, aPrincipal,
-                                  true, aEventObject, aSVGDocument);
+                                  true, aEventObject, aFlavor);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIDocument> document = do_QueryInterface(*aResult);
   
   // created documents are immediately "complete" (ready to use)
   document->SetReadyStateInternal(nsIDocument::READYSTATE_COMPLETE);
   return NS_OK;
 }
--- a/content/base/src/nsDOMParser.cpp
+++ b/content/base/src/nsDOMParser.cpp
@@ -183,17 +183,19 @@ nsDOMParser::ParseFromStream(nsIInputStr
   // Here we have to cheat a little bit...  Setting the base URI won't
   // work if the document has a null principal, so use
   // mOriginalPrincipal when creating the document, then reset the
   // principal.
   nsCOMPtr<nsIDOMDocument> domDocument;
   rv = nsContentUtils::CreateDocument(EmptyString(), EmptyString(), nsnull,
                                       mDocumentURI, mBaseURI,
                                       mOriginalPrincipal,
-                                      scriptHandlingObject, svg,
+                                      scriptHandlingObject,
+                                      svg ? DocumentFlavorSVG :
+                                            DocumentFlavorLegacyGuess,
                                       getter_AddRefs(domDocument));
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Create a fake channel 
   nsCOMPtr<nsIChannel> parserChannel;
   NS_NewInputStreamChannel(getter_AddRefs(parserChannel), mDocumentURI, nsnull,
                            nsDependentCString(contentType), nsnull);
   NS_ENSURE_STATE(parserChannel);
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -1442,17 +1442,19 @@ nsDOMImplementation::CreateDocument(cons
   nsCOMPtr<nsIScriptGlobalObject> scriptHandlingObject =
     do_QueryReferent(mScriptObject);
   
   NS_ENSURE_STATE(!mScriptObject || scriptHandlingObject);
 
   return nsContentUtils::CreateDocument(aNamespaceURI, aQualifiedName, aDoctype,
                                         mDocumentURI, mBaseURI,
                                         mOwner->NodePrincipal(),
-                                        scriptHandlingObject, false, aReturn);
+                                        scriptHandlingObject,
+                                        DocumentFlavorLegacyGuess,
+                                        aReturn);
 }
 
 NS_IMETHODIMP
 nsDOMImplementation::CreateHTMLDocument(const nsAString& aTitle,
                                         nsIDOMDocument** aReturn)
 {
   *aReturn = nsnull;
   NS_ENSURE_STATE(mOwner);
@@ -1474,17 +1476,18 @@ nsDOMImplementation::CreateHTMLDocument(
     do_QueryReferent(mScriptObject);
 
   NS_ENSURE_STATE(!mScriptObject || scriptHandlingObject);
                                                        
   nsCOMPtr<nsIDOMDocument> document;
   rv = nsContentUtils::CreateDocument(EmptyString(), EmptyString(),
                                       doctype, mDocumentURI, mBaseURI,
                                       mOwner->NodePrincipal(),
-                                      scriptHandlingObject, false,
+                                      scriptHandlingObject,
+                                      DocumentFlavorLegacyGuess,
                                       getter_AddRefs(document));
   NS_ENSURE_SUCCESS(rv, rv);
   nsCOMPtr<nsIDocument> doc = do_QueryInterface(document);
 
   nsCOMPtr<nsIContent> root;
   rv = doc->CreateElem(NS_LITERAL_STRING("html"), NULL, kNameSpaceID_XHTML,
                        getter_AddRefs(root));
   NS_ENSURE_SUCCESS(rv, rv);
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -65,17 +65,16 @@
 #include "nsIScriptGlobalObject.h"
 #include "nsDOMClassInfoID.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMWindow.h"
 #include "nsIMIMEService.h"
 #include "nsCExternalHandlerService.h"
 #include "nsIVariant.h"
 #include "xpcprivate.h"
-#include "nsIParser.h"
 #include "XPCQuickStubs.h"
 #include "nsStringStream.h"
 #include "nsIStreamConverterService.h"
 #include "nsICachingChannel.h"
 #include "nsContentUtils.h"
 #include "nsEventDispatcher.h"
 #include "nsDOMJSUtils.h"
 #include "nsCOMArray.h"
@@ -151,16 +150,18 @@ using namespace mozilla;
    XML_HTTP_REQUEST_SENT |                  \
    XML_HTTP_REQUEST_STOPPED)
 
 #define NS_BADCERTHANDLER_CONTRACTID \
   "@mozilla.org/content/xmlhttprequest-bad-cert-handler;1"
 
 #define NS_PROGRESS_EVENT_INTERVAL 50
 
+NS_IMPL_ISUPPORTS1(nsXHRParseEndListener, nsIDOMEventListener)
+
 class nsResumeTimeoutsEvent : public nsRunnable
 {
 public:
   nsResumeTimeoutsEvent(nsPIDOMWindow* aWindow) : mWindow(aWindow) {}
 
   NS_IMETHOD Run()
   {
     mWindow->ResumeTimeouts(false);
@@ -426,17 +427,21 @@ nsXMLHttpRequest::nsXMLHttpRequest()
   : mResponseBodyDecodedPos(0),
     mResponseType(XML_HTTP_RESPONSE_TYPE_DEFAULT),
     mRequestObserver(nsnull), mState(XML_HTTP_REQUEST_UNSENT),
     mUploadTransferred(0), mUploadTotal(0), mUploadComplete(true),
     mProgressSinceLastProgressEvent(false),
     mUploadProgress(0), mUploadProgressMax(0),
     mErrorLoad(false), mTimerIsActive(false),
     mProgressEventWasDelayed(false),
-    mLoadLengthComputable(false), mLoadTotal(0),
+    mLoadLengthComputable(false),
+    mIsHtml(false),
+    mWarnAboutMultipartHtml(false),
+    mWarnAboutSyncHtml(false),
+    mLoadTotal(0),
     mFirstStartRequestSeen(false),
     mInLoadProgressEvent(false),
     mResultJSON(JSVAL_VOID),
     mResultArrayBuffer(nsnull)
 {
   nsLayoutStatics::AddRef();
 }
 
@@ -716,17 +721,44 @@ nsXMLHttpRequest::GetResponseXML(nsIDOMD
   if (mResponseType != XML_HTTP_RESPONSE_TYPE_DEFAULT &&
       mResponseType != XML_HTTP_RESPONSE_TYPE_DOCUMENT) {
     return NS_ERROR_DOM_INVALID_STATE_ERR;
   }
   if ((XML_HTTP_REQUEST_DONE & mState) && mResponseXML) {
     *aResponseXML = mResponseXML;
     NS_ADDREF(*aResponseXML);
   }
-
+  if (mWarnAboutMultipartHtml) {
+    mWarnAboutMultipartHtml = false;
+    nsContentUtils::ReportToConsole(nsContentUtils::eDOM_PROPERTIES,
+                                    "HTMLMultipartXHRWarning",
+                                    nsnull,
+                                    0,
+                                    nsnull, // Response URL not kept around
+                                    EmptyString(),
+                                    0,
+                                    0,
+                                    nsIScriptError::warningFlag,
+                                    "DOM",
+                                    mOwner->WindowID());
+  }
+  if (mWarnAboutSyncHtml) {
+    mWarnAboutSyncHtml = false;
+    nsContentUtils::ReportToConsole(nsContentUtils::eDOM_PROPERTIES,
+                                    "HTMLSyncXHRWarning",
+                                    nsnull,
+                                    0,
+                                    nsnull, // Response URL not kept around
+                                    EmptyString(),
+                                    0,
+                                    0,
+                                    nsIScriptError::warningFlag,
+                                    "DOM",
+                                    mOwner->WindowID());
+  }
   return NS_OK;
 }
 
 /*
  * This piece copied from nsXMLDocument, we try to get the charset
  * from HTTP headers.
  */
 nsresult
@@ -847,17 +879,17 @@ NS_IMETHODIMP nsXMLHttpRequest::GetRespo
 
   if (!(mState & (XML_HTTP_REQUEST_DONE | XML_HTTP_REQUEST_LOADING))) {
     return NS_OK;
   }
 
   // We only decode text lazily if we're also parsing to a doc.
   // Also, if we've decoded all current data already, then no need to decode
   // more.
-  if (!mResponseXML ||
+  if (IsWaitingForHTMLCharset() || !mResponseXML ||
       mResponseBodyDecodedPos == mResponseBody.Length()) {
     aResponseText = mResponseText;
     return NS_OK;
   }
 
   nsresult rv;
 
   nsCOMPtr<nsIDocument> document = do_QueryInterface(mResponseXML);
@@ -1436,16 +1468,26 @@ nsXMLHttpRequest::GetCurrentHttpChannel(
 }
 
 bool
 nsXMLHttpRequest::IsSystemXHR()
 {
   return !!nsContentUtils::IsSystemPrincipal(mPrincipal);
 }
 
+bool
+nsXMLHttpRequest::IsWaitingForHTMLCharset()
+{
+  if (!mIsHtml) {
+    return false;
+  }
+  nsCOMPtr<nsIDocument> doc = do_QueryInterface(mResponseXML);
+  return doc->GetDocumentCharacterSetSource() < kCharsetFromDocTypeDefault;
+}
+
 nsresult
 nsXMLHttpRequest::CheckChannelForCrossSiteRequest(nsIChannel* aChannel)
 {
   // First check if cross-site requests are enabled...
   if (IsSystemXHR()) {
     return NS_OK;
   }
 
@@ -1870,25 +1912,51 @@ nsXMLHttpRequest::OnStartRequest(nsIRequ
                      mResponseType == XML_HTTP_RESPONSE_TYPE_DOCUMENT;
   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
   if (parseBody && httpChannel) {
     nsCAutoString method;
     httpChannel->GetRequestMethod(method);
     parseBody = !method.EqualsLiteral("HEAD");
   }
 
+  mIsHtml = false;
+  mWarnAboutMultipartHtml = false;
+  mWarnAboutSyncHtml = false;
   if (parseBody && NS_SUCCEEDED(status)) {
     // We can gain a huge performance win by not even trying to
     // parse non-XML data. This also protects us from the situation
     // where we have an XML document and sink, but HTML (or other)
     // parser, which can produce unreliable results.
     nsCAutoString type;
     channel->GetContentType(type);
 
-    if (type.Find("xml") == kNotFound) {
+    if (type.EqualsLiteral("text/html")) {
+      if (!(mState & XML_HTTP_REQUEST_ASYNC)) {
+        // We don't make cool new features available in the bad synchronous
+        // mode. The synchronous mode is for legacy only.
+        mWarnAboutSyncHtml = true;
+        mState &= ~XML_HTTP_REQUEST_PARSEBODY;
+      } else if (mState & XML_HTTP_REQUEST_MULTIPART) {
+        // HTML parsing is supported only for non-multipart responses. The
+        // multipart implementation assumes that it's OK to start the next part
+        // immediately after the last part. That doesn't work with the HTML
+        // parser, because when OnStopRequest for one part has fired, the
+        // parser thread still hasn't posted back the runnables that make the
+        // parsing appear finished.
+        //
+        // On the other hand, multipart support seems to be a legacy feature,
+        // so it isn't clear that use cases justify adding support for deferring
+        // the multipart stream events between parts to accommodate the
+        // asynchronous nature of the HTML parser.
+        mWarnAboutMultipartHtml = true;
+        mState &= ~XML_HTTP_REQUEST_PARSEBODY;
+      } else {
+        mIsHtml = true;
+      }
+    } else if (type.Find("xml") == kNotFound) {
       mState &= ~XML_HTTP_REQUEST_PARSEBODY;
     }
   } else {
     // The request failed, so we shouldn't be parsing anyway
     mState &= ~XML_HTTP_REQUEST_PARSEBODY;
   }
 
   if (mState & XML_HTTP_REQUEST_PARSEBODY) {
@@ -1903,17 +1971,19 @@ nsXMLHttpRequest::OnStartRequest(nsIRequ
 
     // Create an empty document from it.  Here we have to cheat a little bit...
     // Setting the base URI to |baseURI| won't work if the document has a null
     // principal, so use mPrincipal when creating the document, then reset the
     // principal.
     const nsAString& emptyStr = EmptyString();
     nsCOMPtr<nsIScriptGlobalObject> global = do_QueryInterface(mOwner);
     rv = nsContentUtils::CreateDocument(emptyStr, emptyStr, nsnull, docURI,
-                                        baseURI, mPrincipal, global, false,
+                                        baseURI, mPrincipal, global,
+                                        mIsHtml ? DocumentFlavorHTML :
+                                                  DocumentFlavorLegacyGuess,
                                         getter_AddRefs(mResponseXML));
     NS_ENSURE_SUCCESS(rv, rv);
     nsCOMPtr<nsIDocument> responseDoc = do_QueryInterface(mResponseXML);
     responseDoc->SetPrincipal(documentPrincipal);
 
     if (IsSystemXHR()) {
       responseDoc->ForceEnableXULXBL();
     }
@@ -1988,32 +2058,31 @@ nsXMLHttpRequest::OnStopRequest(nsIReque
   // make sure to notify the listener if we were aborted
   // XXX in fact, why don't we do the cleanup below in this case??
   if (mState & XML_HTTP_REQUEST_UNSENT) {
     if (mXMLParserStreamListener)
       (void) mXMLParserStreamListener->OnStopRequest(request, ctxt, status);
     return NS_OK;
   }
 
-  nsCOMPtr<nsIParser> parser;
-
   // Is this good enough here?
   if (mState & XML_HTTP_REQUEST_PARSEBODY && mXMLParserStreamListener) {
-    parser = do_QueryInterface(mXMLParserStreamListener);
-    NS_ABORT_IF_FALSE(parser, "stream listener was expected to be a parser");
     mXMLParserStreamListener->OnStopRequest(request, ctxt, status);
   }
 
   mXMLParserStreamListener = nsnull;
   mReadRequest = nsnull;
   mContext = nsnull;
 
   // If we're received data since the last progress event, make sure to fire
-  // an event for it.
-  MaybeDispatchProgressEvents(true);
+  // an event for it, except in the HTML case, defer the last progress event
+  // until the parser is done.
+  if (!mIsHtml) {
+    MaybeDispatchProgressEvents(true);
+  }
 
   nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
   NS_ENSURE_TRUE(channel, NS_ERROR_UNEXPECTED);
 
   if (NS_SUCCEEDED(status) && mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB) {
     if (!mResponseBlob) {
       CreateResponseBlob(request);
     }
@@ -2048,39 +2117,60 @@ nsXMLHttpRequest::OnStopRequest(nsIReque
   if (NS_FAILED(status)) {
     // This can happen if the server is unreachable. Other possible
     // reasons are that the user leaves the page or hits the ESC key.
 
     mErrorLoad = true;
     mResponseXML = nsnull;
   }
 
-  NS_ASSERTION(!parser || parser->IsParserEnabled(),
-               "Parser blocked somehow?");
-
   // If we're uninitialized at this point, we encountered an error
   // earlier and listeners have already been notified. Also we do
   // not want to do this if we already completed.
   if (mState & (XML_HTTP_REQUEST_UNSENT |
                 XML_HTTP_REQUEST_DONE)) {
     return NS_OK;
   }
 
+  if (!mResponseXML) {
+    ChangeStateToDone();
+    return NS_OK;
+  }
+  if (mIsHtml) {
+    NS_ASSERTION(!(mState & XML_HTTP_REQUEST_SYNCLOOPING),
+      "We weren't supposed to support HTML parsing with XHR!");
+    nsCOMPtr<nsIDOMEventTarget> eventTarget = do_QueryInterface(mResponseXML);
+    nsEventListenerManager* manager = eventTarget->GetListenerManager(true);
+    manager->AddEventListenerByType(new nsXHRParseEndListener(this),
+                                    NS_LITERAL_STRING("DOMContentLoaded"),
+                                    NS_EVENT_FLAG_BUBBLE |
+                                    NS_EVENT_FLAG_SYSTEM_EVENT);
+    return NS_OK;
+  }
   // We might have been sent non-XML data. If that was the case,
   // we should null out the document member. The idea in this
   // check here is that if there is no document element it is not
   // an XML document. We might need a fancier check...
-  if (mResponseXML) {
-    nsCOMPtr<nsIDOMElement> root;
-    mResponseXML->GetDocumentElement(getter_AddRefs(root));
-    if (!root) {
-      mResponseXML = nsnull;
-    }
+  nsCOMPtr<nsIDOMElement> root;
+  mResponseXML->GetDocumentElement(getter_AddRefs(root));
+  if (!root) {
+    mResponseXML = nsnull;
   }
-
+  ChangeStateToDone();
+  return NS_OK;
+}
+
+void
+nsXMLHttpRequest::ChangeStateToDone()
+{
+  if (mIsHtml) {
+    // In the HTML case, this has to be deferred, because the parser doesn't
+    // do it's job synchronously.
+    MaybeDispatchProgressEvents(true);
+  }
   ChangeState(XML_HTTP_REQUEST_DONE, true);
 
   NS_NAMED_LITERAL_STRING(errorStr, ERROR_STR);
   NS_NAMED_LITERAL_STRING(loadStr, LOAD_STR);
   DispatchProgressEvent(this,
                         mErrorLoad ? errorStr : loadStr,
                         !mErrorLoad,
                         mLoadTransferred,
@@ -2096,18 +2186,16 @@ nsXMLHttpRequest::OnStopRequest(nsIReque
     // methods/members will not throw.
     // This matches what IE does.
     mChannel = nsnull;
   }
   else if (!(mState & XML_HTTP_REQUEST_GOT_FINAL_STOP)) {
     // We're a multipart request, so we're not done. Reset to opened.
     ChangeState(XML_HTTP_REQUEST_OPENED);
   }
-
-  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsXMLHttpRequest::SendAsBinary(const nsAString &aBody)
 {
   char *data = static_cast<char*>(NS_Alloc(aBody.Length() + 1));
   if (!data)
     return NS_ERROR_OUT_OF_MEMORY;
@@ -3045,21 +3133,23 @@ nsXMLHttpRequest::MaybeDispatchProgressE
                             mUploadTotal, mUploadProgress,
                             mUploadProgressMax);
     }
   } else {
     if (aFinalProgress) {
       mLoadTotal = mLoadTransferred;
       mLoadLengthComputable = true;
     }
-    mInLoadProgressEvent = true;
-    DispatchProgressEvent(this, NS_LITERAL_STRING(PROGRESS_STR),
-                          true, mLoadLengthComputable, mLoadTransferred,
-                          mLoadTotal, mLoadTransferred, mLoadTotal);
-    mInLoadProgressEvent = false;
+    if (aFinalProgress || !IsWaitingForHTMLCharset()) {
+      mInLoadProgressEvent = true;
+      DispatchProgressEvent(this, NS_LITERAL_STRING(PROGRESS_STR),
+                            true, mLoadLengthComputable, mLoadTransferred,
+                            mLoadTotal, mLoadTransferred, mLoadTotal);
+      mInLoadProgressEvent = false;
+    }
     if (mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT ||
         mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER) {
       mResponseBody.Truncate();
       mResponseText.Truncate();
       mResultArrayBuffer = nsnull;
     }
   }
 
--- a/content/base/src/nsXMLHttpRequest.h
+++ b/content/base/src/nsXMLHttpRequest.h
@@ -118,16 +118,17 @@ class nsXMLHttpRequest : public nsXHREve
                          public nsIStreamListener,
                          public nsIChannelEventSink,
                          public nsIProgressEventSink,
                          public nsIInterfaceRequestor,
                          public nsSupportsWeakReference,
                          public nsIJSNativeInitializer,
                          public nsITimerCallback
 {
+  friend class nsXHRParseEndListener;
 public:
   nsXMLHttpRequest();
   virtual ~nsXMLHttpRequest();
 
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIXMLHttpRequest
   NS_DECL_NSIXMLHTTPREQUEST
@@ -230,16 +231,20 @@ protected:
 
   nsresult GetInnerEventListener(nsRefPtr<nsDOMEventListenerWrapper>& aWrapper,
                                  nsIDOMEventListener** aListener);
 
   already_AddRefed<nsIHttpChannel> GetCurrentHttpChannel();
 
   bool IsSystemXHR();
 
+  bool IsWaitingForHTMLCharset();
+
+  void ChangeStateToDone();
+
   /**
    * Check if aChannel is ok for a cross-site request by making sure no
    * inappropriate headers are set, and no username/password is set.
    *
    * Also updates the XML_HTTP_REQUEST_USE_XSITE_AC bit.
    */
   nsresult CheckChannelForCrossSiteRequest(nsIChannel* aChannel);
 
@@ -342,16 +347,19 @@ protected:
   PRUint64 mUploadProgress; // For legacy
   PRUint64 mUploadProgressMax; // For legacy
 
   bool mErrorLoad;
 
   bool mTimerIsActive;
   bool mProgressEventWasDelayed;
   bool mLoadLengthComputable;
+  bool mIsHtml;
+  bool mWarnAboutMultipartHtml;
+  bool mWarnAboutSyncHtml;
   PRUint64 mLoadTotal; // 0 if not known.
   PRUint64 mLoadTransferred;
   nsCOMPtr<nsITimer> mProgressNotifier;
 
   bool mFirstStartRequestSeen;
   bool mInLoadProgressEvent;
   
   nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback;
@@ -427,9 +435,29 @@ protected:
   // Use nsDOMProgressEvent so that we can forward
   // most of the method calls easily.
   nsRefPtr<nsDOMProgressEvent> mInner;
   nsCOMPtr<nsPIDOMWindow> mWindow;
   PRUint64 mCurProgress;
   PRUint64 mMaxProgress;
 };
 
+class nsXHRParseEndListener : public nsIDOMEventListener
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_IMETHOD HandleEvent(nsIDOMEvent *event)
+  {
+    nsCOMPtr<nsIXMLHttpRequest> xhr = do_QueryReferent(mXHR);
+    if (xhr) {
+      static_cast<nsXMLHttpRequest*>(xhr.get())->ChangeStateToDone();
+    }
+    mXHR = nsnull;
+    return NS_OK;
+  }
+  nsXHRParseEndListener(nsIXMLHttpRequest* aXHR)
+    : mXHR(do_GetWeakReference(aXHR)) {}
+  virtual ~nsXHRParseEndListener() {}
+private:
+  nsWeakPtr mXHR;
+};
+
 #endif
--- a/content/base/test/Makefile.in
+++ b/content/base/test/Makefile.in
@@ -500,16 +500,22 @@ include $(topsrcdir)/config/rules.mk
 		accesscontrol.resource^headers^ \
 		invalid_accesscontrol.resource \
 		invalid_accesscontrol.resource^headers^ \
 		test_xhr_progressevents.html \
 		progressserver.sjs \
 		somedatas.resource \
 		somedatas.resource^headers^ \
 		delayedServerEvents.sjs \
+		test_html_in_xhr.html \
+		file_html_in_xhr.html \
+		file_html_in_xhr2.html \
+		file_html_in_xhr3.html \
+		file_html_in_xhr.sjs \
+		file_html_in_xhr_slow.sjs \
 		test_bug664916.html \
 		test_bug666604.html \
 		test_bug675121.html \
 		file_bug675121.sjs \
 		test_bug675166.html \
 		test_bug682554.html \
 		test_bug682592.html \
 		bug682592-subframe.html \
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_html_in_xhr.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html><!--  -->
+<meta charset="Windows-1251">
+<script>
+document.documentElement.setAttribute("data-fail", "FAIL");
+</script>
+<script src="file_html_in_xhr.sjs"></script>
+<script src="file_html_in_xhr.sjs" defer></script>
+<script src="file_html_in_xhr.sjs" async></script>
+<link type="stylesheet" href="file_html_in_xhr.sjs">
+<body onload='document.documentElement.setAttribute("data-fail", "FAIL");'>
+<img src="file_html_in_xhr.sjs">
+<iframe src="file_html_in_xhr.sjs"></iframe>
+<video poster="file_html_in_xhr.sjs" src="file_html_in_xhr.sjs"></video>
+<object data="file_html_in_xhr.sjs"></object>
+<noscript><div></div></noscript>
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_html_in_xhr.sjs
@@ -0,0 +1,15 @@
+function handleRequest(request, response)
+{
+  response.setHeader("Content-Type", "text/javascript", false);
+  if (request.queryString.indexOf("report") != -1) {
+    if (getState("loaded") == "loaded") {
+      response.write("ok(false, 'This script was not supposed to get fetched.'); continueAfterReport();");
+    } else {
+      response.write("ok(true, 'This script was not supposed to get fetched.'); continueAfterReport();");      
+    }
+  } else {
+    setState("loaded", "loaded");
+    response.write('document.documentElement.setAttribute("data-fail", "FAIL");');
+  }
+}
+
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_html_in_xhr2.html
@@ -0,0 +1,1 @@
+<meta charset="windows-1251">
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_html_in_xhr3.html
@@ -0,0 +1,1 @@
+SUCCESS
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_html_in_xhr_slow.sjs
@@ -0,0 +1,24 @@
+var timer;
+
+function handleRequest(request, response)
+{
+  var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"]
+    .createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
+  converter.charset = "windows-1251";
+  var stream = converter.convertToInputStream("\u042E");
+  var out = response.bodyOutputStream;
+  response.setHeader("Cache-Control", "no-cache", false);
+  response.setHeader("Content-Type", "text/html", false);
+  out.writeFrom(stream, 1);
+  var firstPart = "<meta charset='windows";
+  out.write(firstPart, firstPart.length);
+  out.flush();
+  response.processAsync();
+  timer = Components.classes["@mozilla.org/timer;1"]
+    .createInstance(Components.interfaces.nsITimer);
+  timer.initWithCallback(function() {
+      response.write("-1251'>");      
+      response.finish();
+    }, 500, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
+}
+
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_html_in_xhr.html
@@ -0,0 +1,113 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=651072
+-->
+<head>
+  <title>Test for Bug 651072</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload=runTest();>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=651072">Mozilla Bug 651072</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 651072 **/
+SimpleTest.waitForExplicitFinish();
+
+var xhr = new XMLHttpRequest();
+
+function runTest() {
+  xhr.onreadystatechange = function() {
+    if (this.readyState == 4) {
+      ok(this.responseXML, "Should have gotten responseXML");
+      is(this.responseXML.characterSet, "windows-1251", "Wrong character encoding");
+      is(this.responseXML.documentElement.firstChild.data, " \u042E ", "Decoded using the wrong encoding.");
+      is(this.responseText.indexOf("\u042E"), 27, "Bad responseText");
+      is(this.responseXML.getElementsByTagName("div").length, 1, "There should be one div.");
+      ok(!this.responseXML.documentElement.hasAttribute("data-fail"), "Should not have a data-fail attribute.");
+      var scripts = this.responseXML.getElementsByTagName("script");
+      is(scripts.length, 4, "Unexpected number of scripts.");
+      while (scripts.length) {
+        // These should not run when moved to another doc
+        document.body.appendChild(scripts[0]);
+      }
+      var s = document.createElement("script");
+      s.src = "file_html_in_xhr.sjs?report=1";
+      document.body.appendChild(s);
+    }
+  }
+  xhr.open("GET", "file_html_in_xhr.html", true);
+  xhr.send();
+}
+
+function continueAfterReport() {
+  ok(!document.documentElement.hasAttribute("data-fail"), "Should not have a data-fail attribute on mochitest doc.");  
+  xhr = new XMLHttpRequest();
+  xhr.onprogress = function() {
+    ok(this.responseText, "Got falsy responseText");
+    if (this.responseText) {
+      ok(this.responseText.length, "Got zero-length responseText");
+      if (this.responseText.length) {
+        is(this.responseText.charCodeAt(0), 0x042E, "Wrong character encoding for slow text");
+      }
+    }
+  }
+  xhr.onreadystatechange = function() {
+    if (this.readyState == 4) {
+      testNonParsingText();
+    }
+  }
+  xhr.open("GET", "file_html_in_xhr_slow.sjs");
+  xhr.send();   
+}
+
+function testNonParsingText() {
+  xhr = new XMLHttpRequest();
+  xhr.onreadystatechange = function() {
+    if (this.readyState == 4) {
+      is(this.responseText.indexOf("\u042E"), -1, "Honored meta in text mode.");
+      is(this.responseText.indexOf("\uFFFD"), 29, "Honored meta in text mode 2.");
+      testChunkedText();
+    }
+  }
+  xhr.open("GET", "file_html_in_xhr2.html");
+  xhr.responseType = "text";
+  xhr.send();   
+}
+
+function testChunkedText() {
+  xhr = new XMLHttpRequest();
+  xhr.onprogress = function() {
+    is(this.responseText.indexOf("\u042E"), -1, "Honored meta in chunked text mode.");
+  }
+  xhr.onreadystatechange = function() {
+    if (this.readyState == 4) {
+      testSyncXHR();
+    }
+  }
+  xhr.open("GET", "file_html_in_xhr2.html");
+  xhr.responseType = "moz-chunked-text";
+  xhr.send();   
+}
+
+function testSyncXHR() {
+  xhr = new XMLHttpRequest();
+  xhr.open("GET", "file_html_in_xhr3.html", false);
+  xhr.send();   
+  is(xhr.responseText, "SUCCESS\n", "responseText should be ready by now");
+  is(xhr.responseXML, null, "responseXML should be null in the sync case");
+  SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
+
--- a/content/html/document/src/nsHTMLDocument.cpp
+++ b/content/html/document/src/nsHTMLDocument.cpp
@@ -694,17 +694,17 @@ nsHTMLDocument::StartDocumentLoad(const 
   
   if (loadAsHtml5 && !viewSource &&
       (!(contentType.EqualsLiteral("text/html") || plainText) &&
       aCommand && !nsCRT::strcmp(aCommand, "view"))) {
     loadAsHtml5 = false;
   }
   
   // TODO: Proper about:blank treatment is bug 543435
-  if (loadAsHtml5 && !viewSource) {
+  if (loadAsHtml5 && aCommand && !nsCRT::strcmp(aCommand, "view")) {
     // mDocumentURI hasn't been set, yet, so get the URI from the channel
     nsCOMPtr<nsIURI> uri;
     aChannel->GetOriginalURI(getter_AddRefs(uri));
     // Adapted from nsDocShell:
     // GetSpec can be expensive for some URIs, so check the scheme first.
     bool isAbout = false;
     if (uri && NS_SUCCEEDED(uri->SchemeIs("about", &isAbout)) && isAbout) {
       nsCAutoString str;
@@ -766,19 +766,16 @@ nsHTMLDocument::StartDocumentLoad(const 
   // content viewer set up yet, and therefore do not have a useful
   // mParentDocument.
 
   // in this block of code, if we get an error result, we return it
   // but if we get a null pointer, that's perfectly legal for parent
   // and parentContentViewer
   nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(aContainer));
 
-  // No support yet for docshell-less HTML
-  NS_ENSURE_TRUE(docShell || !IsHTML(), NS_ERROR_FAILURE);
-
   nsCOMPtr<nsIDocShellTreeItem> docShellAsItem(do_QueryInterface(docShell));
 
   nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
   if (docShellAsItem) {
     docShellAsItem->GetSameTypeParent(getter_AddRefs(parentAsItem));
   }
 
   nsCOMPtr<nsIDocShell> parent(do_QueryInterface(parentAsItem));
@@ -805,19 +802,16 @@ nsHTMLDocument::StartDocumentLoad(const 
      muCV = do_QueryInterface(cv);
   } else {
     muCV = do_QueryInterface(parentContentViewer);
     if (muCV) {
       muCVIsParent = true;
     }
   }
 
-  nsCAutoString scheme;
-  uri->GetScheme(scheme);
-
   nsCAutoString urlSpec;
   uri->GetSpec(urlSpec);
 #ifdef DEBUG_charset
   printf("Determining charset for %s\n", urlSpec.get());
 #endif
 
   // These are the charset source and charset for our document
   PRInt32 charsetSource;
@@ -825,18 +819,19 @@ nsHTMLDocument::StartDocumentLoad(const 
 
   // These are the charset source and charset for the parser.  This can differ
   // from that for the document if the channel is a wyciwyg channel.
   PRInt32 parserCharsetSource;
   nsCAutoString parserCharset;
 
   nsCOMPtr<nsIWyciwygChannel> wyciwygChannel;
   
-  if (!IsHTML()) {
-    charsetSource = kCharsetFromDocTypeDefault;
+  if (!IsHTML() || !docShell) { // no docshell for text/html XHR
+    charsetSource = IsHTML() ? kCharsetFromWeakDocTypeDefault
+                             : kCharsetFromDocTypeDefault;
     charset.AssignLiteral("UTF-8");
     TryChannelCharset(aChannel, charsetSource, charset);
     parserCharsetSource = charsetSource;
     parserCharset = charset;
   } else {
     NS_ASSERTION(docShell && docShellAsItem, "Unexpected null value");
     
     nsCOMPtr<nsIDocumentCharsetInfo> dcInfo;
@@ -941,16 +936,17 @@ nsHTMLDocument::StartDocumentLoad(const 
     muCV->SetPrevDocCharacterSet(charset);
 
   if (cachingChan) {
     NS_ASSERTION(charset == parserCharset,
                  "How did those end up different here?  wyciwyg channels are "
                  "not nsICachingChannel");
     rv = cachingChan->SetCacheTokenCachedCharset(charset);
     NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "cannot SetMetaDataElement");
+    rv = NS_OK; // don't propagate error
   }
 
   // Set the parser as the stream listener for the document loader...
   if (mParser) {
     rv = NS_OK;
     nsCOMPtr<nsIStreamListener> listener = mParser->GetStreamListener();
     listener.forget(aDocListener);
 
--- a/content/xml/document/src/nsXMLDocument.cpp
+++ b/content/xml/document/src/nsXMLDocument.cpp
@@ -100,31 +100,34 @@ NS_NewDOMDocument(nsIDOMDocument** aInst
                   const nsAString& aNamespaceURI, 
                   const nsAString& aQualifiedName, 
                   nsIDOMDocumentType* aDoctype,
                   nsIURI* aDocumentURI,
                   nsIURI* aBaseURI,
                   nsIPrincipal* aPrincipal,
                   bool aLoadedAsData,
                   nsIScriptGlobalObject* aEventObject,
-                  bool aSVGDocument)
+                  DocumentFlavor aFlavor)
 {
   // Note: can't require that aDocumentURI/aBaseURI/aPrincipal be non-null,
   // since at least one caller (XMLHttpRequest) doesn't have decent args to
   // pass in.
   
   nsresult rv;
 
   *aInstancePtrResult = nsnull;
 
   nsCOMPtr<nsIDocument> d;
   bool isHTML = false;
   bool isXHTML = false;
-  if (aSVGDocument) {
+  if (aFlavor == DocumentFlavorSVG) {
     rv = NS_NewSVGDocument(getter_AddRefs(d));
+  } else if (aFlavor == DocumentFlavorHTML) {
+    rv = NS_NewHTMLDocument(getter_AddRefs(d));
+    isHTML = true;
   } else if (aDoctype) {
     nsAutoString publicId, name;
     aDoctype->GetPublicId(publicId);
     if (publicId.IsEmpty()) {
       aDoctype->GetName(name);
     }
     if (name.EqualsLiteral("html") ||
         publicId.EqualsLiteral("-//W3C//DTD HTML 4.01//EN") ||
@@ -224,17 +227,17 @@ NS_NewXBLDocument(nsIDOMDocument** aInst
                   nsIURI* aDocumentURI,
                   nsIURI* aBaseURI,
                   nsIPrincipal* aPrincipal)
 {
   nsresult rv = NS_NewDOMDocument(aInstancePtrResult,
                                   NS_LITERAL_STRING("http://www.mozilla.org/xbl"),
                                   NS_LITERAL_STRING("bindings"), nsnull,
                                   aDocumentURI, aBaseURI, aPrincipal, false,
-                                  nsnull, false);
+                                  nsnull, DocumentFlavorLegacyGuess);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIDocument> idoc = do_QueryInterface(*aInstancePtrResult);
   nsDocument* doc = static_cast<nsDocument*>(idoc.get());
   doc->SetLoadedAsInteractiveData(true);
   doc->SetReadyStateInternal(nsIDocument::READYSTATE_COMPLETE);
 
   return NS_OK;
--- a/dom/locales/en-US/chrome/dom/dom.properties
+++ b/dom/locales/en-US/chrome/dom/dom.properties
@@ -110,8 +110,10 @@ TextContentWarning=Use of attributes' te
 EnablePrivilegeWarning=Use of enablePrivilege is deprecated.  Please use code that runs with the system principal (e.g. an extension) instead.
 PositionWarning=Use of XMLHttpRequest's progress events' position attribute is deprecated.
 TotalSizeWarning=Use of XMLHttpRequest's progress events' totalSize attribute is deprecated.
 nsIJSONDecodeDeprecatedWarning=nsIJSON.decode is deprecated.  Please use JSON.parse instead.
 nsIJSONEncodeDeprecatedWarning=nsIJSON.encode is deprecated.  Please use JSON.stringify instead.
 nsIDOMWindowInternalWarning=Use of nsIDOMWindowInternal is deprecated. Use nsIDOMWindow instead.
 InputEncodingWarning=Use of inputEncoding is deprecated.
 GlobalStorageWarning=Use of globalStorage is deprecated. Please use localStorage instead.
+HTMLMultipartXHRWarning=HTML parsing in XMLHttpRequest is not supported for multipart responses.
+HTMLSyncXHRWarning=HTML parsing in XMLHttpRequest is not supported in the synchronous mode.
--- a/parser/html/nsHtml5Parser.cpp
+++ b/parser/html/nsHtml5Parser.cpp
@@ -123,18 +123,20 @@ NS_IMETHODIMP_(void)
 nsHtml5Parser::GetCommand(nsCString& aCommand)
 {
   aCommand.Assign("view");
 }
 
 NS_IMETHODIMP_(void)
 nsHtml5Parser::SetCommand(const char* aCommand)
 {
-  NS_ASSERTION(!strcmp(aCommand, "view") || !strcmp(aCommand, "view-source"),
-      "Parser command was not view");
+  NS_ASSERTION(!strcmp(aCommand, "view") ||
+               !strcmp(aCommand, "view-source") ||
+               !strcmp(aCommand, kLoadAsData),
+               "Unsupported parser command");
 }
 
 NS_IMETHODIMP_(void)
 nsHtml5Parser::SetCommand(eParserCommands aParserCommand)
 {
   NS_ASSERTION(aParserCommand == eViewNormal, 
                "Parser command was not eViewNormal.");
 }
@@ -707,16 +709,18 @@ nsHtml5Parser::MarkAsNotScriptCreated(co
   NS_PRECONDITION(!mStreamParser, "Must not call this twice.");
   eParserMode mode = NORMAL;
   if (!nsCRT::strcmp(aCommand, "view-source")) {
     mode = VIEW_SOURCE_HTML;
   } else if (!nsCRT::strcmp(aCommand, "view-source-xml")) {
     mode = VIEW_SOURCE_XML;
   } else if (!nsCRT::strcmp(aCommand, "plain-text")) {
     mode = PLAIN_TEXT;
+  } else if (!nsCRT::strcmp(aCommand, kLoadAsData)) {
+    mode = LOAD_AS_DATA;
   }
 #ifdef DEBUG
   else {
     NS_ASSERTION(!nsCRT::strcmp(aCommand, "view"),
         "Unsupported parser command!");
   }
 #endif
   mStreamParser = new nsHtml5StreamParser(mExecutor, this, mode);
@@ -824,16 +828,19 @@ nsHtml5Parser::Initialize(nsIDocument* a
                           nsISupports* aContainer,
                           nsIChannel* aChannel)
 {
   return mExecutor->Init(aDoc, aURI, aContainer, aChannel);
 }
 
 void
 nsHtml5Parser::StartTokenizer(bool aScriptingEnabled) {
+  if (!aScriptingEnabled) {
+    mExecutor->PreventScriptExecution();
+  }
   mTreeBuilder->setScriptingEnabled(aScriptingEnabled);
   mTokenizer->start();
 }
 
 void
 nsHtml5Parser::InitializeDocWriteParserState(nsAHtml5TreeBuilderState* aState,
                                              PRInt32 aLine)
 {
--- a/parser/html/nsHtml5StreamParser.cpp
+++ b/parser/html/nsHtml5StreamParser.cpp
@@ -351,16 +351,20 @@ nsHtml5StreamParser::SetupDecodingFromBo
   mBomState = BOM_SNIFFING_OVER;
   return rv;
 }
 
 void
 nsHtml5StreamParser::SniffBOMlessUTF16BasicLatin(const PRUint8* aFromSegment,
                                                  PRUint32 aCountToSniffingLimit)
 {
+  // Avoid underspecified heuristic craziness for XHR
+  if (mMode == LOAD_AS_DATA) {
+    return;
+  }
   // Make sure there's enough data. Require room for "<title></title>"
   if (mSniffingLength + aCountToSniffingLimit < 30) {
     return;
   }
   // even-numbered bytes tracked at 0, odd-numbered bytes tracked at 1
   bool byteZero[2] = { false, false };
   bool byteNonZero[2] = { false, false };
   PRUint32 i = 0;
@@ -604,16 +608,25 @@ nsHtml5StreamParser::FinalizeSniffing(co
     }
     // fall thru; callback may have changed charset  
   }
   if (mCharsetSource == kCharsetUninitialized) {
     // Hopefully this case is never needed, but dealing with it anyway
     mCharset.AssignLiteral("windows-1252");
     mCharsetSource = kCharsetFromWeakDocTypeDefault;
     mTreeBuilder->SetDocumentCharset(mCharset, mCharsetSource);
+  } else if (mMode == LOAD_AS_DATA &&
+             mCharsetSource == kCharsetFromWeakDocTypeDefault) {
+    NS_ASSERTION(mReparseForbidden, "Reparse should be forbidden for XHR");
+    NS_ASSERTION(!mFeedChardet, "Should not feed chardet for XHR");
+    NS_ASSERTION(mCharset.EqualsLiteral("UTF-8"),
+                 "XHR should default to UTF-8");
+    // Now mark charset source as non-weak to signal that we have a decision
+    mCharsetSource = kCharsetFromDocTypeDefault;
+    mTreeBuilder->SetDocumentCharset(mCharset, mCharsetSource);
   }
   return SetupDecodingAndWriteSniffingBufferAndCurrentSegment(aFromSegment, aCount, aWriteCount);
 }
 
 nsresult
 nsHtml5StreamParser::SniffStreamBytes(const PRUint8* aFromSegment,
                                       PRUint32 aCount,
                                       PRUint32* aWriteCount)
@@ -685,25 +698,27 @@ nsHtml5StreamParser::SniffStreamBytes(co
         break;
       default:
         mBomState = BOM_SNIFFING_OVER;
         break;
     }
   }
   // if we get here, there either was no BOM or the BOM sniffing isn't complete yet
   
-  if (!mMetaScanner && (mMode == NORMAL || mMode == VIEW_SOURCE_HTML)) {
+  if (!mMetaScanner && (mMode == NORMAL ||
+                        mMode == VIEW_SOURCE_HTML ||
+                        mMode == LOAD_AS_DATA)) {
     mMetaScanner = new nsHtml5MetaScanner();
   }
   
   if (mSniffingLength + aCount >= NS_HTML5_STREAM_PARSER_SNIFFING_BUFFER_SIZE) {
     // this is the last buffer
     PRUint32 countToSniffingLimit =
         NS_HTML5_STREAM_PARSER_SNIFFING_BUFFER_SIZE - mSniffingLength;
-    if (mMode == NORMAL || mMode == VIEW_SOURCE_HTML) {
+    if (mMode == NORMAL || mMode == VIEW_SOURCE_HTML || mMode == LOAD_AS_DATA) {
       nsHtml5ByteReadable readable(aFromSegment, aFromSegment +
           countToSniffingLimit);
       mMetaScanner->sniff(&readable, getter_AddRefs(mUnicodeDecoder), mCharset);
       if (mUnicodeDecoder) {
         mUnicodeDecoder->SetInputErrorBehavior(
             nsIUnicodeDecoder::kOnError_Recover);
         // meta scan successful
         mCharsetSource = kCharsetFromMetaPrescan;
@@ -714,17 +729,17 @@ nsHtml5StreamParser::SniffStreamBytes(co
             aWriteCount);
       }
     }
     return FinalizeSniffing(aFromSegment, aCount, aWriteCount,
         countToSniffingLimit);
   }
 
   // not the last buffer
-  if (mMode == NORMAL || mMode == VIEW_SOURCE_HTML) {
+  if (mMode == NORMAL || mMode == VIEW_SOURCE_HTML || mMode == LOAD_AS_DATA) {
     nsHtml5ByteReadable readable(aFromSegment, aFromSegment + aCount);
     mMetaScanner->sniff(&readable, getter_AddRefs(mUnicodeDecoder), mCharset);
     if (mUnicodeDecoder) {
       // meta scan successful
       mUnicodeDecoder->SetInputErrorBehavior(
           nsIUnicodeDecoder::kOnError_Recover);
       mCharsetSource = kCharsetFromMetaPrescan;
       mFeedChardet = false;
@@ -864,17 +879,18 @@ nsHtml5StreamParser::OnStartRequest(nsIR
 
   mStreamState = STREAM_BEING_READ;
 
   if (mMode == VIEW_SOURCE_HTML || mMode == VIEW_SOURCE_XML) {
     mTokenizer->StartViewSource();
   }
   // For View Source, the parser should run with scripts "enabled" if a normal
   // load would have scripts enabled.
-  bool scriptingEnabled = mExecutor->IsScriptEnabled();
+  bool scriptingEnabled = mMode == LOAD_AS_DATA ?
+                                   false : mExecutor->IsScriptEnabled();
   mOwner->StartTokenizer(scriptingEnabled);
   mTreeBuilder->setScriptingEnabled(scriptingEnabled);
   mTokenizer->start();
   mExecutor->Start();
   mExecutor->StartReadingFromStage();
 
   if (mMode == PLAIN_TEXT) {
     mTreeBuilder->StartPlainText();
--- a/parser/html/nsHtml5StreamParser.h
+++ b/parser/html/nsHtml5StreamParser.h
@@ -74,17 +74,22 @@ enum eParserMode {
   /**
    * View document as XML source
    */
   VIEW_SOURCE_XML,
 
   /**
    * View document as plain text
    */
-  PLAIN_TEXT
+  PLAIN_TEXT,
+
+  /**
+   * Load as data (XHR)
+   */
+  LOAD_AS_DATA
 };
 
 enum eBomState {
   /**
    * BOM sniffing hasn't started.
    */
   BOM_SNIFFING_NOT_STARTED = 0,
 
--- a/parser/html/nsHtml5TreeOpExecutor.cpp
+++ b/parser/html/nsHtml5TreeOpExecutor.cpp
@@ -730,20 +730,20 @@ nsHtml5TreeOpExecutor::RunScript(nsICont
   if (!mParser) {
     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.
     return;
   }
   
+  if (mPreventScriptExecution) {
+    sele->PreventExecution();
+  }
   if (mFragmentMode) {
-    if (mPreventScriptExecution) {
-      sele->PreventExecution();
-    }
     return;
   }
 
   if (sele->GetScriptDeferred() || sele->GetScriptAsync()) {
     DebugOnly<bool> block = sele->AttemptToExecute();
     NS_ASSERTION(!block, "Defer or async script tried to block.");
     return;
   }
--- a/parser/html/nsHtml5TreeOpExecutor.h
+++ b/parser/html/nsHtml5TreeOpExecutor.h
@@ -148,17 +148,17 @@ class nsHtml5TreeOpExecutor : public nsC
      * Unimplemented. For interface compat only.
      */
     NS_IMETHOD WillParse();
 
     /**
      * 
      */
     NS_IMETHOD WillBuildModel(nsDTDMode aDTDMode) {
-      NS_ASSERTION(GetDocument()->GetScriptGlobalObject(), 
+      NS_ASSERTION(!mDocShell || GetDocument()->GetScriptGlobalObject(),
                    "Script global object not ready");
       mDocument->AddObserver(this);
       WillBuildModelImpl();
       GetDocument()->BeginLoad();
       return NS_OK;
     }
 
     /**
@@ -248,16 +248,20 @@ class nsHtml5TreeOpExecutor : public nsC
      * executing; don't set to false when parsing a fragment directly into
      * a document--only when parsing to an actual DOM fragment
      */
     void EnableFragmentMode(bool aPreventScriptExecution) {
       mFragmentMode = true;
       mPreventScriptExecution = aPreventScriptExecution;
     }
     
+    void PreventScriptExecution() {
+      mPreventScriptExecution = true;
+    }
+
     bool IsFragmentMode() {
       return mFragmentMode;
     }
 
     /**
      * Marks this parser as broken and tells the stream parser (if any) to
      * terminate.
      */
--- a/parser/htmlparser/public/nsIParser.h
+++ b/parser/htmlparser/public/nsIParser.h
@@ -86,17 +86,17 @@ enum eParserDocType {
 };
 
 
 // define Charset source constants
 // note: the value order defines the priority; higher numbers take priority
 #define kCharsetUninitialized           0
 #define kCharsetFromWeakDocTypeDefault  1
 #define kCharsetFromUserDefault         2
-#define kCharsetFromDocTypeDefault      3
+#define kCharsetFromDocTypeDefault      3 // This and up confident for XHR
 #define kCharsetFromCache               4
 #define kCharsetFromParentFrame         5
 #define kCharsetFromAutoDetection       6
 #define kCharsetFromHintPrevDoc         7
 #define kCharsetFromMetaPrescan         8 // this one and smaller: HTML5 Tentative
 #define kCharsetFromMetaTag             9 // this one and greater: HTML5 Confident
 #define kCharsetFromIrreversibleAutoDetection 10
 #define kCharsetFromByteOrderMark      11