summaryrefslogtreecommitdiffstats
path: root/WebCore/xml/XMLHttpRequest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/xml/XMLHttpRequest.cpp')
-rw-r--r--WebCore/xml/XMLHttpRequest.cpp391
1 files changed, 85 insertions, 306 deletions
diff --git a/WebCore/xml/XMLHttpRequest.cpp b/WebCore/xml/XMLHttpRequest.cpp
index b62679b..dd3f361 100644
--- a/WebCore/xml/XMLHttpRequest.cpp
+++ b/WebCore/xml/XMLHttpRequest.cpp
@@ -23,6 +23,8 @@
#include "XMLHttpRequest.h"
#include "CString.h"
+#include "CrossOriginAccessControl.h"
+#include "CrossOriginPreflightResultCache.h"
#include "DOMImplementation.h"
#include "Document.h"
#include "Event.h"
@@ -31,23 +33,17 @@
#include "EventNames.h"
#include "File.h"
#include "HTTPParsers.h"
-#include "KURL.h"
-#include "KURLHash.h"
#include "ResourceError.h"
#include "ResourceRequest.h"
-#include "ScriptExecutionContext.h"
+#include "SecurityOrigin.h"
#include "Settings.h"
-#include "SystemTime.h"
#include "TextResourceDecoder.h"
#include "ThreadableLoader.h"
#include "XMLHttpRequestException.h"
#include "XMLHttpRequestProgressEvent.h"
#include "XMLHttpRequestUpload.h"
#include "markup.h"
-#include <wtf/CurrentTime.h>
-#include <wtf/Noncopyable.h>
#include <wtf/StdLibExtras.h>
-#include <wtf/Threading.h>
#if USE(JSC)
#include "JSDOMWindow.h"
@@ -55,14 +51,11 @@
namespace WebCore {
-typedef HashSet<String, CaseFoldingHash> HeadersSet;
-
struct XMLHttpRequestStaticData {
XMLHttpRequestStaticData();
String m_proxyHeaderPrefix;
String m_secHeaderPrefix;
HashSet<String, CaseFoldingHash> m_forbiddenRequestHeaders;
- HashSet<String, CaseFoldingHash> m_allowedCrossSiteResponseHeaders;
};
XMLHttpRequestStaticData::XMLHttpRequestStaticData()
@@ -71,76 +64,26 @@ XMLHttpRequestStaticData::XMLHttpRequestStaticData()
{
m_forbiddenRequestHeaders.add("accept-charset");
m_forbiddenRequestHeaders.add("accept-encoding");
+ m_forbiddenRequestHeaders.add("access-control-request-headers");
+ m_forbiddenRequestHeaders.add("access-control-request-method");
+ m_forbiddenRequestHeaders.add("authorization");
m_forbiddenRequestHeaders.add("connection");
m_forbiddenRequestHeaders.add("content-length");
m_forbiddenRequestHeaders.add("content-transfer-encoding");
+ m_forbiddenRequestHeaders.add("cookie");
+ m_forbiddenRequestHeaders.add("cookie2");
m_forbiddenRequestHeaders.add("date");
m_forbiddenRequestHeaders.add("expect");
m_forbiddenRequestHeaders.add("host");
m_forbiddenRequestHeaders.add("keep-alive");
+ m_forbiddenRequestHeaders.add("origin");
m_forbiddenRequestHeaders.add("referer");
m_forbiddenRequestHeaders.add("te");
m_forbiddenRequestHeaders.add("trailer");
m_forbiddenRequestHeaders.add("transfer-encoding");
m_forbiddenRequestHeaders.add("upgrade");
+ m_forbiddenRequestHeaders.add("user-agent");
m_forbiddenRequestHeaders.add("via");
-
- m_allowedCrossSiteResponseHeaders.add("cache-control");
- m_allowedCrossSiteResponseHeaders.add("content-language");
- m_allowedCrossSiteResponseHeaders.add("content-type");
- m_allowedCrossSiteResponseHeaders.add("expires");
- m_allowedCrossSiteResponseHeaders.add("last-modified");
- m_allowedCrossSiteResponseHeaders.add("pragma");
-}
-
-class PreflightResultCacheItem : Noncopyable {
-public:
- PreflightResultCacheItem(bool credentials)
- : m_absoluteExpiryTime(0)
- , m_credentials(credentials)
- {
- }
-
- bool parse(const ResourceResponse&);
- bool allowsCrossSiteMethod(const String&) const;
- bool allowsCrossSiteHeaders(const HTTPHeaderMap&) const;
- bool allowsRequest(bool includeCredentials, const String& method, const HTTPHeaderMap& requestHeaders) const;
-
-private:
- template<class HashType>
- static void addToAccessControlAllowList(const String& string, unsigned start, unsigned end, HashSet<String, HashType>&);
- template<class HashType>
- static bool parseAccessControlAllowList(const String& string, HashSet<String, HashType>&);
- static bool parseAccessControlMaxAge(const String& string, unsigned& expiryDelta);
-
- // FIXME: A better solution to holding onto the absolute expiration time might be
- // to start a timer for the expiration delta, that removes this from the cache when
- // it fires.
- double m_absoluteExpiryTime;
- bool m_credentials;
- HashSet<String> m_methods;
- HeadersSet m_headers;
-};
-
-class PreflightResultCache : Noncopyable {
-public:
- static PreflightResultCache& shared();
-
- void appendEntry(const String& origin, const KURL&, PreflightResultCacheItem*);
- bool canSkipPreflight(const String& origin, const KURL&, bool includeCredentials, const String& method, const HTTPHeaderMap& requestHeaders);
-
-private:
- PreflightResultCache() { }
-
- typedef HashMap<std::pair<String, KURL>, PreflightResultCacheItem*> PreflightResultHashMap;
-
- PreflightResultHashMap m_preflightHashMap;
- Mutex m_mutex;
-};
-
-static bool isOnAccessControlSimpleRequestHeaderWhitelist(const String& name)
-{
- return equalIgnoringCase(name, "accept") || equalIgnoringCase(name, "accept-language") || equalIgnoringCase(name, "content-type");
}
// Determines if a string is a valid token, as defined by
@@ -177,131 +120,6 @@ static bool isSetCookieHeader(const AtomicString& name)
return equalIgnoringCase(name, "set-cookie") || equalIgnoringCase(name, "set-cookie2");
}
-template<class HashType>
-void PreflightResultCacheItem::addToAccessControlAllowList(const String& string, unsigned start, unsigned end, HashSet<String, HashType>& set)
-{
- StringImpl* stringImpl = string.impl();
- if (!stringImpl)
- return;
-
- // Skip white space from start.
- while (start <= end && isSpaceOrNewline((*stringImpl)[start]))
- start++;
-
- // only white space
- if (start > end)
- return;
-
- // Skip white space from end.
- while (end && isSpaceOrNewline((*stringImpl)[end]))
- end--;
-
- // substringCopy() is called on the strings because the cache is accessed on multiple threads.
- set.add(string.substringCopy(start, end - start + 1));
-}
-
-
-template<class HashType>
-bool PreflightResultCacheItem::parseAccessControlAllowList(const String& string, HashSet<String, HashType>& set)
-{
- int start = 0;
- int end;
- while ((end = string.find(',', start)) != -1) {
- if (start == end)
- return false;
-
- addToAccessControlAllowList(string, start, end - 1, set);
- start = end + 1;
- }
- if (start != static_cast<int>(string.length()))
- addToAccessControlAllowList(string, start, string.length() - 1, set);
-
- return true;
-}
-
-bool PreflightResultCacheItem::parseAccessControlMaxAge(const String& string, unsigned& expiryDelta)
-{
- // FIXME: this will not do the correct thing for a number starting with a '+'
- bool ok = false;
- expiryDelta = string.toUIntStrict(&ok);
- return ok;
-}
-
-bool PreflightResultCacheItem::parse(const ResourceResponse& response)
-{
- m_methods.clear();
- if (!parseAccessControlAllowList(response.httpHeaderField("Access-Control-Allow-Methods"), m_methods))
- return false;
-
- m_headers.clear();
- if (!parseAccessControlAllowList(response.httpHeaderField("Access-Control-Allow-Headers"), m_headers))
- return false;
-
- unsigned expiryDelta = 0;
- if (!parseAccessControlMaxAge(response.httpHeaderField("Access-Control-Max-Age"), expiryDelta))
- expiryDelta = 5;
-
- m_absoluteExpiryTime = currentTime() + expiryDelta;
- return true;
-}
-
-bool PreflightResultCacheItem::allowsCrossSiteMethod(const String& method) const
-{
- return m_methods.contains(method) || method == "GET" || method == "POST";
-}
-
-bool PreflightResultCacheItem::allowsCrossSiteHeaders(const HTTPHeaderMap& requestHeaders) const
-{
- HTTPHeaderMap::const_iterator end = requestHeaders.end();
- for (HTTPHeaderMap::const_iterator it = requestHeaders.begin(); it != end; ++it) {
- if (!m_headers.contains(it->first) && !isOnAccessControlSimpleRequestHeaderWhitelist(it->first))
- return false;
- }
- return true;
-}
-
-bool PreflightResultCacheItem::allowsRequest(bool includeCredentials, const String& method, const HTTPHeaderMap& requestHeaders) const
-{
- if (m_absoluteExpiryTime < currentTime())
- return false;
- if (includeCredentials && !m_credentials)
- return false;
- if (!allowsCrossSiteMethod(method))
- return false;
- if (!allowsCrossSiteHeaders(requestHeaders))
- return false;
- return true;
-}
-
-PreflightResultCache& PreflightResultCache::shared()
-{
- AtomicallyInitializedStatic(PreflightResultCache&, cache = *new PreflightResultCache);
- return cache;
-}
-
-void PreflightResultCache::appendEntry(const String& origin, const KURL& url, PreflightResultCacheItem* preflightResult)
-{
- MutexLocker lock(m_mutex);
- // Note that the entry may already be present in the HashMap if another thread is accessing the same location.
- m_preflightHashMap.set(std::make_pair(origin.copy(), url.copy()), preflightResult);
-}
-
-bool PreflightResultCache::canSkipPreflight(const String& origin, const KURL& url, bool includeCredentials,
- const String& method, const HTTPHeaderMap& requestHeaders)
-{
- MutexLocker lock(m_mutex);
- PreflightResultHashMap::iterator cacheIt = m_preflightHashMap.find(std::make_pair(origin, url));
- if (cacheIt == m_preflightHashMap.end())
- return false;
-
- if (cacheIt->second->allowsRequest(includeCredentials, method, requestHeaders))
- return true;
-
- delete cacheIt->second;
- m_preflightHashMap.remove(cacheIt);
- return false;
-}
-
static const XMLHttpRequestStaticData* staticData = 0;
static const XMLHttpRequestStaticData* createXMLHttpRequestStaticData()
@@ -330,6 +148,7 @@ XMLHttpRequest::XMLHttpRequest(ScriptExecutionContext* context)
, m_inPreflight(false)
, m_receivedLength(0)
, m_lastSendLineNumber(0)
+ , m_exceptionCode(0)
{
initializeXMLHttpRequestStaticData();
}
@@ -379,7 +198,7 @@ Document* XMLHttpRequest::responseXML() const
m_responseXML = document()->implementation()->createDocument(0);
m_responseXML->open();
m_responseXML->setURL(m_url);
- // FIXME: set Last-Modified and cookies (currently, those are only available for HTMLDocuments).
+ // FIXME: Set Last-Modified.
m_responseXML->write(String(m_responseText));
m_responseXML->finishParsing();
m_responseXML->close();
@@ -629,19 +448,28 @@ void XMLHttpRequest::send(File* body, ExceptionCode& ec)
void XMLHttpRequest::createRequest(ExceptionCode& ec)
{
+ // Upload event listeners should be disallowed for simple cross-origin requests, because POSTing to an URL that does not
+ // permit cross origin requests should look exactly like POSTing to an URL that does not respond at all. If a listener exists
+ // when creating the request, it will force preflight.
+ // Also, only async requests support upload progress events.
+ m_uploadEventsAllowed = false;
if (m_async) {
dispatchLoadStartEvent();
- if (m_requestEntityBody && m_upload)
+ if (m_requestEntityBody && m_upload) {
+ m_uploadEventsAllowed = m_upload->hasListeners();
m_upload->dispatchLoadStartEvent();
+ }
}
m_sameOriginRequest = scriptExecutionContext()->securityOrigin()->canRequest(m_url);
if (!m_sameOriginRequest) {
- makeCrossSiteAccessRequest(ec);
+ makeCrossOriginAccessRequest(ec);
return;
}
+ m_uploadEventsAllowed = true;
+
makeSameOriginRequest(ec);
}
@@ -654,6 +482,7 @@ void XMLHttpRequest::makeSameOriginRequest(ExceptionCode& ec)
if (m_requestEntityBody) {
ASSERT(m_method != "GET");
+ ASSERT(m_method != "HEAD");
request.setHTTPBody(m_requestEntityBody.release());
}
@@ -666,33 +495,19 @@ void XMLHttpRequest::makeSameOriginRequest(ExceptionCode& ec)
loadRequestSynchronously(request, ec);
}
-bool XMLHttpRequest::isSimpleCrossSiteAccessRequest() const
-{
- if (m_method != "GET" && m_method != "POST")
- return false;
-
- HTTPHeaderMap::const_iterator end = m_requestHeaders.end();
- for (HTTPHeaderMap::const_iterator it = m_requestHeaders.begin(); it != end; ++it) {
- if (!isOnAccessControlSimpleRequestHeaderWhitelist(it->first))
- return false;
- }
-
- return true;
-}
-
-void XMLHttpRequest::makeCrossSiteAccessRequest(ExceptionCode& ec)
+void XMLHttpRequest::makeCrossOriginAccessRequest(ExceptionCode& ec)
{
ASSERT(!m_sameOriginRequest);
- if (isSimpleCrossSiteAccessRequest())
- makeSimpleCrossSiteAccessRequest(ec);
+ if (!m_uploadEventsAllowed && isSimpleCrossOriginAccessRequest(m_method, m_requestHeaders))
+ makeSimpleCrossOriginAccessRequest(ec);
else
- makeCrossSiteAccessRequestWithPreflight(ec);
+ makeCrossOriginAccessRequestWithPreflight(ec);
}
-void XMLHttpRequest::makeSimpleCrossSiteAccessRequest(ExceptionCode& ec)
+void XMLHttpRequest::makeSimpleCrossOriginAccessRequest(ExceptionCode& ec)
{
- ASSERT(isSimpleCrossSiteAccessRequest());
+ ASSERT(isSimpleCrossOriginAccessRequest(m_method, m_requestHeaders));
KURL url = m_url;
url.setUser(String());
@@ -706,20 +521,26 @@ void XMLHttpRequest::makeSimpleCrossSiteAccessRequest(ExceptionCode& ec)
if (m_requestHeaders.size() > 0)
request.addHTTPHeaderFields(m_requestHeaders);
+ if (m_requestEntityBody) {
+ ASSERT(m_method != "GET");
+ ASSERT(m_method != "HEAD");
+ request.setHTTPBody(m_requestEntityBody.release());
+ }
+
if (m_async)
loadRequestAsynchronously(request);
else
loadRequestSynchronously(request, ec);
}
-void XMLHttpRequest::makeCrossSiteAccessRequestWithPreflight(ExceptionCode& ec)
+void XMLHttpRequest::makeCrossOriginAccessRequestWithPreflight(ExceptionCode& ec)
{
String origin = scriptExecutionContext()->securityOrigin()->toString();
KURL url = m_url;
url.setUser(String());
url.setPass(String());
- if (!PreflightResultCache::shared().canSkipPreflight(origin, url, m_includeCredentials, m_method, m_requestHeaders)) {
+ if (!CrossOriginPreflightResultCache::shared().canSkipPreflight(origin, url, m_includeCredentials, m_method, m_requestHeaders)) {
m_inPreflight = true;
ResourceRequest preflightRequest(url);
preflightRequest.setHTTPMethod("OPTIONS");
@@ -744,6 +565,7 @@ void XMLHttpRequest::makeCrossSiteAccessRequestWithPreflight(ExceptionCode& ec)
}
if (m_async) {
+ m_uploadEventsAllowed = true;
loadRequestAsynchronously(preflightRequest);
return;
}
@@ -766,10 +588,12 @@ void XMLHttpRequest::makeCrossSiteAccessRequestWithPreflight(ExceptionCode& ec)
if (m_requestEntityBody) {
ASSERT(m_method != "GET");
+ ASSERT(m_method != "HEAD");
request.setHTTPBody(m_requestEntityBody.release());
}
if (m_async) {
+ m_uploadEventsAllowed = true;
loadRequestAsynchronously(request);
return;
}
@@ -798,43 +622,30 @@ void XMLHttpRequest::handleAsynchronousPreflightResult()
if (m_requestEntityBody) {
ASSERT(m_method != "GET");
+ ASSERT(m_method != "HEAD");
request.setHTTPBody(m_requestEntityBody.release());
}
+ m_uploadEventsAllowed = true;
loadRequestAsynchronously(request);
}
void XMLHttpRequest::loadRequestSynchronously(ResourceRequest& request, ExceptionCode& ec)
{
ASSERT(!m_async);
- Vector<char> data;
- ResourceError error;
- ResourceResponse response;
- unsigned long identifier = ThreadableLoader::loadResourceSynchronously(scriptExecutionContext(), request, error, response, data);
m_loader = 0;
-
- // No exception for file:/// resources, see <rdar://problem/4962298>.
- // Also, if we have an HTTP response, then it wasn't a network error in fact.
- if (error.isNull() || request.url().isLocalFile() || response.httpStatusCode() > 0) {
- processSyncLoadResults(identifier, data, response, ec);
- return;
- }
-
- if (error.isCancellation()) {
- abortError();
- ec = XMLHttpRequestException::ABORT_ERR;
- return;
- }
-
- networkError();
- ec = XMLHttpRequestException::NETWORK_ERR;
+ m_exceptionCode = 0;
+ ThreadableLoader::loadResourceSynchronously(scriptExecutionContext(), request, *this);
+ if (!m_exceptionCode && m_error)
+ m_exceptionCode = XMLHttpRequestException::NETWORK_ERR;
+ ec = m_exceptionCode;
}
-
void XMLHttpRequest::loadRequestAsynchronously(ResourceRequest& request)
{
ASSERT(m_async);
+ m_exceptionCode = 0;
// SubresourceLoader::create can return null here, for example if we're no longer attached to a page.
// This is true while running onunload handlers.
// FIXME: We need to be able to send XMLHttpRequests from onunload, <http://bugs.webkit.org/show_bug.cgi?id=10904>.
@@ -842,7 +653,11 @@ void XMLHttpRequest::loadRequestAsynchronously(ResourceRequest& request)
// We need to keep content sniffing enabled for local files due to CFNetwork not providing a MIME type
// for local files otherwise, <rdar://problem/5671813>.
LoadCallbacks callbacks = m_inPreflight ? DoNotSendLoadCallbacks : SendLoadCallbacks;
- ContentSniff contentSniff = request.url().isLocalFile() ? SniffContent : DoNotSniffContent;
+ ContentSniff contentSniff = request.url().isLocalFile() ? SniffContent : DoNotSniffContent;
+
+ if (m_upload)
+ request.setReportUploadProgress(true);
+
m_loader = ThreadableLoader::create(scriptExecutionContext(), this, request, callbacks, contentSniff);
if (m_loader) {
@@ -876,7 +691,7 @@ void XMLHttpRequest::abort()
dispatchAbortEvent();
if (!m_uploadComplete) {
m_uploadComplete = true;
- if (m_upload)
+ if (m_upload && m_uploadEventsAllowed)
m_upload->dispatchAbortEvent();
}
}
@@ -931,7 +746,7 @@ void XMLHttpRequest::networkError()
dispatchErrorEvent();
if (!m_uploadComplete) {
m_uploadComplete = true;
- if (m_upload)
+ if (m_upload && m_uploadEventsAllowed)
m_upload->dispatchErrorEvent();
}
internalAbort();
@@ -943,7 +758,7 @@ void XMLHttpRequest::abortError()
dispatchAbortEvent();
if (!m_uploadComplete) {
m_uploadComplete = true;
- if (m_upload)
+ if (m_upload && m_uploadEventsAllowed)
m_upload->dispatchAbortEvent();
}
}
@@ -1082,11 +897,6 @@ String XMLHttpRequest::getResponseHeader(const AtomicString& name, ExceptionCode
return m_response.httpHeaderField(name);
}
-bool XMLHttpRequest::isOnAccessControlResponseHeaderWhitelist(const String& name) const
-{
- return staticData->m_allowedCrossSiteResponseHeaders.contains(name);
-}
-
String XMLHttpRequest::responseMIMEType() const
{
String mimeType = extractMIMETypeFromMediaType(m_mimeTypeOverride);
@@ -1123,53 +933,36 @@ int XMLHttpRequest::status(ExceptionCode& ec) const
String XMLHttpRequest::statusText(ExceptionCode& ec) const
{
- // FIXME: <http://bugs.webkit.org/show_bug.cgi?id=3547> XMLHttpRequest.statusText returns always "OK".
- if (m_response.httpStatusCode())
- return "OK";
+ if (!m_response.httpStatusText().isNull())
+ return m_response.httpStatusText();
if (m_state == OPENED) {
- // See comments in getStatus() above.
+ // See comments in status() above.
ec = INVALID_STATE_ERR;
}
return String();
}
-void XMLHttpRequest::processSyncLoadResults(unsigned long identifier, const Vector<char>& data, const ResourceResponse& response, ExceptionCode& ec)
-{
- if (m_sameOriginRequest && !scriptExecutionContext()->securityOrigin()->canRequest(response.url())) {
- abort();
- return;
- }
-
- didReceiveResponse(response);
- changeState(HEADERS_RECEIVED);
-
- const char* bytes = static_cast<const char*>(data.data());
- int len = static_cast<int>(data.size());
- didReceiveData(bytes, len);
-
- didFinishLoading(identifier);
- if (m_error)
- ec = XMLHttpRequestException::NETWORK_ERR;
-}
-
-void XMLHttpRequest::didFail()
+void XMLHttpRequest::didFail(const ResourceError& error)
{
// If we are already in an error state, for instance we called abort(), bail out early.
if (m_error)
return;
+ if (error.isCancellation()) {
+ m_exceptionCode = XMLHttpRequestException::ABORT_ERR;
+ abortError();
+ return;
+ }
+
+ m_exceptionCode = XMLHttpRequestException::NETWORK_ERR;
networkError();
}
-void XMLHttpRequest::didGetCancelled()
+void XMLHttpRequest::didFailRedirectCheck()
{
- // If we are already in an error state, for instance we called abort(), bail out early.
- if (m_error)
- return;
-
- abortError();
+ networkError();
}
void XMLHttpRequest::didFinishLoading(unsigned long identifier)
@@ -1219,33 +1012,16 @@ void XMLHttpRequest::didSendData(unsigned long long bytesSent, unsigned long lon
if (!m_upload)
return;
- m_upload->dispatchProgressEvent(bytesSent, totalBytesToBeSent);
+ if (m_uploadEventsAllowed)
+ m_upload->dispatchProgressEvent(bytesSent, totalBytesToBeSent);
if (bytesSent == totalBytesToBeSent && !m_uploadComplete) {
m_uploadComplete = true;
- m_upload->dispatchLoadEvent();
+ if (m_uploadEventsAllowed)
+ m_upload->dispatchLoadEvent();
}
}
-bool XMLHttpRequest::accessControlCheck(const ResourceResponse& response)
-{
- const String& accessControlOriginString = response.httpHeaderField("Access-Control-Origin");
- if (accessControlOriginString == "*" && !m_includeCredentials)
- return true;
-
- RefPtr<SecurityOrigin> accessControlOrigin = SecurityOrigin::createFromString(accessControlOriginString);
- if (!accessControlOrigin->isSameSchemeHostPort(scriptExecutionContext()->securityOrigin()))
- return false;
-
- if (m_includeCredentials) {
- const String& accessControlCredentialsString = response.httpHeaderField("Access-Control-Credentials");
- if (accessControlCredentialsString != "true")
- return false;
- }
-
- return true;
-}
-
void XMLHttpRequest::didReceiveResponse(const ResourceResponse& response)
{
if (m_inPreflight) {
@@ -1254,7 +1030,7 @@ void XMLHttpRequest::didReceiveResponse(const ResourceResponse& response)
}
if (!m_sameOriginRequest) {
- if (!accessControlCheck(response)) {
+ if (!passesAccessControlCheck(response, m_includeCredentials, scriptExecutionContext()->securityOrigin())) {
networkError();
return;
}
@@ -1271,20 +1047,20 @@ void XMLHttpRequest::didReceiveResponsePreflight(const ResourceResponse& respons
ASSERT(m_inPreflight);
ASSERT(!m_sameOriginRequest);
- if (!accessControlCheck(response)) {
+ if (!passesAccessControlCheck(response, m_includeCredentials, scriptExecutionContext()->securityOrigin())) {
networkError();
return;
}
- OwnPtr<PreflightResultCacheItem> preflightResult(new PreflightResultCacheItem(m_includeCredentials));
+ OwnPtr<CrossOriginPreflightResultCacheItem> preflightResult(new CrossOriginPreflightResultCacheItem(m_includeCredentials));
if (!preflightResult->parse(response)
- || !preflightResult->allowsCrossSiteMethod(m_method)
- || !preflightResult->allowsCrossSiteHeaders(m_requestHeaders)) {
+ || !preflightResult->allowsCrossOriginMethod(m_method)
+ || !preflightResult->allowsCrossOriginHeaders(m_requestHeaders)) {
networkError();
return;
}
- PreflightResultCache::shared().appendEntry(scriptExecutionContext()->securityOrigin()->toString(), m_url, preflightResult.release());
+ CrossOriginPreflightResultCache::shared().appendEntry(scriptExecutionContext()->securityOrigin()->toString(), m_url, preflightResult.release());
}
void XMLHttpRequest::didReceiveAuthenticationCancellation(const ResourceResponse& failureResponse)
@@ -1304,13 +1080,16 @@ void XMLHttpRequest::didReceiveData(const char* data, int len)
if (!m_responseEncoding.isEmpty())
m_decoder = TextResourceDecoder::create("text/plain", m_responseEncoding);
// allow TextResourceDecoder to look inside the m_response if it's XML or HTML
- else if (responseIsXML())
+ else if (responseIsXML()) {
m_decoder = TextResourceDecoder::create("application/xml");
- else if (responseMIMEType() == "text/html")
+ // Don't stop on encoding errors, unlike it is done for other kinds of XML resources. This matches the behavior of previous WebKit versions, Firefox and Opera.
+ m_decoder->useLenientXMLDecoding();
+ } else if (responseMIMEType() == "text/html")
m_decoder = TextResourceDecoder::create("text/html", "UTF-8");
else
m_decoder = TextResourceDecoder::create("text/plain", "UTF-8");
}
+
if (!len)
return;