summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/core/workers/worker_classic_script_loader.cc
blob: 4b47f551f3662aa7c9a8f9d4633a008a87581212 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
/*
 * Copyright (C) 2009 Apple Inc. All Rights Reserved.
 * Copyright (C) 2009, 2011 Google Inc. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include "third_party/blink/renderer/core/workers/worker_classic_script_loader.h"

#include <memory>
#include "base/memory/scoped_refptr.h"
#include "third_party/blink/public/mojom/net/ip_address_space.mojom-blink.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/html/parser/text_resource_decoder.h"
#include "third_party/blink/renderer/core/inspector/console_message.h"
#include "third_party/blink/renderer/core/loader/allowed_by_nosniff.h"
#include "third_party/blink/renderer/core/loader/resource/script_resource.h"
#include "third_party/blink/renderer/core/loader/worker_threadable_loader.h"
#include "third_party/blink/renderer/core/origin_trials/origin_trial_context.h"
#include "third_party/blink/renderer/core/workers/worker_global_scope.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
#include "third_party/blink/renderer/platform/loader/fetch/text_resource_decoder_options.h"
#include "third_party/blink/renderer/platform/network/content_security_policy_response_headers.h"
#include "third_party/blink/renderer/platform/network/http_names.h"
#include "third_party/blink/renderer/platform/network/network_utils.h"
#include "third_party/blink/renderer/platform/weborigin/security_origin.h"

namespace blink {

WorkerClassicScriptLoader::WorkerClassicScriptLoader()
    : response_address_space_(mojom::IPAddressSpace::kPublic) {}

WorkerClassicScriptLoader::~WorkerClassicScriptLoader() {
  // If |m_threadableLoader| is still working, we have to cancel it here.
  // Otherwise didFail() of the deleted |this| will be called from
  // DocumentThreadableLoader::notifyFinished() when the frame will be
  // destroyed.
  if (need_to_cancel_)
    Cancel();
}

void WorkerClassicScriptLoader::LoadSynchronously(
    ExecutionContext& execution_context,
    const KURL& url,
    WebURLRequest::RequestContext request_context,
    mojom::IPAddressSpace creation_address_space) {
  url_ = url;
  execution_context_ = &execution_context;

  ResourceRequest request(url);
  request.SetHTTPMethod(HTTPNames::GET);
  request.SetExternalRequestStateFromRequestorAddressSpace(
      creation_address_space);
  request.SetRequestContext(request_context);

  SECURITY_DCHECK(execution_context.IsWorkerGlobalScope());

  ThreadableLoaderOptions options;

  ResourceLoaderOptions resource_loader_options;
  resource_loader_options.parser_disposition =
      ParserDisposition::kNotParserInserted;

  WorkerThreadableLoader::LoadResourceSynchronously(
      ToWorkerGlobalScope(execution_context), request, *this, options,
      resource_loader_options);
}

void WorkerClassicScriptLoader::LoadAsynchronously(
    ExecutionContext& execution_context,
    const KURL& url,
    WebURLRequest::RequestContext request_context,
    network::mojom::FetchRequestMode fetch_request_mode,
    network::mojom::FetchCredentialsMode fetch_credentials_mode,
    mojom::IPAddressSpace creation_address_space,
    base::OnceClosure response_callback,
    base::OnceClosure finished_callback) {
  DCHECK(response_callback || finished_callback);
  response_callback_ = std::move(response_callback);
  finished_callback_ = std::move(finished_callback);
  url_ = url;
  execution_context_ = &execution_context;

  ResourceRequest request(url);
  request.SetHTTPMethod(HTTPNames::GET);
  request.SetExternalRequestStateFromRequestorAddressSpace(
      creation_address_space);
  request.SetRequestContext(request_context);
  request.SetFetchRequestMode(fetch_request_mode);
  request.SetFetchCredentialsMode(fetch_credentials_mode);

  ThreadableLoaderOptions options;

  ResourceLoaderOptions resource_loader_options;

  // During create, callbacks may happen which could remove the last reference
  // to this object, while some of the callchain assumes that the client and
  // loader wouldn't be deleted within callbacks.
  // (E.g. see crbug.com/524694 for why we can't easily remove this protect)
  scoped_refptr<WorkerClassicScriptLoader> protect(this);
  need_to_cancel_ = true;
  threadable_loader_ = ThreadableLoader::Create(
      execution_context, this, options, resource_loader_options);
  threadable_loader_->Start(request);
  if (failed_)
    NotifyFinished();
}

const KURL& WorkerClassicScriptLoader::ResponseURL() const {
  DCHECK(!Failed());
  return response_url_;
}

void WorkerClassicScriptLoader::DidReceiveResponse(
    unsigned long identifier,
    const ResourceResponse& response,
    std::unique_ptr<WebDataConsumerHandle> handle) {
  DCHECK(!handle);
  if (response.HttpStatusCode() / 100 != 2 && response.HttpStatusCode()) {
    NotifyError();
    return;
  }
  if (!AllowedByNosniff::MimeTypeAsScript(execution_context_, response)) {
    NotifyError();
    return;
  }
  identifier_ = identifier;
  response_url_ = response.Url();
  response_encoding_ = response.TextEncodingName();
  app_cache_id_ = response.AppCacheID();

  referrer_policy_ = response.HttpHeaderField(HTTPNames::Referrer_Policy);
  ProcessContentSecurityPolicy(response);
  origin_trial_tokens_ = OriginTrialContext::ParseHeaderValue(
      response.HttpHeaderField(HTTPNames::Origin_Trial));

  if (NetworkUtils::IsReservedIPAddress(response.RemoteIPAddress())) {
    response_address_space_ =
        SecurityOrigin::Create(response_url_)->IsLocalhost()
            ? mojom::IPAddressSpace::kLocal
            : mojom::IPAddressSpace::kPrivate;
  }

  if (response_callback_)
    std::move(response_callback_).Run();
}

void WorkerClassicScriptLoader::DidReceiveData(const char* data, unsigned len) {
  if (failed_)
    return;

  if (!decoder_) {
    decoder_ = TextResourceDecoder::Create(TextResourceDecoderOptions(
        TextResourceDecoderOptions::kPlainTextContent,
        response_encoding_.IsEmpty() ? UTF8Encoding()
                                     : WTF::TextEncoding(response_encoding_)));
  }

  if (!len)
    return;

  source_text_.Append(decoder_->Decode(data, len));
}

void WorkerClassicScriptLoader::DidReceiveCachedMetadata(const char* data,
                                                         int size) {
  cached_metadata_ = std::make_unique<Vector<char>>(size);
  memcpy(cached_metadata_->data(), data, size);
}

void WorkerClassicScriptLoader::DidFinishLoading(unsigned long identifier) {
  need_to_cancel_ = false;
  if (!failed_ && decoder_)
    source_text_.Append(decoder_->Flush());

  NotifyFinished();
}

void WorkerClassicScriptLoader::DidFail(const ResourceError& error) {
  need_to_cancel_ = false;
  canceled_ = error.IsCancellation();
  NotifyError();
}

void WorkerClassicScriptLoader::DidFailRedirectCheck() {
  // When didFailRedirectCheck() is called, the ResourceLoader for the script
  // is not canceled yet. So we don't reset |m_needToCancel| here.
  NotifyError();
}

void WorkerClassicScriptLoader::Cancel() {
  need_to_cancel_ = false;
  if (threadable_loader_)
    threadable_loader_->Cancel();
}

String WorkerClassicScriptLoader::SourceText() {
  return source_text_.ToString();
}

void WorkerClassicScriptLoader::NotifyError() {
  failed_ = true;
  // notifyError() could be called before ThreadableLoader::create() returns
  // e.g. from didFail(), and in that case m_threadableLoader is not yet set
  // (i.e. still null).
  // Since the callback invocation in notifyFinished() potentially delete
  // |this| object, the callback invocation should be postponed until the
  // create() call returns. See loadAsynchronously() for the postponed call.
  if (threadable_loader_)
    NotifyFinished();
}

void WorkerClassicScriptLoader::NotifyFinished() {
  if (!finished_callback_)
    return;

  std::move(finished_callback_).Run();
}

void WorkerClassicScriptLoader::ProcessContentSecurityPolicy(
    const ResourceResponse& response) {
  // Per http://www.w3.org/TR/CSP2/#processing-model-workers, if the Worker's
  // URL is not a GUID, then it grabs its CSP from the response headers
  // directly.  Otherwise, the Worker inherits the policy from the parent
  // document (which is implemented in WorkerMessagingProxy, and
  // m_contentSecurityPolicy should be left as nullptr to inherit the policy).
  if (!response.Url().ProtocolIs("blob") &&
      !response.Url().ProtocolIs("file") &&
      !response.Url().ProtocolIs("filesystem")) {
    content_security_policy_ = ContentSecurityPolicy::Create();
    content_security_policy_->SetOverrideURLForSelf(response.Url());
    content_security_policy_->DidReceiveHeaders(
        ContentSecurityPolicyResponseHeaders(response));
  }
}

}  // namespace blink