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 
16 #include <utility>
17 #include "MainThreadUtils.h"
18 #include "js/ComparisonOperators.h"
19 #include "js/CompilationAndEvaluation.h"
20 #include "js/CompileOptions.h"
21 #include "js/Date.h"
22 #include "js/GCVector.h"
23 #include "js/HeapAPI.h"
24 #include "js/Modules.h"
25 #include "js/RootingAPI.h"
26 #include "js/SourceText.h"
27 #include "js/TypeDecls.h"
28 #include "jsfriendapi.h"
29 #include "mozilla/CycleCollectedJSContext.h"
30 #include "mozilla/dom/BindingUtils.h"
31 #include "mozilla/dom/Element.h"
32 #include "mozilla/dom/ScriptSettings.h"
33 #include "mozilla/fallible.h"
34 #include "mozilla/ProfilerLabels.h"
35 #include "nsContentUtils.h"
36 #include "nsDebug.h"
37 #include "nsGlobalWindowInner.h"
38 #include "nsINode.h"
39 #include "nsString.h"
40 #include "nsTPromiseFlatString.h"
41 #include "nscore.h"
42 
43 #if !defined(DEBUG) && !defined(MOZ_ENABLE_JS_DUMP)
44 #  include "mozilla/StaticPrefs_browser.h"
45 #endif
46 
47 using namespace mozilla;
48 using namespace mozilla::dom;
49 
GetCallingLocation(JSContext * aContext,nsACString & aFilename,uint32_t * aLineno,uint32_t * aColumn)50 bool nsJSUtils::GetCallingLocation(JSContext* aContext, nsACString& aFilename,
51                                    uint32_t* aLineno, uint32_t* aColumn) {
52   JS::AutoFilename filename;
53   if (!JS::DescribeScriptedCaller(aContext, &filename, aLineno, aColumn)) {
54     return false;
55   }
56 
57   return aFilename.Assign(filename.get(), fallible);
58 }
59 
GetCallingLocation(JSContext * aContext,nsAString & aFilename,uint32_t * aLineno,uint32_t * aColumn)60 bool nsJSUtils::GetCallingLocation(JSContext* aContext, nsAString& aFilename,
61                                    uint32_t* aLineno, uint32_t* aColumn) {
62   JS::AutoFilename filename;
63   if (!JS::DescribeScriptedCaller(aContext, &filename, aLineno, aColumn)) {
64     return false;
65   }
66 
67   return aFilename.Assign(NS_ConvertUTF8toUTF16(filename.get()), fallible);
68 }
69 
GetCurrentlyRunningCodeInnerWindowID(JSContext * aContext)70 uint64_t nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(JSContext* aContext) {
71   if (!aContext) return 0;
72 
73   nsGlobalWindowInner* win = xpc::CurrentWindowOrNull(aContext);
74   return win ? win->WindowID() : 0;
75 }
76 
UpdateFunctionDebugMetadata(AutoJSAPI & jsapi,JS::Handle<JSObject * > aFun,JS::CompileOptions & aOptions,JS::Handle<JSString * > aElementAttributeName,JS::Handle<JS::Value> aPrivateValue)77 nsresult nsJSUtils::UpdateFunctionDebugMetadata(
78     AutoJSAPI& jsapi, JS::Handle<JSObject*> aFun, JS::CompileOptions& aOptions,
79     JS::Handle<JSString*> aElementAttributeName,
80     JS::Handle<JS::Value> aPrivateValue) {
81   JSContext* cx = jsapi.cx();
82 
83   JS::Rooted<JSFunction*> fun(cx, JS_GetObjectFunction(aFun));
84   if (!fun) {
85     return NS_ERROR_FAILURE;
86   }
87 
88   JS::RootedScript script(cx, JS_GetFunctionScript(cx, fun));
89   if (!script) {
90     return NS_OK;
91   }
92 
93   if (!JS::UpdateDebugMetadata(cx, script, aOptions, aPrivateValue,
94                                aElementAttributeName, nullptr, nullptr)) {
95     return NS_ERROR_FAILURE;
96   }
97   return NS_OK;
98 }
99 
CompileFunction(AutoJSAPI & jsapi,JS::HandleVector<JSObject * > aScopeChain,JS::CompileOptions & aOptions,const nsACString & aName,uint32_t aArgCount,const char ** aArgArray,const nsAString & aBody,JSObject ** aFunctionObject)100 nsresult nsJSUtils::CompileFunction(AutoJSAPI& jsapi,
101                                     JS::HandleVector<JSObject*> aScopeChain,
102                                     JS::CompileOptions& aOptions,
103                                     const nsACString& aName, uint32_t aArgCount,
104                                     const char** aArgArray,
105                                     const nsAString& aBody,
106                                     JSObject** aFunctionObject) {
107   JSContext* cx = jsapi.cx();
108   MOZ_ASSERT(js::GetContextRealm(cx));
109   MOZ_ASSERT_IF(aScopeChain.length() != 0,
110                 js::IsObjectInContextCompartment(aScopeChain[0], cx));
111 
112   // Do the junk Gecko is supposed to do before calling into JSAPI.
113   for (size_t i = 0; i < aScopeChain.length(); ++i) {
114     JS::ExposeObjectToActiveJS(aScopeChain[i]);
115   }
116 
117   // Compile.
118   const nsPromiseFlatString& flatBody = PromiseFlatString(aBody);
119 
120   JS::SourceText<char16_t> source;
121   if (!source.init(cx, flatBody.get(), flatBody.Length(),
122                    JS::SourceOwnership::Borrowed)) {
123     return NS_ERROR_FAILURE;
124   }
125 
126   JS::Rooted<JSFunction*> fun(
127       cx, JS::CompileFunction(cx, aScopeChain, aOptions,
128                               PromiseFlatCString(aName).get(), aArgCount,
129                               aArgArray, source));
130   if (!fun) {
131     return NS_ERROR_FAILURE;
132   }
133 
134   *aFunctionObject = JS_GetFunctionObject(fun);
135   return NS_OK;
136 }
137 
138 template <typename Unit>
CompileJSModule(JSContext * aCx,JS::SourceText<Unit> & aSrcBuf,JS::Handle<JSObject * > aEvaluationGlobal,JS::CompileOptions & aCompileOptions,JS::MutableHandle<JSObject * > aModule)139 static nsresult CompileJSModule(JSContext* aCx, JS::SourceText<Unit>& aSrcBuf,
140                                 JS::Handle<JSObject*> aEvaluationGlobal,
141                                 JS::CompileOptions& aCompileOptions,
142                                 JS::MutableHandle<JSObject*> aModule) {
143   AUTO_PROFILER_LABEL("nsJSUtils::CompileModule", JS);
144   MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext());
145   MOZ_ASSERT(aSrcBuf.get());
146   MOZ_ASSERT(JS_IsGlobalObject(aEvaluationGlobal));
147   MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx) == aEvaluationGlobal);
148   MOZ_ASSERT(NS_IsMainThread());
149   MOZ_ASSERT(CycleCollectedJSContext::Get() &&
150              CycleCollectedJSContext::Get()->MicroTaskLevel());
151 
152   NS_ENSURE_TRUE(xpc::Scriptability::Get(aEvaluationGlobal).Allowed(), NS_OK);
153 
154   JSObject* module = JS::CompileModule(aCx, aCompileOptions, aSrcBuf);
155   if (!module) {
156     return NS_ERROR_FAILURE;
157   }
158 
159   aModule.set(module);
160   return NS_OK;
161 }
162 
CompileModule(JSContext * aCx,JS::SourceText<char16_t> & aSrcBuf,JS::Handle<JSObject * > aEvaluationGlobal,JS::CompileOptions & aCompileOptions,JS::MutableHandle<JSObject * > aModule)163 nsresult nsJSUtils::CompileModule(JSContext* aCx,
164                                   JS::SourceText<char16_t>& aSrcBuf,
165                                   JS::Handle<JSObject*> aEvaluationGlobal,
166                                   JS::CompileOptions& aCompileOptions,
167                                   JS::MutableHandle<JSObject*> aModule) {
168   return CompileJSModule(aCx, aSrcBuf, aEvaluationGlobal, aCompileOptions,
169                          aModule);
170 }
171 
CompileModule(JSContext * aCx,JS::SourceText<Utf8Unit> & aSrcBuf,JS::Handle<JSObject * > aEvaluationGlobal,JS::CompileOptions & aCompileOptions,JS::MutableHandle<JSObject * > aModule)172 nsresult nsJSUtils::CompileModule(JSContext* aCx,
173                                   JS::SourceText<Utf8Unit>& aSrcBuf,
174                                   JS::Handle<JSObject*> aEvaluationGlobal,
175                                   JS::CompileOptions& aCompileOptions,
176                                   JS::MutableHandle<JSObject*> aModule) {
177   return CompileJSModule(aCx, aSrcBuf, aEvaluationGlobal, aCompileOptions,
178                          aModule);
179 }
180 
ModuleInstantiate(JSContext * aCx,JS::Handle<JSObject * > aModule)181 nsresult nsJSUtils::ModuleInstantiate(JSContext* aCx,
182                                       JS::Handle<JSObject*> aModule) {
183   AUTO_PROFILER_LABEL("nsJSUtils::ModuleInstantiate", JS);
184 
185   MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext());
186   MOZ_ASSERT(NS_IsMainThread());
187   MOZ_ASSERT(CycleCollectedJSContext::Get() &&
188              CycleCollectedJSContext::Get()->MicroTaskLevel());
189 
190   NS_ENSURE_TRUE(xpc::Scriptability::Get(aModule).Allowed(), NS_OK);
191 
192   if (!JS::ModuleInstantiate(aCx, aModule)) {
193     return NS_ERROR_FAILURE;
194   }
195 
196   return NS_OK;
197 }
198 
ModuleEvaluate(JSContext * aCx,JS::Handle<JSObject * > aModule,JS::MutableHandle<JS::Value> aResult)199 nsresult nsJSUtils::ModuleEvaluate(JSContext* aCx,
200                                    JS::Handle<JSObject*> aModule,
201                                    JS::MutableHandle<JS::Value> aResult) {
202   AUTO_PROFILER_LABEL("nsJSUtils::ModuleEvaluate", JS);
203 
204   MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext());
205   MOZ_ASSERT(NS_IsMainThread());
206   MOZ_ASSERT(CycleCollectedJSContext::Get() &&
207              CycleCollectedJSContext::Get()->MicroTaskLevel());
208 
209   NS_ENSURE_TRUE(xpc::Scriptability::Get(aModule).Allowed(), NS_OK);
210 
211   if (!JS::ModuleEvaluate(aCx, aModule, aResult)) {
212     return NS_ERROR_FAILURE;
213   }
214 
215   return NS_OK;
216 }
217 
AddScopeChainItem(JSContext * aCx,nsINode * aNode,JS::MutableHandleVector<JSObject * > aScopeChain)218 static bool AddScopeChainItem(JSContext* aCx, nsINode* aNode,
219                               JS::MutableHandleVector<JSObject*> aScopeChain) {
220   JS::RootedValue val(aCx);
221   if (!GetOrCreateDOMReflector(aCx, aNode, &val)) {
222     return false;
223   }
224 
225   if (!aScopeChain.append(&val.toObject())) {
226     return false;
227   }
228 
229   return true;
230 }
231 
232 /* static */
GetScopeChainForElement(JSContext * aCx,Element * aElement,JS::MutableHandleVector<JSObject * > aScopeChain)233 bool nsJSUtils::GetScopeChainForElement(
234     JSContext* aCx, Element* aElement,
235     JS::MutableHandleVector<JSObject*> aScopeChain) {
236   for (nsINode* cur = aElement; cur; cur = cur->GetScopeChainParent()) {
237     if (!AddScopeChainItem(aCx, cur, aScopeChain)) {
238       return false;
239     }
240   }
241 
242   return true;
243 }
244 
245 /* static */
ResetTimeZone()246 void nsJSUtils::ResetTimeZone() { JS::ResetTimeZone(); }
247 
248 /* static */
DumpEnabled()249 bool nsJSUtils::DumpEnabled() {
250 #if defined(DEBUG) || defined(MOZ_ENABLE_JS_DUMP)
251   return true;
252 #else
253   return StaticPrefs::browser_dom_window_dump_enabled();
254 #endif
255 }
256 
257 //
258 // nsDOMJSUtils.h
259 //
260 
261 template <typename T>
init(const JS::Value & v)262 bool nsTAutoJSString<T>::init(const JS::Value& v) {
263   // Note: it's okay to use danger::GetJSContext here instead of AutoJSAPI,
264   // because the init() call below is careful not to run script (for instance,
265   // it only calls JS::ToString for non-object values).
266   JSContext* cx = danger::GetJSContext();
267   if (!init(cx, v)) {
268     JS_ClearPendingException(cx);
269     return false;
270   }
271   return true;
272 }
273 
274 template bool nsTAutoJSString<char16_t>::init(const JS::Value&);
275 template bool nsTAutoJSString<char>::init(const JS::Value&);
276