1 /*
2  * Copyright (C) 2009 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include "third_party/blink/renderer/core/workers/dedicated_worker_global_scope.h"
32 
33 #include <memory>
34 #include "base/feature_list.h"
35 #include "third_party/blink/public/common/features.h"
36 #include "third_party/blink/public/mojom/appcache/appcache.mojom-blink.h"
37 #include "third_party/blink/renderer/bindings/core/v8/serialization/post_message_helper.h"
38 #include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h"
39 #include "third_party/blink/renderer/bindings/core/v8/v8_post_message_options.h"
40 #include "third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.h"
41 #include "third_party/blink/renderer/core/core_initializer.h"
42 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
43 #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
44 #include "third_party/blink/renderer/core/inspector/worker_thread_debugger.h"
45 #include "third_party/blink/renderer/core/messaging/blink_transferable_message.h"
46 #include "third_party/blink/renderer/core/origin_trials/origin_trial_context.h"
47 #include "third_party/blink/renderer/core/probe/core_probes.h"
48 #include "third_party/blink/renderer/core/workers/dedicated_worker_object_proxy.h"
49 #include "third_party/blink/renderer/core/workers/dedicated_worker_thread.h"
50 #include "third_party/blink/renderer/core/workers/global_scope_creation_params.h"
51 #include "third_party/blink/renderer/core/workers/worker_classic_script_loader.h"
52 #include "third_party/blink/renderer/core/workers/worker_clients.h"
53 #include "third_party/blink/renderer/core/workers/worker_module_tree_client.h"
54 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
55 #include "third_party/blink/renderer/platform/bindings/script_state.h"
56 #include "third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object_snapshot.h"
57 #include "third_party/blink/renderer/platform/weborigin/security_policy.h"
58 
59 namespace blink {
60 
61 // static
Create(std::unique_ptr<GlobalScopeCreationParams> creation_params,DedicatedWorkerThread * thread,base::TimeTicks time_origin)62 DedicatedWorkerGlobalScope* DedicatedWorkerGlobalScope::Create(
63     std::unique_ptr<GlobalScopeCreationParams> creation_params,
64     DedicatedWorkerThread* thread,
65     base::TimeTicks time_origin) {
66   std::unique_ptr<Vector<String>> outside_origin_trial_tokens =
67       std::move(creation_params->origin_trial_tokens);
68   BeginFrameProviderParams begin_frame_provider_params =
69       creation_params->begin_frame_provider_params;
70 
71   KURL response_script_url = creation_params->script_url;
72   network::mojom::ReferrerPolicy response_referrer_policy =
73       creation_params->referrer_policy;
74   base::Optional<network::mojom::IPAddressSpace> response_address_space =
75       creation_params->response_address_space;
76 
77   auto* global_scope = MakeGarbageCollected<DedicatedWorkerGlobalScope>(
78       std::move(creation_params), thread, time_origin,
79       std::move(outside_origin_trial_tokens), begin_frame_provider_params);
80 
81   if (global_scope->IsOffMainThreadScriptFetchDisabled()) {
82     // Legacy on-the-main-thread worker script fetch (to be removed):
83     // Pass dummy CSP headers here as it is superseded by outside's CSP headers
84     // in Initialize().
85     // Pass dummy origin trial tokens here as it is already set to outside's
86     // origin trial tokens in DedicatedWorkerGlobalScope's constructor.
87     // Pass kAppCacheNoCacheId here as on-the-main-thread script fetch doesn't
88     // have its own appcache and instead depends on the parent frame's one.
89     global_scope->Initialize(
90         response_script_url, response_referrer_policy, *response_address_space,
91         Vector<CSPHeaderAndType>(), nullptr /* response_origin_trial_tokens */,
92         mojom::blink::kAppCacheNoCacheId);
93     return global_scope;
94   } else {
95     // Off-the-main-thread worker script fetch:
96     // Initialize() is called after script fetch.
97     return global_scope;
98   }
99 }
100 
DedicatedWorkerGlobalScope(std::unique_ptr<GlobalScopeCreationParams> creation_params,DedicatedWorkerThread * thread,base::TimeTicks time_origin,std::unique_ptr<Vector<String>> outside_origin_trial_tokens,const BeginFrameProviderParams & begin_frame_provider_params)101 DedicatedWorkerGlobalScope::DedicatedWorkerGlobalScope(
102     std::unique_ptr<GlobalScopeCreationParams> creation_params,
103     DedicatedWorkerThread* thread,
104     base::TimeTicks time_origin,
105     std::unique_ptr<Vector<String>> outside_origin_trial_tokens,
106     const BeginFrameProviderParams& begin_frame_provider_params)
107     : WorkerGlobalScope(std::move(creation_params), thread, time_origin),
108       animation_frame_provider_(
109           MakeGarbageCollected<WorkerAnimationFrameProvider>(
110               this,
111               begin_frame_provider_params)) {
112   CoreInitializer::GetInstance().ProvideLocalFileSystemToWorker(*this);
113 
114   // Dedicated workers don't need to pause after script fetch.
115   ReadyToRunWorkerScript();
116   // Inherit the outside's origin trial tokens.
117   OriginTrialContext::AddTokens(this, outside_origin_trial_tokens.get());
118 }
119 
120 DedicatedWorkerGlobalScope::~DedicatedWorkerGlobalScope() = default;
121 
InterfaceName() const122 const AtomicString& DedicatedWorkerGlobalScope::InterfaceName() const {
123   return event_target_names::kDedicatedWorkerGlobalScope;
124 }
125 
126 // https://html.spec.whatwg.org/C/#worker-processing-model
Initialize(const KURL & response_url,network::mojom::ReferrerPolicy response_referrer_policy,network::mojom::IPAddressSpace response_address_space,const Vector<CSPHeaderAndType> &,const Vector<String> *,int64_t appcache_id)127 void DedicatedWorkerGlobalScope::Initialize(
128     const KURL& response_url,
129     network::mojom::ReferrerPolicy response_referrer_policy,
130     network::mojom::IPAddressSpace response_address_space,
131     const Vector<CSPHeaderAndType>& /* response_csp_headers */,
132     const Vector<String>* /* response_origin_trial_tokens */,
133     int64_t appcache_id) {
134   // Step 12.3. "Set worker global scope's url to response's url."
135   InitializeURL(response_url);
136 
137   // Step 12.4. "Set worker global scope's HTTPS state to response's HTTPS
138   // state."
139   // This is done in the constructor of WorkerGlobalScope.
140 
141   // Step 12.5. "Set worker global scope's referrer policy to the result of
142   // parsing the `Referrer-Policy` header of response."
143   SetReferrerPolicy(response_referrer_policy);
144 
145   // https://wicg.github.io/cors-rfc1918/#integration-html
146   GetSecurityContext().SetAddressSpace(response_address_space);
147 
148   // Step 12.6. "Execute the Initialize a global object's CSP list algorithm
149   // on worker global scope and response. [CSP]"
150   // DedicatedWorkerGlobalScope inherits the outside's CSP instead of the
151   // response CSP headers. These should be called after SetAddressSpace() to
152   // correctly override the address space by the "treat-as-public-address" CSP
153   // directive.
154   InitContentSecurityPolicyFromVector(OutsideContentSecurityPolicyHeaders());
155   BindContentSecurityPolicyToExecutionContext();
156 
157   // This should be called after OriginTrialContext::AddTokens() to install
158   // origin trial features in JavaScript's global object.
159   // DedicatedWorkerGlobalScope inherits the outside's OriginTrialTokens in the
160   // constructor instead of the response origin trial tokens.
161   ScriptController()->PrepareForEvaluation();
162 
163   // TODO(https://crbug.com/945673): Notify an application cache host of
164   // |appcache_id| here to support AppCache with PlzDedicatedWorker.
165 }
166 
167 // https://html.spec.whatwg.org/C/#worker-processing-model
FetchAndRunClassicScript(const KURL & script_url,const FetchClientSettingsObjectSnapshot & outside_settings_object,WorkerResourceTimingNotifier & outside_resource_timing_notifier,const v8_inspector::V8StackTraceId & stack_id)168 void DedicatedWorkerGlobalScope::FetchAndRunClassicScript(
169     const KURL& script_url,
170     const FetchClientSettingsObjectSnapshot& outside_settings_object,
171     WorkerResourceTimingNotifier& outside_resource_timing_notifier,
172     const v8_inspector::V8StackTraceId& stack_id) {
173   DCHECK(base::FeatureList::IsEnabled(features::kPlzDedicatedWorker));
174   DCHECK(!IsContextPaused());
175 
176   // Step 12. "Fetch a classic worker script given url, outside settings,
177   // destination, and inside settings."
178   mojom::RequestContextType context_type = mojom::RequestContextType::WORKER;
179   network::mojom::RequestDestination destination =
180       network::mojom::RequestDestination::kWorker;
181 
182   // Step 12.1. "Set request's reserved client to inside settings."
183   // The browesr process takes care of this.
184 
185   // Step 12.2. "Fetch request, and asynchronously wait to run the remaining
186   // steps as part of fetch's process response for the response response."
187   WorkerClassicScriptLoader* classic_script_loader =
188       MakeGarbageCollected<WorkerClassicScriptLoader>();
189   classic_script_loader->LoadTopLevelScriptAsynchronously(
190       *this,
191       CreateOutsideSettingsFetcher(outside_settings_object,
192                                    outside_resource_timing_notifier),
193       script_url, context_type, destination,
194       network::mojom::RequestMode::kSameOrigin,
195       network::mojom::CredentialsMode::kSameOrigin,
196       WTF::Bind(&DedicatedWorkerGlobalScope::DidReceiveResponseForClassicScript,
197                 WrapWeakPersistent(this),
198                 WrapPersistent(classic_script_loader)),
199       WTF::Bind(&DedicatedWorkerGlobalScope::DidFetchClassicScript,
200                 WrapWeakPersistent(this), WrapPersistent(classic_script_loader),
201                 stack_id));
202 }
203 
204 // https://html.spec.whatwg.org/C/#worker-processing-model
FetchAndRunModuleScript(const KURL & module_url_record,const FetchClientSettingsObjectSnapshot & outside_settings_object,WorkerResourceTimingNotifier & outside_resource_timing_notifier,network::mojom::CredentialsMode credentials_mode,RejectCoepUnsafeNone reject_coep_unsafe_none)205 void DedicatedWorkerGlobalScope::FetchAndRunModuleScript(
206     const KURL& module_url_record,
207     const FetchClientSettingsObjectSnapshot& outside_settings_object,
208     WorkerResourceTimingNotifier& outside_resource_timing_notifier,
209     network::mojom::CredentialsMode credentials_mode,
210     RejectCoepUnsafeNone reject_coep_unsafe_none) {
211   reject_coep_unsafe_none_ = reject_coep_unsafe_none;
212   // Step 12: "Let destination be "sharedworker" if is shared is true, and
213   // "worker" otherwise."
214   mojom::RequestContextType context_type = mojom::RequestContextType::WORKER;
215   network::mojom::RequestDestination destination =
216       network::mojom::RequestDestination::kWorker;
217 
218   // Step 13: "... Fetch a module worker script graph given url, outside
219   // settings, destination, the value of the credentials member of options, and
220   // inside settings."
221   FetchModuleScript(module_url_record, outside_settings_object,
222                     outside_resource_timing_notifier, context_type, destination,
223                     credentials_mode,
224                     ModuleScriptCustomFetchType::kWorkerConstructor,
225                     MakeGarbageCollected<WorkerModuleTreeClient>(
226                         ScriptController()->GetScriptState()));
227 }
228 
IsOffMainThreadScriptFetchDisabled()229 bool DedicatedWorkerGlobalScope::IsOffMainThreadScriptFetchDisabled() {
230   // The top-level dedicated worker script is loaded on the main thread when the
231   // script type is classic and PlzDedicatedWorker (off-the-main-thread script
232   // fetch) is disabled.
233   // TODO(https://crbug.com/835717): Remove this function after dedicated
234   // workers support off-the-main-thread script fetch by default.
235   return GetScriptType() == mojom::blink::ScriptType::kClassic &&
236          !base::FeatureList::IsEnabled(features::kPlzDedicatedWorker);
237 }
238 
name() const239 const String DedicatedWorkerGlobalScope::name() const {
240   return Name();
241 }
242 
postMessage(ScriptState * script_state,const ScriptValue & message,HeapVector<ScriptValue> & transfer,ExceptionState & exception_state)243 void DedicatedWorkerGlobalScope::postMessage(ScriptState* script_state,
244                                              const ScriptValue& message,
245                                              HeapVector<ScriptValue>& transfer,
246                                              ExceptionState& exception_state) {
247   PostMessageOptions* options = PostMessageOptions::Create();
248   if (!transfer.IsEmpty())
249     options->setTransfer(transfer);
250   postMessage(script_state, message, options, exception_state);
251 }
252 
postMessage(ScriptState * script_state,const ScriptValue & message,const PostMessageOptions * options,ExceptionState & exception_state)253 void DedicatedWorkerGlobalScope::postMessage(ScriptState* script_state,
254                                              const ScriptValue& message,
255                                              const PostMessageOptions* options,
256                                              ExceptionState& exception_state) {
257   Transferables transferables;
258   scoped_refptr<SerializedScriptValue> serialized_message =
259       PostMessageHelper::SerializeMessageByMove(script_state->GetIsolate(),
260                                                 message, options, transferables,
261                                                 exception_state);
262   if (exception_state.HadException())
263     return;
264   DCHECK(serialized_message);
265   BlinkTransferableMessage transferable_message;
266   transferable_message.message = serialized_message;
267   transferable_message.sender_origin =
268       GetExecutionContext()->GetSecurityOrigin()->IsolatedCopy();
269   // Disentangle the port in preparation for sending it to the remote context.
270   transferable_message.ports = MessagePort::DisentanglePorts(
271       ExecutionContext::From(script_state), transferables.message_ports,
272       exception_state);
273   if (exception_state.HadException())
274     return;
275   WorkerThreadDebugger* debugger =
276       WorkerThreadDebugger::From(script_state->GetIsolate());
277   transferable_message.sender_stack_trace_id =
278       debugger->StoreCurrentStackTrace("postMessage");
279   WorkerObjectProxy().PostMessageToWorkerObject(
280       std::move(transferable_message));
281 }
282 
DidReceiveResponseForClassicScript(WorkerClassicScriptLoader * classic_script_loader)283 void DedicatedWorkerGlobalScope::DidReceiveResponseForClassicScript(
284     WorkerClassicScriptLoader* classic_script_loader) {
285   DCHECK(IsContextThread());
286   DCHECK(base::FeatureList::IsEnabled(features::kPlzDedicatedWorker));
287   probe::DidReceiveScriptResponse(this, classic_script_loader->Identifier());
288 }
289 
290 // https://html.spec.whatwg.org/C/#worker-processing-model
DidFetchClassicScript(WorkerClassicScriptLoader * classic_script_loader,const v8_inspector::V8StackTraceId & stack_id)291 void DedicatedWorkerGlobalScope::DidFetchClassicScript(
292     WorkerClassicScriptLoader* classic_script_loader,
293     const v8_inspector::V8StackTraceId& stack_id) {
294   DCHECK(IsContextThread());
295   DCHECK(base::FeatureList::IsEnabled(features::kPlzDedicatedWorker));
296 
297   // Step 12. "If the algorithm asynchronously completes with null, then:"
298   if (classic_script_loader->Failed()) {
299     // Step 12.1. "Queue a task to fire an event named error at worker."
300     // DidFailToFetchClassicScript() will asynchronously fire the event.
301     ReportingProxy().DidFailToFetchClassicScript();
302 
303     // Step 12.2. "Run the environment discarding steps for inside settings."
304     // Do nothing because the HTML spec doesn't define these steps for web
305     // workers.
306 
307     // Schedule worker termination.
308     close();
309 
310     // Step 12.3. "Return."
311     return;
312   }
313   ReportingProxy().DidFetchScript();
314   probe::ScriptImported(this, classic_script_loader->Identifier(),
315                         classic_script_loader->SourceText());
316 
317   auto response_referrer_policy = network::mojom::ReferrerPolicy::kDefault;
318   if (!classic_script_loader->GetReferrerPolicy().IsNull()) {
319     SecurityPolicy::ReferrerPolicyFromHeaderValue(
320         classic_script_loader->GetReferrerPolicy(),
321         kDoNotSupportReferrerPolicyLegacyKeywords, &response_referrer_policy);
322   }
323 
324   // Step 12.3-12.6 are implemented in Initialize().
325   // Pass dummy CSP headers here as it is superseded by outside's CSP headers in
326   // Initialize().
327   // Pass dummy origin trial tokens here as it is already set to outside's
328   // origin trial tokens in DedicatedWorkerGlobalScope's constructor.
329   Initialize(classic_script_loader->ResponseURL(), response_referrer_policy,
330              classic_script_loader->ResponseAddressSpace(),
331              Vector<CSPHeaderAndType>(),
332              nullptr /* response_origin_trial_tokens */,
333              classic_script_loader->AppCacheID());
334 
335   // Step 12.7. "Asynchronously complete the perform the fetch steps with
336   // response."
337   EvaluateClassicScript(
338       classic_script_loader->ResponseURL(), classic_script_loader->SourceText(),
339       classic_script_loader->ReleaseCachedMetadata(), stack_id);
340 }
341 
requestAnimationFrame(V8FrameRequestCallback * callback,ExceptionState & exception_state)342 int DedicatedWorkerGlobalScope::requestAnimationFrame(
343     V8FrameRequestCallback* callback,
344     ExceptionState& exception_state) {
345   auto* frame_callback =
346       MakeGarbageCollected<FrameRequestCallbackCollection::V8FrameCallback>(
347           callback);
348   frame_callback->SetUseLegacyTimeBase(false);
349 
350   int ret = animation_frame_provider_->RegisterCallback(frame_callback);
351 
352   if (ret == WorkerAnimationFrameProvider::kInvalidCallbackId) {
353     exception_state.ThrowDOMException(
354         DOMExceptionCode::kNotSupportedError,
355         "requestAnimationFrame not supported in this Worker.");
356   }
357 
358   return ret;
359 }
360 
cancelAnimationFrame(int id)361 void DedicatedWorkerGlobalScope::cancelAnimationFrame(int id) {
362   animation_frame_provider_->CancelCallback(id);
363 }
364 
WorkerObjectProxy() const365 DedicatedWorkerObjectProxy& DedicatedWorkerGlobalScope::WorkerObjectProxy()
366     const {
367   return static_cast<DedicatedWorkerThread*>(GetThread())->WorkerObjectProxy();
368 }
369 
Trace(Visitor * visitor)370 void DedicatedWorkerGlobalScope::Trace(Visitor* visitor) {
371   visitor->Trace(animation_frame_provider_);
372   WorkerGlobalScope::Trace(visitor);
373 }
374 
375 }  // namespace blink
376