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
GetCallingLocation(JSContext * aContext,nsACString & aFilename,uint32_t * aLineno,uint32_t * aColumn)37 nsJSUtils::GetCallingLocation(JSContext* aContext, nsACString& aFilename,
38 uint32_t* aLineno, uint32_t* aColumn)
39 {
40 JS::AutoFilename filename;
41 if (!JS::DescribeScriptedCaller(aContext, &filename, aLineno, aColumn)) {
42 return false;
43 }
44
45 aFilename.Assign(filename.get());
46 return true;
47 }
48
49 bool
GetCallingLocation(JSContext * aContext,nsAString & aFilename,uint32_t * aLineno,uint32_t * aColumn)50 nsJSUtils::GetCallingLocation(JSContext* aContext, nsAString& aFilename,
51 uint32_t* aLineno, uint32_t* aColumn)
52 {
53 JS::AutoFilename filename;
54 if (!JS::DescribeScriptedCaller(aContext, &filename, aLineno, aColumn)) {
55 return false;
56 }
57
58 aFilename.Assign(NS_ConvertUTF8toUTF16(filename.get()));
59 return true;
60 }
61
62 nsIScriptGlobalObject *
GetStaticScriptGlobal(JSObject * aObj)63 nsJSUtils::GetStaticScriptGlobal(JSObject* aObj)
64 {
65 if (!aObj)
66 return nullptr;
67 return xpc::WindowGlobalOrNull(aObj);
68 }
69
70 nsIScriptContext *
GetStaticScriptContext(JSObject * aObj)71 nsJSUtils::GetStaticScriptContext(JSObject* aObj)
72 {
73 nsIScriptGlobalObject *nativeGlobal = GetStaticScriptGlobal(aObj);
74 if (!nativeGlobal)
75 return nullptr;
76
77 return nativeGlobal->GetScriptContext();
78 }
79
80 uint64_t
GetCurrentlyRunningCodeInnerWindowID(JSContext * aContext)81 nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(JSContext *aContext)
82 {
83 if (!aContext)
84 return 0;
85
86 nsGlobalWindow* win = xpc::CurrentWindowOrNull(aContext);
87 return win ? win->WindowID() : 0;
88 }
89
90 nsresult
CompileFunction(AutoJSAPI & jsapi,JS::AutoObjectVector & aScopeChain,JS::CompileOptions & aOptions,const nsACString & aName,uint32_t aArgCount,const char ** aArgArray,const nsAString & aBody,JSObject ** aFunctionObject)91 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 JSContext* cx = jsapi.cx();
101 MOZ_ASSERT(js::GetEnterCompartmentDepth(cx) > 0);
102 MOZ_ASSERT_IF(aScopeChain.length() != 0,
103 js::IsObjectInContextCompartment(aScopeChain[0], cx));
104 MOZ_ASSERT_IF(aOptions.versionSet, aOptions.version != JSVERSION_UNKNOWN);
105
106 // Do the junk Gecko is supposed to do before calling into JSAPI.
107 for (size_t i = 0; i < aScopeChain.length(); ++i) {
108 JS::ExposeObjectToActiveJS(aScopeChain[i]);
109 }
110
111 // Compile.
112 JS::Rooted<JSFunction*> fun(cx);
113 if (!JS::CompileFunction(cx, aScopeChain, aOptions,
114 PromiseFlatCString(aName).get(),
115 aArgCount, aArgArray,
116 PromiseFlatString(aBody).get(),
117 aBody.Length(), &fun))
118 {
119 return NS_ERROR_FAILURE;
120 }
121
122 *aFunctionObject = JS_GetFunctionObject(fun);
123 return NS_OK;
124 }
125
126 nsresult
EvaluateString(JSContext * aCx,const nsAString & aScript,JS::Handle<JSObject * > aEvaluationGlobal,JS::CompileOptions & aCompileOptions,const EvaluateOptions & aEvaluateOptions,JS::MutableHandle<JS::Value> aRetValue)127 nsJSUtils::EvaluateString(JSContext* aCx,
128 const nsAString& aScript,
129 JS::Handle<JSObject*> aEvaluationGlobal,
130 JS::CompileOptions& aCompileOptions,
131 const EvaluateOptions& aEvaluateOptions,
132 JS::MutableHandle<JS::Value> aRetValue)
133 {
134 const nsPromiseFlatString& flatScript = PromiseFlatString(aScript);
135 JS::SourceBufferHolder srcBuf(flatScript.get(), aScript.Length(),
136 JS::SourceBufferHolder::NoOwnership);
137 return EvaluateString(aCx, srcBuf, aEvaluationGlobal, aCompileOptions,
138 aEvaluateOptions, aRetValue, nullptr);
139 }
140
141 nsresult
EvaluateString(JSContext * aCx,JS::SourceBufferHolder & aSrcBuf,JS::Handle<JSObject * > aEvaluationGlobal,JS::CompileOptions & aCompileOptions,const EvaluateOptions & aEvaluateOptions,JS::MutableHandle<JS::Value> aRetValue,void ** aOffThreadToken)142 nsJSUtils::EvaluateString(JSContext* aCx,
143 JS::SourceBufferHolder& aSrcBuf,
144 JS::Handle<JSObject*> aEvaluationGlobal,
145 JS::CompileOptions& aCompileOptions,
146 const EvaluateOptions& aEvaluateOptions,
147 JS::MutableHandle<JS::Value> aRetValue,
148 void **aOffThreadToken)
149 {
150 PROFILER_LABEL("nsJSUtils", "EvaluateString",
151 js::ProfileEntry::Category::JS);
152
153 MOZ_ASSERT_IF(aCompileOptions.versionSet,
154 aCompileOptions.version != JSVERSION_UNKNOWN);
155 MOZ_ASSERT_IF(aEvaluateOptions.coerceToString, !aCompileOptions.noScriptRval);
156 MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext());
157 MOZ_ASSERT(aSrcBuf.get());
158 MOZ_ASSERT(js::GetGlobalForObjectCrossCompartment(aEvaluationGlobal) ==
159 aEvaluationGlobal);
160 MOZ_ASSERT_IF(aOffThreadToken, aCompileOptions.noScriptRval);
161 MOZ_ASSERT(NS_IsMainThread());
162 MOZ_ASSERT(nsContentUtils::IsInMicroTask());
163
164 // Unfortunately, the JS engine actually compiles scripts with a return value
165 // in a different, less efficient way. Furthermore, it can't JIT them in many
166 // cases. So we need to be explicitly told whether the caller cares about the
167 // return value. Callers can do this by calling the other overload of
168 // EvaluateString() which calls this function with
169 // aCompileOptions.noScriptRval set to true.
170 aRetValue.setUndefined();
171
172 nsresult rv = NS_OK;
173
174 NS_ENSURE_TRUE(xpc::Scriptability::Get(aEvaluationGlobal).Allowed(), NS_OK);
175
176 bool ok = true;
177 // Scope the JSAutoCompartment so that we can later wrap the return value
178 // into the caller's cx.
179 {
180 JSAutoCompartment ac(aCx, aEvaluationGlobal);
181
182 // Now make sure to wrap the scope chain into the right compartment.
183 JS::AutoObjectVector scopeChain(aCx);
184 if (!scopeChain.reserve(aEvaluateOptions.scopeChain.length())) {
185 return NS_ERROR_OUT_OF_MEMORY;
186 }
187
188 for (size_t i = 0; i < aEvaluateOptions.scopeChain.length(); ++i) {
189 JS::ExposeObjectToActiveJS(aEvaluateOptions.scopeChain[i]);
190 scopeChain.infallibleAppend(aEvaluateOptions.scopeChain[i]);
191 if (!JS_WrapObject(aCx, scopeChain[i])) {
192 ok = false;
193 break;
194 }
195 }
196
197 if (ok && aOffThreadToken) {
198 JS::Rooted<JSScript*>
199 script(aCx, JS::FinishOffThreadScript(aCx, *aOffThreadToken));
200 *aOffThreadToken = nullptr; // Mark the token as having been finished.
201 if (script) {
202 ok = JS_ExecuteScript(aCx, scopeChain, script);
203 } else {
204 ok = false;
205 }
206 } else if (ok) {
207 ok = JS::Evaluate(aCx, scopeChain, aCompileOptions, aSrcBuf, aRetValue);
208 }
209
210 if (ok && aEvaluateOptions.coerceToString && !aRetValue.isUndefined()) {
211 JS::Rooted<JS::Value> value(aCx, aRetValue);
212 JSString* str = JS::ToString(aCx, value);
213 ok = !!str;
214 aRetValue.set(ok ? JS::StringValue(str) : JS::UndefinedValue());
215 }
216 }
217
218 if (!ok) {
219 if (JS_IsExceptionPending(aCx)) {
220 rv = NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW;
221 } else {
222 rv = NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW_UNCATCHABLE;
223 }
224
225 if (!aCompileOptions.noScriptRval) {
226 aRetValue.setUndefined();
227 }
228 }
229
230 // Wrap the return value into whatever compartment aCx was in.
231 if (ok && !aCompileOptions.noScriptRval) {
232 if (!JS_WrapValue(aCx, aRetValue)) {
233 return NS_ERROR_OUT_OF_MEMORY;
234 }
235 }
236 return rv;
237 }
238
239 nsresult
EvaluateString(JSContext * aCx,JS::SourceBufferHolder & aSrcBuf,JS::Handle<JSObject * > aEvaluationGlobal,JS::CompileOptions & aCompileOptions,const EvaluateOptions & aEvaluateOptions,JS::MutableHandle<JS::Value> aRetValue)240 nsJSUtils::EvaluateString(JSContext* aCx,
241 JS::SourceBufferHolder& aSrcBuf,
242 JS::Handle<JSObject*> aEvaluationGlobal,
243 JS::CompileOptions& aCompileOptions,
244 const EvaluateOptions& aEvaluateOptions,
245 JS::MutableHandle<JS::Value> aRetValue)
246 {
247 return EvaluateString(aCx, aSrcBuf, aEvaluationGlobal, aCompileOptions,
248 aEvaluateOptions, aRetValue, nullptr);
249 }
250
251 nsresult
EvaluateString(JSContext * aCx,const nsAString & aScript,JS::Handle<JSObject * > aEvaluationGlobal,JS::CompileOptions & aCompileOptions)252 nsJSUtils::EvaluateString(JSContext* aCx,
253 const nsAString& aScript,
254 JS::Handle<JSObject*> aEvaluationGlobal,
255 JS::CompileOptions& aCompileOptions)
256 {
257 EvaluateOptions options(aCx);
258 aCompileOptions.setNoScriptRval(true);
259 JS::RootedValue unused(aCx);
260 return EvaluateString(aCx, aScript, aEvaluationGlobal, aCompileOptions,
261 options, &unused);
262 }
263
264 nsresult
EvaluateString(JSContext * aCx,JS::SourceBufferHolder & aSrcBuf,JS::Handle<JSObject * > aEvaluationGlobal,JS::CompileOptions & aCompileOptions,void ** aOffThreadToken)265 nsJSUtils::EvaluateString(JSContext* aCx,
266 JS::SourceBufferHolder& aSrcBuf,
267 JS::Handle<JSObject*> aEvaluationGlobal,
268 JS::CompileOptions& aCompileOptions,
269 void **aOffThreadToken)
270 {
271 EvaluateOptions options(aCx);
272 aCompileOptions.setNoScriptRval(true);
273 JS::RootedValue unused(aCx);
274 return EvaluateString(aCx, aSrcBuf, aEvaluationGlobal, aCompileOptions,
275 options, &unused, aOffThreadToken);
276 }
277
278 nsresult
CompileModule(JSContext * aCx,JS::SourceBufferHolder & aSrcBuf,JS::Handle<JSObject * > aEvaluationGlobal,JS::CompileOptions & aCompileOptions,JS::MutableHandle<JSObject * > aModule)279 nsJSUtils::CompileModule(JSContext* aCx,
280 JS::SourceBufferHolder& aSrcBuf,
281 JS::Handle<JSObject*> aEvaluationGlobal,
282 JS::CompileOptions &aCompileOptions,
283 JS::MutableHandle<JSObject*> aModule)
284 {
285 PROFILER_LABEL("nsJSUtils", "CompileModule",
286 js::ProfileEntry::Category::JS);
287
288 MOZ_ASSERT_IF(aCompileOptions.versionSet,
289 aCompileOptions.version != JSVERSION_UNKNOWN);
290 MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext());
291 MOZ_ASSERT(aSrcBuf.get());
292 MOZ_ASSERT(js::GetGlobalForObjectCrossCompartment(aEvaluationGlobal) ==
293 aEvaluationGlobal);
294 MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx) == aEvaluationGlobal);
295 MOZ_ASSERT(NS_IsMainThread());
296 MOZ_ASSERT(nsContentUtils::IsInMicroTask());
297
298 NS_ENSURE_TRUE(xpc::Scriptability::Get(aEvaluationGlobal).Allowed(), NS_OK);
299
300 if (!JS::CompileModule(aCx, aCompileOptions, aSrcBuf, aModule)) {
301 return NS_ERROR_FAILURE;
302 }
303
304 return NS_OK;
305 }
306
307 nsresult
ModuleDeclarationInstantiation(JSContext * aCx,JS::Handle<JSObject * > aModule)308 nsJSUtils::ModuleDeclarationInstantiation(JSContext* aCx, JS::Handle<JSObject*> aModule)
309 {
310 PROFILER_LABEL("nsJSUtils", "ModuleDeclarationInstantiation",
311 js::ProfileEntry::Category::JS);
312
313 MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext());
314 MOZ_ASSERT(NS_IsMainThread());
315
316 NS_ENSURE_TRUE(xpc::Scriptability::Get(aModule).Allowed(), NS_OK);
317
318 if (!JS::ModuleDeclarationInstantiation(aCx, aModule)) {
319 return NS_ERROR_FAILURE;
320 }
321
322 return NS_OK;
323 }
324
325 nsresult
ModuleEvaluation(JSContext * aCx,JS::Handle<JSObject * > aModule)326 nsJSUtils::ModuleEvaluation(JSContext* aCx, JS::Handle<JSObject*> aModule)
327 {
328 PROFILER_LABEL("nsJSUtils", "ModuleEvaluation",
329 js::ProfileEntry::Category::JS);
330
331 MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext());
332 MOZ_ASSERT(NS_IsMainThread());
333 MOZ_ASSERT(nsContentUtils::IsInMicroTask());
334
335 NS_ENSURE_TRUE(xpc::Scriptability::Get(aModule).Allowed(), NS_OK);
336
337 if (!JS::ModuleEvaluation(aCx, aModule)) {
338 return NS_ERROR_FAILURE;
339 }
340
341 return NS_OK;
342 }
343
344 /* static */
345 bool
GetScopeChainForElement(JSContext * aCx,mozilla::dom::Element * aElement,JS::AutoObjectVector & aScopeChain)346 nsJSUtils::GetScopeChainForElement(JSContext* aCx,
347 mozilla::dom::Element* aElement,
348 JS::AutoObjectVector& aScopeChain)
349 {
350 for (nsINode* cur = aElement; cur; cur = cur->GetScopeChainParent()) {
351 JS::RootedValue val(aCx);
352 if (!GetOrCreateDOMReflector(aCx, cur, &val)) {
353 return false;
354 }
355
356 if (!aScopeChain.append(&val.toObject())) {
357 return false;
358 }
359 }
360
361 return true;
362 }
363
364 /* static */
365 void
ResetTimeZone()366 nsJSUtils::ResetTimeZone()
367 {
368 JS::ResetTimeZone();
369 }
370
371 //
372 // nsDOMJSUtils.h
373 //
374
init(const JS::Value & v)375 bool nsAutoJSString::init(const JS::Value &v)
376 {
377 // Note: it's okay to use danger::GetJSContext here instead of AutoJSAPI,
378 // because the init() call below is careful not to run script (for instance,
379 // it only calls JS::ToString for non-object values).
380 JSContext* cx = danger::GetJSContext();
381 if (!init(cx, v)) {
382 JS_ClearPendingException(cx);
383 return false;
384 }
385
386 return true;
387 }
388
389