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();
67 nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(JSContext
*aContext
)
72 uint64_t innerWindowID
= 0;
74 JSObject
*jsGlobal
= JS::CurrentGlobalOrNull(aContext
);
76 nsIScriptGlobalObject
*scriptGlobal
= GetStaticScriptGlobal(jsGlobal
);
78 nsCOMPtr
<nsPIDOMWindow
> win
= do_QueryInterface(scriptGlobal
);
80 innerWindowID
= win
->WindowID();
88 nsJSUtils::ReportPendingException(JSContext
*aContext
)
90 if (JS_IsExceptionPending(aContext
)) {
91 bool saved
= JS_SaveFrameChain(aContext
);
93 // JS_SaveFrameChain set the compartment of aContext to null, so we need
94 // to enter a compartment. The question is, which one? We don't want to
95 // enter the original compartment of aContext (or the compartment of the
96 // current exception on aContext, for that matter) because when we
97 // JS_ReportPendingException the JS engine can try to duck-type the
98 // exception and produce a JSErrorReport. It will then pass that
99 // JSErrorReport to the error reporter on aContext, which might expose
100 // information from it to script via onerror handlers. So it's very
101 // important that the duck typing happen in the same compartment as the
102 // onerror handler. In practice, that's the compartment of the window (or
103 // otherwise default global) of aContext, so use that here.
104 nsIScriptContext
* scx
= GetScriptContextFromJSContext(aContext
);
105 JS::Rooted
<JSObject
*> scope(aContext
);
106 scope
= scx
? scx
->GetWindowProxy() : nullptr;
108 // The SafeJSContext has no default object associated with it.
109 MOZ_ASSERT(NS_IsMainThread());
110 MOZ_ASSERT(aContext
== nsContentUtils::GetSafeJSContext());
111 scope
= xpc::UnprivilegedJunkScope(); // Usage approved by bholley
113 JSAutoCompartment
ac(aContext
, scope
);
114 JS_ReportPendingException(aContext
);
117 JS_RestoreFrameChain(aContext
);
123 nsJSUtils::CompileFunction(JSContext
* aCx
,
124 JS::Handle
<JSObject
*> aTarget
,
125 JS::CompileOptions
& aOptions
,
126 const nsACString
& aName
,
128 const char** aArgArray
,
129 const nsAString
& aBody
,
130 JSObject
** aFunctionObject
)
132 MOZ_ASSERT(js::GetEnterCompartmentDepth(aCx
) > 0);
133 MOZ_ASSERT_IF(aTarget
, js::IsObjectInContextCompartment(aTarget
, aCx
));
134 MOZ_ASSERT_IF(aOptions
.versionSet
, aOptions
.version
!= JSVERSION_UNKNOWN
);
135 mozilla::DebugOnly
<nsIScriptContext
*> ctx
= GetScriptContextFromJSContext(aCx
);
136 MOZ_ASSERT_IF(ctx
, ctx
->IsContextInitialized());
138 // Do the junk Gecko is supposed to do before calling into JSAPI.
140 JS::ExposeObjectToActiveJS(aTarget
);
144 JS::Rooted
<JSFunction
*> fun(aCx
);
145 if (!JS::CompileFunction(aCx
, aTarget
, aOptions
,
146 PromiseFlatCString(aName
).get(),
147 aArgCount
, aArgArray
,
148 PromiseFlatString(aBody
).get(),
149 aBody
.Length(), &fun
))
151 ReportPendingException(aCx
);
152 return NS_ERROR_FAILURE
;
155 *aFunctionObject
= JS_GetFunctionObject(fun
);
160 nsJSUtils::EvaluateString(JSContext
* aCx
,
161 const nsAString
& aScript
,
162 JS::Handle
<JSObject
*> aScopeObject
,
163 JS::CompileOptions
& aCompileOptions
,
164 const EvaluateOptions
& aEvaluateOptions
,
165 JS::MutableHandle
<JS::Value
> aRetValue
,
166 void **aOffThreadToken
)
168 const nsPromiseFlatString
& flatScript
= PromiseFlatString(aScript
);
169 JS::SourceBufferHolder
srcBuf(flatScript
.get(), aScript
.Length(),
170 JS::SourceBufferHolder::NoOwnership
);
171 return EvaluateString(aCx
, srcBuf
, aScopeObject
, aCompileOptions
,
172 aEvaluateOptions
, aRetValue
, aOffThreadToken
);
176 nsJSUtils::EvaluateString(JSContext
* aCx
,
177 JS::SourceBufferHolder
& aSrcBuf
,
178 JS::Handle
<JSObject
*> aScopeObject
,
179 JS::CompileOptions
& aCompileOptions
,
180 const EvaluateOptions
& aEvaluateOptions
,
181 JS::MutableHandle
<JS::Value
> aRetValue
,
182 void **aOffThreadToken
)
184 PROFILER_LABEL("nsJSUtils", "EvaluateString",
185 js::ProfileEntry::Category::JS
);
187 MOZ_ASSERT_IF(aCompileOptions
.versionSet
,
188 aCompileOptions
.version
!= JSVERSION_UNKNOWN
);
189 MOZ_ASSERT_IF(aEvaluateOptions
.coerceToString
, aEvaluateOptions
.needResult
);
190 MOZ_ASSERT_IF(!aEvaluateOptions
.reportUncaught
, aEvaluateOptions
.needResult
);
191 MOZ_ASSERT(aCx
== nsContentUtils::GetCurrentJSContext());
192 MOZ_ASSERT(aSrcBuf
.get());
194 // Unfortunately, the JS engine actually compiles scripts with a return value
195 // in a different, less efficient way. Furthermore, it can't JIT them in many
196 // cases. So we need to be explicitly told whether the caller cares about the
197 // return value. Callers can do this by calling the other overload of
198 // EvaluateString() which calls this function with aEvaluateOptions.needResult
200 aRetValue
.setUndefined();
202 JS::ExposeObjectToActiveJS(aScopeObject
);
207 nsIScriptSecurityManager
* ssm
= nsContentUtils::GetSecurityManager();
208 NS_ENSURE_TRUE(ssm
->ScriptAllowed(js::GetGlobalForObjectCrossCompartment(aScopeObject
)), NS_OK
);
210 mozilla::Maybe
<AutoDontReportUncaught
> dontReport
;
211 if (!aEvaluateOptions
.reportUncaught
) {
212 // We need to prevent AutoLastFrameCheck from reporting and clearing
213 // any pending exceptions.
214 dontReport
.emplace(aCx
);
217 // Scope the JSAutoCompartment so that we can later wrap the return value
218 // into the caller's cx.
220 JSAutoCompartment
ac(aCx
, aScopeObject
);
222 JS::Rooted
<JSObject
*> rootedScope(aCx
, aScopeObject
);
223 if (aOffThreadToken
) {
224 JS::Rooted
<JSScript
*>
225 script(aCx
, JS::FinishOffThreadScript(aCx
, JS_GetRuntime(aCx
), *aOffThreadToken
));
226 *aOffThreadToken
= nullptr; // Mark the token as having been finished.
228 if (aEvaluateOptions
.needResult
) {
229 ok
= JS_ExecuteScript(aCx
, rootedScope
, script
, aRetValue
);
231 ok
= JS_ExecuteScript(aCx
, rootedScope
, script
);
237 if (aEvaluateOptions
.needResult
) {
238 ok
= JS::Evaluate(aCx
, rootedScope
, aCompileOptions
,
241 ok
= JS::Evaluate(aCx
, rootedScope
, aCompileOptions
,
246 if (ok
&& aEvaluateOptions
.coerceToString
&& !aRetValue
.isUndefined()) {
247 JS::Rooted
<JS::Value
> value(aCx
, aRetValue
);
248 JSString
* str
= JS::ToString(aCx
, value
);
250 aRetValue
.set(ok
? JS::StringValue(str
) : JS::UndefinedValue());
255 if (aEvaluateOptions
.reportUncaught
) {
256 ReportPendingException(aCx
);
257 if (aEvaluateOptions
.needResult
) {
258 aRetValue
.setUndefined();
261 rv
= JS_IsExceptionPending(aCx
) ? NS_ERROR_FAILURE
262 : NS_ERROR_OUT_OF_MEMORY
;
263 JS::Rooted
<JS::Value
> exn(aCx
);
264 JS_GetPendingException(aCx
, &exn
);
265 if (aEvaluateOptions
.needResult
) {
268 JS_ClearPendingException(aCx
);
272 // Wrap the return value into whatever compartment aCx was in.
273 if (aEvaluateOptions
.needResult
) {
274 JS::Rooted
<JS::Value
> v(aCx
, aRetValue
);
275 if (!JS_WrapValue(aCx
, &v
)) {
276 return NS_ERROR_OUT_OF_MEMORY
;
284 nsJSUtils::EvaluateString(JSContext
* aCx
,
285 const nsAString
& aScript
,
286 JS::Handle
<JSObject
*> aScopeObject
,
287 JS::CompileOptions
& aCompileOptions
,
288 void **aOffThreadToken
)
290 EvaluateOptions options
;
291 options
.setNeedResult(false);
292 JS::RootedValue
unused(aCx
);
293 return EvaluateString(aCx
, aScript
, aScopeObject
, aCompileOptions
,
294 options
, &unused
, aOffThreadToken
);
298 nsJSUtils::EvaluateString(JSContext
* aCx
,
299 JS::SourceBufferHolder
& aSrcBuf
,
300 JS::Handle
<JSObject
*> aScopeObject
,
301 JS::CompileOptions
& aCompileOptions
,
302 void **aOffThreadToken
)
304 EvaluateOptions options
;
305 options
.setNeedResult(false);
306 JS::RootedValue
unused(aCx
);
307 return EvaluateString(aCx
, aSrcBuf
, aScopeObject
, aCompileOptions
,
308 options
, &unused
, aOffThreadToken
);
315 JSObject
* GetDefaultScopeFromJSContext(JSContext
*cx
)
317 // DOM JSContexts don't store their default compartment object on
318 // the cx, so in those cases we need to fetch it via the scx
320 nsIScriptContext
*scx
= GetScriptContextFromJSContext(cx
);
321 return scx
? scx
->GetWindowProxy() : nullptr;