forked from mirrors/gecko-dev
		
	 d2fa4ee51a
			
		
	
	
		d2fa4ee51a
		
	
	
	
	
		
			
			I was over eager in introducing the strong pointers to WorkerLoadContext. It turns out that previously when we were cycle collecting our ScriptLoadRequests, we were also cleaning up everything related to WorkerLoadContext. Also, in an attempt to fix the cancellation for workers, We accidentally stopped cleaning up the reference to the cache creator. This patch does the following: 1) cleans up the cache creator reference whenever we finish with a cache load handler. This is done by calling Fail appropriately. This solves both bug 1798667 (we no longer reach NEW_URI if we fail) and 1781295 (we no longer leak memory because things were not cleaned up properly). 2) Does no remove the back reference to WorkerLoadContext from the LoadRequest. It turns out that this is sometimes necessary. There is a chance that we will accidently try to access the WorkerLoadContext after cancellation. This actually causes problems later on in the module code. Differential Revision: https://phabricator.services.mozilla.com/D161288
		
			
				
	
	
		
			302 lines
		
	
	
	
		
			9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			302 lines
		
	
	
	
		
			9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| #include "ScriptLoadRequest.h"
 | |
| #include "GeckoProfiler.h"
 | |
| 
 | |
| #include "mozilla/dom/Document.h"
 | |
| #include "mozilla/dom/ScriptLoadContext.h"
 | |
| #include "mozilla/dom/WorkerLoadContext.h"
 | |
| #include "mozilla/dom/ScriptSettings.h"
 | |
| #include "mozilla/HoldDropJSObjects.h"
 | |
| #include "mozilla/StaticPrefs_dom.h"
 | |
| #include "mozilla/Unused.h"
 | |
| #include "mozilla/Utf8.h"  // mozilla::Utf8Unit
 | |
| 
 | |
| #include "js/OffThreadScriptCompilation.h"
 | |
| #include "js/SourceText.h"
 | |
| 
 | |
| #include "ModuleLoadRequest.h"
 | |
| #include "nsContentUtils.h"
 | |
| #include "nsICacheInfoChannel.h"
 | |
| #include "nsIClassOfService.h"
 | |
| #include "nsISupportsPriority.h"
 | |
| 
 | |
| using JS::SourceText;
 | |
| 
 | |
| namespace JS::loader {
 | |
| 
 | |
| //////////////////////////////////////////////////////////////
 | |
| // ScriptFetchOptions
 | |
| //////////////////////////////////////////////////////////////
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION(ScriptFetchOptions, mTriggeringPrincipal, mElement)
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(ScriptFetchOptions, AddRef)
 | |
| NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(ScriptFetchOptions, Release)
 | |
| 
 | |
| ScriptFetchOptions::ScriptFetchOptions(
 | |
|     mozilla::CORSMode aCORSMode, mozilla::dom::ReferrerPolicy aReferrerPolicy,
 | |
|     nsIPrincipal* aTriggeringPrincipal, mozilla::dom::Element* aElement)
 | |
|     : mCORSMode(aCORSMode),
 | |
|       mReferrerPolicy(aReferrerPolicy),
 | |
|       mTriggeringPrincipal(aTriggeringPrincipal),
 | |
|       mElement(aElement) {}
 | |
| 
 | |
| ScriptFetchOptions::~ScriptFetchOptions() = default;
 | |
| 
 | |
| //////////////////////////////////////////////////////////////
 | |
| // ScriptLoadRequest
 | |
| //////////////////////////////////////////////////////////////
 | |
| 
 | |
| NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ScriptLoadRequest)
 | |
| NS_INTERFACE_MAP_END
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTING_ADDREF(ScriptLoadRequest)
 | |
| NS_IMPL_CYCLE_COLLECTING_RELEASE(ScriptLoadRequest)
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_CLASS(ScriptLoadRequest)
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ScriptLoadRequest)
 | |
|   NS_IMPL_CYCLE_COLLECTION_UNLINK(mFetchOptions, mCacheInfo, mLoadContext)
 | |
|   tmp->mScriptForBytecodeEncoding = nullptr;
 | |
|   tmp->DropBytecodeCacheReferences();
 | |
| NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ScriptLoadRequest)
 | |
|   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFetchOptions, mCacheInfo, mLoadContext)
 | |
| NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(ScriptLoadRequest)
 | |
|   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mScriptForBytecodeEncoding)
 | |
| NS_IMPL_CYCLE_COLLECTION_TRACE_END
 | |
| 
 | |
| ScriptLoadRequest::ScriptLoadRequest(ScriptKind aKind, nsIURI* aURI,
 | |
|                                      ScriptFetchOptions* aFetchOptions,
 | |
|                                      const SRIMetadata& aIntegrity,
 | |
|                                      nsIURI* aReferrer,
 | |
|                                      LoadContextBase* aContext)
 | |
|     : mKind(aKind),
 | |
|       mState(State::Fetching),
 | |
|       mFetchSourceOnly(false),
 | |
|       mDataType(DataType::eUnknown),
 | |
