📄 xmlhttprequest.cpp
字号:
dispatchReadyStateChangeEvent(); if (m_state == DONE) dispatchLoadEvent();}void XMLHttpRequest::open(const String& method, const KURL& url, bool async, ExceptionCode& ec){ internalAbort(); State previousState = m_state; m_state = UNSENT; m_error = false; m_uploadComplete = false; // clear stuff from possible previous load clearResponse(); clearRequest(); ASSERT(m_state == UNSENT); if (!isValidToken(method)) { ec = SYNTAX_ERR; return; } // Method names are case sensitive. But since Firefox uppercases method names it knows, we'll do the same. String methodUpper(method.upper()); if (methodUpper == "TRACE" || methodUpper == "TRACK" || methodUpper == "CONNECT") { ec = SECURITY_ERR; return; } m_url = url; if (methodUpper == "COPY" || methodUpper == "DELETE" || methodUpper == "GET" || methodUpper == "HEAD" || methodUpper == "INDEX" || methodUpper == "LOCK" || methodUpper == "M-POST" || methodUpper == "MKCOL" || methodUpper == "MOVE" || methodUpper == "OPTIONS" || methodUpper == "POST" || methodUpper == "PROPFIND" || methodUpper == "PROPPATCH" || methodUpper == "PUT" || methodUpper == "UNLOCK") m_method = methodUpper; else m_method = method; m_async = async; ASSERT(!m_loader); // Check previous state to avoid dispatching readyState event // when calling open several times in a row. if (previousState != OPENED) changeState(OPENED); else m_state = OPENED;}void XMLHttpRequest::open(const String& method, const KURL& url, bool async, const String& user, ExceptionCode& ec){ KURL urlWithCredentials(url); urlWithCredentials.setUser(user); open(method, urlWithCredentials, async, ec);}void XMLHttpRequest::open(const String& method, const KURL& url, bool async, const String& user, const String& password, ExceptionCode& ec){ KURL urlWithCredentials(url); urlWithCredentials.setUser(user); urlWithCredentials.setPass(password); open(method, urlWithCredentials, async, ec);}bool XMLHttpRequest::initSend(ExceptionCode& ec){ if (!scriptExecutionContext()) return false; if (m_state != OPENED || m_loader) { ec = INVALID_STATE_ERR; return false; } m_error = false; return true;}void XMLHttpRequest::send(ExceptionCode& ec){ send(String(), ec);}void XMLHttpRequest::send(Document* document, ExceptionCode& ec){ ASSERT(document); if (!initSend(ec)) return; if (m_method != "GET" && m_method != "HEAD" && (m_url.protocolIs("http") || m_url.protocolIs("https"))) { String contentType = getRequestHeader("Content-Type"); if (contentType.isEmpty()) {#if ENABLE(DASHBOARD_SUPPORT) if (usesDashboardBackwardCompatibilityMode()) setRequestHeaderInternal("Content-Type", "application/x-www-form-urlencoded"); else#endif // FIXME: this should include the charset used for encoding. setRequestHeaderInternal("Content-Type", "application/xml"); } // FIXME: According to XMLHttpRequest Level 2, this should use the Document.innerHTML algorithm // from the HTML5 specification to serialize the document. String body = createMarkup(document); // FIXME: this should use value of document.inputEncoding to determine the encoding to use. TextEncoding encoding = UTF8Encoding(); m_requestEntityBody = FormData::create(encoding.encode(body.characters(), body.length(), EntitiesForUnencodables)); if (m_upload) m_requestEntityBody->setAlwaysStream(true); } createRequest(ec);}void XMLHttpRequest::send(const String& body, ExceptionCode& ec){ if (!initSend(ec)) return; if (!body.isNull() && m_method != "GET" && m_method != "HEAD" && (m_url.protocolIs("http") || m_url.protocolIs("https"))) { String contentType = getRequestHeader("Content-Type"); if (contentType.isEmpty()) {#if ENABLE(DASHBOARD_SUPPORT) if (usesDashboardBackwardCompatibilityMode()) setRequestHeaderInternal("Content-Type", "application/x-www-form-urlencoded"); else#endif setRequestHeaderInternal("Content-Type", "application/xml"); } m_requestEntityBody = FormData::create(UTF8Encoding().encode(body.characters(), body.length(), EntitiesForUnencodables)); if (m_upload) m_requestEntityBody->setAlwaysStream(true); } createRequest(ec);}void XMLHttpRequest::send(File* body, ExceptionCode& ec){ if (!initSend(ec)) return; if (m_method != "GET" && m_method != "HEAD" && (m_url.protocolIs("http") || m_url.protocolIs("https"))) { // FIXME: Should we set a Content-Type if one is not set. // FIXME: add support for uploading bundles. m_requestEntityBody = FormData::create(); m_requestEntityBody->appendFile(body->path(), false); } createRequest(ec);}void XMLHttpRequest::createRequest(ExceptionCode& ec){ if (m_async) { dispatchLoadStartEvent(); if (m_requestEntityBody && m_upload) m_upload->dispatchLoadStartEvent(); } m_sameOriginRequest = scriptExecutionContext()->securityOrigin()->canRequest(m_url); if (!m_sameOriginRequest) { makeCrossSiteAccessRequest(ec); return; } makeSameOriginRequest(ec);}void XMLHttpRequest::makeSameOriginRequest(ExceptionCode& ec){ ASSERT(m_sameOriginRequest); ResourceRequest request(m_url); request.setHTTPMethod(m_method); if (m_requestEntityBody) { ASSERT(m_method != "GET"); request.setHTTPBody(m_requestEntityBody.release()); } if (m_requestHeaders.size() > 0) request.addHTTPHeaderFields(m_requestHeaders); if (m_async) loadRequestAsynchronously(request); else 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){ ASSERT(!m_sameOriginRequest); if (isSimpleCrossSiteAccessRequest()) makeSimpleCrossSiteAccessRequest(ec); else makeCrossSiteAccessRequestWithPreflight(ec);}void XMLHttpRequest::makeSimpleCrossSiteAccessRequest(ExceptionCode& ec){ ASSERT(isSimpleCrossSiteAccessRequest()); KURL url = m_url; url.setUser(String()); url.setPass(String()); ResourceRequest request(url); request.setHTTPMethod(m_method); request.setAllowHTTPCookies(m_includeCredentials); request.setHTTPOrigin(scriptExecutionContext()->securityOrigin()->toString()); if (m_requestHeaders.size() > 0) request.addHTTPHeaderFields(m_requestHeaders); if (m_async) loadRequestAsynchronously(request); else loadRequestSynchronously(request, ec);}void XMLHttpRequest::makeCrossSiteAccessRequestWithPreflight(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)) { m_inPreflight = true; ResourceRequest preflightRequest(url); preflightRequest.setHTTPMethod("OPTIONS"); preflightRequest.setHTTPHeaderField("Origin", origin); preflightRequest.setHTTPHeaderField("Access-Control-Request-Method", m_method); if (m_requestHeaders.size() > 0) { Vector<UChar> headerBuffer; HTTPHeaderMap::const_iterator it = m_requestHeaders.begin(); append(headerBuffer, it->first); ++it; HTTPHeaderMap::const_iterator end = m_requestHeaders.end(); for (; it != end; ++it) { headerBuffer.append(','); headerBuffer.append(' '); append(headerBuffer, it->first); } preflightRequest.setHTTPHeaderField("Access-Control-Request-Headers", String::adopt(headerBuffer)); preflightRequest.addHTTPHeaderFields(m_requestHeaders); } if (m_async) { loadRequestAsynchronously(preflightRequest); return; } loadRequestSynchronously(preflightRequest, ec); m_inPreflight = false; if (ec) return; } // Send the actual request. ResourceRequest request(url); request.setHTTPMethod(m_method); request.setAllowHTTPCookies(m_includeCredentials); request.setHTTPHeaderField("Origin", origin); if (m_requestHeaders.size() > 0) request.addHTTPHeaderFields(m_requestHeaders); if (m_requestEntityBody) { ASSERT(m_method != "GET"); request.setHTTPBody(m_requestEntityBody.release()); } if (m_async) { loadRequestAsynchronously(request); return; } loadRequestSynchronously(request, ec);}void XMLHttpRequest::handleAsynchronousPreflightResult(){ ASSERT(m_inPreflight); ASSERT(m_async); m_inPreflight = false; KURL url = m_url; url.setUser(String()); url.setPass(String()); ResourceRequest request(url); request.setHTTPMethod(m_method); request.setAllowHTTPCookies(m_includeCredentials); request.setHTTPOrigin(scriptExecutionContext()->securityOrigin()->toString()); if (m_requestHeaders.size() > 0) request.addHTTPHeaderFields(m_requestHeaders); if (m_requestEntityBody) { ASSERT(m_method != "GET"); request.setHTTPBody(m_requestEntityBody.release()); } loadRequestAsynchronously(request);}void XMLHttpRequest::loadRequestSynchronously(ResourceRequest& request, ExceptionCode& ec){ ASSERT(!m_async); m_loader = 0; 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>. // FIXME: Maybe create can return null for other reasons too? // 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; if (m_upload) request.setReportUploadProgress(true); m_loader = ThreadableLoader::create(scriptExecutionContext(), this, request, callbacks, contentSniff); if (m_loader) { // Neither this object nor the JavaScript wrapper should be deleted while // a request is in progress because we need to keep the listeners alive, // and they are referenced by the JavaScript wrapper. setPendingActivity(this); }}void XMLHttpRequest::abort(){ // internalAbort() calls dropProtection(), which may release the last reference. RefPtr<XMLHttpRequest> protect(this); bool sendFlag = m_loader; internalAbort(); // Clear headers as required by the spec m_requestHeaders.clear(); if ((m_state <= OPENED && !sendFlag) || m_state == DONE) m_state = UNSENT; else { ASSERT(!m_loader); changeState(DONE); m_state = UNSENT; } dispatchAbortEvent(); if (!m_uploadComplete) { m_uploadComplete = true; if (m_upload) m_upload->dispatchAbortEvent(); }}void XMLHttpRequest::internalAbort(){ bool hadLoader = m_loader; m_error = true; // FIXME: when we add the support for multi-part XHR, we will have to think be careful with this initialization. m_receivedLength = 0; if (hadLoader) { m_loader->cancel(); m_loader = 0; } m_decoder = 0; if (hadLoader) dropProtection();}void XMLHttpRequest::clearResponse(){ m_response = ResourceResponse(); m_responseText = ""; m_createdDocument = false; m_responseXML = 0;}void XMLHttpRequest::clearRequest(){ m_requestHeaders.clear(); m_requestEntityBody = 0;}void XMLHttpRequest::genericError(){ clearResponse(); clearRequest(); m_error = true; // The spec says we should "Synchronously switch the state to DONE." and then "Synchronously dispatch a readystatechange event on the object" // but this does not match Firefox.}void XMLHttpRequest::networkError(){ genericError(); dispatchErrorEvent(); if (!m_uploadComplete) { m_uploadComplete = true; if (m_upload) m_upload->dispatchErrorEvent(); }}void XMLHttpRequest::abortError(){ genericError();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -