mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-29 15:52:07 +00:00
Bug 651072 - Support HTML parsing in XMLHttpRequest. r=smaug.
This commit is contained in:
parent
b39c6a08d9
commit
689c5fc755
@ -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);
|
||||
|
||||
/**
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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 \
|
||||
|
16
content/base/test/file_html_in_xhr.html
Normal file
16
content/base/test/file_html_in_xhr.html
Normal 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>
|
15
content/base/test/file_html_in_xhr.sjs
Normal file
15
content/base/test/file_html_in_xhr.sjs
Normal 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");');
|
||||
}
|
||||
}
|
||||
|
1
content/base/test/file_html_in_xhr2.html
Normal file
1
content/base/test/file_html_in_xhr2.html
Normal file
@ -0,0 +1 @@
|
||||
<meta charset="windows-1251">Þ
|
1
content/base/test/file_html_in_xhr3.html
Normal file
1
content/base/test/file_html_in_xhr3.html
Normal file
@ -0,0 +1 @@
|
||||
SUCCESS
|
24
content/base/test/file_html_in_xhr_slow.sjs
Normal file
24
content/base/test/file_html_in_xhr_slow.sjs
Normal 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);
|
||||
}
|
||||
|
118
content/base/test/test_html_in_xhr.html
Normal file
118
content/base/test/test_html_in_xhr.html
Normal 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>
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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.
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -79,7 +79,12 @@ enum eParserMode {
|
||||
/**
|
||||
* View document as plain text
|
||||
*/
|
||||
PLAIN_TEXT
|
||||
PLAIN_TEXT,
|
||||
|
||||
/**
|
||||
* Load as data (XHR)
|
||||
*/
|
||||
LOAD_AS_DATA
|
||||
};
|
||||
|
||||
enum eBomState {
|
||||
|
@ -737,10 +737,10 @@ nsHtml5TreeOpExecutor::RunScript(nsIContent* aScriptElement)
|
||||
return;
|
||||
}
|
||||
|
||||
if (mPreventScriptExecution) {
|
||||
sele->PreventExecution();
|
||||
}
|
||||
if (mFragmentMode) {
|
||||
if (mPreventScriptExecution) {
|
||||
sele->PreventExecution();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user