void XMLHttpRequest::abort() { // internalAbort() calls dropProtection(), which may release the last reference. Ref<XMLHttpRequest> protectedThis(*this); if (!internalAbort()) return; clearResponseBuffers(); // Clear headers as required by the spec m_requestHeaders.clear(); if ((m_state == OPENED && m_sendFlag) || m_state == HEADERS_RECEIVED || m_state == LOADING) { ASSERT(!m_loader); m_sendFlag = false; changeState(DONE); dispatchErrorEvents(eventNames().abortEvent); } m_state = UNSENT; }
void SubresourceLoader::didFinishLoading(double finishTime) { if (m_state != Initialized) return; ASSERT(!reachedTerminalState()); ASSERT(!m_resource->resourceToRevalidate()); // FIXME (129394): We should cancel the load when a decode error occurs instead of continuing the load to completion. ASSERT(!m_resource->errorOccurred() || m_resource->status() == CachedResource::DecodeError); LOG(ResourceLoading, "Received '%s'.", m_resource->url().string().latin1().data()); logResourceLoaded(m_frame.get(), m_resource->type()); Ref<SubresourceLoader> protectedThis(*this); CachedResourceHandle<CachedResource> protectResource(m_resource); // FIXME: The finishTime that is passed in is from the NetworkProcess and is more accurate. // However, all other load times are generated from the web process or offsets. // Mixing times from different processes can cause the finish time to be earlier than // the response received time due to inter-process communication lag. UNUSED_PARAM(finishTime); double responseEndTime = monotonicallyIncreasingTime(); m_loadTiming.setResponseEnd(responseEndTime); #if ENABLE(WEB_TIMING) if (m_documentLoader->cachedResourceLoader().document() && RuntimeEnabledFeatures::sharedFeatures().resourceTimingEnabled()) m_documentLoader->cachedResourceLoader().resourceTimingInformation().addResourceTiming(m_resource, *m_documentLoader->cachedResourceLoader().document(), m_resource->loader()->loadTiming()); #endif m_state = Finishing; m_resource->setLoadFinishTime(responseEndTime); // FIXME: Users of the loadFinishTime should use the LoadTiming struct instead. m_resource->finishLoading(resourceData()); if (wasCancelled()) return; m_resource->finish(); ASSERT(!reachedTerminalState()); didFinishLoadingOnePart(responseEndTime); notifyDone(); if (reachedTerminalState()) return; releaseResources(); }
bool JSTestCallbackFunction::callbackWithSerializedScriptValueParam(PassRefPtr<SerializedScriptValue> srzParam, const String& strArg) { if (!canInvokeCallback()) return true; Ref<JSTestCallbackFunction> protectedThis(*this); JSLockHolder lock(m_data->globalObject()->vm()); ExecState* state = m_data->globalObject()->globalExec(); MarkedArgumentBuffer args; args.append(srzParam ? srzParam->deserialize(state, castedThis->globalObject(), 0) : jsNull()); args.append(jsStringWithCache(state, strArg)); NakedPtr<Exception> returnedException; UNUSED_PARAM(state); m_data->invokeCallback(args, JSCallbackData::CallbackType::Function, Identifier(), returnedException); if (returnedException) reportException(state, returnedException); return !returnedException; }
void SubresourceLoader::didReceiveDataOrBuffer(const char* data, int length, RefPtr<SharedBuffer>&& prpBuffer, long long encodedDataLength, DataPayloadType dataPayloadType) { if (m_resource->response().httpStatusCode() >= 400 && !m_resource->shouldIgnoreHTTPStatusCodeErrors()) return; ASSERT(!m_resource->resourceToRevalidate()); ASSERT(!m_resource->errorOccurred()); ASSERT(m_state == Initialized); // Reference the object in this method since the additional processing can do // anything including removing the last reference to this object; one example of this is 3266216. Ref<SubresourceLoader> protectedThis(*this); RefPtr<SharedBuffer> buffer = prpBuffer; ResourceLoader::didReceiveDataOrBuffer(data, length, WTFMove(buffer), encodedDataLength, dataPayloadType); if (!m_loadingMultipartContent) { if (auto* resourceData = this->resourceData()) m_resource->addDataBuffer(*resourceData); else m_resource->addData(buffer ? buffer->data() : data, buffer ? buffer->size() : length); } }
bool JSTestCallbackFunction::callbackRequiresThisToPass(int32_t longParam, TestNode* testNodeParam) { if (!canInvokeCallback()) return true; Ref<JSTestCallbackFunction> protectedThis(*this); JSLockHolder lock(m_data->globalObject()->vm()); ExecState* state = m_data->globalObject()->globalExec(); MarkedArgumentBuffer args; args.append(jsNumber(longParam)); args.append(toJS(state, m_data->globalObject(), testNodeParam)); NakedPtr<Exception> returnedException; UNUSED_PARAM(state); m_data->invokeCallback(args, JSCallbackData::CallbackType::Function, Identifier(), returnedException); if (returnedException) reportException(state, returnedException); return !returnedException; }
void CACFLayerTreeHost::flushPendingLayerChangesNow() { // Calling out to the client could cause our last reference to go away. RefPtr<CACFLayerTreeHost> protectedThis(this); updateDebugInfoLayer(m_page->settings().showTiledScrollingIndicator()); m_isFlushingLayerChanges = true; // Flush changes stored up in GraphicsLayers to their underlying PlatformCALayers, if // requested. if (m_client && m_shouldFlushPendingGraphicsLayerChanges) { m_shouldFlushPendingGraphicsLayerChanges = false; m_client->flushPendingGraphicsLayerChanges(); } // Flush changes stored up in PlatformCALayers to the context so they will be rendered. flushContext(); m_isFlushingLayerChanges = false; }
void SubresourceLoader::didFail(const ResourceError& error) { if (m_state != Initialized) return; ASSERT(!reachedTerminalState()); LOG(ResourceLoading, "Failed to load '%s'.\n", m_resource->url().string().latin1().data()); Ref<SubresourceLoader> protectedThis(*this); CachedResourceHandle<CachedResource> protectResource(m_resource); m_state = Finishing; if (m_resource->resourceToRevalidate()) MemoryCache::singleton().revalidationFailed(*m_resource); m_resource->setResourceError(error); if (!m_resource->isPreloaded()) MemoryCache::singleton().remove(*m_resource); m_resource->error(CachedResource::LoadError); cleanupForError(error); notifyDone(); if (reachedTerminalState()) return; releaseResources(); }
void CachedRawResource::finishLoading(SharedBuffer* data) { CachedResourceHandle<CachedRawResource> protectedThis(this); DataBufferingPolicy dataBufferingPolicy = this->dataBufferingPolicy(); if (dataBufferingPolicy == BufferData) { m_data = data; unsigned incrementalDataLength; const char* incrementalData = calculateIncrementalDataChunk(data, incrementalDataLength); if (data) setEncodedSize(data->size()); notifyClientsDataWasReceived(incrementalData, incrementalDataLength); } m_allowEncodedDataReplacement = !m_loader->isQuickLookResource(); CachedResource::finishLoading(data); if (dataBufferingPolicy == BufferData && this->dataBufferingPolicy() == DoNotBufferData) { if (m_loader) m_loader->setDataBufferingPolicy(DoNotBufferData); clear(); } }
void MediaDevicesRequest::didCompleteRequest(const TrackSourceInfoVector& capturedDevices) { if (!m_scriptExecutionContext) { m_protector = nullptr; return; } Vector<RefPtr<MediaDeviceInfo>> deviceInfo; for (auto device : capturedDevices) { TrackSourceInfo* trackInfo = device.get(); String deviceType = trackInfo->kind() == TrackSourceInfo::SourceKind::Audio ? MediaDeviceInfo::audioInputType() : MediaDeviceInfo::videoInputType(); AtomicString label = m_canShowLabels ? trackInfo->label() : emptyAtom; deviceInfo.append(MediaDeviceInfo::create(m_scriptExecutionContext, label, trackInfo->id(), trackInfo->groupId(), deviceType)); } RefPtr<MediaDevicesRequest> protectedThis(this); callOnMainThread([protectedThis, deviceInfo] { protectedThis->m_promise.resolve(deviceInfo); }); m_protector = nullptr; }
void call() { if (!canInvokeCallback()) return; Ref<JSGlobalObjectCallback> protectedThis(*this); JSLockHolder lock(m_globalObject->vm()); ExecState* exec = m_globalObject->globalExec(); ScriptExecutionContext* context = m_globalObject->scriptExecutionContext(); // We will fail to get the context if the frame has been detached. if (!context) return; // When on the main thread (e.g. the document's thread), we need to make sure to // push the current ExecState on to the JSMainThreadExecState stack. if (context->isDocument()) JSMainThreadExecState::runTask(exec, m_task); else m_task->run(exec); ASSERT(!exec->hadException()); }
void ResourceLoader::didReceiveAuthenticationChallenge(const AuthenticationChallenge& challenge) { ASSERT(m_handle->hasAuthenticationChallenge()); // Protect this in this delegate method since the additional processing can do // anything including possibly derefing this; one example of this is Radar 3266216. Ref<ResourceLoader> protectedThis(*this); if (m_options.allowCredentials() == AllowStoredCredentials) { if (isAllowedToAskUserForCredentials()) { frameLoader()->notifier().didReceiveAuthenticationChallenge(this, challenge); return; } } // Only these platforms provide a way to continue without credentials. // If we can't continue with credentials, we need to cancel the load altogether. #if PLATFORM(COCOA) || USE(CFNETWORK) || USE(CURL) || PLATFORM(GTK) || PLATFORM(EFL) challenge.authenticationClient()->receivedRequestToContinueWithoutCredential(challenge); ASSERT(!m_handle || !m_handle->hasAuthenticationChallenge()); #else didFail(blockedError()); #endif }
void CachedRawResource::didAddClient(CachedResourceClient& c) { if (!hasClient(c)) return; // The calls to the client can result in events running, potentially causing // this resource to be evicted from the cache and all clients to be removed, // so a protector is necessary. CachedResourceHandle<CachedRawResource> protectedThis(this); CachedRawResourceClient& client = static_cast<CachedRawResourceClient&>(c); size_t redirectCount = m_redirectChain.size(); for (size_t i = 0; i < redirectCount; i++) { RedirectPair redirect = m_redirectChain[i]; ResourceRequest request(redirect.m_request); client.redirectReceived(*this, request, redirect.m_redirectResponse); if (!hasClient(c)) return; } ASSERT(redirectCount == m_redirectChain.size()); if (!m_response.isNull()) { ResourceResponse response(m_response); if (validationCompleting()) response.setSource(ResourceResponse::Source::MemoryCacheAfterValidation); else { ASSERT(!validationInProgress()); response.setSource(ResourceResponse::Source::MemoryCache); } client.responseReceived(*this, response); } if (!hasClient(c)) return; if (m_data) client.dataReceived(*this, m_data->data(), m_data->size()); if (!hasClient(c)) return; CachedResource::didAddClient(client); }
void Geolocation::setIsAllowed(bool allowed) { // Protect the Geolocation object from garbage collection during a callback. Ref<Geolocation> protectedThis(*this); // This may be due to either a new position from the service, or a cached // position. m_allowGeolocation = allowed ? Yes : No; if (m_isSuspended) return; // Permission request was made during the startRequest process if (!m_pendingForPermissionNotifiers.isEmpty()) { handlePendingPermissionNotifiers(); m_pendingForPermissionNotifiers.clear(); return; } if (!isAllowed()) { auto error = PositionError::create(PositionError::PERMISSION_DENIED, permissionDeniedErrorMessage); error->setIsFatal(true); handleError(error); m_requestsAwaitingCachedPosition.clear(); m_hasChangedPosition = false; m_errorWaitingForResume = nullptr; return; } // If the service has a last position, use it to call back for all requests. // If any of the requests are waiting for permission for a cached position, // the position from the service will be at least as fresh. if (RefPtr<Geoposition> position = lastPosition()) makeSuccessCallbacks(*position); else makeCachedPositionCallbacks(); }
void BlobResourceHandle::doStart() { ASSERT(isMainThread()); // Do not continue if the request is aborted or an error occurs. if (m_aborted || m_errorCode) return; if (!equalLettersIgnoringASCIICase(firstRequest().httpMethod(), "get")) { notifyFail(methodNotAllowed); return; } // If the blob data is not found, fail now. if (!m_blobData) { m_errorCode = notFoundError; notifyResponse(); return; } // Parse the "Range" header we care about. String range = firstRequest().httpHeaderField(HTTPHeaderName::Range); if (!range.isEmpty() && !parseRange(range, m_rangeOffset, m_rangeEnd, m_rangeSuffixLength)) { m_errorCode = rangeError; notifyResponse(); return; } if (m_async) getSizeForNext(); else { Ref<BlobResourceHandle> protectedThis(*this); // getSizeForNext calls the client for (size_t i = 0; i < m_blobData->items().size() && !m_aborted && !m_errorCode; ++i) getSizeForNext(); notifyResponse(); } }
void ResourceLoader::releaseResources() { ASSERT(!m_reachedTerminalState); // It's possible that when we release the handle, it will be // deallocated and release the last reference to this object. // We need to retain to avoid accessing the object after it // has been deallocated and also to avoid reentering this method. Ref<ResourceLoader> protectedThis(*this); m_frame = nullptr; m_documentLoader = nullptr; // We need to set reachedTerminalState to true before we release // the resources to prevent a double dealloc of WebView <rdar://problem/4372628> m_reachedTerminalState = true; finishNetworkLoad(); m_identifier = 0; m_resourceData = nullptr; m_deferredRequest = ResourceRequest(); }
ThreadableWebSocketChannel::SendResult WorkerThreadableWebSocketChannel::Bridge::send(const ArrayBuffer& binaryData, unsigned byteOffset, unsigned byteLength) { if (!m_peer) return ThreadableWebSocketChannel::SendFail; // ArrayBuffer isn't thread-safe, hence the content of ArrayBuffer is copied into Vector<char>. Vector<char> data(byteLength); if (binaryData.byteLength()) memcpy(data.data(), static_cast<const char*>(binaryData.data()) + byteOffset, byteLength); setMethodNotCompleted(); m_loaderProxy.postTaskToLoader([peer = m_peer, data = WTFMove(data)](ScriptExecutionContext& context) { ASSERT(isMainThread()); ASSERT_UNUSED(context, context.isDocument()); ASSERT(peer); auto arrayBuffer = ArrayBuffer::create(data.data(), data.size()); peer->send(arrayBuffer); }); Ref<Bridge> protectedThis(*this); waitForMethodCompletion(); return m_workerClientWrapper->sendRequestResult(); }
void ResourceLoader::loadDataURL() { auto url = m_request.url(); ASSERT(url.protocolIsData()); RefPtr<ResourceLoader> protectedThis(this); DataURLDecoder::ScheduleContext scheduleContext; #if HAVE(RUNLOOP_TIMER) if (auto* scheduledPairs = m_frame->page()->scheduledRunLoopPairs()) scheduleContext.scheduledPairs = *scheduledPairs; #endif DataURLDecoder::decode(url, scheduleContext, [protectedThis, url](auto decodeResult) { if (protectedThis->reachedTerminalState()) return; if (!decodeResult) { protectedThis->didFail(ResourceError(errorDomainWebKitInternal, 0, url, "Data URL decoding failed")); return; } if (protectedThis->wasCancelled()) return; auto& result = decodeResult.value(); auto dataSize = result.data ? result.data->size() : 0; ResourceResponse dataResponse { url, result.mimeType, dataSize, result.charset }; dataResponse.setHTTPStatusCode(200); dataResponse.setHTTPStatusText(ASCIILiteral("OK")); dataResponse.setHTTPHeaderField(HTTPHeaderName::ContentType, result.contentType); protectedThis->didReceiveResponse(dataResponse); if (!protectedThis->reachedTerminalState() && dataSize) protectedThis->didReceiveBuffer(result.data.releaseNonNull(), dataSize, DataPayloadWholeResource); if (!protectedThis->reachedTerminalState()) protectedThis->didFinishLoading(currentTime()); }); }
void NetworkDataTaskBlob::didReceiveResponse(Error errorCode) { LOG(NetworkSession, "%p - NetworkDataTaskBlob::didReceiveResponse(%u)", this, static_cast<unsigned>(errorCode)); Ref<NetworkDataTaskBlob> protectedThis(*this); ResourceResponse response(m_firstRequest.url(), errorCode != Error::NoError ? "text/plain" : m_blobData->contentType(), errorCode != Error::NoError ? 0 : m_totalRemainingSize, String()); switch (errorCode) { case Error::NoError: { bool isRangeRequest = m_rangeOffset != kPositionNotSpecified; response.setHTTPStatusCode(isRangeRequest ? httpPartialContent : httpOK); response.setHTTPStatusText(isRangeRequest ? httpPartialContentText : httpOKText); response.setHTTPHeaderField(HTTPHeaderName::ContentType, m_blobData->contentType()); response.setHTTPHeaderField(HTTPHeaderName::ContentLength, String::number(m_totalRemainingSize)); if (isRangeRequest) response.setHTTPHeaderField(HTTPHeaderName::ContentRange, ParsedContentRange(m_rangeOffset, m_rangeEnd, m_totalSize).headerValue()); // FIXME: If a resource identified with a blob: URL is a File object, user agents must use that file's name attribute, // as if the response had a Content-Disposition header with the filename parameter set to the File's name attribute. // Notably, this will affect a name suggested in "File Save As". break; } case Error::RangeError: response.setHTTPStatusCode(httpRequestedRangeNotSatisfiable); response.setHTTPStatusText(httpRequestedRangeNotSatisfiableText); break; case Error::SecurityError: response.setHTTPStatusCode(httpNotAllowed); response.setHTTPStatusText(httpNotAllowedText); break; default: response.setHTTPStatusCode(httpInternalError); response.setHTTPStatusText(httpInternalErrorText); break; } m_client->didReceiveResponseNetworkSession(WTFMove(response), [this, protectedThis = WTFMove(protectedThis), errorCode](PolicyAction policyAction) { LOG(NetworkSession, "%p - NetworkDataTaskBlob::didReceiveResponse completionHandler (%u)", this, static_cast<unsigned>(policyAction)); if (m_state == State::Canceling || m_state == State::Completed) { clearStream(); return; } if (errorCode != Error::NoError) { didFinish(); return; } switch (policyAction) { case PolicyAction::PolicyUse: m_buffer.resize(bufferSize); read(); break; case PolicyAction::PolicyIgnore: break; case PolicyAction::PolicyDownload: download(); break; } }); }
void PendingScript::notifyClientFinished() { Ref<PendingScript> protectedThis(*this); if (m_client) m_client->notifyFinished(*this); }
void JSErrorHandler::handleEvent(ScriptExecutionContext* scriptExecutionContext, Event* event) { if (!is<ErrorEvent>(*event)) return JSEventListener::handleEvent(scriptExecutionContext, event); ASSERT(scriptExecutionContext); if (!scriptExecutionContext) return; ErrorEvent& errorEvent = downcast<ErrorEvent>(*event); JSLockHolder lock(scriptExecutionContext->vm()); JSObject* jsFunction = this->jsFunction(scriptExecutionContext); if (!jsFunction) return; JSDOMGlobalObject* globalObject = toJSDOMGlobalObject(scriptExecutionContext, isolatedWorld()); if (!globalObject) return; ExecState* exec = globalObject->globalExec(); CallData callData; CallType callType = jsFunction->methodTable()->getCallData(jsFunction, callData); if (callType != CallType::None) { Ref<JSErrorHandler> protectedThis(*this); Event* savedEvent = globalObject->currentEvent(); globalObject->setCurrentEvent(event); MarkedArgumentBuffer args; args.append(toJS<IDLDOMString>(*exec, errorEvent.message())); args.append(toJS<IDLUSVString>(*exec, errorEvent.filename())); args.append(toJS<IDLUnsignedLong>(errorEvent.lineno())); args.append(toJS<IDLUnsignedLong>(errorEvent.colno())); args.append(errorEvent.error(*exec, *globalObject)); VM& vm = globalObject->vm(); VMEntryScope entryScope(vm, vm.entryScope ? vm.entryScope->globalObject() : globalObject); InspectorInstrumentationCookie cookie = JSMainThreadExecState::instrumentFunctionCall(scriptExecutionContext, callType, callData); NakedPtr<JSC::Exception> exception; JSValue returnValue = scriptExecutionContext->isDocument() ? JSMainThreadExecState::profiledCall(exec, JSC::ProfilingReason::Other, jsFunction, callType, callData, globalObject, args, exception) : JSC::profiledCall(exec, JSC::ProfilingReason::Other, jsFunction, callType, callData, globalObject, args, exception); InspectorInstrumentation::didCallFunction(cookie, scriptExecutionContext); globalObject->setCurrentEvent(savedEvent); if (exception) reportException(exec, exception); else { if (returnValue.isTrue()) event->preventDefault(); } } }
void SubresourceLoader::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent) { ASSERT(m_state == Initialized); Ref<SubresourceLoader> protectedThis(*this); m_resource->didSendData(bytesSent, totalBytesToBeSent); }
void SubresourceLoader::didReceiveResponse(const ResourceResponse& response) { ASSERT(!response.isNull()); ASSERT(m_state == Initialized); // Reference the object in this method since the additional processing can do // anything including removing the last reference to this object; one example of this is 3266216. Ref<SubresourceLoader> protectedThis(*this); if (shouldIncludeCertificateInfo()) response.includeCertificateInfo(); if (response.isHttpVersion0_9()) { if (m_frame) { String message = "Sandboxing '" + response.url().string() + "' because it is using HTTP/0.9."; m_frame->document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, message, identifier()); frameLoader()->forceSandboxFlags(SandboxScripts | SandboxPlugins); } } if (m_resource->resourceToRevalidate()) { if (response.httpStatusCode() == 304) { // 304 Not modified / Use local copy // Existing resource is ok, just use it updating the expiration time. m_resource->setResponse(response); MemoryCache::singleton().revalidationSucceeded(*m_resource, response); if (m_frame && m_frame->page()) m_frame->page()->diagnosticLoggingClient().logDiagnosticMessageWithResult(DiagnosticLoggingKeys::cachedResourceRevalidationKey(), emptyString(), DiagnosticLoggingResultPass, ShouldSample::Yes); if (!reachedTerminalState()) ResourceLoader::didReceiveResponse(response); return; } // Did not get 304 response, continue as a regular resource load. MemoryCache::singleton().revalidationFailed(*m_resource); if (m_frame && m_frame->page()) m_frame->page()->diagnosticLoggingClient().logDiagnosticMessageWithResult(DiagnosticLoggingKeys::cachedResourceRevalidationKey(), emptyString(), DiagnosticLoggingResultFail, ShouldSample::Yes); } m_resource->responseReceived(response); if (reachedTerminalState()) return; ResourceLoader::didReceiveResponse(response); if (reachedTerminalState()) return; // FIXME: Main resources have a different set of rules for multipart than images do. // Hopefully we can merge those 2 paths. if (response.isMultipart() && m_resource->type() != CachedResource::MainResource) { m_loadingMultipartContent = true; // We don't count multiParts in a CachedResourceLoader's request count m_requestCountTracker = Nullopt; if (!m_resource->isImage()) { cancel(); return; } } auto* buffer = resourceData(); if (m_loadingMultipartContent && buffer && buffer->size()) { // The resource data will change as the next part is loaded, so we need to make a copy. m_resource->finishLoading(buffer->copy().ptr()); clearResourceData(); // Since a subresource loader does not load multipart sections progressively, data was delivered to the loader all at once. // After the first multipart section is complete, signal to delegates that this load is "finished" m_documentLoader->subresourceLoaderFinishedLoadingOnePart(this); didFinishLoadingOnePart(0); } checkForHTTPStatusCodeError(); }
void SubresourceLoader::willSendRequestInternal(ResourceRequest& newRequest, const ResourceResponse& redirectResponse) { // Store the previous URL because the call to ResourceLoader::willSendRequest will modify it. URL previousURL = request().url(); Ref<SubresourceLoader> protectedThis(*this); if (!newRequest.url().isValid()) { cancel(cannotShowURLError()); return; } ASSERT(!newRequest.isNull()); if (!redirectResponse.isNull()) { if (options().redirect != FetchOptions::Redirect::Follow) { if (options().redirect == FetchOptions::Redirect::Error) { cancel(); return; } ResourceResponse opaqueRedirectedResponse; opaqueRedirectedResponse.setType(ResourceResponse::Type::Opaqueredirect); m_resource->responseReceived(opaqueRedirectedResponse); didFinishLoading(currentTime()); return; } // CachedResources are keyed off their original request URL. // Requesting the same original URL a second time can redirect to a unique second resource. // Therefore, if a redirect to a different destination URL occurs, we should no longer consider this a revalidation of the first resource. // Doing so would have us reusing the resource from the first request if the second request's revalidation succeeds. if (newRequest.isConditional() && m_resource->resourceToRevalidate() && newRequest.url() != m_resource->resourceToRevalidate()->response().url()) { newRequest.makeUnconditional(); MemoryCache::singleton().revalidationFailed(*m_resource); if (m_frame && m_frame->page()) m_frame->page()->diagnosticLoggingClient().logDiagnosticMessageWithResult(DiagnosticLoggingKeys::cachedResourceRevalidationKey(), emptyString(), DiagnosticLoggingResultFail, ShouldSample::Yes); } if (!m_documentLoader->cachedResourceLoader().canRequest(m_resource->type(), newRequest.url(), options(), false /* forPreload */, true /* didReceiveRedirectResponse */)) { cancel(); return; } if (!checkRedirectionCrossOriginAccessControl(request(), redirectResponse, newRequest)) { cancel(); return; } if (m_resource->isImage() && m_documentLoader->cachedResourceLoader().shouldDeferImageLoad(newRequest.url())) { cancel(); return; } m_resource->redirectReceived(newRequest, redirectResponse); } if (newRequest.isNull() || reachedTerminalState()) return; ResourceLoader::willSendRequestInternal(newRequest, redirectResponse); if (newRequest.isNull()) cancel(); ResourceLoadObserver::sharedObserver().logSubresourceLoading(m_frame.get(), newRequest, redirectResponse); }
void SubresourceLoader::willSendRequestInternal(ResourceRequest& newRequest, const ResourceResponse& redirectResponse) { // Store the previous URL because the call to ResourceLoader::willSendRequest will modify it. URL previousURL = request().url(); Ref<SubresourceLoader> protectedThis(*this); if (!newRequest.url().isValid()) { cancel(cannotShowURLError()); return; } if (newRequest.requester() != ResourceRequestBase::Requester::Main) ResourceLoadObserver::sharedObserver().logSubresourceLoading(m_frame.get(), newRequest, redirectResponse); ASSERT(!newRequest.isNull()); if (!redirectResponse.isNull()) { if (options().redirect != FetchOptions::Redirect::Follow) { if (options().redirect == FetchOptions::Redirect::Error) { cancel(); return; } ResourceResponse opaqueRedirectedResponse; opaqueRedirectedResponse.setURL(redirectResponse.url()); opaqueRedirectedResponse.setType(ResourceResponse::Type::Opaqueredirect); m_resource->responseReceived(opaqueRedirectedResponse); didFinishLoading(currentTime()); return; } else if (m_redirectCount++ >= options().maxRedirectCount) { cancel(ResourceError(String(), 0, request().url(), ASCIILiteral("Too many redirections"), ResourceError::Type::General)); return; } // CachedResources are keyed off their original request URL. // Requesting the same original URL a second time can redirect to a unique second resource. // Therefore, if a redirect to a different destination URL occurs, we should no longer consider this a revalidation of the first resource. // Doing so would have us reusing the resource from the first request if the second request's revalidation succeeds. if (newRequest.isConditional() && m_resource->resourceToRevalidate() && newRequest.url() != m_resource->resourceToRevalidate()->response().url()) { newRequest.makeUnconditional(); MemoryCache::singleton().revalidationFailed(*m_resource); if (m_frame && m_frame->page()) m_frame->page()->diagnosticLoggingClient().logDiagnosticMessageWithResult(DiagnosticLoggingKeys::cachedResourceRevalidationKey(), emptyString(), DiagnosticLoggingResultFail, ShouldSample::Yes); } if (!m_documentLoader->cachedResourceLoader().updateRequestAfterRedirection(m_resource->type(), newRequest, options())) { cancel(); return; } String errorDescription; if (!checkRedirectionCrossOriginAccessControl(request(), redirectResponse, newRequest, errorDescription)) { String errorMessage = "Cross-origin redirection to " + newRequest.url().string() + " denied by Cross-Origin Resource Sharing policy: " + errorDescription; if (m_frame && m_frame->document()) m_frame->document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, errorMessage); cancel(ResourceError(String(), 0, request().url(), errorMessage, ResourceError::Type::AccessControl)); return; } if (m_resource->isImage() && m_documentLoader->cachedResourceLoader().shouldDeferImageLoad(newRequest.url())) { cancel(); return; } m_loadTiming.addRedirect(redirectResponse.url(), newRequest.url()); m_resource->redirectReceived(newRequest, redirectResponse); } if (newRequest.isNull() || reachedTerminalState()) return; ResourceLoader::willSendRequestInternal(newRequest, redirectResponse); if (newRequest.isNull()) { cancel(); return; } if (m_resource->type() == CachedResource::MainResource && !redirectResponse.isNull()) m_documentLoader->willContinueMainResourceLoadAfterRedirect(newRequest); }
void JSEventListener::handleEvent(ScriptExecutionContext* scriptExecutionContext, Event* event) { ASSERT(scriptExecutionContext); if (!scriptExecutionContext || scriptExecutionContext->isJSExecutionForbidden()) return; VM& vm = scriptExecutionContext->vm(); JSLockHolder lock(vm); auto scope = DECLARE_CATCH_SCOPE(vm); // See https://dom.spec.whatwg.org/#dispatching-events spec on calling handleEvent. // "If this throws an exception, report the exception." It should not propagate the // exception. JSObject* jsFunction = this->jsFunction(scriptExecutionContext); if (!jsFunction) return; JSDOMGlobalObject* globalObject = toJSDOMGlobalObject(scriptExecutionContext, *m_isolatedWorld); if (!globalObject) return; if (scriptExecutionContext->isDocument()) { JSDOMWindow* window = jsCast<JSDOMWindow*>(globalObject); if (!window->wrapped().isCurrentlyDisplayedInFrame()) return; if (wasCreatedFromMarkup() && !scriptExecutionContext->contentSecurityPolicy()->allowInlineEventHandlers(sourceURL(), sourcePosition().m_line)) return; // FIXME: Is this check needed for other contexts? ScriptController& script = window->wrapped().frame()->script(); if (!script.canExecuteScripts(AboutToExecuteScript) || script.isPaused()) return; } ExecState* exec = globalObject->globalExec(); JSValue handleEventFunction = jsFunction; CallData callData; CallType callType = getCallData(handleEventFunction, callData); // If jsFunction is not actually a function, see if it implements the EventListener interface and use that if (callType == CallType::None) { handleEventFunction = jsFunction->get(exec, Identifier::fromString(exec, "handleEvent")); if (UNLIKELY(scope.exception())) { auto* exception = scope.exception(); scope.clearException(); event->target()->uncaughtExceptionInEventHandler(); reportException(exec, exception); return; } callType = getCallData(handleEventFunction, callData); } if (callType != CallType::None) { Ref<JSEventListener> protectedThis(*this); MarkedArgumentBuffer args; args.append(toJS(exec, globalObject, event)); Event* savedEvent = globalObject->currentEvent(); globalObject->setCurrentEvent(event); VMEntryScope entryScope(vm, vm.entryScope ? vm.entryScope->globalObject() : globalObject); InspectorInstrumentationCookie cookie = JSMainThreadExecState::instrumentFunctionCall(scriptExecutionContext, callType, callData); JSValue thisValue = handleEventFunction == jsFunction ? toJS(exec, globalObject, event->currentTarget()) : jsFunction; NakedPtr<JSC::Exception> exception; JSValue retval = scriptExecutionContext->isDocument() ? JSMainThreadExecState::profiledCall(exec, JSC::ProfilingReason::Other, handleEventFunction, callType, callData, thisValue, args, exception) : JSC::profiledCall(exec, JSC::ProfilingReason::Other, handleEventFunction, callType, callData, thisValue, args, exception); InspectorInstrumentation::didCallFunction(cookie, scriptExecutionContext); globalObject->setCurrentEvent(savedEvent); if (is<WorkerGlobalScope>(*scriptExecutionContext)) { auto scriptController = downcast<WorkerGlobalScope>(*scriptExecutionContext).script(); bool terminatorCausedException = (scope.exception() && isTerminatedExecutionException(scope.exception())); if (terminatorCausedException || scriptController->isTerminatingExecution()) scriptController->forbidExecution(); } if (exception) { event->target()->uncaughtExceptionInEventHandler(); reportException(exec, exception); } else { if (!retval.isUndefinedOrNull() && is<BeforeUnloadEvent>(*event)) downcast<BeforeUnloadEvent>(*event).setReturnValue(retval.toWTFString(exec)); if (m_isAttribute) { if (retval.isFalse()) event->preventDefault(); } } } }
void ResourceLoader::willSendRequestInternal(ResourceRequest& request, const ResourceResponse& redirectResponse) { // Protect this in this delegate method since the additional processing can do // anything including possibly derefing this; one example of this is Radar 3266216. Ref<ResourceLoader> protectedThis(*this); ASSERT(!m_reachedTerminalState); #if ENABLE(CONTENT_EXTENSIONS) ASSERT(m_resourceType != ResourceType::Invalid); #endif // We need a resource identifier for all requests, even if FrameLoader is never going to see it (such as with CORS preflight requests). bool createdResourceIdentifier = false; if (!m_identifier) { m_identifier = m_frame->page()->progress().createUniqueIdentifier(); createdResourceIdentifier = true; } #if ENABLE(CONTENT_EXTENSIONS) if (frameLoader()) { Page* page = frameLoader()->frame().page(); if (page && m_documentLoader) { if (page->userContentProvider().processContentExtensionRulesForLoad(request, m_resourceType, *m_documentLoader) == ContentExtensions::BlockedStatus::Blocked) { request = { }; didFail(blockedByContentBlockerError()); return; } } } #endif if (request.isNull()) { didFail(cannotShowURLError()); return; } if (m_options.sendLoadCallbacks() == SendCallbacks) { if (createdResourceIdentifier) frameLoader()->notifier().assignIdentifierToInitialRequest(m_identifier, documentLoader(), request); #if PLATFORM(IOS) // If this ResourceLoader was stopped as a result of assignIdentifierToInitialRequest, bail out if (m_reachedTerminalState) return; #endif frameLoader()->notifier().willSendRequest(this, request, redirectResponse); } else InspectorInstrumentation::willSendRequest(m_frame.get(), m_identifier, m_frame->loader().documentLoader(), request, redirectResponse); bool isRedirect = !redirectResponse.isNull(); if (isRedirect) platformStrategies()->loaderStrategy()->crossOriginRedirectReceived(this, request.url()); m_request = request; if (isRedirect) { auto& redirectURL = request.url(); if (!m_documentLoader->isCommitted()) frameLoader()->client().dispatchDidReceiveServerRedirectForProvisionalLoad(); if (redirectURL.protocolIsData()) { // Handle data URL decoding locally. finishNetworkLoad(); loadDataURL(); } } }
bool ResourceLoader::canAuthenticateAgainstProtectionSpace(const ProtectionSpace& protectionSpace) { Ref<ResourceLoader> protectedThis(*this); return frameLoader()->client().canAuthenticateAgainstProtectionSpace(documentLoader(), identifier(), protectionSpace); }