Worker: Make sure to install features controlled by origin trial after OriginTrialContext::AddTokens()

JavaScript's properties controlled by origin trials are installed for workers in
WorkerOrWorkletScriptController::Initialize(). This is too early for
shared/service workers with off-the-main-thread worker script fetch. This is
because their origin trial tokens are served via worker script's response
headers, and are applied to OriginTrialContext *after*
WorkerOrWorkletScriptController::Initialize().

This CL separates WorkerOrWorkletScriptController::Initialize() into 2 parts:
Initialize() to create and initialize the global object, and ReadyToEvaluate()
to install the conditional features. Then, this makes sure to call
ReadyToEvaluate() after the tokens are applied.

This change fixes a following test with off-the-main-thread service worker
script fetch:
- virtual/origin-trials-runtimeflags-disabled/http/tests/origin_trials/webexposed/cookiestore-origin-trial-interfaces.html

Change-Id: I024915b470600d6dc4835d7d3aef297da616da3e
Bug: 945215
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1614645
Commit-Queue: Kenichi Ishibashi <bashi@chromium.org>
Auto-Submit: Hiroki Nakagawa <nhiroki@chromium.org>
Reviewed-by: Kenichi Ishibashi <bashi@chromium.org>
Reviewed-by: Hiroshige Hayashizaki <hiroshige@chromium.org>
Cr-Commit-Position: refs/heads/master@{#661245}
diff --git a/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.cc b/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.cc
index 9083897..26a0f26 100644
--- a/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.cc
+++ b/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.cc
@@ -100,7 +100,6 @@
     v8::Isolate* isolate)
     : global_scope_(global_scope),
       isolate_(isolate),
-      execution_forbidden_(false),
       rejected_promises_(RejectedPromises::Create()),
       execution_state_(nullptr) {
   DCHECK(isolate);
@@ -220,10 +219,6 @@
   V8DOMWrapper::SetNativeInfo(isolate_, global_object, wrapper_type_info,
                               script_wrappable);
 
-  // All interfaces must be registered to V8PerContextData.
-  // So we explicitly call constructorForType for the global object.
-  V8PerContextData::From(context)->ConstructorForType(wrapper_type_info);
-
   if (global_scope_->IsMainThreadWorkletGlobalScope()) {
     // Set the human readable name for the world.
     DCHECK(!global_scope_->Name().IsEmpty());
@@ -237,20 +232,91 @@
                              context);
   }
 
-  wrapper_type_info->InstallConditionalFeatures(
-      context, *world_, global_object, v8::Local<v8::Object>(),
-      v8::Local<v8::Function>(), global_interface_template);
-
   if (!disable_eval_pending_.IsEmpty()) {
     DisableEvalInternal(disable_eval_pending_);
     disable_eval_pending_ = String();
   }
 
