Bug 1550519 - Show a translucent parent highlight when a subgrid is highlighted....
[gecko.git] / dom / base / nsJSUtils.cpp
blobf627f9659912febd9e7547a7780bb02a65eaba4a
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 "nsJSUtils.h"
15 #include "jsapi.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"
25 #include "nsCOMPtr.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)) {
47 return false;
50 aFilename.Assign(filename.get());
51 return true;
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)) {
58 return false;
61 aFilename.Assign(NS_ConvertUTF8toUTF16(filename.get()));
62 return true;
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]);
89 // Compile.
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,
101 aArgArray, source));
102 if (!fun) {
103 return NS_ERROR_FAILURE;
106 *aFunctionObject = JS_GetFunctionObject(fun);
107 return NS_OK;
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),
124 #endif
125 mCx(aCx),
126 mRealm(aCx, aGlobal),
127 mRetValue(aCx),
128 mScopeChain(aCx),
129 mScript(aCx),
130 mRv(NS_OK),
131 mSkip(false),
132 mCoerceToString(false),
133 mEncodeBytecode(false)
134 #ifdef DEBUG
136 mWantsReturnValue(false),
137 mExpectScopeChain(false),
138 mScriptUsed(false)
139 #endif
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())) {
149 mSkip = true;
150 mRv = NS_OK;
154 void nsJSUtils::ExecutionContext::SetScopeChain(
155 JS::HandleVector<JSObject*> aScopeChain) {
156 if (mSkip) {
157 return;
160 #ifdef DEBUG
161 mExpectScopeChain = true;
162 #endif
163 // Now make sure to wrap the scope chain into the right compartment.
164 if (!mScopeChain.reserve(aScopeChain.length())) {
165 mSkip = true;
166 mRv = NS_ERROR_OUT_OF_MEMORY;
167 return;
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])) {
174 mSkip = true;
175 mRv = NS_ERROR_OUT_OF_MEMORY;
176 return;
181 nsresult nsJSUtils::ExecutionContext::JoinCompile(
182 JS::OffThreadToken** aOffThreadToken) {
183 if (mSkip) {
184 return mRv;
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.
192 if (!mScript) {
193 mSkip = true;
194 mRv = EvaluationExceptionToNSResult(mCx);
195 return mRv;
198 if (mEncodeBytecode && !StartIncrementalEncoding(mCx, mScript)) {
199 mSkip = true;
200 mRv = EvaluationExceptionToNSResult(mCx);
201 return mRv;
204 return NS_OK;
207 nsresult nsJSUtils::ExecutionContext::Compile(
208 JS::CompileOptions& aCompileOptions, JS::SourceText<char16_t>& aSrcBuf) {
209 if (mSkip) {
210 return mRv;
213 MOZ_ASSERT(aSrcBuf.get());
214 MOZ_ASSERT(mRetValue.isUndefined());
215 #ifdef DEBUG
216 mWantsReturnValue = !aCompileOptions.noScriptRval;
217 #endif
219 MOZ_ASSERT(!mScript);
220 mScript =
221 mScopeChain.length() == 0
222 ? JS::Compile(mCx, aCompileOptions, aSrcBuf)
223 : JS::CompileForNonSyntacticScope(mCx, aCompileOptions, aSrcBuf);
225 if (!mScript) {
226 mSkip = true;
227 mRv = EvaluationExceptionToNSResult(mCx);
228 return mRv;
231 if (mEncodeBytecode && !StartIncrementalEncoding(mCx, mScript)) {
232 mSkip = true;
233 mRv = EvaluationExceptionToNSResult(mCx);
234 return mRv;
237 return NS_OK;
240 nsresult nsJSUtils::ExecutionContext::Compile(
241 JS::CompileOptions& aCompileOptions, const nsAString& aScript) {
242 if (mSkip) {
243 return mRv;
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)) {
250 mSkip = true;
251 mRv = EvaluationExceptionToNSResult(mCx);
252 return mRv;
255 return Compile(aCompileOptions, srcBuf);
258 nsresult nsJSUtils::ExecutionContext::Decode(
259 JS::CompileOptions& aCompileOptions, mozilla::Vector<uint8_t>& aBytecodeBuf,
260 size_t aBytecodeIndex) {
261 if (mSkip) {
262 return mRv;
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) {
274 mSkip = true;
275 mRv = NS_ERROR_DOM_JS_DECODING_ERROR;
276 return mRv;
279 return mRv;
282 nsresult nsJSUtils::ExecutionContext::JoinDecode(
283 JS::OffThreadToken** aOffThreadToken) {
284 if (mSkip) {
285 return mRv;
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.
292 if (!mScript) {
293 mSkip = true;
294 mRv = EvaluationExceptionToNSResult(mCx);
295 return mRv;
298 return NS_OK;
301 nsresult nsJSUtils::ExecutionContext::JoinDecodeBinAST(
302 JS::OffThreadToken** aOffThreadToken) {
303 #ifdef JS_BUILD_BINAST
304 if (mSkip) {
305 return mRv;
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.
314 if (!mScript) {
315 mSkip = true;
316 mRv = EvaluationExceptionToNSResult(mCx);
317 return mRv;
320 if (mEncodeBytecode && !StartIncrementalEncoding(mCx, mScript)) {
321 mSkip = true;
322 mRv = EvaluationExceptionToNSResult(mCx);
323 return mRv;
326 return NS_OK;
327 #else
328 return NS_ERROR_NOT_IMPLEMENTED;
329 #endif
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");
338 if (mSkip) {
339 return mRv;
342 MOZ_ASSERT(aBuf);
343 MOZ_ASSERT(mRetValue.isUndefined());
344 # ifdef DEBUG
345 mWantsReturnValue = !aCompileOptions.noScriptRval;
346 # endif
348 mScript.set(JS::DecodeBinAST(mCx, aCompileOptions, aBuf, aLength));
350 if (!mScript) {
351 mSkip = true;
352 mRv = EvaluationExceptionToNSResult(mCx);
353 return mRv;
356 if (mEncodeBytecode && !StartIncrementalEncoding(mCx, mScript)) {
357 mSkip = true;
358 mRv = EvaluationExceptionToNSResult(mCx);
359 return mRv;
362 return NS_OK;
363 #else
364 return NS_ERROR_NOT_IMPLEMENTED;
365 #endif
368 JSScript* nsJSUtils::ExecutionContext::GetScript() {
369 #ifdef DEBUG
370 MOZ_ASSERT(!mSkip);
371 MOZ_ASSERT(mScript);
372 mScriptUsed = true;
373 #endif
375 return MaybeGetScript();
378 JSScript* nsJSUtils::ExecutionContext::MaybeGetScript() { return mScript; }
380 nsresult nsJSUtils::ExecutionContext::ExecScript() {
381 if (mSkip) {
382 return mRv;
385 MOZ_ASSERT(mScript);
387 if (!JS_ExecuteScript(mCx, mScopeChain, mScript)) {
388 mSkip = true;
389 mRv = EvaluationExceptionToNSResult(mCx);
390 return mRv;
393 return NS_OK;
396 static bool IsPromiseValue(JSContext* aCx, JS::Handle<JS::Value> aValue) {
397 if (!aValue.isObject()) {
398 return false;
401 // We only care about Promise here, so CheckedUnwrapStatic is fine.
402 JS::Rooted<JSObject*> obj(aCx, js::CheckedUnwrapStatic(&aValue.toObject()));
403 if (!obj) {
404 return false;
407 return JS::IsPromiseObject(obj);
410 nsresult nsJSUtils::ExecutionContext::ExecScript(
411 JS::MutableHandle<JS::Value> aRetValue) {
412 if (mSkip) {
413 aRetValue.setUndefined();
414 return mRv;
417 MOZ_ASSERT(mScript);
418 MOZ_ASSERT(mWantsReturnValue);
420 if (!JS_ExecuteScript(mCx, mScopeChain, mScript, aRetValue)) {
421 mSkip = true;
422 mRv = EvaluationExceptionToNSResult(mCx);
423 return mRv;
426 #ifdef DEBUG
427 mWantsReturnValue = false;
428 #endif
429 if (mCoerceToString && IsPromiseValue(mCx, aRetValue)) {
430 // We're a javascript: url and we should treat Promise return values as
431 // undefined.
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);
440 if (!str) {
441 // ToString can be a function call, so an exception can be raised while
442 // executing the function.
443 mSkip = true;
444 return EvaluationExceptionToNSResult(mCx);
446 aRetValue.set(JS::StringValue(str));
449 return NS_OK;
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);
469 if (!module) {
470 return NS_ERROR_FAILURE;
473 aModule.set(module);
474 return NS_OK;
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);
483 if (NS_FAILED(rv)) {
484 return rv;
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;
495 return NS_OK;
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;
513 return NS_OK;
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;
531 return NS_OK;
534 static bool AddScopeChainItem(JSContext* aCx, nsINode* aNode,
535 JS::MutableHandleVector<JSObject*> aScopeChain) {
536 JS::RootedValue val(aCx);
537 if (!GetOrCreateDOMReflector(aCx, aNode, &val)) {
538 return false;
541 if (!aScopeChain.append(&val.toObject())) {
542 return false;
545 return true;
548 /* static */
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)) {
554 return false;
558 return true;
561 /* static */
562 bool nsJSUtils::GetScopeChainForXBL(
563 JSContext* aCx, Element* aElement,
564 const nsXBLPrototypeBinding& aProtoBinding,
565 JS::MutableHandleVector<JSObject*> aScopeChain) {
566 if (!aElement) {
567 return true;
570 if (!aProtoBinding.SimpleScopeChain()) {
571 return GetScopeChainForElement(aCx, aElement, aScopeChain);
574 if (!AddScopeChainItem(aCx, aElement, aScopeChain)) {
575 return false;
578 if (!AddScopeChainItem(aCx, aElement->OwnerDoc(), aScopeChain)) {
579 return false;
581 return true;
584 /* static */
585 void nsJSUtils::ResetTimeZone() { JS::ResetTimeZone(); }
588 // nsDOMJSUtils.h
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();
596 if (!init(cx, v)) {
597 JS_ClearPendingException(cx);
598 return false;
601 return true;