Line data Source code
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/. */
6 :
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.
12 : */
13 :
14 : #include "nsJSUtils.h"
15 : #include "jsapi.h"
16 : #include "jsfriendapi.h"
17 : #include "nsIScriptContext.h"
18 : #include "nsIScriptGlobalObject.h"
19 : #include "nsIXPConnect.h"
20 : #include "nsCOMPtr.h"
21 : #include "nsIScriptSecurityManager.h"
22 : #include "nsPIDOMWindow.h"
23 : #include "GeckoProfiler.h"
24 : #include "nsJSPrincipals.h"
25 : #include "xpcpublic.h"
26 : #include "nsContentUtils.h"
27 : #include "nsGlobalWindow.h"
28 :
29 : #include "mozilla/dom/BindingUtils.h"
30 : #include "mozilla/dom/Date.h"
31 : #include "mozilla/dom/Element.h"
32 : #include "mozilla/dom/ScriptSettings.h"
33 :
34 : using namespace mozilla::dom;
35 :
36 : bool
37 13 : nsJSUtils::GetCallingLocation(JSContext* aContext, nsACString& aFilename,
38 : uint32_t* aLineno, uint32_t* aColumn)
39 : {
40 26 : JS::AutoFilename filename;
41 13 : if (!JS::DescribeScriptedCaller(aContext, &filename, aLineno, aColumn)) {
42 0 : return false;
43 : }
44 :
45 13 : aFilename.Assign(filename.get());
46 13 : return true;
47 : }
48 :
49 : bool
50 0 : nsJSUtils::GetCallingLocation(JSContext* aContext, nsAString& aFilename,
51 : uint32_t* aLineno, uint32_t* aColumn)
52 : {
53 0 : JS::AutoFilename filename;
54 0 : if (!JS::DescribeScriptedCaller(aContext, &filename, aLineno, aColumn)) {
55 0 : return false;
56 : }
57 :
58 0 : aFilename.Assign(NS_ConvertUTF8toUTF16(filename.get()));
59 0 : return true;
60 : }
61 :
62 : nsIScriptGlobalObject *
63 1 : nsJSUtils::GetStaticScriptGlobal(JSObject* aObj)
64 : {
65 1 : if (!aObj)
66 0 : return nullptr;
67 1 : return xpc::WindowGlobalOrNull(aObj);
68 : }
69 :
70 : nsIScriptContext *
71 0 : nsJSUtils::GetStaticScriptContext(JSObject* aObj)
72 : {
73 0 : nsIScriptGlobalObject *nativeGlobal = GetStaticScriptGlobal(aObj);
74 0 : if (!nativeGlobal)
75 0 : return nullptr;
76 :
77 0 : return nativeGlobal->GetScriptContext();
78 : }
79 :
80 : uint64_t
81 0 : nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(JSContext *aContext)
82 : {
83 0 : if (!aContext)
84 0 : return 0;
85 :
86 0 : nsGlobalWindow* win = xpc::CurrentWindowOrNull(aContext);
87 0 : return win ? win->WindowID() : 0;
88 : }
89 :
90 : nsresult
91 11 : nsJSUtils::CompileFunction(AutoJSAPI& jsapi,
92 : JS::AutoObjectVector& aScopeChain,
93 : JS::CompileOptions& aOptions,
94 : const nsACString& aName,
95 : uint32_t aArgCount,
96 : const char** aArgArray,
97 : const nsAString& aBody,
98 : JSObject** aFunctionObject)
99 : {
100 11 : JSContext* cx = jsapi.cx();
101 11 : MOZ_ASSERT(js::GetEnterCompartmentDepth(cx) > 0);
102 11 : MOZ_ASSERT_IF(aScopeChain.length() != 0,
103 : js::IsObjectInContextCompartment(aScopeChain[0], cx));
104 11 : MOZ_ASSERT_IF(aOptions.versionSet, aOptions.version != JSVERSION_UNKNOWN);
105 :
106 : // Do the junk Gecko is supposed to do before calling into JSAPI.
107 38 : for (size_t i = 0; i < aScopeChain.length(); ++i) {
108 27 : JS::ExposeObjectToActiveJS(aScopeChain[i]);
109 : }
110 :
111 : // Compile.
112 22 : JS::Rooted<JSFunction*> fun(cx);
113 55 : if (!JS::CompileFunction(cx, aScopeChain, aOptions,
114 22 : PromiseFlatCString(aName).get(),
115 : aArgCount, aArgArray,
116 22 : PromiseFlatString(aBody).get(),
117 11 : aBody.Length(), &fun))
118 : {
119 0 : return NS_ERROR_FAILURE;
120 : }
121 :
122 11 : *aFunctionObject = JS_GetFunctionObject(fun);
123 11 : return NS_OK;
124 : }
125 :
126 : static nsresult
127 0 : EvaluationExceptionToNSResult(JSContext* aCx)
128 : {
129 0 : if (JS_IsExceptionPending(aCx)) {
130 0 : return NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW;
131 : }
132 0 : return NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW_UNCATCHABLE;
133 : }
134 :
135 117 : nsJSUtils::ExecutionContext::ExecutionContext(JSContext* aCx,
136 117 : JS::Handle<JSObject*> aGlobal)
137 : :
138 : mAutoProfilerLabel("nsJSUtils::ExecutionContext", /* dynamicStr */ nullptr,
139 : __LINE__, js::ProfileEntry::Category::JS),
140 : mCx(aCx)
141 : , mCompartment(aCx, aGlobal)
142 : , mRetValue(aCx)
143 : , mScopeChain(aCx)
144 : , mRv(NS_OK)
145 : , mSkip(false)
146 : , mCoerceToString(false)
147 : , mEncodeBytecode(false)
148 : #ifdef DEBUG
149 : , mWantsReturnValue(false)
150 117 : , mExpectScopeChain(false)
151 : #endif
152 : {
153 117 : MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext());
154 117 : MOZ_ASSERT(NS_IsMainThread());
155 117 : MOZ_ASSERT(nsContentUtils::IsInMicroTask());
156 117 : MOZ_ASSERT(mRetValue.isUndefined());
157 :
158 117 : MOZ_ASSERT(js::GetGlobalForObjectCrossCompartment(aGlobal) == aGlobal);
159 117 : if (MOZ_UNLIKELY(!xpc::Scriptability::Get(aGlobal).Allowed())) {
160 0 : mSkip = true;
161 0 : mRv = NS_OK;
162 : }
163 117 : }
164 :
165 : void
166 112 : nsJSUtils::ExecutionContext::SetScopeChain(
167 : const JS::AutoObjectVector& aScopeChain)
168 : {
169 112 : if (mSkip) {
170 0 : return;
171 : }
172 :
173 : #ifdef DEBUG
174 112 : mExpectScopeChain = true;
175 : #endif
176 : // Now make sure to wrap the scope chain into the right compartment.
177 112 : if (!mScopeChain.reserve(aScopeChain.length())) {
178 0 : mSkip = true;
179 0 : mRv = NS_ERROR_OUT_OF_MEMORY;
180 0 : return;
181 : }
182 :
183 1282 : for (size_t i = 0; i < aScopeChain.length(); ++i) {
184 1170 : JS::ExposeObjectToActiveJS(aScopeChain[i]);
185 1170 : mScopeChain.infallibleAppend(aScopeChain[i]);
186 1170 : if (!JS_WrapObject(mCx, mScopeChain[i])) {
187 0 : mSkip = true;
188 0 : mRv = NS_ERROR_OUT_OF_MEMORY;
189 0 : return;
190 : }
191 : }
192 : }
193 :
194 : nsresult
195 0 : nsJSUtils::ExecutionContext::JoinAndExec(void **aOffThreadToken,
196 : JS::MutableHandle<JSScript*> aScript)
197 : {
198 0 : if (mSkip) {
199 0 : return mRv;
200 : }
201 :
202 0 : MOZ_ASSERT(!mWantsReturnValue);
203 0 : MOZ_ASSERT(!mExpectScopeChain);
204 0 : aScript.set(JS::FinishOffThreadScript(mCx, *aOffThreadToken));
205 0 : *aOffThreadToken = nullptr; // Mark the token as having been finished.
206 0 : if (!aScript) {
207 0 : mSkip = true;
208 0 : mRv = EvaluationExceptionToNSResult(mCx);
209 0 : return mRv;
210 : }
211 :
212 0 : if (mEncodeBytecode && !StartIncrementalEncoding(mCx, aScript)) {
213 0 : mSkip = true;
214 0 : mRv = EvaluationExceptionToNSResult(mCx);
215 0 : return mRv;
216 : }
217 :
218 0 : if (!JS_ExecuteScript(mCx, mScopeChain, aScript)) {
219 0 : mSkip = true;
220 0 : mRv = EvaluationExceptionToNSResult(mCx);
221 0 : return mRv;
222 : }
223 :
224 0 : return NS_OK;
225 : }
226 :
227 : nsresult
228 117 : nsJSUtils::ExecutionContext::CompileAndExec(JS::CompileOptions& aCompileOptions,
229 : JS::SourceBufferHolder& aSrcBuf,
230 : JS::MutableHandle<JSScript*> aScript)
231 : {
232 117 : if (mSkip) {
233 0 : return mRv;
234 : }
235 :
236 117 : MOZ_ASSERT_IF(aCompileOptions.versionSet,
237 : aCompileOptions.version != JSVERSION_UNKNOWN);
238 117 : MOZ_ASSERT(aSrcBuf.get());
239 117 : MOZ_ASSERT(mRetValue.isUndefined());
240 : #ifdef DEBUG
241 117 : mWantsReturnValue = !aCompileOptions.noScriptRval;
242 : #endif
243 :
244 117 : bool compiled = true;
245 117 : if (mScopeChain.length() == 0) {
246 5 : compiled = JS::Compile(mCx, aCompileOptions, aSrcBuf, aScript);
247 : } else {
248 112 : compiled = JS::CompileForNonSyntacticScope(mCx, aCompileOptions, aSrcBuf, aScript);
249 : }
250 :
251 117 : MOZ_ASSERT_IF(compiled, aScript);
252 117 : if (!compiled) {
253 0 : mSkip = true;
254 0 : mRv = EvaluationExceptionToNSResult(mCx);
255 0 : return mRv;
256 : }
257 :
258 117 : if (mEncodeBytecode && !StartIncrementalEncoding(mCx, aScript)) {
259 0 : mSkip = true;
260 0 : mRv = EvaluationExceptionToNSResult(mCx);
261 0 : return mRv;
262 : }
263 :
264 117 : MOZ_ASSERT(!mCoerceToString || mWantsReturnValue);
265 117 : if (!JS_ExecuteScript(mCx, mScopeChain, aScript, &mRetValue)) {
266 0 : mSkip = true;
267 0 : mRv = EvaluationExceptionToNSResult(mCx);
268 0 : return mRv;
269 : }
270 :
271 117 : return NS_OK;
272 : }
273 :
274 : nsresult
275 112 : nsJSUtils::ExecutionContext::CompileAndExec(JS::CompileOptions& aCompileOptions,
276 : const nsAString& aScript)
277 : {
278 112 : MOZ_ASSERT(!mEncodeBytecode, "A JSScript is needed for calling FinishIncrementalEncoding");
279 112 : if (mSkip) {
280 0 : return mRv;
281 : }
282 :
283 224 : const nsPromiseFlatString& flatScript = PromiseFlatString(aScript);
284 112 : JS::SourceBufferHolder srcBuf(flatScript.get(), aScript.Length(),
285 224 : JS::SourceBufferHolder::NoOwnership);
286 224 : JS::Rooted<JSScript*> script(mCx);
287 112 : return CompileAndExec(aCompileOptions, srcBuf, &script);
288 : }
289 :
290 : nsresult
291 0 : nsJSUtils::ExecutionContext::DecodeAndExec(JS::CompileOptions& aCompileOptions,
292 : mozilla::Vector<uint8_t>& aBytecodeBuf,
293 : size_t aBytecodeIndex)
294 : {
295 0 : MOZ_ASSERT(!mEncodeBytecode, "A JSScript is needed for calling FinishIncrementalEncoding");
296 0 : if (mSkip) {
297 0 : return mRv;
298 : }
299 :
300 0 : MOZ_ASSERT(!mWantsReturnValue);
301 0 : JS::Rooted<JSScript*> script(mCx);
302 0 : JS::TranscodeResult tr = JS::DecodeScript(mCx, aBytecodeBuf, &script, aBytecodeIndex);
303 : // These errors are external parameters which should be handled before the
304 : // decoding phase, and which are the only reasons why you might want to
305 : // fallback on decoding failures.
306 0 : MOZ_ASSERT(tr != JS::TranscodeResult_Failure_BadBuildId &&
307 : tr != JS::TranscodeResult_Failure_WrongCompileOption);
308 0 : if (tr != JS::TranscodeResult_Ok) {
309 0 : mSkip = true;
310 0 : mRv = NS_ERROR_DOM_JS_DECODING_ERROR;
311 0 : return mRv;
312 : }
313 :
314 0 : if (!JS_ExecuteScript(mCx, mScopeChain, script)) {
315 0 : mSkip = true;
316 0 : mRv = EvaluationExceptionToNSResult(mCx);
317 0 : return mRv;
318 : }
319 :
320 0 : return mRv;
321 : }
322 :
323 : nsresult
324 0 : nsJSUtils::ExecutionContext::DecodeJoinAndExec(void **aOffThreadToken)
325 : {
326 0 : if (mSkip) {
327 0 : return mRv;
328 : }
329 :
330 0 : MOZ_ASSERT(!mWantsReturnValue);
331 0 : MOZ_ASSERT(!mExpectScopeChain);
332 0 : JS::Rooted<JSScript*> script(mCx);
333 0 : script.set(JS::FinishOffThreadScriptDecoder(mCx, *aOffThreadToken));
334 0 : *aOffThreadToken = nullptr; // Mark the token as having been finished.
335 0 : if (!script || !JS_ExecuteScript(mCx, mScopeChain, script)) {
336 0 : mSkip = true;
337 0 : mRv = EvaluationExceptionToNSResult(mCx);
338 0 : return mRv;
339 : }
340 :
341 0 : return NS_OK;
342 : }
343 :
344 : nsresult
345 112 : nsJSUtils::ExecutionContext::ExtractReturnValue(JS::MutableHandle<JS::Value> aRetValue)
346 : {
347 112 : MOZ_ASSERT(aRetValue.isUndefined());
348 112 : if (mSkip) {
349 : // Repeat earlier result, as NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW are not
350 : // failures cases.
351 : #ifdef DEBUG
352 0 : mWantsReturnValue = false;
353 : #endif
354 0 : return mRv;
355 : }
356 :
357 112 : MOZ_ASSERT(mWantsReturnValue);
358 : #ifdef DEBUG
359 112 : mWantsReturnValue = false;
360 : #endif
361 112 : if (mCoerceToString && !mRetValue.isUndefined()) {
362 0 : JSString* str = JS::ToString(mCx, mRetValue);
363 0 : if (!str) {
364 : // ToString can be a function call, so an exception can be raised while
365 : // executing the function.
366 0 : mSkip = true;
367 0 : return EvaluationExceptionToNSResult(mCx);
368 : }
369 0 : mRetValue.set(JS::StringValue(str));
370 : }
371 :
372 112 : aRetValue.set(mRetValue);
373 112 : return NS_OK;
374 : }
375 :
376 : nsresult
377 0 : nsJSUtils::CompileModule(JSContext* aCx,
378 : JS::SourceBufferHolder& aSrcBuf,
379 : JS::Handle<JSObject*> aEvaluationGlobal,
380 : JS::CompileOptions &aCompileOptions,
381 : JS::MutableHandle<JSObject*> aModule)
382 : {
383 0 : AUTO_PROFILER_LABEL("nsJSUtils::CompileModule", JS);
384 :
385 0 : MOZ_ASSERT_IF(aCompileOptions.versionSet,
386 : aCompileOptions.version != JSVERSION_UNKNOWN);
387 0 : MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext());
388 0 : MOZ_ASSERT(aSrcBuf.get());
389 0 : MOZ_ASSERT(js::GetGlobalForObjectCrossCompartment(aEvaluationGlobal) ==
390 : aEvaluationGlobal);
391 0 : MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx) == aEvaluationGlobal);
392 0 : MOZ_ASSERT(NS_IsMainThread());
393 0 : MOZ_ASSERT(nsContentUtils::IsInMicroTask());
394 :
395 0 : NS_ENSURE_TRUE(xpc::Scriptability::Get(aEvaluationGlobal).Allowed(), NS_OK);
396 :
397 0 : if (!JS::CompileModule(aCx, aCompileOptions, aSrcBuf, aModule)) {
398 0 : return NS_ERROR_FAILURE;
399 : }
400 :
401 0 : return NS_OK;
402 : }
403 :
404 : nsresult
405 0 : nsJSUtils::ModuleDeclarationInstantiation(JSContext* aCx, JS::Handle<JSObject*> aModule)
406 : {
407 0 : AUTO_PROFILER_LABEL("nsJSUtils::ModuleDeclarationInstantiation", JS);
408 :
409 0 : MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext());
410 0 : MOZ_ASSERT(NS_IsMainThread());
411 :
412 0 : NS_ENSURE_TRUE(xpc::Scriptability::Get(aModule).Allowed(), NS_OK);
413 :
414 0 : if (!JS::ModuleDeclarationInstantiation(aCx, aModule)) {
415 0 : return NS_ERROR_FAILURE;
416 : }
417 :
418 0 : return NS_OK;
419 : }
420 :
421 : nsresult
422 0 : nsJSUtils::ModuleEvaluation(JSContext* aCx, JS::Handle<JSObject*> aModule)
423 : {
424 0 : AUTO_PROFILER_LABEL("nsJSUtils::ModuleEvaluation", JS);
425 :
426 0 : MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext());
427 0 : MOZ_ASSERT(NS_IsMainThread());
428 0 : MOZ_ASSERT(nsContentUtils::IsInMicroTask());
429 :
430 0 : NS_ENSURE_TRUE(xpc::Scriptability::Get(aModule).Allowed(), NS_OK);
431 :
432 0 : if (!JS::ModuleEvaluation(aCx, aModule)) {
433 0 : return NS_ERROR_FAILURE;
434 : }
435 :
436 0 : return NS_OK;
437 : }
438 :
439 : /* static */
440 : bool
441 173 : nsJSUtils::GetScopeChainForElement(JSContext* aCx,
442 : mozilla::dom::Element* aElement,
443 : JS::AutoObjectVector& aScopeChain)
444 : {
445 1746 : for (nsINode* cur = aElement; cur; cur = cur->GetScopeChainParent()) {
446 3146 : JS::RootedValue val(aCx);
447 1573 : if (!GetOrCreateDOMReflector(aCx, cur, &val)) {
448 0 : return false;
449 : }
450 :
451 1573 : if (!aScopeChain.append(&val.toObject())) {
452 0 : return false;
453 : }
454 : }
455 :
456 173 : return true;
457 : }
458 :
459 : /* static */
460 : void
461 3 : nsJSUtils::ResetTimeZone()
462 : {
463 3 : JS::ResetTimeZone();
464 3 : }
465 :
466 : //
467 : // nsDOMJSUtils.h
468 : //
469 :
470 0 : bool nsAutoJSString::init(const JS::Value &v)
471 : {
472 : // Note: it's okay to use danger::GetJSContext here instead of AutoJSAPI,
473 : // because the init() call below is careful not to run script (for instance,
474 : // it only calls JS::ToString for non-object values).
475 0 : JSContext* cx = danger::GetJSContext();
476 0 : if (!init(cx, v)) {
477 0 : JS_ClearPendingException(cx);
478 0 : return false;
479 : }
480 :
481 0 : return true;
482 : }
483 :
|