Bug 1850713: remove duplicated setting of early hint preloader id in `ScriptLoader...
[gecko.git] / dom / base / JSExecutionContext.cpp
blobcd355127410ffa23bf1394808d1c91a279bab40f
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 /**
8 * This is not a generated file. It contains common utility functions
9 * invoked from the JavaScript code generated from IDL interfaces.
10 * The goal of the utility functions is to cut down on the size of
11 * the generated code itself.
14 #include "mozilla/dom/JSExecutionContext.h"
16 #include <utility>
17 #include "ErrorList.h"
18 #include "MainThreadUtils.h"
19 #include "js/CompilationAndEvaluation.h"
20 #include "js/CompileOptions.h"
21 #include "js/Conversions.h"
22 #include "js/experimental/JSStencil.h"
23 #include "js/HeapAPI.h"
24 #include "js/ProfilingCategory.h"
25 #include "js/Promise.h"
26 #include "js/SourceText.h"
27 #include "js/Transcoding.h"
28 #include "js/Value.h"
29 #include "js/Wrapper.h"
30 #include "jsapi.h"
31 #include "mozilla/CycleCollectedJSContext.h"
32 #include "mozilla/dom/ScriptLoadContext.h"
33 #include "mozilla/Likely.h"
34 #include "nsContentUtils.h"
35 #include "nsTPromiseFlatString.h"
36 #include "xpcpublic.h"
38 #if !defined(DEBUG) && !defined(MOZ_ENABLE_JS_DUMP)
39 # include "mozilla/StaticPrefs_browser.h"
40 #endif
42 using namespace mozilla;
43 using namespace mozilla::dom;
45 static nsresult EvaluationExceptionToNSResult(JSContext* aCx) {
46 if (JS_IsExceptionPending(aCx)) {
47 return NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW;
49 return NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW_UNCATCHABLE;
52 JSExecutionContext::JSExecutionContext(
53 JSContext* aCx, JS::Handle<JSObject*> aGlobal,
54 JS::CompileOptions& aCompileOptions,
55 JS::Handle<JS::Value> aDebuggerPrivateValue,
56 JS::Handle<JSScript*> aDebuggerIntroductionScript)
57 : mAutoProfilerLabel("JSExecutionContext",
58 /* dynamicStr */ nullptr,
59 JS::ProfilingCategoryPair::JS),
60 mCx(aCx),
61 mRealm(aCx, aGlobal),
62 mRetValue(aCx),
63 mScript(aCx),
64 mCompileOptions(aCompileOptions),
65 mDebuggerPrivateValue(aCx, aDebuggerPrivateValue),
66 mDebuggerIntroductionScript(aCx, aDebuggerIntroductionScript),
67 mRv(NS_OK),
68 mSkip(false),
69 mCoerceToString(false),
70 mEncodeBytecode(false)
71 #ifdef DEBUG
73 mWantsReturnValue(false),
74 mScriptUsed(false)
75 #endif
77 MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext());
78 MOZ_ASSERT(NS_IsMainThread());
79 MOZ_ASSERT(CycleCollectedJSContext::Get() &&
80 CycleCollectedJSContext::Get()->MicroTaskLevel());
81 MOZ_ASSERT(mRetValue.isUndefined());
83 MOZ_ASSERT(JS_IsGlobalObject(aGlobal));
84 if (MOZ_UNLIKELY(!xpc::Scriptability::Get(aGlobal).Allowed())) {
85 mSkip = true;
86 mRv = NS_OK;
90 nsresult JSExecutionContext::JoinOffThread(ScriptLoadContext* aContext) {
91 if (mSkip) {
92 return mRv;
95 MOZ_ASSERT(!mWantsReturnValue);
97 JS::InstantiationStorage storage;
98 RefPtr<JS::Stencil> stencil = aContext->StealOffThreadResult(mCx, &storage);
99 if (!stencil) {
100 mSkip = true;
101 mRv = EvaluationExceptionToNSResult(mCx);
102 return mRv;
105 return InstantiateStencil(std::move(stencil), &storage);
108 template <typename Unit>
109 nsresult JSExecutionContext::InternalCompile(JS::SourceText<Unit>& aSrcBuf) {
110 if (mSkip) {
111 return mRv;
114 MOZ_ASSERT(aSrcBuf.get());
115 MOZ_ASSERT(mRetValue.isUndefined());
116 #ifdef DEBUG
117 mWantsReturnValue = !mCompileOptions.noScriptRval;
118 #endif
120 RefPtr<JS::Stencil> stencil =
121 CompileGlobalScriptToStencil(mCx, mCompileOptions, aSrcBuf);
122 if (!stencil) {
123 mSkip = true;
124 mRv = EvaluationExceptionToNSResult(mCx);
125 return mRv;
128 return InstantiateStencil(std::move(stencil));
131 nsresult JSExecutionContext::Compile(JS::SourceText<char16_t>& aSrcBuf) {
132 return InternalCompile(aSrcBuf);
135 nsresult JSExecutionContext::Compile(JS::SourceText<Utf8Unit>& aSrcBuf) {
136 return InternalCompile(aSrcBuf);
139 nsresult JSExecutionContext::Compile(const nsAString& aScript) {
140 if (mSkip) {
141 return mRv;
144 const nsPromiseFlatString& flatScript = PromiseFlatString(aScript);
145 JS::SourceText<char16_t> srcBuf;
146 if (!srcBuf.init(mCx, flatScript.get(), flatScript.Length(),
147 JS::SourceOwnership::Borrowed)) {
148 mSkip = true;
149 mRv = EvaluationExceptionToNSResult(mCx);
150 return mRv;
153 return Compile(srcBuf);
156 nsresult JSExecutionContext::Decode(mozilla::Vector<uint8_t>& aBytecodeBuf,
157 size_t aBytecodeIndex) {
158 if (mSkip) {
159 return mRv;
162 JS::DecodeOptions decodeOptions(mCompileOptions);
163 decodeOptions.borrowBuffer = true;
165 JS::TranscodeRange range(aBytecodeBuf.begin() + aBytecodeIndex,
166 aBytecodeBuf.length() - aBytecodeIndex);
168 MOZ_ASSERT(!mWantsReturnValue);
169 RefPtr<JS::Stencil> stencil;
170 JS::TranscodeResult tr =
171 JS::DecodeStencil(mCx, decodeOptions, range, getter_AddRefs(stencil));
172 // These errors are external parameters which should be handled before the
173 // decoding phase, and which are the only reasons why you might want to
174 // fallback on decoding failures.
175 MOZ_ASSERT(tr != JS::TranscodeResult::Failure_BadBuildId);
176 if (tr != JS::TranscodeResult::Ok) {
177 mSkip = true;
178 mRv = NS_ERROR_DOM_JS_DECODING_ERROR;
179 return mRv;
182 return InstantiateStencil(std::move(stencil));
185 nsresult JSExecutionContext::InstantiateStencil(
186 RefPtr<JS::Stencil>&& aStencil, JS::InstantiationStorage* aStorage) {
187 JS::InstantiateOptions instantiateOptions(mCompileOptions);
188 JS::Rooted<JSScript*> script(
189 mCx, JS::InstantiateGlobalStencil(mCx, instantiateOptions, aStencil,
190 aStorage));
191 if (!script) {
192 mSkip = true;
193 mRv = EvaluationExceptionToNSResult(mCx);
194 return mRv;
197 if (mEncodeBytecode) {
198 if (!JS::StartIncrementalEncoding(mCx, std::move(aStencil))) {
199 mSkip = true;
200 mRv = EvaluationExceptionToNSResult(mCx);
201 return mRv;
205 MOZ_ASSERT(!mScript);
206 mScript.set(script);
208 if (instantiateOptions.deferDebugMetadata) {
209 if (!JS::UpdateDebugMetadata(mCx, mScript, instantiateOptions,
210 mDebuggerPrivateValue, nullptr,
211 mDebuggerIntroductionScript, nullptr)) {
212 return NS_ERROR_OUT_OF_MEMORY;
216 return NS_OK;
219 JSScript* JSExecutionContext::GetScript() {
220 #ifdef DEBUG
221 MOZ_ASSERT(!mSkip);
222 MOZ_ASSERT(mScript);
223 mScriptUsed = true;
224 #endif
226 return MaybeGetScript();
229 JSScript* JSExecutionContext::MaybeGetScript() { return mScript; }
231 nsresult JSExecutionContext::ExecScript() {
232 if (mSkip) {
233 return mRv;
236 MOZ_ASSERT(mScript);
238 if (!JS_ExecuteScript(mCx, mScript)) {
239 mSkip = true;
240 mRv = EvaluationExceptionToNSResult(mCx);
241 return mRv;
244 return NS_OK;
247 static bool IsPromiseValue(JSContext* aCx, JS::Handle<JS::Value> aValue) {
248 if (!aValue.isObject()) {
249 return false;
252 // We only care about Promise here, so CheckedUnwrapStatic is fine.
253 JS::Rooted<JSObject*> obj(aCx, js::CheckedUnwrapStatic(&aValue.toObject()));
254 if (!obj) {
255 return false;
258 return JS::IsPromiseObject(obj);
261 nsresult JSExecutionContext::ExecScript(
262 JS::MutableHandle<JS::Value> aRetValue) {
263 if (mSkip) {
264 aRetValue.setUndefined();
265 return mRv;
268 MOZ_ASSERT(mScript);
269 MOZ_ASSERT(mWantsReturnValue);
271 if (!JS_ExecuteScript(mCx, mScript, aRetValue)) {
272 mSkip = true;
273 mRv = EvaluationExceptionToNSResult(mCx);
274 return mRv;
277 #ifdef DEBUG
278 mWantsReturnValue = false;
279 #endif
280 if (mCoerceToString && IsPromiseValue(mCx, aRetValue)) {
281 // We're a javascript: url and we should treat Promise return values as
282 // undefined.
284 // Once bug 1477821 is fixed this code might be able to go away, or will
285 // become enshrined in the spec, depending.
286 aRetValue.setUndefined();
289 if (mCoerceToString && !aRetValue.isUndefined()) {
290 JSString* str = JS::ToString(mCx, aRetValue);
291 if (!str) {
292 // ToString can be a function call, so an exception can be raised while
293 // executing the function.
294 mSkip = true;
295 return EvaluationExceptionToNSResult(mCx);
297 aRetValue.set(JS::StringValue(str));
300 return NS_OK;