nsJSUtils.h (7485B)
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 #ifndef nsJSUtils_h__ 8 #define nsJSUtils_h__ 9 10 /** 11 * This is not a generated file. It contains common utility functions 12 * invoked from the JavaScript code generated from IDL interfaces. 13 * The goal of the utility functions is to cut down on the size of 14 * the generated code itself. 15 */ 16 17 #include "js/CompileOptions.h" 18 #include "js/Conversions.h" 19 #include "js/String.h" // JS::{,Lossy}CopyLinearStringChars, JS::CopyStringChars, JS::Get{,Linear}StringLength, JS::MaxStringLength, JS::StringHasLatin1Chars 20 #include "js/Utility.h" // JS::FreePolicy 21 #include "jsapi.h" 22 #include "mozilla/Assertions.h" 23 #include "nsString.h" 24 #include "xpcpublic.h" 25 26 class nsIScriptContext; 27 class nsIScriptElement; 28 class nsIScriptGlobalObject; 29 class nsXBLPrototypeBinding; 30 31 namespace JS { 32 class JS_PUBLIC_API EnvironmentChain; 33 }; 34 35 namespace mozilla { 36 union Utf8Unit; 37 38 namespace dom { 39 class AutoJSAPI; 40 class Element; 41 } // namespace dom 42 } // namespace mozilla 43 44 class nsJSUtils { 45 public: 46 /** 47 * Retrieve the inner window ID based on the given JSContext. 48 * 49 * @param JSContext aContext 50 * The JSContext from which you want to find the inner window ID. 51 * 52 * @returns uint64_t the inner window ID. 53 */ 54 static uint64_t GetCurrentlyRunningCodeInnerWindowID(JSContext* aContext); 55 56 static nsresult CompileFunction(mozilla::dom::AutoJSAPI& jsapi, 57 const JS::EnvironmentChain& aEnvChain, 58 JS::CompileOptions& aOptions, 59 const nsACString& aName, uint32_t aArgCount, 60 const char** aArgArray, 61 const nsAString& aBody, 62 JSObject** aFunctionObject); 63 64 static nsresult UpdateFunctionDebugMetadata( 65 mozilla::dom::AutoJSAPI& jsapi, JS::Handle<JSObject*> aFun, 66 JS::CompileOptions& aOptions, JS::Handle<JSString*> aElementAttributeName, 67 JS::Handle<JS::Value> aPrivateValue); 68 69 static bool IsScriptable(JS::Handle<JSObject*> aEvaluationGlobal); 70 71 // Returns false if an exception got thrown on aCx. Passing a null 72 // aElement is allowed; that wil produce an empty aEnvChain. 73 static bool GetEnvironmentChainForElement(JSContext* aCx, 74 mozilla::dom::Element* aElement, 75 JS::EnvironmentChain& aEnvChain); 76 77 static void ResetTimeZone(); 78 79 static bool DumpEnabled(); 80 81 // A helper function that receives buffer pointer, creates ArrayBuffer, and 82 // convert it to Uint8Array. 83 // Note that the buffer needs to be created by JS_malloc (or at least can be 84 // freed by JS_free), as the resulting Uint8Array takes the ownership of the 85 // buffer. 86 static JSObject* MoveBufferAsUint8Array( 87 JSContext* aCx, size_t aSize, 88 mozilla::UniquePtr<uint8_t[], JS::FreePolicy> aBuffer); 89 }; 90 91 template <typename T, typename std::enable_if_t<std::is_same< 92 typename T::char_type, char16_t>::value>* = nullptr> 93 inline bool AssignJSString(JSContext* cx, T& dest, JSString* s) { 94 size_t len = JS::GetStringLength(s); 95 static_assert(JS::MaxStringLength < (1 << 30), 96 "Shouldn't overflow here or in SetCapacity"); 97 98 if (XPCStringConvert::MaybeAssignUCStringChars(s, len, dest)) { 99 return true; 100 } 101 102 // We don't bother checking for a dynamic-atom external string, because we'd 103 // just need to copy out of it anyway. 104 105 if (MOZ_UNLIKELY(!dest.SetLength(len, mozilla::fallible))) { 106 JS_ReportOutOfMemory(cx); 107 return false; 108 } 109 return JS::CopyStringChars(cx, dest.BeginWriting(), s, len); 110 } 111 112 // Specialization for UTF8String. 113 template <typename T, typename std::enable_if_t<std::is_same< 114 typename T::char_type, char>::value>* = nullptr> 115 inline bool AssignJSString(JSContext* cx, T& dest, JSString* s) { 116 using namespace mozilla; 117 CheckedInt<size_t> bufLen(JS::GetStringLength(s)); 118 119 if (XPCStringConvert::MaybeAssignUTF8StringChars(s, bufLen.value(), dest)) { 120 return true; 121 } 122 123 // From the contract for JS_EncodeStringToUTF8BufferPartial, to guarantee that 124 // the whole string is converted. 125 if (JS::StringHasLatin1Chars(s)) { 126 bufLen *= 2; 127 } else { 128 bufLen *= 3; 129 } 130 131 if (MOZ_UNLIKELY(!bufLen.isValid())) { 132 JS_ReportOutOfMemory(cx); 133 return false; 134 } 135 136 // Shouldn't really matter, but worth being safe. 137 const bool kAllowShrinking = true; 138 139 auto handleOrErr = dest.BulkWrite(bufLen.value(), 0, kAllowShrinking); 140 if (MOZ_UNLIKELY(handleOrErr.isErr())) { 141 JS_ReportOutOfMemory(cx); 142 return false; 143 } 144 145 auto handle = handleOrErr.unwrap(); 146 147 auto maybe = JS_EncodeStringToUTF8BufferPartial(cx, s, handle.AsSpan()); 148 if (MOZ_UNLIKELY(!maybe)) { 149 JS_ReportOutOfMemory(cx); 150 return false; 151 } 152 153 size_t read; 154 size_t written; 155 std::tie(read, written) = *maybe; 156 157 MOZ_ASSERT(read == JS::GetStringLength(s)); 158 handle.Finish(written, kAllowShrinking); 159 return true; 160 } 161 162 inline void AssignJSLinearString(nsAString& dest, JSLinearString* s) { 163 size_t len = JS::GetLinearStringLength(s); 164 static_assert(JS::MaxStringLength < (1 << 30), 165 "Shouldn't overflow here or in SetCapacity"); 166 dest.SetLength(len); 167 JS::CopyLinearStringChars(dest.BeginWriting(), s, len); 168 } 169 170 inline void AssignJSLinearString(nsACString& dest, JSLinearString* s) { 171 size_t len = JS::GetLinearStringLength(s); 172 static_assert(JS::MaxStringLength < (1 << 30), 173 "Shouldn't overflow here or in SetCapacity"); 174 dest.SetLength(len); 175 JS::LossyCopyLinearStringChars(dest.BeginWriting(), s, len); 176 } 177 178 template <typename T> 179 class nsTAutoJSLinearString : public nsTAutoString<T> { 180 public: 181 explicit nsTAutoJSLinearString(JSLinearString* str) { 182 AssignJSLinearString(*this, str); 183 } 184 }; 185 186 using nsAutoJSLinearString = nsTAutoJSLinearString<char16_t>; 187 using nsAutoJSLinearCString = nsTAutoJSLinearString<char>; 188 189 template <typename T> 190 class nsTAutoJSString : public nsTAutoString<T> { 191 public: 192 /** 193 * nsTAutoJSString should be default constructed, which leaves it empty 194 * (this->IsEmpty()), and initialized with one of the init() methods below. 195 */ 196 nsTAutoJSString() = default; 197 198 bool init(JSContext* aContext, JSString* str) { 199 return AssignJSString(aContext, *this, str); 200 } 201 202 bool init(JSContext* aContext, const JS::Value& v) { 203 if (v.isString()) { 204 return init(aContext, v.toString()); 205 } 206 207 // Stringify, making sure not to run script. 208 JS::Rooted<JSString*> str(aContext); 209 if (v.isObject()) { 210 str = JS_NewStringCopyZ(aContext, "[Object]"); 211 } else { 212 JS::Rooted<JS::Value> rootedVal(aContext, v); 213 str = JS::ToString(aContext, rootedVal); 214 } 215 216 return str && init(aContext, str); 217 } 218 219 bool init(JSContext* aContext, jsid id) { 220 JS::Rooted<JS::Value> v(aContext); 221 return JS_IdToValue(aContext, id, &v) && init(aContext, v); 222 } 223 224 bool init(const JS::Value& v); 225 226 ~nsTAutoJSString() = default; 227 }; 228 229 using nsAutoJSString = nsTAutoJSString<char16_t>; 230 231 // Note that this is guaranteed to be UTF-8. 232 using nsAutoJSCString = nsTAutoJSString<char>; 233 234 #endif /* nsJSUtils_h__ */