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(); } }
v8::Local<v8::Value> ScriptController::executeScriptAndReturnValue(v8::Handle<v8::Context> context, const ScriptSourceCode& source, AccessControlStatus corsStatus) { v8::Context::Scope scope(context); InspectorInstrumentationCookie cookie = InspectorInstrumentation::willEvaluateScript(m_frame, source.url().isNull() ? String() : source.url().string(), source.startLine()); v8::Local<v8::Value> result; { // Isolate exceptions that occur when compiling and executing // the code. These exceptions should not interfere with // javascript code we might evaluate from C++ when returning // from here. v8::TryCatch tryCatch; tryCatch.SetVerbose(true); v8::Handle<v8::String> code = v8String(m_isolate, source.source()); OwnPtr<v8::ScriptData> scriptData = V8ScriptRunner::precompileScript(code, source.resource()); // NOTE: For compatibility with WebCore, ScriptSourceCode's line starts at // 1, whereas v8 starts at 0. v8::Handle<v8::Script> script = V8ScriptRunner::compileScript(code, source.url(), source.startPosition(), scriptData.get(), m_isolate, corsStatus); // Keep LocalFrame (and therefore ScriptController) alive. RefPtr<LocalFrame> protect(m_frame); result = V8ScriptRunner::runCompiledScript(script, m_frame->document(), m_isolate); ASSERT(!tryCatch.HasCaught() || result.IsEmpty()); } InspectorInstrumentation::didEvaluateScript(cookie); return result; }
v8::Local<v8::Value> ScriptController::executeScriptAndReturnValue(v8::Local<v8::Context> context, const ScriptSourceCode& source, AccessControlStatus accessControlStatus, double* compilationFinishTime) { TRACE_EVENT1("devtools.timeline", "EvaluateScript", "data", InspectorEvaluateScriptEvent::data(frame(), source.url().getString(), source.startPosition())); InspectorInstrumentation::NativeBreakpoint nativeBreakpoint(frame()->document(), "scriptFirstStatement", false); v8::Local<v8::Value> result; { V8CacheOptions v8CacheOptions(V8CacheOptionsDefault); if (frame()->settings()) v8CacheOptions = frame()->settings()->v8CacheOptions(); if (source.resource() && !source.resource()->response().cacheStorageCacheName().isNull()) { switch (frame()->settings()->v8CacheStrategiesForCacheStorage()) { case V8CacheStrategiesForCacheStorage::None: v8CacheOptions = V8CacheOptionsNone; break; case V8CacheStrategiesForCacheStorage::Normal: v8CacheOptions = V8CacheOptionsCode; break; case V8CacheStrategiesForCacheStorage::Default: case V8CacheStrategiesForCacheStorage::Aggressive: v8CacheOptions = V8CacheOptionsAlways; break; } } // Isolate exceptions that occur when compiling and executing // the code. These exceptions should not interfere with // javascript code we might evaluate from C++ when returning // from here. v8::TryCatch tryCatch(isolate()); tryCatch.SetVerbose(true); v8::Local<v8::Script> script; if (!v8Call(V8ScriptRunner::compileScript(source, isolate(), accessControlStatus, v8CacheOptions), script, tryCatch)) return result; if (compilationFinishTime) { *compilationFinishTime = WTF::monotonicallyIncreasingTime(); } if (!v8Call(V8ScriptRunner::runCompiledScript(isolate(), script, frame()->document()), result, tryCatch)) return result; } TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "UpdateCounters", TRACE_EVENT_SCOPE_THREAD, "data", InspectorUpdateCountersEvent::data()); return result; }
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; }
v8::Local<v8::Script> V8ScriptRunner::compileScript(const ScriptSourceCode& source, v8::Isolate* isolate, AccessControlStatus corsStatus) { return compileScript(v8String(isolate, source.source()), source.url(), source.startPosition(), source.resource(), isolate, corsStatus); }
v8::MaybeLocal<v8::Script> V8ScriptRunner::compileScript(const ScriptSourceCode& source, v8::Isolate* isolate, AccessControlStatus corsStatus, V8CacheOptions cacheOptions) { if (source.source().length() >= v8::String::kMaxLength) { V8ThrowException::throwGeneralError(isolate, "Source file too large."); return v8::Local<v8::Script>(); } return compileScript(v8String(isolate, source.source()), source.url(), source.sourceMapUrl(), source.startPosition(), isolate, source.resource(), source.streamer(), source.resource() ? source.resource()->cacheHandler() : nullptr, corsStatus, cacheOptions); }