Bug 1472338: part 2) Change `clipboard.readText()` to read from the clipboard asynchr...
[gecko.git] / dom / script / ScriptLoadHandler.cpp
blobae5a277996a58d81ba22d8f38a77b0d981dc00ac
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "ScriptLoadHandler.h"
9 #include <stdlib.h>
10 #include <utility>
11 #include "ScriptLoader.h"
12 #include "ScriptTrace.h"
13 #include "js/Transcoding.h"
14 #include "js/loader/ScriptLoadRequest.h"
15 #include "mozilla/Assertions.h"
16 #include "mozilla/CheckedInt.h"
17 #include "mozilla/DebugOnly.h"
18 #include "mozilla/Encoding.h"
19 #include "mozilla/Logging.h"
20 #include "mozilla/NotNull.h"
21 #include "mozilla/ScopeExit.h"
22 #include "mozilla/StaticPrefs_dom.h"
23 #include "mozilla/Utf8.h"
24 #include "mozilla/Vector.h"
25 #include "mozilla/dom/Document.h"
26 #include "mozilla/dom/SRICheck.h"
27 #include "mozilla/dom/ScriptDecoding.h"
28 #include "nsCOMPtr.h"
29 #include "nsContentUtils.h"
30 #include "nsDebug.h"
31 #include "nsICacheInfoChannel.h"
32 #include "nsIChannel.h"
33 #include "nsIHttpChannel.h"
34 #include "nsIRequest.h"
35 #include "nsIScriptElement.h"
36 #include "nsIURI.h"
37 #include "nsJSUtils.h"
38 #include "nsMimeTypes.h"
39 #include "nsString.h"
40 #include "nsTArray.h"
42 namespace mozilla {
43 namespace dom {
45 #undef LOG
46 #define LOG(args) \
47 MOZ_LOG(ScriptLoader::gScriptLoaderLog, mozilla::LogLevel::Debug, args)
49 #define LOG_ENABLED() \
50 MOZ_LOG_TEST(ScriptLoader::gScriptLoaderLog, mozilla::LogLevel::Debug)
52 ScriptLoadHandler::ScriptLoadHandler(
53 ScriptLoader* aScriptLoader, JS::loader::ScriptLoadRequest* aRequest,
54 UniquePtr<SRICheckDataVerifier>&& aSRIDataVerifier)
55 : mScriptLoader(aScriptLoader),
56 mRequest(aRequest),
57 mSRIDataVerifier(std::move(aSRIDataVerifier)),
58 mSRIStatus(NS_OK),
59 mDecoder() {
60 MOZ_ASSERT(mRequest->IsUnknownDataType());
61 MOZ_ASSERT(mRequest->IsLoading());
64 ScriptLoadHandler::~ScriptLoadHandler() = default;
66 NS_IMPL_ISUPPORTS(ScriptLoadHandler, nsIIncrementalStreamLoaderObserver)
68 template <typename Unit>
69 nsresult ScriptLoadHandler::DecodeRawDataHelper(const uint8_t* aData,
70 uint32_t aDataLength,
71 bool aEndOfStream) {
72 CheckedInt<size_t> needed =
73 ScriptDecoding<Unit>::MaxBufferLength(mDecoder, aDataLength);
74 if (!needed.isValid()) {
75 return NS_ERROR_OUT_OF_MEMORY;
78 // Reference to the script source buffer which we will update.
79 JS::loader::ScriptLoadRequest::ScriptTextBuffer<Unit>& scriptText =
80 mRequest->ScriptText<Unit>();
82 uint32_t haveRead = scriptText.length();
84 CheckedInt<uint32_t> capacity = haveRead;
85 capacity += needed.value();
87 if (!capacity.isValid() || !scriptText.resize(capacity.value())) {
88 return NS_ERROR_OUT_OF_MEMORY;
91 size_t written = ScriptDecoding<Unit>::DecodeInto(
92 mDecoder, Span(aData, aDataLength),
93 Span(scriptText.begin() + haveRead, needed.value()), aEndOfStream);
94 MOZ_ASSERT(written <= needed.value());
96 haveRead += written;
97 MOZ_ASSERT(haveRead <= capacity.value(),
98 "mDecoder produced more data than expected");
99 MOZ_ALWAYS_TRUE(scriptText.resize(haveRead));
100 mRequest->mScriptTextLength = scriptText.length();
102 return NS_OK;
105 nsresult ScriptLoadHandler::DecodeRawData(const uint8_t* aData,
106 uint32_t aDataLength,
107 bool aEndOfStream) {
108 if (mRequest->IsUTF16Text()) {
109 return DecodeRawDataHelper<char16_t>(aData, aDataLength, aEndOfStream);
112 return DecodeRawDataHelper<Utf8Unit>(aData, aDataLength, aEndOfStream);
115 NS_IMETHODIMP
116 ScriptLoadHandler::OnIncrementalData(nsIIncrementalStreamLoader* aLoader,
117 nsISupports* aContext,
118 uint32_t aDataLength, const uint8_t* aData,
119 uint32_t* aConsumedLength) {
120 nsCOMPtr<nsIRequest> channelRequest;
121 aLoader->GetRequest(getter_AddRefs(channelRequest));
123 if (!mPreloadStartNotified) {
124 mPreloadStartNotified = true;
125 mRequest->mLoadContext->NotifyStart(channelRequest);
128 if (mRequest->IsCanceled()) {
129 // If request cancelled, ignore any incoming data.
130 *aConsumedLength = aDataLength;
131 return NS_OK;
134 nsresult rv = NS_OK;
135 if (mRequest->IsUnknownDataType()) {
136 rv = EnsureKnownDataType(aLoader);
137 NS_ENSURE_SUCCESS(rv, rv);
140 if (mRequest->IsTextSource()) {
141 if (!EnsureDecoder(aLoader, aData, aDataLength,
142 /* aEndOfStream = */ false)) {
143 return NS_OK;
146 // Below we will/shall consume entire data chunk.
147 *aConsumedLength = aDataLength;
149 // Decoder has already been initialized. -- trying to decode all loaded
150 // bytes.
151 rv = DecodeRawData(aData, aDataLength, /* aEndOfStream = */ false);
152 NS_ENSURE_SUCCESS(rv, rv);
154 // If SRI is required for this load, appending new bytes to the hash.
155 if (mSRIDataVerifier && NS_SUCCEEDED(mSRIStatus)) {
156 mSRIStatus = mSRIDataVerifier->Update(aDataLength, aData);
158 } else {
159 MOZ_ASSERT(mRequest->IsBytecode());
160 if (!mRequest->mScriptBytecode.append(aData, aDataLength)) {
161 return NS_ERROR_OUT_OF_MEMORY;
164 *aConsumedLength = aDataLength;
165 uint32_t sriLength = 0;
166 rv = MaybeDecodeSRI(&sriLength);
167 if (NS_FAILED(rv)) {
168 return channelRequest->Cancel(mScriptLoader->RestartLoad(mRequest));
170 if (sriLength) {
171 mRequest->mBytecodeOffset = JS::AlignTranscodingBytecodeOffset(sriLength);
175 return rv;
178 bool ScriptLoadHandler::TrySetDecoder(nsIIncrementalStreamLoader* aLoader,
179 const uint8_t* aData,
180 uint32_t aDataLength, bool aEndOfStream) {
181 MOZ_ASSERT(mDecoder == nullptr,
182 "can't have a decoder already if we're trying to set one");
184 // JavaScript modules are always UTF-8.
185 if (mRequest->IsModuleRequest()) {
186 mDecoder = UTF_8_ENCODING->NewDecoderWithBOMRemoval();
187 return true;
190 // Determine if BOM check should be done. This occurs either
191 // if end-of-stream has been reached, or at least 3 bytes have
192 // been read from input.
193 if (!aEndOfStream && (aDataLength < 3)) {
194 return false;
197 // Do BOM detection.
198 const Encoding* encoding;
199 std::tie(encoding, std::ignore) = Encoding::ForBOM(Span(aData, aDataLength));
200 if (encoding) {
201 mDecoder = encoding->NewDecoderWithBOMRemoval();
202 return true;
205 // BOM detection failed, check content stream for charset.
206 nsCOMPtr<nsIRequest> req;
207 nsresult rv = aLoader->GetRequest(getter_AddRefs(req));
208 NS_ASSERTION(req, "StreamLoader's request went away prematurely");
209 NS_ENSURE_SUCCESS(rv, false);
211 nsCOMPtr<nsIChannel> channel = do_QueryInterface(req);
213 if (channel) {
214 nsAutoCString label;
215 if (NS_SUCCEEDED(channel->GetContentCharset(label)) &&
216 (encoding = Encoding::ForLabel(label))) {
217 mDecoder = encoding->NewDecoderWithoutBOMHandling();
218 return true;
222 // Check the hint charset from the script element or preload
223 // request.
224 nsAutoString hintCharset;
225 if (!mRequest->mLoadContext->IsPreload()) {
226 mRequest->mLoadContext->GetScriptElement()->GetScriptCharset(hintCharset);
227 } else {
228 nsTArray<ScriptLoader::PreloadInfo>::index_type i =
229 mScriptLoader->mPreloads.IndexOf(
230 mRequest, 0, ScriptLoader::PreloadRequestComparator());
232 NS_ASSERTION(i != mScriptLoader->mPreloads.NoIndex,
233 "Incorrect preload bookkeeping");
234 hintCharset = mScriptLoader->mPreloads[i].mCharset;
237 if ((encoding = Encoding::ForLabel(hintCharset))) {
238 mDecoder = encoding->NewDecoderWithoutBOMHandling();
239 return true;
242 // Get the charset from the charset of the document.
243 if (mScriptLoader->mDocument) {
244 encoding = mScriptLoader->mDocument->GetDocumentCharacterSet();
245 mDecoder = encoding->NewDecoderWithoutBOMHandling();
246 return true;
249 // Curiously, there are various callers that don't pass aDocument. The
250 // fallback in the old code was ISO-8859-1, which behaved like
251 // windows-1252.
252 mDecoder = WINDOWS_1252_ENCODING->NewDecoderWithoutBOMHandling();
253 return true;
256 nsresult ScriptLoadHandler::MaybeDecodeSRI(uint32_t* sriLength) {
257 *sriLength = 0;
259 if (!mSRIDataVerifier || mSRIDataVerifier->IsComplete() ||
260 NS_FAILED(mSRIStatus)) {
261 return NS_OK;
264 // Skip until the content is large enough to be decoded.
265 if (mRequest->mScriptBytecode.length() <=
266 mSRIDataVerifier->DataSummaryLength()) {
267 return NS_OK;
270 mSRIStatus = mSRIDataVerifier->ImportDataSummary(
271 mRequest->mScriptBytecode.length(), mRequest->mScriptBytecode.begin());
273 if (NS_FAILED(mSRIStatus)) {
274 // We are unable to decode the hash contained in the alternate data which
275 // contains the bytecode, or it does not use the same algorithm.
276 LOG(
277 ("ScriptLoadHandler::MaybeDecodeSRI, failed to decode SRI, restart "
278 "request"));
279 return mSRIStatus;
282 *sriLength = mSRIDataVerifier->DataSummaryLength();
283 MOZ_ASSERT(*sriLength > 0);
284 return NS_OK;
287 nsresult ScriptLoadHandler::EnsureKnownDataType(
288 nsIIncrementalStreamLoader* aLoader) {
289 MOZ_ASSERT(mRequest->IsUnknownDataType());
290 MOZ_ASSERT(mRequest->IsLoading());
292 nsCOMPtr<nsIRequest> req;
293 nsresult rv = aLoader->GetRequest(getter_AddRefs(req));
294 MOZ_ASSERT(req, "StreamLoader's request went away prematurely");
295 NS_ENSURE_SUCCESS(rv, rv);
297 if (mRequest->IsLoadingSource()) {
298 mRequest->SetTextSource();
299 TRACE_FOR_TEST(mRequest->mLoadContext->GetScriptElement(),
300 "scriptloader_load_source");
301 return NS_OK;
304 nsCOMPtr<nsICacheInfoChannel> cic(do_QueryInterface(req));
305 if (cic) {
306 nsAutoCString altDataType;
307 cic->GetAlternativeDataType(altDataType);
308 if (altDataType.Equals(nsContentUtils::JSBytecodeMimeType())) {
309 mRequest->SetBytecode();
310 TRACE_FOR_TEST(mRequest->mLoadContext->GetScriptElement(),
311 "scriptloader_load_bytecode");
312 return NS_OK;
314 MOZ_ASSERT(altDataType.IsEmpty());
317 mRequest->SetTextSource();
318 TRACE_FOR_TEST(mRequest->mLoadContext->GetScriptElement(),
319 "scriptloader_load_source");
321 MOZ_ASSERT(!mRequest->IsUnknownDataType());
322 MOZ_ASSERT(mRequest->IsLoading());
323 return NS_OK;
326 NS_IMETHODIMP
327 ScriptLoadHandler::OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
328 nsISupports* aContext, nsresult aStatus,
329 uint32_t aDataLength,
330 const uint8_t* aData) {
331 nsresult rv = NS_OK;
332 if (LOG_ENABLED()) {
333 nsAutoCString url;
334 mRequest->mURI->GetAsciiSpec(url);
335 LOG(("ScriptLoadRequest (%p): Stream complete (url = %s)", mRequest.get(),
336 url.get()));
339 nsCOMPtr<nsIRequest> channelRequest;
340 aLoader->GetRequest(getter_AddRefs(channelRequest));
342 if (!mPreloadStartNotified) {
343 mPreloadStartNotified = true;
344 mRequest->mLoadContext->NotifyStart(channelRequest);
347 auto notifyStop = MakeScopeExit(
348 [&] { mRequest->mLoadContext->NotifyStop(channelRequest, rv); });
350 if (!mRequest->IsCanceled()) {
351 if (mRequest->IsUnknownDataType()) {
352 rv = EnsureKnownDataType(aLoader);
353 NS_ENSURE_SUCCESS(rv, rv);
356 if (mRequest->IsTextSource()) {
357 DebugOnly<bool> encoderSet =
358 EnsureDecoder(aLoader, aData, aDataLength, /* aEndOfStream = */ true);
359 MOZ_ASSERT(encoderSet);
360 rv = DecodeRawData(aData, aDataLength, /* aEndOfStream = */ true);
361 NS_ENSURE_SUCCESS(rv, rv);
363 LOG(("ScriptLoadRequest (%p): Source length in code units = %u",
364 mRequest.get(), unsigned(mRequest->ScriptTextLength())));
366 // If SRI is required for this load, appending new bytes to the hash.
367 if (mSRIDataVerifier && NS_SUCCEEDED(mSRIStatus)) {
368 mSRIStatus = mSRIDataVerifier->Update(aDataLength, aData);
370 } else {
371 MOZ_ASSERT(mRequest->IsBytecode());
372 if (!mRequest->mScriptBytecode.append(aData, aDataLength)) {
373 return NS_ERROR_OUT_OF_MEMORY;
376 LOG(("ScriptLoadRequest (%p): Bytecode length = %u", mRequest.get(),
377 unsigned(mRequest->mScriptBytecode.length())));
379 // If we abort while decoding the SRI, we fallback on explictly requesting
380 // the source. Thus, we should not continue in
381 // ScriptLoader::OnStreamComplete, which removes the request from the
382 // waiting lists.
384 // We calculate the SRI length below.
385 uint32_t unused;
386 rv = MaybeDecodeSRI(&unused);
387 if (NS_FAILED(rv)) {
388 return channelRequest->Cancel(mScriptLoader->RestartLoad(mRequest));
391 // The bytecode cache always starts with the SRI hash, thus even if there
392 // is no SRI data verifier instance, we still want to skip the hash.
393 uint32_t sriLength;
394 rv = SRICheckDataVerifier::DataSummaryLength(
395 mRequest->mScriptBytecode.length(), mRequest->mScriptBytecode.begin(),
396 &sriLength);
397 if (NS_FAILED(rv)) {
398 return channelRequest->Cancel(mScriptLoader->RestartLoad(mRequest));
401 mRequest->mBytecodeOffset = JS::AlignTranscodingBytecodeOffset(sriLength);
405 // Everything went well, keep the CacheInfoChannel alive such that we can
406 // later save the bytecode on the cache entry.
407 if (NS_SUCCEEDED(rv) && mRequest->IsSource() &&
408 StaticPrefs::dom_script_loader_bytecode_cache_enabled()) {
409 mRequest->mCacheInfo = do_QueryInterface(channelRequest);
410 LOG(("ScriptLoadRequest (%p): nsICacheInfoChannel = %p", mRequest.get(),
411 mRequest->mCacheInfo.get()));
414 // we have to mediate and use mRequest.
415 rv = mScriptLoader->OnStreamComplete(aLoader, mRequest, aStatus, mSRIStatus,
416 mSRIDataVerifier.get());
418 // In case of failure, clear the mCacheInfoChannel to avoid keeping it alive.
419 if (NS_FAILED(rv)) {
420 mRequest->mCacheInfo = nullptr;
423 return rv;
426 #undef LOG_ENABLED
427 #undef LOG
429 } // namespace dom
430 } // namespace mozilla