1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 sw=2 et tw=78: */
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 "js/OldDebugAPI.h"
17 #include "jsfriendapi.h"
18 #include "nsIScriptContext.h"
19 #include "nsIScriptGlobalObject.h"
20 #include "nsIXPConnect.h"
22 #include "nsIScriptSecurityManager.h"
23 #include "nsPIDOMWindow.h"
24 #include "GeckoProfiler.h"
25 #include "nsDOMJSUtils.h" // for GetScriptContextFromJSContext
26 #include "nsJSPrincipals.h"
27 #include "xpcpublic.h"
28 #include "nsContentUtils.h"
29 #include "nsGlobalWindow.h"
32 nsJSUtils::GetCallingLocation(JSContext
* aContext
, const char* *aFilename
,
35 JS::AutoFilename filename
;
38 if (!JS::DescribeScriptedCaller(aContext
, &filename
, &lineno
)) {
42 *aFilename
= filename
.get();
48 nsIScriptGlobalObject
*
49 nsJSUtils::GetStaticScriptGlobal(JSObject
* aObj
)
53 return xpc::WindowGlobalOrNull(aObj
);
57 nsJSUtils::GetStaticScriptContext(JSObject
* aObj
)
59 nsIScriptGlobalObject
*nativeGlobal
= GetStaticScriptGlobal(aObj
);
63 return nativeGlobal
->GetScriptContext();
66 nsIScriptGlobalObject
*
67 nsJSUtils::GetDynamicScriptGlobal(JSContext
* aContext
)
69 nsIScriptContext
*scriptCX
= GetDynamicScriptContext(aContext
);
72 return scriptCX
->GetGlobalObject();
76 nsJSUtils::GetDynamicScriptContext(JSContext
*aContext
)
78 return GetScriptContextFromJSContext(aContext
);
82 nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(JSContext
*aContext
)
87 uint64_t innerWindowID
= 0;
89 JSObject
*jsGlobal
= JS::CurrentGlobalOrNull(aContext
);
91 nsIScriptGlobalObject
*scriptGlobal
= GetStaticScriptGlobal(jsGlobal
);
93 nsCOMPtr
<nsPIDOMWindow
> win
= do_QueryInterface(scriptGlobal
);
95 innerWindowID
= win
->WindowID();
103 nsJSUtils::ReportPendingException(JSContext
*aContext
)
105 if (JS_IsExceptionPending(aContext
)) {
106 bool saved
= JS_SaveFrameChain(aContext
);
108 // JS_SaveFrameChain set the compartment of aContext to null, so we need
109 // to enter a compartment. The question is, which one? We don't want to
110 // enter the original compartment of aContext (or the compartment of the
111 // current exception on aContext, for that matter) because when we
112 // JS_ReportPendingException the JS engine can try to duck-type the
113 // exception and produce a JSErrorReport. It will then pass that
114 // JSErrorReport to the error reporter on aContext, which might expose
115 // information from it to script via onerror handlers. So it's very
116 // important that the duck typing happen in the same compartment as the
117 // onerror handler. In practice, that's the compartment of the window (or
118 // otherwise default global) of aContext, so use that here.
119 nsIScriptContext
* scx
= GetScriptContextFromJSContext(aContext
);
120 JS::Rooted
<JSObject
*> scope(aContext
);
121 scope
= scx
? scx
->GetWindowProxy()
122 : js::DefaultObjectForContextOrNull(aContext
);
124 // The SafeJSContext has no default object associated with it.
125 MOZ_ASSERT(NS_IsMainThread());
126 MOZ_ASSERT(aContext
== nsContentUtils::GetSafeJSContext());
127 scope
= xpc::GetSafeJSContextGlobal();
129 JSAutoCompartment
ac(aContext
, scope
);
130 JS_ReportPendingException(aContext
);
133 JS_RestoreFrameChain(aContext
);
139 nsJSUtils::CompileFunction(JSContext
* aCx
,
140 JS::Handle
<JSObject
*> aTarget
,
141 JS::CompileOptions
& aOptions
,
142 const nsACString
& aName
,
144 const char** aArgArray
,
145 const nsAString
& aBody
,
146 JSObject
** aFunctionObject
)
148 MOZ_ASSERT(js::GetEnterCompartmentDepth(aCx
) > 0);
149 MOZ_ASSERT_IF(aTarget
, js::IsObjectInContextCompartment(aTarget
, aCx
));
150 MOZ_ASSERT_IF(aOptions
.versionSet
, aOptions
.version
!= JSVERSION_UNKNOWN
);
151 mozilla::DebugOnly
<nsIScriptContext
*> ctx
= GetScriptContextFromJSContext(aCx
);
152 MOZ_ASSERT_IF(ctx
, ctx
->IsContextInitialized());
154 // Do the junk Gecko is supposed to do before calling into JSAPI.
156 JS::ExposeObjectToActiveJS(aTarget
);
160 JSFunction
* fun
= JS::CompileFunction(aCx
, aTarget
, aOptions
,
161 PromiseFlatCString(aName
).get(),
162 aArgCount
, aArgArray
,
163 PromiseFlatString(aBody
).get(),
166 ReportPendingException(aCx
);
167 return NS_ERROR_FAILURE
;
170 *aFunctionObject
= JS_GetFunctionObject(fun
);
175 nsJSUtils::EvaluateString(JSContext
* aCx
,
176 const nsAString
& aScript
,
177 JS::Handle
<JSObject
*> aScopeObject
,
178 JS::CompileOptions
& aCompileOptions
,
179 const EvaluateOptions
& aEvaluateOptions
,
180 JS::MutableHandle
<JS::Value
> aRetValue
,
181 void **aOffThreadToken
)
183 const nsPromiseFlatString
& flatScript
= PromiseFlatString(aScript
);
184 JS::SourceBufferHolder
srcBuf(flatScript
.get(), aScript
.Length(),
185 JS::SourceBufferHolder::NoOwnership
);
186 return EvaluateString(aCx
, srcBuf
, aScopeObject
, aCompileOptions
,
187 aEvaluateOptions
, aRetValue
, aOffThreadToken
);
191 nsJSUtils::EvaluateString(JSContext
* aCx
,
192 JS::SourceBufferHolder
& aSrcBuf
,
193 JS::Handle
<JSObject
*> aScopeObject
,
194 JS::CompileOptions
& aCompileOptions
,
195 const EvaluateOptions
& aEvaluateOptions
,
196 JS::MutableHandle
<JS::Value
> aRetValue
,
197 void **aOffThreadToken
)
199 PROFILER_LABEL("nsJSUtils", "EvaluateString",
200 js::ProfileEntry::Category::JS
);
202 MOZ_ASSERT_IF(aCompileOptions
.versionSet
,
203 aCompileOptions
.version
!= JSVERSION_UNKNOWN
);
204 MOZ_ASSERT_IF(aEvaluateOptions
.coerceToString
, aEvaluateOptions
.needResult
);
205 MOZ_ASSERT_IF(!aEvaluateOptions
.reportUncaught
, aEvaluateOptions
.needResult
);
206 MOZ_ASSERT(aCx
== nsContentUtils::GetCurrentJSContext());
207 MOZ_ASSERT(aSrcBuf
.get());
209 // Unfortunately, the JS engine actually compiles scripts with a return value
210 // in a different, less efficient way. Furthermore, it can't JIT them in many
211 // cases. So we need to be explicitly told whether the caller cares about the
212 // return value. Callers can do this by calling the other overload of
213 // EvaluateString() which calls this function with aEvaluateOptions.needResult
215 aRetValue
.setUndefined();
217 JS::ExposeObjectToActiveJS(aScopeObject
);
222 nsIScriptSecurityManager
* ssm
= nsContentUtils::GetSecurityManager();
223 NS_ENSURE_TRUE(ssm
->ScriptAllowed(js::GetGlobalForObjectCrossCompartment(aScopeObject
)), NS_OK
);
225 mozilla::Maybe
<AutoDontReportUncaught
> dontReport
;
226 if (!aEvaluateOptions
.reportUncaught
) {
227 // We need to prevent AutoLastFrameCheck from reporting and clearing
228 // any pending exceptions.
229 dontReport
.construct(aCx
);
232 // Scope the JSAutoCompartment so that we can later wrap the return value
233 // into the caller's cx.
235 JSAutoCompartment
ac(aCx
, aScopeObject
);
237 JS::Rooted
<JSObject
*> rootedScope(aCx
, aScopeObject
);
238 if (aOffThreadToken
) {
239 JS::Rooted
<JSScript
*>
240 script(aCx
, JS::FinishOffThreadScript(aCx
, JS_GetRuntime(aCx
), *aOffThreadToken
));
241 *aOffThreadToken
= nullptr; // Mark the token as having been finished.
243 if (aEvaluateOptions
.needResult
) {
244 ok
= JS_ExecuteScript(aCx
, rootedScope
, script
, aRetValue
);
246 ok
= JS_ExecuteScript(aCx
, rootedScope
, script
);
252 if (aEvaluateOptions
.needResult
) {
253 ok
= JS::Evaluate(aCx
, rootedScope
, aCompileOptions
,
256 ok
= JS::Evaluate(aCx
, rootedScope
, aCompileOptions
,
261 if (ok
&& aEvaluateOptions
.coerceToString
&& !aRetValue
.isUndefined()) {
262 JS::Rooted
<JS::Value
> value(aCx
, aRetValue
);
263 JSString
* str
= JS::ToString(aCx
, value
);
265 aRetValue
.set(ok
? JS::StringValue(str
) : JS::UndefinedValue());
270 if (aEvaluateOptions
.reportUncaught
) {
271 ReportPendingException(aCx
);
272 if (aEvaluateOptions
.needResult
) {
273 aRetValue
.setUndefined();
276 rv
= JS_IsExceptionPending(aCx
) ? NS_ERROR_FAILURE
277 : NS_ERROR_OUT_OF_MEMORY
;
278 JS::Rooted
<JS::Value
> exn(aCx
);
279 JS_GetPendingException(aCx
, &exn
);
280 if (aEvaluateOptions
.needResult
) {
283 JS_ClearPendingException(aCx
);
287 // Wrap the return value into whatever compartment aCx was in.
288 if (aEvaluateOptions
.needResult
) {
289 JS::Rooted
<JS::Value
> v(aCx
, aRetValue
);
290 if (!JS_WrapValue(aCx
, &v
)) {
291 return NS_ERROR_OUT_OF_MEMORY
;
299 nsJSUtils::EvaluateString(JSContext
* aCx
,
300 const nsAString
& aScript
,
301 JS::Handle
<JSObject
*> aScopeObject
,
302 JS::CompileOptions
& aCompileOptions
,
303 void **aOffThreadToken
)
305 EvaluateOptions options
;
306 options
.setNeedResult(false);
307 JS::RootedValue
unused(aCx
);
308 return EvaluateString(aCx
, aScript
, aScopeObject
, aCompileOptions
,
309 options
, &unused
, aOffThreadToken
);
313 nsJSUtils::EvaluateString(JSContext
* aCx
,
314 JS::SourceBufferHolder
& aSrcBuf
,
315 JS::Handle
<JSObject
*> aScopeObject
,
316 JS::CompileOptions
& aCompileOptions
,
317 void **aOffThreadToken
)
319 EvaluateOptions options
;
320 options
.setNeedResult(false);
321 JS::RootedValue
unused(aCx
);
322 return EvaluateString(aCx
, aSrcBuf
, aScopeObject
, aCompileOptions
,
323 options
, &unused
, aOffThreadToken
);
330 JSObject
* GetDefaultScopeFromJSContext(JSContext
*cx
)
332 // DOM JSContexts don't store their default compartment object on
333 // the cx, so in those cases we need to fetch it via the scx
335 nsIScriptContext
*scx
= GetScriptContextFromJSContext(cx
);
337 return scx
->GetWindowProxy();
339 return js::DefaultObjectForContextOrNull(cx
);