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/. */
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 "nsJSUtils.h"
16 #include "jsfriendapi.h"
17 #include "js/CompilationAndEvaluation.h"
18 #include "js/Modules.h" // JS::CompileModule, JS::GetModuleScript, JS::Module{Instantiate,Evaluate}
19 #include "js/OffThreadScriptCompilation.h"
20 #include "js/SourceText.h"
21 #include "nsIScriptContext.h"
22 #include "nsIScriptElement.h"
23 #include "nsIScriptGlobalObject.h"
24 #include "nsIXPConnect.h"
26 #include "nsIScriptSecurityManager.h"
27 #include "nsPIDOMWindow.h"
28 #include "GeckoProfiler.h"
29 #include "nsJSPrincipals.h"
30 #include "xpcpublic.h"
31 #include "nsContentUtils.h"
32 #include "nsGlobalWindow.h"
33 #include "nsXBLPrototypeBinding.h"
34 #include "mozilla/CycleCollectedJSContext.h"
35 #include "mozilla/dom/BindingUtils.h"
36 #include "mozilla/dom/Date.h"
37 #include "mozilla/dom/Element.h"
38 #include "mozilla/dom/ScriptSettings.h"
40 using namespace mozilla
;
41 using namespace mozilla::dom
;
43 bool nsJSUtils::GetCallingLocation(JSContext
* aContext
, nsACString
& aFilename
,
44 uint32_t* aLineno
, uint32_t* aColumn
) {
45 JS::AutoFilename filename
;
46 if (!JS::DescribeScriptedCaller(aContext
, &filename
, aLineno
, aColumn
)) {
50 aFilename
.Assign(filename
.get());
54 bool nsJSUtils::GetCallingLocation(JSContext
* aContext
, nsAString
& aFilename
,
55 uint32_t* aLineno
, uint32_t* aColumn
) {
56 JS::AutoFilename filename
;
57 if (!JS::DescribeScriptedCaller(aContext
, &filename
, aLineno
, aColumn
)) {
61 aFilename
.Assign(NS_ConvertUTF8toUTF16(filename
.get()));
65 uint64_t nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(JSContext
* aContext
) {
66 if (!aContext
) return 0;
68 nsGlobalWindowInner
* win
= xpc::CurrentWindowOrNull(aContext
);
69 return win
? win
->WindowID() : 0;
72 nsresult
nsJSUtils::CompileFunction(AutoJSAPI
& jsapi
,
73 JS::HandleVector
<JSObject
*> aScopeChain
,
74 JS::CompileOptions
& aOptions
,
75 const nsACString
& aName
, uint32_t aArgCount
,
76 const char** aArgArray
,
77 const nsAString
& aBody
,
78 JSObject
** aFunctionObject
) {
79 JSContext
* cx
= jsapi
.cx();
80 MOZ_ASSERT(js::GetContextRealm(cx
));
81 MOZ_ASSERT_IF(aScopeChain
.length() != 0,
82 js::IsObjectInContextCompartment(aScopeChain
[0], cx
));
84 // Do the junk Gecko is supposed to do before calling into JSAPI.
85 for (size_t i
= 0; i
< aScopeChain
.length(); ++i
) {
86 JS::ExposeObjectToActiveJS(aScopeChain
[i
]);
90 const nsPromiseFlatString
& flatBody
= PromiseFlatString(aBody
);
92 JS::SourceText
<char16_t
> source
;
93 if (!source
.init(cx
, flatBody
.get(), flatBody
.Length(),
94 JS::SourceOwnership::Borrowed
)) {
95 return NS_ERROR_FAILURE
;
98 JS::Rooted
<JSFunction
*> fun(
99 cx
, JS::CompileFunction(cx
, aScopeChain
, aOptions
,
100 PromiseFlatCString(aName
).get(), aArgCount
,
103 return NS_ERROR_FAILURE
;
106 *aFunctionObject
= JS_GetFunctionObject(fun
);
110 static nsresult
EvaluationExceptionToNSResult(JSContext
* aCx
) {
111 if (JS_IsExceptionPending(aCx
)) {
112 return NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW
;
114 return NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW_UNCATCHABLE
;
117 nsJSUtils::ExecutionContext::ExecutionContext(JSContext
* aCx
,
118 JS::Handle
<JSObject
*> aGlobal
)
120 #ifdef MOZ_GECKO_PROFILER
121 mAutoProfilerLabel("nsJSUtils::ExecutionContext",
122 /* dynamicStr */ nullptr,
123 JS::ProfilingCategoryPair::JS
),
126 mRealm(aCx
, aGlobal
),
132 mCoerceToString(false),
133 mEncodeBytecode(false)
136 mWantsReturnValue(false),
137 mExpectScopeChain(false),
141 MOZ_ASSERT(aCx
== nsContentUtils::GetCurrentJSContext());
142 MOZ_ASSERT(NS_IsMainThread());
143 MOZ_ASSERT(CycleCollectedJSContext::Get() &&
144 CycleCollectedJSContext::Get()->MicroTaskLevel());
145 MOZ_ASSERT(mRetValue
.isUndefined());
147 MOZ_ASSERT(JS_IsGlobalObject(aGlobal
));
148 if (MOZ_UNLIKELY(!xpc::Scriptability::Get(aGlobal
).Allowed())) {
154 void nsJSUtils::ExecutionContext::SetScopeChain(
155 JS::HandleVector
<JSObject
*> aScopeChain
) {
161 mExpectScopeChain
= true;
163 // Now make sure to wrap the scope chain into the right compartment.
164 if (!mScopeChain
.reserve(aScopeChain
.length())) {
166 mRv
= NS_ERROR_OUT_OF_MEMORY
;
170 for (size_t i
= 0; i
< aScopeChain
.length(); ++i
) {
171 JS::ExposeObjectToActiveJS(aScopeChain
[i
]);
172 mScopeChain
.infallibleAppend(aScopeChain
[i
]);
173 if (!JS_WrapObject(mCx
, mScopeChain
[i
])) {
175 mRv
= NS_ERROR_OUT_OF_MEMORY
;
181 nsresult
nsJSUtils::ExecutionContext::JoinCompile(
182 JS::OffThreadToken
** aOffThreadToken
) {
187 MOZ_ASSERT(!mWantsReturnValue
);
188 MOZ_ASSERT(!mExpectScopeChain
);
189 MOZ_ASSERT(!mScript
);
190 mScript
.set(JS::FinishOffThreadScript(mCx
, *aOffThreadToken
));
191 *aOffThreadToken
= nullptr; // Mark the token as having been finished.
194 mRv
= EvaluationExceptionToNSResult(mCx
);
198 if (mEncodeBytecode
&& !StartIncrementalEncoding(mCx
, mScript
)) {
200 mRv
= EvaluationExceptionToNSResult(mCx
);
207 nsresult
nsJSUtils::ExecutionContext::Compile(
208 JS::CompileOptions
& aCompileOptions
, JS::SourceText
<char16_t
>& aSrcBuf
) {
213 MOZ_ASSERT(aSrcBuf
.get());
214 MOZ_ASSERT(mRetValue
.isUndefined());
216 mWantsReturnValue
= !aCompileOptions
.noScriptRval
;
219 MOZ_ASSERT(!mScript
);
221 mScopeChain
.length() == 0
222 ? JS::Compile(mCx
, aCompileOptions
, aSrcBuf
)
223 : JS::CompileForNonSyntacticScope(mCx
, aCompileOptions
, aSrcBuf
);
227 mRv
= EvaluationExceptionToNSResult(mCx
);
231 if (mEncodeBytecode
&& !StartIncrementalEncoding(mCx
, mScript
)) {
233 mRv
= EvaluationExceptionToNSResult(mCx
);
240 nsresult
nsJSUtils::ExecutionContext::Compile(
241 JS::CompileOptions
& aCompileOptions
, const nsAString
& aScript
) {
246 const nsPromiseFlatString
& flatScript
= PromiseFlatString(aScript
);
247 JS::SourceText
<char16_t
> srcBuf
;
248 if (!srcBuf
.init(mCx
, flatScript
.get(), flatScript
.Length(),
249 JS::SourceOwnership::Borrowed
)) {
251 mRv
= EvaluationExceptionToNSResult(mCx
);
255 return Compile(aCompileOptions
, srcBuf
);
258 nsresult
nsJSUtils::ExecutionContext::Decode(
259 JS::CompileOptions
& aCompileOptions
, mozilla::Vector
<uint8_t>& aBytecodeBuf
,
260 size_t aBytecodeIndex
) {
265 MOZ_ASSERT(!mWantsReturnValue
);
266 JS::TranscodeResult tr
=
267 JS::DecodeScript(mCx
, aBytecodeBuf
, &mScript
, aBytecodeIndex
);
268 // These errors are external parameters which should be handled before the
269 // decoding phase, and which are the only reasons why you might want to
270 // fallback on decoding failures.
271 MOZ_ASSERT(tr
!= JS::TranscodeResult_Failure_BadBuildId
&&
272 tr
!= JS::TranscodeResult_Failure_WrongCompileOption
);
273 if (tr
!= JS::TranscodeResult_Ok
) {
275 mRv
= NS_ERROR_DOM_JS_DECODING_ERROR
;
282 nsresult
nsJSUtils::ExecutionContext::JoinDecode(
283 JS::OffThreadToken
** aOffThreadToken
) {
288 MOZ_ASSERT(!mWantsReturnValue
);
289 MOZ_ASSERT(!mExpectScopeChain
);
290 mScript
.set(JS::FinishOffThreadScriptDecoder(mCx
, *aOffThreadToken
));
291 *aOffThreadToken
= nullptr; // Mark the token as having been finished.
294 mRv
= EvaluationExceptionToNSResult(mCx
);
301 nsresult
nsJSUtils::ExecutionContext::JoinDecodeBinAST(
302 JS::OffThreadToken
** aOffThreadToken
) {
303 #ifdef JS_BUILD_BINAST
308 MOZ_ASSERT(!mWantsReturnValue
);
309 MOZ_ASSERT(!mExpectScopeChain
);
311 mScript
.set(JS::FinishOffThreadBinASTDecode(mCx
, *aOffThreadToken
));
312 *aOffThreadToken
= nullptr; // Mark the token as having been finished.
316 mRv
= EvaluationExceptionToNSResult(mCx
);
320 if (mEncodeBytecode
&& !StartIncrementalEncoding(mCx
, mScript
)) {
322 mRv
= EvaluationExceptionToNSResult(mCx
);
328 return NS_ERROR_NOT_IMPLEMENTED
;
332 nsresult
nsJSUtils::ExecutionContext::DecodeBinAST(
333 JS::CompileOptions
& aCompileOptions
, const uint8_t* aBuf
, size_t aLength
) {
334 #ifdef JS_BUILD_BINAST
335 MOZ_ASSERT(mScopeChain
.length() == 0,
336 "BinAST decoding is not supported in non-syntactic scopes");
343 MOZ_ASSERT(mRetValue
.isUndefined());
345 mWantsReturnValue
= !aCompileOptions
.noScriptRval
;
348 mScript
.set(JS::DecodeBinAST(mCx
, aCompileOptions
, aBuf
, aLength
));
352 mRv
= EvaluationExceptionToNSResult(mCx
);
356 if (mEncodeBytecode
&& !StartIncrementalEncoding(mCx
, mScript
)) {
358 mRv
= EvaluationExceptionToNSResult(mCx
);
364 return NS_ERROR_NOT_IMPLEMENTED
;
368 JSScript
* nsJSUtils::ExecutionContext::GetScript() {
375 return MaybeGetScript();
378 JSScript
* nsJSUtils::ExecutionContext::MaybeGetScript() { return mScript
; }
380 nsresult
nsJSUtils::ExecutionContext::ExecScript() {
387 if (!JS_ExecuteScript(mCx
, mScopeChain
, mScript
)) {
389 mRv
= EvaluationExceptionToNSResult(mCx
);
396 static bool IsPromiseValue(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
) {
397 if (!aValue
.isObject()) {
401 // We only care about Promise here, so CheckedUnwrapStatic is fine.
402 JS::Rooted
<JSObject
*> obj(aCx
, js::CheckedUnwrapStatic(&aValue
.toObject()));
407 return JS::IsPromiseObject(obj
);
410 nsresult
nsJSUtils::ExecutionContext::ExecScript(
411 JS::MutableHandle
<JS::Value
> aRetValue
) {
413 aRetValue
.setUndefined();
418 MOZ_ASSERT(mWantsReturnValue
);
420 if (!JS_ExecuteScript(mCx
, mScopeChain
, mScript
, aRetValue
)) {
422 mRv
= EvaluationExceptionToNSResult(mCx
);
427 mWantsReturnValue
= false;
429 if (mCoerceToString
&& IsPromiseValue(mCx
, aRetValue
)) {
430 // We're a javascript: url and we should treat Promise return values as
433 // Once bug 1477821 is fixed this code might be able to go away, or will
434 // become enshrined in the spec, depending.
435 aRetValue
.setUndefined();
438 if (mCoerceToString
&& !aRetValue
.isUndefined()) {
439 JSString
* str
= JS::ToString(mCx
, aRetValue
);
441 // ToString can be a function call, so an exception can be raised while
442 // executing the function.
444 return EvaluationExceptionToNSResult(mCx
);
446 aRetValue
.set(JS::StringValue(str
));
452 nsresult
nsJSUtils::CompileModule(JSContext
* aCx
,
453 JS::SourceText
<char16_t
>& aSrcBuf
,
454 JS::Handle
<JSObject
*> aEvaluationGlobal
,
455 JS::CompileOptions
& aCompileOptions
,
456 JS::MutableHandle
<JSObject
*> aModule
) {
457 AUTO_PROFILER_LABEL("nsJSUtils::CompileModule", JS
);
458 MOZ_ASSERT(aCx
== nsContentUtils::GetCurrentJSContext());
459 MOZ_ASSERT(aSrcBuf
.get());
460 MOZ_ASSERT(JS_IsGlobalObject(aEvaluationGlobal
));
461 MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx
) == aEvaluationGlobal
);
462 MOZ_ASSERT(NS_IsMainThread());
463 MOZ_ASSERT(CycleCollectedJSContext::Get() &&
464 CycleCollectedJSContext::Get()->MicroTaskLevel());
466 NS_ENSURE_TRUE(xpc::Scriptability::Get(aEvaluationGlobal
).Allowed(), NS_OK
);
468 JSObject
* module
= JS::CompileModule(aCx
, aCompileOptions
, aSrcBuf
);
470 return NS_ERROR_FAILURE
;
477 nsresult
nsJSUtils::InitModuleSourceElement(JSContext
* aCx
,
478 JS::Handle
<JSObject
*> aModule
,
479 nsIScriptElement
* aElement
) {
480 JS::Rooted
<JS::Value
> value(aCx
);
481 nsresult rv
= nsContentUtils::WrapNative(aCx
, aElement
, &value
,
482 /* aAllowWrapping = */ true);
487 MOZ_ASSERT(value
.isObject());
488 JS::Rooted
<JSObject
*> object(aCx
, &value
.toObject());
490 JS::Rooted
<JSScript
*> script(aCx
, JS::GetModuleScript(aModule
));
491 if (!JS::InitScriptSourceElement(aCx
, script
, object
, nullptr)) {
492 return NS_ERROR_FAILURE
;
498 nsresult
nsJSUtils::ModuleInstantiate(JSContext
* aCx
,
499 JS::Handle
<JSObject
*> aModule
) {
500 AUTO_PROFILER_LABEL("nsJSUtils::ModuleInstantiate", JS
);
502 MOZ_ASSERT(aCx
== nsContentUtils::GetCurrentJSContext());
503 MOZ_ASSERT(NS_IsMainThread());
504 MOZ_ASSERT(CycleCollectedJSContext::Get() &&
505 CycleCollectedJSContext::Get()->MicroTaskLevel());
507 NS_ENSURE_TRUE(xpc::Scriptability::Get(aModule
).Allowed(), NS_OK
);
509 if (!JS::ModuleInstantiate(aCx
, aModule
)) {
510 return NS_ERROR_FAILURE
;
516 nsresult
nsJSUtils::ModuleEvaluate(JSContext
* aCx
,
517 JS::Handle
<JSObject
*> aModule
) {
518 AUTO_PROFILER_LABEL("nsJSUtils::ModuleEvaluate", JS
);
520 MOZ_ASSERT(aCx
== nsContentUtils::GetCurrentJSContext());
521 MOZ_ASSERT(NS_IsMainThread());
522 MOZ_ASSERT(CycleCollectedJSContext::Get() &&
523 CycleCollectedJSContext::Get()->MicroTaskLevel());
525 NS_ENSURE_TRUE(xpc::Scriptability::Get(aModule
).Allowed(), NS_OK
);
527 if (!JS::ModuleEvaluate(aCx
, aModule
)) {
528 return NS_ERROR_FAILURE
;
534 static bool AddScopeChainItem(JSContext
* aCx
, nsINode
* aNode
,
535 JS::MutableHandleVector
<JSObject
*> aScopeChain
) {
536 JS::RootedValue
val(aCx
);
537 if (!GetOrCreateDOMReflector(aCx
, aNode
, &val
)) {
541 if (!aScopeChain
.append(&val
.toObject())) {
549 bool nsJSUtils::GetScopeChainForElement(
550 JSContext
* aCx
, Element
* aElement
,
551 JS::MutableHandleVector
<JSObject
*> aScopeChain
) {
552 for (nsINode
* cur
= aElement
; cur
; cur
= cur
->GetScopeChainParent()) {
553 if (!AddScopeChainItem(aCx
, cur
, aScopeChain
)) {
562 bool nsJSUtils::GetScopeChainForXBL(
563 JSContext
* aCx
, Element
* aElement
,
564 const nsXBLPrototypeBinding
& aProtoBinding
,
565 JS::MutableHandleVector
<JSObject
*> aScopeChain
) {
570 if (!aProtoBinding
.SimpleScopeChain()) {
571 return GetScopeChainForElement(aCx
, aElement
, aScopeChain
);
574 if (!AddScopeChainItem(aCx
, aElement
, aScopeChain
)) {
578 if (!AddScopeChainItem(aCx
, aElement
->OwnerDoc(), aScopeChain
)) {
585 void nsJSUtils::ResetTimeZone() { JS::ResetTimeZone(); }
591 bool nsAutoJSString::init(const JS::Value
& v
) {
592 // Note: it's okay to use danger::GetJSContext here instead of AutoJSAPI,
593 // because the init() call below is careful not to run script (for instance,
594 // it only calls JS::ToString for non-object values).
595 JSContext
* cx
= danger::GetJSContext();
597 JS_ClearPendingException(cx
);