|       mFetchOptions(aFetchOptions),
 | |
|       mIntegrity(aIntegrity),
 | |
|       mReferrer(aReferrer),
 | |
|       mScriptTextLength(0),
 | |
|       mScriptBytecode(),
 | |
|       mBytecodeOffset(0),
 | |
|       mURI(aURI),
 | |
|       mLoadContext(aContext) {
 | |
|   MOZ_ASSERT(mFetchOptions);
 | |
|   if (mLoadContext) {
 | |
|     mLoadContext->SetRequest(this);
 | |
|   }
 | |
| }
 | |
| 
 | |
| ScriptLoadRequest::~ScriptLoadRequest() {
 | |
|   if (IsMarkedForBytecodeEncoding()) {
 | |
|     DropBytecodeCacheReferences();
 | |
|   }
 | |
|   mLoadContext = nullptr;
 | |
|   DropJSObjects(this);
 | |
| }
 | |
| 
 | |
| void ScriptLoadRequest::SetReady() {
 | |
|   MOZ_ASSERT(!IsReadyToRun());
 | |
|   mState = State::Ready;
 | |
| }
 | |
| 
 | |
| void ScriptLoadRequest::Cancel() {
 | |
|   mState = State::Canceled;
 | |
|   if (HasScriptLoadContext()) {
 | |
|     GetScriptLoadContext()->MaybeCancelOffThreadScript();
 | |
|   }
 | |
|   if (HasWorkerLoadContext()) {
 | |
|     // The back reference needs to be cleared for workers, as there is no CC.
 | |
|     // However, we don't want to remove our pointer to the worker load
 | |
|     // context as it is used to determine load failure information.
 | |
|     GetWorkerLoadContext()->mRequest = nullptr;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void ScriptLoadRequest::DropBytecodeCacheReferences() {
 | |
|   mCacheInfo = nullptr;
 | |
|   DropJSObjects(this);
 | |
| }
 | |
| 
 | |
| bool ScriptLoadRequest::HasScriptLoadContext() const {
 | |
|   return HasLoadContext() && mLoadContext->IsWindowContext();
 | |
| }
 | |
| 
 | |
| bool ScriptLoadRequest::HasWorkerLoadContext() const {
 | |
|   return HasLoadContext() && mLoadContext->IsWorkerContext();
 | |
| }
 | |
| 
 | |
| mozilla::dom::ScriptLoadContext* ScriptLoadRequest::GetScriptLoadContext() {
 | |
|   MOZ_ASSERT(mLoadContext);
 | |
|   return mLoadContext->AsWindowContext();
 | |
| }
 | |
| 
 | |
| mozilla::loader::ComponentLoadContext*
 | |
| ScriptLoadRequest::GetComponentLoadContext() {
 | |
|   MOZ_ASSERT(mLoadContext);
 | |
|   return mLoadContext->AsComponentContext();
 | |
| }
 | |
| 
 | |
| mozilla::dom::WorkerLoadContext* ScriptLoadRequest::GetWorkerLoadContext() {
 | |
|   MOZ_ASSERT(mLoadContext);
 | |
|   return mLoadContext->AsWorkerContext();
 | |
| }
 | |
| 
 | |
| already_AddRefed<mozilla::dom::WorkerLoadContext>
 | |
| ScriptLoadRequest::StealWorkerLoadContext() {
 | |
|   MOZ_ASSERT(mLoadContext);
 | |
|   RefPtr<mozilla::dom::WorkerLoadContext> workerContext =
 | |
|       mLoadContext->AsWorkerContext();
 | |
|   // Break cycle.
 | |
|   mLoadContext->mRequest = nullptr;
 | |
|   mLoadContext = nullptr;
 | |
|   return workerContext.forget();
 | |
| }
 | |
| 
 | |
| ModuleLoadRequest* ScriptLoadRequest::AsModuleRequest() {
 | |
|   MOZ_ASSERT(IsModuleRequest());
 | |
|   return static_cast<ModuleLoadRequest*>(this);
 | |
| }
 | |
| 
 | |
| const ModuleLoadRequest* ScriptLoadRequest::AsModuleRequest() const {
 | |
|   MOZ_ASSERT(IsModuleRequest());
 | |
|   return static_cast<const ModuleLoadRequest*>(this);
 | |
| }
 | |
| 
 | |
| void ScriptLoadRequest::SetBytecode() {
 | |
|   MOZ_ASSERT(IsUnknownDataType());
 | |
|   mDataType = DataType::eBytecode;
 | |
| }
 | |
| 
 | |
| bool ScriptLoadRequest::IsUTF8ParsingEnabled() {
 | |
|   if (HasLoadContext()) {
 | |
|     if (mLoadContext->IsWindowContext()) {
 | |
|       return mozilla::StaticPrefs::
 | |
|           dom_script_loader_external_scripts_utf8_parsing_enabled();
 | |
|     }
 | |
|     if (mLoadContext->IsWorkerContext()) {
 | |
|       return mozilla::StaticPrefs::
 | |
|           dom_worker_script_loader_utf8_parsing_enabled();
 | |
|     }
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| void ScriptLoadRequest::ClearScriptSource() {
 | |
|   if (IsTextSource()) {
 | |
|     ClearScriptText();
 | |
|   }
 | |
| }
 | |
| 
 | |
| void ScriptLoadRequest::MarkForBytecodeEncoding(JSScript* aScript) {
 | |
|   MOZ_ASSERT(!IsModuleRequest());
 | |
|   MOZ_ASSERT(!IsMarkedForBytecodeEncoding());
 | |
|   mScriptForBytecodeEncoding = aScript;
 | |
|   HoldJSObjects(this);
 | |
| }
 | |
| 
 | |
| bool ScriptLoadRequest::IsMarkedForBytecodeEncoding() const {
 | |
|   if (IsModuleRequest()) {
 | |
|     return AsModuleRequest()->IsModuleMarkedForBytecodeEncoding();
 | |
|   }
 | |
| 
 | |
|   return !!mScriptForBytecodeEncoding;
 | |
| }
 | |
| 
 | |
| nsresult ScriptLoadRequest::GetScriptSource(JSContext* aCx,
 | |
|                                             MaybeSourceText* aMaybeSource) {
 | |
|   // If there's no script text, we try to get it from the element
 | |
|   if (HasScriptLoadContext() && GetScriptLoadContext()->mIsInline) {
 | |
|     nsAutoString inlineData;
 | |
|     GetScriptLoadContext()->GetScriptElement()->GetScriptText(inlineData);
 | |
| 
 | |
|     size_t nbytes = inlineData.Length() * sizeof(char16_t);
 | |
|     JS::UniqueTwoByteChars chars(
 | |
|         static_cast<char16_t*>(JS_malloc(aCx, nbytes)));
 | |
|     if (!chars) {
 | |
|       return NS_ERROR_OUT_OF_MEMORY;
 | |
|     }
 | |
| 
 | |
|     memcpy(chars.get(), inlineData.get(), nbytes);
 | |
| 
 | |
|     SourceText<char16_t> srcBuf;
 | |
|     if (!srcBuf.init(aCx, std::move(chars), inlineData.Length())) {
 | |
|       return NS_ERROR_OUT_OF_MEMORY;
 | |
|     }
 | |
| 
 | |
|     aMaybeSource->construct<SourceText<char16_t>>(std::move(srcBuf));
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   size_t length = ScriptTextLength();
 | |
|   if (IsUTF16Text()) {
 | |
|     JS::UniqueTwoByteChars chars;
 | |
|     chars.reset(ScriptText<char16_t>().extractOrCopyRawBuffer());
 | |
|     if (!chars) {
 | |
|       JS_ReportOutOfMemory(aCx);
 | |
|       return NS_ERROR_OUT_OF_MEMORY;
 | |
|     }
 | |
| 
 | |
|     SourceText<char16_t> srcBuf;
 | |
|     if (!srcBuf.init(aCx, std::move(chars), length)) {
 | |
|       return NS_ERROR_OUT_OF_MEMORY;
 | |
|     }
 | |
| 
 | |
|     aMaybeSource->construct<SourceText<char16_t>>(std::move(srcBuf));
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   MOZ_ASSERT(IsUTF8Text());
 | |
|   UniquePtr<Utf8Unit[], JS::FreePolicy> chars;
 | |
|   chars.reset(ScriptText<Utf8Unit>().extractOrCopyRawBuffer());
 | |
|   if (!chars) {
 | |
|     JS_ReportOutOfMemory(aCx);
 | |
|     return NS_ERROR_OUT_OF_MEMORY;
 | |
|   }
 | |
| 
 | |
|   SourceText<Utf8Unit> srcBuf;
 | |
|   if (!srcBuf.init(aCx, std::move(chars), length)) {
 | |
|     return NS_ERROR_OUT_OF_MEMORY;
 | |
|   }
 | |
| 
 | |
|   aMaybeSource->construct<SourceText<Utf8Unit>>(std::move(srcBuf));
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| //////////////////////////////////////////////////////////////
 | |
| // ScriptLoadRequestList
 | |
| //////////////////////////////////////////////////////////////
 | |
| 
 | |
| ScriptLoadRequestList::~ScriptLoadRequestList() { CancelRequestsAndClear(); }
 | |
| 
 | |
| void ScriptLoadRequestList::CancelRequestsAndClear() {
 | |
|   while (!isEmpty()) {
 | |
|     RefPtr<ScriptLoadRequest> first = StealFirst();
 | |
|     first->Cancel();
 | |
|     // And just let it go out of scope and die.
 | |
|   }
 | |
| }
 | |
| 
 | |
| #ifdef DEBUG
 | |
| bool ScriptLoadRequestList::Contains(ScriptLoadRequest* aElem) const {
 | |
|   for (const ScriptLoadRequest* req = getFirst(); req; req = req->getNext()) {
 | |
|     if (req == aElem) {
 | |
|       return true;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| #endif  // DEBUG
 | |
| 
 | |
| }  // namespace JS::loader
 |