📄 xmlhttprequest.cpp
字号:
dispatchAbortEvent(); if (!m_uploadComplete) { m_uploadComplete = true; if (m_upload) m_upload->dispatchAbortEvent(); }}void XMLHttpRequest::dropProtection() {#if USE(JSC) // The XHR object itself holds on to the responseText, and // thus has extra cost even independent of any // responseText or responseXML objects it has handed // out. But it is protected from GC while loading, so this // can't be recouped until the load is done, so only // report the extra cost at that point. if (JSDOMGlobalObject* globalObject = toJSDOMGlobalObject(scriptExecutionContext())) if (DOMObject* wrapper = getCachedDOMObjectWrapper(*globalObject->globalData(), this)) JSC::Heap::heap(wrapper)->reportExtraMemoryCost(m_responseText.size() * 2);#endif unsetPendingActivity(this);}void XMLHttpRequest::overrideMimeType(const String& override){ m_mimeTypeOverride = override;}static void reportUnsafeUsage(ScriptExecutionContext* context, const String& message){ if (!context) return; // FIXME: It's not good to report the bad usage without indicating what source line it came from. // We should pass additional parameters so we can tell the console where the mistake occurred. context->addMessage(ConsoleDestination, JSMessageSource, ErrorMessageLevel, message, 1, String());}void XMLHttpRequest::setRequestHeader(const AtomicString& name, const String& value, ExceptionCode& ec){ if (m_state != OPENED || m_loader) {#if ENABLE(DASHBOARD_SUPPORT) if (usesDashboardBackwardCompatibilityMode()) return;#endif ec = INVALID_STATE_ERR; return; } if (!isValidToken(name) || !isValidHeaderValue(value)) { ec = SYNTAX_ERR; return; } // A privileged script (e.g. a Dashboard widget) can set any headers. if (!scriptExecutionContext()->securityOrigin()->canLoadLocalResources() && !isSafeRequestHeader(name)) { reportUnsafeUsage(scriptExecutionContext(), "Refused to set unsafe header \"" + name + "\""); return; } setRequestHeaderInternal(name, value);}void XMLHttpRequest::setRequestHeaderInternal(const AtomicString& name, const String& value){ pair<HTTPHeaderMap::iterator, bool> result = m_requestHeaders.add(name, value); if (!result.second) result.first->second += ", " + value;}bool XMLHttpRequest::isSafeRequestHeader(const String& name) const{ return !staticData->m_forbiddenRequestHeaders.contains(name) && !name.startsWith(staticData->m_proxyHeaderPrefix, false) && !name.startsWith(staticData->m_secHeaderPrefix, false);}String XMLHttpRequest::getRequestHeader(const AtomicString& name) const{ return m_requestHeaders.get(name);}String XMLHttpRequest::getAllResponseHeaders(ExceptionCode& ec) const{ if (m_state < LOADING) { ec = INVALID_STATE_ERR; return ""; } Vector<UChar> stringBuilder; HTTPHeaderMap::const_iterator end = m_response.httpHeaderFields().end(); for (HTTPHeaderMap::const_iterator it = m_response.httpHeaderFields().begin(); it!= end; ++it) { // Hide Set-Cookie header fields from the XMLHttpRequest client for these reasons: // 1) If the client did have access to the fields, then it could read HTTP-only // cookies; those cookies are supposed to be hidden from scripts. // 2) There's no known harm in hiding Set-Cookie header fields entirely; we don't // know any widely used technique that requires access to them. // 3) Firefox has implemented this policy. if (isSetCookieHeader(it->first) && !scriptExecutionContext()->securityOrigin()->canLoadLocalResources()) continue; if (!m_sameOriginRequest && !isOnAccessControlResponseHeaderWhitelist(it->first)) continue; stringBuilder.append(it->first.characters(), it->first.length()); stringBuilder.append(':'); stringBuilder.append(' '); stringBuilder.append(it->second.characters(), it->second.length()); stringBuilder.append('\r'); stringBuilder.append('\n'); } return String::adopt(stringBuilder);}String XMLHttpRequest::getResponseHeader(const AtomicString& name, ExceptionCode& ec) const{ if (m_state < LOADING) { ec = INVALID_STATE_ERR; return ""; } if (!isValidToken(name)) return ""; // See comment in getAllResponseHeaders above. if (isSetCookieHeader(name) && !scriptExecutionContext()->securityOrigin()->canLoadLocalResources()) { reportUnsafeUsage(scriptExecutionContext(), "Refused to get unsafe header \"" + name + "\""); return ""; } if (!m_sameOriginRequest && !isOnAccessControlResponseHeaderWhitelist(name)) { reportUnsafeUsage(scriptExecutionContext(), "Refused to get unsafe header \"" + name + "\""); return ""; } 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); if (mimeType.isEmpty()) { if (m_response.isHTTP()) mimeType = extractMIMETypeFromMediaType(m_response.httpHeaderField("Content-Type")); else mimeType = m_response.mimeType(); } if (mimeType.isEmpty()) mimeType = "text/xml"; return mimeType;}bool XMLHttpRequest::responseIsXML() const{ return DOMImplementation::isXMLMIMEType(responseMIMEType());}int XMLHttpRequest::status(ExceptionCode& ec) const{ if (m_response.httpStatusCode()) return m_response.httpStatusCode(); if (m_state == OPENED) { // Firefox only raises an exception in this state; we match it. // Note the case of local file requests, where we have no HTTP response code! Firefox never raises an exception for those, but we match HTTP case for consistency. ec = INVALID_STATE_ERR; } return 0;}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_state == OPENED) { // See comments in getStatus() above. ec = INVALID_STATE_ERR; } return String();}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::didFailRedirectCheck(){ internalAbort(); networkError();}void XMLHttpRequest::didFinishLoading(unsigned long identifier){ if (m_error) return; if (m_inPreflight) { didFinishLoadingPreflight(); return; } if (m_state < HEADERS_RECEIVED) changeState(HEADERS_RECEIVED); if (m_decoder) m_responseText += m_decoder->flush(); scriptExecutionContext()->resourceRetrievedByXMLHttpRequest(identifier, m_responseText); scriptExecutionContext()->addMessage(InspectorControllerDestination, JSMessageSource, LogMessageLevel, "XHR finished loading: \"" + m_url + "\".", m_lastSendLineNumber, m_lastSendURL); bool hadLoader = m_loader; m_loader = 0; changeState(DONE); m_decoder = 0; if (hadLoader) dropProtection();}void XMLHttpRequest::didFinishLoadingPreflight(){ ASSERT(m_inPreflight); ASSERT(!m_sameOriginRequest); // FIXME: this can probably be moved to didReceiveResponsePreflight. if (m_async) handleAsynchronousPreflightResult(); if (m_loader) unsetPendingActivity(this);}void XMLHttpRequest::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent){ if (!m_upload) return; m_upload->dispatchProgressEvent(bytesSent, totalBytesToBeSent); if (bytesSent == totalBytesToBeSent && !m_uploadComplete) { m_uploadComplete = true; m_upload->dispatchLoadEvent(); }}bool XMLHttpRequest::accessControlCheck(const ResourceResponse& response){ const String& accessControlOriginString = response.httpHeaderField("Access-Control-Allow-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-Allow-Credentials"); if (accessControlCredentialsString != "true") return false; } return true;}void XMLHttpRequest::didReceiveResponse(const ResourceResponse& response){ if (m_inPreflight) { didReceiveResponsePreflight(response); return; } if (!m_sameOriginRequest) { if (!accessControlCheck(response)) { networkError(); return; } } m_response = response; m_responseEncoding = extractCharsetFromMediaType(m_mimeTypeOverride); if (m_responseEncoding.isEmpty()) m_responseEncoding = response.textEncodingName();}void XMLHttpRequest::didReceiveResponsePreflight(const ResourceResponse& response){ ASSERT(m_inPreflight); ASSERT(!m_sameOriginRequest); if (!accessControlCheck(response)) { networkError(); return; } OwnPtr<PreflightResultCacheItem> preflightResult(new PreflightResultCacheItem(m_includeCredentials)); if (!preflightResult->parse(response) || !preflightResult->allowsCrossSiteMethod(m_method) || !preflightResult->allowsCrossSiteHeaders(m_requestHeaders)) { networkError(); return; } PreflightResultCache::shared().appendEntry(scriptExecutionContext()->securityOrigin()->toString(), m_url, preflightResult.release());}void XMLHttpRequest::didReceiveAuthenticationCancellation(const ResourceResponse& failureResponse){ m_response = failureResponse;}void XMLHttpRequest::didReceiveData(const char* data, int len){ if (m_inPreflight) return; if (m_state < HEADERS_RECEIVED) changeState(HEADERS_RECEIVED); if (!m_decoder) { 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()) { m_decoder = TextResourceDecoder::create("application/xml"); // 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; if (len == -1) len = strlen(data); m_responseText += m_decoder->decode(data, len); if (!m_error) { updateAndDispatchOnProgress(len); if (m_state != LOADING) changeState(LOADING); else // Firefox calls readyStateChanged every time it receives data, 4449442 callReadyStateChangeListener(); }}void XMLHttpRequest::updateAndDispatchOnProgress(unsigned int len){ long long expectedLength = m_response.expectedContentLength(); m_receivedLength += len; // FIXME: the spec requires that we dispatch the event according to the least // frequent method between every 350ms (+/-200ms) and for every byte received. dispatchProgressEvent(expectedLength);}void XMLHttpRequest::dispatchReadyStateChangeEvent(){ RefPtr<Event> evt = Event::create(eventNames().readystatechangeEvent, false, false); if (m_onReadyStateChangeListener) { evt->setTarget(this); evt->setCurrentTarget(this); m_onReadyStateChangeListener->handleEvent(evt.get(), false); } ExceptionCode ec = 0; dispatchEvent(evt.release(), ec); ASSERT(!ec);}void XMLHttpRequest::dispatchXMLHttpRequestProgressEvent(EventListener* listener, const AtomicString& type, bool lengthComputable, unsigned loaded, unsigned total){ RefPtr<XMLHttpRequestProgressEvent> evt = XMLHttpRequestProgressEvent::create(type, lengthComputable, loaded, total); if (listener) { evt->setTarget(this); evt->setCurrentTarget(this); listener->handleEvent(evt.get(), false); } ExceptionCode ec = 0; dispatchEvent(evt.release(), ec); ASSERT(!ec);}void XMLHttpRequest::dispatchAbortEvent(){ dispatchXMLHttpRequestProgressEvent(m_onAbortListener.get(), eventNames().abortEvent, false, 0, 0);}void XMLHttpRequest::dispatchErrorEvent(){ dispatchXMLHttpRequestProgressEvent(m_onErrorListener.get(), eventNames().errorEvent, false, 0, 0);}void XMLHttpRequest::dispatchLoadEvent(){ dispatchXMLHttpRequestProgressEvent(m_onLoadListener.get(), eventNames().loadEvent, false, 0, 0);}void XMLHttpRequest::dispatchLoadStartEvent(){ dispatchXMLHttpRequestProgressEvent(m_onLoadStartListener.get(), eventNames().loadstartEvent, false, 0, 0);}void XMLHttpRequest::dispatchProgressEvent(long long expectedLength){ dispatchXMLHttpRequestProgressEvent(m_onProgressListener.get(), eventNames().progressEvent, expectedLength && m_receivedLength <= expectedLength, static_cast<unsigned>(m_receivedLength), static_cast<unsigned>(expectedLength));}bool XMLHttpRequest::canSuspend() const{ return !m_loader;}void XMLHttpRequest::stop(){ internalAbort();}void XMLHttpRequest::contextDestroyed(){ ASSERT(!m_loader); ActiveDOMObject::contextDestroyed();}ScriptExecutionContext* XMLHttpRequest::scriptExecutionContext() const{ return ActiveDOMObject::scriptExecutionContext();}} // namespace WebCore
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -