| // Copyright 2019 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "third_party/blink/renderer/core/script/value_wrapper_synthetic_module_script.h" |
| |
| #include "third_party/blink/public/common/features.h" |
| #include "third_party/blink/public/platform/web_vector.h" |
| #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h" |
| #include "third_party/blink/renderer/bindings/core/v8/v8_css_style_sheet_init.h" |
| #include "third_party/blink/renderer/core/css/css_style_sheet.h" |
| #include "third_party/blink/renderer/core/dom/document.h" |
| #include "third_party/blink/renderer/core/execution_context/execution_context.h" |
| #include "third_party/blink/renderer/core/frame/local_dom_window.h" |
| #include "third_party/blink/renderer/core/loader/modulescript/module_script_creation_params.h" |
| #include "third_party/blink/renderer/core/script/modulator.h" |
| #include "third_party/blink/renderer/core/script/module_record_resolver.h" |
| #include "third_party/blink/renderer/platform/bindings/to_v8.h" |
| #include "third_party/blink/renderer/platform/instrumentation/use_counter.h" |
| #include "third_party/blink/renderer/platform/weborigin/kurl.h" |
| #include "third_party/blink/renderer/platform/wtf/text/text_position.h" |
| #include "v8/include/v8.h" |
| |
| namespace blink { |
| |
| // https://whatpr.org/html/4898/webappapis.html#creating-a-css-module-script |
| ValueWrapperSyntheticModuleScript* |
| ValueWrapperSyntheticModuleScript::CreateCSSWrapperSyntheticModuleScript( |
| const ModuleScriptCreationParams& params, |
| Modulator* settings_object) { |
| DCHECK(settings_object->HasValidContext()); |
| ScriptState* script_state = settings_object->GetScriptState(); |
| ScriptState::Scope scope(script_state); |
| v8::Isolate* isolate = script_state->GetIsolate(); |
| ExceptionState exception_state(isolate, ExceptionState::kExecutionContext, |
| "ModuleScriptLoader", |
| "CreateCSSWrapperSyntheticModuleScript"); |
| ExecutionContext* execution_context = ExecutionContext::From(script_state); |
| UseCounter::Count(execution_context, WebFeature::kCreateCSSModuleScript); |
| auto* context_window = DynamicTo<LocalDOMWindow>(execution_context); |
| if (!context_window) { |
| v8::Local<v8::Value> error = V8ThrowException::CreateTypeError( |
| isolate, "Cannot create CSS Module in non-document context"); |
| return ValueWrapperSyntheticModuleScript::CreateWithError( |
| v8::Local<v8::Value>(), settings_object, params.SourceURL(), KURL(), |
| ScriptFetchOptions(), error); |
| } |
| CSSStyleSheetInit* init = CSSStyleSheetInit::Create(); |
| CSSStyleSheet* style_sheet = |
| CSSStyleSheet::Create(*context_window->document(), init, exception_state); |
| if (exception_state.HadException()) { |
| v8::Local<v8::Value> error = exception_state.GetException(); |
| exception_state.ClearException(); |
| return ValueWrapperSyntheticModuleScript::CreateWithError( |
| v8::Local<v8::Value>(), settings_object, params.SourceURL(), KURL(), |
| ScriptFetchOptions(), error); |
| } |
| style_sheet->replaceSync(params.GetSourceText().ToString(), exception_state); |
| if (exception_state.HadException()) { |
| v8::Local<v8::Value> error = exception_state.GetException(); |
| exception_state.ClearException(); |
| return ValueWrapperSyntheticModuleScript::CreateWithError( |
| v8::Local<v8::Value>(), settings_object, params.SourceURL(), KURL(), |
| ScriptFetchOptions(), error); |
| } |
| v8::Local<v8::Value> v8_value_stylesheet = ToV8(style_sheet, script_state); |
| return ValueWrapperSyntheticModuleScript::CreateWithDefaultExport( |
| v8_value_stylesheet, settings_object, params.SourceURL(), KURL(), |
| ScriptFetchOptions()); |
| } |
| |
| ValueWrapperSyntheticModuleScript* |
| ValueWrapperSyntheticModuleScript::CreateJSONWrapperSyntheticModuleScript( |
| const ModuleScriptCreationParams& params, |
| Modulator* settings_object) { |
| DCHECK(settings_object->HasValidContext()); |
| ScriptState::Scope scope(settings_object->GetScriptState()); |
| v8::Local<v8::Context> context = |
| settings_object->GetScriptState()->GetContext(); |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::TryCatch try_catch(isolate); |
| v8::Local<v8::String> original_json = |
| V8String(isolate, params.GetSourceText()); |
| v8::Local<v8::Value> parsed_json; |
| ExceptionState exception_state(isolate, ExceptionState::kExecutionContext, |
| "ModuleScriptLoader", |
| "CreateJSONWrapperSyntheticModuleScript"); |
| UseCounter::Count(ExecutionContext::From(settings_object->GetScriptState()), |
| WebFeature::kCreateJSONModuleScript); |
| // Step 1. "Let script be a new module script that this algorithm will |
| // subsequently initialize." |
| // [spec text] |
| // Step 2. "Set script's settings object to settings." |
| // [spec text] |
| // Step 3. "Set script's base URL and fetch options to null." |
| // [spec text] |
| // Step 4. "Set script's parse error and error to rethrow to null." |
| // [spec text] |
| // Step 5. "Let json be ? Call(%JSONParse%, undefined, « source »). |
| // If this throws an exception, set script's parse error to that exception, |
| // and return script." |
| // [spec text] |
| if (!v8::JSON::Parse(context, original_json).ToLocal(&parsed_json)) { |
| DCHECK(try_catch.HasCaught()); |
| exception_state.RethrowV8Exception(try_catch.Exception()); |
| v8::Local<v8::Value> error = exception_state.GetException(); |
| exception_state.ClearException(); |
| return ValueWrapperSyntheticModuleScript::CreateWithError( |
| parsed_json, settings_object, params.SourceURL(), KURL(), |
| ScriptFetchOptions(), error); |
| } else { |
| return ValueWrapperSyntheticModuleScript::CreateWithDefaultExport( |
| parsed_json, settings_object, params.SourceURL(), KURL(), |
| ScriptFetchOptions()); |
| } |
| } |
| |
| ValueWrapperSyntheticModuleScript* |
| ValueWrapperSyntheticModuleScript::CreateWithDefaultExport( |
| v8::Local<v8::Value> value, |
| Modulator* settings_object, |
| const KURL& source_url, |
| const KURL& base_url, |
| const ScriptFetchOptions& fetch_options, |
| const TextPosition& start_position) { |
| v8::Isolate* isolate = settings_object->GetScriptState()->GetIsolate(); |
| std::vector<v8::Local<v8::String>> export_names{V8String(isolate, "default")}; |
| v8::Local<v8::Module> v8_synthetic_module = v8::Module::CreateSyntheticModule( |
| isolate, V8String(isolate, source_url.GetString()), export_names, |
| ValueWrapperSyntheticModuleScript::EvaluationSteps); |
| // Step 6. "Set script's record to the result of creating a synthetic module |
| // record with a default export of json with settings." |
| // [spec text] |
| ValueWrapperSyntheticModuleScript* value_wrapper_module_script = |
| MakeGarbageCollected<ValueWrapperSyntheticModuleScript>( |
| settings_object, v8_synthetic_module, source_url, base_url, |
| fetch_options, value, start_position); |
| settings_object->GetModuleRecordResolver()->RegisterModuleScript( |
| value_wrapper_module_script); |
| // Step 7. "Return script." |
| // [spec text] |
| return value_wrapper_module_script; |
| } |
| |
| ValueWrapperSyntheticModuleScript* |
| ValueWrapperSyntheticModuleScript::CreateWithError( |
| v8::Local<v8::Value> value, |
| Modulator* settings_object, |
| const KURL& source_url, |
| const KURL& base_url, |
| const ScriptFetchOptions& fetch_options, |
| v8::Local<v8::Value> error, |
| const TextPosition& start_position) { |
| ValueWrapperSyntheticModuleScript* value_wrapper_module_script = |
| MakeGarbageCollected<ValueWrapperSyntheticModuleScript>( |
| settings_object, v8::Local<v8::Module>(), source_url, base_url, |
| fetch_options, value, start_position); |
| settings_object->GetModuleRecordResolver()->RegisterModuleScript( |
| value_wrapper_module_script); |
| value_wrapper_module_script->SetParseErrorAndClearRecord( |
| ScriptValue(settings_object->GetScriptState()->GetIsolate(), error)); |
| // Step 7. "Return script." |
| // [spec text] |
| return value_wrapper_module_script; |
| } |
| |
| ValueWrapperSyntheticModuleScript::ValueWrapperSyntheticModuleScript( |
| Modulator* settings_object, |
| v8::Local<v8::Module> record, |
| const KURL& source_url, |
| const KURL& base_url, |
| const ScriptFetchOptions& fetch_options, |
| v8::Local<v8::Value> value, |
| const TextPosition& start_position) |
| : ModuleScript(settings_object, |
| record, |
| source_url, |
| base_url, |
| fetch_options), |
| export_value_(v8::Isolate::GetCurrent(), value) {} |
| |
| // This is the definition of [[EvaluationSteps]] As per the synthetic module |
| // spec https://heycam.github.io/webidl/#synthetic-module-records |
| // It is responsible for setting the default export of the provided module to |
| // the value wrapped by the ValueWrapperSyntheticModuleScript |
| v8::MaybeLocal<v8::Value> ValueWrapperSyntheticModuleScript::EvaluationSteps( |
| v8::Local<v8::Context> context, |
| v8::Local<v8::Module> module) { |
| v8::Isolate* isolate = context->GetIsolate(); |
| ScriptState* script_state = ScriptState::From(context); |
| Modulator* modulator = Modulator::From(script_state); |
| ModuleRecordResolver* module_record_resolver = |
| modulator->GetModuleRecordResolver(); |
| const ValueWrapperSyntheticModuleScript* |
| value_wrapper_synthetic_module_script = |
| static_cast<const ValueWrapperSyntheticModuleScript*>( |
| module_record_resolver->GetModuleScriptFromModuleRecord(module)); |
| v8::TryCatch try_catch(isolate); |
| v8::Maybe<bool> result = module->SetSyntheticModuleExport( |
| isolate, V8String(isolate, "default"), |
| value_wrapper_synthetic_module_script->export_value_.NewLocal(isolate)); |
| |
| // Setting the default export should never fail. |
| DCHECK(!try_catch.HasCaught()); |
| DCHECK(!result.IsNothing() && result.FromJust()); |
| |
| if (base::FeatureList::IsEnabled(features::kTopLevelAwait)) { |
| v8::Local<v8::Promise::Resolver> promise_resolver; |
| if (!v8::Promise::Resolver::New(context).ToLocal(&promise_resolver)) { |
| if (!isolate->IsExecutionTerminating()) { |
| LOG(FATAL) << "Cannot recover from failure to create a new " |
| "v8::Promise::Resolver object (OOM?)"; |
| } |
| return v8::MaybeLocal<v8::Value>(); |
| } |
| promise_resolver->Resolve(context, v8::Undefined(isolate)).ToChecked(); |
| return promise_resolver->GetPromise(); |
| } |
| |
| return v8::Undefined(isolate); |
| } |
| |
| void ValueWrapperSyntheticModuleScript::Trace(Visitor* visitor) const { |
| visitor->Trace(export_value_); |
| ModuleScript::Trace(visitor); |
| } |
| |
| } // namespace blink |