// static
void MixedContentChecker::logToConsoleAboutWebSocket(LocalFrame* frame, const KURL& mainResourceUrl, const KURL& url, bool allowed)
{
    String message = String::format(
        "Mixed Content: The page at '%s' was loaded over HTTPS, but attempted to connect to the insecure WebSocket endpoint '%s'. %s",
        mainResourceUrl.elidedString().utf8().data(), url.elidedString().utf8().data(),
        allowed ? "This endpoint should be available via WSS. Insecure access is deprecated." : "This request has been blocked; this endpoint must be available over WSS.");
    MessageLevel messageLevel = allowed ? WarningMessageLevel : ErrorMessageLevel;
    frame->document()->addConsoleMessage(ConsoleMessage::create(SecurityMessageSource, messageLevel, message));
}
void FrameFetchContext::printAccessDeniedMessage(const KURL& url) const
{
    if (url.isNull())
        return;

    String message;
    if (!m_document || m_document->url().isNull())
        message = "Unsafe attempt to load URL " + url.elidedString() + '.';
    else if (url.isLocalFile() || m_document->url().isLocalFile())
        message = "Unsafe attempt to load URL " + url.elidedString() + " from frame with URL " + m_document->url().elidedString() + ". 'file:' URLs are treated as unique security origins.\n";
    else
        message = "Unsafe attempt to load URL " + url.elidedString() + " from frame with URL " + m_document->url().elidedString() + ". Domains, protocols and ports must match.\n";

    frame()->document()->addConsoleMessage(ConsoleMessage::create(SecurityMessageSource, ErrorMessageLevel, message));
}
Example #3
0
ScriptPromise ServiceWorkerClients::openWindow(ScriptState* scriptState,
                                               const String& url) {
  ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState);
  ScriptPromise promise = resolver->promise();
  ExecutionContext* context = scriptState->getExecutionContext();

  KURL parsedUrl = KURL(toWorkerGlobalScope(context)->location()->url(), url);
  if (!parsedUrl.isValid()) {
    resolver->reject(V8ThrowException::createTypeError(
        scriptState->isolate(), "'" + url + "' is not a valid URL."));
    return promise;
  }

  if (!context->getSecurityOrigin()->canDisplay(parsedUrl)) {
    resolver->reject(V8ThrowException::createTypeError(
        scriptState->isolate(),
        "'" + parsedUrl.elidedString() + "' cannot be opened."));
    return promise;
  }

  if (!context->isWindowInteractionAllowed()) {
    resolver->reject(DOMException::create(InvalidAccessError,
                                          "Not allowed to open a window."));
    return promise;
  }
  context->consumeWindowInteraction();

  ServiceWorkerGlobalScopeClient::from(context)->openWindow(
      parsedUrl, WTF::makeUnique<NavigateClientCallback>(resolver));
  return promise;
}
Example #4
0
PassRefPtr<EventSource> EventSource::create(ExecutionContext* context, const String& url, const Dictionary& eventSourceInit, ExceptionState& exceptionState)
{
    if (url.isEmpty()) {
        exceptionState.throwDOMException(SyntaxError, "Cannot open an EventSource to an empty URL.");
        return 0;
    }

    KURL fullURL = context->completeURL(url);
    if (!fullURL.isValid()) {
        exceptionState.throwDOMException(SyntaxError, "Cannot open an EventSource to '" + url + "'. The URL is invalid.");
        return 0;
    }

    // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved.
    bool shouldBypassMainWorldContentSecurityPolicy = false;
    if (context->isDocument()) {
        Document* document = toDocument(context);
        shouldBypassMainWorldContentSecurityPolicy = document->frame()->script().shouldBypassMainWorldContentSecurityPolicy();
    }
    if (!shouldBypassMainWorldContentSecurityPolicy && !context->contentSecurityPolicy()->allowConnectToSource(fullURL)) {
        // We can safely expose the URL to JavaScript, as this exception is generate synchronously before any redirects take place.
        exceptionState.throwSecurityError("Refused to connect to '" + fullURL.elidedString() + "' because it violates the document's Content Security Policy.");
        return 0;
    }

    RefPtr<EventSource> source = adoptRef(new EventSource(context, fullURL, eventSourceInit));

    source->setPendingActivity(source.get());
    source->scheduleInitialConnect();
    source->suspendIfNeeded();

    return source.release();
}
EventSource* EventSource::create(ExecutionContext* context, const String& url, const EventSourceInit& eventSourceInit, ExceptionState& exceptionState)
{
    if (url.isEmpty()) {
        exceptionState.throwDOMException(SyntaxError, "Cannot open an EventSource to an empty URL.");
        return nullptr;
    }

    KURL fullURL = context->completeURL(url);
    if (!fullURL.isValid()) {
        exceptionState.throwDOMException(SyntaxError, "Cannot open an EventSource to '" + url + "'. The URL is invalid.");
        return nullptr;
    }

    // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved.
    if (!ContentSecurityPolicy::shouldBypassMainWorld(context) && !context->contentSecurityPolicy()->allowConnectToSource(fullURL)) {
        // We can safely expose the URL to JavaScript, as this exception is generate synchronously before any redirects take place.
        exceptionState.throwSecurityError("Refused to connect to '" + fullURL.elidedString() + "' because it violates the document's Content Security Policy.");
        return nullptr;
    }

    EventSource* source = new EventSource(context, fullURL, eventSourceInit);

    source->scheduleInitialConnect();
    source->suspendIfNeeded();
    return source;
}
KURL AbstractWorker::resolveURL(const String& url, ExceptionState& es)
{
    if (url.isEmpty()) {
        es.throwDOMException(SyntaxError, "Failed to create a worker: an empty URL was provided.");
        return KURL();
    }

    // FIXME: This should use the dynamic global scope (bug #27887)
    KURL scriptURL = executionContext()->completeURL(url);
    if (!scriptURL.isValid()) {
        es.throwDOMException(SyntaxError, "Failed to create a worker: '" + url + "' is not a valid URL.");
        return KURL();
    }

    // We can safely expose the URL in the following exceptions, as these checks happen synchronously before redirection. JavaScript receives no new information.
    if (!executionContext()->securityOrigin()->canRequest(scriptURL)) {
        es.throwSecurityError("Failed to create a worker: script at '" + scriptURL.elidedString() + "' cannot be accessed from origin '" + executionContext()->securityOrigin()->toString() + "'.");
        return KURL();
    }

    if (executionContext()->contentSecurityPolicy() && !executionContext()->contentSecurityPolicy()->allowScriptFromSource(scriptURL)) {
        es.throwSecurityError("Failed to create a worker: access to the script at '" + scriptURL.elidedString() + "' is denied by the document's Content Security Policy.");
        return KURL();
    }

    return scriptURL;
}
bool MixedContentChecker::isMixedFormAction(LocalFrame* frame, const KURL& url, ReportingStatus reportingStatus)
{
    // For whatever reason, some folks handle forms via JavaScript, and submit to `javascript:void(0)`
    // rather than calling `preventDefault()`. We special-case `javascript:` URLs here, as they don't
    // introduce MixedContent for form submissions.
    if (url.protocolIs("javascript"))
        return false;

    Frame* mixedFrame = inWhichFrameIsContentMixed(frame, WebURLRequest::FrameTypeNone, url);
    if (!mixedFrame)
        return false;

    UseCounter::count(mixedFrame, UseCounter::MixedContentPresent);

    // Use the current local frame's client; the embedder doesn't
    // distinguish mixed content signals from different frames on the
    // same page.
    frame->loader().client()->didDisplayInsecureContent();

    if (reportingStatus == SendReport) {
        String message = String::format(
            "Mixed Content: The page at '%s' was loaded over a secure connection, but contains a form which targets an insecure endpoint '%s'. This endpoint should be made available over a secure connection.",
            mainResourceUrlForFrame(mixedFrame).elidedString().utf8().data(), url.elidedString().utf8().data());
        frame->document()->addConsoleMessage(ConsoleMessage::create(SecurityMessageSource, WarningMessageLevel, message));
    }

    return true;
}
Example #8
0
void ResourceFetcher::printAccessDeniedMessage(const KURL& url) const
{
    if (url.isNull())
        return;

    if (!frame())
        return;

    String message;
    if (!m_document || m_document->url().isNull())
        message = "Unsafe attempt to load URL " + url.elidedString() + '.';
    else
        message = "Unsafe attempt to load URL " + url.elidedString() + " from frame with URL " + m_document->url().elidedString() + ". Domains, protocols and ports must match.\n";

    frame()->document()->addConsoleMessage(ConsoleMessage::create(SecurityMessageSource, ErrorMessageLevel, message));
}
Example #9
0
bool HTMLPlugInElement::allowedToLoadPlugin(const KURL& url, const String& mimeType)
{
    if (document().isSandboxed(SandboxPlugins)) {
        document().addConsoleMessage(ConsoleMessage::create(SecurityMessageSource, ErrorMessageLevel,
            "Failed to load '" + url.elidedString() + "' as a plugin, because the frame into which the plugin is loading is sandboxed."));
        return false;
    }
    return true;
}
// static
void MixedContentChecker::logToConsoleAboutFetch(LocalFrame* frame, const KURL& mainResourceUrl, const KURL& url, WebURLRequest::RequestContext requestContext, bool allowed)
{
    String message = String::format(
        "Mixed Content: The page at '%s' was loaded over HTTPS, but requested an insecure %s '%s'. %s",
        mainResourceUrl.elidedString().utf8().data(), typeNameFromContext(requestContext), url.elidedString().utf8().data(),
        allowed ? "This content should also be served over HTTPS." : "This request has been blocked; the content must be served over HTTPS.");
    MessageLevel messageLevel = allowed ? WarningMessageLevel : ErrorMessageLevel;
    frame->document()->addConsoleMessage(ConsoleMessage::create(SecurityMessageSource, messageLevel, message));
}
void MixedContentChecker::logWarning(bool allowed, const KURL& target, const MixedContentType type) const
{
    StringBuilder message;
    message.append((allowed ? "" : "[blocked] "));
    message.append("The page at '" + m_frame->document()->url().elidedString() + "' was loaded over HTTPS, but ");
    switch (type) {
    case Display:
        message.append("displayed insecure content from '" + target.elidedString() + "': this content should also be loaded over HTTPS.\n");
        break;
    case Execution:
    case WebSocket:
        message.append("ran insecure content from '" + target.elidedString() + "': this content should also be loaded over HTTPS.\n");
        break;
    case Submission:
        message.append("is submitting data to an insecure location at '" + target.elidedString() + "': this content should also be submitted over HTTPS.\n");
        break;
    }
    MessageLevel messageLevel = allowed ? WarningMessageLevel : ErrorMessageLevel;
    m_frame->document()->addConsoleMessage(ConsoleMessage::create(SecurityMessageSource, messageLevel, message.toString()));
}
Example #12
0
void History::stateObjectAdded(PassRefPtr<SerializedScriptValue> data, const String& /* title */, const String& urlString, UpdateBackForwardListPolicy updateBackForwardListPolicy, ExceptionState& exceptionState)
{
    if (!m_frame || !m_frame->page())
        return;

    KURL fullURL = urlForState(urlString);
    if (!fullURL.isValid() || !m_frame->document()->securityOrigin()->canRequest(fullURL)) {
        // We can safely expose the URL to JavaScript, as a) no redirection takes place: JavaScript already had this URL, b) JavaScript can only access a same-origin History object.
        exceptionState.throwSecurityError("A history state object with URL '" + fullURL.elidedString() + "' cannot be created in a document with origin '" + m_frame->document()->securityOrigin()->toString() + "'.");
        return;
    }
    m_frame->loader().updateForSameDocumentNavigation(fullURL, SameDocumentNavigationHistoryApi, data, updateBackForwardListPolicy);
}
void History::stateObjectAdded(PassRefPtr<SerializedScriptValue> data, const String& /* title */, const String& urlString, HistoryScrollRestorationType restorationType, FrameLoadType type, ExceptionState& exceptionState)
{
    if (!m_frame || !m_frame->page() || !m_frame->loader().documentLoader())
        return;

    KURL fullURL = urlForState(urlString);
    if (!canChangeToUrl(fullURL, m_frame->document()->getSecurityOrigin(), m_frame->document()->url())) {
        // We can safely expose the URL to JavaScript, as a) no redirection takes place: JavaScript already had this URL, b) JavaScript can only access a same-origin History object.
        exceptionState.throwSecurityError("A history state object with URL '" + fullURL.elidedString() + "' cannot be created in a document with origin '" + m_frame->document()->getSecurityOrigin()->toString() + "' and URL '" + m_frame->document()->url().elidedString() + "'.");
        return;
    }

    m_frame->loader().updateForSameDocumentNavigation(fullURL, SameDocumentNavigationHistoryApi, data, restorationType, type);
}
bool SubresourceIntegrity::CheckSubresourceIntegrity(const IntegrityMetadataSet& metadataSet, const Element& element, const char* content, size_t size, const KURL& resourceUrl, const Resource& resource)
{
    Document& document = element.document();

    if (!resource.isEligibleForIntegrityCheck(document.getSecurityOrigin())) {
        UseCounter::count(document, UseCounter::SRIElementIntegrityAttributeButIneligible);
        logErrorToConsole("Subresource Integrity: The resource '" + resourceUrl.elidedString() + "' has an integrity attribute, but the resource requires the request to be CORS enabled to check the integrity, and it is not. The resource has been blocked because the integrity cannot be enforced.", document);
        return false;
    }

    String errorMessage;
    bool result = CheckSubresourceIntegrity(metadataSet, content, size, resourceUrl, document, errorMessage);
    if (!result)
        logErrorToConsole(errorMessage, document);
    return result;
}
Example #15
0
bool CSPDirectiveList::checkSourceAndReportViolation(SourceListDirective* directive, const KURL& url, const String& effectiveDirective) const
{
    if (checkSource(directive, url))
        return true;

    String prefix;
    if (ContentSecurityPolicy::BaseURI == effectiveDirective)
        prefix = "Refused to set the document's base URI to '";
    else if (ContentSecurityPolicy::ChildSrc == effectiveDirective)
        prefix = "Refused to create a child context containing '";
    else if (ContentSecurityPolicy::ConnectSrc == effectiveDirective)
        prefix = "Refused to connect to '";
    else if (ContentSecurityPolicy::FontSrc == effectiveDirective)
        prefix = "Refused to load the font '";
    else if (ContentSecurityPolicy::FormAction == effectiveDirective)
        prefix = "Refused to send form data to '";
    else if (ContentSecurityPolicy::FrameSrc == effectiveDirective)
        prefix = "Refused to frame '";
    else if (ContentSecurityPolicy::ImgSrc == effectiveDirective)
        prefix = "Refused to load the image '";
    else if (ContentSecurityPolicy::MediaSrc == effectiveDirective)
        prefix = "Refused to load media from '";
    else if (ContentSecurityPolicy::ManifestSrc == effectiveDirective)
        prefix = "Refused to load manifest from '";
    else if (ContentSecurityPolicy::ObjectSrc == effectiveDirective)
        prefix = "Refused to load plugin data from '";
    else if (ContentSecurityPolicy::ScriptSrc == effectiveDirective)
        prefix = "Refused to load the script '";
    else if (ContentSecurityPolicy::StyleSrc == effectiveDirective)
        prefix = "Refused to load the stylesheet '";

    String suffix = String();
    if (directive == m_defaultSrc)
        suffix = " Note that '" + effectiveDirective + "' was not explicitly set, so 'default-src' is used as a fallback.";

    reportViolation(directive->text(), effectiveDirective, prefix + url.elidedString() + "' because it violates the following Content Security Policy directive: \"" + directive->text() + "\"." + suffix + "\n", url);
    return denyIfEnforcingPolicy();
}
ScriptPromise ServiceWorkerWindowClient::navigate(ScriptState* scriptState,
                                                  const String& url) {
  ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState);
  ScriptPromise promise = resolver->promise();
  ExecutionContext* context = scriptState->getExecutionContext();

  KURL parsedUrl = KURL(toWorkerGlobalScope(context)->location()->url(), url);
  if (!parsedUrl.isValid() || parsedUrl.protocolIsAbout()) {
    resolver->reject(V8ThrowException::createTypeError(
        scriptState->isolate(), "'" + url + "' is not a valid URL."));
    return promise;
  }
  if (!context->getSecurityOrigin()->canDisplay(parsedUrl)) {
    resolver->reject(V8ThrowException::createTypeError(
        scriptState->isolate(),
        "'" + parsedUrl.elidedString() + "' cannot navigate."));
    return promise;
  }

  ServiceWorkerGlobalScopeClient::from(context)->navigate(
      uuid(), parsedUrl, new NavigateClientCallback(resolver));
  return promise;
}
bool NavigatorBeacon::canSendBeacon(ExecutionContext* context, const KURL& url, ExceptionState& exceptionState)
{
    if (!url.isValid()) {
        exceptionState.throwDOMException(SyntaxError, "The URL argument is ill-formed or unsupported.");
        return false;
    }
    // For now, only support HTTP and related.
    if (!url.protocolIsInHTTPFamily()) {
        exceptionState.throwDOMException(SyntaxError, "Beacons are only supported over HTTP(S).");
        return false;
    }
    // FIXME: CSP is not enforced on redirects, crbug.com/372197
    if (!ContentSecurityPolicy::shouldBypassMainWorld(context) && !context->contentSecurityPolicy()->allowConnectToSource(url)) {
        // We can safely expose the URL to JavaScript, as these checks happen synchronously before redirection. JavaScript receives no new information.
        exceptionState.throwSecurityError("Refused to send beacon to '" + url.elidedString() + "' because it violates the document's Content Security Policy.");
        return false;
    }

    // If detached from frame, do not allow sending a Beacon.
    if (!frame() || !frame()->client())
        return false;

    return true;
}
Example #18
0
bool CSPDirectiveList::allowPluginType(const String& type, const String& typeAttribute, const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
{
    return reportingStatus == ContentSecurityPolicy::SendReport ?
        checkMediaTypeAndReportViolation(m_pluginTypes.get(), type, typeAttribute, "Refused to load '" + url.elidedString() + "' (MIME type '" + typeAttribute + "') because it violates the following Content Security Policy Directive: ") :
        checkMediaType(m_pluginTypes.get(), type, typeAttribute);
}
Example #19
0
bool CSPDirectiveList::checkAncestorsAndReportViolation(SourceListDirective* directive, LocalFrame* frame, const KURL& url) const
{
    if (checkAncestors(directive, frame))
        return true;

    reportViolationWithFrame(directive->text(), "frame-ancestors", "Refused to display '" + url.elidedString() + "' in a frame because an ancestor violates the following Content Security Policy directive: \"" + directive->text() + "\".", url, frame);
    return denyIfEnforcingPolicy();
}
bool SubresourceIntegrity::CheckSubresourceIntegrity(const IntegrityMetadataSet& metadataSet, const char* content, size_t size, const KURL& resourceUrl, Document& document, String& errorMessage)
{
    if (!metadataSet.size())
        return true;

    HashAlgorithm strongestAlgorithm = HashAlgorithmSha256;
    for (const IntegrityMetadata& metadata : metadataSet)
        strongestAlgorithm = getPrioritizedHashFunction(metadata.algorithm(), strongestAlgorithm);

    DigestValue digest;
    for (const IntegrityMetadata& metadata : metadataSet) {
        if (metadata.algorithm() != strongestAlgorithm)
            continue;

        digest.clear();
        bool digestSuccess = computeDigest(metadata.algorithm(), content, size, digest);

        if (digestSuccess) {
            Vector<char> hashVector;
            base64Decode(metadata.digest(), hashVector);
            DigestValue convertedHashVector;
            convertedHashVector.append(reinterpret_cast<uint8_t*>(hashVector.data()), hashVector.size());

            if (DigestsEqual(digest, convertedHashVector)) {
                UseCounter::count(document, UseCounter::SRIElementWithMatchingIntegrityAttribute);
                return true;
            }
        }
    }

    digest.clear();
    if (computeDigest(HashAlgorithmSha256, content, size, digest)) {
        // This message exposes the digest of the resource to the console.
        // Because this is only to the console, that's okay for now, but we
        // need to be very careful not to expose this in exceptions or
        // JavaScript, otherwise it risks exposing information about the
        // resource cross-origin.
        errorMessage = "Failed to find a valid digest in the 'integrity' attribute for resource '" + resourceUrl.elidedString() + "' with computed SHA-256 integrity '" + digestToString(digest) + "'. The resource has been blocked.";
    } else {
        errorMessage = "There was an error computing an integrity value for resource '" + resourceUrl.elidedString() + "'. The resource has been blocked.";
    }
    UseCounter::count(document, UseCounter::SRIElementWithNonMatchingIntegrityAttribute);
    return false;
}
void MixedContentChecker::logWarning(bool allowed, const String& action, const KURL& target) const
{
    String message = makeString((allowed ? "" : "[blocked] "), "The page at ", m_frame->document()->url().elidedString(), " ", action, " insecure content from ", target.elidedString(), ".\n");
    m_frame->document()->addConsoleMessage(SecurityMessageSource, WarningMessageLevel, message);
}
void SharedWorkerRepositoryClientImpl::connect(SharedWorker* worker, PassOwnPtr<WebMessagePortChannel> port, const KURL& url, const String& name, ExceptionState& exceptionState)
{
    ASSERT(m_client);

    // No nested workers (for now) - connect() should only be called from document context.
    ASSERT(worker->executionContext()->isDocument());
    Document* document = toDocument(worker->executionContext());

    // TODO(estark): this is broken, as it only uses the first header
    // when multiple might have been sent. Fix by making the
    // SharedWorkerConnector interface take a map that can contain
    // multiple headers.
    OwnPtr<Vector<CSPHeaderAndType>> headers = worker->executionContext()->contentSecurityPolicy()->headers();
    WebString header;
    WebContentSecurityPolicyType headerType = WebContentSecurityPolicyTypeReport;

    if (headers->size() > 0) {
        header = (*headers)[0].first;
        headerType = static_cast<WebContentSecurityPolicyType>((*headers)[0].second);
    }

    WebWorkerCreationError creationError;
    String unusedSecureContextError;
    bool isSecureContext = worker->executionContext()->isSecureContext(unusedSecureContextError);
    OwnPtr<WebSharedWorkerConnector> webWorkerConnector = adoptPtr(m_client->createSharedWorkerConnector(url, name, getId(document), header, headerType, isSecureContext ? WebSharedWorkerCreationContextTypeSecure : WebSharedWorkerCreationContextTypeNonsecure, &creationError));
    if (creationError != WebWorkerCreationErrorNone) {
        if (creationError == WebWorkerCreationErrorURLMismatch) {
            // Existing worker does not match this url, so return an error back to the caller.
            exceptionState.throwDOMException(URLMismatchError, "The location of the SharedWorker named '" + name + "' does not exactly match the provided URL ('" + url.elidedString() + "').");
            return;
        } else if (creationError == WebWorkerCreationErrorSecureContextMismatch) {
            if (isSecureContext) {
                UseCounter::count(document, UseCounter::NonSecureSharedWorkerAccessedFromSecureContext);
            } else {
                UseCounter::count(document, UseCounter::SecureSharedWorkerAccessedFromNonSecureContext);
            }
        }
    }

    // The connector object manages its own lifecycle (and the lifecycles of the two worker objects).
    // It will free itself once connecting is completed.
    SharedWorkerConnector* connector = new SharedWorkerConnector(worker, url, name, port, webWorkerConnector.release());
    connector->connect();
}
Example #23
0
CachedResourceHandle<CachedResource> CachedResourceLoader::requestResource(CachedResource::Type type, CachedResourceRequest& request)
{
    KURL url = request.resourceRequest().url();
    
    LOG(ResourceLoading, "CachedResourceLoader::requestResource '%s', charset '%s', priority=%d, forPreload=%u", url.elidedString().latin1().data(), request.charset().latin1().data(), request.priority(), request.forPreload());
    
    // If only the fragment identifiers differ, it is the same resource.
    url = MemoryCache::removeFragmentIdentifierIfNeeded(url);

    if (!url.isValid())
        return 0;

    if (!canRequest(type, url, request.forPreload()))
        return 0;

    if (Frame* f = frame())
        f->loader()->client()->dispatchWillRequestResource(&request);

    if (memoryCache()->disabled()) {
        DocumentResourceMap::iterator it = m_documentResources.find(url.string());
        if (it != m_documentResources.end()) {
            it->value->setOwningCachedResourceLoader(0);
            m_documentResources.remove(it);
        }
    }

    // See if we can use an existing resource from the cache.
    CachedResourceHandle<CachedResource> resource;
#if ENABLE(CACHE_PARTITIONING)
    if (document())
        request.mutableResourceRequest().setCachePartition(document()->topOrigin()->cachePartition());
#endif

    resource = memoryCache()->resourceForRequest(request.resourceRequest());

    const RevalidationPolicy policy = determineRevalidationPolicy(type, request.mutableResourceRequest(), request.forPreload(), resource.get(), request.defer());
    switch (policy) {
    case Reload:
        memoryCache()->remove(resource.get());
        // Fall through
    case Load:
        resource = loadResource(type, request, request.charset());
        break;
    case Revalidate:
        resource = revalidateResource(request, resource.get());
        break;
    case Use:
        memoryCache()->resourceAccessed(resource.get());
        notifyLoadedFromMemoryCache(resource.get());
        break;
    }

    if (!resource)
        return 0;

    if (!request.forPreload() || policy != Use)
        resource->setLoadPriority(request.priority());

    if ((policy != Use || resource->stillNeedsLoad()) && CachedResourceRequest::NoDefer == request.defer()) {
        resource->load(this, request.options());

        // We don't support immediate loads, but we do support immediate failure.
        if (resource->errorOccurred()) {
            if (resource->inCache())
                memoryCache()->remove(resource.get());
            return 0;
        }
    }

#if PLATFORM(CHROMIUM)
    // FIXME: Temporarily leave main resource caching disabled for chromium, see https://bugs.webkit.org/show_bug.cgi?id=107962
    // Ensure main resources aren't preloaded, and other main resource loads are removed from cache to prevent reuse.
    if (type == CachedResource::MainResource) {
        ASSERT(policy != Use);
        ASSERT(policy != Revalidate);
        memoryCache()->remove(resource.get());
        if (request.forPreload())
            return 0;
    }
#endif

    if (!request.resourceRequest().url().protocolIsData())
        m_validatedURLs.add(request.resourceRequest().url());

    ASSERT(resource->url() == url.string());
    m_documentResources.set(resource->url(), resource);
    return resource;
}
void MixedContentChecker::logWarning(bool allowed, const String& action, const KURL& target) const
{
    String message = String(allowed ? "" : "[blocked] ") + "The page at '" + m_frame->document()->url().elidedString() + "' was loaded over HTTPS, but " + action + " insecure content from '" + target.elidedString() + "': this content should also be loaded over HTTPS.\n";
    m_frame->document()->addConsoleMessage(SecurityMessageSource, WarningMessageLevel, message);
}
void FrameFetchContext::reportLocalLoadFailed(const KURL& url)
{
    FrameLoader::reportLocalLoadFailed(m_frame, url.elidedString());
}
ResourceRequestBlockedReason FrameFetchContext::canRequestInternal(Resource::Type type, const ResourceRequest& resourceRequest, const KURL& url, const ResourceLoaderOptions& options, bool forPreload, FetchRequest::OriginRestriction originRestriction) const
{
    InstrumentingAgents* agents = InspectorInstrumentation::instrumentingAgentsFor(frame());
    if (agents && agents->inspectorResourceAgent()) {
        if (agents->inspectorResourceAgent()->shouldBlockRequest(resourceRequest))
            return ResourceRequestBlockedReasonInspector;
    }

    SecurityOrigin* securityOrigin = options.securityOrigin.get();
    if (!securityOrigin && m_document)
        securityOrigin = m_document->securityOrigin();

    if (originRestriction != FetchRequest::NoOriginRestriction && securityOrigin && !securityOrigin->canDisplay(url)) {
        if (!forPreload)
            FrameLoader::reportLocalLoadFailed(frame(), url.elidedString());
        WTF_LOG(ResourceLoading, "ResourceFetcher::requestResource URL was not allowed by SecurityOrigin::canDisplay");
        return ResourceRequestBlockedReasonOther;
    }

    // Some types of resources can be loaded only from the same origin. Other
    // types of resources, like Images, Scripts, and CSS, can be loaded from
    // any URL.
    switch (type) {
    case Resource::MainResource:
    case Resource::Image:
    case Resource::CSSStyleSheet:
    case Resource::Script:
    case Resource::Font:
    case Resource::Raw:
    case Resource::LinkPrefetch:
    case Resource::LinkSubresource:
    case Resource::LinkPreload:
    case Resource::TextTrack:
    case Resource::ImportResource:
    case Resource::Media:
        // By default these types of resources can be loaded from any origin.
        // FIXME: Are we sure about Resource::Font?
        if (originRestriction == FetchRequest::RestrictToSameOrigin && !securityOrigin->canRequest(url)) {
            printAccessDeniedMessage(url);
            return ResourceRequestBlockedReasonOrigin;
        }
        break;
    case Resource::XSLStyleSheet:
        ASSERT(RuntimeEnabledFeatures::xsltEnabled());
    case Resource::SVGDocument:
        if (!securityOrigin->canRequest(url)) {
            printAccessDeniedMessage(url);
            return ResourceRequestBlockedReasonOrigin;
        }
        break;
    }

    // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved.
    bool shouldBypassMainWorldCSP = frame()->script().shouldBypassMainWorldCSP() || options.contentSecurityPolicyOption == DoNotCheckContentSecurityPolicy;

    // Don't send CSP messages for preloads, we might never actually display those items.
    ContentSecurityPolicy::ReportingStatus cspReporting = forPreload ?
        ContentSecurityPolicy::SuppressReport : ContentSecurityPolicy::SendReport;

    // As of CSP2, for requests that are the results of redirects, the match
    // algorithm should ignore the path component of the URL.
    ContentSecurityPolicy::RedirectStatus redirectStatus = resourceRequest.followedRedirect() ? ContentSecurityPolicy::DidRedirect : ContentSecurityPolicy::DidNotRedirect;

    // m_document can be null, but not in any of the cases where csp is actually used below.
    // ImageResourceTest.MultipartImage crashes w/o the m_document null check.
    // I believe it's the Resource::Raw case.
    const ContentSecurityPolicy* csp = m_document ? m_document->contentSecurityPolicy() : nullptr;

    // FIXME: This would be cleaner if moved this switch into an allowFromSource()
    // helper on this object which took a Resource::Type, then this block would
    // collapse to about 10 lines for handling Raw and Script special cases.
    switch (type) {
    case Resource::XSLStyleSheet:
        ASSERT(RuntimeEnabledFeatures::xsltEnabled());
        ASSERT(ContentSecurityPolicy::isScriptResource(resourceRequest));
        if (!shouldBypassMainWorldCSP && !csp->allowScriptFromSource(url, redirectStatus, cspReporting))
            return ResourceRequestBlockedReasonCSP;
        break;
    case Resource::Script:
    case Resource::ImportResource:
        ASSERT(ContentSecurityPolicy::isScriptResource(resourceRequest));
        if (!shouldBypassMainWorldCSP && !csp->allowScriptFromSource(url, redirectStatus, cspReporting))
            return ResourceRequestBlockedReasonCSP;

        if (!frame()->loader().client()->allowScriptFromSource(!frame()->settings() || frame()->settings()->scriptEnabled(), url)) {
            frame()->loader().client()->didNotAllowScript();
            return ResourceRequestBlockedReasonCSP;
        }
        break;
    case Resource::CSSStyleSheet:
        ASSERT(ContentSecurityPolicy::isStyleResource(resourceRequest));
        if (!shouldBypassMainWorldCSP && !csp->allowStyleFromSource(url, redirectStatus, cspReporting))
            return ResourceRequestBlockedReasonCSP;
        break;
    case Resource::SVGDocument:
    case Resource::Image:
        ASSERT(ContentSecurityPolicy::isImageResource(resourceRequest));
        if (!shouldBypassMainWorldCSP && !csp->allowImageFromSource(url, redirectStatus, cspReporting))
            return ResourceRequestBlockedReasonCSP;
        break;
    case Resource::Font: {
        ASSERT(ContentSecurityPolicy::isFontResource(resourceRequest));
        if (!shouldBypassMainWorldCSP && !csp->allowFontFromSource(url, redirectStatus, cspReporting))
            return ResourceRequestBlockedReasonCSP;
        break;
    }
    case Resource::MainResource:
    case Resource::Raw:
    case Resource::LinkPrefetch:
    case Resource::LinkSubresource:
    case Resource::LinkPreload:
        break;
    case Resource::Media:
    case Resource::TextTrack:
        ASSERT(ContentSecurityPolicy::isMediaResource(resourceRequest));
        if (!shouldBypassMainWorldCSP && !csp->allowMediaFromSource(url, redirectStatus, cspReporting))
            return ResourceRequestBlockedReasonCSP;

        if (!frame()->loader().client()->allowMedia(url))
            return ResourceRequestBlockedReasonOther;
        break;
    }

    // SVG Images have unique security rules that prevent all subresource requests
    // except for data urls.
    if (type != Resource::MainResource && frame()->chromeClient().isSVGImageChromeClient() && !url.protocolIsData())
        return ResourceRequestBlockedReasonOrigin;

    // FIXME: Once we use RequestContext for CSP (http://crbug.com/390497), remove this extra check.
    if (resourceRequest.requestContext() == WebURLRequest::RequestContextManifest) {
        if (!shouldBypassMainWorldCSP && !csp->allowManifestFromSource(url, redirectStatus, cspReporting))
            return ResourceRequestBlockedReasonCSP;
    }

    // Measure the number of legacy URL schemes ('ftp://') and the number of embedded-credential
    // ('http://*****:*****@...') resources embedded as subresources. in the hopes that we can
    // block them at some point in the future.
    if (resourceRequest.frameType() != WebURLRequest::FrameTypeTopLevel) {
        ASSERT(frame()->document());
        if (SchemeRegistry::shouldTreatURLSchemeAsLegacy(url.protocol()) && !SchemeRegistry::shouldTreatURLSchemeAsLegacy(frame()->document()->securityOrigin()->protocol()))
            UseCounter::count(frame()->document(), UseCounter::LegacyProtocolEmbeddedAsSubresource);
        if (!url.user().isEmpty() || !url.pass().isEmpty())
            UseCounter::count(frame()->document(), UseCounter::RequestedSubresourceWithEmbeddedCredentials);
    }

    // Measure the number of pages that load resources after a redirect
    // when a CSP is active, to see if implementing CSP
    // 'unsafe-redirect' is feasible.
    if (csp && csp->isActive() && resourceRequest.frameType() != WebURLRequest::FrameTypeTopLevel && resourceRequest.frameType() != WebURLRequest::FrameTypeAuxiliary && redirectStatus == ContentSecurityPolicy::DidRedirect) {
        ASSERT(frame()->document());
        UseCounter::count(frame()->document(), UseCounter::ResourceLoadedAfterRedirectWithCSP);
    }

    // Last of all, check for mixed content. We do this last so that when
    // folks block mixed content with a CSP policy, they don't get a warning.
    // They'll still get a warning in the console about CSP blocking the load.
    MixedContentChecker::ReportingStatus mixedContentReporting = forPreload ?
        MixedContentChecker::SuppressReport : MixedContentChecker::SendReport;
    if (MixedContentChecker::shouldBlockFetch(MixedContentChecker::effectiveFrameForFrameType(frame(), resourceRequest.frameType()), resourceRequest, url, mixedContentReporting))
        return ResourceRequestBlockedReasonMixedContent;

    return ResourceRequestBlockedReasonNone;
}
Example #27
0
bool CachedResourceLoader::canRequest(CachedResource::Type type, const KURL& url, bool forPreload)
{
    if (document() && !document()->securityOrigin()->canDisplay(url)) {
        if (!forPreload)
            FrameLoader::reportLocalLoadFailed(frame(), url.elidedString());
        LOG(ResourceLoading, "CachedResourceLoader::requestResource URL was not allowed by SecurityOrigin::canDisplay");
        return 0;
    }

    // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved.
    bool shouldBypassMainWorldContentSecurityPolicy = (frame() && frame()->script()->shouldBypassMainWorldContentSecurityPolicy());

    // Some types of resources can be loaded only from the same origin.  Other
    // types of resources, like Images, Scripts, and CSS, can be loaded from
    // any URL.
    switch (type) {
    case CachedResource::MainResource:
    case CachedResource::ImageResource:
    case CachedResource::CSSStyleSheet:
    case CachedResource::Script:
    case CachedResource::FontResource:
    case CachedResource::RawResource:
#if ENABLE(LINK_PREFETCH)
    case CachedResource::LinkPrefetch:
    case CachedResource::LinkSubresource:
#endif
#if ENABLE(VIDEO_TRACK)
    case CachedResource::TextTrackResource:
#endif
#if ENABLE(CSS_SHADERS)
    case CachedResource::ShaderResource:
#endif
        // These types of resources can be loaded from any origin.
        // FIXME: Are we sure about CachedResource::FontResource?
        break;
#if ENABLE(SVG)
    case CachedResource::SVGDocumentResource:
#endif
#if ENABLE(XSLT)
    case CachedResource::XSLStyleSheet:
        if (!m_document->securityOrigin()->canRequest(url)) {
            printAccessDeniedMessage(url);
            return false;
        }
#endif
        break;
    }

    switch (type) {
#if ENABLE(XSLT)
    case CachedResource::XSLStyleSheet:
        if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowScriptFromSource(url))
            return false;
        break;
#endif
    case CachedResource::Script:
        if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowScriptFromSource(url))
            return false;

        if (frame()) {
            Settings* settings = frame()->settings();
            if (!frame()->loader()->client()->allowScriptFromSource(!settings || settings->isScriptEnabled(), url)) {
                frame()->loader()->client()->didNotAllowScript();
                return false;
            }
        }
        break;
#if ENABLE(CSS_SHADERS)
    case CachedResource::ShaderResource:
        // Since shaders are referenced from CSS Styles use the same rules here.
#endif
    case CachedResource::CSSStyleSheet:
        if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowStyleFromSource(url))
            return false;
        break;
