Bug 651072 - Support HTML parsing in XMLHttpRequest. r=smaug.

This commit is contained in:
Henri Sivonen 2011-11-16 09:38:51 +02:00
parent efbbb37472
commit 09985ff269
23 changed files with 384 additions and 65 deletions

View File

@ -1107,7 +1107,7 @@ public:
* @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,
@ -1117,7 +1117,7 @@ public:
nsIURI* aBaseURI,
nsIPrincipal* aPrincipal,
nsIScriptGlobalObject* aScriptObject,
bool aSVGDocument,
DocumentFlavor aFlavor,
nsIDOMDocument** aResult);
/**

View File

@ -130,6 +130,13 @@ class Element;
// 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
@ -1891,7 +1898,7 @@ NS_NewDOMDocument(nsIDOMDocument** aInstancePtrResult,
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.

View File

@ -3706,12 +3706,12 @@ nsContentUtils::CreateDocument(const nsAString& aNamespaceURI,
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);

View File

@ -188,7 +188,9 @@ nsDOMParser::ParseFromStream(nsIInputStream *stream,
rv = nsContentUtils::CreateDocument(EmptyString(), EmptyString(), nsnull,
mDocumentURI, mBaseURI,
mOriginalPrincipal,
scriptHandlingObject, svg,
scriptHandlingObject,
svg ? DocumentFlavorSVG :
DocumentFlavorLegacyGuess,
getter_AddRefs(domDocument));
NS_ENSURE_SUCCESS(rv, rv);

View File

@ -1447,7 +1447,9 @@ nsDOMImplementation::CreateDocument(const nsAString& aNamespaceURI,
return nsContentUtils::CreateDocument(aNamespaceURI, aQualifiedName, aDoctype,
mDocumentURI, mBaseURI,
mOwner->NodePrincipal(),
scriptHandlingObject, false, aReturn);
scriptHandlingObject,
DocumentFlavorLegacyGuess,
aReturn);
}
NS_IMETHODIMP
@ -1479,7 +1481,8 @@ nsDOMImplementation::CreateHTMLDocument(const nsAString& aTitle,
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);

View File

@ -70,7 +70,6 @@
#include "nsCExternalHandlerService.h"
#include "nsIVariant.h"
#include "xpcprivate.h"
#include "nsIParser.h"
#include "nsStringStream.h"
#include "nsIStreamConverterService.h"
#include "nsICachingChannel.h"
@ -154,6 +153,8 @@ using namespace mozilla;
#define NS_PROGRESS_EVENT_INTERVAL 50
NS_IMPL_ISUPPORTS1(nsXHRParseEndListener, nsIDOMEventListener)
class nsResumeTimeoutsEvent : public nsRunnable
{
public:
@ -429,7 +430,10 @@ nsXMLHttpRequest::nsXMLHttpRequest()
mUploadProgress(0), mUploadProgressMax(0),
mErrorLoad(false), mTimerIsActive(false),
mProgressEventWasDelayed(false),
mLoadLengthComputable(false), mLoadTotal(0),
mLoadLengthComputable(false),
mIsHtml(false),
mWarnAboutMultipartHtml(false),
mLoadTotal(0),
mFirstStartRequestSeen(false),
mInLoadProgressEvent(false),
mResultJSON(JSVAL_VOID),
@ -719,7 +723,20 @@ nsXMLHttpRequest::GetResponseXML(nsIDOMDocument **aResponseXML)
*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 Events",
mOwner->WindowID());
}
return NS_OK;
}
@ -850,7 +867,7 @@ NS_IMETHODIMP nsXMLHttpRequest::GetResponseText(nsAString& aResponseText)
// 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;
@ -1446,6 +1463,16 @@ 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)
{
@ -1878,6 +1905,8 @@ nsXMLHttpRequest::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
parseBody = !method.EqualsLiteral("HEAD");
}
mIsHtml = false;
mWarnAboutMultipartHtml = 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
@ -1886,7 +1915,25 @@ nsXMLHttpRequest::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
nsCAutoString type;
channel->GetContentType(type);
if (type.Find("xml") == kNotFound) {
if (type.EqualsLiteral("text/html")) {
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 {
@ -1911,7 +1958,9 @@ nsXMLHttpRequest::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
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);
@ -1996,12 +2045,8 @@ nsXMLHttpRequest::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult
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);
}
@ -2010,8 +2055,11 @@ nsXMLHttpRequest::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult
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);
@ -2046,8 +2094,6 @@ nsXMLHttpRequest::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult
mChannelEventSink = nsnull;
mProgressEventSink = nsnull;
mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
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.
@ -2056,29 +2102,51 @@ nsXMLHttpRequest::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult
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)) {
// If we never get far enough to call ChangeStateToDone(), we must be
// careful to stop sync looping.
mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
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;
if (mIsHtml) {
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);
} else {
// 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 (!mIsHtml && mResponseXML) {
nsCOMPtr<nsIDOMElement> root;
mResponseXML->GetDocumentElement(getter_AddRefs(root));
if (!root) {
mResponseXML = nsnull;
}
}
ChangeStateToDone();
}
return NS_OK;
}
void
nsXMLHttpRequest::ChangeStateToDone()
{
mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
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);
@ -2104,8 +2172,6 @@ nsXMLHttpRequest::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult
// We're a multipart request, so we're not done. Reset to opened.
ChangeState(XML_HTTP_REQUEST_OPENED);
}
return NS_OK;
}
NS_IMETHODIMP
@ -3053,11 +3119,13 @@ nsXMLHttpRequest::MaybeDispatchProgressEvents(bool 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();

View File

@ -123,6 +123,7 @@ class nsXMLHttpRequest : public nsXHREventTarget,
public nsIJSNativeInitializer,
public nsITimerCallback
{
friend class nsXHRParseEndListener;
public:
nsXMLHttpRequest();
virtual ~nsXMLHttpRequest();
@ -235,6 +236,10 @@ protected:
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.
@ -347,6 +352,8 @@ protected:
bool mTimerIsActive;
bool mProgressEventWasDelayed;
bool mLoadLengthComputable;
bool mIsHtml;
bool mWarnAboutMultipartHtml;
PRUint64 mLoadTotal; // 0 if not known.
PRUint64 mLoadTransferred;
nsCOMPtr<nsITimer> mProgressNotifier;
@ -432,4 +439,24 @@ protected:
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

View File

@ -505,6 +505,12 @@ _TEST_FILES2 = \
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 \

View File

@ -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>

View File

@ -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");');
}
}

View File

@ -0,0 +1 @@
<meta charset="windows-1251">Þ

View File

@ -0,0 +1 @@
SUCCESS

View File

@ -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);
}

View File

@ -0,0 +1,118 @@
<!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();
var runNumber = 0;
function runTest() {
runNumber++;
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]);
}
if (runNumber == 1) {
runTest();
} else {
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", runNumber == 1);
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");
SimpleTest.finish();
}
</script>
</pre>
</body>
</html>

View File

@ -699,7 +699,7 @@ nsHTMLDocument::StartDocumentLoad(const char* aCommand,
}
// 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));
@ -771,9 +771,6 @@ nsHTMLDocument::StartDocumentLoad(const char* aCommand,
// 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;
@ -810,9 +807,6 @@ nsHTMLDocument::StartDocumentLoad(const char* aCommand,
}
}
nsCAutoString scheme;
uri->GetScheme(scheme);
nsCAutoString urlSpec;
uri->GetSpec(urlSpec);
#ifdef DEBUG_charset
@ -830,8 +824,9 @@ nsHTMLDocument::StartDocumentLoad(const char* aCommand,
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;

View File

@ -105,7 +105,7 @@ NS_NewDOMDocument(nsIDOMDocument** aInstancePtrResult,
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
@ -118,8 +118,11 @@ NS_NewDOMDocument(nsIDOMDocument** aInstancePtrResult,
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);
@ -229,7 +232,7 @@ NS_NewXBLDocument(nsIDOMDocument** 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);

View File

@ -115,3 +115,4 @@ nsIJSONEncodeDeprecatedWarning=nsIJSON.encode is deprecated. Please use JSON.st
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.

View File

@ -128,8 +128,10 @@ nsHtml5Parser::GetCommand(nsCString& aCommand)
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)
@ -713,6 +715,8 @@ nsHtml5Parser::MarkAsNotScriptCreated(const char* aCommand)
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 {
@ -830,6 +834,9 @@ nsHtml5Parser::Initialize(nsIDocument* aDoc,
void
nsHtml5Parser::StartTokenizer(bool aScriptingEnabled) {
if (!aScriptingEnabled) {
mExecutor->PreventScriptExecution();
}
mTreeBuilder->setScriptingEnabled(aScriptingEnabled);
mTokenizer->start();
}

View File

@ -356,6 +356,10 @@ 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;
@ -609,6 +613,15 @@ nsHtml5StreamParser::FinalizeSniffing(const PRUint8* aFromSegment, // can be nul
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);
}
@ -690,7 +703,9 @@ nsHtml5StreamParser::SniffStreamBytes(const PRUint8* aFromSegment,
}
// 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();
}
@ -698,7 +713,7 @@ nsHtml5StreamParser::SniffStreamBytes(const PRUint8* aFromSegment,
// 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);
@ -719,7 +734,7 @@ nsHtml5StreamParser::SniffStreamBytes(const PRUint8* aFromSegment,
}
// 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) {
@ -869,7 +884,8 @@ nsHtml5StreamParser::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
}
// 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();

View File

@ -79,7 +79,12 @@ enum eParserMode {
/**
* View document as plain text
*/
PLAIN_TEXT
PLAIN_TEXT,
/**
* Load as data (XHR)
*/
LOAD_AS_DATA
};
enum eBomState {

View File

@ -737,10 +737,10 @@ nsHtml5TreeOpExecutor::RunScript(nsIContent* aScriptElement)
return;
}
if (mPreventScriptExecution) {
sele->PreventExecution();
}
if (mFragmentMode) {
if (mPreventScriptExecution) {
sele->PreventExecution();
}
return;
}

View File

@ -153,7 +153,7 @@ class nsHtml5TreeOpExecutor : public nsContentSink,
*
*/
NS_IMETHOD WillBuildModel(nsDTDMode aDTDMode) {
NS_ASSERTION(GetDocument()->GetScriptGlobalObject(),
NS_ASSERTION(!mDocShell || GetDocument()->GetScriptGlobalObject(),
"Script global object not ready");
mDocument->AddObserver(this);
WillBuildModelImpl();
@ -253,6 +253,10 @@ class nsHtml5TreeOpExecutor : public nsContentSink,
mPreventScriptExecution = aPreventScriptExecution;
}
void PreventScriptExecution() {
mPreventScriptExecution = true;
}
bool IsFragmentMode() {
return mFragmentMode;
}

View File

@ -91,7 +91,7 @@ enum eParserDocType {
#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