Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ |
michael@0 | 2 | /* vim: set ts=2 sw=2 et tw=79: */ |
michael@0 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
michael@0 | 5 | * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | #include "BindingUtils.h" |
michael@0 | 8 | |
michael@0 | 9 | #include <algorithm> |
michael@0 | 10 | #include <stdarg.h> |
michael@0 | 11 | |
michael@0 | 12 | #include "JavaScriptParent.h" |
michael@0 | 13 | |
michael@0 | 14 | #include "mozilla/DebugOnly.h" |
michael@0 | 15 | #include "mozilla/FloatingPoint.h" |
michael@0 | 16 | #include "mozilla/Assertions.h" |
michael@0 | 17 | |
michael@0 | 18 | #include "AccessCheck.h" |
michael@0 | 19 | #include "jsfriendapi.h" |
michael@0 | 20 | #include "js/OldDebugAPI.h" |
michael@0 | 21 | #include "nsContentUtils.h" |
michael@0 | 22 | #include "nsIDOMGlobalPropertyInitializer.h" |
michael@0 | 23 | #include "nsIPrincipal.h" |
michael@0 | 24 | #include "nsIXPConnect.h" |
michael@0 | 25 | #include "WrapperFactory.h" |
michael@0 | 26 | #include "xpcprivate.h" |
michael@0 | 27 | #include "XPCQuickStubs.h" |
michael@0 | 28 | #include "XrayWrapper.h" |
michael@0 | 29 | #include "nsPrintfCString.h" |
michael@0 | 30 | #include "prprf.h" |
michael@0 | 31 | |
michael@0 | 32 | #include "mozilla/dom/ScriptSettings.h" |
michael@0 | 33 | #include "mozilla/dom/DOMError.h" |
michael@0 | 34 | #include "mozilla/dom/DOMErrorBinding.h" |
michael@0 | 35 | #include "mozilla/dom/HTMLObjectElement.h" |
michael@0 | 36 | #include "mozilla/dom/HTMLObjectElementBinding.h" |
michael@0 | 37 | #include "mozilla/dom/HTMLSharedObjectElement.h" |
michael@0 | 38 | #include "mozilla/dom/HTMLEmbedElementBinding.h" |
michael@0 | 39 | #include "mozilla/dom/HTMLAppletElementBinding.h" |
michael@0 | 40 | #include "mozilla/dom/Promise.h" |
michael@0 | 41 | #include "WorkerPrivate.h" |
michael@0 | 42 | |
michael@0 | 43 | namespace mozilla { |
michael@0 | 44 | namespace dom { |
michael@0 | 45 | |
michael@0 | 46 | JSErrorFormatString ErrorFormatString[] = { |
michael@0 | 47 | #define MSG_DEF(_name, _argc, _str) \ |
michael@0 | 48 | { _str, _argc, JSEXN_TYPEERR }, |
michael@0 | 49 | #include "mozilla/dom/Errors.msg" |
michael@0 | 50 | #undef MSG_DEF |
michael@0 | 51 | }; |
michael@0 | 52 | |
michael@0 | 53 | const JSErrorFormatString* |
michael@0 | 54 | GetErrorMessage(void* aUserRef, const char* aLocale, |
michael@0 | 55 | const unsigned aErrorNumber) |
michael@0 | 56 | { |
michael@0 | 57 | MOZ_ASSERT(aErrorNumber < ArrayLength(ErrorFormatString)); |
michael@0 | 58 | return &ErrorFormatString[aErrorNumber]; |
michael@0 | 59 | } |
michael@0 | 60 | |
michael@0 | 61 | bool |
michael@0 | 62 | ThrowErrorMessage(JSContext* aCx, const ErrNum aErrorNumber, ...) |
michael@0 | 63 | { |
michael@0 | 64 | va_list ap; |
michael@0 | 65 | va_start(ap, aErrorNumber); |
michael@0 | 66 | JS_ReportErrorNumberVA(aCx, GetErrorMessage, nullptr, |
michael@0 | 67 | static_cast<const unsigned>(aErrorNumber), ap); |
michael@0 | 68 | va_end(ap); |
michael@0 | 69 | return false; |
michael@0 | 70 | } |
michael@0 | 71 | |
michael@0 | 72 | bool |
michael@0 | 73 | ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs, |
michael@0 | 74 | const ErrNum aErrorNumber, |
michael@0 | 75 | const char* aInterfaceName) |
michael@0 | 76 | { |
michael@0 | 77 | NS_ConvertASCIItoUTF16 ifaceName(aInterfaceName); |
michael@0 | 78 | // This should only be called for DOM methods/getters/setters, which |
michael@0 | 79 | // are JSNative-backed functions, so we can assume that |
michael@0 | 80 | // JS_ValueToFunction and JS_GetFunctionDisplayId will both return |
michael@0 | 81 | // non-null and that JS_GetStringCharsZ returns non-null. |
michael@0 | 82 | JS::Rooted<JSFunction*> func(aCx, JS_ValueToFunction(aCx, aArgs.calleev())); |
michael@0 | 83 | MOZ_ASSERT(func); |
michael@0 | 84 | JS::Rooted<JSString*> funcName(aCx, JS_GetFunctionDisplayId(func)); |
michael@0 | 85 | MOZ_ASSERT(funcName); |
michael@0 | 86 | JS_ReportErrorNumberUC(aCx, GetErrorMessage, nullptr, |
michael@0 | 87 | static_cast<const unsigned>(aErrorNumber), |
michael@0 | 88 | JS_GetStringCharsZ(aCx, funcName), |
michael@0 | 89 | ifaceName.get()); |
michael@0 | 90 | return false; |
michael@0 | 91 | } |
michael@0 | 92 | |
michael@0 | 93 | bool |
michael@0 | 94 | ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs, |
michael@0 | 95 | const ErrNum aErrorNumber, |
michael@0 | 96 | prototypes::ID aProtoId) |
michael@0 | 97 | { |
michael@0 | 98 | return ThrowInvalidThis(aCx, aArgs, aErrorNumber, |
michael@0 | 99 | NamesOfInterfacesWithProtos(aProtoId)); |
michael@0 | 100 | } |
michael@0 | 101 | |
michael@0 | 102 | bool |
michael@0 | 103 | ThrowNoSetterArg(JSContext* aCx, prototypes::ID aProtoId) |
michael@0 | 104 | { |
michael@0 | 105 | nsPrintfCString errorMessage("%s attribute setter", |
michael@0 | 106 | NamesOfInterfacesWithProtos(aProtoId)); |
michael@0 | 107 | return ThrowErrorMessage(aCx, MSG_MISSING_ARGUMENTS, errorMessage.get()); |
michael@0 | 108 | } |
michael@0 | 109 | |
michael@0 | 110 | } // namespace dom |
michael@0 | 111 | |
michael@0 | 112 | struct ErrorResult::Message { |
michael@0 | 113 | nsTArray<nsString> mArgs; |
michael@0 | 114 | dom::ErrNum mErrorNumber; |
michael@0 | 115 | }; |
michael@0 | 116 | |
michael@0 | 117 | void |
michael@0 | 118 | ErrorResult::ThrowTypeError(const dom::ErrNum errorNumber, ...) |
michael@0 | 119 | { |
michael@0 | 120 | va_list ap; |
michael@0 | 121 | va_start(ap, errorNumber); |
michael@0 | 122 | if (IsJSException()) { |
michael@0 | 123 | // We have rooted our mJSException, and we don't have the info |
michael@0 | 124 | // needed to unroot here, so just bail. |
michael@0 | 125 | va_end(ap); |
michael@0 | 126 | MOZ_ASSERT(false, |
michael@0 | 127 | "Ignoring ThrowTypeError call because we have a JS exception"); |
michael@0 | 128 | return; |
michael@0 | 129 | } |
michael@0 | 130 | if (IsTypeError()) { |
michael@0 | 131 | delete mMessage; |
michael@0 | 132 | } |
michael@0 | 133 | mResult = NS_ERROR_TYPE_ERR; |
michael@0 | 134 | Message* message = new Message(); |
michael@0 | 135 | message->mErrorNumber = errorNumber; |
michael@0 | 136 | uint16_t argCount = |
michael@0 | 137 | dom::GetErrorMessage(nullptr, nullptr, errorNumber)->argCount; |
michael@0 | 138 | MOZ_ASSERT(argCount <= 10); |
michael@0 | 139 | argCount = std::min<uint16_t>(argCount, 10); |
michael@0 | 140 | while (argCount--) { |
michael@0 | 141 | message->mArgs.AppendElement(*va_arg(ap, nsString*)); |
michael@0 | 142 | } |
michael@0 | 143 | mMessage = message; |
michael@0 | 144 | va_end(ap); |
michael@0 | 145 | } |
michael@0 | 146 | |
michael@0 | 147 | void |
michael@0 | 148 | ErrorResult::ReportTypeError(JSContext* aCx) |
michael@0 | 149 | { |
michael@0 | 150 | MOZ_ASSERT(mMessage, "ReportTypeError() can be called only once"); |
michael@0 | 151 | |
michael@0 | 152 | Message* message = mMessage; |
michael@0 | 153 | const uint32_t argCount = message->mArgs.Length(); |
michael@0 | 154 | const jschar* args[11]; |
michael@0 | 155 | for (uint32_t i = 0; i < argCount; ++i) { |
michael@0 | 156 | args[i] = message->mArgs.ElementAt(i).get(); |
michael@0 | 157 | } |
michael@0 | 158 | args[argCount] = nullptr; |
michael@0 | 159 | |
michael@0 | 160 | JS_ReportErrorNumberUCArray(aCx, dom::GetErrorMessage, nullptr, |
michael@0 | 161 | static_cast<const unsigned>(message->mErrorNumber), |
michael@0 | 162 | argCount > 0 ? args : nullptr); |
michael@0 | 163 | |
michael@0 | 164 | ClearMessage(); |
michael@0 | 165 | } |
michael@0 | 166 | |
michael@0 | 167 | void |
michael@0 | 168 | ErrorResult::ClearMessage() |
michael@0 | 169 | { |
michael@0 | 170 | if (IsTypeError()) { |
michael@0 | 171 | delete mMessage; |
michael@0 | 172 | mMessage = nullptr; |
michael@0 | 173 | } |
michael@0 | 174 | } |
michael@0 | 175 | |
michael@0 | 176 | void |
michael@0 | 177 | ErrorResult::ThrowJSException(JSContext* cx, JS::Handle<JS::Value> exn) |
michael@0 | 178 | { |
michael@0 | 179 | MOZ_ASSERT(mMightHaveUnreportedJSException, |
michael@0 | 180 | "Why didn't you tell us you planned to throw a JS exception?"); |
michael@0 | 181 | |
michael@0 | 182 | if (IsTypeError()) { |
michael@0 | 183 | delete mMessage; |
michael@0 | 184 | } |
michael@0 | 185 | |
michael@0 | 186 | // Make sure mJSException is initialized _before_ we try to root it. But |
michael@0 | 187 | // don't set it to exn yet, because we don't want to do that until after we |
michael@0 | 188 | // root. |
michael@0 | 189 | mJSException = JS::UndefinedValue(); |
michael@0 | 190 | if (!js::AddRawValueRoot(cx, &mJSException, "ErrorResult::mJSException")) { |
michael@0 | 191 | // Don't use NS_ERROR_DOM_JS_EXCEPTION, because that indicates we have |
michael@0 | 192 | // in fact rooted mJSException. |
michael@0 | 193 | mResult = NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 194 | } else { |
michael@0 | 195 | mJSException = exn; |
michael@0 | 196 | mResult = NS_ERROR_DOM_JS_EXCEPTION; |
michael@0 | 197 | } |
michael@0 | 198 | } |
michael@0 | 199 | |
michael@0 | 200 | void |
michael@0 | 201 | ErrorResult::ReportJSException(JSContext* cx) |
michael@0 | 202 | { |
michael@0 | 203 | MOZ_ASSERT(!mMightHaveUnreportedJSException, |
michael@0 | 204 | "Why didn't you tell us you planned to handle JS exceptions?"); |
michael@0 | 205 | |
michael@0 | 206 | JS::Rooted<JS::Value> exception(cx, mJSException); |
michael@0 | 207 | if (JS_WrapValue(cx, &exception)) { |
michael@0 | 208 | JS_SetPendingException(cx, exception); |
michael@0 | 209 | } |
michael@0 | 210 | mJSException = exception; |
michael@0 | 211 | // If JS_WrapValue failed, not much we can do about it... No matter |
michael@0 | 212 | // what, go ahead and unroot mJSException. |
michael@0 | 213 | js::RemoveRawValueRoot(cx, &mJSException); |
michael@0 | 214 | } |
michael@0 | 215 | |
michael@0 | 216 | void |
michael@0 | 217 | ErrorResult::ReportJSExceptionFromJSImplementation(JSContext* aCx) |
michael@0 | 218 | { |
michael@0 | 219 | MOZ_ASSERT(!mMightHaveUnreportedJSException, |
michael@0 | 220 | "Why didn't you tell us you planned to handle JS exceptions?"); |
michael@0 | 221 | |
michael@0 | 222 | dom::DOMError* domError; |
michael@0 | 223 | nsresult rv = UNWRAP_OBJECT(DOMError, &mJSException.toObject(), domError); |
michael@0 | 224 | if (NS_FAILED(rv)) { |
michael@0 | 225 | // Unwrapping really shouldn't fail here, if mExceptionHandling is set to |
michael@0 | 226 | // eRethrowContentExceptions then the CallSetup destructor only stores an |
michael@0 | 227 | // exception if it unwraps to DOMError. If we reach this then either |
michael@0 | 228 | // mExceptionHandling wasn't set to eRethrowContentExceptions and we |
michael@0 | 229 | // shouldn't be calling ReportJSExceptionFromJSImplementation or something |
michael@0 | 230 | // went really wrong. |
michael@0 | 231 | NS_RUNTIMEABORT("We stored a non-DOMError exception!"); |
michael@0 | 232 | } |
michael@0 | 233 | |
michael@0 | 234 | nsString message; |
michael@0 | 235 | domError->GetMessage(message); |
michael@0 | 236 | |
michael@0 | 237 | JS_ReportError(aCx, "%hs", message.get()); |
michael@0 | 238 | js::RemoveRawValueRoot(aCx, &mJSException); |
michael@0 | 239 | |
michael@0 | 240 | // We no longer have a useful exception but we do want to signal that an error |
michael@0 | 241 | // occured. |
michael@0 | 242 | mResult = NS_ERROR_FAILURE; |
michael@0 | 243 | } |
michael@0 | 244 | |
michael@0 | 245 | void |
michael@0 | 246 | ErrorResult::StealJSException(JSContext* cx, |
michael@0 | 247 | JS::MutableHandle<JS::Value> value) |
michael@0 | 248 | { |
michael@0 | 249 | MOZ_ASSERT(!mMightHaveUnreportedJSException, |
michael@0 | 250 | "Must call WouldReportJSException unconditionally in all codepaths that might call StealJSException"); |
michael@0 | 251 | MOZ_ASSERT(IsJSException(), "No exception to steal"); |
michael@0 | 252 | |
michael@0 | 253 | value.set(mJSException); |
michael@0 | 254 | js::RemoveRawValueRoot(cx, &mJSException); |
michael@0 | 255 | mResult = NS_OK; |
michael@0 | 256 | } |
michael@0 | 257 | |
michael@0 | 258 | void |
michael@0 | 259 | ErrorResult::ReportNotEnoughArgsError(JSContext* cx, |
michael@0 | 260 | const char* ifaceName, |
michael@0 | 261 | const char* memberName) |
michael@0 | 262 | { |
michael@0 | 263 | MOZ_ASSERT(ErrorCode() == NS_ERROR_XPC_NOT_ENOUGH_ARGS); |
michael@0 | 264 | |
michael@0 | 265 | nsPrintfCString errorMessage("%s.%s", ifaceName, memberName); |
michael@0 | 266 | ThrowErrorMessage(cx, dom::MSG_MISSING_ARGUMENTS, errorMessage.get()); |
michael@0 | 267 | } |
michael@0 | 268 | |
michael@0 | 269 | namespace dom { |
michael@0 | 270 | |
michael@0 | 271 | bool |
michael@0 | 272 | DefineConstants(JSContext* cx, JS::Handle<JSObject*> obj, |
michael@0 | 273 | const ConstantSpec* cs) |
michael@0 | 274 | { |
michael@0 | 275 | JS::Rooted<JS::Value> value(cx); |
michael@0 | 276 | for (; cs->name; ++cs) { |
michael@0 | 277 | value = cs->value; |
michael@0 | 278 | bool ok = |
michael@0 | 279 | JS_DefineProperty(cx, obj, cs->name, value, |
michael@0 | 280 | JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT); |
michael@0 | 281 | if (!ok) { |
michael@0 | 282 | return false; |
michael@0 | 283 | } |
michael@0 | 284 | } |
michael@0 | 285 | return true; |
michael@0 | 286 | } |
michael@0 | 287 | |
michael@0 | 288 | static inline bool |
michael@0 | 289 | Define(JSContext* cx, JS::Handle<JSObject*> obj, const JSFunctionSpec* spec) { |
michael@0 | 290 | return JS_DefineFunctions(cx, obj, spec); |
michael@0 | 291 | } |
michael@0 | 292 | static inline bool |
michael@0 | 293 | Define(JSContext* cx, JS::Handle<JSObject*> obj, const JSPropertySpec* spec) { |
michael@0 | 294 | return JS_DefineProperties(cx, obj, spec); |
michael@0 | 295 | } |
michael@0 | 296 | static inline bool |
michael@0 | 297 | Define(JSContext* cx, JS::Handle<JSObject*> obj, const ConstantSpec* spec) { |
michael@0 | 298 | return DefineConstants(cx, obj, spec); |
michael@0 | 299 | } |
michael@0 | 300 | |
michael@0 | 301 | template<typename T> |
michael@0 | 302 | bool |
michael@0 | 303 | DefinePrefable(JSContext* cx, JS::Handle<JSObject*> obj, |
michael@0 | 304 | const Prefable<T>* props) |
michael@0 | 305 | { |
michael@0 | 306 | MOZ_ASSERT(props); |
michael@0 | 307 | MOZ_ASSERT(props->specs); |
michael@0 | 308 | do { |
michael@0 | 309 | // Define if enabled |
michael@0 | 310 | if (props->isEnabled(cx, obj)) { |
michael@0 | 311 | if (!Define(cx, obj, props->specs)) { |
michael@0 | 312 | return false; |
michael@0 | 313 | } |
michael@0 | 314 | } |
michael@0 | 315 | } while ((++props)->specs); |
michael@0 | 316 | return true; |
michael@0 | 317 | } |
michael@0 | 318 | |
michael@0 | 319 | bool |
michael@0 | 320 | DefineUnforgeableAttributes(JSContext* cx, JS::Handle<JSObject*> obj, |
michael@0 | 321 | const Prefable<const JSPropertySpec>* props) |
michael@0 | 322 | { |
michael@0 | 323 | return DefinePrefable(cx, obj, props); |
michael@0 | 324 | } |
michael@0 | 325 | |
michael@0 | 326 | |
michael@0 | 327 | // We should use JSFunction objects for interface objects, but we need a custom |
michael@0 | 328 | // hasInstance hook because we have new interface objects on prototype chains of |
michael@0 | 329 | // old (XPConnect-based) bindings. Because Function.prototype.toString throws if |
michael@0 | 330 | // passed a non-Function object we also need to provide our own toString method |
michael@0 | 331 | // for interface objects. |
michael@0 | 332 | |
michael@0 | 333 | enum { |
michael@0 | 334 | TOSTRING_CLASS_RESERVED_SLOT = 0, |
michael@0 | 335 | TOSTRING_NAME_RESERVED_SLOT = 1 |
michael@0 | 336 | }; |
michael@0 | 337 | |
michael@0 | 338 | static bool |
michael@0 | 339 | InterfaceObjectToString(JSContext* cx, unsigned argc, JS::Value *vp) |
michael@0 | 340 | { |
michael@0 | 341 | JS::CallArgs args = JS::CallArgsFromVp(argc, vp); |
michael@0 | 342 | JS::Rooted<JSObject*> callee(cx, &args.callee()); |
michael@0 | 343 | |
michael@0 | 344 | if (!args.thisv().isObject()) { |
michael@0 | 345 | JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, |
michael@0 | 346 | JSMSG_CANT_CONVERT_TO, "null", "object"); |
michael@0 | 347 | return false; |
michael@0 | 348 | } |
michael@0 | 349 | |
michael@0 | 350 | JS::Value v = js::GetFunctionNativeReserved(callee, |
michael@0 | 351 | TOSTRING_CLASS_RESERVED_SLOT); |
michael@0 | 352 | const JSClass* clasp = static_cast<const JSClass*>(v.toPrivate()); |
michael@0 | 353 | |
michael@0 | 354 | v = js::GetFunctionNativeReserved(callee, TOSTRING_NAME_RESERVED_SLOT); |
michael@0 | 355 | JSString* jsname = static_cast<JSString*>(JSVAL_TO_STRING(v)); |
michael@0 | 356 | size_t length; |
michael@0 | 357 | const jschar* name = JS_GetInternedStringCharsAndLength(jsname, &length); |
michael@0 | 358 | |
michael@0 | 359 | if (js::GetObjectJSClass(&args.thisv().toObject()) != clasp) { |
michael@0 | 360 | JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, |
michael@0 | 361 | JSMSG_INCOMPATIBLE_PROTO, |
michael@0 | 362 | NS_ConvertUTF16toUTF8(name).get(), "toString", |
michael@0 | 363 | "object"); |
michael@0 | 364 | return false; |
michael@0 | 365 | } |
michael@0 | 366 | |
michael@0 | 367 | nsString str; |
michael@0 | 368 | str.AppendLiteral("function "); |
michael@0 | 369 | str.Append(name, length); |
michael@0 | 370 | str.AppendLiteral("() {"); |
michael@0 | 371 | str.Append('\n'); |
michael@0 | 372 | str.AppendLiteral(" [native code]"); |
michael@0 | 373 | str.Append('\n'); |
michael@0 | 374 | str.AppendLiteral("}"); |
michael@0 | 375 | |
michael@0 | 376 | return xpc::NonVoidStringToJsval(cx, str, args.rval()); |
michael@0 | 377 | } |
michael@0 | 378 | |
michael@0 | 379 | bool |
michael@0 | 380 | Constructor(JSContext* cx, unsigned argc, JS::Value* vp) |
michael@0 | 381 | { |
michael@0 | 382 | JS::CallArgs args = JS::CallArgsFromVp(argc, vp); |
michael@0 | 383 | const JS::Value& v = |
michael@0 | 384 | js::GetFunctionNativeReserved(&args.callee(), |
michael@0 | 385 | CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT); |
michael@0 | 386 | const JSNativeHolder* nativeHolder = |
michael@0 | 387 | static_cast<const JSNativeHolder*>(v.toPrivate()); |
michael@0 | 388 | return (nativeHolder->mNative)(cx, argc, vp); |
michael@0 | 389 | } |
michael@0 | 390 | |
michael@0 | 391 | static JSObject* |
michael@0 | 392 | CreateConstructor(JSContext* cx, JS::Handle<JSObject*> global, const char* name, |
michael@0 | 393 | const JSNativeHolder* nativeHolder, unsigned ctorNargs) |
michael@0 | 394 | { |
michael@0 | 395 | JSFunction* fun = js::NewFunctionWithReserved(cx, Constructor, ctorNargs, |
michael@0 | 396 | JSFUN_CONSTRUCTOR, global, |
michael@0 | 397 | name); |
michael@0 | 398 | if (!fun) { |
michael@0 | 399 | return nullptr; |
michael@0 | 400 | } |
michael@0 | 401 | |
michael@0 | 402 | JSObject* constructor = JS_GetFunctionObject(fun); |
michael@0 | 403 | js::SetFunctionNativeReserved(constructor, |
michael@0 | 404 | CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT, |
michael@0 | 405 | js::PrivateValue(const_cast<JSNativeHolder*>(nativeHolder))); |
michael@0 | 406 | return constructor; |
michael@0 | 407 | } |
michael@0 | 408 | |
michael@0 | 409 | static bool |
michael@0 | 410 | DefineConstructor(JSContext* cx, JS::Handle<JSObject*> global, const char* name, |
michael@0 | 411 | JS::Handle<JSObject*> constructor) |
michael@0 | 412 | { |
michael@0 | 413 | bool alreadyDefined; |
michael@0 | 414 | if (!JS_AlreadyHasOwnProperty(cx, global, name, &alreadyDefined)) { |
michael@0 | 415 | return false; |
michael@0 | 416 | } |
michael@0 | 417 | |
michael@0 | 418 | // This is Enumerable: False per spec. |
michael@0 | 419 | return alreadyDefined || |
michael@0 | 420 | JS_DefineProperty(cx, global, name, constructor, 0); |
michael@0 | 421 | } |
michael@0 | 422 | |
michael@0 | 423 | static JSObject* |
michael@0 | 424 | CreateInterfaceObject(JSContext* cx, JS::Handle<JSObject*> global, |
michael@0 | 425 | JS::Handle<JSObject*> constructorProto, |
michael@0 | 426 | const JSClass* constructorClass, |
michael@0 | 427 | const JSNativeHolder* constructorNative, |
michael@0 | 428 | unsigned ctorNargs, const NamedConstructor* namedConstructors, |
michael@0 | 429 | JS::Handle<JSObject*> proto, |
michael@0 | 430 | const NativeProperties* properties, |
michael@0 | 431 | const NativeProperties* chromeOnlyProperties, |
michael@0 | 432 | const char* name, bool defineOnGlobal) |
michael@0 | 433 | { |
michael@0 | 434 | JS::Rooted<JSObject*> constructor(cx); |
michael@0 | 435 | if (constructorClass) { |
michael@0 | 436 | MOZ_ASSERT(constructorProto); |
michael@0 | 437 | constructor = JS_NewObject(cx, constructorClass, constructorProto, global); |
michael@0 | 438 | } else { |
michael@0 | 439 | MOZ_ASSERT(constructorNative); |
michael@0 | 440 | MOZ_ASSERT(constructorProto == JS_GetFunctionPrototype(cx, global)); |
michael@0 | 441 | constructor = CreateConstructor(cx, global, name, constructorNative, |
michael@0 | 442 | ctorNargs); |
michael@0 | 443 | } |
michael@0 | 444 | if (!constructor) { |
michael@0 | 445 | return nullptr; |
michael@0 | 446 | } |
michael@0 | 447 | |
michael@0 | 448 | if (constructorClass) { |
michael@0 | 449 | // Have to shadow Function.prototype.toString, since that throws |
michael@0 | 450 | // on things that are not js::FunctionClass. |
michael@0 | 451 | JS::Rooted<JSFunction*> toString(cx, |
michael@0 | 452 | js::DefineFunctionWithReserved(cx, constructor, |
michael@0 | 453 | "toString", |
michael@0 | 454 | InterfaceObjectToString, |
michael@0 | 455 | 0, 0)); |
michael@0 | 456 | if (!toString) { |
michael@0 | 457 | return nullptr; |
michael@0 | 458 | } |
michael@0 | 459 | |
michael@0 | 460 | JSString *str = ::JS_InternString(cx, name); |
michael@0 | 461 | if (!str) { |
michael@0 | 462 | return nullptr; |
michael@0 | 463 | } |
michael@0 | 464 | JSObject* toStringObj = JS_GetFunctionObject(toString); |
michael@0 | 465 | js::SetFunctionNativeReserved(toStringObj, TOSTRING_CLASS_RESERVED_SLOT, |
michael@0 | 466 | PRIVATE_TO_JSVAL(const_cast<JSClass *>(constructorClass))); |
michael@0 | 467 | |
michael@0 | 468 | js::SetFunctionNativeReserved(toStringObj, TOSTRING_NAME_RESERVED_SLOT, |
michael@0 | 469 | STRING_TO_JSVAL(str)); |
michael@0 | 470 | |
michael@0 | 471 | if (!JS_DefineProperty(cx, constructor, "length", ctorNargs, |
michael@0 | 472 | JSPROP_READONLY | JSPROP_PERMANENT)) { |
michael@0 | 473 | return nullptr; |
michael@0 | 474 | } |
michael@0 | 475 | } |
michael@0 | 476 | |
michael@0 | 477 | if (properties) { |
michael@0 | 478 | if (properties->staticMethods && |
michael@0 | 479 | !DefinePrefable(cx, constructor, properties->staticMethods)) { |
michael@0 | 480 | return nullptr; |
michael@0 | 481 | } |
michael@0 | 482 | |
michael@0 | 483 | if (properties->staticAttributes && |
michael@0 | 484 | !DefinePrefable(cx, constructor, properties->staticAttributes)) { |
michael@0 | 485 | return nullptr; |
michael@0 | 486 | } |
michael@0 | 487 | |
michael@0 | 488 | if (properties->constants && |
michael@0 | 489 | !DefinePrefable(cx, constructor, properties->constants)) { |
michael@0 | 490 | return nullptr; |
michael@0 | 491 | } |
michael@0 | 492 | } |
michael@0 | 493 | |
michael@0 | 494 | if (chromeOnlyProperties) { |
michael@0 | 495 | if (chromeOnlyProperties->staticMethods && |
michael@0 | 496 | !DefinePrefable(cx, constructor, chromeOnlyProperties->staticMethods)) { |
michael@0 | 497 | return nullptr; |
michael@0 | 498 | } |
michael@0 | 499 | |
michael@0 | 500 | if (chromeOnlyProperties->staticAttributes && |
michael@0 | 501 | !DefinePrefable(cx, constructor, |
michael@0 | 502 | chromeOnlyProperties->staticAttributes)) { |
michael@0 | 503 | return nullptr; |
michael@0 | 504 | } |
michael@0 | 505 | |
michael@0 | 506 | if (chromeOnlyProperties->constants && |
michael@0 | 507 | !DefinePrefable(cx, constructor, chromeOnlyProperties->constants)) { |
michael@0 | 508 | return nullptr; |
michael@0 | 509 | } |
michael@0 | 510 | } |
michael@0 | 511 | |
michael@0 | 512 | if (proto && !JS_LinkConstructorAndPrototype(cx, constructor, proto)) { |
michael@0 | 513 | return nullptr; |
michael@0 | 514 | } |
michael@0 | 515 | |
michael@0 | 516 | if (defineOnGlobal && !DefineConstructor(cx, global, name, constructor)) { |
michael@0 | 517 | return nullptr; |
michael@0 | 518 | } |
michael@0 | 519 | |
michael@0 | 520 | if (namedConstructors) { |
michael@0 | 521 | int namedConstructorSlot = DOM_INTERFACE_SLOTS_BASE; |
michael@0 | 522 | while (namedConstructors->mName) { |
michael@0 | 523 | JS::Rooted<JSObject*> namedConstructor(cx, |
michael@0 | 524 | CreateConstructor(cx, global, namedConstructors->mName, |
michael@0 | 525 | &namedConstructors->mHolder, |
michael@0 | 526 | namedConstructors->mNargs)); |
michael@0 | 527 | if (!namedConstructor || |
michael@0 | 528 | !JS_DefineProperty(cx, namedConstructor, "prototype", |
michael@0 | 529 | proto, JSPROP_PERMANENT | JSPROP_READONLY, |
michael@0 | 530 | JS_PropertyStub, JS_StrictPropertyStub) || |
michael@0 | 531 | (defineOnGlobal && |
michael@0 | 532 | !DefineConstructor(cx, global, namedConstructors->mName, |
michael@0 | 533 | namedConstructor))) { |
michael@0 | 534 | return nullptr; |
michael@0 | 535 | } |
michael@0 | 536 | js::SetReservedSlot(constructor, namedConstructorSlot++, |
michael@0 | 537 | JS::ObjectValue(*namedConstructor)); |
michael@0 | 538 | ++namedConstructors; |
michael@0 | 539 | } |
michael@0 | 540 | } |
michael@0 | 541 | |
michael@0 | 542 | return constructor; |
michael@0 | 543 | } |
michael@0 | 544 | |
michael@0 | 545 | bool |
michael@0 | 546 | DefineWebIDLBindingPropertiesOnXPCObject(JSContext* cx, |
michael@0 | 547 | JS::Handle<JSObject*> obj, |
michael@0 | 548 | const NativeProperties* properties, |
michael@0 | 549 | bool defineUnforgeableAttributes) |
michael@0 | 550 | { |
michael@0 | 551 | if (properties->methods && |
michael@0 | 552 | !DefinePrefable(cx, obj, properties->methods)) { |
michael@0 | 553 | return false; |
michael@0 | 554 | } |
michael@0 | 555 | |
michael@0 | 556 | if (properties->attributes && |
michael@0 | 557 | !DefinePrefable(cx, obj, properties->attributes)) { |
michael@0 | 558 | return false; |
michael@0 | 559 | } |
michael@0 | 560 | |
michael@0 | 561 | if (defineUnforgeableAttributes && properties->unforgeableAttributes && |
michael@0 | 562 | !DefinePrefable(cx, obj, properties->unforgeableAttributes)) { |
michael@0 | 563 | return false; |
michael@0 | 564 | } |
michael@0 | 565 | |
michael@0 | 566 | return true; |
michael@0 | 567 | } |
michael@0 | 568 | |
michael@0 | 569 | static JSObject* |
michael@0 | 570 | CreateInterfacePrototypeObject(JSContext* cx, JS::Handle<JSObject*> global, |
michael@0 | 571 | JS::Handle<JSObject*> parentProto, |
michael@0 | 572 | const JSClass* protoClass, |
michael@0 | 573 | const NativeProperties* properties, |
michael@0 | 574 | const NativeProperties* chromeOnlyProperties) |
michael@0 | 575 | { |
michael@0 | 576 | JS::Rooted<JSObject*> ourProto(cx, |
michael@0 | 577 | JS_NewObjectWithUniqueType(cx, protoClass, parentProto, global)); |
michael@0 | 578 | if (!ourProto) { |
michael@0 | 579 | return nullptr; |
michael@0 | 580 | } |
michael@0 | 581 | |
michael@0 | 582 | if (properties) { |
michael@0 | 583 | if (properties->methods && |
michael@0 | 584 | !DefinePrefable(cx, ourProto, properties->methods)) { |
michael@0 | 585 | return nullptr; |
michael@0 | 586 | } |
michael@0 | 587 | |
michael@0 | 588 | if (properties->attributes && |
michael@0 | 589 | !DefinePrefable(cx, ourProto, properties->attributes)) { |
michael@0 | 590 | return nullptr; |
michael@0 | 591 | } |
michael@0 | 592 | |
michael@0 | 593 | if (properties->constants && |
michael@0 | 594 | !DefinePrefable(cx, ourProto, properties->constants)) { |
michael@0 | 595 | return nullptr; |
michael@0 | 596 | } |
michael@0 | 597 | } |
michael@0 | 598 | |
michael@0 | 599 | if (chromeOnlyProperties) { |
michael@0 | 600 | if (chromeOnlyProperties->methods && |
michael@0 | 601 | !DefinePrefable(cx, ourProto, chromeOnlyProperties->methods)) { |
michael@0 | 602 | return nullptr; |
michael@0 | 603 | } |
michael@0 | 604 | |
michael@0 | 605 | if (chromeOnlyProperties->attributes && |
michael@0 | 606 | !DefinePrefable(cx, ourProto, chromeOnlyProperties->attributes)) { |
michael@0 | 607 | return nullptr; |
michael@0 | 608 | } |
michael@0 | 609 | |
michael@0 | 610 | if (chromeOnlyProperties->constants && |
michael@0 | 611 | !DefinePrefable(cx, ourProto, chromeOnlyProperties->constants)) { |
michael@0 | 612 | return nullptr; |
michael@0 | 613 | } |
michael@0 | 614 | } |
michael@0 | 615 | |
michael@0 | 616 | return ourProto; |
michael@0 | 617 | } |
michael@0 | 618 | |
michael@0 | 619 | void |
michael@0 | 620 | CreateInterfaceObjects(JSContext* cx, JS::Handle<JSObject*> global, |
michael@0 | 621 | JS::Handle<JSObject*> protoProto, |
michael@0 | 622 | const JSClass* protoClass, JS::Heap<JSObject*>* protoCache, |
michael@0 | 623 | JS::Handle<JSObject*> constructorProto, |
michael@0 | 624 | const JSClass* constructorClass, const JSNativeHolder* constructor, |
michael@0 | 625 | unsigned ctorNargs, const NamedConstructor* namedConstructors, |
michael@0 | 626 | JS::Heap<JSObject*>* constructorCache, const DOMClass* domClass, |
michael@0 | 627 | const NativeProperties* properties, |
michael@0 | 628 | const NativeProperties* chromeOnlyProperties, |
michael@0 | 629 | const char* name, bool defineOnGlobal) |
michael@0 | 630 | { |
michael@0 | 631 | MOZ_ASSERT(protoClass || constructorClass || constructor, |
michael@0 | 632 | "Need at least one class or a constructor!"); |
michael@0 | 633 | MOZ_ASSERT(!((properties && |
michael@0 | 634 | (properties->methods || properties->attributes)) || |
michael@0 | 635 | (chromeOnlyProperties && |
michael@0 | 636 | (chromeOnlyProperties->methods || |
michael@0 | 637 | chromeOnlyProperties->attributes))) || protoClass, |
michael@0 | 638 | "Methods or properties but no protoClass!"); |
michael@0 | 639 | MOZ_ASSERT(!((properties && |
michael@0 | 640 | (properties->staticMethods || properties->staticAttributes)) || |
michael@0 | 641 | (chromeOnlyProperties && |
michael@0 | 642 | (chromeOnlyProperties->staticMethods || |
michael@0 | 643 | chromeOnlyProperties->staticAttributes))) || |
michael@0 | 644 | constructorClass || constructor, |
michael@0 | 645 | "Static methods but no constructorClass or constructor!"); |
michael@0 | 646 | MOZ_ASSERT(bool(name) == bool(constructorClass || constructor), |
michael@0 | 647 | "Must have name precisely when we have an interface object"); |
michael@0 | 648 | MOZ_ASSERT(!constructorClass || !constructor); |
michael@0 | 649 | MOZ_ASSERT(!protoClass == !protoCache, |
michael@0 | 650 | "If, and only if, there is an interface prototype object we need " |
michael@0 | 651 | "to cache it"); |
michael@0 | 652 | MOZ_ASSERT(!(constructorClass || constructor) == !constructorCache, |
michael@0 | 653 | "If, and only if, there is an interface object we need to cache " |
michael@0 | 654 | "it"); |
michael@0 | 655 | |
michael@0 | 656 | JS::Rooted<JSObject*> proto(cx); |
michael@0 | 657 | if (protoClass) { |
michael@0 | 658 | proto = |
michael@0 | 659 | CreateInterfacePrototypeObject(cx, global, protoProto, protoClass, |
michael@0 | 660 | properties, chromeOnlyProperties); |
michael@0 | 661 | if (!proto) { |
michael@0 | 662 | return; |
michael@0 | 663 | } |
michael@0 | 664 | |
michael@0 | 665 | js::SetReservedSlot(proto, DOM_PROTO_INSTANCE_CLASS_SLOT, |
michael@0 | 666 | JS::PrivateValue(const_cast<DOMClass*>(domClass))); |
michael@0 | 667 | |
michael@0 | 668 | *protoCache = proto; |
michael@0 | 669 | } |
michael@0 | 670 | else { |
michael@0 | 671 | MOZ_ASSERT(!proto); |
michael@0 | 672 | } |
michael@0 | 673 | |
michael@0 | 674 | JSObject* interface; |
michael@0 | 675 | if (constructorClass || constructor) { |
michael@0 | 676 | interface = CreateInterfaceObject(cx, global, constructorProto, |
michael@0 | 677 | constructorClass, constructor, |
michael@0 | 678 | ctorNargs, namedConstructors, proto, |
michael@0 | 679 | properties, chromeOnlyProperties, name, |
michael@0 | 680 | defineOnGlobal); |
michael@0 | 681 | if (!interface) { |
michael@0 | 682 | if (protoCache) { |
michael@0 | 683 | // If we fail we need to make sure to clear the value of protoCache we |
michael@0 | 684 | // set above. |
michael@0 | 685 | *protoCache = nullptr; |
michael@0 | 686 | } |
michael@0 | 687 | return; |
michael@0 | 688 | } |
michael@0 | 689 | *constructorCache = interface; |
michael@0 | 690 | } |
michael@0 | 691 | } |
michael@0 | 692 | |
michael@0 | 693 | bool |
michael@0 | 694 | NativeInterface2JSObjectAndThrowIfFailed(JSContext* aCx, |
michael@0 | 695 | JS::Handle<JSObject*> aScope, |
michael@0 | 696 | JS::MutableHandle<JS::Value> aRetval, |
michael@0 | 697 | xpcObjectHelper& aHelper, |
michael@0 | 698 | const nsIID* aIID, |
michael@0 | 699 | bool aAllowNativeWrapper) |
michael@0 | 700 | { |
michael@0 | 701 | js::AssertSameCompartment(aCx, aScope); |
michael@0 | 702 | nsresult rv; |
michael@0 | 703 | // Inline some logic from XPCConvert::NativeInterfaceToJSObject that we need |
michael@0 | 704 | // on all threads. |
michael@0 | 705 | nsWrapperCache *cache = aHelper.GetWrapperCache(); |
michael@0 | 706 | |
michael@0 | 707 | if (cache && cache->IsDOMBinding()) { |
michael@0 | 708 | JS::Rooted<JSObject*> obj(aCx, cache->GetWrapper()); |
michael@0 | 709 | if (!obj) { |
michael@0 | 710 | obj = cache->WrapObject(aCx); |
michael@0 | 711 | } |
michael@0 | 712 | |
michael@0 | 713 | if (obj && aAllowNativeWrapper && !JS_WrapObject(aCx, &obj)) { |
michael@0 | 714 | return false; |
michael@0 | 715 | } |
michael@0 | 716 | |
michael@0 | 717 | if (obj) { |
michael@0 | 718 | aRetval.setObject(*obj); |
michael@0 | 719 | return true; |
michael@0 | 720 | } |
michael@0 | 721 | } |
michael@0 | 722 | |
michael@0 | 723 | MOZ_ASSERT(NS_IsMainThread()); |
michael@0 | 724 | |
michael@0 | 725 | if (!XPCConvert::NativeInterface2JSObject(aRetval, nullptr, aHelper, aIID, |
michael@0 | 726 | nullptr, aAllowNativeWrapper, &rv)) { |
michael@0 | 727 | // I can't tell if NativeInterface2JSObject throws JS exceptions |
michael@0 | 728 | // or not. This is a sloppy stab at the right semantics; the |
michael@0 | 729 | // method really ought to be fixed to behave consistently. |
michael@0 | 730 | if (!JS_IsExceptionPending(aCx)) { |
michael@0 | 731 | Throw(aCx, NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED); |
michael@0 | 732 | } |
michael@0 | 733 | return false; |
michael@0 | 734 | } |
michael@0 | 735 | return true; |
michael@0 | 736 | } |
michael@0 | 737 | |
michael@0 | 738 | bool |
michael@0 | 739 | TryPreserveWrapper(JSObject* obj) |
michael@0 | 740 | { |
michael@0 | 741 | MOZ_ASSERT(IsDOMObject(obj)); |
michael@0 | 742 | |
michael@0 | 743 | if (nsISupports* native = UnwrapDOMObjectToISupports(obj)) { |
michael@0 | 744 | nsWrapperCache* cache = nullptr; |
michael@0 | 745 | CallQueryInterface(native, &cache); |
michael@0 | 746 | if (cache) { |
michael@0 | 747 | cache->PreserveWrapper(native); |
michael@0 | 748 | } |
michael@0 | 749 | return true; |
michael@0 | 750 | } |
michael@0 | 751 | |
michael@0 | 752 | // If this DOMClass is not cycle collected, then it isn't wrappercached, |
michael@0 | 753 | // so it does not need to be preserved. If it is cycle collected, then |
michael@0 | 754 | // we can't tell if it is wrappercached or not, so we just return false. |
michael@0 | 755 | const DOMClass* domClass = GetDOMClass(obj); |
michael@0 | 756 | return domClass && !domClass->mParticipant; |
michael@0 | 757 | } |
michael@0 | 758 | |
michael@0 | 759 | // Can only be called with the immediate prototype of the instance object. Can |
michael@0 | 760 | // only be called on the prototype of an object known to be a DOM instance. |
michael@0 | 761 | bool |
michael@0 | 762 | InstanceClassHasProtoAtDepth(JSObject* protoObject, uint32_t protoID, |
michael@0 | 763 | uint32_t depth) |
michael@0 | 764 | { |
michael@0 | 765 | const DOMClass* domClass = static_cast<const DOMClass*>( |
michael@0 | 766 | js::GetReservedSlot(protoObject, DOM_PROTO_INSTANCE_CLASS_SLOT).toPrivate()); |
michael@0 | 767 | return (uint32_t)domClass->mInterfaceChain[depth] == protoID; |
michael@0 | 768 | } |
michael@0 | 769 | |
michael@0 | 770 | // Only set allowNativeWrapper to false if you really know you need it, if in |
michael@0 | 771 | // doubt use true. Setting it to false disables security wrappers. |
michael@0 | 772 | bool |
michael@0 | 773 | XPCOMObjectToJsval(JSContext* cx, JS::Handle<JSObject*> scope, |
michael@0 | 774 | xpcObjectHelper& helper, const nsIID* iid, |
michael@0 | 775 | bool allowNativeWrapper, JS::MutableHandle<JS::Value> rval) |
michael@0 | 776 | { |
michael@0 | 777 | if (!NativeInterface2JSObjectAndThrowIfFailed(cx, scope, rval, helper, iid, |
michael@0 | 778 | allowNativeWrapper)) { |
michael@0 | 779 | return false; |
michael@0 | 780 | } |
michael@0 | 781 | |
michael@0 | 782 | #ifdef DEBUG |
michael@0 | 783 | JSObject* jsobj = rval.toObjectOrNull(); |
michael@0 | 784 | if (jsobj && !js::GetObjectParent(jsobj)) |
michael@0 | 785 | NS_ASSERTION(js::GetObjectClass(jsobj)->flags & JSCLASS_IS_GLOBAL, |
michael@0 | 786 | "Why did we recreate this wrapper?"); |
michael@0 | 787 | #endif |
michael@0 | 788 | |
michael@0 | 789 | return true; |
michael@0 | 790 | } |
michael@0 | 791 | |
michael@0 | 792 | bool |
michael@0 | 793 | VariantToJsval(JSContext* aCx, nsIVariant* aVariant, |
michael@0 | 794 | JS::MutableHandle<JS::Value> aRetval) |
michael@0 | 795 | { |
michael@0 | 796 | nsresult rv; |
michael@0 | 797 | if (!XPCVariant::VariantDataToJS(aVariant, &rv, aRetval)) { |
michael@0 | 798 | // Does it throw? Who knows |
michael@0 | 799 | if (!JS_IsExceptionPending(aCx)) { |
michael@0 | 800 | Throw(aCx, NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED); |
michael@0 | 801 | } |
michael@0 | 802 | return false; |
michael@0 | 803 | } |
michael@0 | 804 | |
michael@0 | 805 | return true; |
michael@0 | 806 | } |
michael@0 | 807 | |
michael@0 | 808 | bool |
michael@0 | 809 | QueryInterface(JSContext* cx, unsigned argc, JS::Value* vp) |
michael@0 | 810 | { |
michael@0 | 811 | JS::CallArgs args = JS::CallArgsFromVp(argc, vp); |
michael@0 | 812 | JS::Rooted<JS::Value> thisv(cx, JS_THIS(cx, vp)); |
michael@0 | 813 | if (thisv.isNull()) |
michael@0 | 814 | return false; |
michael@0 | 815 | |
michael@0 | 816 | // Get the object. It might be a security wrapper, in which case we do a checked |
michael@0 | 817 | // unwrap. |
michael@0 | 818 | JS::Rooted<JSObject*> origObj(cx, &thisv.toObject()); |
michael@0 | 819 | JSObject* obj = js::CheckedUnwrap(origObj, /* stopAtOuter = */ false); |
michael@0 | 820 | if (!obj) { |
michael@0 | 821 | JS_ReportError(cx, "Permission denied to access object"); |
michael@0 | 822 | return false; |
michael@0 | 823 | } |
michael@0 | 824 | |
michael@0 | 825 | // Switch this to UnwrapDOMObjectToISupports once our global objects are |
michael@0 | 826 | // using new bindings. |
michael@0 | 827 | JS::Rooted<JS::Value> val(cx, JS::ObjectValue(*obj)); |
michael@0 | 828 | nsISupports* native = nullptr; |
michael@0 | 829 | nsCOMPtr<nsISupports> nativeRef; |
michael@0 | 830 | xpc_qsUnwrapArg<nsISupports>(cx, val, &native, |
michael@0 | 831 | static_cast<nsISupports**>(getter_AddRefs(nativeRef)), |
michael@0 | 832 | &val); |
michael@0 | 833 | if (!native) { |
michael@0 | 834 | return Throw(cx, NS_ERROR_FAILURE); |
michael@0 | 835 | } |
michael@0 | 836 | |
michael@0 | 837 | if (argc < 1) { |
michael@0 | 838 | return Throw(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS); |
michael@0 | 839 | } |
michael@0 | 840 | |
michael@0 | 841 | if (!args[0].isObject()) { |
michael@0 | 842 | return Throw(cx, NS_ERROR_XPC_BAD_CONVERT_JS); |
michael@0 | 843 | } |
michael@0 | 844 | |
michael@0 | 845 | nsIJSID* iid; |
michael@0 | 846 | SelfRef iidRef; |
michael@0 | 847 | if (NS_FAILED(xpc_qsUnwrapArg<nsIJSID>(cx, args[0], &iid, &iidRef.ptr, |
michael@0 | 848 | args[0]))) { |
michael@0 | 849 | return Throw(cx, NS_ERROR_XPC_BAD_CONVERT_JS); |
michael@0 | 850 | } |
michael@0 | 851 | MOZ_ASSERT(iid); |
michael@0 | 852 | |
michael@0 | 853 | if (iid->GetID()->Equals(NS_GET_IID(nsIClassInfo))) { |
michael@0 | 854 | nsresult rv; |
michael@0 | 855 | nsCOMPtr<nsIClassInfo> ci = do_QueryInterface(native, &rv); |
michael@0 | 856 | if (NS_FAILED(rv)) { |
michael@0 | 857 | return Throw(cx, rv); |
michael@0 | 858 | } |
michael@0 | 859 | |
michael@0 | 860 | return WrapObject(cx, ci, &NS_GET_IID(nsIClassInfo), args.rval()); |
michael@0 | 861 | } |
michael@0 | 862 | |
michael@0 | 863 | nsCOMPtr<nsISupports> unused; |
michael@0 | 864 | nsresult rv = native->QueryInterface(*iid->GetID(), getter_AddRefs(unused)); |
michael@0 | 865 | if (NS_FAILED(rv)) { |
michael@0 | 866 | return Throw(cx, rv); |
michael@0 | 867 | } |
michael@0 | 868 | |
michael@0 | 869 | *vp = thisv; |
michael@0 | 870 | return true; |
michael@0 | 871 | } |
michael@0 | 872 | |
michael@0 | 873 | void |
michael@0 | 874 | GetInterfaceImpl(JSContext* aCx, nsIInterfaceRequestor* aRequestor, |
michael@0 | 875 | nsWrapperCache* aCache, nsIJSID* aIID, |
michael@0 | 876 | JS::MutableHandle<JS::Value> aRetval, ErrorResult& aError) |
michael@0 | 877 | { |
michael@0 | 878 | const nsID* iid = aIID->GetID(); |
michael@0 | 879 | |
michael@0 | 880 | nsRefPtr<nsISupports> result; |
michael@0 | 881 | aError = aRequestor->GetInterface(*iid, getter_AddRefs(result)); |
michael@0 | 882 | if (aError.Failed()) { |
michael@0 | 883 | return; |
michael@0 | 884 | } |
michael@0 | 885 | |
michael@0 | 886 | if (!WrapObject(aCx, result, iid, aRetval)) { |
michael@0 | 887 | aError.Throw(NS_ERROR_FAILURE); |
michael@0 | 888 | } |
michael@0 | 889 | } |
michael@0 | 890 | |
michael@0 | 891 | bool |
michael@0 | 892 | ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp) |
michael@0 | 893 | { |
michael@0 | 894 | return ThrowErrorMessage(cx, MSG_ILLEGAL_CONSTRUCTOR); |
michael@0 | 895 | } |
michael@0 | 896 | |
michael@0 | 897 | bool |
michael@0 | 898 | ThrowConstructorWithoutNew(JSContext* cx, const char* name) |
michael@0 | 899 | { |
michael@0 | 900 | return ThrowErrorMessage(cx, MSG_CONSTRUCTOR_WITHOUT_NEW, name); |
michael@0 | 901 | } |
michael@0 | 902 | |
michael@0 | 903 | inline const NativePropertyHooks* |
michael@0 | 904 | GetNativePropertyHooks(JSContext *cx, JS::Handle<JSObject*> obj, |
michael@0 | 905 | DOMObjectType& type) |
michael@0 | 906 | { |
michael@0 | 907 | const DOMClass* domClass = GetDOMClass(obj); |
michael@0 | 908 | if (domClass) { |
michael@0 | 909 | type = eInstance; |
michael@0 | 910 | return domClass->mNativeHooks; |
michael@0 | 911 | } |
michael@0 | 912 | |
michael@0 | 913 | if (JS_ObjectIsFunction(cx, obj)) { |
michael@0 | 914 | MOZ_ASSERT(JS_IsNativeFunction(obj, Constructor)); |
michael@0 | 915 | type = eInterface; |
michael@0 | 916 | const JS::Value& v = |
michael@0 | 917 | js::GetFunctionNativeReserved(obj, |
michael@0 | 918 | CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT); |
michael@0 | 919 | const JSNativeHolder* nativeHolder = |
michael@0 | 920 | static_cast<const JSNativeHolder*>(v.toPrivate()); |
michael@0 | 921 | return nativeHolder->mPropertyHooks; |
michael@0 | 922 | } |
michael@0 | 923 | |
michael@0 | 924 | MOZ_ASSERT(IsDOMIfaceAndProtoClass(js::GetObjectClass(obj))); |
michael@0 | 925 | const DOMIfaceAndProtoJSClass* ifaceAndProtoJSClass = |
michael@0 | 926 | DOMIfaceAndProtoJSClass::FromJSClass(js::GetObjectClass(obj)); |
michael@0 | 927 | type = ifaceAndProtoJSClass->mType; |
michael@0 | 928 | return ifaceAndProtoJSClass->mNativeHooks; |
michael@0 | 929 | } |
michael@0 | 930 | |
michael@0 | 931 | // Try to resolve a property as an unforgeable property from the given |
michael@0 | 932 | // NativeProperties, if it's there. nativeProperties is allowed to be null (in |
michael@0 | 933 | // which case we of course won't resolve anything). |
michael@0 | 934 | static bool |
michael@0 | 935 | XrayResolveUnforgeableProperty(JSContext* cx, JS::Handle<JSObject*> wrapper, |
michael@0 | 936 | JS::Handle<JSObject*> obj, JS::Handle<jsid> id, |
michael@0 | 937 | JS::MutableHandle<JSPropertyDescriptor> desc, |
michael@0 | 938 | const NativeProperties* nativeProperties); |
michael@0 | 939 | |
michael@0 | 940 | static bool |
michael@0 | 941 | XrayResolveNativeProperty(JSContext* cx, JS::Handle<JSObject*> wrapper, |
michael@0 | 942 | const NativePropertyHooks* nativePropertyHooks, |
michael@0 | 943 | DOMObjectType type, JS::Handle<JSObject*> obj, |
michael@0 | 944 | JS::Handle<jsid> id, |
michael@0 | 945 | JS::MutableHandle<JSPropertyDescriptor> desc); |
michael@0 | 946 | |
michael@0 | 947 | bool |
michael@0 | 948 | XrayResolveOwnProperty(JSContext* cx, JS::Handle<JSObject*> wrapper, |
michael@0 | 949 | JS::Handle<JSObject*> obj, JS::Handle<jsid> id, |
michael@0 | 950 | JS::MutableHandle<JSPropertyDescriptor> desc) |
michael@0 | 951 | { |
michael@0 | 952 | DOMObjectType type; |
michael@0 | 953 | const NativePropertyHooks *nativePropertyHooks = |
michael@0 | 954 | GetNativePropertyHooks(cx, obj, type); |
michael@0 | 955 | |
michael@0 | 956 | if (type != eInstance) { |
michael@0 | 957 | // For prototype objects and interface objects, just return their |
michael@0 | 958 | // normal set of properties. |
michael@0 | 959 | return XrayResolveNativeProperty(cx, wrapper, nativePropertyHooks, type, |
michael@0 | 960 | obj, id, desc); |
michael@0 | 961 | } |
michael@0 | 962 | |
michael@0 | 963 | // Check for unforgeable properties before doing mResolveOwnProperty weirdness |
michael@0 | 964 | const NativePropertiesHolder& nativeProperties = |
michael@0 | 965 | nativePropertyHooks->mNativeProperties; |
michael@0 | 966 | if (!XrayResolveUnforgeableProperty(cx, wrapper, obj, id, desc, |
michael@0 | 967 | nativeProperties.regular)) { |
michael@0 | 968 | return false; |
michael@0 | 969 | } |
michael@0 | 970 | if (desc.object()) { |
michael@0 | 971 | return true; |
michael@0 | 972 | } |
michael@0 | 973 | if (!XrayResolveUnforgeableProperty(cx, wrapper, obj, id, desc, |
michael@0 | 974 | nativeProperties.chromeOnly)) { |
michael@0 | 975 | return false; |
michael@0 | 976 | } |
michael@0 | 977 | if (desc.object()) { |
michael@0 | 978 | return true; |
michael@0 | 979 | } |
michael@0 | 980 | |
michael@0 | 981 | return !nativePropertyHooks->mResolveOwnProperty || |
michael@0 | 982 | nativePropertyHooks->mResolveOwnProperty(cx, wrapper, obj, id, desc); |
michael@0 | 983 | } |
michael@0 | 984 | |
michael@0 | 985 | static bool |
michael@0 | 986 | XrayResolveAttribute(JSContext* cx, JS::Handle<JSObject*> wrapper, |
michael@0 | 987 | JS::Handle<JSObject*> obj, JS::Handle<jsid> id, |
michael@0 | 988 | const Prefable<const JSPropertySpec>* attributes, jsid* attributeIds, |
michael@0 | 989 | const JSPropertySpec* attributeSpecs, JS::MutableHandle<JSPropertyDescriptor> desc) |
michael@0 | 990 | { |
michael@0 | 991 | for (; attributes->specs; ++attributes) { |
michael@0 | 992 | if (attributes->isEnabled(cx, obj)) { |
michael@0 | 993 | // Set i to be the index into our full list of ids/specs that we're |
michael@0 | 994 | // looking at now. |
michael@0 | 995 | size_t i = attributes->specs - attributeSpecs; |
michael@0 | 996 | for ( ; attributeIds[i] != JSID_VOID; ++i) { |
michael@0 | 997 | if (id == attributeIds[i]) { |
michael@0 | 998 | const JSPropertySpec& attrSpec = attributeSpecs[i]; |
michael@0 | 999 | // Because of centralization, we need to make sure we fault in the |
michael@0 | 1000 | // JitInfos as well. At present, until the JSAPI changes, the easiest |
michael@0 | 1001 | // way to do this is wrap them up as functions ourselves. |
michael@0 | 1002 | desc.setAttributes(attrSpec.flags & ~JSPROP_NATIVE_ACCESSORS); |
michael@0 | 1003 | // They all have getters, so we can just make it. |
michael@0 | 1004 | JS::Rooted<JSFunction*> fun(cx, |
michael@0 | 1005 | JS_NewFunctionById(cx, (JSNative)attrSpec.getter.propertyOp.op, |
michael@0 | 1006 | 0, 0, wrapper, id)); |
michael@0 | 1007 | if (!fun) |
michael@0 | 1008 | return false; |
michael@0 | 1009 | SET_JITINFO(fun, attrSpec.getter.propertyOp.info); |
michael@0 | 1010 | JSObject *funobj = JS_GetFunctionObject(fun); |
michael@0 | 1011 | desc.setGetterObject(funobj); |
michael@0 | 1012 | desc.attributesRef() |= JSPROP_GETTER; |
michael@0 | 1013 | if (attrSpec.setter.propertyOp.op) { |
michael@0 | 1014 | // We have a setter! Make it. |
michael@0 | 1015 | fun = JS_NewFunctionById(cx, (JSNative)attrSpec.setter.propertyOp.op, 1, 0, |
michael@0 | 1016 | wrapper, id); |
michael@0 | 1017 | if (!fun) |
michael@0 | 1018 | return false; |
michael@0 | 1019 | SET_JITINFO(fun, attrSpec.setter.propertyOp.info); |
michael@0 | 1020 | funobj = JS_GetFunctionObject(fun); |
michael@0 | 1021 | desc.setSetterObject(funobj); |
michael@0 | 1022 | desc.attributesRef() |= JSPROP_SETTER; |
michael@0 | 1023 | } else { |
michael@0 | 1024 | desc.setSetter(nullptr); |
michael@0 | 1025 | } |
michael@0 | 1026 | desc.object().set(wrapper); |
michael@0 | 1027 | return true; |
michael@0 | 1028 | } |
michael@0 | 1029 | } |
michael@0 | 1030 | } |
michael@0 | 1031 | } |
michael@0 | 1032 | return true; |
michael@0 | 1033 | } |
michael@0 | 1034 | |
michael@0 | 1035 | /* static */ bool |
michael@0 | 1036 | XrayResolveUnforgeableProperty(JSContext* cx, JS::Handle<JSObject*> wrapper, |
michael@0 | 1037 | JS::Handle<JSObject*> obj, JS::Handle<jsid> id, |
michael@0 | 1038 | JS::MutableHandle<JSPropertyDescriptor> desc, |
michael@0 | 1039 | const NativeProperties* nativeProperties) |
michael@0 | 1040 | { |
michael@0 | 1041 | return !nativeProperties || !nativeProperties->unforgeableAttributes || |
michael@0 | 1042 | XrayResolveAttribute(cx, wrapper, obj, id, |
michael@0 | 1043 | nativeProperties->unforgeableAttributes, |
michael@0 | 1044 | nativeProperties->unforgeableAttributeIds, |
michael@0 | 1045 | nativeProperties->unforgeableAttributeSpecs, |
michael@0 | 1046 | desc); |
michael@0 | 1047 | } |
michael@0 | 1048 | |
michael@0 | 1049 | static bool |
michael@0 | 1050 | XrayResolveProperty(JSContext* cx, JS::Handle<JSObject*> wrapper, |
michael@0 | 1051 | JS::Handle<JSObject*> obj, JS::Handle<jsid> id, |
michael@0 | 1052 | JS::MutableHandle<JSPropertyDescriptor> desc, DOMObjectType type, |
michael@0 | 1053 | const NativeProperties* nativeProperties) |
michael@0 | 1054 | { |
michael@0 | 1055 | const Prefable<const JSFunctionSpec>* methods; |
michael@0 | 1056 | jsid* methodIds; |
michael@0 | 1057 | const JSFunctionSpec* methodsSpecs; |
michael@0 | 1058 | if (type == eInterface) { |
michael@0 | 1059 | methods = nativeProperties->staticMethods; |
michael@0 | 1060 | methodIds = nativeProperties->staticMethodIds; |
michael@0 | 1061 | methodsSpecs = nativeProperties->staticMethodsSpecs; |
michael@0 | 1062 | } else { |
michael@0 | 1063 | methods = nativeProperties->methods; |
michael@0 | 1064 | methodIds = nativeProperties->methodIds; |
michael@0 | 1065 | methodsSpecs = nativeProperties->methodsSpecs; |
michael@0 | 1066 | } |
michael@0 | 1067 | if (methods) { |
michael@0 | 1068 | const Prefable<const JSFunctionSpec>* method; |
michael@0 | 1069 | for (method = methods; method->specs; ++method) { |
michael@0 | 1070 | if (method->isEnabled(cx, obj)) { |
michael@0 | 1071 | // Set i to be the index into our full list of ids/specs that we're |
michael@0 | 1072 | // looking at now. |
michael@0 | 1073 | size_t i = method->specs - methodsSpecs; |
michael@0 | 1074 | for ( ; methodIds[i] != JSID_VOID; ++i) { |
michael@0 | 1075 | if (id == methodIds[i]) { |
michael@0 | 1076 | const JSFunctionSpec& methodSpec = methodsSpecs[i]; |
michael@0 | 1077 | JSFunction *fun; |
michael@0 | 1078 | if (methodSpec.selfHostedName) { |
michael@0 | 1079 | fun = JS::GetSelfHostedFunction(cx, methodSpec.selfHostedName, id, methodSpec.nargs); |
michael@0 | 1080 | if (!fun) { |
michael@0 | 1081 | return false; |
michael@0 | 1082 | } |
michael@0 | 1083 | MOZ_ASSERT(!methodSpec.call.op, "Bad FunctionSpec declaration: non-null native"); |
michael@0 | 1084 | MOZ_ASSERT(!methodSpec.call.info, "Bad FunctionSpec declaration: non-null jitinfo"); |
michael@0 | 1085 | } else { |
michael@0 | 1086 | fun = JS_NewFunctionById(cx, methodSpec.call.op, methodSpec.nargs, 0, wrapper, id); |
michael@0 | 1087 | if (!fun) { |
michael@0 | 1088 | return false; |
michael@0 | 1089 | } |
michael@0 | 1090 | SET_JITINFO(fun, methodSpec.call.info); |
michael@0 | 1091 | } |
michael@0 | 1092 | JSObject *funobj = JS_GetFunctionObject(fun); |
michael@0 | 1093 | desc.value().setObject(*funobj); |
michael@0 | 1094 | desc.setAttributes(methodSpec.flags); |
michael@0 | 1095 | desc.object().set(wrapper); |
michael@0 | 1096 | desc.setSetter(nullptr); |
michael@0 | 1097 | desc.setGetter(nullptr); |
michael@0 | 1098 | return true; |
michael@0 | 1099 | } |
michael@0 | 1100 | } |
michael@0 | 1101 | } |
michael@0 | 1102 | } |
michael@0 | 1103 | } |
michael@0 | 1104 | |
michael@0 | 1105 | if (type == eInterface) { |
michael@0 | 1106 | if (nativeProperties->staticAttributes) { |
michael@0 | 1107 | if (!XrayResolveAttribute(cx, wrapper, obj, id, |
michael@0 | 1108 | nativeProperties->staticAttributes, |
michael@0 | 1109 | nativeProperties->staticAttributeIds, |
michael@0 | 1110 | nativeProperties->staticAttributeSpecs, desc)) { |
michael@0 | 1111 | return false; |
michael@0 | 1112 | } |
michael@0 | 1113 | if (desc.object()) { |
michael@0 | 1114 | return true; |
michael@0 | 1115 | } |
michael@0 | 1116 | } |
michael@0 | 1117 | } else { |
michael@0 | 1118 | if (nativeProperties->attributes) { |
michael@0 | 1119 | if (!XrayResolveAttribute(cx, wrapper, obj, id, |
michael@0 | 1120 | nativeProperties->attributes, |
michael@0 | 1121 | nativeProperties->attributeIds, |
michael@0 | 1122 | nativeProperties->attributeSpecs, desc)) { |
michael@0 | 1123 | return false; |
michael@0 | 1124 | } |
michael@0 | 1125 | if (desc.object()) { |
michael@0 | 1126 | return true; |
michael@0 | 1127 | } |
michael@0 | 1128 | } |
michael@0 | 1129 | } |
michael@0 | 1130 | |
michael@0 | 1131 | if (nativeProperties->constants) { |
michael@0 | 1132 | const Prefable<const ConstantSpec>* constant; |
michael@0 | 1133 | for (constant = nativeProperties->constants; constant->specs; ++constant) { |
michael@0 | 1134 | if (constant->isEnabled(cx, obj)) { |
michael@0 | 1135 | // Set i to be the index into our full list of ids/specs that we're |
michael@0 | 1136 | // looking at now. |
michael@0 | 1137 | size_t i = constant->specs - nativeProperties->constantSpecs; |
michael@0 | 1138 | for ( ; nativeProperties->constantIds[i] != JSID_VOID; ++i) { |
michael@0 | 1139 | if (id == nativeProperties->constantIds[i]) { |
michael@0 | 1140 | desc.setAttributes(JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT); |
michael@0 | 1141 | desc.object().set(wrapper); |
michael@0 | 1142 | desc.value().set(nativeProperties->constantSpecs[i].value); |
michael@0 | 1143 | return true; |
michael@0 | 1144 | } |
michael@0 | 1145 | } |
michael@0 | 1146 | } |
michael@0 | 1147 | } |
michael@0 | 1148 | } |
michael@0 | 1149 | |
michael@0 | 1150 | return true; |
michael@0 | 1151 | } |
michael@0 | 1152 | |
michael@0 | 1153 | static bool |
michael@0 | 1154 | ResolvePrototypeOrConstructor(JSContext* cx, JS::Handle<JSObject*> wrapper, |
michael@0 | 1155 | JS::Handle<JSObject*> obj, |
michael@0 | 1156 | size_t protoAndIfaceCacheIndex, unsigned attrs, |
michael@0 | 1157 | JS::MutableHandle<JSPropertyDescriptor> desc) |
michael@0 | 1158 | { |
michael@0 | 1159 | JS::Rooted<JSObject*> global(cx, js::GetGlobalForObjectCrossCompartment(obj)); |
michael@0 | 1160 | { |
michael@0 | 1161 | JSAutoCompartment ac(cx, global); |
michael@0 | 1162 | ProtoAndIfaceCache& protoAndIfaceCache = *GetProtoAndIfaceCache(global); |
michael@0 | 1163 | JSObject* protoOrIface = |
michael@0 | 1164 | protoAndIfaceCache.EntrySlotIfExists(protoAndIfaceCacheIndex); |
michael@0 | 1165 | if (!protoOrIface) { |
michael@0 | 1166 | return false; |
michael@0 | 1167 | } |
michael@0 | 1168 | desc.object().set(wrapper); |
michael@0 | 1169 | desc.setAttributes(attrs); |
michael@0 | 1170 | desc.setGetter(JS_PropertyStub); |
michael@0 | 1171 | desc.setSetter(JS_StrictPropertyStub); |
michael@0 | 1172 | desc.value().set(JS::ObjectValue(*protoOrIface)); |
michael@0 | 1173 | } |
michael@0 | 1174 | return JS_WrapPropertyDescriptor(cx, desc); |
michael@0 | 1175 | } |
michael@0 | 1176 | |
michael@0 | 1177 | /* static */ bool |
michael@0 | 1178 | XrayResolveNativeProperty(JSContext* cx, JS::Handle<JSObject*> wrapper, |
michael@0 | 1179 | const NativePropertyHooks* nativePropertyHooks, |
michael@0 | 1180 | DOMObjectType type, JS::Handle<JSObject*> obj, |
michael@0 | 1181 | JS::Handle<jsid> id, |
michael@0 | 1182 | JS::MutableHandle<JSPropertyDescriptor> desc) |
michael@0 | 1183 | { |
michael@0 | 1184 | if (type == eInterface && IdEquals(id, "prototype")) { |
michael@0 | 1185 | return nativePropertyHooks->mPrototypeID == prototypes::id::_ID_Count || |
michael@0 | 1186 | ResolvePrototypeOrConstructor(cx, wrapper, obj, |
michael@0 | 1187 | nativePropertyHooks->mPrototypeID, |
michael@0 | 1188 | JSPROP_PERMANENT | JSPROP_READONLY, |
michael@0 | 1189 | desc); |
michael@0 | 1190 | } |
michael@0 | 1191 | |
michael@0 | 1192 | if (type == eInterfacePrototype && IdEquals(id, "constructor")) { |
michael@0 | 1193 | return nativePropertyHooks->mConstructorID == constructors::id::_ID_Count || |
michael@0 | 1194 | ResolvePrototypeOrConstructor(cx, wrapper, obj, |
michael@0 | 1195 | nativePropertyHooks->mConstructorID, |
michael@0 | 1196 | 0, desc); |
michael@0 | 1197 | } |
michael@0 | 1198 | |
michael@0 | 1199 | const NativePropertiesHolder& nativeProperties = |
michael@0 | 1200 | nativePropertyHooks->mNativeProperties; |
michael@0 | 1201 | |
michael@0 | 1202 | if (nativeProperties.regular && |
michael@0 | 1203 | !XrayResolveProperty(cx, wrapper, obj, id, desc, type, |
michael@0 | 1204 | nativeProperties.regular)) { |
michael@0 | 1205 | return false; |
michael@0 | 1206 | } |
michael@0 | 1207 | |
michael@0 | 1208 | if (!desc.object() && |
michael@0 | 1209 | nativeProperties.chromeOnly && |
michael@0 | 1210 | xpc::AccessCheck::isChrome(js::GetObjectCompartment(wrapper)) && |
michael@0 | 1211 | !XrayResolveProperty(cx, wrapper, obj, id, desc, type, |
michael@0 | 1212 | nativeProperties.chromeOnly)) { |
michael@0 | 1213 | return false; |
michael@0 | 1214 | } |
michael@0 | 1215 | |
michael@0 | 1216 | return true; |
michael@0 | 1217 | } |
michael@0 | 1218 | |
michael@0 | 1219 | bool |
michael@0 | 1220 | XrayResolveNativeProperty(JSContext* cx, JS::Handle<JSObject*> wrapper, |
michael@0 | 1221 | JS::Handle<JSObject*> obj, |
michael@0 | 1222 | JS::Handle<jsid> id, JS::MutableHandle<JSPropertyDescriptor> desc) |
michael@0 | 1223 | { |
michael@0 | 1224 | DOMObjectType type; |
michael@0 | 1225 | const NativePropertyHooks* nativePropertyHooks = |
michael@0 | 1226 | GetNativePropertyHooks(cx, obj, type); |
michael@0 | 1227 | |
michael@0 | 1228 | if (type == eInstance) { |
michael@0 | 1229 | // Force the type to be eInterfacePrototype, since we need to walk the |
michael@0 | 1230 | // prototype chain. |
michael@0 | 1231 | type = eInterfacePrototype; |
michael@0 | 1232 | } |
michael@0 | 1233 | |
michael@0 | 1234 | if (type == eInterfacePrototype) { |
michael@0 | 1235 | do { |
michael@0 | 1236 | if (!XrayResolveNativeProperty(cx, wrapper, nativePropertyHooks, type, |
michael@0 | 1237 | obj, id, desc)) { |
michael@0 | 1238 | return false; |
michael@0 | 1239 | } |
michael@0 | 1240 | |
michael@0 | 1241 | if (desc.object()) { |
michael@0 | 1242 | return true; |
michael@0 | 1243 | } |
michael@0 | 1244 | } while ((nativePropertyHooks = nativePropertyHooks->mProtoHooks)); |
michael@0 | 1245 | |
michael@0 | 1246 | return true; |
michael@0 | 1247 | } |
michael@0 | 1248 | |
michael@0 | 1249 | return XrayResolveNativeProperty(cx, wrapper, nativePropertyHooks, type, obj, |
michael@0 | 1250 | id, desc); |
michael@0 | 1251 | } |
michael@0 | 1252 | |
michael@0 | 1253 | bool |
michael@0 | 1254 | XrayDefineProperty(JSContext* cx, JS::Handle<JSObject*> wrapper, |
michael@0 | 1255 | JS::Handle<JSObject*> obj, JS::Handle<jsid> id, |
michael@0 | 1256 | JS::MutableHandle<JSPropertyDescriptor> desc, bool* defined) |
michael@0 | 1257 | { |
michael@0 | 1258 | if (!js::IsProxy(obj)) |
michael@0 | 1259 | return true; |
michael@0 | 1260 | |
michael@0 | 1261 | MOZ_ASSERT(IsDOMProxy(obj), "What kind of proxy is this?"); |
michael@0 | 1262 | |
michael@0 | 1263 | DOMProxyHandler* handler = |
michael@0 | 1264 | static_cast<DOMProxyHandler*>(js::GetProxyHandler(obj)); |
michael@0 | 1265 | return handler->defineProperty(cx, wrapper, id, desc, defined); |
michael@0 | 1266 | } |
michael@0 | 1267 | |
michael@0 | 1268 | bool |
michael@0 | 1269 | XrayEnumerateAttributes(JSContext* cx, JS::Handle<JSObject*> wrapper, |
michael@0 | 1270 | JS::Handle<JSObject*> obj, |
michael@0 | 1271 | const Prefable<const JSPropertySpec>* attributes, |
michael@0 | 1272 | jsid* attributeIds, const JSPropertySpec* attributeSpecs, |
michael@0 | 1273 | unsigned flags, JS::AutoIdVector& props) |
michael@0 | 1274 | { |
michael@0 | 1275 | for (; attributes->specs; ++attributes) { |
michael@0 | 1276 | if (attributes->isEnabled(cx, obj)) { |
michael@0 | 1277 | // Set i to be the index into our full list of ids/specs that we're |
michael@0 | 1278 | // looking at now. |
michael@0 | 1279 | size_t i = attributes->specs - attributeSpecs; |
michael@0 | 1280 | for ( ; attributeIds[i] != JSID_VOID; ++i) { |
michael@0 | 1281 | if (((flags & JSITER_HIDDEN) || |
michael@0 | 1282 | (attributeSpecs[i].flags & JSPROP_ENUMERATE)) && |
michael@0 | 1283 | !props.append(attributeIds[i])) { |
michael@0 | 1284 | return false; |
michael@0 | 1285 | } |
michael@0 | 1286 | } |
michael@0 | 1287 | } |
michael@0 | 1288 | } |
michael@0 | 1289 | return true; |
michael@0 | 1290 | } |
michael@0 | 1291 | |
michael@0 | 1292 | bool |
michael@0 | 1293 | XrayEnumerateProperties(JSContext* cx, JS::Handle<JSObject*> wrapper, |
michael@0 | 1294 | JS::Handle<JSObject*> obj, |
michael@0 | 1295 | unsigned flags, JS::AutoIdVector& props, |
michael@0 | 1296 | DOMObjectType type, |
michael@0 | 1297 | const NativeProperties* nativeProperties) |
michael@0 | 1298 | { |
michael@0 | 1299 | const Prefable<const JSFunctionSpec>* methods; |
michael@0 | 1300 | jsid* methodIds; |
michael@0 | 1301 | const JSFunctionSpec* methodsSpecs; |
michael@0 | 1302 | if (type == eInterface) { |
michael@0 | 1303 | methods = nativeProperties->staticMethods; |
michael@0 | 1304 | methodIds = nativeProperties->staticMethodIds; |
michael@0 | 1305 | methodsSpecs = nativeProperties->staticMethodsSpecs; |
michael@0 | 1306 | } else { |
michael@0 | 1307 | methods = nativeProperties->methods; |
michael@0 | 1308 | methodIds = nativeProperties->methodIds; |
michael@0 | 1309 | methodsSpecs = nativeProperties->methodsSpecs; |
michael@0 | 1310 | } |
michael@0 | 1311 | if (methods) { |
michael@0 | 1312 | const Prefable<const JSFunctionSpec>* method; |
michael@0 | 1313 | for (method = methods; method->specs; ++method) { |
michael@0 | 1314 | if (method->isEnabled(cx, obj)) { |
michael@0 | 1315 | // Set i to be the index into our full list of ids/specs that we're |
michael@0 | 1316 | // looking at now. |
michael@0 | 1317 | size_t i = method->specs - methodsSpecs; |
michael@0 | 1318 | for ( ; methodIds[i] != JSID_VOID; ++i) { |
michael@0 | 1319 | if (((flags & JSITER_HIDDEN) || |
michael@0 | 1320 | (methodsSpecs[i].flags & JSPROP_ENUMERATE)) && |
michael@0 | 1321 | !props.append(methodIds[i])) { |
michael@0 | 1322 | return false; |
michael@0 | 1323 | } |
michael@0 | 1324 | } |
michael@0 | 1325 | } |
michael@0 | 1326 | } |
michael@0 | 1327 | } |
michael@0 | 1328 | |
michael@0 | 1329 | if (type == eInterface) { |
michael@0 | 1330 | if (nativeProperties->staticAttributes && |
michael@0 | 1331 | !XrayEnumerateAttributes(cx, wrapper, obj, |
michael@0 | 1332 | nativeProperties->staticAttributes, |
michael@0 | 1333 | nativeProperties->staticAttributeIds, |
michael@0 | 1334 | nativeProperties->staticAttributeSpecs, |
michael@0 | 1335 | flags, props)) { |
michael@0 | 1336 | return false; |
michael@0 | 1337 | } |
michael@0 | 1338 | } else { |
michael@0 | 1339 | if (nativeProperties->attributes && |
michael@0 | 1340 | !XrayEnumerateAttributes(cx, wrapper, obj, |
michael@0 | 1341 | nativeProperties->attributes, |
michael@0 | 1342 | nativeProperties->attributeIds, |
michael@0 | 1343 | nativeProperties->attributeSpecs, |
michael@0 | 1344 | flags, props)) { |
michael@0 | 1345 | return false; |
michael@0 | 1346 | } |
michael@0 | 1347 | if (nativeProperties->unforgeableAttributes && |
michael@0 | 1348 | !XrayEnumerateAttributes(cx, wrapper, obj, |
michael@0 | 1349 | nativeProperties->unforgeableAttributes, |
michael@0 | 1350 | nativeProperties->unforgeableAttributeIds, |
michael@0 | 1351 | nativeProperties->unforgeableAttributeSpecs, |
michael@0 | 1352 | flags, props)) { |
michael@0 | 1353 | return false; |
michael@0 | 1354 | } |
michael@0 | 1355 | } |
michael@0 | 1356 | |
michael@0 | 1357 | if (nativeProperties->constants) { |
michael@0 | 1358 | const Prefable<const ConstantSpec>* constant; |
michael@0 | 1359 | for (constant = nativeProperties->constants; constant->specs; ++constant) { |
michael@0 | 1360 | if (constant->isEnabled(cx, obj)) { |
michael@0 | 1361 | // Set i to be the index into our full list of ids/specs that we're |
michael@0 | 1362 | // looking at now. |
michael@0 | 1363 | size_t i = constant->specs - nativeProperties->constantSpecs; |
michael@0 | 1364 | for ( ; nativeProperties->constantIds[i] != JSID_VOID; ++i) { |
michael@0 | 1365 | if (!props.append(nativeProperties->constantIds[i])) { |
michael@0 | 1366 | return false; |
michael@0 | 1367 | } |
michael@0 | 1368 | } |
michael@0 | 1369 | } |
michael@0 | 1370 | } |
michael@0 | 1371 | } |
michael@0 | 1372 | |
michael@0 | 1373 | return true; |
michael@0 | 1374 | } |
michael@0 | 1375 | |
michael@0 | 1376 | bool |
michael@0 | 1377 | XrayEnumerateNativeProperties(JSContext* cx, JS::Handle<JSObject*> wrapper, |
michael@0 | 1378 | const NativePropertyHooks* nativePropertyHooks, |
michael@0 | 1379 | DOMObjectType type, JS::Handle<JSObject*> obj, |
michael@0 | 1380 | unsigned flags, JS::AutoIdVector& props) |
michael@0 | 1381 | { |
michael@0 | 1382 | if (type == eInterface && |
michael@0 | 1383 | nativePropertyHooks->mPrototypeID != prototypes::id::_ID_Count && |
michael@0 | 1384 | !AddStringToIDVector(cx, props, "prototype")) { |
michael@0 | 1385 | return false; |
michael@0 | 1386 | } |
michael@0 | 1387 | |
michael@0 | 1388 | if (type == eInterfacePrototype && |
michael@0 | 1389 | nativePropertyHooks->mConstructorID != constructors::id::_ID_Count && |
michael@0 | 1390 | (flags & JSITER_HIDDEN) && |
michael@0 | 1391 | !AddStringToIDVector(cx, props, "constructor")) { |
michael@0 | 1392 | return false; |
michael@0 | 1393 | } |
michael@0 | 1394 | |
michael@0 | 1395 | const NativePropertiesHolder& nativeProperties = |
michael@0 | 1396 | nativePropertyHooks->mNativeProperties; |
michael@0 | 1397 | |
michael@0 | 1398 | if (nativeProperties.regular && |
michael@0 | 1399 | !XrayEnumerateProperties(cx, wrapper, obj, flags, props, type, |
michael@0 | 1400 | nativeProperties.regular)) { |
michael@0 | 1401 | return false; |
michael@0 | 1402 | } |
michael@0 | 1403 | |
michael@0 | 1404 | if (nativeProperties.chromeOnly && |
michael@0 | 1405 | xpc::AccessCheck::isChrome(js::GetObjectCompartment(wrapper)) && |
michael@0 | 1406 | !XrayEnumerateProperties(cx, wrapper, obj, flags, props, type, |
michael@0 | 1407 | nativeProperties.chromeOnly)) { |
michael@0 | 1408 | return false; |
michael@0 | 1409 | } |
michael@0 | 1410 | |
michael@0 | 1411 | return true; |
michael@0 | 1412 | } |
michael@0 | 1413 | |
michael@0 | 1414 | bool |
michael@0 | 1415 | XrayEnumerateProperties(JSContext* cx, JS::Handle<JSObject*> wrapper, |
michael@0 | 1416 | JS::Handle<JSObject*> obj, |
michael@0 | 1417 | unsigned flags, JS::AutoIdVector& props) |
michael@0 | 1418 | { |
michael@0 | 1419 | DOMObjectType type; |
michael@0 | 1420 | const NativePropertyHooks* nativePropertyHooks = |
michael@0 | 1421 | GetNativePropertyHooks(cx, obj, type); |
michael@0 | 1422 | |
michael@0 | 1423 | if (type == eInstance) { |
michael@0 | 1424 | if (nativePropertyHooks->mEnumerateOwnProperties && |
michael@0 | 1425 | !nativePropertyHooks->mEnumerateOwnProperties(cx, wrapper, obj, |
michael@0 | 1426 | props)) { |
michael@0 | 1427 | return false; |
michael@0 | 1428 | } |
michael@0 | 1429 | |
michael@0 | 1430 | if (flags & JSITER_OWNONLY) { |
michael@0 | 1431 | return true; |
michael@0 | 1432 | } |
michael@0 | 1433 | |
michael@0 | 1434 | // Force the type to be eInterfacePrototype, since we need to walk the |
michael@0 | 1435 | // prototype chain. |
michael@0 | 1436 | type = eInterfacePrototype; |
michael@0 | 1437 | } |
michael@0 | 1438 | |
michael@0 | 1439 | if (type == eInterfacePrototype) { |
michael@0 | 1440 | do { |
michael@0 | 1441 | if (!XrayEnumerateNativeProperties(cx, wrapper, nativePropertyHooks, type, |
michael@0 | 1442 | obj, flags, props)) { |
michael@0 | 1443 | return false; |
michael@0 | 1444 | } |
michael@0 | 1445 | |
michael@0 | 1446 | if (flags & JSITER_OWNONLY) { |
michael@0 | 1447 | return true; |
michael@0 | 1448 | } |
michael@0 | 1449 | } while ((nativePropertyHooks = nativePropertyHooks->mProtoHooks)); |
michael@0 | 1450 | |
michael@0 | 1451 | return true; |
michael@0 | 1452 | } |
michael@0 | 1453 | |
michael@0 | 1454 | return XrayEnumerateNativeProperties(cx, wrapper, nativePropertyHooks, type, |
michael@0 | 1455 | obj, flags, props); |
michael@0 | 1456 | } |
michael@0 | 1457 | |
michael@0 | 1458 | NativePropertyHooks sWorkerNativePropertyHooks = { |
michael@0 | 1459 | nullptr, |
michael@0 | 1460 | nullptr, |
michael@0 | 1461 | { |
michael@0 | 1462 | nullptr, |
michael@0 | 1463 | nullptr |
michael@0 | 1464 | }, |
michael@0 | 1465 | prototypes::id::_ID_Count, |
michael@0 | 1466 | constructors::id::_ID_Count, |
michael@0 | 1467 | nullptr |
michael@0 | 1468 | }; |
michael@0 | 1469 | |
michael@0 | 1470 | bool |
michael@0 | 1471 | GetPropertyOnPrototype(JSContext* cx, JS::Handle<JSObject*> proxy, |
michael@0 | 1472 | JS::Handle<jsid> id, bool* found, |
michael@0 | 1473 | JS::Value* vp) |
michael@0 | 1474 | { |
michael@0 | 1475 | JS::Rooted<JSObject*> proto(cx); |
michael@0 | 1476 | if (!js::GetObjectProto(cx, proxy, &proto)) { |
michael@0 | 1477 | return false; |
michael@0 | 1478 | } |
michael@0 | 1479 | if (!proto) { |
michael@0 | 1480 | *found = false; |
michael@0 | 1481 | return true; |
michael@0 | 1482 | } |
michael@0 | 1483 | |
michael@0 | 1484 | bool hasProp; |
michael@0 | 1485 | if (!JS_HasPropertyById(cx, proto, id, &hasProp)) { |
michael@0 | 1486 | return false; |
michael@0 | 1487 | } |
michael@0 | 1488 | |
michael@0 | 1489 | *found = hasProp; |
michael@0 | 1490 | if (!hasProp || !vp) { |
michael@0 | 1491 | return true; |
michael@0 | 1492 | } |
michael@0 | 1493 | |
michael@0 | 1494 | JS::Rooted<JS::Value> value(cx); |
michael@0 | 1495 | if (!JS_ForwardGetPropertyTo(cx, proto, id, proxy, &value)) { |
michael@0 | 1496 | return false; |
michael@0 | 1497 | } |
michael@0 | 1498 | |
michael@0 | 1499 | *vp = value; |
michael@0 | 1500 | return true; |
michael@0 | 1501 | } |
michael@0 | 1502 | |
michael@0 | 1503 | bool |
michael@0 | 1504 | HasPropertyOnPrototype(JSContext* cx, JS::Handle<JSObject*> proxy, |
michael@0 | 1505 | JS::Handle<jsid> id) |
michael@0 | 1506 | { |
michael@0 | 1507 | JS::Rooted<JSObject*> obj(cx, proxy); |
michael@0 | 1508 | Maybe<JSAutoCompartment> ac; |
michael@0 | 1509 | if (xpc::WrapperFactory::IsXrayWrapper(obj)) { |
michael@0 | 1510 | obj = js::UncheckedUnwrap(obj); |
michael@0 | 1511 | ac.construct(cx, obj); |
michael@0 | 1512 | } |
michael@0 | 1513 | |
michael@0 | 1514 | bool found; |
michael@0 | 1515 | // We ignore an error from GetPropertyOnPrototype. We pass nullptr |
michael@0 | 1516 | // for vp so that GetPropertyOnPrototype won't actually do a get. |
michael@0 | 1517 | return !GetPropertyOnPrototype(cx, obj, id, &found, nullptr) || found; |
michael@0 | 1518 | } |
michael@0 | 1519 | |
michael@0 | 1520 | bool |
michael@0 | 1521 | AppendNamedPropertyIds(JSContext* cx, JS::Handle<JSObject*> proxy, |
michael@0 | 1522 | nsTArray<nsString>& names, |
michael@0 | 1523 | bool shadowPrototypeProperties, |
michael@0 | 1524 | JS::AutoIdVector& props) |
michael@0 | 1525 | { |
michael@0 | 1526 | for (uint32_t i = 0; i < names.Length(); ++i) { |
michael@0 | 1527 | JS::Rooted<JS::Value> v(cx); |
michael@0 | 1528 | if (!xpc::NonVoidStringToJsval(cx, names[i], &v)) { |
michael@0 | 1529 | return false; |
michael@0 | 1530 | } |
michael@0 | 1531 | |
michael@0 | 1532 | JS::Rooted<jsid> id(cx); |
michael@0 | 1533 | if (!JS_ValueToId(cx, v, &id)) { |
michael@0 | 1534 | return false; |
michael@0 | 1535 | } |
michael@0 | 1536 | |
michael@0 | 1537 | if (shadowPrototypeProperties || !HasPropertyOnPrototype(cx, proxy, id)) { |
michael@0 | 1538 | if (!props.append(id)) { |
michael@0 | 1539 | return false; |
michael@0 | 1540 | } |
michael@0 | 1541 | } |
michael@0 | 1542 | } |
michael@0 | 1543 | |
michael@0 | 1544 | return true; |
michael@0 | 1545 | } |
michael@0 | 1546 | |
michael@0 | 1547 | bool |
michael@0 | 1548 | DictionaryBase::ParseJSON(JSContext* aCx, |
michael@0 | 1549 | const nsAString& aJSON, |
michael@0 | 1550 | JS::MutableHandle<JS::Value> aVal) |
michael@0 | 1551 | { |
michael@0 | 1552 | if (aJSON.IsEmpty()) { |
michael@0 | 1553 | return true; |
michael@0 | 1554 | } |
michael@0 | 1555 | return JS_ParseJSON(aCx, |
michael@0 | 1556 | static_cast<const jschar*>(PromiseFlatString(aJSON).get()), |
michael@0 | 1557 | aJSON.Length(), aVal); |
michael@0 | 1558 | } |
michael@0 | 1559 | |
michael@0 | 1560 | static JSString* |
michael@0 | 1561 | ConcatJSString(JSContext* cx, const char* pre, JS::Handle<JSString*> str, const char* post) |
michael@0 | 1562 | { |
michael@0 | 1563 | if (!str) { |
michael@0 | 1564 | return nullptr; |
michael@0 | 1565 | } |
michael@0 | 1566 | |
michael@0 | 1567 | JS::Rooted<JSString*> preString(cx, JS_NewStringCopyN(cx, pre, strlen(pre))); |
michael@0 | 1568 | JS::Rooted<JSString*> postString(cx, JS_NewStringCopyN(cx, post, strlen(post))); |
michael@0 | 1569 | if (!preString || !postString) { |
michael@0 | 1570 | return nullptr; |
michael@0 | 1571 | } |
michael@0 | 1572 | |
michael@0 | 1573 | preString = JS_ConcatStrings(cx, preString, str); |
michael@0 | 1574 | if (!preString) { |
michael@0 | 1575 | return nullptr; |
michael@0 | 1576 | } |
michael@0 | 1577 | |
michael@0 | 1578 | return JS_ConcatStrings(cx, preString, postString); |
michael@0 | 1579 | } |
michael@0 | 1580 | |
michael@0 | 1581 | bool |
michael@0 | 1582 | NativeToString(JSContext* cx, JS::Handle<JSObject*> wrapper, |
michael@0 | 1583 | JS::Handle<JSObject*> obj, const char* pre, |
michael@0 | 1584 | const char* post, |
michael@0 | 1585 | JS::MutableHandle<JS::Value> v) |
michael@0 | 1586 | { |
michael@0 | 1587 | JS::Rooted<JSPropertyDescriptor> toStringDesc(cx); |
michael@0 | 1588 | toStringDesc.object().set(nullptr); |
michael@0 | 1589 | toStringDesc.setAttributes(0); |
michael@0 | 1590 | toStringDesc.setGetter(nullptr); |
michael@0 | 1591 | toStringDesc.setSetter(nullptr); |
michael@0 | 1592 | toStringDesc.value().set(JS::UndefinedValue()); |
michael@0 | 1593 | JS::Rooted<jsid> id(cx, |
michael@0 | 1594 | nsXPConnect::GetRuntimeInstance()->GetStringID(XPCJSRuntime::IDX_TO_STRING)); |
michael@0 | 1595 | if (!XrayResolveNativeProperty(cx, wrapper, obj, id, &toStringDesc)) { |
michael@0 | 1596 | return false; |
michael@0 | 1597 | } |
michael@0 | 1598 | |
michael@0 | 1599 | JS::Rooted<JSString*> str(cx); |
michael@0 | 1600 | { |
michael@0 | 1601 | JSAutoCompartment ac(cx, obj); |
michael@0 | 1602 | if (toStringDesc.object()) { |
michael@0 | 1603 | JS::Rooted<JS::Value> toString(cx, toStringDesc.value()); |
michael@0 | 1604 | if (!JS_WrapValue(cx, &toString)) { |
michael@0 | 1605 | return false; |
michael@0 | 1606 | } |
michael@0 | 1607 | MOZ_ASSERT(JS_ObjectIsCallable(cx, &toString.toObject())); |
michael@0 | 1608 | JS::Rooted<JS::Value> toStringResult(cx); |
michael@0 | 1609 | if (JS_CallFunctionValue(cx, obj, toString, JS::HandleValueArray::empty(), |
michael@0 | 1610 | &toStringResult)) { |
michael@0 | 1611 | str = toStringResult.toString(); |
michael@0 | 1612 | } else { |
michael@0 | 1613 | str = nullptr; |
michael@0 | 1614 | } |
michael@0 | 1615 | } else { |
michael@0 | 1616 | const js::Class* clasp = js::GetObjectClass(obj); |
michael@0 | 1617 | if (IsDOMClass(clasp)) { |
michael@0 | 1618 | str = JS_NewStringCopyZ(cx, clasp->name); |
michael@0 | 1619 | str = ConcatJSString(cx, "[object ", str, "]"); |
michael@0 | 1620 | } else if (IsDOMIfaceAndProtoClass(clasp)) { |
michael@0 | 1621 | const DOMIfaceAndProtoJSClass* ifaceAndProtoJSClass = |
michael@0 | 1622 | DOMIfaceAndProtoJSClass::FromJSClass(clasp); |
michael@0 | 1623 | str = JS_NewStringCopyZ(cx, ifaceAndProtoJSClass->mToString); |
michael@0 | 1624 | } else { |
michael@0 | 1625 | MOZ_ASSERT(JS_IsNativeFunction(obj, Constructor)); |
michael@0 | 1626 | JS::Rooted<JSFunction*> fun(cx, JS_GetObjectFunction(obj)); |
michael@0 | 1627 | str = JS_DecompileFunction(cx, fun, 0); |
michael@0 | 1628 | } |
michael@0 | 1629 | str = ConcatJSString(cx, pre, str, post); |
michael@0 | 1630 | } |
michael@0 | 1631 | } |
michael@0 | 1632 | |
michael@0 | 1633 | if (!str) { |
michael@0 | 1634 | return false; |
michael@0 | 1635 | } |
michael@0 | 1636 | |
michael@0 | 1637 | v.setString(str); |
michael@0 | 1638 | return JS_WrapValue(cx, v); |
michael@0 | 1639 | } |
michael@0 | 1640 | |
michael@0 | 1641 | // Dynamically ensure that two objects don't end up with the same reserved slot. |
michael@0 | 1642 | class MOZ_STACK_CLASS AutoCloneDOMObjectSlotGuard |
michael@0 | 1643 | { |
michael@0 | 1644 | public: |
michael@0 | 1645 | AutoCloneDOMObjectSlotGuard(JSContext* aCx, JSObject* aOld, JSObject* aNew) |
michael@0 | 1646 | : mOldReflector(aCx, aOld), mNewReflector(aCx, aNew) |
michael@0 | 1647 | { |
michael@0 | 1648 | MOZ_ASSERT(js::GetReservedSlot(aOld, DOM_OBJECT_SLOT) == |
michael@0 | 1649 | js::GetReservedSlot(aNew, DOM_OBJECT_SLOT)); |
michael@0 | 1650 | } |
michael@0 | 1651 | |
michael@0 | 1652 | ~AutoCloneDOMObjectSlotGuard() |
michael@0 | 1653 | { |
michael@0 | 1654 | if (js::GetReservedSlot(mOldReflector, DOM_OBJECT_SLOT).toPrivate()) { |
michael@0 | 1655 | js::SetReservedSlot(mNewReflector, DOM_OBJECT_SLOT, |
michael@0 | 1656 | JS::PrivateValue(nullptr)); |
michael@0 | 1657 | } |
michael@0 | 1658 | } |
michael@0 | 1659 | |
michael@0 | 1660 | private: |
michael@0 | 1661 | JS::Rooted<JSObject*> mOldReflector; |
michael@0 | 1662 | JS::Rooted<JSObject*> mNewReflector; |
michael@0 | 1663 | }; |
michael@0 | 1664 | |
michael@0 | 1665 | nsresult |
michael@0 | 1666 | ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObjArg) |
michael@0 | 1667 | { |
michael@0 | 1668 | js::AssertSameCompartment(aCx, aObjArg); |
michael@0 | 1669 | |
michael@0 | 1670 | // Check if we're near the stack limit before we get anywhere near the |
michael@0 | 1671 | // transplanting code. |
michael@0 | 1672 | JS_CHECK_RECURSION(aCx, return NS_ERROR_FAILURE); |
michael@0 | 1673 | |
michael@0 | 1674 | JS::Rooted<JSObject*> aObj(aCx, aObjArg); |
michael@0 | 1675 | const DOMClass* domClass = GetDOMClass(aObj); |
michael@0 | 1676 | |
michael@0 | 1677 | JS::Rooted<JSObject*> oldParent(aCx, JS_GetParent(aObj)); |
michael@0 | 1678 | JS::Rooted<JSObject*> newParent(aCx, domClass->mGetParent(aCx, aObj)); |
michael@0 | 1679 | |
michael@0 | 1680 | JSAutoCompartment oldAc(aCx, oldParent); |
michael@0 | 1681 | |
michael@0 | 1682 | JSCompartment* oldCompartment = js::GetObjectCompartment(oldParent); |
michael@0 | 1683 | JSCompartment* newCompartment = js::GetObjectCompartment(newParent); |
michael@0 | 1684 | if (oldCompartment == newCompartment) { |
michael@0 | 1685 | if (!JS_SetParent(aCx, aObj, newParent)) { |
michael@0 | 1686 | MOZ_CRASH(); |
michael@0 | 1687 | } |
michael@0 | 1688 | return NS_OK; |
michael@0 | 1689 | } |
michael@0 | 1690 | |
michael@0 | 1691 | // Telemetry. |
michael@0 | 1692 | xpc::RecordDonatedNode(oldCompartment); |
michael@0 | 1693 | xpc::RecordAdoptedNode(newCompartment); |
michael@0 | 1694 | |
michael@0 | 1695 | nsISupports* native = UnwrapDOMObjectToISupports(aObj); |
michael@0 | 1696 | if (!native) { |
michael@0 | 1697 | return NS_OK; |
michael@0 | 1698 | } |
michael@0 | 1699 | |
michael@0 | 1700 | bool isProxy = js::IsProxy(aObj); |
michael@0 | 1701 | JS::Rooted<JSObject*> expandoObject(aCx); |
michael@0 | 1702 | if (isProxy) { |
michael@0 | 1703 | expandoObject = DOMProxyHandler::GetAndClearExpandoObject(aObj); |
michael@0 | 1704 | } |
michael@0 | 1705 | |
michael@0 | 1706 | JSAutoCompartment newAc(aCx, newParent); |
michael@0 | 1707 | |
michael@0 | 1708 | // First we clone the reflector. We get a copy of its properties and clone its |
michael@0 | 1709 | // expando chain. The only part that is dangerous here is that if we have to |
michael@0 | 1710 | // return early we must avoid ending up with two reflectors pointing to the |
michael@0 | 1711 | // same native. Other than that, the objects we create will just go away. |
michael@0 | 1712 | |
michael@0 | 1713 | JS::Rooted<JSObject*> global(aCx, |
michael@0 | 1714 | js::GetGlobalForObjectCrossCompartment(newParent)); |
michael@0 | 1715 | JS::Handle<JSObject*> proto = (domClass->mGetProto)(aCx, global); |
michael@0 | 1716 | if (!proto) { |
michael@0 | 1717 | return NS_ERROR_FAILURE; |
michael@0 | 1718 | } |
michael@0 | 1719 | |
michael@0 | 1720 | JS::Rooted<JSObject*> newobj(aCx, JS_CloneObject(aCx, aObj, proto, newParent)); |
michael@0 | 1721 | if (!newobj) { |
michael@0 | 1722 | return NS_ERROR_FAILURE; |
michael@0 | 1723 | } |
michael@0 | 1724 | |
michael@0 | 1725 | js::SetReservedSlot(newobj, DOM_OBJECT_SLOT, |
michael@0 | 1726 | js::GetReservedSlot(aObj, DOM_OBJECT_SLOT)); |
michael@0 | 1727 | |
michael@0 | 1728 | // At this point, both |aObj| and |newobj| point to the same native |
michael@0 | 1729 | // which is bad, because one of them will end up being finalized with a |
michael@0 | 1730 | // native it does not own. |cloneGuard| ensures that if we exit before |
michael@0 | 1731 | // clearing |aObj|'s reserved slot the reserved slot of |newobj| will be |
michael@0 | 1732 | // set to null. |aObj| will go away soon, because we swap it with |
michael@0 | 1733 | // another object during the transplant and let that object die. |
michael@0 | 1734 | JS::Rooted<JSObject*> propertyHolder(aCx); |
michael@0 | 1735 | { |
michael@0 | 1736 | AutoCloneDOMObjectSlotGuard cloneGuard(aCx, aObj, newobj); |
michael@0 | 1737 | |
michael@0 | 1738 | JS::Rooted<JSObject*> copyFrom(aCx, isProxy ? expandoObject : aObj); |
michael@0 | 1739 | if (copyFrom) { |
michael@0 | 1740 | propertyHolder = JS_NewObjectWithGivenProto(aCx, nullptr, JS::NullPtr(), |
michael@0 | 1741 | newParent); |
michael@0 | 1742 | if (!propertyHolder) { |
michael@0 | 1743 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 1744 | } |
michael@0 | 1745 | |
michael@0 | 1746 | if (!JS_CopyPropertiesFrom(aCx, propertyHolder, copyFrom)) { |
michael@0 | 1747 | return NS_ERROR_FAILURE; |
michael@0 | 1748 | } |
michael@0 | 1749 | } else { |
michael@0 | 1750 | propertyHolder = nullptr; |
michael@0 | 1751 | } |
michael@0 | 1752 | |
michael@0 | 1753 | // Expandos from other compartments are attached to the target JS object. |
michael@0 | 1754 | // Copy them over, and let the old ones die a natural death. |
michael@0 | 1755 | if (!xpc::XrayUtils::CloneExpandoChain(aCx, newobj, aObj)) { |
michael@0 | 1756 | return NS_ERROR_FAILURE; |
michael@0 | 1757 | } |
michael@0 | 1758 | |
michael@0 | 1759 | // We've set up |newobj|, so we make it own the native by nulling |
michael@0 | 1760 | // out the reserved slot of |obj|. |
michael@0 | 1761 | // |
michael@0 | 1762 | // NB: It's important to do this _after_ copying the properties to |
michael@0 | 1763 | // propertyHolder. Otherwise, an object with |foo.x === foo| will |
michael@0 | 1764 | // crash when JS_CopyPropertiesFrom tries to call wrap() on foo.x. |
michael@0 | 1765 | js::SetReservedSlot(aObj, DOM_OBJECT_SLOT, JS::PrivateValue(nullptr)); |
michael@0 | 1766 | } |
michael@0 | 1767 | |
michael@0 | 1768 | aObj = xpc::TransplantObject(aCx, aObj, newobj); |
michael@0 | 1769 | if (!aObj) { |
michael@0 | 1770 | MOZ_CRASH(); |
michael@0 | 1771 | } |
michael@0 | 1772 | |
michael@0 | 1773 | nsWrapperCache* cache = nullptr; |
michael@0 | 1774 | CallQueryInterface(native, &cache); |
michael@0 | 1775 | bool preserving = cache->PreservingWrapper(); |
michael@0 | 1776 | cache->SetPreservingWrapper(false); |
michael@0 | 1777 | cache->SetWrapper(aObj); |
michael@0 | 1778 | cache->SetPreservingWrapper(preserving); |
michael@0 | 1779 | |
michael@0 | 1780 | if (propertyHolder) { |
michael@0 | 1781 | JS::Rooted<JSObject*> copyTo(aCx); |
michael@0 | 1782 | if (isProxy) { |
michael@0 | 1783 | copyTo = DOMProxyHandler::EnsureExpandoObject(aCx, aObj); |
michael@0 | 1784 | } else { |
michael@0 | 1785 | copyTo = aObj; |
michael@0 | 1786 | } |
michael@0 | 1787 | |
michael@0 | 1788 | if (!copyTo || !JS_CopyPropertiesFrom(aCx, copyTo, propertyHolder)) { |
michael@0 | 1789 | MOZ_CRASH(); |
michael@0 | 1790 | } |
michael@0 | 1791 | } |
michael@0 | 1792 | |
michael@0 | 1793 | nsObjectLoadingContent* htmlobject; |
michael@0 | 1794 | nsresult rv = UNWRAP_OBJECT(HTMLObjectElement, aObj, htmlobject); |
michael@0 | 1795 | if (NS_FAILED(rv)) { |
michael@0 | 1796 | rv = UnwrapObject<prototypes::id::HTMLEmbedElement, |
michael@0 | 1797 | HTMLSharedObjectElement>(aObj, htmlobject); |
michael@0 | 1798 | if (NS_FAILED(rv)) { |
michael@0 | 1799 | rv = UnwrapObject<prototypes::id::HTMLAppletElement, |
michael@0 | 1800 | HTMLSharedObjectElement>(aObj, htmlobject); |
michael@0 | 1801 | if (NS_FAILED(rv)) { |
michael@0 | 1802 | htmlobject = nullptr; |
michael@0 | 1803 | } |
michael@0 | 1804 | } |
michael@0 | 1805 | } |
michael@0 | 1806 | if (htmlobject) { |
michael@0 | 1807 | htmlobject->SetupProtoChain(aCx, aObj); |
michael@0 | 1808 | } |
michael@0 | 1809 | |
michael@0 | 1810 | // Now we can just fix up the parent and return the wrapper |
michael@0 | 1811 | |
michael@0 | 1812 | if (newParent && !JS_SetParent(aCx, aObj, newParent)) { |
michael@0 | 1813 | MOZ_CRASH(); |
michael@0 | 1814 | } |
michael@0 | 1815 | |
michael@0 | 1816 | return NS_OK; |
michael@0 | 1817 | } |
michael@0 | 1818 | |
michael@0 | 1819 | GlobalObject::GlobalObject(JSContext* aCx, JSObject* aObject) |
michael@0 | 1820 | : mGlobalJSObject(aCx), |
michael@0 | 1821 | mCx(aCx), |
michael@0 | 1822 | mGlobalObject(nullptr) |
michael@0 | 1823 | { |
michael@0 | 1824 | JS::Rooted<JSObject*> obj(aCx, aObject); |
michael@0 | 1825 | if (js::IsWrapper(obj)) { |
michael@0 | 1826 | obj = js::CheckedUnwrap(obj, /* stopAtOuter = */ false); |
michael@0 | 1827 | if (!obj) { |
michael@0 | 1828 | // We should never end up here on a worker thread, since there shouldn't |
michael@0 | 1829 | // be any security wrappers to worry about. |
michael@0 | 1830 | if (!MOZ_LIKELY(NS_IsMainThread())) { |
michael@0 | 1831 | MOZ_CRASH(); |
michael@0 | 1832 | } |
michael@0 | 1833 | |
michael@0 | 1834 | Throw(aCx, NS_ERROR_XPC_SECURITY_MANAGER_VETO); |
michael@0 | 1835 | return; |
michael@0 | 1836 | } |
michael@0 | 1837 | } |
michael@0 | 1838 | |
michael@0 | 1839 | mGlobalJSObject = js::GetGlobalForObjectCrossCompartment(obj); |
michael@0 | 1840 | } |
michael@0 | 1841 | |
michael@0 | 1842 | nsISupports* |
michael@0 | 1843 | GlobalObject::GetAsSupports() const |
michael@0 | 1844 | { |
michael@0 | 1845 | if (mGlobalObject) { |
michael@0 | 1846 | return mGlobalObject; |
michael@0 | 1847 | } |
michael@0 | 1848 | |
michael@0 | 1849 | if (!NS_IsMainThread()) { |
michael@0 | 1850 | mGlobalObject = UnwrapDOMObjectToISupports(mGlobalJSObject); |
michael@0 | 1851 | return mGlobalObject; |
michael@0 | 1852 | } |
michael@0 | 1853 | |
michael@0 | 1854 | JS::Rooted<JS::Value> val(mCx, JS::ObjectValue(*mGlobalJSObject)); |
michael@0 | 1855 | |
michael@0 | 1856 | // Switch this to UnwrapDOMObjectToISupports once our global objects are |
michael@0 | 1857 | // using new bindings. |
michael@0 | 1858 | nsresult rv = xpc_qsUnwrapArg<nsISupports>(mCx, val, &mGlobalObject, |
michael@0 | 1859 | static_cast<nsISupports**>(getter_AddRefs(mGlobalObjectRef)), |
michael@0 | 1860 | &val); |
michael@0 | 1861 | if (NS_FAILED(rv)) { |
michael@0 | 1862 | mGlobalObject = nullptr; |
michael@0 | 1863 | Throw(mCx, NS_ERROR_XPC_BAD_CONVERT_JS); |
michael@0 | 1864 | } |
michael@0 | 1865 | |
michael@0 | 1866 | return mGlobalObject; |
michael@0 | 1867 | } |
michael@0 | 1868 | |
michael@0 | 1869 | bool |
michael@0 | 1870 | InterfaceHasInstance(JSContext* cx, JS::Handle<JSObject*> obj, |
michael@0 | 1871 | JS::Handle<JSObject*> instance, |
michael@0 | 1872 | bool* bp) |
michael@0 | 1873 | { |
michael@0 | 1874 | const DOMIfaceAndProtoJSClass* clasp = |
michael@0 | 1875 | DOMIfaceAndProtoJSClass::FromJSClass(js::GetObjectClass(obj)); |
michael@0 | 1876 | |
michael@0 | 1877 | const DOMClass* domClass = GetDOMClass(js::UncheckedUnwrap(instance)); |
michael@0 | 1878 | |
michael@0 | 1879 | MOZ_ASSERT(!domClass || clasp->mPrototypeID != prototypes::id::_ID_Count, |
michael@0 | 1880 | "Why do we have a hasInstance hook if we don't have a prototype " |
michael@0 | 1881 | "ID?"); |
michael@0 | 1882 | |
michael@0 | 1883 | if (domClass && |
michael@0 | 1884 | domClass->mInterfaceChain[clasp->mDepth] == clasp->mPrototypeID) { |
michael@0 | 1885 | *bp = true; |
michael@0 | 1886 | return true; |
michael@0 | 1887 | } |
michael@0 | 1888 | |
michael@0 | 1889 | JS::Rooted<JSObject*> unwrapped(cx, js::CheckedUnwrap(instance, true)); |
michael@0 | 1890 | if (unwrapped && jsipc::JavaScriptParent::IsCPOW(unwrapped)) { |
michael@0 | 1891 | bool boolp = false; |
michael@0 | 1892 | if (!jsipc::JavaScriptParent::DOMInstanceOf(cx, unwrapped, clasp->mPrototypeID, |
michael@0 | 1893 | clasp->mDepth, &boolp)) { |
michael@0 | 1894 | return false; |
michael@0 | 1895 | } |
michael@0 | 1896 | *bp = boolp; |
michael@0 | 1897 | return true; |
michael@0 | 1898 | } |
michael@0 | 1899 | |
michael@0 | 1900 | JS::Rooted<JS::Value> protov(cx); |
michael@0 | 1901 | DebugOnly<bool> ok = JS_GetProperty(cx, obj, "prototype", &protov); |
michael@0 | 1902 | MOZ_ASSERT(ok, "Someone messed with our prototype property?"); |
michael@0 | 1903 | |
michael@0 | 1904 | JS::Rooted<JSObject*> interfacePrototype(cx, &protov.toObject()); |
michael@0 | 1905 | MOZ_ASSERT(IsDOMIfaceAndProtoClass(js::GetObjectClass(interfacePrototype)), |
michael@0 | 1906 | "Someone messed with our prototype property?"); |
michael@0 | 1907 | |
michael@0 | 1908 | JS::Rooted<JSObject*> proto(cx); |
michael@0 | 1909 | if (!JS_GetPrototype(cx, instance, &proto)) { |
michael@0 | 1910 | return false; |
michael@0 | 1911 | } |
michael@0 | 1912 | |
michael@0 | 1913 | while (proto) { |
michael@0 | 1914 | if (proto == interfacePrototype) { |
michael@0 | 1915 | *bp = true; |
michael@0 | 1916 | return true; |
michael@0 | 1917 | } |
michael@0 | 1918 | |
michael@0 | 1919 | if (!JS_GetPrototype(cx, proto, &proto)) { |
michael@0 | 1920 | return false; |
michael@0 | 1921 | } |
michael@0 | 1922 | } |
michael@0 | 1923 | |
michael@0 | 1924 | *bp = false; |
michael@0 | 1925 | return true; |
michael@0 | 1926 | } |
michael@0 | 1927 | |
michael@0 | 1928 | bool |
michael@0 | 1929 | InterfaceHasInstance(JSContext* cx, JS::Handle<JSObject*> obj, JS::MutableHandle<JS::Value> vp, |
michael@0 | 1930 | bool* bp) |
michael@0 | 1931 | { |
michael@0 | 1932 | if (!vp.isObject()) { |
michael@0 | 1933 | *bp = false; |
michael@0 | 1934 | return true; |
michael@0 | 1935 | } |
michael@0 | 1936 | |
michael@0 | 1937 | JS::Rooted<JSObject*> instanceObject(cx, &vp.toObject()); |
michael@0 | 1938 | return InterfaceHasInstance(cx, obj, instanceObject, bp); |
michael@0 | 1939 | } |
michael@0 | 1940 | |
michael@0 | 1941 | bool |
michael@0 | 1942 | InterfaceHasInstance(JSContext* cx, int prototypeID, int depth, |
michael@0 | 1943 | JS::Handle<JSObject*> instance, |
michael@0 | 1944 | bool* bp) |
michael@0 | 1945 | { |
michael@0 | 1946 | const DOMClass* domClass = GetDOMClass(js::UncheckedUnwrap(instance)); |
michael@0 | 1947 | |
michael@0 | 1948 | MOZ_ASSERT(!domClass || prototypeID != prototypes::id::_ID_Count, |
michael@0 | 1949 | "Why do we have a hasInstance hook if we don't have a prototype " |
michael@0 | 1950 | "ID?"); |
michael@0 | 1951 | |
michael@0 | 1952 | *bp = (domClass && domClass->mInterfaceChain[depth] == prototypeID); |
michael@0 | 1953 | return true; |
michael@0 | 1954 | } |
michael@0 | 1955 | |
michael@0 | 1956 | bool |
michael@0 | 1957 | ReportLenientThisUnwrappingFailure(JSContext* cx, JSObject* obj) |
michael@0 | 1958 | { |
michael@0 | 1959 | JS::Rooted<JSObject*> rootedObj(cx, obj); |
michael@0 | 1960 | GlobalObject global(cx, rootedObj); |
michael@0 | 1961 | if (global.Failed()) { |
michael@0 | 1962 | return false; |
michael@0 | 1963 | } |
michael@0 | 1964 | nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(global.GetAsSupports()); |
michael@0 | 1965 | if (window && window->GetDoc()) { |
michael@0 | 1966 | window->GetDoc()->WarnOnceAbout(nsIDocument::eLenientThis); |
michael@0 | 1967 | } |
michael@0 | 1968 | return true; |
michael@0 | 1969 | } |
michael@0 | 1970 | |
michael@0 | 1971 | bool |
michael@0 | 1972 | GetWindowForJSImplementedObject(JSContext* cx, JS::Handle<JSObject*> obj, |
michael@0 | 1973 | nsPIDOMWindow** window) |
michael@0 | 1974 | { |
michael@0 | 1975 | // Be very careful to not get tricked here. |
michael@0 | 1976 | MOZ_ASSERT(NS_IsMainThread()); |
michael@0 | 1977 | if (!xpc::AccessCheck::isChrome(js::GetObjectCompartment(obj))) { |
michael@0 | 1978 | NS_RUNTIMEABORT("Should have a chrome object here"); |
michael@0 | 1979 | } |
michael@0 | 1980 | |
michael@0 | 1981 | // Look up the content-side object. |
michael@0 | 1982 | JS::Rooted<JS::Value> domImplVal(cx); |
michael@0 | 1983 | if (!JS_GetProperty(cx, obj, "__DOM_IMPL__", &domImplVal)) { |
michael@0 | 1984 | return false; |
michael@0 | 1985 | } |
michael@0 | 1986 | |
michael@0 | 1987 | if (!domImplVal.isObject()) { |
michael@0 | 1988 | ThrowErrorMessage(cx, MSG_NOT_OBJECT, "Value"); |
michael@0 | 1989 | return false; |
michael@0 | 1990 | } |
michael@0 | 1991 | |
michael@0 | 1992 | // Go ahead and get the global from it. GlobalObject will handle |
michael@0 | 1993 | // doing unwrapping as needed. |
michael@0 | 1994 | GlobalObject global(cx, &domImplVal.toObject()); |
michael@0 | 1995 | if (global.Failed()) { |
michael@0 | 1996 | return false; |
michael@0 | 1997 | } |
michael@0 | 1998 | |
michael@0 | 1999 | // It's OK if we have null here: that just means the content-side |
michael@0 | 2000 | // object really wasn't associated with any window. |
michael@0 | 2001 | nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(global.GetAsSupports())); |
michael@0 | 2002 | win.forget(window); |
michael@0 | 2003 | return true; |
michael@0 | 2004 | } |
michael@0 | 2005 | |
michael@0 | 2006 | already_AddRefed<nsPIDOMWindow> |
michael@0 | 2007 | ConstructJSImplementation(JSContext* aCx, const char* aContractId, |
michael@0 | 2008 | const GlobalObject& aGlobal, |
michael@0 | 2009 | JS::MutableHandle<JSObject*> aObject, |
michael@0 | 2010 | ErrorResult& aRv) |
michael@0 | 2011 | { |
michael@0 | 2012 | // Get the window to use as a parent and for initialization. |
michael@0 | 2013 | nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports()); |
michael@0 | 2014 | if (!window) { |
michael@0 | 2015 | aRv.Throw(NS_ERROR_FAILURE); |
michael@0 | 2016 | return nullptr; |
michael@0 | 2017 | } |
michael@0 | 2018 | |
michael@0 | 2019 | ConstructJSImplementation(aCx, aContractId, window, aObject, aRv); |
michael@0 | 2020 | |
michael@0 | 2021 | if (aRv.Failed()) { |
michael@0 | 2022 | return nullptr; |
michael@0 | 2023 | } |
michael@0 | 2024 | return window.forget(); |
michael@0 | 2025 | } |
michael@0 | 2026 | |
michael@0 | 2027 | void |
michael@0 | 2028 | ConstructJSImplementation(JSContext* aCx, const char* aContractId, |
michael@0 | 2029 | nsPIDOMWindow* aWindow, |
michael@0 | 2030 | JS::MutableHandle<JSObject*> aObject, |
michael@0 | 2031 | ErrorResult& aRv) |
michael@0 | 2032 | { |
michael@0 | 2033 | // Make sure to divorce ourselves from the calling JS while creating and |
michael@0 | 2034 | // initializing the object, so exceptions from that will get reported |
michael@0 | 2035 | // properly, since those are never exceptions that a spec wants to be thrown. |
michael@0 | 2036 | { |
michael@0 | 2037 | AutoNoJSAPI nojsapi; |
michael@0 | 2038 | |
michael@0 | 2039 | // Get the XPCOM component containing the JS implementation. |
michael@0 | 2040 | nsCOMPtr<nsISupports> implISupports = do_CreateInstance(aContractId); |
michael@0 | 2041 | if (!implISupports) { |
michael@0 | 2042 | NS_WARNING("Failed to get JS implementation for contract"); |
michael@0 | 2043 | aRv.Throw(NS_ERROR_FAILURE); |
michael@0 | 2044 | return; |
michael@0 | 2045 | } |
michael@0 | 2046 | // Initialize the object, if it implements nsIDOMGlobalPropertyInitializer. |
michael@0 | 2047 | nsCOMPtr<nsIDOMGlobalPropertyInitializer> gpi = |
michael@0 | 2048 | do_QueryInterface(implISupports); |
michael@0 | 2049 | if (gpi) { |
michael@0 | 2050 | JS::Rooted<JS::Value> initReturn(aCx); |
michael@0 | 2051 | nsresult rv = gpi->Init(aWindow, &initReturn); |
michael@0 | 2052 | if (NS_FAILED(rv)) { |
michael@0 | 2053 | aRv.Throw(rv); |
michael@0 | 2054 | return; |
michael@0 | 2055 | } |
michael@0 | 2056 | // With JS-implemented WebIDL, the return value of init() is not used to determine |
michael@0 | 2057 | // if init() failed, so init() should only return undefined. Any kind of permission |
michael@0 | 2058 | // or pref checking must happen by adding an attribute to the WebIDL interface. |
michael@0 | 2059 | if (!initReturn.isUndefined()) { |
michael@0 | 2060 | MOZ_ASSERT(false, "The init() method for JS-implemented WebIDL should not return anything"); |
michael@0 | 2061 | MOZ_CRASH(); |
michael@0 | 2062 | } |
michael@0 | 2063 | } |
michael@0 | 2064 | // Extract the JS implementation from the XPCOM object. |
michael@0 | 2065 | nsCOMPtr<nsIXPConnectWrappedJS> implWrapped = |
michael@0 | 2066 | do_QueryInterface(implISupports); |
michael@0 | 2067 | MOZ_ASSERT(implWrapped, "Failed to get wrapped JS from XPCOM component."); |
michael@0 | 2068 | if (!implWrapped) { |
michael@0 | 2069 | aRv.Throw(NS_ERROR_FAILURE); |
michael@0 | 2070 | return; |
michael@0 | 2071 | } |
michael@0 | 2072 | aObject.set(implWrapped->GetJSObject()); |
michael@0 | 2073 | if (!aObject) { |
michael@0 | 2074 | aRv.Throw(NS_ERROR_FAILURE); |
michael@0 | 2075 | } |
michael@0 | 2076 | } |
michael@0 | 2077 | } |
michael@0 | 2078 | |
michael@0 | 2079 | bool |
michael@0 | 2080 | NonVoidByteStringToJsval(JSContext *cx, const nsACString &str, |
michael@0 | 2081 | JS::MutableHandle<JS::Value> rval) |
michael@0 | 2082 | { |
michael@0 | 2083 | // ByteStrings are not UTF-8 encoded. |
michael@0 | 2084 | JSString* jsStr = JS_NewStringCopyN(cx, str.Data(), str.Length()); |
michael@0 | 2085 | |
michael@0 | 2086 | if (!jsStr) |
michael@0 | 2087 | return false; |
michael@0 | 2088 | |
michael@0 | 2089 | rval.setString(jsStr); |
michael@0 | 2090 | return true; |
michael@0 | 2091 | } |
michael@0 | 2092 | |
michael@0 | 2093 | bool |
michael@0 | 2094 | ConvertJSValueToByteString(JSContext* cx, JS::Handle<JS::Value> v, |
michael@0 | 2095 | JS::MutableHandle<JS::Value> pval, bool nullable, |
michael@0 | 2096 | nsACString& result) |
michael@0 | 2097 | { |
michael@0 | 2098 | JS::Rooted<JSString*> s(cx); |
michael@0 | 2099 | if (v.isString()) { |
michael@0 | 2100 | s = v.toString(); |
michael@0 | 2101 | } else { |
michael@0 | 2102 | |
michael@0 | 2103 | if (nullable && v.isNullOrUndefined()) { |
michael@0 | 2104 | result.SetIsVoid(true); |
michael@0 | 2105 | return true; |
michael@0 | 2106 | } |
michael@0 | 2107 | |
michael@0 | 2108 | s = JS::ToString(cx, v); |
michael@0 | 2109 | if (!s) { |
michael@0 | 2110 | return false; |
michael@0 | 2111 | } |
michael@0 | 2112 | pval.set(JS::StringValue(s)); // Root the new string. |
michael@0 | 2113 | } |
michael@0 | 2114 | |
michael@0 | 2115 | size_t length; |
michael@0 | 2116 | const jschar *chars = JS_GetStringCharsZAndLength(cx, s, &length); |
michael@0 | 2117 | if (!chars) { |
michael@0 | 2118 | return false; |
michael@0 | 2119 | } |
michael@0 | 2120 | |
michael@0 | 2121 | // Conversion from Javascript string to ByteString is only valid if all |
michael@0 | 2122 | // characters < 256. |
michael@0 | 2123 | for (size_t i = 0; i < length; i++) { |
michael@0 | 2124 | if (chars[i] > 255) { |
michael@0 | 2125 | // The largest unsigned 64 bit number (18,446,744,073,709,551,615) has |
michael@0 | 2126 | // 20 digits, plus one more for the null terminator. |
michael@0 | 2127 | char index[21]; |
michael@0 | 2128 | static_assert(sizeof(size_t) <= 8, "index array too small"); |
michael@0 | 2129 | PR_snprintf(index, sizeof(index), "%d", i); |
michael@0 | 2130 | // A jschar is 16 bits long. The biggest unsigned 16 bit |
michael@0 | 2131 | // number (65,535) has 5 digits, plus one more for the null |
michael@0 | 2132 | // terminator. |
michael@0 | 2133 | char badChar[6]; |
michael@0 | 2134 | static_assert(sizeof(jschar) <= 2, "badChar array too small"); |
michael@0 | 2135 | PR_snprintf(badChar, sizeof(badChar), "%d", chars[i]); |
michael@0 | 2136 | ThrowErrorMessage(cx, MSG_INVALID_BYTESTRING, index, badChar); |
michael@0 | 2137 | return false; |
michael@0 | 2138 | } |
michael@0 | 2139 | } |
michael@0 | 2140 | |
michael@0 | 2141 | if (length >= UINT32_MAX) { |
michael@0 | 2142 | return false; |
michael@0 | 2143 | } |
michael@0 | 2144 | result.SetCapacity(length+1); |
michael@0 | 2145 | JS_EncodeStringToBuffer(cx, s, result.BeginWriting(), length); |
michael@0 | 2146 | result.BeginWriting()[length] = '\0'; |
michael@0 | 2147 | result.SetLength(length); |
michael@0 | 2148 | |
michael@0 | 2149 | return true; |
michael@0 | 2150 | } |
michael@0 | 2151 | |
michael@0 | 2152 | bool |
michael@0 | 2153 | IsInPrivilegedApp(JSContext* aCx, JSObject* aObj) |
michael@0 | 2154 | { |
michael@0 | 2155 | using mozilla::dom::workers::GetWorkerPrivateFromContext; |
michael@0 | 2156 | if (!NS_IsMainThread()) { |
michael@0 | 2157 | return GetWorkerPrivateFromContext(aCx)->IsInPrivilegedApp(); |
michael@0 | 2158 | } |
michael@0 | 2159 | |
michael@0 | 2160 | nsIPrincipal* principal = nsContentUtils::GetObjectPrincipal(aObj); |
michael@0 | 2161 | uint16_t appStatus = principal->GetAppStatus(); |
michael@0 | 2162 | return (appStatus == nsIPrincipal::APP_STATUS_CERTIFIED || |
michael@0 | 2163 | appStatus == nsIPrincipal::APP_STATUS_PRIVILEGED); |
michael@0 | 2164 | } |
michael@0 | 2165 | |
michael@0 | 2166 | bool |
michael@0 | 2167 | IsInCertifiedApp(JSContext* aCx, JSObject* aObj) |
michael@0 | 2168 | { |
michael@0 | 2169 | using mozilla::dom::workers::GetWorkerPrivateFromContext; |
michael@0 | 2170 | if (!NS_IsMainThread()) { |
michael@0 | 2171 | return GetWorkerPrivateFromContext(aCx)->IsInCertifiedApp(); |
michael@0 | 2172 | } |
michael@0 | 2173 | |
michael@0 | 2174 | nsIPrincipal* principal = nsContentUtils::GetObjectPrincipal(aObj); |
michael@0 | 2175 | return principal->GetAppStatus() == nsIPrincipal::APP_STATUS_CERTIFIED; |
michael@0 | 2176 | } |
michael@0 | 2177 | |
michael@0 | 2178 | void |
michael@0 | 2179 | TraceGlobal(JSTracer* aTrc, JSObject* aObj) |
michael@0 | 2180 | { |
michael@0 | 2181 | MOZ_ASSERT(js::GetObjectClass(aObj)->flags & JSCLASS_DOM_GLOBAL); |
michael@0 | 2182 | mozilla::dom::TraceProtoAndIfaceCache(aTrc, aObj); |
michael@0 | 2183 | } |
michael@0 | 2184 | |
michael@0 | 2185 | void |
michael@0 | 2186 | FinalizeGlobal(JSFreeOp* aFreeOp, JSObject* aObj) |
michael@0 | 2187 | { |
michael@0 | 2188 | MOZ_ASSERT(js::GetObjectClass(aObj)->flags & JSCLASS_DOM_GLOBAL); |
michael@0 | 2189 | mozilla::dom::DestroyProtoAndIfaceCache(aObj); |
michael@0 | 2190 | } |
michael@0 | 2191 | |
michael@0 | 2192 | bool |
michael@0 | 2193 | ResolveGlobal(JSContext* aCx, JS::Handle<JSObject*> aObj, |
michael@0 | 2194 | JS::Handle<jsid> aId, JS::MutableHandle<JSObject*> aObjp) |
michael@0 | 2195 | { |
michael@0 | 2196 | bool resolved; |
michael@0 | 2197 | if (!JS_ResolveStandardClass(aCx, aObj, aId, &resolved)) { |
michael@0 | 2198 | return false; |
michael@0 | 2199 | } |
michael@0 | 2200 | |
michael@0 | 2201 | aObjp.set(resolved ? aObj.get() : nullptr); |
michael@0 | 2202 | return true; |
michael@0 | 2203 | } |
michael@0 | 2204 | |
michael@0 | 2205 | bool |
michael@0 | 2206 | EnumerateGlobal(JSContext* aCx, JS::Handle<JSObject*> aObj) |
michael@0 | 2207 | { |
michael@0 | 2208 | return JS_EnumerateStandardClasses(aCx, aObj); |
michael@0 | 2209 | } |
michael@0 | 2210 | |
michael@0 | 2211 | bool |
michael@0 | 2212 | GenericBindingGetter(JSContext* cx, unsigned argc, JS::Value* vp) |
michael@0 | 2213 | { |
michael@0 | 2214 | JS::CallArgs args = JS::CallArgsFromVp(argc, vp); |
michael@0 | 2215 | const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev()); |
michael@0 | 2216 | prototypes::ID protoID = static_cast<prototypes::ID>(info->protoID); |
michael@0 | 2217 | if (!args.thisv().isObject()) { |
michael@0 | 2218 | return ThrowInvalidThis(cx, args, |
michael@0 | 2219 | MSG_GETTER_THIS_DOES_NOT_IMPLEMENT_INTERFACE, |
michael@0 | 2220 | protoID); |
michael@0 | 2221 | } |
michael@0 | 2222 | JS::Rooted<JSObject*> obj(cx, &args.thisv().toObject()); |
michael@0 | 2223 | |
michael@0 | 2224 | void* self; |
michael@0 | 2225 | { |
michael@0 | 2226 | nsresult rv = UnwrapObject<void>(obj, self, protoID, info->depth); |
michael@0 | 2227 | if (NS_FAILED(rv)) { |
michael@0 | 2228 | return ThrowInvalidThis(cx, args, |
michael@0 | 2229 | GetInvalidThisErrorForGetter(rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO), |
michael@0 | 2230 | protoID); |
michael@0 | 2231 | } |
michael@0 | 2232 | } |
michael@0 | 2233 | |
michael@0 | 2234 | MOZ_ASSERT(info->type() == JSJitInfo::Getter); |
michael@0 | 2235 | JSJitGetterOp getter = info->getter; |
michael@0 | 2236 | return getter(cx, obj, self, JSJitGetterCallArgs(args)); |
michael@0 | 2237 | } |
michael@0 | 2238 | |
michael@0 | 2239 | bool |
michael@0 | 2240 | GenericBindingSetter(JSContext* cx, unsigned argc, JS::Value* vp) |
michael@0 | 2241 | { |
michael@0 | 2242 | JS::CallArgs args = JS::CallArgsFromVp(argc, vp); |
michael@0 | 2243 | const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev()); |
michael@0 | 2244 | prototypes::ID protoID = static_cast<prototypes::ID>(info->protoID); |
michael@0 | 2245 | if (!args.thisv().isObject()) { |
michael@0 | 2246 | return ThrowInvalidThis(cx, args, |
michael@0 | 2247 | MSG_SETTER_THIS_DOES_NOT_IMPLEMENT_INTERFACE, |
michael@0 | 2248 | protoID); |
michael@0 | 2249 | } |
michael@0 | 2250 | JS::Rooted<JSObject*> obj(cx, &args.thisv().toObject()); |
michael@0 | 2251 | |
michael@0 | 2252 | void* self; |
michael@0 | 2253 | { |
michael@0 | 2254 | nsresult rv = UnwrapObject<void>(obj, self, protoID, info->depth); |
michael@0 | 2255 | if (NS_FAILED(rv)) { |
michael@0 | 2256 | return ThrowInvalidThis(cx, args, |
michael@0 | 2257 | GetInvalidThisErrorForSetter(rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO), |
michael@0 | 2258 | protoID); |
michael@0 | 2259 | } |
michael@0 | 2260 | } |
michael@0 | 2261 | if (args.length() == 0) { |
michael@0 | 2262 | return ThrowNoSetterArg(cx, protoID); |
michael@0 | 2263 | } |
michael@0 | 2264 | MOZ_ASSERT(info->type() == JSJitInfo::Setter); |
michael@0 | 2265 | JSJitSetterOp setter = info->setter; |
michael@0 | 2266 | if (!setter(cx, obj, self, JSJitSetterCallArgs(args))) { |
michael@0 | 2267 | return false; |
michael@0 | 2268 | } |
michael@0 | 2269 | args.rval().set(JSVAL_VOID); |
michael@0 | 2270 | return true; |
michael@0 | 2271 | } |
michael@0 | 2272 | |
michael@0 | 2273 | bool |
michael@0 | 2274 | GenericBindingMethod(JSContext* cx, unsigned argc, JS::Value* vp) |
michael@0 | 2275 | { |
michael@0 | 2276 | JS::CallArgs args = JS::CallArgsFromVp(argc, vp); |
michael@0 | 2277 | const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev()); |
michael@0 | 2278 | prototypes::ID protoID = static_cast<prototypes::ID>(info->protoID); |
michael@0 | 2279 | if (!args.thisv().isObject()) { |
michael@0 | 2280 | return ThrowInvalidThis(cx, args, |
michael@0 | 2281 | MSG_METHOD_THIS_DOES_NOT_IMPLEMENT_INTERFACE, |
michael@0 | 2282 | protoID); |
michael@0 | 2283 | } |
michael@0 | 2284 | JS::Rooted<JSObject*> obj(cx, &args.thisv().toObject()); |
michael@0 | 2285 | |
michael@0 | 2286 | void* self; |
michael@0 | 2287 | { |
michael@0 | 2288 | nsresult rv = UnwrapObject<void>(obj, self, protoID, info->depth); |
michael@0 | 2289 | if (NS_FAILED(rv)) { |
michael@0 | 2290 | return ThrowInvalidThis(cx, args, |
michael@0 | 2291 | GetInvalidThisErrorForMethod(rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO), |
michael@0 | 2292 | protoID); |
michael@0 | 2293 | } |
michael@0 | 2294 | } |
michael@0 | 2295 | MOZ_ASSERT(info->type() == JSJitInfo::Method); |
michael@0 | 2296 | JSJitMethodOp method = info->method; |
michael@0 | 2297 | return method(cx, obj, self, JSJitMethodCallArgs(args)); |
michael@0 | 2298 | } |
michael@0 | 2299 | |
michael@0 | 2300 | bool |
michael@0 | 2301 | GenericPromiseReturningBindingMethod(JSContext* cx, unsigned argc, JS::Value* vp) |
michael@0 | 2302 | { |
michael@0 | 2303 | // Make sure to save the callee before someone maybe messes with rval(). |
michael@0 | 2304 | JS::CallArgs args = JS::CallArgsFromVp(argc, vp); |
michael@0 | 2305 | JS::Rooted<JSObject*> callee(cx, &args.callee()); |
michael@0 | 2306 | |
michael@0 | 2307 | // We could invoke GenericBindingMethod here, but that involves an |
michael@0 | 2308 | // extra call. Manually inline it instead. |
michael@0 | 2309 | const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev()); |
michael@0 | 2310 | prototypes::ID protoID = static_cast<prototypes::ID>(info->protoID); |
michael@0 | 2311 | if (!args.thisv().isObject()) { |
michael@0 | 2312 | ThrowInvalidThis(cx, args, |
michael@0 | 2313 | MSG_METHOD_THIS_DOES_NOT_IMPLEMENT_INTERFACE, |
michael@0 | 2314 | protoID); |
michael@0 | 2315 | return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee), |
michael@0 | 2316 | args.rval()); |
michael@0 | 2317 | } |
michael@0 | 2318 | JS::Rooted<JSObject*> obj(cx, &args.thisv().toObject()); |
michael@0 | 2319 | |
michael@0 | 2320 | void* self; |
michael@0 | 2321 | { |
michael@0 | 2322 | nsresult rv = UnwrapObject<void>(obj, self, protoID, info->depth); |
michael@0 | 2323 | if (NS_FAILED(rv)) { |
michael@0 | 2324 | ThrowInvalidThis(cx, args, |
michael@0 | 2325 | GetInvalidThisErrorForMethod(rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO), |
michael@0 | 2326 | protoID); |
michael@0 | 2327 | return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee), |
michael@0 | 2328 | args.rval()); |
michael@0 | 2329 | } |
michael@0 | 2330 | } |
michael@0 | 2331 | MOZ_ASSERT(info->type() == JSJitInfo::Method); |
michael@0 | 2332 | JSJitMethodOp method = info->method; |
michael@0 | 2333 | bool ok = method(cx, obj, self, JSJitMethodCallArgs(args)); |
michael@0 | 2334 | if (ok) { |
michael@0 | 2335 | return true; |
michael@0 | 2336 | } |
michael@0 | 2337 | |
michael@0 | 2338 | return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee), |
michael@0 | 2339 | args.rval()); |
michael@0 | 2340 | } |
michael@0 | 2341 | |
michael@0 | 2342 | bool |
michael@0 | 2343 | StaticMethodPromiseWrapper(JSContext* cx, unsigned argc, JS::Value* vp) |
michael@0 | 2344 | { |
michael@0 | 2345 | // Make sure to save the callee before someone maybe messes with rval(). |
michael@0 | 2346 | JS::CallArgs args = JS::CallArgsFromVp(argc, vp); |
michael@0 | 2347 | JS::Rooted<JSObject*> callee(cx, &args.callee()); |
michael@0 | 2348 | |
michael@0 | 2349 | const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev()); |
michael@0 | 2350 | MOZ_ASSERT(info); |
michael@0 | 2351 | MOZ_ASSERT(info->type() == JSJitInfo::StaticMethod); |
michael@0 | 2352 | |
michael@0 | 2353 | bool ok = info->staticMethod(cx, argc, vp); |
michael@0 | 2354 | if (ok) { |
michael@0 | 2355 | return true; |
michael@0 | 2356 | } |
michael@0 | 2357 | |
michael@0 | 2358 | return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee), |
michael@0 | 2359 | args.rval()); |
michael@0 | 2360 | } |
michael@0 | 2361 | |
michael@0 | 2362 | bool |
michael@0 | 2363 | ConvertExceptionToPromise(JSContext* cx, |
michael@0 | 2364 | JSObject* promiseScope, |
michael@0 | 2365 | JS::MutableHandle<JS::Value> rval) |
michael@0 | 2366 | { |
michael@0 | 2367 | GlobalObject global(cx, promiseScope); |
michael@0 | 2368 | if (global.Failed()) { |
michael@0 | 2369 | return false; |
michael@0 | 2370 | } |
michael@0 | 2371 | |
michael@0 | 2372 | JS::Rooted<JS::Value> exn(cx); |
michael@0 | 2373 | if (!JS_GetPendingException(cx, &exn)) { |
michael@0 | 2374 | return false; |
michael@0 | 2375 | } |
michael@0 | 2376 | |
michael@0 | 2377 | JS_ClearPendingException(cx); |
michael@0 | 2378 | ErrorResult rv; |
michael@0 | 2379 | nsRefPtr<Promise> promise = Promise::Reject(global, cx, exn, rv); |
michael@0 | 2380 | if (rv.Failed()) { |
michael@0 | 2381 | // We just give up. Make sure to not leak memory on the |
michael@0 | 2382 | // ErrorResult, but then just put the original exception back. |
michael@0 | 2383 | ThrowMethodFailedWithDetails(cx, rv, "", ""); |
michael@0 | 2384 | JS_SetPendingException(cx, exn); |
michael@0 | 2385 | return false; |
michael@0 | 2386 | } |
michael@0 | 2387 | |
michael@0 | 2388 | return WrapNewBindingObject(cx, promise, rval); |
michael@0 | 2389 | } |
michael@0 | 2390 | |
michael@0 | 2391 | } // namespace dom |
michael@0 | 2392 | } // namespace mozilla |