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