void ScriptLoader::executeScript(const ScriptSourceCode& sourceCode, double* compilationFinishTime) { ASSERT(m_alreadyStarted); if (sourceCode.isEmpty()) return; RefPtrWillBeRawPtr<Document> elementDocument(m_element->document()); RefPtrWillBeRawPtr<Document> contextDocument = elementDocument->contextDocument().get(); if (!contextDocument) return; LocalFrame* frame = contextDocument->frame(); const ContentSecurityPolicy* csp = elementDocument->contentSecurityPolicy(); bool shouldBypassMainWorldCSP = (frame && frame->script().shouldBypassMainWorldCSP()) || csp->allowScriptWithNonce(m_element->fastGetAttribute(HTMLNames::nonceAttr)) || csp->allowScriptWithHash(sourceCode.source()); if (!m_isExternalScript && (!shouldBypassMainWorldCSP && !csp->allowInlineScript(elementDocument->url(), m_startLineNumber))) return; if (m_isExternalScript) { ScriptResource* resource = m_resource ? m_resource.get() : sourceCode.resource(); if (resource && !resource->mimeTypeAllowedByNosniff()) { contextDocument->addConsoleMessage(ConsoleMessage::create(SecurityMessageSource, ErrorMessageLevel, "Refused to execute script from '" + resource->url().elidedString() + "' because its MIME type ('" + resource->mimeType() + "') is not executable, and strict MIME type checking is enabled.")); return; } if (!SubresourceIntegrity::CheckSubresourceIntegrity(*m_element, sourceCode.source(), sourceCode.resource()->url(), sourceCode.resource()->mimeType())) return; } // FIXME: Can this be moved earlier in the function? // Why are we ever attempting to execute scripts without a frame? if (!frame) return; const bool isImportedScript = contextDocument != elementDocument; // http://www.whatwg.org/specs/web-apps/current-work/#execute-the-script-block step 2.3 // with additional support for HTML imports. IgnoreDestructiveWriteCountIncrementer ignoreDestructiveWriteCountIncrementer(m_isExternalScript || isImportedScript ? contextDocument.get() : 0); if (isHTMLScriptLoader(m_element)) contextDocument->pushCurrentScript(toHTMLScriptElement(m_element)); AccessControlStatus corsCheck = NotSharableCrossOrigin; if (!m_isExternalScript || (sourceCode.resource() && sourceCode.resource()->passesAccessControlCheck(&m_element->document(), m_element->document().securityOrigin()))) corsCheck = SharableCrossOrigin; // Create a script from the script element node, using the script // block's source and the script block's type. // Note: This is where the script is compiled and actually executed. frame->script().executeScriptInMainWorld(sourceCode, corsCheck, compilationFinishTime); if (isHTMLScriptLoader(m_element)) { ASSERT(contextDocument->currentScript() == m_element); contextDocument->popCurrentScript(); } }
void PendingScript::notifyFinished(Resource* resource) { // The following SRI checks need to be here because, unfortunately, fetches // are not done purely according to the Fetch spec. In particular, // different requests for the same resource do not have different // responses; the memory cache can (and will) return the exact same // Resource object. // // For different requests, the same Resource object will be returned and // will not be associated with the particular request. Therefore, when the // body of the response comes in, there's no way to validate the integrity // of the Resource object against a particular request (since there may be // several pending requests all tied to the identical object, and the // actual requests are not stored). // // In order to simulate the correct behavior, Blink explicitly does the SRI // checks here, when a PendingScript tied to a particular request is // finished (and in the case of a StyleSheet, at the point of execution), // while having proper Fetch checks in the fetch module for use in the // fetch JavaScript API. In a future world where the ResourceFetcher uses // the Fetch algorithm, this should be fixed by having separate Response // objects (perhaps attached to identical Resource objects) per request. // // See https://crbug.com/500701 for more information. if (m_element) { DCHECK_EQ(resource->getType(), Resource::Script); ScriptResource* scriptResource = toScriptResource(resource); String integrityAttr = m_element->fastGetAttribute(HTMLNames::integrityAttr); // It is possible to get back a script resource with integrity metadata // for a request with an empty integrity attribute. In that case, the // integrity check should be skipped, so this check ensures that the // integrity attribute isn't empty in addition to checking if the // resource has empty integrity metadata. if (!integrityAttr.isEmpty() && !scriptResource->integrityMetadata().isEmpty()) { ResourceIntegrityDisposition disposition = scriptResource->integrityDisposition(); if (disposition == ResourceIntegrityDisposition::Failed) { // TODO(jww): This should probably also generate a console // message identical to the one produced by // CheckSubresourceIntegrity below. See https://crbug.com/585267. m_integrityFailure = true; } else if (disposition == ResourceIntegrityDisposition::NotChecked && resource->resourceBuffer()) { m_integrityFailure = !SubresourceIntegrity::CheckSubresourceIntegrity( scriptResource->integrityMetadata(), *m_element, resource->resourceBuffer()->data(), resource->resourceBuffer()->size(), resource->url(), *resource); scriptResource->setIntegrityDisposition( m_integrityFailure ? ResourceIntegrityDisposition::Failed : ResourceIntegrityDisposition::Passed); } } } if (m_streamer) m_streamer->notifyFinished(resource); }
ScriptResource* ScriptResource::fetch(FetchRequest& request, ResourceFetcher* fetcher) { DCHECK_EQ(request.resourceRequest().frameType(), WebURLRequest::FrameTypeNone); request.mutableResourceRequest().setRequestContext( WebURLRequest::RequestContextScript); ScriptResource* resource = toScriptResource( fetcher->requestResource(request, ScriptResourceFactory())); if (resource && !request.integrityMetadata().isEmpty()) resource->setIntegrityMetadata(request.integrityMetadata()); return resource; }
bool ScriptStreamer::startStreamingInternal(PendingScript& script, Settings* settings, ScriptState* scriptState, PendingScript::Type scriptType) { ASSERT(isMainThread()); if (!settings || !settings->v8ScriptStreamingEnabled()) return false; if (settings->v8ScriptStreamingMode() == ScriptStreamingModeOnlyAsyncAndDefer && scriptType == PendingScript::ParsingBlocking) return false; ScriptResource* resource = script.resource(); if (resource->isLoaded()) return false; if (!resource->url().protocolIsInHTTPFamily()) return false; if (resource->resourceToRevalidate()) { // This happens e.g., during reloads. We're actually not going to load // the current Resource of the PendingScript but switch to another // Resource -> don't stream. return false; } // We cannot filter out short scripts, even if we wait for the HTTP headers // to arrive. In general, the web servers don't seem to send the // Content-Length HTTP header for scripts. if (!scriptState->contextIsValid()) return false; // Decide what kind of cached data we should produce while streaming. By // default, we generate the parser cache for streamed scripts, to emulate // the non-streaming behavior (see V8ScriptRunner::compileScript). v8::ScriptCompiler::CompileOptions compileOption = v8::ScriptCompiler::kProduceParserCache; if (settings->v8CacheOptions() == V8CacheOptionsCode) compileOption = v8::ScriptCompiler::kProduceCodeCache; // The Resource might go out of scope if the script is no longer // needed. This makes PendingScript notify the ScriptStreamer when it is // destroyed. script.setStreamer(adoptRef(new ScriptStreamer(resource, scriptType, settings->v8ScriptStreamingMode(), scriptState, compileOption))); return true; }
MonoArray* ScriptEditorUtility::internal_FindDependencies(MonoObject* resource, bool recursive) { ScriptResource* srcResource = ScriptResource::toNative(resource); if (srcResource == nullptr) { ScriptArray emptyArray = ScriptArray::create<ScriptResource>(0); return emptyArray.getInternal(); } HResource srcHandle = srcResource->getGenericHandle(); Vector<ResourceDependency> dependencies = Utility::findResourceDependencies(srcHandle, recursive); UINT32 numEntries = (UINT32)dependencies.size(); ScriptArray output = ScriptArray::create<ScriptResource>(numEntries); for (UINT32 i = 0; i < numEntries; i++) { ScriptResourceBase* dependency = ScriptResourceManager::instance().getScriptResource(dependencies[i].resource, true); output.set(i, dependency->getManagedInstance()); } return output.getInternal(); }
bool ScriptLoader::doExecuteScript(const ScriptSourceCode& sourceCode) { DCHECK(m_alreadyStarted); if (sourceCode.isEmpty()) return true; Document* elementDocument = &(m_element->document()); Document* contextDocument = elementDocument->contextDocument(); if (!contextDocument) return true; LocalFrame* frame = contextDocument->frame(); const ContentSecurityPolicy* csp = elementDocument->contentSecurityPolicy(); bool shouldBypassMainWorldCSP = (frame && frame->script().shouldBypassMainWorldCSP()) || csp->allowScriptWithHash(sourceCode.source(), ContentSecurityPolicy::InlineType::Block); AtomicString nonce = ContentSecurityPolicy::isNonceableElement(m_element.get()) ? m_element->fastGetAttribute(HTMLNames::nonceAttr) : AtomicString(); if (!m_isExternalScript && (!shouldBypassMainWorldCSP && !csp->allowInlineScript(m_element, elementDocument->url(), nonce, m_startLineNumber, sourceCode.source()))) { return false; } if (m_isExternalScript) { ScriptResource* resource = m_resource ? m_resource.get() : sourceCode.resource(); if (resource) { if (!resource->mimeTypeAllowedByNosniff()) { contextDocument->addConsoleMessage(ConsoleMessage::create( SecurityMessageSource, ErrorMessageLevel, "Refused to execute script from '" + resource->url().elidedString() + "' because its MIME type ('" + resource->httpContentType() + "') is not executable, and " "strict MIME type checking is " "enabled.")); return false; } String mimeType = resource->httpContentType(); if (mimeType.startsWith("image/") || mimeType == "text/csv" || mimeType.startsWith("audio/") || mimeType.startsWith("video/")) { contextDocument->addConsoleMessage(ConsoleMessage::create( SecurityMessageSource, ErrorMessageLevel, "Refused to execute script from '" + resource->url().elidedString() + "' because its MIME type ('" + mimeType + "') is not executable.")); if (mimeType.startsWith("image/")) UseCounter::count(frame, UseCounter::BlockedSniffingImageToScript); else if (mimeType.startsWith("audio/")) UseCounter::count(frame, UseCounter::BlockedSniffingAudioToScript); else if (mimeType.startsWith("video/")) UseCounter::count(frame, UseCounter::BlockedSniffingVideoToScript); else if (mimeType == "text/csv") UseCounter::count(frame, UseCounter::BlockedSniffingCSVToScript); return false; } logScriptMIMEType(frame, resource, mimeType); } } // FIXME: Can this be moved earlier in the function? // Why are we ever attempting to execute scripts without a frame? if (!frame) return true; AccessControlStatus accessControlStatus = NotSharableCrossOrigin; if (!m_isExternalScript) { accessControlStatus = SharableCrossOrigin; } else if (sourceCode.resource()) { if (sourceCode.resource()->response().wasFetchedViaServiceWorker()) { if (sourceCode.resource()->response().serviceWorkerResponseType() == WebServiceWorkerResponseTypeOpaque) accessControlStatus = OpaqueResource; else accessControlStatus = SharableCrossOrigin; } else if (sourceCode.resource()->passesAccessControlCheck( m_element->document().getSecurityOrigin())) { accessControlStatus = SharableCrossOrigin; } } const bool isImportedScript = contextDocument != elementDocument; // http://www.whatwg.org/specs/web-apps/current-work/#execute-the-script-block // step 2.3 with additional support for HTML imports. IgnoreDestructiveWriteCountIncrementer ignoreDestructiveWriteCountIncrementer( m_isExternalScript || isImportedScript ? contextDocument : 0); if (isHTMLScriptLoader(m_element) || isSVGScriptLoader(m_element)) contextDocument->pushCurrentScript(m_element); // Create a script from the script element node, using the script // block's source and the script block's type. // Note: This is where the script is compiled and actually executed. frame->script().executeScriptInMainWorld(sourceCode, accessControlStatus); if (isHTMLScriptLoader(m_element) || isSVGScriptLoader(m_element)) { DCHECK(contextDocument->currentScript() == m_element); contextDocument->popCurrentScript(); } return true; }