#if ENABLE(SVG)
    case CachedResource::SVGDocumentResource:
#endif
    case CachedResource::ImageResource:
        if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowImageFromSource(url))
            return false;
        break;
    case CachedResource::FontResource: {
        if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowFontFromSource(url))
            return false;
        break;
    }
    case CachedResource::MainResource:
    case CachedResource::RawResource:
#if ENABLE(LINK_PREFETCH)
    case CachedResource::LinkPrefetch:
    case CachedResource::LinkSubresource:
#endif
        break;
#if ENABLE(VIDEO_TRACK)
    case CachedResource::TextTrackResource:
        // Cues aren't called out in the CPS spec yet, but they only work with a media element
        // so use the media policy.
        if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowMediaFromSource(url))
            return false;
        break;
#endif
    }

    // Last of all, check for insecure content. We do this last so that when
    // folks block insecure content with a CSP policy, they don't get a warning.
    // They'll still get a warning in the console about CSP blocking the load.

    // FIXME: Should we consider forPreload here?
    if (!checkInsecureContent(type, url))
        return false;

    return true;
}
Example #28
0
ResourcePtr<Resource> ResourceFetcher::requestResource(Resource::Type type, FetchRequest& request)
{
    ASSERT(request.options().synchronousPolicy == RequestAsynchronously || type == Resource::Raw);

    TRACE_EVENT0("blink", "ResourceFetcher::requestResource");

    KURL url = request.resourceRequest().url();

    WTF_LOG(ResourceLoading, "ResourceFetcher::requestResource '%s', charset '%s', priority=%d, type=%s", url.elidedString().latin1().data(), request.charset().latin1().data(), request.priority(), ResourceTypeName(type));

    // If only the fragment identifiers differ, it is the same resource.
    url = MemoryCache::removeFragmentIdentifierIfNeeded(url);

    if (!url.isValid())
        return 0;

    if (!canRequest(type, url, request.options(), request.originRestriction()))
        return 0;

    if (LocalFrame* f = frame())
        f->loaderClient()->dispatchWillRequestResource(&request);

    // See if we can use an existing resource from the cache.
    ResourcePtr<Resource> resource = memoryCache()->resourceForURL(url);

    const RevalidationPolicy policy = determineRevalidationPolicy(type, request, resource.get());
    switch (policy) {
    case Reload:
        memoryCache()->remove(resource.get());
        // Fall through
    case Load:
        resource = createResourceForLoading(type, request, request.charset());
        break;
    case Revalidate:
        resource = createResourceForRevalidation(request, resource.get());
        break;
    case Use:
        memoryCache()->updateForAccess(resource.get());
        break;
    }

    if (!resource)
        return 0;

    if (!resource->hasClients())
        m_deadStatsRecorder.update(policy);

    if (policy != Use)
        resource->setIdentifier(createUniqueIdentifier());

    ResourceLoadPriority priority = loadPriority(type, request);
    if (priority != resource->resourceRequest().priority()) {
        resource->mutableResourceRequest().setPriority(priority);
        resource->didChangePriority(priority, 0);
    }

    if (resourceNeedsLoad(resource.get(), request, policy)) {
        if (!shouldLoadNewResource(type)) {
            if (memoryCache()->contains(resource.get()))
                memoryCache()->remove(resource.get());
            return 0;
        }

        resource->load(this, request.options());

        // For asynchronous loads that immediately fail, it's sufficient to return a
        // null Resource, as it indicates that something prevented the load from starting.
        // If there's a network error, that failure will happen asynchronously. However, if
        // a sync load receives a network error, it will have already happened by this point.
        // In that case, the requester should have access to the relevant ResourceError, so
        // we need to return a non-null Resource.
        if (resource->errorOccurred()) {
            if (memoryCache()->contains(resource.get()))
                memoryCache()->remove(resource.get());
            return 0;
        }
    }

    requestLoadStarted(resource.get(), request, policy == Use ? ResourceLoadingFromCache : ResourceLoadingFromNetwork);

    ASSERT(resource->url() == url.string());
    m_documentResources.set(resource->url(), resource);
    return resource;
}
Example #29
0
void XMLHttpRequest::open(const AtomicString& method, const KURL& url, bool async, ExceptionState& exceptionState)
{
    WTF_LOG(Network, "XMLHttpRequest %p open('%s', '%s', %d)", this, method.string().utf8().data(), url.elidedString().utf8().data(), async);

    if (!internalAbort())
        return;

    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 (!isValidHTTPToken(method)) {
        exceptionState.throwDOMException(SyntaxError, "'" + method + "' is not a valid HTTP method.");
        return;
    }

    if (!isAllowedHTTPMethod(method)) {
        exceptionState.throwSecurityError("'" + method + "' HTTP method is unsupported.");
        return;
    }

    if (!ContentSecurityPolicy::shouldBypassMainWorld(executionContext()) && !executionContext()->contentSecurityPolicy()->allowConnectToSource(url)) {
        // We can safely expose the URL to JavaScript, as these checks happen synchronously before redirection. JavaScript receives no new information.
        exceptionState.throwSecurityError("Refused to connect to '" + url.elidedString() + "' because it violates the document's Content Security Policy.");
        return;
    }

    if (!async && executionContext()->isDocument()) {
        if (document()->settings() && !document()->settings()->syncXHRInDocumentsEnabled()) {
            exceptionState.throwDOMException(InvalidAccessError, "Synchronous requests are disabled for this page.");
            return;
        }

        // Newer functionality is not available to synchronous requests in window contexts, as a spec-mandated
        // attempt to discourage synchronous XHR use. responseType is one such piece of functionality.
        // We'll only disable this functionality for HTTP(S) requests since sync requests for local protocols
        // such as file: and data: still make sense to allow.
        if (url.protocolIsInHTTPFamily() && m_responseTypeCode != ResponseTypeDefault) {
            exceptionState.throwDOMException(InvalidAccessError, "Synchronous HTTP requests from a document must not set a response type.");
            return;
        }

        // Similarly, timeouts are disabled for synchronous requests as well.
        if (m_timeoutMilliseconds > 0) {
            exceptionState.throwDOMException(InvalidAccessError, "Synchronous requests must not set a timeout.");
            return;
        }
    }

    m_method = uppercaseKnownHTTPMethod(method);

    m_url = url;

    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;
}
ResourceRequestBlockedReason FrameFetchContext::canRequestInternal(Resource::Type type, const ResourceRequest& resourceRequest, const KURL& url, const ResourceLoaderOptions& options, bool forPreload, FetchRequest::OriginRestriction originRestriction, ResourceRequest::RedirectStatus redirectStatus) const
{
    if (InspectorInstrumentation::shouldBlockRequest(frame(), resourceRequest))
        return ResourceRequestBlockedReasonInspector;

    SecurityOrigin* securityOrigin = options.securityOrigin.get();
    if (!securityOrigin && m_document)
        securityOrigin = m_document->getSecurityOrigin();

    if (originRestriction != FetchRequest::NoOriginRestriction && securityOrigin && !securityOrigin->canDisplay(url)) {
        if (!forPreload)
            FrameLoader::reportLocalLoadFailed(frame(), url.elidedString());
        WTF_LOG(ResourceLoading, "ResourceFetcher::requestResource URL was not allowed by SecurityOrigin::canDisplay");
        return ResourceRequestBlockedReasonOther;
    }

    // Some types of resources can be loaded only from the same origin. Other
    // types of resources, like Images, Scripts, and CSS, can be loaded from
    // any URL.
    switch (type) {
    case Resource::MainResource:
    case Resource::Image:
    case Resource::CSSStyleSheet:
    case Resource::Script:
    case Resource::Font:
    case Resource::Raw:
    case Resource::LinkPrefetch:
    case Resource::LinkPreload:
    case Resource::TextTrack:
    case Resource::ImportResource:
    case Resource::Media:
    case Resource::Manifest:
        // By default these types of resources can be loaded from any origin.
        // FIXME: Are we sure about Resource::Font?
        if (originRestriction == FetchRequest::RestrictToSameOrigin && !securityOrigin->canRequest(url)) {
            printAccessDeniedMessage(url);
            return ResourceRequestBlockedReasonOrigin;
        }
        break;
    case Resource::XSLStyleSheet:
        ASSERT(RuntimeEnabledFeatures::xsltEnabled());
    case Resource::SVGDocument:
        if (!securityOrigin->canRequest(url)) {
            printAccessDeniedMessage(url);
            return ResourceRequestBlockedReasonOrigin;
        }
        break;
    }

    // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved.
    bool shouldBypassMainWorldCSP = frame()->script().shouldBypassMainWorldCSP() || options.contentSecurityPolicyOption == DoNotCheckContentSecurityPolicy;

    // Don't send CSP messages for preloads, we might never actually display those items.
    ContentSecurityPolicy::ReportingStatus cspReporting = forPreload ?
        ContentSecurityPolicy::SuppressReport : ContentSecurityPolicy::SendReport;

    if (m_document) {
        DCHECK(m_document->contentSecurityPolicy());
        if (!shouldBypassMainWorldCSP && !m_document->contentSecurityPolicy()->allowRequest(resourceRequest.requestContext(), url, options.contentSecurityPolicyNonce, redirectStatus, cspReporting))
            return ResourceRequestBlockedReasonCSP;
    }

    if (type == Resource::Script || type == Resource::ImportResource) {
        ASSERT(frame());
        if (!frame()->loader().client()->allowScriptFromSource(!frame()->settings() || frame()->settings()->scriptEnabled(), url)) {
            frame()->loader().client()->didNotAllowScript();
            // TODO(estark): Use a different ResourceRequestBlockedReason
            // here, since this check has nothing to do with
            // CSP. https://crbug.com/600795
            return ResourceRequestBlockedReasonCSP;
        }
    } else if (type == Resource::Media || type == Resource::TextTrack) {
        ASSERT(frame());
        if (!frame()->loader().client()->allowMedia(url))
            return ResourceRequestBlockedReasonOther;
    }

    // SVG Images have unique security rules that prevent all subresource requests
    // except for data urls.
    if (type != Resource::MainResource && frame()->chromeClient().isSVGImageChromeClient() && !url.protocolIsData())
        return ResourceRequestBlockedReasonOrigin;

    // Measure the number of legacy URL schemes ('ftp://') and the number of embedded-credential
    // ('http://*****:*****@...') resources embedded as subresources. in the hopes that we can
    // block them at some point in the future.
    if (resourceRequest.frameType() != WebURLRequest::FrameTypeTopLevel) {
        ASSERT(frame()->document());
        if (SchemeRegistry::shouldTreatURLSchemeAsLegacy(url.protocol()) && !SchemeRegistry::shouldTreatURLSchemeAsLegacy(frame()->document()->getSecurityOrigin()->protocol()))
            UseCounter::count(frame()->document(), UseCounter::LegacyProtocolEmbeddedAsSubresource);
        if (!url.user().isEmpty() || !url.pass().isEmpty())
            UseCounter::count(frame()->document(), UseCounter::RequestedSubresourceWithEmbeddedCredentials);
    }

    // Check for mixed content. We do this second-to-last so that when folks block
    // mixed content with a CSP policy, they don't get a warning. They'll still
    // get a warning in the console about CSP blocking the load.
    MixedContentChecker::ReportingStatus mixedContentReporting = forPreload ?
        MixedContentChecker::SuppressReport : MixedContentChecker::SendReport;
    if (MixedContentChecker::shouldBlockFetch(frame(), resourceRequest, url, mixedContentReporting))
        return ResourceRequestBlockedReasonMixedContent;

    // Let the client have the final say into whether or not the load should proceed.
    DocumentLoader* documentLoader = masterDocumentLoader();
    if (documentLoader && documentLoader->subresourceFilter() && type != Resource::MainResource && type != Resource::ImportResource && !documentLoader->subresourceFilter()->allowLoad(url, resourceRequest.requestContext()))
        return ResourceRequestBlockedReasonSubresourceFilter;

    return ResourceRequestBlockedReasonNone;
}