+  // This is a workaround for worker with on-the-main-thread script fetch and
+  // worklets.
+  // - For workers with off-the-main-thread worker script fetch,
+  //   PrepareForEvaluation() is called in WorkerGlobalScope::Initialize() after
+  //   top-level worker script fetch and before script evaluation.
+  // - For workers with on-the-main-thread worker script fetch, it's too early
+  //   to call PrepareForEvaluation() in WorkerGlobalScope::Initialize() because
+  //   it's called immediately after WorkerGlobalScope's constructor, that is,
+  //   before WorkerOrWorkletScriptController::Initialize(). Therefore, we
+  //   ignore the first call of PrepareForEvaluation() from
+  //   WorkerGlobalScope::Initialize(), and call it here again.
+  // TODO(nhiroki): Remove this workaround once off-the-main-thread worker
+  // script fetch is enabled by default for all worker types.
+  //
+  // - For worklets, there is no appropriate timing to call
+  //   PrepareForEvaluation() other than here because worklets have various
+  //   initialization sequences depending on thread model (on-main-thread vs.
+  //   off-main-thread) and unique script fetch (fetching a top-level script per
+  //   addModule() call in JS).
+  // TODO(nhiroki): Unify worklet initialization sequences, and move this to an
+  // appropriate place.
+  if (global_scope_->GetOffMainThreadWorkerScriptFetchOption() ==
+          OffMainThreadWorkerScriptFetchOption::kDisabled ||
+      global_scope_->IsWorkletGlobalScope()) {
+    // This should be called after origin trial tokens are applied for
+    // OriginTrialContext in WorkerGlobalScope::Initialize() to install origin
+    // trial features in JavaScript's global object. Workers with
+    // on-the-main-thread script fetch and worklets apply origin trial tokens
+    // before WorkerOrWorkletScriptController::initialize(), so it's safe to
+    // call this here.
+    PrepareForEvaluation();
+  }
+
+  return true;
+}
+
+void WorkerOrWorkletScriptController::PrepareForEvaluation() {
+  if (!IsContextInitialized()) {
+    // For workers with off-the-main-thread worker script fetch, this can be
+    // called before WorkerOrWorkletScriptController::Initialize() via
+    // WorkerGlobalScope creation function. In this case, PrepareForEvaluation()
+    // calls this function again. See comments in PrepareForEvaluation().
+    DCHECK(global_scope_->IsWorkerGlobalScope());
+    DCHECK_EQ(OffMainThreadWorkerScriptFetchOption::kDisabled,
+              global_scope_->GetOffMainThreadWorkerScriptFetchOption());
+    return;
+  }
+  DCHECK(!is_ready_to_evaluate_);
+  is_ready_to_evaluate_ = true;
+
+  v8::HandleScope handle_scope(isolate_);
+
+  ScriptState::Scope scope(script_state_);
+  v8::Local<v8::Context> context = script_state_->GetContext();
+
+  auto* script_wrappable = static_cast<ScriptWrappable*>(global_scope_);
+  const WrapperTypeInfo* wrapper_type_info =
+      script_wrappable->GetWrapperTypeInfo();
+
+  // All interfaces must be registered to V8PerContextData.
+  // So we explicitly call constructorForType for the global object.
+  // This should be called after OriginTrialContext::AddTokens() in
+  // WorkerGlobalScope::Initialize() to install origin trial features.
+  V8PerContextData::From(context)->ConstructorForType(wrapper_type_info);
+
+  v8::Local<v8::Object> global_object =
+      context->Global()->GetPrototype().As<v8::Object>();
+  DCHECK(!global_object.IsEmpty());
+
+  v8::Local<v8::FunctionTemplate> global_interface_template =
+      wrapper_type_info->DomTemplate(isolate_, *world_);
+  DCHECK(!global_interface_template.IsEmpty());
+
+  wrapper_type_info->InstallConditionalFeatures(
+      context, *world_, global_object, v8::Local<v8::Object>(),
+      v8::Local<v8::Function>(), global_interface_template);
+
   // This can only be called after the global object is fully initialised, as it
   // reads values from it.
   InitializeV8ExtrasBinding(script_state_);
-
-  return true;
 }
 
 void WorkerOrWorkletScriptController::DisableEvalInternal(
@@ -269,6 +335,7 @@
     SanitizeScriptErrors sanitize_script_errors,
     V8CacheOptions v8_cache_options) {
   DCHECK(IsContextInitialized());
+  DCHECK(is_ready_to_evaluate_);
 
   TRACE_EVENT1("devtools.timeline", "EvaluateScript", "data",
                inspector_evaluate_script_event::Data(
diff --git a/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.h b/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.h
index 03f829b..fd3f5a3a 100644
--- a/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.h
+++ b/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.h
@@ -75,6 +75,10 @@
   // This should be called only once.
   bool Initialize(const KURL& url_for_debugger);
 
+  // Prepares for script evaluation. This must be called after Initialize()
+  // before Evaluate().
+  void PrepareForEvaluation();
+
   // Used by WorkerGlobalScope:
   void RethrowExceptionFromImportedScript(ErrorEvent*, ExceptionState&);
   // Disables `eval()` on JavaScript. This must be called before Evaluate().
@@ -125,7 +129,8 @@
   // Keeps the error message for `eval()` on JavaScript until Initialize().
   String disable_eval_pending_;
 
-  bool execution_forbidden_;
+  bool is_ready_to_evaluate_ = false;
+  bool execution_forbidden_ = false;
 
   scoped_refptr<RejectedPromises> rejected_promises_;
 
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc b/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc
index 7431ad9..1666ed9 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc
@@ -138,8 +138,11 @@
   InitContentSecurityPolicyFromVector(OutsideContentSecurityPolicyHeaders());
   BindContentSecurityPolicyToExecutionContext();
 
+  // This should be called after OriginTrialContext::AddTokens() to install
+  // origin trial features in JavaScript's global object.
   // DedicatedWorkerGlobalScope inherits the outside's OriginTrialTokens in the
   // constructor instead of the response origin trial tokens.
+  ScriptController()->PrepareForEvaluation();
 }
 
 // https://html.spec.whatwg.org/C/#worker-processing-model
diff --git a/third_party/blink/renderer/core/workers/experimental/thread_pool_thread.cc b/third_party/blink/renderer/core/workers/experimental/thread_pool_thread.cc
index 61cb937..a78a82e 100644
--- a/third_party/blink/renderer/core/workers/experimental/thread_pool_thread.cc
+++ b/third_party/blink/renderer/core/workers/experimental/thread_pool_thread.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/core/workers/experimental/thread_pool_thread.h"
 
+#include "third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/origin_trials/origin_trial_context.h"
 #include "third_party/blink/renderer/core/workers/experimental/task_worklet_global_scope.h"
@@ -52,6 +53,10 @@
     BindContentSecurityPolicyToExecutionContext();
 
     OriginTrialContext::AddTokens(this, response_origin_trial_tokens);
+
+    // This should be called after OriginTrialContext::AddTokens() to install
+    // origin trial features in JavaScript's global object.
+    ScriptController()->PrepareForEvaluation();
   }
   void FetchAndRunClassicScript(
       const KURL& script_url,
diff --git a/third_party/blink/renderer/core/workers/shared_worker_global_scope.cc b/third_party/blink/renderer/core/workers/shared_worker_global_scope.cc
index 13acda4..9e47feb 100644
--- a/third_party/blink/renderer/core/workers/shared_worker_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/shared_worker_global_scope.cc
@@ -34,6 +34,7 @@
 #include "base/feature_list.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/renderer/bindings/core/v8/source_location.h"
+#include "third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.h"
 #include "third_party/blink/renderer/core/events/message_event.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/inspector/console_message.h"
@@ -126,6 +127,10 @@
   BindContentSecurityPolicyToExecutionContext();
 
   OriginTrialContext::AddTokens(this, response_origin_trial_tokens);
+
+  // This should be called after OriginTrialContext::AddTokens() to install
+  // origin trial features in JavaScript's global object.
+  ScriptController()->PrepareForEvaluation();
 }
 
 // https://html.spec.whatwg.org/C/#worker-processing-model
diff --git a/third_party/blink/renderer/core/workers/worker_global_scope.cc b/third_party/blink/renderer/core/workers/worker_global_scope.cc
index b16111a..ea2197e 100644
--- a/third_party/blink/renderer/core/workers/worker_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/worker_global_scope.cc
@@ -411,6 +411,7 @@
     base::TimeTicks time_origin)
     : WorkerOrWorkletGlobalScope(
           thread->GetIsolate(),
+          creation_params->off_main_thread_fetch_option,
           creation_params->global_scope_name,
           creation_params->parent_devtools_token,
           creation_params->v8_cache_options,
diff --git a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc
index fbff9c2..b5c7400 100644
--- a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc
@@ -166,6 +166,7 @@
 
 WorkerOrWorkletGlobalScope::WorkerOrWorkletGlobalScope(
     v8::Isolate* isolate,
+    OffMainThreadWorkerScriptFetchOption off_main_thread_fetch_option,
     const String& name,
     const base::UnguessableToken& parent_devtools_token,
     V8CacheOptions v8_cache_options,
@@ -173,6 +174,7 @@
     scoped_refptr<WebWorkerFetchContext> web_worker_fetch_context,
     WorkerReportingProxy& reporting_proxy)
     : ExecutionContext(isolate, Agent::CreateForWorkerOrWorklet(isolate)),
+      off_main_thread_fetch_option_(off_main_thread_fetch_option),
       name_(name),
       parent_devtools_token_(parent_devtools_token),
       worker_clients_(worker_clients),
diff --git a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h
index c9feb18..f401caf 100644
--- a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h
+++ b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h
@@ -17,6 +17,7 @@
 #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
 #include "third_party/blink/renderer/core/frame/web_feature_forward.h"
 #include "third_party/blink/renderer/core/script/modulator.h"
+#include "third_party/blink/renderer/core/workers/global_scope_creation_params.h"
 #include "third_party/blink/renderer/core/workers/worker_clients.h"
 #include "third_party/blink/renderer/core/workers/worker_navigator.h"
 #include "third_party/blink/renderer/platform/scheduler/public/worker_scheduler.h"
@@ -45,6 +46,7 @@
 
   WorkerOrWorkletGlobalScope(
       v8::Isolate*,
+      OffMainThreadWorkerScriptFetchOption,
       const String& name,
       const base::UnguessableToken& parent_devtools_token,
       V8CacheOptions,
@@ -135,6 +137,11 @@
   scheduler::WorkerScheduler* GetScheduler() override;
   scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(TaskType) override;
 
+  OffMainThreadWorkerScriptFetchOption GetOffMainThreadWorkerScriptFetchOption()
+      const {
+    return off_main_thread_fetch_option_;
+  }
+
  protected:
   // Sets outside's CSP used for off-main-thread top-level worker script
   // fetch.
@@ -167,6 +174,7 @@
 
   bool web_fetch_context_initialized_ = false;
 
+  const OffMainThreadWorkerScriptFetchOption off_main_thread_fetch_option_;
   const String name_;
   const base::UnguessableToken parent_devtools_token_;
 
diff --git a/third_party/blink/renderer/core/workers/worker_thread_test_helper.h b/third_party/blink/renderer/core/workers/worker_thread_test_helper.h
index 93afffd..30bf1e2 100644
--- a/third_party/blink/renderer/core/workers/worker_thread_test_helper.h
+++ b/third_party/blink/renderer/core/workers/worker_thread_test_helper.h
@@ -14,6 +14,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/source_location.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_cache_options.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_gc_controller.h"
+#include "third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.h"
 #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
 #include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/inspector/console_message.h"
@@ -73,6 +74,10 @@
     BindContentSecurityPolicyToExecutionContext();
 
     OriginTrialContext::AddTokens(this, response_origin_trial_tokens);
+
+    // This should be called after OriginTrialContext::AddTokens() to install
+    // origin trial features in JavaScript's global object.
+    ScriptController()->PrepareForEvaluation();
   }
   void FetchAndRunClassicScript(
       const KURL& script_url,
diff --git a/third_party/blink/renderer/core/workers/worklet_global_scope.cc b/third_party/blink/renderer/core/workers/worklet_global_scope.cc
index b0cc318..9ce4921 100644
--- a/third_party/blink/renderer/core/workers/worklet_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/worklet_global_scope.cc
@@ -62,6 +62,7 @@
     WorkerThread* worker_thread)
     : WorkerOrWorkletGlobalScope(
           isolate,
+          creation_params->off_main_thread_fetch_option,
           creation_params->global_scope_name,
           creation_params->parent_devtools_token,
           creation_params->v8_cache_options,
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
index 71d551c..1d1c826 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
@@ -473,6 +473,10 @@
 
   // TODO(nhiroki): Clarify mappings between the steps 4.8-4.11 and
   // implementation.
+
+  // This should be called after OriginTrialContext::AddTokens() to install
+  // origin trial features in JavaScript's global object.
+  ScriptController()->PrepareForEvaluation();
 }
 
 // https://w3c.github.io/ServiceWorker/#run-service-worker-algorithm