/src/mozilla-central/js/xpconnect/wrappers/XrayWrapper.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | /* vim: set ts=8 sts=4 et sw=4 tw=99: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "XrayWrapper.h" |
8 | | #include "AccessCheck.h" |
9 | | #include "WrapperFactory.h" |
10 | | |
11 | | #include "nsDependentString.h" |
12 | | #include "nsIScriptError.h" |
13 | | #include "mozilla/dom/Element.h" |
14 | | #include "mozilla/dom/ScriptSettings.h" |
15 | | |
16 | | #include "XPCWrapper.h" |
17 | | #include "xpcprivate.h" |
18 | | |
19 | | #include "jsapi.h" |
20 | | #include "nsJSUtils.h" |
21 | | #include "nsPrintfCString.h" |
22 | | |
23 | | #include "mozilla/dom/BindingUtils.h" |
24 | | #include "mozilla/dom/WindowBinding.h" |
25 | | #include "mozilla/dom/XrayExpandoClass.h" |
26 | | #include "nsGlobalWindow.h" |
27 | | |
28 | | using namespace mozilla::dom; |
29 | | using namespace JS; |
30 | | using namespace mozilla; |
31 | | |
32 | | using js::Wrapper; |
33 | | using js::BaseProxyHandler; |
34 | | using js::IsCrossCompartmentWrapper; |
35 | | using js::UncheckedUnwrap; |
36 | | using js::CheckedUnwrap; |
37 | | |
38 | | namespace xpc { |
39 | | |
40 | | using namespace XrayUtils; |
41 | | |
42 | | #define Between(x, a, b) (a <= x && x <= b) |
43 | | |
44 | | static_assert(JSProto_URIError - JSProto_Error == 7, "New prototype added in error object range"); |
45 | | #define AssertErrorObjectKeyInBounds(key) \ |
46 | | static_assert(Between(key, JSProto_Error, JSProto_URIError), "We depend on js/ProtoKey.h ordering here"); |
47 | | MOZ_FOR_EACH(AssertErrorObjectKeyInBounds, (), |
48 | | (JSProto_Error, JSProto_InternalError, JSProto_EvalError, JSProto_RangeError, |
49 | | JSProto_ReferenceError, JSProto_SyntaxError, JSProto_TypeError, JSProto_URIError)); |
50 | | |
51 | | static_assert(JSProto_Uint8ClampedArray - JSProto_Int8Array == 8, "New prototype added in typed array range"); |
52 | | #define AssertTypedArrayKeyInBounds(key) \ |
53 | | static_assert(Between(key, JSProto_Int8Array, JSProto_Uint8ClampedArray), "We depend on js/ProtoKey.h ordering here"); |
54 | | MOZ_FOR_EACH(AssertTypedArrayKeyInBounds, (), |
55 | | (JSProto_Int8Array, JSProto_Uint8Array, JSProto_Int16Array, JSProto_Uint16Array, |
56 | | JSProto_Int32Array, JSProto_Uint32Array, JSProto_Float32Array, JSProto_Float64Array, JSProto_Uint8ClampedArray)); |
57 | | |
58 | | #undef Between |
59 | | |
60 | | inline bool |
61 | | IsErrorObjectKey(JSProtoKey key) |
62 | 0 | { |
63 | 0 | return key >= JSProto_Error && key <= JSProto_URIError; |
64 | 0 | } |
65 | | |
66 | | inline bool |
67 | | IsTypedArrayKey(JSProtoKey key) |
68 | 0 | { |
69 | 0 | return key >= JSProto_Int8Array && key <= JSProto_Uint8ClampedArray; |
70 | 0 | } |
71 | | |
72 | | // Whitelist for the standard ES classes we can Xray to. |
73 | | static bool |
74 | | IsJSXraySupported(JSProtoKey key) |
75 | 0 | { |
76 | 0 | if (IsTypedArrayKey(key)) { |
77 | 0 | return true; |
78 | 0 | } |
79 | 0 | if (IsErrorObjectKey(key)) { |
80 | 0 | return true; |
81 | 0 | } |
82 | 0 | switch (key) { |
83 | 0 | case JSProto_Date: |
84 | 0 | case JSProto_DataView: |
85 | 0 | case JSProto_Object: |
86 | 0 | case JSProto_Array: |
87 | 0 | case JSProto_Function: |
88 | 0 | case JSProto_TypedArray: |
89 | 0 | case JSProto_SavedFrame: |
90 | 0 | case JSProto_RegExp: |
91 | 0 | case JSProto_Promise: |
92 | 0 | case JSProto_ArrayBuffer: |
93 | 0 | case JSProto_SharedArrayBuffer: |
94 | 0 | case JSProto_Map: |
95 | 0 | case JSProto_Set: |
96 | 0 | return true; |
97 | 0 | default: |
98 | 0 | return false; |
99 | 0 | } |
100 | 0 | } |
101 | | |
102 | | XrayType |
103 | | GetXrayType(JSObject* obj) |
104 | 0 | { |
105 | 0 | obj = js::UncheckedUnwrap(obj, /* stopAtWindowProxy = */ false); |
106 | 0 | if (mozilla::dom::UseDOMXray(obj)) { |
107 | 0 | return XrayForDOMObject; |
108 | 0 | } |
109 | 0 | |
110 | 0 | MOZ_ASSERT(!js::IsWindowProxy(obj)); |
111 | 0 |
|
112 | 0 | JSProtoKey standardProto = IdentifyStandardInstanceOrPrototype(obj); |
113 | 0 | if (IsJSXraySupported(standardProto)) { |
114 | 0 | return XrayForJSObject; |
115 | 0 | } |
116 | 0 | |
117 | 0 | // Modulo a few exceptions, everything else counts as an XrayWrapper to an |
118 | 0 | // opaque object, which means that more-privileged code sees nothing from |
119 | 0 | // the underlying object. This is very important for security. In some cases |
120 | 0 | // though, we need to make an exception for compatibility. |
121 | 0 | if (IsSandbox(obj)) { |
122 | 0 | return NotXray; |
123 | 0 | } |
124 | 0 | |
125 | 0 | return XrayForOpaqueObject; |
126 | 0 | } |
127 | | |
128 | | JSObject* |
129 | | XrayAwareCalleeGlobal(JSObject* fun) |
130 | 14 | { |
131 | 14 | MOZ_ASSERT(js::IsFunctionObject(fun)); |
132 | 14 | |
133 | 14 | if (!js::FunctionHasNativeReserved(fun)) { |
134 | 14 | // Just a normal function, no Xrays involved. |
135 | 14 | return JS::GetNonCCWObjectGlobal(fun); |
136 | 14 | } |
137 | 0 | |
138 | 0 | // The functions we expect here have the Xray wrapper they're associated with |
139 | 0 | // in their XRAY_DOM_FUNCTION_PARENT_WRAPPER_SLOT and, in a debug build, |
140 | 0 | // themselves in their XRAY_DOM_FUNCTION_NATIVE_SLOT_FOR_SELF. Assert that |
141 | 0 | // last bit. |
142 | 0 | MOZ_ASSERT(&js::GetFunctionNativeReserved(fun, XRAY_DOM_FUNCTION_NATIVE_SLOT_FOR_SELF).toObject() == |
143 | 0 | fun); |
144 | 0 |
|
145 | 0 | Value v = |
146 | 0 | js::GetFunctionNativeReserved(fun, XRAY_DOM_FUNCTION_PARENT_WRAPPER_SLOT); |
147 | 0 | MOZ_ASSERT(IsXrayWrapper(&v.toObject())); |
148 | 0 |
|
149 | 0 | JSObject* xrayTarget = js::UncheckedUnwrap(&v.toObject()); |
150 | 0 | return JS::GetNonCCWObjectGlobal(xrayTarget); |
151 | 0 | } |
152 | | |
153 | | JSObject* |
154 | | XrayTraits::getExpandoChain(HandleObject obj) |
155 | 0 | { |
156 | 0 | return ObjectScope(obj)->GetExpandoChain(obj); |
157 | 0 | } |
158 | | |
159 | | JSObject* |
160 | | XrayTraits::detachExpandoChain(HandleObject obj) |
161 | 0 | { |
162 | 0 | return ObjectScope(obj)->DetachExpandoChain(obj); |
163 | 0 | } |
164 | | |
165 | | bool |
166 | | XrayTraits::setExpandoChain(JSContext* cx, HandleObject obj, HandleObject chain) |
167 | 0 | { |
168 | 0 | return ObjectScope(obj)->SetExpandoChain(cx, obj, chain); |
169 | 0 | } |
170 | | |
171 | | const JSClass XrayTraits::HolderClass = { |
172 | | "XrayHolder", JSCLASS_HAS_RESERVED_SLOTS(HOLDER_SHARED_SLOT_COUNT) |
173 | | }; |
174 | | |
175 | | const JSClass JSXrayTraits::HolderClass = { |
176 | | "JSXrayHolder", JSCLASS_HAS_RESERVED_SLOTS(SLOT_COUNT) |
177 | | }; |
178 | | |
179 | | bool |
180 | | OpaqueXrayTraits::resolveOwnProperty(JSContext* cx, HandleObject wrapper, HandleObject target, |
181 | | HandleObject holder, HandleId id, |
182 | | MutableHandle<PropertyDescriptor> desc) |
183 | 0 | { |
184 | 0 | bool ok = XrayTraits::resolveOwnProperty(cx, wrapper, target, holder, id, desc); |
185 | 0 | if (!ok || desc.object()) { |
186 | 0 | return ok; |
187 | 0 | } |
188 | 0 | |
189 | 0 | return ReportWrapperDenial(cx, id, WrapperDenialForXray, "object is not safely Xrayable"); |
190 | 0 | } |
191 | | |
192 | | bool |
193 | | ReportWrapperDenial(JSContext* cx, HandleId id, WrapperDenialType type, const char* reason) |
194 | 0 | { |
195 | 0 | CompartmentPrivate* priv = CompartmentPrivate::Get(CurrentGlobalOrNull(cx)); |
196 | 0 | bool alreadyWarnedOnce = priv->wrapperDenialWarnings[type]; |
197 | 0 | priv->wrapperDenialWarnings[type] = true; |
198 | 0 |
|
199 | 0 | // The browser console warning is only emitted for the first violation, |
200 | 0 | // whereas the (debug-only) NS_WARNING is emitted for each violation. |
201 | 0 | #ifndef DEBUG |
202 | 0 | if (alreadyWarnedOnce) { |
203 | 0 | return true; |
204 | 0 | } |
205 | 0 | #endif |
206 | 0 | |
207 | 0 | nsAutoJSString propertyName; |
208 | 0 | RootedValue idval(cx); |
209 | 0 | if (!JS_IdToValue(cx, id, &idval)) { |
210 | 0 | return false; |
211 | 0 | } |
212 | 0 | JSString* str = JS_ValueToSource(cx, idval); |
213 | 0 | if (!str) { |
214 | 0 | return false; |
215 | 0 | } |
216 | 0 | if (!propertyName.init(cx, str)) { |
217 | 0 | return false; |
218 | 0 | } |
219 | 0 | AutoFilename filename; |
220 | 0 | unsigned line = 0, column = 0; |
221 | 0 | DescribeScriptedCaller(cx, &filename, &line, &column); |
222 | 0 |
|
223 | 0 | // Warn to the terminal for the logs. |
224 | 0 | NS_WARNING(nsPrintfCString("Silently denied access to property %s: %s (@%s:%u:%u)", |
225 | 0 | NS_LossyConvertUTF16toASCII(propertyName).get(), reason, |
226 | 0 | filename.get(), line, column).get()); |
227 | 0 |
|
228 | 0 | // If this isn't the first warning on this topic for this global, we've |
229 | 0 | // already bailed out in opt builds. Now that the NS_WARNING is done, bail |
230 | 0 | // out in debug builds as well. |
231 | 0 | if (alreadyWarnedOnce) { |
232 | 0 | return true; |
233 | 0 | } |
234 | 0 | |
235 | 0 | // |
236 | 0 | // Log a message to the console service. |
237 | 0 | // |
238 | 0 | |
239 | 0 | // Grab the pieces. |
240 | 0 | nsCOMPtr<nsIConsoleService> consoleService = do_GetService(NS_CONSOLESERVICE_CONTRACTID); |
241 | 0 | NS_ENSURE_TRUE(consoleService, true); |
242 | 0 | nsCOMPtr<nsIScriptError> errorObject = do_CreateInstance(NS_SCRIPTERROR_CONTRACTID); |
243 | 0 | NS_ENSURE_TRUE(errorObject, true); |
244 | 0 |
|
245 | 0 | // Compute the current window id if any. |
246 | 0 | uint64_t windowId = 0; |
247 | 0 | if (nsGlobalWindowInner* win = CurrentWindowOrNull(cx)) { |
248 | 0 | windowId = win->WindowID(); |
249 | 0 | } |
250 | 0 |
|
251 | 0 |
|
252 | 0 | Maybe<nsPrintfCString> errorMessage; |
253 | 0 | if (type == WrapperDenialForXray) { |
254 | 0 | errorMessage.emplace("XrayWrapper denied access to property %s (reason: %s). " |
255 | 0 | "See https://developer.mozilla.org/en-US/docs/Xray_vision " |
256 | 0 | "for more information. Note that only the first denied " |
257 | 0 | "property access from a given global object will be reported.", |
258 | 0 | NS_LossyConvertUTF16toASCII(propertyName).get(), |
259 | 0 | reason); |
260 | 0 | } else { |
261 | 0 | MOZ_ASSERT(type == WrapperDenialForCOW); |
262 | 0 | errorMessage.emplace("Security wrapper denied access to property %s on privileged " |
263 | 0 | "Javascript object. Support for exposing privileged objects " |
264 | 0 | "to untrusted content via __exposedProps__ has been " |
265 | 0 | "removed - use WebIDL bindings or Components.utils.cloneInto " |
266 | 0 | "instead. Note that only the first denied property access from a " |
267 | 0 | "given global object will be reported.", |
268 | 0 | NS_LossyConvertUTF16toASCII(propertyName).get()); |
269 | 0 | } |
270 | 0 | nsString filenameStr(NS_ConvertASCIItoUTF16(filename.get())); |
271 | 0 | nsresult rv = errorObject->InitWithWindowID(NS_ConvertASCIItoUTF16(errorMessage.ref()), |
272 | 0 | filenameStr, |
273 | 0 | EmptyString(), |
274 | 0 | line, column, |
275 | 0 | nsIScriptError::warningFlag, |
276 | 0 | "XPConnect", |
277 | 0 | windowId); |
278 | 0 | NS_ENSURE_SUCCESS(rv, true); |
279 | 0 | rv = consoleService->LogMessage(errorObject); |
280 | 0 | NS_ENSURE_SUCCESS(rv, true); |
281 | 0 |
|
282 | 0 | return true; |
283 | 0 | } |
284 | | |
285 | | bool JSXrayTraits::getOwnPropertyFromWrapperIfSafe(JSContext* cx, |
286 | | HandleObject wrapper, |
287 | | HandleId id, |
288 | | MutableHandle<PropertyDescriptor> outDesc) |
289 | 0 | { |
290 | 0 | MOZ_ASSERT(js::IsObjectInContextCompartment(wrapper, cx)); |
291 | 0 | RootedObject target(cx, getTargetObject(wrapper)); |
292 | 0 | RootedObject wrapperGlobal(cx, JS::CurrentGlobalOrNull(cx)); |
293 | 0 | { |
294 | 0 | JSAutoRealm ar(cx, target); |
295 | 0 | JS_MarkCrossZoneId(cx, id); |
296 | 0 | if (!getOwnPropertyFromTargetIfSafe(cx, target, wrapper, wrapperGlobal, id, outDesc)) { |
297 | 0 | return false; |
298 | 0 | } |
299 | 0 | } |
300 | 0 | return JS_WrapPropertyDescriptor(cx, outDesc); |
301 | 0 | } |
302 | | |
303 | | bool JSXrayTraits::getOwnPropertyFromTargetIfSafe(JSContext* cx, |
304 | | HandleObject target, |
305 | | HandleObject wrapper, |
306 | | HandleObject wrapperGlobal, |
307 | | HandleId id, |
308 | | MutableHandle<PropertyDescriptor> outDesc) |
309 | 0 | { |
310 | 0 | // Note - This function operates in the target compartment, because it |
311 | 0 | // avoids a bunch of back-and-forth wrapping in enumerateNames. |
312 | 0 | MOZ_ASSERT(getTargetObject(wrapper) == target); |
313 | 0 | MOZ_ASSERT(js::IsObjectInContextCompartment(target, cx)); |
314 | 0 | MOZ_ASSERT(WrapperFactory::IsXrayWrapper(wrapper)); |
315 | 0 | MOZ_ASSERT(JS_IsGlobalObject(wrapperGlobal)); |
316 | 0 | js::AssertSameCompartment(wrapper, wrapperGlobal); |
317 | 0 | MOZ_ASSERT(outDesc.object() == nullptr); |
318 | 0 |
|
319 | 0 | Rooted<PropertyDescriptor> desc(cx); |
320 | 0 | if (!JS_GetOwnPropertyDescriptorById(cx, target, id, &desc)) { |
321 | 0 | return false; |
322 | 0 | } |
323 | 0 | |
324 | 0 | // If the property doesn't exist at all, we're done. |
325 | 0 | if (!desc.object()) { |
326 | 0 | return true; |
327 | 0 | } |
328 | 0 | |
329 | 0 | // Disallow accessor properties. |
330 | 0 | if (desc.hasGetterOrSetter()) { |
331 | 0 | JSAutoRealm ar(cx, wrapperGlobal); |
332 | 0 | JS_MarkCrossZoneId(cx, id); |
333 | 0 | return ReportWrapperDenial(cx, id, WrapperDenialForXray, "property has accessor"); |
334 | 0 | } |
335 | 0 | |
336 | 0 | // Apply extra scrutiny to objects. |
337 | 0 | if (desc.value().isObject()) { |
338 | 0 | RootedObject propObj(cx, js::UncheckedUnwrap(&desc.value().toObject())); |
339 | 0 | JSAutoRealm ar(cx, propObj); |
340 | 0 |
|
341 | 0 | // Disallow non-subsumed objects. |
342 | 0 | if (!AccessCheck::subsumes(target, propObj)) { |
343 | 0 | JSAutoRealm ar(cx, wrapperGlobal); |
344 | 0 | JS_MarkCrossZoneId(cx, id); |
345 | 0 | return ReportWrapperDenial(cx, id, WrapperDenialForXray, "value not same-origin with target"); |
346 | 0 | } |
347 | 0 | |
348 | 0 | // Disallow non-Xrayable objects. |
349 | 0 | XrayType xrayType = GetXrayType(propObj); |
350 | 0 | if (xrayType == NotXray || xrayType == XrayForOpaqueObject) { |
351 | 0 | JSAutoRealm ar(cx, wrapperGlobal); |
352 | 0 | JS_MarkCrossZoneId(cx, id); |
353 | 0 | return ReportWrapperDenial(cx, id, WrapperDenialForXray, "value not Xrayable"); |
354 | 0 | } |
355 | 0 | |
356 | 0 | // Disallow callables. |
357 | 0 | if (JS::IsCallable(propObj)) { |
358 | 0 | JSAutoRealm ar(cx, wrapperGlobal); |
359 | 0 | JS_MarkCrossZoneId(cx, id); |
360 | 0 | return ReportWrapperDenial(cx, id, WrapperDenialForXray, "value is callable"); |
361 | 0 | } |
362 | 0 | } |
363 | 0 | |
364 | 0 | // Disallow any property that shadows something on its (Xrayed) |
365 | 0 | // prototype chain. |
366 | 0 | JSAutoRealm ar2(cx, wrapperGlobal); |
367 | 0 | JS_MarkCrossZoneId(cx, id); |
368 | 0 | RootedObject proto(cx); |
369 | 0 | bool foundOnProto = false; |
370 | 0 | if (!JS_GetPrototype(cx, wrapper, &proto) || |
371 | 0 | (proto && !JS_HasPropertyById(cx, proto, id, &foundOnProto))) |
372 | 0 | { |
373 | 0 | return false; |
374 | 0 | } |
375 | 0 | if (foundOnProto) { |
376 | 0 | return ReportWrapperDenial(cx, id, WrapperDenialForXray, "value shadows a property on the standard prototype"); |
377 | 0 | } |
378 | 0 | |
379 | 0 | // We made it! Assign over the descriptor, and don't forget to wrap. |
380 | 0 | outDesc.assign(desc.get()); |
381 | 0 | return true; |
382 | 0 | } |
383 | | |
384 | | // Returns true on success (in the JSAPI sense), false on failure. If true is |
385 | | // returned, desc.object() will indicate whether we actually resolved |
386 | | // the property. |
387 | | // |
388 | | // id is the property id we're looking for. |
389 | | // holder is the object to define the property on. |
390 | | // fs is the relevant JSFunctionSpec*. |
391 | | // ps is the relevant JSPropertySpec*. |
392 | | // desc is the descriptor we're resolving into. |
393 | | static bool |
394 | | TryResolvePropertyFromSpecs(JSContext* cx, HandleId id, HandleObject holder, |
395 | | const JSFunctionSpec* fs, |
396 | | const JSPropertySpec* ps, |
397 | | MutableHandle<PropertyDescriptor> desc) |
398 | 0 | { |
399 | 0 | // Scan through the functions. |
400 | 0 | const JSFunctionSpec* fsMatch = nullptr; |
401 | 0 | for ( ; fs && fs->name; ++fs) { |
402 | 0 | if (PropertySpecNameEqualsId(fs->name, id)) { |
403 | 0 | fsMatch = fs; |
404 | 0 | break; |
405 | 0 | } |
406 | 0 | } |
407 | 0 | if (fsMatch) { |
408 | 0 | // Generate an Xrayed version of the method. |
409 | 0 | RootedFunction fun(cx, JS::NewFunctionFromSpec(cx, fsMatch, id)); |
410 | 0 | if (!fun) { |
411 | 0 | return false; |
412 | 0 | } |
413 | 0 | |
414 | 0 | // The generic Xray machinery only defines non-own properties of the target on |
415 | 0 | // the holder. This is broken, and will be fixed at some point, but for now we |
416 | 0 | // need to cache the value explicitly. See the corresponding call to |
417 | 0 | // JS_GetOwnPropertyDescriptorById at the top of |
418 | 0 | // JSXrayTraits::resolveOwnProperty. |
419 | 0 | RootedObject funObj(cx, JS_GetFunctionObject(fun)); |
420 | 0 | return JS_DefinePropertyById(cx, holder, id, funObj, 0) && |
421 | 0 | JS_GetOwnPropertyDescriptorById(cx, holder, id, desc); |
422 | 0 | } |
423 | 0 |
|
424 | 0 | // Scan through the properties. |
425 | 0 | const JSPropertySpec* psMatch = nullptr; |
426 | 0 | for ( ; ps && ps->name; ++ps) { |
427 | 0 | if (PropertySpecNameEqualsId(ps->name, id)) { |
428 | 0 | psMatch = ps; |
429 | 0 | break; |
430 | 0 | } |
431 | 0 | } |
432 | 0 | if (psMatch) { |
433 | 0 | // The generic Xray machinery only defines non-own properties on the holder. |
434 | 0 | // This is broken, and will be fixed at some point, but for now we need to |
435 | 0 | // cache the value explicitly. See the corresponding call to |
436 | 0 | // JS_GetPropertyById at the top of JSXrayTraits::resolveOwnProperty. |
437 | 0 | // |
438 | 0 | // Note also that the public-facing API here doesn't give us a way to |
439 | 0 | // pass along JITInfo. It's probably ok though, since Xrays are already |
440 | 0 | // pretty slow. |
441 | 0 | desc.value().setUndefined(); |
442 | 0 | unsigned flags = psMatch->flags; |
443 | 0 | if (psMatch->isAccessor()) { |
444 | 0 | if (psMatch->isSelfHosted()) { |
445 | 0 | JSFunction* getterFun = JS::GetSelfHostedFunction(cx, psMatch->accessors.getter.selfHosted.funname, id, 0); |
446 | 0 | if (!getterFun) { |
447 | 0 | return false; |
448 | 0 | } |
449 | 0 | RootedObject getterObj(cx, JS_GetFunctionObject(getterFun)); |
450 | 0 | RootedObject setterObj(cx); |
451 | 0 | if (psMatch->accessors.setter.selfHosted.funname) { |
452 | 0 | MOZ_ASSERT(flags & JSPROP_SETTER); |
453 | 0 | JSFunction* setterFun = JS::GetSelfHostedFunction(cx, psMatch->accessors.setter.selfHosted.funname, id, 0); |
454 | 0 | if (!setterFun) { |
455 | 0 | return false; |
456 | 0 | } |
457 | 0 | setterObj = JS_GetFunctionObject(setterFun); |
458 | 0 | } |
459 | 0 | if (!JS_DefinePropertyById(cx, holder, id, getterObj, setterObj, flags)) { |
460 | 0 | return false; |
461 | 0 | } |
462 | 0 | } else { |
463 | 0 | if (!JS_DefinePropertyById(cx, holder, id, |
464 | 0 | psMatch->accessors.getter.native.op, |
465 | 0 | psMatch->accessors.setter.native.op, |
466 | 0 | flags)) |
467 | 0 | { |
468 | 0 | return false; |
469 | 0 | } |
470 | 0 | } |
471 | 0 | } else { |
472 | 0 | RootedValue v(cx); |
473 | 0 | if (!psMatch->getValue(cx, &v)) { |
474 | 0 | return false; |
475 | 0 | } |
476 | 0 | if (!JS_DefinePropertyById(cx, holder, id, v, flags & ~JSPROP_INTERNAL_USE_BIT)) { |
477 | 0 | return false; |
478 | 0 | } |
479 | 0 | } |
480 | 0 | |
481 | 0 | return JS_GetOwnPropertyDescriptorById(cx, holder, id, desc); |
482 | 0 | } |
483 | 0 | |
484 | 0 | return true; |
485 | 0 | } |
486 | | |
487 | | static bool |
488 | | ShouldResolveStaticProperties(JSProtoKey key) |
489 | 0 | { |
490 | 0 | // Don't try to resolve static properties on RegExp, because they |
491 | 0 | // have issues. In particular, some of them grab state off the |
492 | 0 | // global of the RegExp constructor that describes the last regexp |
493 | 0 | // evaluation in that global, which is not a useful thing to do |
494 | 0 | // over Xrays. |
495 | 0 | return key != JSProto_RegExp; |
496 | 0 | } |
497 | | |
498 | | bool |
499 | | JSXrayTraits::resolveOwnProperty(JSContext* cx, HandleObject wrapper, |
500 | | HandleObject target, HandleObject holder, |
501 | | HandleId id, |
502 | | MutableHandle<PropertyDescriptor> desc) |
503 | 0 | { |
504 | 0 | // Call the common code. |
505 | 0 | bool ok = XrayTraits::resolveOwnProperty(cx, wrapper, target, holder, |
506 | 0 | id, desc); |
507 | 0 | if (!ok || desc.object()) { |
508 | 0 | return ok; |
509 | 0 | } |
510 | 0 | |
511 | 0 | // The non-HasPrototypes semantics implemented by traditional Xrays are kind |
512 | 0 | // of broken with respect to |own|-ness and the holder. The common code |
513 | 0 | // muddles through by only checking the holder for non-|own| lookups, but |
514 | 0 | // that doesn't work for us. So we do an explicit holder check here, and hope |
515 | 0 | // that this mess gets fixed up soon. |
516 | 0 | if (!JS_GetOwnPropertyDescriptorById(cx, holder, id, desc)) { |
517 | 0 | return false; |
518 | 0 | } |
519 | 0 | if (desc.object()) { |
520 | 0 | desc.object().set(wrapper); |
521 | 0 | return true; |
522 | 0 | } |
523 | 0 | |
524 | 0 | JSProtoKey key = getProtoKey(holder); |
525 | 0 | if (!isPrototype(holder)) { |
526 | 0 | // For Object and Array instances, we expose some properties from the |
527 | 0 | // underlying object, but only after filtering them carefully. |
528 | 0 | // |
529 | 0 | // Note that, as far as JS observables go, Arrays are just Objects with |
530 | 0 | // a different prototype and a magic (own, non-configurable) |.length| that |
531 | 0 | // serves as a non-tight upper bound on |own| indexed properties. So while |
532 | 0 | // it's tempting to try to impose some sort of structure on what Arrays |
533 | 0 | // "should" look like over Xrays, the underlying object is squishy enough |
534 | 0 | // that it makes sense to just treat them like Objects for Xray purposes. |
535 | 0 | if (key == JSProto_Object || key == JSProto_Array) { |
536 | 0 | return getOwnPropertyFromWrapperIfSafe(cx, wrapper, id, desc); |
537 | 0 | } |
538 | 0 | if (IsTypedArrayKey(key)) { |
539 | 0 | if (IsArrayIndex(GetArrayIndexFromId(cx, id))) { |
540 | 0 | // WebExtensions can't use cloneInto(), so we just let them do |
541 | 0 | // the slow thing to maximize compatibility. |
542 | 0 | if (CompartmentPrivate::Get(CurrentGlobalOrNull(cx))->isWebExtensionContentScript) { |
543 | 0 | Rooted<PropertyDescriptor> innerDesc(cx); |
544 | 0 | { |
545 | 0 | JSAutoRealm ar(cx, target); |
546 | 0 | JS_MarkCrossZoneId(cx, id); |
547 | 0 | if (!JS_GetOwnPropertyDescriptorById(cx, target, id, &innerDesc)) { |
548 | 0 | return false; |
549 | 0 | } |
550 | 0 | } |
551 | 0 | if (innerDesc.isDataDescriptor() && innerDesc.value().isNumber()) { |
552 | 0 | desc.setValue(innerDesc.value()); |
553 | 0 | desc.object().set(wrapper); |
554 | 0 | } |
555 | 0 | return true; |
556 | 0 | } |
557 | 0 | JS_ReportErrorASCII(cx, "Accessing TypedArray data over Xrays is slow, and forbidden " |
558 | 0 | "in order to encourage performant code. To copy TypedArrays " |
559 | 0 | "across origin boundaries, consider using Components.utils.cloneInto()."); |
560 | 0 | return false; |
561 | 0 | } |
562 | 0 | } else if (key == JSProto_Function) { |
563 | 0 | if (id == GetJSIDByIndex(cx, XPCJSContext::IDX_LENGTH)) { |
564 | 0 | uint16_t length; |
565 | 0 | RootedFunction fun(cx, JS_GetObjectFunction(target)); |
566 | 0 | { |
567 | 0 | JSAutoRealm ar(cx, target); |
568 | 0 | if (!JS_GetFunctionLength(cx, fun, &length)) { |
569 | 0 | return false; |
570 | 0 | } |
571 | 0 | } |
572 | 0 | FillPropertyDescriptor(desc, wrapper, JSPROP_PERMANENT | JSPROP_READONLY, |
573 | 0 | NumberValue(length)); |
574 | 0 | return true; |
575 | 0 | } |
576 | 0 | if (id == GetJSIDByIndex(cx, XPCJSContext::IDX_NAME)) { |
577 | 0 | RootedString fname(cx, JS_GetFunctionId(JS_GetObjectFunction(target))); |
578 | 0 | if (fname) { |
579 | 0 | JS_MarkCrossZoneIdValue(cx, StringValue(fname)); |
580 | 0 | } |
581 | 0 | FillPropertyDescriptor(desc, wrapper, JSPROP_PERMANENT | JSPROP_READONLY, |
582 | 0 | fname ? StringValue(fname) : JS_GetEmptyStringValue(cx)); |
583 | 0 | } else { |
584 | 0 | // Look for various static properties/methods and the |
585 | 0 | // 'prototype' property. |
586 | 0 | JSProtoKey standardConstructor = constructorFor(holder); |
587 | 0 | if (standardConstructor != JSProto_Null) { |
588 | 0 | // Handle the 'prototype' property to make |
589 | 0 | // xrayedGlobal.StandardClass.prototype work. |
590 | 0 | if (id == GetJSIDByIndex(cx, XPCJSContext::IDX_PROTOTYPE)) { |
591 | 0 | RootedObject standardProto(cx); |
592 | 0 | { |
593 | 0 | JSAutoRealm ar(cx, target); |
594 | 0 | if (!JS_GetClassPrototype(cx, standardConstructor, &standardProto)) { |
595 | 0 | return false; |
596 | 0 | } |
597 | 0 | MOZ_ASSERT(standardProto); |
598 | 0 | } |
599 | 0 |
|
600 | 0 | if (!JS_WrapObject(cx, &standardProto)) { |
601 | 0 | return false; |
602 | 0 | } |
603 | 0 | FillPropertyDescriptor(desc, wrapper, JSPROP_PERMANENT | JSPROP_READONLY, |
604 | 0 | ObjectValue(*standardProto)); |
605 | 0 | return true; |
606 | 0 | } |
607 | 0 | |
608 | 0 | if (ShouldResolveStaticProperties(standardConstructor)) { |
609 | 0 | const js::Class* clasp = js::ProtoKeyToClass(standardConstructor); |
610 | 0 | MOZ_ASSERT(clasp->specDefined()); |
611 | 0 |
|
612 | 0 | if (!TryResolvePropertyFromSpecs(cx, id, holder, |
613 | 0 | clasp->specConstructorFunctions(), |
614 | 0 | clasp->specConstructorProperties(), desc)) { |
615 | 0 | return false; |
616 | 0 | } |
617 | 0 | |
618 | 0 | if (desc.object()) { |
619 | 0 | desc.object().set(wrapper); |
620 | 0 | return true; |
621 | 0 | } |
622 | 0 | } |
623 | 0 | } |
624 | 0 | } |
625 | 0 | } else if (IsErrorObjectKey(key)) { |
626 | 0 | // The useful state of error objects (except for .stack) is |
627 | 0 | // (unfortunately) represented as own data properties per-spec. This |
628 | 0 | // means that we can't have a a clean representation of the data |
629 | 0 | // (free from tampering) without doubling the slots of Error |
630 | 0 | // objects, which isn't great. So we forward these properties to the |
631 | 0 | // underlying object and then just censor any values with the wrong |
632 | 0 | // type. This limits the ability of content to do anything all that |
633 | 0 | // confusing. |
634 | 0 | bool isErrorIntProperty = |
635 | 0 | id == GetJSIDByIndex(cx, XPCJSContext::IDX_LINENUMBER) || |
636 | 0 | id == GetJSIDByIndex(cx, XPCJSContext::IDX_COLUMNNUMBER); |
637 | 0 | bool isErrorStringProperty = |
638 | 0 | id == GetJSIDByIndex(cx, XPCJSContext::IDX_FILENAME) || |
639 | 0 | id == GetJSIDByIndex(cx, XPCJSContext::IDX_MESSAGE); |
640 | 0 | if (isErrorIntProperty || isErrorStringProperty) { |
641 | 0 | RootedObject waiver(cx, wrapper); |
642 | 0 | if (!WrapperFactory::WaiveXrayAndWrap(cx, &waiver)) { |
643 | 0 | return false; |
644 | 0 | } |
645 | 0 | if (!JS_GetOwnPropertyDescriptorById(cx, waiver, id, desc)) { |
646 | 0 | return false; |
647 | 0 | } |
648 | 0 | bool valueMatchesType = (isErrorIntProperty && desc.value().isInt32()) || |
649 | 0 | (isErrorStringProperty && desc.value().isString()); |
650 | 0 | if (desc.hasGetterOrSetter() || !valueMatchesType) { |
651 | 0 | FillPropertyDescriptor(desc, nullptr, 0, UndefinedValue()); |
652 | 0 | } |
653 | 0 | return true; |
654 | 0 | } |
655 | 0 | } else if (key == JSProto_RegExp) { |
656 | 0 | if (id == GetJSIDByIndex(cx, XPCJSContext::IDX_LASTINDEX)) { |
657 | 0 | return getOwnPropertyFromWrapperIfSafe(cx, wrapper, id, desc); |
658 | 0 | } |
659 | 0 | } |
660 | 0 | |
661 | 0 | // The rest of this function applies only to prototypes. |
662 | 0 | return true; |
663 | 0 | } |
664 | 0 | |
665 | 0 | // Handle the 'constructor' property. |
666 | 0 | if (id == GetJSIDByIndex(cx, XPCJSContext::IDX_CONSTRUCTOR)) { |
667 | 0 | RootedObject constructor(cx); |
668 | 0 | { |
669 | 0 | JSAutoRealm ar(cx, target); |
670 | 0 | if (!JS_GetClassObject(cx, key, &constructor)) { |
671 | 0 | return false; |
672 | 0 | } |
673 | 0 | } |
674 | 0 | if (!JS_WrapObject(cx, &constructor)) { |
675 | 0 | return false; |
676 | 0 | } |
677 | 0 | desc.object().set(wrapper); |
678 | 0 | desc.setAttributes(0); |
679 | 0 | desc.setGetter(nullptr); |
680 | 0 | desc.setSetter(nullptr); |
681 | 0 | desc.value().setObject(*constructor); |
682 | 0 | return true; |
683 | 0 | } |
684 | 0 | |
685 | 0 | // Grab the JSClass. We require all Xrayable classes to have a ClassSpec. |
686 | 0 | const js::Class* clasp = js::GetObjectClass(target); |
687 | 0 | MOZ_ASSERT(clasp->specDefined()); |
688 | 0 |
|
689 | 0 | // Indexed array properties are handled above, so we can just work with the |
690 | 0 | // class spec here. |
691 | 0 | if (!TryResolvePropertyFromSpecs(cx, id, holder, |
692 | 0 | clasp->specPrototypeFunctions(), |
693 | 0 | clasp->specPrototypeProperties(), |
694 | 0 | desc)) { |
695 | 0 | return false; |
696 | 0 | } |
697 | 0 | |
698 | 0 | if (desc.object()) { |
699 | 0 | desc.object().set(wrapper); |
700 | 0 | } |
701 | 0 |
|
702 | 0 | return true; |
703 | 0 | } |
704 | | |
705 | | bool |
706 | | JSXrayTraits::delete_(JSContext* cx, HandleObject wrapper, HandleId id, ObjectOpResult& result) |
707 | 0 | { |
708 | 0 | MOZ_ASSERT(js::IsObjectInContextCompartment(wrapper, cx)); |
709 | 0 |
|
710 | 0 | RootedObject holder(cx, ensureHolder(cx, wrapper)); |
711 | 0 | if (!holder) { |
712 | 0 | return false; |
713 | 0 | } |
714 | 0 | |
715 | 0 | // If we're using Object Xrays, we allow callers to attempt to delete any |
716 | 0 | // property from the underlying object that they are able to resolve. Note |
717 | 0 | // that this deleting may fail if the property is non-configurable. |
718 | 0 | JSProtoKey key = getProtoKey(holder); |
719 | 0 | bool isObjectOrArrayInstance = (key == JSProto_Object || key == JSProto_Array) && |
720 | 0 | !isPrototype(holder); |
721 | 0 | if (isObjectOrArrayInstance) { |
722 | 0 | RootedObject wrapperGlobal(cx, JS::CurrentGlobalOrNull(cx)); |
723 | 0 | RootedObject target(cx, getTargetObject(wrapper)); |
724 | 0 | JSAutoRealm ar(cx, target); |
725 | 0 | JS_MarkCrossZoneId(cx, id); |
726 | 0 | Rooted<PropertyDescriptor> desc(cx); |
727 | 0 | if (!getOwnPropertyFromTargetIfSafe(cx, target, wrapper, wrapperGlobal, id, &desc)) { |
728 | 0 | return false; |
729 | 0 | } |
730 | 0 | if (desc.object()) { |
731 | 0 | return JS_DeletePropertyById(cx, target, id, result); |
732 | 0 | } |
733 | 0 | } |
734 | 0 | return result.succeed(); |
735 | 0 | } |
736 | | |
737 | | bool |
738 | | JSXrayTraits::defineProperty(JSContext* cx, HandleObject wrapper, HandleId id, |
739 | | Handle<PropertyDescriptor> desc, |
740 | | Handle<PropertyDescriptor> existingDesc, |
741 | | ObjectOpResult& result, |
742 | | bool* defined) |
743 | 0 | { |
744 | 0 | *defined = false; |
745 | 0 | RootedObject holder(cx, ensureHolder(cx, wrapper)); |
746 | 0 | if (!holder) { |
747 | 0 | return false; |
748 | 0 | } |
749 | 0 | |
750 | 0 | |
751 | 0 | // Object and Array instances are special. For those cases, we forward property |
752 | 0 | // definitions to the underlying object if the following conditions are met: |
753 | 0 | // * The property being defined is a value-prop. |
754 | 0 | // * The property being defined is either a primitive or subsumed by the target. |
755 | 0 | // * As seen from the Xray, any existing property that we would overwrite is an |
756 | 0 | // |own| value-prop. |
757 | 0 | // |
758 | 0 | // To avoid confusion, we disallow expandos on Object and Array instances, and |
759 | 0 | // therefore raise an exception here if the above conditions aren't met. |
760 | 0 | JSProtoKey key = getProtoKey(holder); |
761 | 0 | bool isInstance = !isPrototype(holder); |
762 | 0 | bool isObjectOrArray = (key == JSProto_Object || key == JSProto_Array); |
763 | 0 | if (isObjectOrArray && isInstance) { |
764 | 0 | RootedObject target(cx, getTargetObject(wrapper)); |
765 | 0 | if (desc.hasGetterOrSetter()) { |
766 | 0 | JS_ReportErrorASCII(cx, "Not allowed to define accessor property on [Object] or [Array] XrayWrapper"); |
767 | 0 | return false; |
768 | 0 | } |
769 | 0 | if (desc.value().isObject() && |
770 | 0 | !AccessCheck::subsumes(target, js::UncheckedUnwrap(&desc.value().toObject()))) |
771 | 0 | { |
772 | 0 | JS_ReportErrorASCII(cx, "Not allowed to define cross-origin object as property on [Object] or [Array] XrayWrapper"); |
773 | 0 | return false; |
774 | 0 | } |
775 | 0 | if (existingDesc.hasGetterOrSetter()) { |
776 | 0 | JS_ReportErrorASCII(cx, "Not allowed to overwrite accessor property on [Object] or [Array] XrayWrapper"); |
777 | 0 | return false; |
778 | 0 | } |
779 | 0 | if (existingDesc.object() && existingDesc.object() != wrapper) { |
780 | 0 | JS_ReportErrorASCII(cx, "Not allowed to shadow non-own Xray-resolved property on [Object] or [Array] XrayWrapper"); |
781 | 0 | return false; |
782 | 0 | } |
783 | 0 | |
784 | 0 | Rooted<PropertyDescriptor> wrappedDesc(cx, desc); |
785 | 0 | JSAutoRealm ar(cx, target); |
786 | 0 | JS_MarkCrossZoneId(cx, id); |
787 | 0 | if (!JS_WrapPropertyDescriptor(cx, &wrappedDesc) || |
788 | 0 | !JS_DefinePropertyById(cx, target, id, wrappedDesc, result)) |
789 | 0 | { |
790 | 0 | return false; |
791 | 0 | } |
792 | 0 | *defined = true; |
793 | 0 | return true; |
794 | 0 | } |
795 | 0 | |
796 | 0 | // For WebExtensions content scripts, we forward the definition of indexed properties. By |
797 | 0 | // validating that the key and value are both numbers, we can avoid doing any wrapping. |
798 | 0 | if (isInstance && IsTypedArrayKey(key) && |
799 | 0 | CompartmentPrivate::Get(JS::CurrentGlobalOrNull(cx))->isWebExtensionContentScript && |
800 | 0 | desc.isDataDescriptor() && (desc.value().isNumber() || desc.value().isUndefined()) && |
801 | 0 | IsArrayIndex(GetArrayIndexFromId(cx, id))) |
802 | 0 | { |
803 | 0 | RootedObject target(cx, getTargetObject(wrapper)); |
804 | 0 | JSAutoRealm ar(cx, target); |
805 | 0 | JS_MarkCrossZoneId(cx, id); |
806 | 0 | if (!JS_DefinePropertyById(cx, target, id, desc, result)) { |
807 | 0 | return false; |
808 | 0 | } |
809 | 0 | *defined = true; |
810 | 0 | return true; |
811 | 0 | } |
812 | 0 | |
813 | 0 | return true; |
814 | 0 | } |
815 | | |
816 | | static bool |
817 | | MaybeAppend(jsid id, unsigned flags, AutoIdVector& props) |
818 | 0 | { |
819 | 0 | MOZ_ASSERT(!(flags & JSITER_SYMBOLSONLY)); |
820 | 0 | if (!(flags & JSITER_SYMBOLS) && JSID_IS_SYMBOL(id)) { |
821 | 0 | return true; |
822 | 0 | } |
823 | 0 | return props.append(id); |
824 | 0 | } |
825 | | |
826 | | // Append the names from the given function and property specs to props. |
827 | | static bool |
828 | | AppendNamesFromFunctionAndPropertySpecs(JSContext* cx, |
829 | | const JSFunctionSpec* fs, |
830 | | const JSPropertySpec* ps, |
831 | | unsigned flags, |
832 | | AutoIdVector& props) |
833 | 0 | { |
834 | 0 | // Convert the method and property names to jsids and pass them to the caller. |
835 | 0 | for ( ; fs && fs->name; ++fs) { |
836 | 0 | jsid id; |
837 | 0 | if (!PropertySpecNameToPermanentId(cx, fs->name, &id)) { |
838 | 0 | return false; |
839 | 0 | } |
840 | 0 | if (!MaybeAppend(id, flags, props)) { |
841 | 0 | return false; |
842 | 0 | } |
843 | 0 | } |
844 | 0 | for ( ; ps && ps->name; ++ps) { |
845 | 0 | jsid id; |
846 | 0 | if (!PropertySpecNameToPermanentId(cx, ps->name, &id)) { |
847 | 0 | return false; |
848 | 0 | } |
849 | 0 | if (!MaybeAppend(id, flags, props)) { |
850 | 0 | return false; |
851 | 0 | } |
852 | 0 | } |
853 | 0 |
|
854 | 0 | return true; |
855 | 0 | } |
856 | | |
857 | | bool |
858 | | JSXrayTraits::enumerateNames(JSContext* cx, HandleObject wrapper, unsigned flags, |
859 | | AutoIdVector& props) |
860 | 0 | { |
861 | 0 | MOZ_ASSERT(js::IsObjectInContextCompartment(wrapper, cx)); |
862 | 0 |
|
863 | 0 | RootedObject target(cx, getTargetObject(wrapper)); |
864 | 0 | RootedObject holder(cx, ensureHolder(cx, wrapper)); |
865 | 0 | if (!holder) { |
866 | 0 | return false; |
867 | 0 | } |
868 | 0 | |
869 | 0 | JSProtoKey key = getProtoKey(holder); |
870 | 0 | if (!isPrototype(holder)) { |
871 | 0 | // For Object and Array instances, we expose some properties from the underlying |
872 | 0 | // object, but only after filtering them carefully. |
873 | 0 | if (key == JSProto_Object || key == JSProto_Array) { |
874 | 0 | MOZ_ASSERT(props.empty()); |
875 | 0 | RootedObject wrapperGlobal(cx, JS::CurrentGlobalOrNull(cx)); |
876 | 0 | { |
877 | 0 | JSAutoRealm ar(cx, target); |
878 | 0 | AutoIdVector targetProps(cx); |
879 | 0 | if (!js::GetPropertyKeys(cx, target, flags | JSITER_OWNONLY, &targetProps)) { |
880 | 0 | return false; |
881 | 0 | } |
882 | 0 | // Loop over the properties, and only pass along the ones that |
883 | 0 | // we determine to be safe. |
884 | 0 | if (!props.reserve(targetProps.length())) { |
885 | 0 | return false; |
886 | 0 | } |
887 | 0 | for (size_t i = 0; i < targetProps.length(); ++i) { |
888 | 0 | Rooted<PropertyDescriptor> desc(cx); |
889 | 0 | RootedId id(cx, targetProps[i]); |
890 | 0 | if (!getOwnPropertyFromTargetIfSafe(cx, target, wrapper, wrapperGlobal, id, |
891 | 0 | &desc)) |
892 | 0 | { |
893 | 0 | return false; |
894 | 0 | } |
895 | 0 | if (desc.object()) { |
896 | 0 | props.infallibleAppend(id); |
897 | 0 | } |
898 | 0 | } |
899 | 0 | } |
900 | 0 | for (size_t i = 0; i < props.length(); ++i) { |
901 | 0 | JS_MarkCrossZoneId(cx, props[i]); |
902 | 0 | } |
903 | 0 | return true; |
904 | 0 | } |
905 | 0 | if (IsTypedArrayKey(key)) { |
906 | 0 | uint32_t length = JS_GetTypedArrayLength(target); |
907 | 0 | // TypedArrays enumerate every indexed property in range, but |
908 | 0 | // |length| is a getter that lives on the proto, like it should be. |
909 | 0 | if (!props.reserve(length)) { |
910 | 0 | return false; |
911 | 0 | } |
912 | 0 | for (int32_t i = 0; i <= int32_t(length - 1); ++i) { |
913 | 0 | props.infallibleAppend(INT_TO_JSID(i)); |
914 | 0 | } |
915 | 0 | } else if (key == JSProto_Function) { |
916 | 0 | if (!props.append(GetJSIDByIndex(cx, XPCJSContext::IDX_LENGTH))) { |
917 | 0 | return false; |
918 | 0 | } |
919 | 0 | if (!props.append(GetJSIDByIndex(cx, XPCJSContext::IDX_NAME))) { |
920 | 0 | return false; |
921 | 0 | } |
922 | 0 | // Handle the .prototype property and static properties on standard |
923 | 0 | // constructors. |
924 | 0 | JSProtoKey standardConstructor = constructorFor(holder); |
925 | 0 | if (standardConstructor != JSProto_Null) { |
926 | 0 | if (!props.append(GetJSIDByIndex(cx, XPCJSContext::IDX_PROTOTYPE))) { |
927 | 0 | return false; |
928 | 0 | } |
929 | 0 | |
930 | 0 | if (ShouldResolveStaticProperties(standardConstructor)) { |
931 | 0 | const js::Class* clasp = js::ProtoKeyToClass(standardConstructor); |
932 | 0 | MOZ_ASSERT(clasp->specDefined()); |
933 | 0 |
|
934 | 0 | if (!AppendNamesFromFunctionAndPropertySpecs( |
935 | 0 | cx, clasp->specConstructorFunctions(), |
936 | 0 | clasp->specConstructorProperties(), flags, props)) { |
937 | 0 | return false; |
938 | 0 | } |
939 | 0 | } |
940 | 0 | } |
941 | 0 | } else if (IsErrorObjectKey(key)) { |
942 | 0 | if (!props.append(GetJSIDByIndex(cx, XPCJSContext::IDX_FILENAME)) || |
943 | 0 | !props.append(GetJSIDByIndex(cx, XPCJSContext::IDX_LINENUMBER)) || |
944 | 0 | !props.append(GetJSIDByIndex(cx, XPCJSContext::IDX_COLUMNNUMBER)) || |
945 | 0 | !props.append(GetJSIDByIndex(cx, XPCJSContext::IDX_STACK)) || |
946 | 0 | !props.append(GetJSIDByIndex(cx, XPCJSContext::IDX_MESSAGE))) |
947 | 0 | { |
948 | 0 | return false; |
949 | 0 | } |
950 | 0 | } else if (key == JSProto_RegExp) { |
951 | 0 | if (!props.append(GetJSIDByIndex(cx, XPCJSContext::IDX_LASTINDEX))) { |
952 | 0 | return false; |
953 | 0 | } |
954 | 0 | } |
955 | 0 | |
956 | 0 | // The rest of this function applies only to prototypes. |
957 | 0 | return true; |
958 | 0 | } |
959 | 0 | |
960 | 0 | // Add the 'constructor' property. |
961 | 0 | if (!props.append(GetJSIDByIndex(cx, XPCJSContext::IDX_CONSTRUCTOR))) { |
962 | 0 | return false; |
963 | 0 | } |
964 | 0 | |
965 | 0 | // Grab the JSClass. We require all Xrayable classes to have a ClassSpec. |
966 | 0 | const js::Class* clasp = js::GetObjectClass(target); |
967 | 0 | MOZ_ASSERT(clasp->specDefined()); |
968 | 0 |
|
969 | 0 | return AppendNamesFromFunctionAndPropertySpecs( |
970 | 0 | cx, clasp->specPrototypeFunctions(), |
971 | 0 | clasp->specPrototypeProperties(), flags, props); |
972 | 0 | } |
973 | | |
974 | | bool |
975 | | JSXrayTraits::construct(JSContext* cx, HandleObject wrapper, |
976 | | const JS::CallArgs& args, const js::Wrapper& baseInstance) |
977 | 0 | { |
978 | 0 | JSXrayTraits& self = JSXrayTraits::singleton; |
979 | 0 | JS::RootedObject holder(cx, self.ensureHolder(cx, wrapper)); |
980 | 0 | if (!holder) { |
981 | 0 | return false; |
982 | 0 | } |
983 | 0 | |
984 | 0 | if (xpc::JSXrayTraits::getProtoKey(holder) == JSProto_Function) { |
985 | 0 | JSProtoKey standardConstructor = constructorFor(holder); |
986 | 0 | if (standardConstructor == JSProto_Null) { |
987 | 0 | return baseInstance.construct(cx, wrapper, args); |
988 | 0 | } |
989 | 0 | |
990 | 0 | const js::Class* clasp = js::ProtoKeyToClass(standardConstructor); |
991 | 0 | MOZ_ASSERT(clasp); |
992 | 0 | if (!(clasp->flags & JSCLASS_HAS_XRAYED_CONSTRUCTOR)) { |
993 | 0 | return baseInstance.construct(cx, wrapper, args); |
994 | 0 | } |
995 | 0 | |
996 | 0 | // If the JSCLASS_HAS_XRAYED_CONSTRUCTOR flag is set on the Class, |
997 | 0 | // we don't use the constructor at hand. Instead, we retrieve the |
998 | 0 | // equivalent standard constructor in the xray compartment and run |
999 | 0 | // it in that compartment. The newTarget isn't unwrapped, and the |
1000 | 0 | // constructor has to be able to detect and handle this situation. |
1001 | 0 | // See the comments in js/public/Class.h and PromiseConstructor for |
1002 | 0 | // details and an example. |
1003 | 0 | RootedObject ctor(cx); |
1004 | 0 | if (!JS_GetClassObject(cx, standardConstructor, &ctor)) { |
1005 | 0 | return false; |
1006 | 0 | } |
1007 | 0 | |
1008 | 0 | RootedValue ctorVal(cx, ObjectValue(*ctor)); |
1009 | 0 | HandleValueArray vals(args); |
1010 | 0 | RootedObject result(cx); |
1011 | 0 | if (!JS::Construct(cx, ctorVal, wrapper, vals, &result)) { |
1012 | 0 | return false; |
1013 | 0 | } |
1014 | 0 | AssertSameCompartment(cx, result); |
1015 | 0 | args.rval().setObject(*result); |
1016 | 0 | return true; |
1017 | 0 | } |
1018 | 0 | |
1019 | 0 | JS::RootedValue v(cx, JS::ObjectValue(*wrapper)); |
1020 | 0 | js::ReportIsNotFunction(cx, v); |
1021 | 0 | return false; |
1022 | 0 | } |
1023 | | |
1024 | | JSObject* |
1025 | | JSXrayTraits::createHolder(JSContext* cx, JSObject* wrapper) |
1026 | 0 | { |
1027 | 0 | RootedObject target(cx, getTargetObject(wrapper)); |
1028 | 0 | RootedObject holder(cx, JS_NewObjectWithGivenProto(cx, &HolderClass, |
1029 | 0 | nullptr)); |
1030 | 0 | if (!holder) { |
1031 | 0 | return nullptr; |
1032 | 0 | } |
1033 | 0 | |
1034 | 0 | // Compute information about the target. |
1035 | 0 | bool isPrototype = false; |
1036 | 0 | JSProtoKey key = IdentifyStandardInstance(target); |
1037 | 0 | if (key == JSProto_Null) { |
1038 | 0 | isPrototype = true; |
1039 | 0 | key = IdentifyStandardPrototype(target); |
1040 | 0 | } |
1041 | 0 | MOZ_ASSERT(key != JSProto_Null); |
1042 | 0 |
|
1043 | 0 | // Special case: pretend Arguments objects are arrays for Xrays. |
1044 | 0 | // |
1045 | 0 | // Arguments objects are strange beasts - they inherit Object.prototype, |
1046 | 0 | // and implement iteration by defining an |own| property for |
1047 | 0 | // Symbol.iterator. Since this value is callable, Array/Object Xrays will |
1048 | 0 | // filter it out, causing the Xray view to be non-iterable, which in turn |
1049 | 0 | // breaks consumers. |
1050 | 0 | // |
1051 | 0 | // We can't trust the iterator value from the content compartment, |
1052 | 0 | // but the generic one on Array.prototype works well enough. So we force |
1053 | 0 | // the Xray view of Arguments objects to inherit Array.prototype, which |
1054 | 0 | // in turn allows iteration via the inherited Array.prototype[Symbol.iterator]. |
1055 | 0 | // This doesn't emulate any of the weird semantics of Arguments iterators, |
1056 | 0 | // but is probably good enough. |
1057 | 0 | // |
1058 | 0 | // Note that there are various Xray traps that do other special behavior for |
1059 | 0 | // JSProto_Array, but they also provide that special behavior for |
1060 | 0 | // JSProto_Object, and since Arguments would otherwise get JSProto_Object, |
1061 | 0 | // this does not cause any behavior change at those sites. |
1062 | 0 | if (key == JSProto_Object && js::IsArgumentsObject(target)) { |
1063 | 0 | key = JSProto_Array; |
1064 | 0 | } |
1065 | 0 |
|
1066 | 0 | // Store it on the holder. |
1067 | 0 | RootedValue v(cx); |
1068 | 0 | v.setNumber(static_cast<uint32_t>(key)); |
1069 | 0 | js::SetReservedSlot(holder, SLOT_PROTOKEY, v); |
1070 | 0 | v.setBoolean(isPrototype); |
1071 | 0 | js::SetReservedSlot(holder, SLOT_ISPROTOTYPE, v); |
1072 | 0 |
|
1073 | 0 | // If this is a function, also compute whether it serves as a constructor |
1074 | 0 | // for a standard class. |
1075 | 0 | if (key == JSProto_Function) { |
1076 | 0 | v.setNumber(static_cast<uint32_t>(IdentifyStandardConstructor(target))); |
1077 | 0 | js::SetReservedSlot(holder, SLOT_CONSTRUCTOR_FOR, v); |
1078 | 0 | } |
1079 | 0 |
|
1080 | 0 | return holder; |
1081 | 0 | } |
1082 | | |
1083 | | DOMXrayTraits DOMXrayTraits::singleton; |
1084 | | JSXrayTraits JSXrayTraits::singleton; |
1085 | | OpaqueXrayTraits OpaqueXrayTraits::singleton; |
1086 | | |
1087 | | XrayTraits* |
1088 | | GetXrayTraits(JSObject* obj) |
1089 | | { |
1090 | | switch (GetXrayType(obj)) { |
1091 | | case XrayForDOMObject: |
1092 | | return &DOMXrayTraits::singleton; |
1093 | | case XrayForJSObject: |
1094 | | return &JSXrayTraits::singleton; |
1095 | | case XrayForOpaqueObject: |
1096 | | return &OpaqueXrayTraits::singleton; |
1097 | | default: |
1098 | | return nullptr; |
1099 | | } |
1100 | | } |
1101 | | |
1102 | | /* |
1103 | | * Xray expando handling. |
1104 | | * |
1105 | | * We hang expandos for Xray wrappers off a reserved slot on the target object |
1106 | | * so that same-origin compartments can share expandos for a given object. We |
1107 | | * have a linked list of expando objects, one per origin. The properties on these |
1108 | | * objects are generally wrappers pointing back to the compartment that applied |
1109 | | * them. |
1110 | | * |
1111 | | * The expando objects should _never_ be exposed to script. The fact that they |
1112 | | * live in the target compartment is a detail of the implementation, and does |
1113 | | * not imply that code in the target compartment should be allowed to inspect |
1114 | | * them. They are private to the origin that placed them. |
1115 | | */ |
1116 | | |
1117 | | // Certain compartments do not share expandos with other compartments. Xrays in |
1118 | | // these compartments cache expandos on the wrapper's holder, as there is only |
1119 | | // one such wrapper which can create or access the expando. This allows for |
1120 | | // faster access to the expando, including through JIT inline caches. |
1121 | | static inline bool |
1122 | | CompartmentHasExclusiveExpandos(JSObject* obj) |
1123 | 0 | { |
1124 | 0 | return IsInSandboxCompartment(obj); |
1125 | 0 | } |
1126 | | |
1127 | | static inline JSObject* |
1128 | | GetCachedXrayExpando(JSObject* wrapper); |
1129 | | |
1130 | | static inline void |
1131 | | SetCachedXrayExpando(JSObject* holder, JSObject* expandoWrapper); |
1132 | | |
1133 | | static nsIPrincipal* |
1134 | | WrapperPrincipal(JSObject* obj) |
1135 | 0 | { |
1136 | 0 | // Use the principal stored in CompartmentOriginInfo. That works because |
1137 | 0 | // consumers are only interested in the origin-ignoring-document.domain. |
1138 | 0 | // See expandoObjectMatchesConsumer. |
1139 | 0 | MOZ_ASSERT(IsXrayWrapper(obj)); |
1140 | 0 | JS::Compartment* comp = js::GetObjectCompartment(obj); |
1141 | 0 | CompartmentPrivate* priv = CompartmentPrivate::Get(comp); |
1142 | 0 | return priv->originInfo.GetPrincipalIgnoringDocumentDomain(); |
1143 | 0 | } |
1144 | | |
1145 | | static nsIPrincipal* |
1146 | | GetExpandoObjectPrincipal(JSObject* expandoObject) |
1147 | 0 | { |
1148 | 0 | Value v = JS_GetReservedSlot(expandoObject, JSSLOT_EXPANDO_ORIGIN); |
1149 | 0 | return static_cast<nsIPrincipal*>(v.toPrivate()); |
1150 | 0 | } |
1151 | | |
1152 | | static void |
1153 | | ExpandoObjectFinalize(JSFreeOp* fop, JSObject* obj) |
1154 | 0 | { |
1155 | 0 | // Release the principal. |
1156 | 0 | nsIPrincipal* principal = GetExpandoObjectPrincipal(obj); |
1157 | 0 | NS_RELEASE(principal); |
1158 | 0 | } |
1159 | | |
1160 | | const JSClassOps XrayExpandoObjectClassOps = { |
1161 | | nullptr, nullptr, nullptr, nullptr, |
1162 | | nullptr, nullptr, |
1163 | | ExpandoObjectFinalize |
1164 | | }; |
1165 | | |
1166 | | bool |
1167 | | XrayTraits::expandoObjectMatchesConsumer(JSContext* cx, |
1168 | | HandleObject expandoObject, |
1169 | | nsIPrincipal* consumerOrigin) |
1170 | 0 | { |
1171 | 0 | MOZ_ASSERT(js::IsObjectInContextCompartment(expandoObject, cx)); |
1172 | 0 |
|
1173 | 0 | // First, compare the principals. |
1174 | 0 | nsIPrincipal* o = GetExpandoObjectPrincipal(expandoObject); |
1175 | 0 | // Note that it's very important here to ignore document.domain. We |
1176 | 0 | // pull the principal for the expando object off of the first consumer |
1177 | 0 | // for a given origin, and freely share the expandos amongst multiple |
1178 | 0 | // same-origin consumers afterwards. However, this means that we have |
1179 | 0 | // no way to know whether _all_ consumers have opted in to collaboration |
1180 | 0 | // by explicitly setting document.domain. So we just mandate that expando |
1181 | 0 | // sharing is unaffected by it. |
1182 | 0 | if (!consumerOrigin->Equals(o)) { |
1183 | 0 | return false; |
1184 | 0 | } |
1185 | 0 | |
1186 | 0 | // Certain globals exclusively own the associated expandos, in which case |
1187 | 0 | // the caller should have used the cached expando on the wrapper instead. |
1188 | 0 | JSObject* owner = JS_GetReservedSlot(expandoObject, |
1189 | 0 | JSSLOT_EXPANDO_EXCLUSIVE_WRAPPER_HOLDER) |
1190 | 0 | .toObjectOrNull(); |
1191 | 0 | return owner == nullptr; |
1192 | 0 | } |
1193 | | |
1194 | | bool |
1195 | | XrayTraits::getExpandoObjectInternal(JSContext* cx, JSObject* expandoChain, |
1196 | | HandleObject exclusiveWrapper, |
1197 | | nsIPrincipal* origin, |
1198 | | MutableHandleObject expandoObject) |
1199 | 0 | { |
1200 | 0 | MOZ_ASSERT(!JS_IsExceptionPending(cx)); |
1201 | 0 | expandoObject.set(nullptr); |
1202 | 0 |
|
1203 | 0 | // Use the cached expando if this wrapper has exclusive access to it. |
1204 | 0 | if (exclusiveWrapper) { |
1205 | 0 | JSObject* expandoWrapper = GetCachedXrayExpando(exclusiveWrapper); |
1206 | 0 | expandoObject.set(expandoWrapper ? UncheckedUnwrap(expandoWrapper) : nullptr); |
1207 | | #ifdef DEBUG |
1208 | | // Make sure the expando we found is on the target's chain. While we |
1209 | | // don't use this chain to look up expandos for the wrapper, |
1210 | | // the expando still needs to be on the chain to keep the wrapper and |
1211 | | // expando alive. |
1212 | | if (expandoObject) { |
1213 | | JSObject* head = expandoChain; |
1214 | | while (head && head != expandoObject) { |
1215 | | head = JS_GetReservedSlot(head, JSSLOT_EXPANDO_NEXT).toObjectOrNull(); |
1216 | | } |
1217 | | MOZ_ASSERT(head == expandoObject); |
1218 | | } |
1219 | | #endif |
1220 | | return true; |
1221 | 0 | } |
1222 | 0 |
|
1223 | 0 | // The expando object lives in the compartment of the target, so all our |
1224 | 0 | // work needs to happen there. |
1225 | 0 | RootedObject head(cx, expandoChain); |
1226 | 0 | JSAutoRealm ar(cx, head); |
1227 | 0 |
|
1228 | 0 | // Iterate through the chain, looking for a same-origin object. |
1229 | 0 | while (head) { |
1230 | 0 | if (expandoObjectMatchesConsumer(cx, head, origin)) { |
1231 | 0 | expandoObject.set(head); |
1232 | 0 | return true; |
1233 | 0 | } |
1234 | 0 | head = JS_GetReservedSlot(head, JSSLOT_EXPANDO_NEXT).toObjectOrNull(); |
1235 | 0 | } |
1236 | 0 |
|
1237 | 0 | // Not found. |
1238 | 0 | return true; |
1239 | 0 | } |
1240 | | |
1241 | | bool |
1242 | | XrayTraits::getExpandoObject(JSContext* cx, HandleObject target, HandleObject consumer, |
1243 | | MutableHandleObject expandoObject) |
1244 | 0 | { |
1245 | 0 | // Return early if no expando object has ever been attached, which is |
1246 | 0 | // usually the case. |
1247 | 0 | JSObject* chain = getExpandoChain(target); |
1248 | 0 | if (!chain) { |
1249 | 0 | return true; |
1250 | 0 | } |
1251 | 0 | |
1252 | 0 | bool isExclusive = CompartmentHasExclusiveExpandos(consumer); |
1253 | 0 | return getExpandoObjectInternal(cx, chain, isExclusive ? consumer : nullptr, |
1254 | 0 | WrapperPrincipal(consumer), expandoObject); |
1255 | 0 | } |
1256 | | |
1257 | | // Wrappers which have exclusive access to the expando on their target object |
1258 | | // need to be kept alive as long as the target object exists. This is done by |
1259 | | // keeping the expando in the expando chain on the target (even though it will |
1260 | | // not be used while looking up the expando for the wrapper), and keeping a |
1261 | | // strong reference from that expando to the wrapper itself, via the |
1262 | | // JSSLOT_EXPANDO_EXCLUSIVE_WRAPPER_HOLDER reserved slot. This slot does not |
1263 | | // point to the wrapper itself, because it is a cross compartment edge and we |
1264 | | // can't create a wrapper for a wrapper. Instead, the slot points to an |
1265 | | // instance of the holder class below in the wrapper's compartment, and the |
1266 | | // wrapper is held via this holder object's reserved slot. |
1267 | | static const JSClass gWrapperHolderClass = { |
1268 | | "XrayExpandoWrapperHolder", |
1269 | | JSCLASS_HAS_RESERVED_SLOTS(1) |
1270 | | }; |
1271 | | static const size_t JSSLOT_WRAPPER_HOLDER_CONTENTS = 0; |
1272 | | |
1273 | | JSObject* |
1274 | | XrayTraits::attachExpandoObject(JSContext* cx, HandleObject target, |
1275 | | HandleObject exclusiveWrapper, |
1276 | | HandleObject exclusiveWrapperGlobal, |
1277 | | nsIPrincipal* origin) |
1278 | 0 | { |
1279 | 0 | // Make sure the compartments are sane. |
1280 | 0 | MOZ_ASSERT(js::IsObjectInContextCompartment(target, cx)); |
1281 | 0 | if (exclusiveWrapper) { |
1282 | 0 | MOZ_ASSERT(!js::IsObjectInContextCompartment(exclusiveWrapper, cx)); |
1283 | 0 | MOZ_ASSERT(JS_IsGlobalObject(exclusiveWrapperGlobal)); |
1284 | 0 | js::AssertSameCompartment(exclusiveWrapper, exclusiveWrapperGlobal); |
1285 | 0 | } |
1286 | 0 |
|
1287 | 0 | // No duplicates allowed. |
1288 | | #ifdef DEBUG |
1289 | | { |
1290 | | JSObject* chain = getExpandoChain(target); |
1291 | | if (chain) { |
1292 | | RootedObject existingExpandoObject(cx); |
1293 | | if (getExpandoObjectInternal(cx, chain, exclusiveWrapper, origin, &existingExpandoObject)) { |
1294 | | MOZ_ASSERT(!existingExpandoObject); |
1295 | | } else { |
1296 | | JS_ClearPendingException(cx); |
1297 | | } |
1298 | | } |
1299 | | } |
1300 | | #endif |
1301 | |
|
1302 | 0 | // Create the expando object. |
1303 | 0 | const JSClass* expandoClass = getExpandoClass(cx, target); |
1304 | 0 | MOZ_ASSERT(!strcmp(expandoClass->name, "XrayExpandoObject")); |
1305 | 0 | RootedObject expandoObject(cx, |
1306 | 0 | JS_NewObjectWithGivenProto(cx, expandoClass, nullptr)); |
1307 | 0 | if (!expandoObject) { |
1308 | 0 | return nullptr; |
1309 | 0 | } |
1310 | 0 | |
1311 | 0 | // AddRef and store the principal. |
1312 | 0 | NS_ADDREF(origin); |
1313 | 0 | JS_SetReservedSlot(expandoObject, JSSLOT_EXPANDO_ORIGIN, JS::PrivateValue(origin)); |
1314 | 0 |
|
1315 | 0 | // Note the exclusive wrapper, if there is one. |
1316 | 0 | RootedObject wrapperHolder(cx); |
1317 | 0 | if (exclusiveWrapper) { |
1318 | 0 | JSAutoRealm ar(cx, exclusiveWrapperGlobal); |
1319 | 0 | wrapperHolder = JS_NewObjectWithGivenProto(cx, &gWrapperHolderClass, nullptr); |
1320 | 0 | if (!wrapperHolder) { |
1321 | 0 | return nullptr; |
1322 | 0 | } |
1323 | 0 | JS_SetReservedSlot(wrapperHolder, JSSLOT_WRAPPER_HOLDER_CONTENTS, ObjectValue(*exclusiveWrapper)); |
1324 | 0 | } |
1325 | 0 | if (!JS_WrapObject(cx, &wrapperHolder)) { |
1326 | 0 | return nullptr; |
1327 | 0 | } |
1328 | 0 | JS_SetReservedSlot(expandoObject, JSSLOT_EXPANDO_EXCLUSIVE_WRAPPER_HOLDER, |
1329 | 0 | ObjectOrNullValue(wrapperHolder)); |
1330 | 0 |
|
1331 | 0 | // Store it on the exclusive wrapper, if there is one. |
1332 | 0 | if (exclusiveWrapper) { |
1333 | 0 | RootedObject cachedExpandoObject(cx, expandoObject); |
1334 | 0 | JSAutoRealm ar(cx, exclusiveWrapperGlobal); |
1335 | 0 | if (!JS_WrapObject(cx, &cachedExpandoObject)) { |
1336 | 0 | return nullptr; |
1337 | 0 | } |
1338 | 0 | JSObject* holder = ensureHolder(cx, exclusiveWrapper); |
1339 | 0 | if (!holder) { |
1340 | 0 | return nullptr; |
1341 | 0 | } |
1342 | 0 | SetCachedXrayExpando(holder, cachedExpandoObject); |
1343 | 0 | } |
1344 | 0 |
|
1345 | 0 | // If this is our first expando object, take the opportunity to preserve |
1346 | 0 | // the wrapper. This keeps our expandos alive even if the Xray wrapper gets |
1347 | 0 | // collected. |
1348 | 0 | RootedObject chain(cx, getExpandoChain(target)); |
1349 | 0 | if (!chain) { |
1350 | 0 | preserveWrapper(target); |
1351 | 0 | } |
1352 | 0 |
|
1353 | 0 | // Insert it at the front of the chain. |
1354 | 0 | JS_SetReservedSlot(expandoObject, JSSLOT_EXPANDO_NEXT, ObjectOrNullValue(chain)); |
1355 | 0 | setExpandoChain(cx, target, expandoObject); |
1356 | 0 |
|
1357 | 0 | return expandoObject; |
1358 | 0 | } |
1359 | | |
1360 | | JSObject* |
1361 | | XrayTraits::ensureExpandoObject(JSContext* cx, HandleObject wrapper, |
1362 | | HandleObject target) |
1363 | 0 | { |
1364 | 0 | MOZ_ASSERT(js::IsObjectInContextCompartment(wrapper, cx)); |
1365 | 0 | RootedObject wrapperGlobal(cx, JS::CurrentGlobalOrNull(cx)); |
1366 | 0 |
|
1367 | 0 | // Expando objects live in the target compartment. |
1368 | 0 | JSAutoRealm ar(cx, target); |
1369 | 0 | RootedObject expandoObject(cx); |
1370 | 0 | if (!getExpandoObject(cx, target, wrapper, &expandoObject)) { |
1371 | 0 | return nullptr; |
1372 | 0 | } |
1373 | 0 | if (!expandoObject) { |
1374 | 0 | bool isExclusive = CompartmentHasExclusiveExpandos(wrapper); |
1375 | 0 | expandoObject = attachExpandoObject(cx, target, isExclusive ? wrapper : nullptr, |
1376 | 0 | wrapperGlobal, |
1377 | 0 | WrapperPrincipal(wrapper)); |
1378 | 0 | } |
1379 | 0 | return expandoObject; |
1380 | 0 | } |
1381 | | |
1382 | | bool |
1383 | | XrayTraits::cloneExpandoChain(JSContext* cx, HandleObject dst, HandleObject srcChain) |
1384 | 0 | { |
1385 | 0 | MOZ_ASSERT(js::IsObjectInContextCompartment(dst, cx)); |
1386 | 0 | MOZ_ASSERT(getExpandoChain(dst) == nullptr); |
1387 | 0 |
|
1388 | 0 | RootedObject oldHead(cx, srcChain); |
1389 | 0 | while (oldHead) { |
1390 | 0 | // If movingIntoXrayCompartment is true, then our new reflector is in a |
1391 | 0 | // compartment that used to have an Xray-with-expandos to the old reflector |
1392 | 0 | // and we should copy the expandos to the new reflector directly. |
1393 | 0 | bool movingIntoXrayCompartment; |
1394 | 0 |
|
1395 | 0 | // exclusiveWrapper is only used if movingIntoXrayCompartment ends up true. |
1396 | 0 | RootedObject exclusiveWrapper(cx); |
1397 | 0 | RootedObject exclusiveWrapperGlobal(cx); |
1398 | 0 | RootedObject wrapperHolder(cx, JS_GetReservedSlot(oldHead, |
1399 | 0 | JSSLOT_EXPANDO_EXCLUSIVE_WRAPPER_HOLDER) |
1400 | 0 | .toObjectOrNull()); |
1401 | 0 | if (wrapperHolder) { |
1402 | 0 | RootedObject unwrappedHolder(cx, UncheckedUnwrap(wrapperHolder)); |
1403 | 0 | // unwrappedHolder is the compartment of the relevant Xray, so check |
1404 | 0 | // whether that matches the compartment of cx (which matches the |
1405 | 0 | // compartment of dst). |
1406 | 0 | movingIntoXrayCompartment = |
1407 | 0 | js::IsObjectInContextCompartment(unwrappedHolder, cx); |
1408 | 0 |
|
1409 | 0 | if (!movingIntoXrayCompartment) { |
1410 | 0 | // The global containing this wrapper holder has an xray for |src| |
1411 | 0 | // with expandos. Create an xray in the global for |dst| which |
1412 | 0 | // will be associated with a clone of |src|'s expando object. |
1413 | 0 | JSAutoRealm ar(cx, unwrappedHolder); |
1414 | 0 | exclusiveWrapper = dst; |
1415 | 0 | if (!JS_WrapObject(cx, &exclusiveWrapper)) { |
1416 | 0 | return false; |
1417 | 0 | } |
1418 | 0 | exclusiveWrapperGlobal = JS::CurrentGlobalOrNull(cx); |
1419 | 0 | } |
1420 | 0 | } else { |
1421 | 0 | JSAutoRealm ar(cx, oldHead); |
1422 | 0 | movingIntoXrayCompartment = |
1423 | 0 | expandoObjectMatchesConsumer(cx, oldHead, GetObjectPrincipal(dst)); |
1424 | 0 | } |
1425 | 0 |
|
1426 | 0 | if (movingIntoXrayCompartment) { |
1427 | 0 | // Just copy properties directly onto dst. |
1428 | 0 | if (!JS_CopyPropertiesFrom(cx, dst, oldHead)) { |
1429 | 0 | return false; |
1430 | 0 | } |
1431 | 0 | } else { |
1432 | 0 | // Create a new expando object in the compartment of dst to replace |
1433 | 0 | // oldHead. |
1434 | 0 | RootedObject newHead(cx, attachExpandoObject(cx, dst, exclusiveWrapper, |
1435 | 0 | exclusiveWrapperGlobal, |
1436 | 0 | GetExpandoObjectPrincipal(oldHead))); |
1437 | 0 | if (!JS_CopyPropertiesFrom(cx, newHead, oldHead)) { |
1438 | 0 | return false; |
1439 | 0 | } |
1440 | 0 | } |
1441 | 0 | oldHead = JS_GetReservedSlot(oldHead, JSSLOT_EXPANDO_NEXT).toObjectOrNull(); |
1442 | 0 | } |
1443 | 0 | return true; |
1444 | 0 | } |
1445 | | |
1446 | | void |
1447 | | ClearXrayExpandoSlots(JSObject* target, size_t slotIndex) |
1448 | 0 | { |
1449 | 0 | if (!NS_IsMainThread()) { |
1450 | 0 | // No Xrays |
1451 | 0 | return; |
1452 | 0 | } |
1453 | 0 | |
1454 | 0 | MOZ_ASSERT(slotIndex != JSSLOT_EXPANDO_NEXT); |
1455 | 0 | MOZ_ASSERT(slotIndex != JSSLOT_EXPANDO_EXCLUSIVE_WRAPPER_HOLDER); |
1456 | 0 | MOZ_ASSERT(GetXrayTraits(target) == &DOMXrayTraits::singleton); |
1457 | 0 | RootingContext* rootingCx = RootingCx(); |
1458 | 0 | RootedObject rootedTarget(rootingCx, target); |
1459 | 0 | RootedObject head(rootingCx, |
1460 | 0 | DOMXrayTraits::singleton.getExpandoChain(rootedTarget)); |
1461 | 0 | while (head) { |
1462 | 0 | MOZ_ASSERT(JSCLASS_RESERVED_SLOTS(js::GetObjectClass(head)) > slotIndex); |
1463 | 0 | js::SetReservedSlot(head, slotIndex, UndefinedValue()); |
1464 | 0 | head = js::GetReservedSlot(head, JSSLOT_EXPANDO_NEXT).toObjectOrNull(); |
1465 | 0 | } |
1466 | 0 | } |
1467 | | |
1468 | | JSObject* |
1469 | | EnsureXrayExpandoObject(JSContext* cx, JS::HandleObject wrapper) |
1470 | 0 | { |
1471 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
1472 | 0 | MOZ_ASSERT(GetXrayTraits(wrapper) == &DOMXrayTraits::singleton); |
1473 | 0 | MOZ_ASSERT(IsXrayWrapper(wrapper)); |
1474 | 0 |
|
1475 | 0 | RootedObject target(cx, DOMXrayTraits::getTargetObject(wrapper)); |
1476 | 0 | return DOMXrayTraits::singleton.ensureExpandoObject(cx, wrapper, target); |
1477 | 0 | } |
1478 | | |
1479 | | const JSClass* |
1480 | | XrayTraits::getExpandoClass(JSContext* cx, HandleObject target) const |
1481 | 0 | { |
1482 | 0 | return &DefaultXrayExpandoObjectClass; |
1483 | 0 | } |
1484 | | |
1485 | | static const size_t JSSLOT_XRAY_HOLDER = 0; |
1486 | | |
1487 | | /* static */ JSObject* |
1488 | | XrayTraits::getHolder(JSObject* wrapper) |
1489 | 0 | { |
1490 | 0 | MOZ_ASSERT(WrapperFactory::IsXrayWrapper(wrapper)); |
1491 | 0 | js::Value v = js::GetProxyReservedSlot(wrapper, JSSLOT_XRAY_HOLDER); |
1492 | 0 | return v.isObject() ? &v.toObject() : nullptr; |
1493 | 0 | } |
1494 | | |
1495 | | JSObject* |
1496 | | XrayTraits::ensureHolder(JSContext* cx, HandleObject wrapper) |
1497 | 0 | { |
1498 | 0 | RootedObject holder(cx, getHolder(wrapper)); |
1499 | 0 | if (holder) { |
1500 | 0 | return holder; |
1501 | 0 | } |
1502 | 0 | holder = createHolder(cx, wrapper); // virtual trap. |
1503 | 0 | if (holder) { |
1504 | 0 | js::SetProxyReservedSlot(wrapper, JSSLOT_XRAY_HOLDER, ObjectValue(*holder)); |
1505 | 0 | } |
1506 | 0 | return holder; |
1507 | 0 | } |
1508 | | |
1509 | | static inline JSObject* |
1510 | | GetCachedXrayExpando(JSObject* wrapper) |
1511 | 0 | { |
1512 | 0 | JSObject* holder = XrayTraits::getHolder(wrapper); |
1513 | 0 | if (!holder) { |
1514 | 0 | return nullptr; |
1515 | 0 | } |
1516 | 0 | Value v = JS_GetReservedSlot(holder, XrayTraits::HOLDER_SLOT_EXPANDO); |
1517 | 0 | return v.isObject() ? &v.toObject() : nullptr; |
1518 | 0 | } |
1519 | | |
1520 | | static inline void |
1521 | | SetCachedXrayExpando(JSObject* holder, JSObject* expandoWrapper) |
1522 | 0 | { |
1523 | 0 | MOZ_ASSERT(js::GetObjectCompartment(holder) == |
1524 | 0 | js::GetObjectCompartment(expandoWrapper)); |
1525 | 0 | JS_SetReservedSlot(holder, XrayTraits::HOLDER_SLOT_EXPANDO, ObjectValue(*expandoWrapper)); |
1526 | 0 | } |
1527 | | |
1528 | | static nsGlobalWindowInner* |
1529 | | AsWindow(JSContext* cx, JSObject* wrapper) |
1530 | 0 | { |
1531 | 0 | // We want to use our target object here, since we don't want to be |
1532 | 0 | // doing a security check while unwrapping. |
1533 | 0 | JSObject* target = XrayTraits::getTargetObject(wrapper); |
1534 | 0 | return WindowOrNull(target); |
1535 | 0 | } |
1536 | | |
1537 | | static bool |
1538 | | IsWindow(JSContext* cx, JSObject* wrapper) |
1539 | 0 | { |
1540 | 0 | return !!AsWindow(cx, wrapper); |
1541 | 0 | } |
1542 | | |
1543 | | static bool |
1544 | | wrappedJSObject_getter(JSContext* cx, unsigned argc, Value* vp) |
1545 | 0 | { |
1546 | 0 | CallArgs args = CallArgsFromVp(argc, vp); |
1547 | 0 | if (!args.thisv().isObject()) { |
1548 | 0 | JS_ReportErrorASCII(cx, "This value not an object"); |
1549 | 0 | return false; |
1550 | 0 | } |
1551 | 0 | RootedObject wrapper(cx, &args.thisv().toObject()); |
1552 | 0 | if (!IsWrapper(wrapper) || !WrapperFactory::IsXrayWrapper(wrapper) || |
1553 | 0 | !WrapperFactory::AllowWaiver(wrapper)) { |
1554 | 0 | JS_ReportErrorASCII(cx, "Unexpected object"); |
1555 | 0 | return false; |
1556 | 0 | } |
1557 | 0 | |
1558 | 0 | args.rval().setObject(*wrapper); |
1559 | 0 |
|
1560 | 0 | return WrapperFactory::WaiveXrayAndWrap(cx, args.rval()); |
1561 | 0 | } |
1562 | | |
1563 | | bool |
1564 | | XrayTraits::resolveOwnProperty(JSContext* cx, HandleObject wrapper, HandleObject target, |
1565 | | HandleObject holder, HandleId id, |
1566 | | MutableHandle<PropertyDescriptor> desc) |
1567 | 0 | { |
1568 | 0 | desc.object().set(nullptr); |
1569 | 0 | RootedObject expando(cx); |
1570 | 0 | if (!getExpandoObject(cx, target, wrapper, &expando)) { |
1571 | 0 | return false; |
1572 | 0 | } |
1573 | 0 | |
1574 | 0 | // Check for expando properties first. Note that the expando object lives |
1575 | 0 | // in the target compartment. |
1576 | 0 | bool found = false; |
1577 | 0 | if (expando) { |
1578 | 0 | JSAutoRealm ar(cx, expando); |
1579 | 0 | JS_MarkCrossZoneId(cx, id); |
1580 | 0 | if (!JS_GetOwnPropertyDescriptorById(cx, expando, id, desc)) { |
1581 | 0 | return false; |
1582 | 0 | } |
1583 | 0 | found = !!desc.object(); |
1584 | 0 | } |
1585 | 0 |
|
1586 | 0 | // Next, check for ES builtins. |
1587 | 0 | if (!found && JS_IsGlobalObject(target)) { |
1588 | 0 | JSProtoKey key = JS_IdToProtoKey(cx, id); |
1589 | 0 | JSAutoRealm ar(cx, target); |
1590 | 0 | if (key != JSProto_Null) { |
1591 | 0 | MOZ_ASSERT(key < JSProto_LIMIT); |
1592 | 0 | RootedObject constructor(cx); |
1593 | 0 | if (!JS_GetClassObject(cx, key, &constructor)) { |
1594 | 0 | return false; |
1595 | 0 | } |
1596 | 0 | MOZ_ASSERT(constructor); |
1597 | 0 | desc.value().set(ObjectValue(*constructor)); |
1598 | 0 | found = true; |
1599 | 0 | } else if (id == GetJSIDByIndex(cx, XPCJSContext::IDX_EVAL)) { |
1600 | 0 | RootedObject eval(cx); |
1601 | 0 | if (!js::GetRealmOriginalEval(cx, &eval)) { |
1602 | 0 | return false; |
1603 | 0 | } |
1604 | 0 | desc.value().set(ObjectValue(*eval)); |
1605 | 0 | found = true; |
1606 | 0 | } |
1607 | 0 | } |
1608 | 0 |
|
1609 | 0 | if (found) { |
1610 | 0 | if (!JS_WrapPropertyDescriptor(cx, desc)) { |
1611 | 0 | return false; |
1612 | 0 | } |
1613 | 0 | // Pretend the property lives on the wrapper. |
1614 | 0 | desc.object().set(wrapper); |
1615 | 0 | return true; |
1616 | 0 | } |
1617 | 0 | |
1618 | 0 | // Handle .wrappedJSObject for subsuming callers. This should move once we |
1619 | 0 | // sort out own-ness for the holder. |
1620 | 0 | if (id == GetJSIDByIndex(cx, XPCJSContext::IDX_WRAPPED_JSOBJECT) && |
1621 | 0 | WrapperFactory::AllowWaiver(wrapper)) |
1622 | 0 | { |
1623 | 0 | if (!JS_AlreadyHasOwnPropertyById(cx, holder, id, &found)) { |
1624 | 0 | return false; |
1625 | 0 | } |
1626 | 0 | if (!found && !JS_DefinePropertyById(cx, holder, id, wrappedJSObject_getter, nullptr, |
1627 | 0 | JSPROP_ENUMERATE)) { |
1628 | 0 | return false; |
1629 | 0 | } |
1630 | 0 | if (!JS_GetOwnPropertyDescriptorById(cx, holder, id, desc)) { |
1631 | 0 | return false; |
1632 | 0 | } |
1633 | 0 | desc.object().set(wrapper); |
1634 | 0 | return true; |
1635 | 0 | } |
1636 | 0 | |
1637 | 0 | return true; |
1638 | 0 | } |
1639 | | |
1640 | | bool |
1641 | | DOMXrayTraits::resolveOwnProperty(JSContext* cx, HandleObject wrapper, HandleObject target, |
1642 | | HandleObject holder, HandleId id, |
1643 | | MutableHandle<PropertyDescriptor> desc) |
1644 | 0 | { |
1645 | 0 | // Call the common code. |
1646 | 0 | bool ok = XrayTraits::resolveOwnProperty(cx, wrapper, target, holder, id, desc); |
1647 | 0 | if (!ok || desc.object()) { |
1648 | 0 | return ok; |
1649 | 0 | } |
1650 | 0 | |
1651 | 0 | // Check for indexed access on a window. |
1652 | 0 | uint32_t index = GetArrayIndexFromId(cx, id); |
1653 | 0 | if (IsArrayIndex(index)) { |
1654 | 0 | nsGlobalWindowInner* win = AsWindow(cx, wrapper); |
1655 | 0 | // Note: As() unwraps outer windows to get to the inner window. |
1656 | 0 | if (win) { |
1657 | 0 | nsCOMPtr<nsPIDOMWindowOuter> subframe = win->IndexedGetter(index); |
1658 | 0 | if (subframe) { |
1659 | 0 | subframe->EnsureInnerWindow(); |
1660 | 0 | nsGlobalWindowOuter* global = nsGlobalWindowOuter::Cast(subframe); |
1661 | 0 | JSObject* obj = global->FastGetGlobalJSObject(); |
1662 | 0 | if (MOZ_UNLIKELY(!obj)) { |
1663 | 0 | // It's gone? |
1664 | 0 | return xpc::Throw(cx, NS_ERROR_FAILURE); |
1665 | 0 | } |
1666 | 0 | ExposeObjectToActiveJS(obj); |
1667 | 0 | desc.value().setObject(*obj); |
1668 | 0 | FillPropertyDescriptor(desc, wrapper, true); |
1669 | 0 | return JS_WrapPropertyDescriptor(cx, desc); |
1670 | 0 | } |
1671 | 0 | } |
1672 | 0 | } |
1673 | 0 |
|
1674 | 0 | if (!JS_GetOwnPropertyDescriptorById(cx, holder, id, desc)) { |
1675 | 0 | return false; |
1676 | 0 | } |
1677 | 0 | if (desc.object()) { |
1678 | 0 | desc.object().set(wrapper); |
1679 | 0 | return true; |
1680 | 0 | } |
1681 | 0 | |
1682 | 0 | bool cacheOnHolder; |
1683 | 0 | if (!XrayResolveOwnProperty(cx, wrapper, target, id, desc, cacheOnHolder)) { |
1684 | 0 | return false; |
1685 | 0 | } |
1686 | 0 | |
1687 | 0 | MOZ_ASSERT(!desc.object() || desc.object() == wrapper, "What did we resolve this on?"); |
1688 | 0 |
|
1689 | 0 | if (!desc.object() || !cacheOnHolder) { |
1690 | 0 | return true; |
1691 | 0 | } |
1692 | 0 | |
1693 | 0 | return JS_DefinePropertyById(cx, holder, id, desc) && |
1694 | 0 | JS_GetOwnPropertyDescriptorById(cx, holder, id, desc); |
1695 | 0 | } |
1696 | | |
1697 | | bool |
1698 | | DOMXrayTraits::delete_(JSContext* cx, JS::HandleObject wrapper, |
1699 | | JS::HandleId id, JS::ObjectOpResult& result) |
1700 | 0 | { |
1701 | 0 | RootedObject target(cx, getTargetObject(wrapper)); |
1702 | 0 | return XrayDeleteNamedProperty(cx, wrapper, target, id, result); |
1703 | 0 | } |
1704 | | |
1705 | | bool |
1706 | | DOMXrayTraits::defineProperty(JSContext* cx, HandleObject wrapper, HandleId id, |
1707 | | Handle<PropertyDescriptor> desc, |
1708 | | Handle<PropertyDescriptor> existingDesc, |
1709 | | JS::ObjectOpResult& result, bool* defined) |
1710 | 0 | { |
1711 | 0 | // Check for an indexed property on a Window. If that's happening, do |
1712 | 0 | // nothing but claim we defined it so it won't get added as an expando. |
1713 | 0 | if (IsWindow(cx, wrapper)) { |
1714 | 0 | if (IsArrayIndex(GetArrayIndexFromId(cx, id))) { |
1715 | 0 | *defined = true; |
1716 | 0 | return result.succeed(); |
1717 | 0 | } |
1718 | 0 | } |
1719 | 0 | |
1720 | 0 | JS::Rooted<JSObject*> obj(cx, getTargetObject(wrapper)); |
1721 | 0 | return XrayDefineProperty(cx, wrapper, obj, id, desc, result, defined); |
1722 | 0 | } |
1723 | | |
1724 | | bool |
1725 | | DOMXrayTraits::enumerateNames(JSContext* cx, HandleObject wrapper, unsigned flags, |
1726 | | AutoIdVector& props) |
1727 | 0 | { |
1728 | 0 | // Put the indexed properties for a window first. |
1729 | 0 | nsGlobalWindowInner* win = AsWindow(cx, wrapper); |
1730 | 0 | if (win) { |
1731 | 0 | uint32_t length = win->Length(); |
1732 | 0 | if (!props.reserve(props.length() + length)) { |
1733 | 0 | return false; |
1734 | 0 | } |
1735 | 0 | JS::RootedId indexId(cx); |
1736 | 0 | for (uint32_t i = 0; i < length; ++i) { |
1737 | 0 | if (!JS_IndexToId(cx, i, &indexId)) { |
1738 | 0 | return false; |
1739 | 0 | } |
1740 | 0 | props.infallibleAppend(indexId); |
1741 | 0 | } |
1742 | 0 | } |
1743 | 0 |
|
1744 | 0 | JS::Rooted<JSObject*> obj(cx, getTargetObject(wrapper)); |
1745 | 0 | return XrayOwnPropertyKeys(cx, wrapper, obj, flags, props); |
1746 | 0 | } |
1747 | | |
1748 | | bool |
1749 | | DOMXrayTraits::call(JSContext* cx, HandleObject wrapper, |
1750 | | const JS::CallArgs& args, const js::Wrapper& baseInstance) |
1751 | 0 | { |
1752 | 0 | RootedObject obj(cx, getTargetObject(wrapper)); |
1753 | 0 | const js::Class* clasp = js::GetObjectClass(obj); |
1754 | 0 | // What we have is either a WebIDL interface object, a WebIDL prototype |
1755 | 0 | // object, or a WebIDL instance object. WebIDL prototype objects never have |
1756 | 0 | // a clasp->call. WebIDL interface objects we want to invoke on the xray |
1757 | 0 | // compartment. WebIDL instance objects either don't have a clasp->call or |
1758 | 0 | // are using "legacycaller", which basically means plug-ins. We want to |
1759 | 0 | // call those on the content compartment. |
1760 | 0 | if (clasp->flags & JSCLASS_IS_DOMIFACEANDPROTOJSCLASS) { |
1761 | 0 | if (JSNative call = clasp->getCall()) { |
1762 | 0 | // call it on the Xray compartment |
1763 | 0 | if (!call(cx, args.length(), args.base())) { |
1764 | 0 | return false; |
1765 | 0 | } |
1766 | 0 | } else { |
1767 | 0 | RootedValue v(cx, ObjectValue(*wrapper)); |
1768 | 0 | js::ReportIsNotFunction(cx, v); |
1769 | 0 | return false; |
1770 | 0 | } |
1771 | 0 | } else { |
1772 | 0 | // This is only reached for WebIDL instance objects, and in practice |
1773 | 0 | // only for plugins. Just call them on the content compartment. |
1774 | 0 | if (!baseInstance.call(cx, wrapper, args)) { |
1775 | 0 | return false; |
1776 | 0 | } |
1777 | 0 | } |
1778 | 0 | return JS_WrapValue(cx, args.rval()); |
1779 | 0 | } |
1780 | | |
1781 | | bool |
1782 | | DOMXrayTraits::construct(JSContext* cx, HandleObject wrapper, |
1783 | | const JS::CallArgs& args, const js::Wrapper& baseInstance) |
1784 | 0 | { |
1785 | 0 | RootedObject obj(cx, getTargetObject(wrapper)); |
1786 | 0 | MOZ_ASSERT(mozilla::dom::HasConstructor(obj)); |
1787 | 0 | const js::Class* clasp = js::GetObjectClass(obj); |
1788 | 0 | // See comments in DOMXrayTraits::call() explaining what's going on here. |
1789 | 0 | if (clasp->flags & JSCLASS_IS_DOMIFACEANDPROTOJSCLASS) { |
1790 | 0 | if (JSNative construct = clasp->getConstruct()) { |
1791 | 0 | if (!construct(cx, args.length(), args.base())) { |
1792 | 0 | return false; |
1793 | 0 | } |
1794 | 0 | } else { |
1795 | 0 | RootedValue v(cx, ObjectValue(*wrapper)); |
1796 | 0 | js::ReportIsNotFunction(cx, v); |
1797 | 0 | return false; |
1798 | 0 | } |
1799 | 0 | } else { |
1800 | 0 | if (!baseInstance.construct(cx, wrapper, args)) { |
1801 | 0 | return false; |
1802 | 0 | } |
1803 | 0 | } |
1804 | 0 | if (!args.rval().isObject() || !JS_WrapValue(cx, args.rval())) { |
1805 | 0 | return false; |
1806 | 0 | } |
1807 | 0 | return true; |
1808 | 0 | } |
1809 | | |
1810 | | bool |
1811 | | DOMXrayTraits::getPrototype(JSContext* cx, JS::HandleObject wrapper, |
1812 | | JS::HandleObject target, |
1813 | | JS::MutableHandleObject protop) |
1814 | 0 | { |
1815 | 0 | return mozilla::dom::XrayGetNativeProto(cx, target, protop); |
1816 | 0 | } |
1817 | | |
1818 | | void |
1819 | | DOMXrayTraits::preserveWrapper(JSObject* target) |
1820 | 0 | { |
1821 | 0 | nsISupports* identity = mozilla::dom::UnwrapDOMObjectToISupports(target); |
1822 | 0 | if (!identity) { |
1823 | 0 | return; |
1824 | 0 | } |
1825 | 0 | nsWrapperCache* cache = nullptr; |
1826 | 0 | CallQueryInterface(identity, &cache); |
1827 | 0 | if (cache) { |
1828 | 0 | cache->PreserveWrapper(identity); |
1829 | 0 | } |
1830 | 0 | } |
1831 | | |
1832 | | JSObject* |
1833 | | DOMXrayTraits::createHolder(JSContext* cx, JSObject* wrapper) |
1834 | 0 | { |
1835 | 0 | return JS_NewObjectWithGivenProto(cx, &HolderClass, nullptr); |
1836 | 0 | } |
1837 | | |
1838 | | const JSClass* |
1839 | | DOMXrayTraits::getExpandoClass(JSContext* cx, HandleObject target) const |
1840 | 0 | { |
1841 | 0 | return XrayGetExpandoClass(cx, target); |
1842 | 0 | } |
1843 | | |
1844 | | namespace XrayUtils { |
1845 | | |
1846 | | bool |
1847 | | HasNativeProperty(JSContext* cx, HandleObject wrapper, HandleId id, bool* hasProp) |
1848 | 0 | { |
1849 | 0 | MOZ_ASSERT(WrapperFactory::IsXrayWrapper(wrapper)); |
1850 | 0 | XrayTraits* traits = GetXrayTraits(wrapper); |
1851 | 0 | MOZ_ASSERT(traits); |
1852 | 0 | RootedObject target(cx, XrayTraits::getTargetObject(wrapper)); |
1853 | 0 | RootedObject holder(cx, traits->ensureHolder(cx, wrapper)); |
1854 | 0 | NS_ENSURE_TRUE(holder, false); |
1855 | 0 | *hasProp = false; |
1856 | 0 | Rooted<PropertyDescriptor> desc(cx); |
1857 | 0 |
|
1858 | 0 | // Try resolveOwnProperty. |
1859 | 0 | if (!traits->resolveOwnProperty(cx, wrapper, target, holder, id, &desc)) { |
1860 | 0 | return false; |
1861 | 0 | } |
1862 | 0 | if (desc.object()) { |
1863 | 0 | *hasProp = true; |
1864 | 0 | return true; |
1865 | 0 | } |
1866 | 0 | |
1867 | 0 | // Try the holder. |
1868 | 0 | return JS_AlreadyHasOwnPropertyById(cx, holder, id, hasProp); |
1869 | 0 | } |
1870 | | |
1871 | | } // namespace XrayUtils |
1872 | | |
1873 | | |
1874 | | template <typename Base, typename Traits> |
1875 | | bool |
1876 | | XrayWrapper<Base, Traits>::preventExtensions(JSContext* cx, HandleObject wrapper, |
1877 | | ObjectOpResult& result) const |
1878 | 0 | { |
1879 | 0 | // Xray wrappers are supposed to provide a clean view of the target |
1880 | 0 | // reflector, hiding any modifications by script in the target scope. So |
1881 | 0 | // even if that script freezes the reflector, we don't want to make that |
1882 | 0 | // visible to the caller. DOM reflectors are always extensible by default, |
1883 | 0 | // so we can just return failure here. |
1884 | 0 | return result.failCantPreventExtensions(); |
1885 | 0 | } Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::DOMXrayTraits>::preventExtensions(JSContext*, JS::Handle<JSObject*>, JS::ObjectOpResult&) const Unexecuted instantiation: xpc::XrayWrapper<js::SecurityWrapper<js::CrossCompartmentWrapper>, xpc::DOMXrayTraits>::preventExtensions(JSContext*, JS::Handle<JSObject*>, JS::ObjectOpResult&) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::JSXrayTraits>::preventExtensions(JSContext*, JS::Handle<JSObject*>, JS::ObjectOpResult&) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::OpaqueXrayTraits>::preventExtensions(JSContext*, JS::Handle<JSObject*>, JS::ObjectOpResult&) const |
1886 | | |
1887 | | template <typename Base, typename Traits> |
1888 | | bool |
1889 | | XrayWrapper<Base, Traits>::isExtensible(JSContext* cx, JS::Handle<JSObject*> wrapper, |
1890 | | bool* extensible) const |
1891 | 0 | { |
1892 | 0 | // See above. |
1893 | 0 | *extensible = true; |
1894 | 0 | return true; |
1895 | 0 | } Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::DOMXrayTraits>::isExtensible(JSContext*, JS::Handle<JSObject*>, bool*) const Unexecuted instantiation: xpc::XrayWrapper<js::SecurityWrapper<js::CrossCompartmentWrapper>, xpc::DOMXrayTraits>::isExtensible(JSContext*, JS::Handle<JSObject*>, bool*) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::JSXrayTraits>::isExtensible(JSContext*, JS::Handle<JSObject*>, bool*) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::OpaqueXrayTraits>::isExtensible(JSContext*, JS::Handle<JSObject*>, bool*) const |
1896 | | |
1897 | | template <typename Base, typename Traits> |
1898 | | bool |
1899 | | XrayWrapper<Base, Traits>::getPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id, |
1900 | | JS::MutableHandle<PropertyDescriptor> desc) |
1901 | | const |
1902 | 0 | { |
1903 | 0 | // CrossOriginXrayWrapper::getOwnPropertyDescriptor calls this. |
1904 | 0 |
|
1905 | 0 | assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::GET | BaseProxyHandler::SET | |
1906 | 0 | BaseProxyHandler::GET_PROPERTY_DESCRIPTOR); |
1907 | 0 | RootedObject target(cx, Traits::getTargetObject(wrapper)); |
1908 | 0 | RootedObject holder(cx, Traits::singleton.ensureHolder(cx, wrapper)); |
1909 | 0 |
|
1910 | 0 | if (!holder) { |
1911 | 0 | return false; |
1912 | 0 | } |
1913 | 0 | |
1914 | 0 | // Ordering is important here. |
1915 | 0 | // |
1916 | 0 | // We first need to call resolveOwnProperty, even before checking the holder, |
1917 | 0 | // because there might be a new dynamic |own| property that appears and |
1918 | 0 | // shadows a previously-resolved non-own property that we cached on the |
1919 | 0 | // holder. This can happen with indexed properties on NodeLists, for example, |
1920 | 0 | // which are |own| value props. |
1921 | 0 | // |
1922 | 0 | // resolveOwnProperty may or may not cache what it finds on the holder, |
1923 | 0 | // depending on how ephemeral it decides the property is. This means that we have to |
1924 | 0 | // first check the result of resolveOwnProperty, and _then_, if that comes up blank, |
1925 | 0 | // check the holder for any cached native properties. |
1926 | 0 | |
1927 | 0 | // Check resolveOwnProperty. |
1928 | 0 | if (!Traits::singleton.resolveOwnProperty(cx, wrapper, target, holder, id, desc)) { |
1929 | 0 | return false; |
1930 | 0 | } |
1931 | 0 | |
1932 | 0 | // Check the holder. |
1933 | 0 | if (!desc.object() && !JS_GetOwnPropertyDescriptorById(cx, holder, id, desc)) { |
1934 | 0 | return false; |
1935 | 0 | } |
1936 | 0 | if (desc.object()) { |
1937 | 0 | desc.object().set(wrapper); |
1938 | 0 | return true; |
1939 | 0 | } |
1940 | 0 | |
1941 | 0 | // We need to handle named access on the Window somewhere other than |
1942 | 0 | // Traits::resolveOwnProperty, because per spec it happens on the Global |
1943 | 0 | // Scope Polluter and thus the resulting properties are non-|own|. However, |
1944 | 0 | // we're set up (above) to cache (on the holder), |
1945 | 0 | // which we don't want for something dynamic like named access. |
1946 | 0 | // So we just handle it separately here. Note that this is |
1947 | 0 | // only relevant for CrossOriginXrayWrapper, which calls |
1948 | 0 | // getPropertyDescriptor from getOwnPropertyDescriptor. |
1949 | 0 | nsGlobalWindowInner* win = nullptr; |
1950 | 0 | if (!desc.object() && |
1951 | 0 | JSID_IS_STRING(id) && |
1952 | 0 | (win = AsWindow(cx, wrapper))) |
1953 | 0 | { |
1954 | 0 | nsAutoJSString name; |
1955 | 0 | if (!name.init(cx, JSID_TO_STRING(id))) { |
1956 | 0 | return false; |
1957 | 0 | } |
1958 | 0 | if (nsCOMPtr<nsPIDOMWindowOuter> childDOMWin = win->GetChildWindow(name)) { |
1959 | 0 | auto* cwin = nsGlobalWindowOuter::Cast(childDOMWin); |
1960 | 0 | JSObject* childObj = cwin->FastGetGlobalJSObject(); |
1961 | 0 | if (MOZ_UNLIKELY(!childObj)) { |
1962 | 0 | return xpc::Throw(cx, NS_ERROR_FAILURE); |
1963 | 0 | } |
1964 | 0 | ExposeObjectToActiveJS(childObj); |
1965 | 0 | FillPropertyDescriptor(desc, wrapper, ObjectValue(*childObj), |
1966 | 0 | /* readOnly = */ true); |
1967 | 0 | return JS_WrapPropertyDescriptor(cx, desc); |
1968 | 0 | } |
1969 | 0 | } |
1970 | 0 |
|
1971 | 0 | // We found nothing, we're done. |
1972 | 0 | MOZ_ASSERT(!desc.object()); |
1973 | 0 | return true; |
1974 | 0 | } Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::DOMXrayTraits>::getPropertyDescriptor(JSContext*, JS::Handle<JSObject*>, JS::Handle<jsid>, JS::MutableHandle<JS::PropertyDescriptor>) const Unexecuted instantiation: xpc::XrayWrapper<js::SecurityWrapper<js::CrossCompartmentWrapper>, xpc::DOMXrayTraits>::getPropertyDescriptor(JSContext*, JS::Handle<JSObject*>, JS::Handle<jsid>, JS::MutableHandle<JS::PropertyDescriptor>) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::JSXrayTraits>::getPropertyDescriptor(JSContext*, JS::Handle<JSObject*>, JS::Handle<jsid>, JS::MutableHandle<JS::PropertyDescriptor>) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::OpaqueXrayTraits>::getPropertyDescriptor(JSContext*, JS::Handle<JSObject*>, JS::Handle<jsid>, JS::MutableHandle<JS::PropertyDescriptor>) const |
1975 | | |
1976 | | template <typename Base, typename Traits> |
1977 | | bool |
1978 | | XrayWrapper<Base, Traits>::getOwnPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id, |
1979 | | JS::MutableHandle<PropertyDescriptor> desc) |
1980 | | const |
1981 | 0 | { |
1982 | 0 | assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::GET | BaseProxyHandler::SET | |
1983 | 0 | BaseProxyHandler::GET_PROPERTY_DESCRIPTOR); |
1984 | 0 | RootedObject target(cx, Traits::getTargetObject(wrapper)); |
1985 | 0 | RootedObject holder(cx, Traits::singleton.ensureHolder(cx, wrapper)); |
1986 | 0 | if (!holder) { |
1987 | 0 | return false; |
1988 | 0 | } |
1989 | 0 | |
1990 | 0 | if (!Traits::singleton.resolveOwnProperty(cx, wrapper, target, holder, id, desc)) { |
1991 | 0 | return false; |
1992 | 0 | } |
1993 | 0 | if (desc.object()) { |
1994 | 0 | desc.object().set(wrapper); |
1995 | 0 | } |
1996 | 0 | return true; |
1997 | 0 | } Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::DOMXrayTraits>::getOwnPropertyDescriptor(JSContext*, JS::Handle<JSObject*>, JS::Handle<jsid>, JS::MutableHandle<JS::PropertyDescriptor>) const Unexecuted instantiation: xpc::XrayWrapper<js::SecurityWrapper<js::CrossCompartmentWrapper>, xpc::DOMXrayTraits>::getOwnPropertyDescriptor(JSContext*, JS::Handle<JSObject*>, JS::Handle<jsid>, JS::MutableHandle<JS::PropertyDescriptor>) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::JSXrayTraits>::getOwnPropertyDescriptor(JSContext*, JS::Handle<JSObject*>, JS::Handle<jsid>, JS::MutableHandle<JS::PropertyDescriptor>) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::OpaqueXrayTraits>::getOwnPropertyDescriptor(JSContext*, JS::Handle<JSObject*>, JS::Handle<jsid>, JS::MutableHandle<JS::PropertyDescriptor>) const |
1998 | | |
1999 | | // Consider what happens when chrome does |xray.expando = xray.wrappedJSObject|. |
2000 | | // |
2001 | | // Since the expando comes from the target compartment, wrapping it back into |
2002 | | // the target compartment to define it on the expando object ends up stripping |
2003 | | // off the Xray waiver that gives |xray| and |xray.wrappedJSObject| different |
2004 | | // identities. This is generally the right thing to do when wrapping across |
2005 | | // compartments, but is incorrect in the special case of the Xray expando |
2006 | | // object. Manually re-apply Xrays if necessary. |
2007 | | // |
2008 | | // NB: In order to satisfy the invariants of WaiveXray, we need to pass |
2009 | | // in an object sans security wrapper, which means we need to strip off any |
2010 | | // potential same-compartment security wrapper that may have been applied |
2011 | | // to the content object. This is ok, because the the expando object is only |
2012 | | // ever accessed by code across the compartment boundary. |
2013 | | static bool |
2014 | | RecreateLostWaivers(JSContext* cx, const PropertyDescriptor* orig, |
2015 | | MutableHandle<PropertyDescriptor> wrapped) |
2016 | 0 | { |
2017 | 0 | // Compute whether the original objects were waived, and implicitly, whether |
2018 | 0 | // they were objects at all. |
2019 | 0 | bool valueWasWaived = |
2020 | 0 | orig->value.isObject() && |
2021 | 0 | WrapperFactory::HasWaiveXrayFlag(&orig->value.toObject()); |
2022 | 0 | bool getterWasWaived = |
2023 | 0 | (orig->attrs & JSPROP_GETTER) && orig->getter && |
2024 | 0 | WrapperFactory::HasWaiveXrayFlag(JS_FUNC_TO_DATA_PTR(JSObject*, orig->getter)); |
2025 | 0 | bool setterWasWaived = |
2026 | 0 | (orig->attrs & JSPROP_SETTER) && orig->setter && |
2027 | 0 | WrapperFactory::HasWaiveXrayFlag(JS_FUNC_TO_DATA_PTR(JSObject*, orig->setter)); |
2028 | 0 |
|
2029 | 0 | // Recreate waivers. Note that for value, we need an extra UncheckedUnwrap |
2030 | 0 | // to handle same-compartment security wrappers (see above). This should |
2031 | 0 | // never happen for getters/setters. |
2032 | 0 |
|
2033 | 0 | RootedObject rewaived(cx); |
2034 | 0 | if (valueWasWaived && !IsCrossCompartmentWrapper(&wrapped.value().toObject())) { |
2035 | 0 | rewaived = &wrapped.value().toObject(); |
2036 | 0 | rewaived = WrapperFactory::WaiveXray(cx, UncheckedUnwrap(rewaived)); |
2037 | 0 | NS_ENSURE_TRUE(rewaived, false); |
2038 | 0 | wrapped.value().set(ObjectValue(*rewaived)); |
2039 | 0 | } |
2040 | 0 | if (getterWasWaived && !IsCrossCompartmentWrapper(wrapped.getterObject())) { |
2041 | 0 | MOZ_ASSERT(CheckedUnwrap(wrapped.getterObject())); |
2042 | 0 | rewaived = WrapperFactory::WaiveXray(cx, wrapped.getterObject()); |
2043 | 0 | NS_ENSURE_TRUE(rewaived, false); |
2044 | 0 | wrapped.setGetterObject(rewaived); |
2045 | 0 | } |
2046 | 0 | if (setterWasWaived && !IsCrossCompartmentWrapper(wrapped.setterObject())) { |
2047 | 0 | MOZ_ASSERT(CheckedUnwrap(wrapped.setterObject())); |
2048 | 0 | rewaived = WrapperFactory::WaiveXray(cx, wrapped.setterObject()); |
2049 | 0 | NS_ENSURE_TRUE(rewaived, false); |
2050 | 0 | wrapped.setSetterObject(rewaived); |
2051 | 0 | } |
2052 | 0 |
|
2053 | 0 | return true; |
2054 | 0 | } |
2055 | | |
2056 | | template <typename Base, typename Traits> |
2057 | | bool |
2058 | | XrayWrapper<Base, Traits>::defineProperty(JSContext* cx, HandleObject wrapper, |
2059 | | HandleId id, Handle<PropertyDescriptor> desc, |
2060 | | ObjectOpResult& result) const |
2061 | 0 | { |
2062 | 0 | assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::SET); |
2063 | 0 |
|
2064 | 0 | Rooted<PropertyDescriptor> existing_desc(cx); |
2065 | 0 | if (!JS_GetPropertyDescriptorById(cx, wrapper, id, &existing_desc)) { |
2066 | 0 | return false; |
2067 | 0 | } |
2068 | 0 | |
2069 | 0 | // Note that the check here is intended to differentiate between own and |
2070 | 0 | // non-own properties, since the above lookup is not limited to own |
2071 | 0 | // properties. At present, this may not always do the right thing because |
2072 | 0 | // we often lie (sloppily) about where we found properties and set |
2073 | 0 | // desc.object() to |wrapper|. Once we fully fix our Xray prototype semantics, |
2074 | 0 | // this should work as intended. |
2075 | 0 | if (existing_desc.object() == wrapper && !existing_desc.configurable()) { |
2076 | 0 | // We have a non-configurable property. See if the caller is trying to |
2077 | 0 | // re-configure it in any way other than making it non-writable. |
2078 | 0 | if (existing_desc.isAccessorDescriptor() || desc.isAccessorDescriptor() || |
2079 | 0 | (desc.hasEnumerable() && existing_desc.enumerable() != desc.enumerable()) || |
2080 | 0 | (desc.hasWritable() && !existing_desc.writable() && desc.writable())) |
2081 | 0 | { |
2082 | 0 | // We should technically report non-configurability in strict mode, but |
2083 | 0 | // doing that via JSAPI used to be a lot of trouble. See bug 1135997. |
2084 | 0 | return result.succeed(); |
2085 | 0 | } |
2086 | 0 | if (!existing_desc.writable()) { |
2087 | 0 | // Same as the above for non-writability. |
2088 | 0 | return result.succeed(); |
2089 | 0 | } |
2090 | 0 | } |
2091 | 0 | |
2092 | 0 | bool defined = false; |
2093 | 0 | if (!Traits::singleton.defineProperty(cx, wrapper, id, desc, existing_desc, result, &defined)) { |
2094 | 0 | return false; |
2095 | 0 | } |
2096 | 0 | if (defined) { |
2097 | 0 | return true; |
2098 | 0 | } |
2099 | 0 | |
2100 | 0 | // Grab the relevant expando object. |
2101 | 0 | RootedObject target(cx, Traits::getTargetObject(wrapper)); |
2102 | 0 | RootedObject expandoObject(cx, Traits::singleton.ensureExpandoObject(cx, wrapper, |
2103 | 0 | target)); |
2104 | 0 | if (!expandoObject) { |
2105 | 0 | return false; |
2106 | 0 | } |
2107 | 0 | |
2108 | 0 | // We're placing an expando. The expando objects live in the target |
2109 | 0 | // compartment, so we need to enter it. |
2110 | 0 | JSAutoRealm ar(cx, target); |
2111 | 0 | JS_MarkCrossZoneId(cx, id); |
2112 | 0 |
|
2113 | 0 | // Wrap the property descriptor for the target compartment. |
2114 | 0 | Rooted<PropertyDescriptor> wrappedDesc(cx, desc); |
2115 | 0 | if (!JS_WrapPropertyDescriptor(cx, &wrappedDesc)) { |
2116 | 0 | return false; |
2117 | 0 | } |
2118 | 0 | |
2119 | 0 | // Fix up Xray waivers. |
2120 | 0 | if (!RecreateLostWaivers(cx, desc.address(), &wrappedDesc)) { |
2121 | 0 | return false; |
2122 | 0 | } |
2123 | 0 | |
2124 | 0 | return JS_DefinePropertyById(cx, expandoObject, id, wrappedDesc, result); |
2125 | 0 | } Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::DOMXrayTraits>::defineProperty(JSContext*, JS::Handle<JSObject*>, JS::Handle<jsid>, JS::Handle<JS::PropertyDescriptor>, JS::ObjectOpResult&) const Unexecuted instantiation: xpc::XrayWrapper<js::SecurityWrapper<js::CrossCompartmentWrapper>, xpc::DOMXrayTraits>::defineProperty(JSContext*, JS::Handle<JSObject*>, JS::Handle<jsid>, JS::Handle<JS::PropertyDescriptor>, JS::ObjectOpResult&) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::JSXrayTraits>::defineProperty(JSContext*, JS::Handle<JSObject*>, JS::Handle<jsid>, JS::Handle<JS::PropertyDescriptor>, JS::ObjectOpResult&) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::OpaqueXrayTraits>::defineProperty(JSContext*, JS::Handle<JSObject*>, JS::Handle<jsid>, JS::Handle<JS::PropertyDescriptor>, JS::ObjectOpResult&) const |
2126 | | |
2127 | | template <typename Base, typename Traits> |
2128 | | bool |
2129 | | XrayWrapper<Base, Traits>::ownPropertyKeys(JSContext* cx, HandleObject wrapper, |
2130 | | AutoIdVector& props) const |
2131 | 0 | { |
2132 | 0 | assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::ENUMERATE); |
2133 | 0 | return getPropertyKeys(cx, wrapper, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, props); |
2134 | 0 | } Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::DOMXrayTraits>::ownPropertyKeys(JSContext*, JS::Handle<JSObject*>, JS::AutoVector<jsid>&) const Unexecuted instantiation: xpc::XrayWrapper<js::SecurityWrapper<js::CrossCompartmentWrapper>, xpc::DOMXrayTraits>::ownPropertyKeys(JSContext*, JS::Handle<JSObject*>, JS::AutoVector<jsid>&) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::JSXrayTraits>::ownPropertyKeys(JSContext*, JS::Handle<JSObject*>, JS::AutoVector<jsid>&) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::OpaqueXrayTraits>::ownPropertyKeys(JSContext*, JS::Handle<JSObject*>, JS::AutoVector<jsid>&) const |
2135 | | |
2136 | | template <typename Base, typename Traits> |
2137 | | bool |
2138 | | XrayWrapper<Base, Traits>::delete_(JSContext* cx, HandleObject wrapper, |
2139 | | HandleId id, ObjectOpResult& result) const |
2140 | 0 | { |
2141 | 0 | assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::SET); |
2142 | 0 |
|
2143 | 0 | // Check the expando object. |
2144 | 0 | RootedObject target(cx, Traits::getTargetObject(wrapper)); |
2145 | 0 | RootedObject expando(cx); |
2146 | 0 | if (!Traits::singleton.getExpandoObject(cx, target, wrapper, &expando)) { |
2147 | 0 | return false; |
2148 | 0 | } |
2149 | 0 | |
2150 | 0 | if (expando) { |
2151 | 0 | JSAutoRealm ar(cx, expando); |
2152 | 0 | JS_MarkCrossZoneId(cx, id); |
2153 | 0 | bool hasProp; |
2154 | 0 | if (!JS_HasPropertyById(cx, expando, id, &hasProp)) { |
2155 | 0 | return false; |
2156 | 0 | } |
2157 | 0 | if (hasProp) { |
2158 | 0 | return JS_DeletePropertyById(cx, expando, id, result); |
2159 | 0 | } |
2160 | 0 | } |
2161 | 0 | |
2162 | 0 | return Traits::singleton.delete_(cx, wrapper, id, result); |
2163 | 0 | } Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::DOMXrayTraits>::delete_(JSContext*, JS::Handle<JSObject*>, JS::Handle<jsid>, JS::ObjectOpResult&) const Unexecuted instantiation: xpc::XrayWrapper<js::SecurityWrapper<js::CrossCompartmentWrapper>, xpc::DOMXrayTraits>::delete_(JSContext*, JS::Handle<JSObject*>, JS::Handle<jsid>, JS::ObjectOpResult&) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::JSXrayTraits>::delete_(JSContext*, JS::Handle<JSObject*>, JS::Handle<jsid>, JS::ObjectOpResult&) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::OpaqueXrayTraits>::delete_(JSContext*, JS::Handle<JSObject*>, JS::Handle<jsid>, JS::ObjectOpResult&) const |
2164 | | |
2165 | | template <typename Base, typename Traits> |
2166 | | bool |
2167 | | XrayWrapper<Base, Traits>::get(JSContext* cx, HandleObject wrapper, |
2168 | | HandleValue receiver, HandleId id, |
2169 | | MutableHandleValue vp) const |
2170 | 0 | { |
2171 | 0 | // Skip our Base if it isn't already ProxyHandler. |
2172 | 0 |
|
2173 | 0 | // This uses getPropertyDescriptor for backward compatibility with |
2174 | 0 | // the old BaseProxyHandler::get implementation. |
2175 | 0 | Rooted<PropertyDescriptor> desc(cx); |
2176 | 0 | if (!getPropertyDescriptor(cx, wrapper, id, &desc)) { |
2177 | 0 | return false; |
2178 | 0 | } |
2179 | 0 | desc.assertCompleteIfFound(); |
2180 | 0 |
|
2181 | 0 | if (!desc.object()) { |
2182 | 0 | vp.setUndefined(); |
2183 | 0 | return true; |
2184 | 0 | } |
2185 | 0 | |
2186 | 0 | // Everything after here follows [[Get]] for ordinary objects. |
2187 | 0 | if (desc.isDataDescriptor()) { |
2188 | 0 | vp.set(desc.value()); |
2189 | 0 | return true; |
2190 | 0 | } |
2191 | 0 | |
2192 | 0 | MOZ_ASSERT(desc.isAccessorDescriptor()); |
2193 | 0 | RootedObject getter(cx, desc.getterObject()); |
2194 | 0 |
|
2195 | 0 | if (!getter) { |
2196 | 0 | vp.setUndefined(); |
2197 | 0 | return true; |
2198 | 0 | } |
2199 | 0 | |
2200 | 0 | return Call(cx, receiver, getter, HandleValueArray::empty(), vp); |
2201 | 0 | } Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::DOMXrayTraits>::get(JSContext*, JS::Handle<JSObject*>, JS::Handle<JS::Value>, JS::Handle<jsid>, JS::MutableHandle<JS::Value>) const Unexecuted instantiation: xpc::XrayWrapper<js::SecurityWrapper<js::CrossCompartmentWrapper>, xpc::DOMXrayTraits>::get(JSContext*, JS::Handle<JSObject*>, JS::Handle<JS::Value>, JS::Handle<jsid>, JS::MutableHandle<JS::Value>) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::JSXrayTraits>::get(JSContext*, JS::Handle<JSObject*>, JS::Handle<JS::Value>, JS::Handle<jsid>, JS::MutableHandle<JS::Value>) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::OpaqueXrayTraits>::get(JSContext*, JS::Handle<JSObject*>, JS::Handle<JS::Value>, JS::Handle<jsid>, JS::MutableHandle<JS::Value>) const |
2202 | | |
2203 | | template <typename Base, typename Traits> |
2204 | | bool |
2205 | | XrayWrapper<Base, Traits>::set(JSContext* cx, HandleObject wrapper, HandleId id, HandleValue v, |
2206 | | HandleValue receiver, ObjectOpResult& result) const |
2207 | 0 | { |
2208 | 0 | MOZ_CRASH("Shouldn't be called"); |
2209 | 0 | return false; |
2210 | 0 | } Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::DOMXrayTraits>::set(JSContext*, JS::Handle<JSObject*>, JS::Handle<jsid>, JS::Handle<JS::Value>, JS::Handle<JS::Value>, JS::ObjectOpResult&) const Unexecuted instantiation: xpc::XrayWrapper<js::SecurityWrapper<js::CrossCompartmentWrapper>, xpc::DOMXrayTraits>::set(JSContext*, JS::Handle<JSObject*>, JS::Handle<jsid>, JS::Handle<JS::Value>, JS::Handle<JS::Value>, JS::ObjectOpResult&) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::JSXrayTraits>::set(JSContext*, JS::Handle<JSObject*>, JS::Handle<jsid>, JS::Handle<JS::Value>, JS::Handle<JS::Value>, JS::ObjectOpResult&) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::OpaqueXrayTraits>::set(JSContext*, JS::Handle<JSObject*>, JS::Handle<jsid>, JS::Handle<JS::Value>, JS::Handle<JS::Value>, JS::ObjectOpResult&) const |
2211 | | |
2212 | | template <typename Base, typename Traits> |
2213 | | bool |
2214 | | XrayWrapper<Base, Traits>::has(JSContext* cx, HandleObject wrapper, |
2215 | | HandleId id, bool* bp) const |
2216 | 0 | { |
2217 | 0 | // This uses getPropertyDescriptor for backward compatibility with |
2218 | 0 | // the old BaseProxyHandler::has implementation. |
2219 | 0 | Rooted<PropertyDescriptor> desc(cx); |
2220 | 0 | if (!getPropertyDescriptor(cx, wrapper, id, &desc)) { |
2221 | 0 | return false; |
2222 | 0 | } |
2223 | 0 | |
2224 | 0 | *bp = !!desc.object(); |
2225 | 0 | return true; |
2226 | 0 | } Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::DOMXrayTraits>::has(JSContext*, JS::Handle<JSObject*>, JS::Handle<jsid>, bool*) const Unexecuted instantiation: xpc::XrayWrapper<js::SecurityWrapper<js::CrossCompartmentWrapper>, xpc::DOMXrayTraits>::has(JSContext*, JS::Handle<JSObject*>, JS::Handle<jsid>, bool*) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::JSXrayTraits>::has(JSContext*, JS::Handle<JSObject*>, JS::Handle<jsid>, bool*) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::OpaqueXrayTraits>::has(JSContext*, JS::Handle<JSObject*>, JS::Handle<jsid>, bool*) const |
2227 | | |
2228 | | template <typename Base, typename Traits> |
2229 | | bool |
2230 | | XrayWrapper<Base, Traits>::hasOwn(JSContext* cx, HandleObject wrapper, |
2231 | | HandleId id, bool* bp) const |
2232 | 0 | { |
2233 | 0 | // Skip our Base if it isn't already ProxyHandler. |
2234 | 0 | return js::BaseProxyHandler::hasOwn(cx, wrapper, id, bp); |
2235 | 0 | } Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::DOMXrayTraits>::hasOwn(JSContext*, JS::Handle<JSObject*>, JS::Handle<jsid>, bool*) const Unexecuted instantiation: xpc::XrayWrapper<js::SecurityWrapper<js::CrossCompartmentWrapper>, xpc::DOMXrayTraits>::hasOwn(JSContext*, JS::Handle<JSObject*>, JS::Handle<jsid>, bool*) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::JSXrayTraits>::hasOwn(JSContext*, JS::Handle<JSObject*>, JS::Handle<jsid>, bool*) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::OpaqueXrayTraits>::hasOwn(JSContext*, JS::Handle<JSObject*>, JS::Handle<jsid>, bool*) const |
2236 | | |
2237 | | template <typename Base, typename Traits> |
2238 | | bool |
2239 | | XrayWrapper<Base, Traits>::getOwnEnumerablePropertyKeys(JSContext* cx, |
2240 | | HandleObject wrapper, |
2241 | | AutoIdVector& props) const |
2242 | 0 | { |
2243 | 0 | // Skip our Base if it isn't already ProxyHandler. |
2244 | 0 | return js::BaseProxyHandler::getOwnEnumerablePropertyKeys(cx, wrapper, props); |
2245 | 0 | } Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::DOMXrayTraits>::getOwnEnumerablePropertyKeys(JSContext*, JS::Handle<JSObject*>, JS::AutoVector<jsid>&) const Unexecuted instantiation: xpc::XrayWrapper<js::SecurityWrapper<js::CrossCompartmentWrapper>, xpc::DOMXrayTraits>::getOwnEnumerablePropertyKeys(JSContext*, JS::Handle<JSObject*>, JS::AutoVector<jsid>&) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::JSXrayTraits>::getOwnEnumerablePropertyKeys(JSContext*, JS::Handle<JSObject*>, JS::AutoVector<jsid>&) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::OpaqueXrayTraits>::getOwnEnumerablePropertyKeys(JSContext*, JS::Handle<JSObject*>, JS::AutoVector<jsid>&) const |
2246 | | |
2247 | | template <typename Base, typename Traits> |
2248 | | JSObject* |
2249 | | XrayWrapper<Base, Traits>::enumerate(JSContext* cx, HandleObject wrapper) const |
2250 | 0 | { |
2251 | 0 | MOZ_CRASH("Shouldn't be called"); |
2252 | 0 | return nullptr; |
2253 | 0 | } Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::DOMXrayTraits>::enumerate(JSContext*, JS::Handle<JSObject*>) const Unexecuted instantiation: xpc::XrayWrapper<js::SecurityWrapper<js::CrossCompartmentWrapper>, xpc::DOMXrayTraits>::enumerate(JSContext*, JS::Handle<JSObject*>) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::JSXrayTraits>::enumerate(JSContext*, JS::Handle<JSObject*>) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::OpaqueXrayTraits>::enumerate(JSContext*, JS::Handle<JSObject*>) const |
2254 | | |
2255 | | template <typename Base, typename Traits> |
2256 | | bool |
2257 | | XrayWrapper<Base, Traits>::call(JSContext* cx, HandleObject wrapper, const JS::CallArgs& args) const |
2258 | 0 | { |
2259 | 0 | assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::CALL); |
2260 | 0 | // Hard cast the singleton since SecurityWrapper doesn't have one. |
2261 | 0 | return Traits::call(cx, wrapper, args, Base::singleton); |
2262 | 0 | } Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::DOMXrayTraits>::call(JSContext*, JS::Handle<JSObject*>, JS::CallArgs const&) const Unexecuted instantiation: xpc::XrayWrapper<js::SecurityWrapper<js::CrossCompartmentWrapper>, xpc::DOMXrayTraits>::call(JSContext*, JS::Handle<JSObject*>, JS::CallArgs const&) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::JSXrayTraits>::call(JSContext*, JS::Handle<JSObject*>, JS::CallArgs const&) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::OpaqueXrayTraits>::call(JSContext*, JS::Handle<JSObject*>, JS::CallArgs const&) const |
2263 | | |
2264 | | template <typename Base, typename Traits> |
2265 | | bool |
2266 | | XrayWrapper<Base, Traits>::construct(JSContext* cx, HandleObject wrapper, const JS::CallArgs& args) const |
2267 | 0 | { |
2268 | 0 | assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::CALL); |
2269 | 0 | // Hard cast the singleton since SecurityWrapper doesn't have one. |
2270 | 0 | return Traits::construct(cx, wrapper, args, Base::singleton); |
2271 | 0 | } Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::DOMXrayTraits>::construct(JSContext*, JS::Handle<JSObject*>, JS::CallArgs const&) const Unexecuted instantiation: xpc::XrayWrapper<js::SecurityWrapper<js::CrossCompartmentWrapper>, xpc::DOMXrayTraits>::construct(JSContext*, JS::Handle<JSObject*>, JS::CallArgs const&) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::JSXrayTraits>::construct(JSContext*, JS::Handle<JSObject*>, JS::CallArgs const&) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::OpaqueXrayTraits>::construct(JSContext*, JS::Handle<JSObject*>, JS::CallArgs const&) const |
2272 | | |
2273 | | template <typename Base, typename Traits> |
2274 | | bool |
2275 | | XrayWrapper<Base, Traits>::getBuiltinClass(JSContext* cx, JS::HandleObject wrapper, js::ESClass* cls) const |
2276 | 0 | { |
2277 | 0 | return Traits::getBuiltinClass(cx, wrapper, Base::singleton, cls); |
2278 | 0 | } Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::DOMXrayTraits>::getBuiltinClass(JSContext*, JS::Handle<JSObject*>, js::ESClass*) const Unexecuted instantiation: xpc::XrayWrapper<js::SecurityWrapper<js::CrossCompartmentWrapper>, xpc::DOMXrayTraits>::getBuiltinClass(JSContext*, JS::Handle<JSObject*>, js::ESClass*) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::JSXrayTraits>::getBuiltinClass(JSContext*, JS::Handle<JSObject*>, js::ESClass*) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::OpaqueXrayTraits>::getBuiltinClass(JSContext*, JS::Handle<JSObject*>, js::ESClass*) const |
2279 | | |
2280 | | template <typename Base, typename Traits> |
2281 | | const char* |
2282 | | XrayWrapper<Base, Traits>::className(JSContext* cx, HandleObject wrapper) const |
2283 | 0 | { |
2284 | 0 | return Traits::className(cx, wrapper, Base::singleton); |
2285 | 0 | } Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::DOMXrayTraits>::className(JSContext*, JS::Handle<JSObject*>) const Unexecuted instantiation: xpc::XrayWrapper<js::SecurityWrapper<js::CrossCompartmentWrapper>, xpc::DOMXrayTraits>::className(JSContext*, JS::Handle<JSObject*>) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::JSXrayTraits>::className(JSContext*, JS::Handle<JSObject*>) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::OpaqueXrayTraits>::className(JSContext*, JS::Handle<JSObject*>) const |
2286 | | |
2287 | | template <typename Base, typename Traits> |
2288 | | bool |
2289 | | XrayWrapper<Base, Traits>::getPrototype(JSContext* cx, JS::HandleObject wrapper, |
2290 | | JS::MutableHandleObject protop) const |
2291 | 0 | { |
2292 | 0 | // We really only want this override for non-SecurityWrapper-inheriting |
2293 | 0 | // |Base|. But doing that statically with templates requires partial method |
2294 | 0 | // specializations (and therefore a helper class), which is all more trouble |
2295 | 0 | // than it's worth. Do a dynamic check. |
2296 | 0 | if (Base::hasSecurityPolicy()) { |
2297 | 0 | return Base::getPrototype(cx, wrapper, protop); |
2298 | 0 | } |
2299 | 0 | |
2300 | 0 | RootedObject target(cx, Traits::getTargetObject(wrapper)); |
2301 | 0 | RootedObject expando(cx); |
2302 | 0 | if (!Traits::singleton.getExpandoObject(cx, target, wrapper, &expando)) { |
2303 | 0 | return false; |
2304 | 0 | } |
2305 | 0 | |
2306 | 0 | // We want to keep the Xray's prototype distinct from that of content, but |
2307 | 0 | // only if there's been a set. If there's not an expando, or the expando |
2308 | 0 | // slot is |undefined|, hand back the default proto, appropriately wrapped. |
2309 | 0 | |
2310 | 0 | if (expando) { |
2311 | 0 | RootedValue v(cx); |
2312 | 0 | { // Scope for JSAutoRealm |
2313 | 0 | JSAutoRealm ar(cx, expando); |
2314 | 0 | v = JS_GetReservedSlot(expando, JSSLOT_EXPANDO_PROTOTYPE); |
2315 | 0 | } |
2316 | 0 | if (!v.isUndefined()) { |
2317 | 0 | protop.set(v.toObjectOrNull()); |
2318 | 0 | return JS_WrapObject(cx, protop); |
2319 | 0 | } |
2320 | 0 | } |
2321 | 0 | |
2322 | 0 | // Check our holder, and cache there if we don't have it cached already. |
2323 | 0 | RootedObject holder(cx, Traits::singleton.ensureHolder(cx, wrapper)); |
2324 | 0 | if (!holder) { |
2325 | 0 | return false; |
2326 | 0 | } |
2327 | 0 | |
2328 | 0 | Value cached = js::GetReservedSlot(holder, |
2329 | 0 | Traits::HOLDER_SLOT_CACHED_PROTO); |
2330 | 0 | if (cached.isUndefined()) { |
2331 | 0 | if (!Traits::singleton.getPrototype(cx, wrapper, target, protop)) { |
2332 | 0 | return false; |
2333 | 0 | } |
2334 | 0 | |
2335 | 0 | js::SetReservedSlot(holder, Traits::HOLDER_SLOT_CACHED_PROTO, |
2336 | 0 | ObjectOrNullValue(protop)); |
2337 | 0 | } else { |
2338 | 0 | protop.set(cached.toObjectOrNull()); |
2339 | 0 | } |
2340 | 0 | return true; |
2341 | 0 | } Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::DOMXrayTraits>::getPrototype(JSContext*, JS::Handle<JSObject*>, JS::MutableHandle<JSObject*>) const Unexecuted instantiation: xpc::XrayWrapper<js::SecurityWrapper<js::CrossCompartmentWrapper>, xpc::DOMXrayTraits>::getPrototype(JSContext*, JS::Handle<JSObject*>, JS::MutableHandle<JSObject*>) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::JSXrayTraits>::getPrototype(JSContext*, JS::Handle<JSObject*>, JS::MutableHandle<JSObject*>) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::OpaqueXrayTraits>::getPrototype(JSContext*, JS::Handle<JSObject*>, JS::MutableHandle<JSObject*>) const |
2342 | | |
2343 | | template <typename Base, typename Traits> |
2344 | | bool |
2345 | | XrayWrapper<Base, Traits>::setPrototype(JSContext* cx, JS::HandleObject wrapper, |
2346 | | JS::HandleObject proto, JS::ObjectOpResult& result) const |
2347 | 0 | { |
2348 | 0 | // Do this only for non-SecurityWrapper-inheriting |Base|. See the comment |
2349 | 0 | // in getPrototype(). |
2350 | 0 | if (Base::hasSecurityPolicy()) { |
2351 | 0 | return Base::setPrototype(cx, wrapper, proto, result); |
2352 | 0 | } |
2353 | 0 | |
2354 | 0 | RootedObject target(cx, Traits::getTargetObject(wrapper)); |
2355 | 0 | RootedObject expando(cx, Traits::singleton.ensureExpandoObject(cx, wrapper, target)); |
2356 | 0 | if (!expando) { |
2357 | 0 | return false; |
2358 | 0 | } |
2359 | 0 | |
2360 | 0 | // The expando lives in the target's realm, so do our installation there. |
2361 | 0 | JSAutoRealm ar(cx, target); |
2362 | 0 |
|
2363 | 0 | RootedValue v(cx, ObjectOrNullValue(proto)); |
2364 | 0 | if (!JS_WrapValue(cx, &v)) { |
2365 | 0 | return false; |
2366 | 0 | } |
2367 | 0 | JS_SetReservedSlot(expando, JSSLOT_EXPANDO_PROTOTYPE, v); |
2368 | 0 | return result.succeed(); |
2369 | 0 | } Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::DOMXrayTraits>::setPrototype(JSContext*, JS::Handle<JSObject*>, JS::Handle<JSObject*>, JS::ObjectOpResult&) const Unexecuted instantiation: xpc::XrayWrapper<js::SecurityWrapper<js::CrossCompartmentWrapper>, xpc::DOMXrayTraits>::setPrototype(JSContext*, JS::Handle<JSObject*>, JS::Handle<JSObject*>, JS::ObjectOpResult&) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::JSXrayTraits>::setPrototype(JSContext*, JS::Handle<JSObject*>, JS::Handle<JSObject*>, JS::ObjectOpResult&) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::OpaqueXrayTraits>::setPrototype(JSContext*, JS::Handle<JSObject*>, JS::Handle<JSObject*>, JS::ObjectOpResult&) const |
2370 | | |
2371 | | template <typename Base, typename Traits> |
2372 | | bool |
2373 | | XrayWrapper<Base, Traits>::getPrototypeIfOrdinary(JSContext* cx, JS::HandleObject wrapper, |
2374 | | bool* isOrdinary, |
2375 | | JS::MutableHandleObject protop) const |
2376 | 0 | { |
2377 | 0 | // We want to keep the Xray's prototype distinct from that of content, but |
2378 | 0 | // only if there's been a set. This different-prototype-over-time behavior |
2379 | 0 | // means that the [[GetPrototypeOf]] trap *can't* be ECMAScript's ordinary |
2380 | 0 | // [[GetPrototypeOf]]. This also covers cross-origin Window behavior that |
2381 | 0 | // per <https://html.spec.whatwg.org/multipage/browsers.html#windowproxy-getprototypeof> |
2382 | 0 | // must be non-ordinary. |
2383 | 0 | *isOrdinary = false; |
2384 | 0 | return true; |
2385 | 0 | } Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::DOMXrayTraits>::getPrototypeIfOrdinary(JSContext*, JS::Handle<JSObject*>, bool*, JS::MutableHandle<JSObject*>) const Unexecuted instantiation: xpc::XrayWrapper<js::SecurityWrapper<js::CrossCompartmentWrapper>, xpc::DOMXrayTraits>::getPrototypeIfOrdinary(JSContext*, JS::Handle<JSObject*>, bool*, JS::MutableHandle<JSObject*>) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::JSXrayTraits>::getPrototypeIfOrdinary(JSContext*, JS::Handle<JSObject*>, bool*, JS::MutableHandle<JSObject*>) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::OpaqueXrayTraits>::getPrototypeIfOrdinary(JSContext*, JS::Handle<JSObject*>, bool*, JS::MutableHandle<JSObject*>) const |
2386 | | |
2387 | | template <typename Base, typename Traits> |
2388 | | bool |
2389 | | XrayWrapper<Base, Traits>::setImmutablePrototype(JSContext* cx, JS::HandleObject wrapper, |
2390 | | bool* succeeded) const |
2391 | 0 | { |
2392 | 0 | // For now, lacking an obvious place to store a bit, prohibit making an |
2393 | 0 | // Xray's [[Prototype]] immutable. We can revisit this (or maybe give all |
2394 | 0 | // Xrays immutable [[Prototype]], because who does this, really?) later if |
2395 | 0 | // necessary. |
2396 | 0 | *succeeded = false; |
2397 | 0 | return true; |
2398 | 0 | } Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::DOMXrayTraits>::setImmutablePrototype(JSContext*, JS::Handle<JSObject*>, bool*) const Unexecuted instantiation: xpc::XrayWrapper<js::SecurityWrapper<js::CrossCompartmentWrapper>, xpc::DOMXrayTraits>::setImmutablePrototype(JSContext*, JS::Handle<JSObject*>, bool*) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::JSXrayTraits>::setImmutablePrototype(JSContext*, JS::Handle<JSObject*>, bool*) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::OpaqueXrayTraits>::setImmutablePrototype(JSContext*, JS::Handle<JSObject*>, bool*) const |
2399 | | |
2400 | | template <typename Base, typename Traits> |
2401 | | bool |
2402 | | XrayWrapper<Base, Traits>::getPropertyKeys(JSContext* cx, HandleObject wrapper, unsigned flags, |
2403 | | AutoIdVector& props) const |
2404 | 0 | { |
2405 | 0 | assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::ENUMERATE); |
2406 | 0 |
|
2407 | 0 | // Enumerate expando properties first. Note that the expando object lives |
2408 | 0 | // in the target compartment. |
2409 | 0 | RootedObject target(cx, Traits::getTargetObject(wrapper)); |
2410 | 0 | RootedObject expando(cx); |
2411 | 0 | if (!Traits::singleton.getExpandoObject(cx, target, wrapper, &expando)) { |
2412 | 0 | return false; |
2413 | 0 | } |
2414 | 0 | |
2415 | 0 | if (expando) { |
2416 | 0 | JSAutoRealm ar(cx, expando); |
2417 | 0 | if (!js::GetPropertyKeys(cx, expando, flags, &props)) { |
2418 | 0 | return false; |
2419 | 0 | } |
2420 | 0 | } |
2421 | 0 | for (size_t i = 0; i < props.length(); ++i) { |
2422 | 0 | JS_MarkCrossZoneId(cx, props[i]); |
2423 | 0 | } |
2424 | 0 |
|
2425 | 0 | return Traits::singleton.enumerateNames(cx, wrapper, flags, props); |
2426 | 0 | } Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::DOMXrayTraits>::getPropertyKeys(JSContext*, JS::Handle<JSObject*>, unsigned int, JS::AutoVector<jsid>&) const Unexecuted instantiation: xpc::XrayWrapper<js::SecurityWrapper<js::CrossCompartmentWrapper>, xpc::DOMXrayTraits>::getPropertyKeys(JSContext*, JS::Handle<JSObject*>, unsigned int, JS::AutoVector<jsid>&) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::JSXrayTraits>::getPropertyKeys(JSContext*, JS::Handle<JSObject*>, unsigned int, JS::AutoVector<jsid>&) const Unexecuted instantiation: xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::OpaqueXrayTraits>::getPropertyKeys(JSContext*, JS::Handle<JSObject*>, unsigned int, JS::AutoVector<jsid>&) const |
2427 | | |
2428 | | /* |
2429 | | * The Permissive / Security variants should be used depending on whether the |
2430 | | * compartment of the wrapper is guranteed to subsume the compartment of the |
2431 | | * wrapped object (i.e. - whether it is safe from a security perspective to |
2432 | | * unwrap the wrapper). |
2433 | | */ |
2434 | | |
2435 | | template<typename Base, typename Traits> |
2436 | | const xpc::XrayWrapper<Base, Traits> |
2437 | | xpc::XrayWrapper<Base, Traits>::singleton(0); |
2438 | | |
2439 | | template class PermissiveXrayDOM; |
2440 | | template class SecurityXrayDOM; |
2441 | | template class PermissiveXrayJS; |
2442 | | template class PermissiveXrayOpaque; |
2443 | | |
2444 | | /* |
2445 | | * This callback is used by the JS engine to test if a proxy handler is for a |
2446 | | * cross compartment xray with no security requirements. |
2447 | | */ |
2448 | | static bool |
2449 | | IsCrossCompartmentXrayCallback(const js::BaseProxyHandler* handler) |
2450 | 0 | { |
2451 | 0 | return handler == &PermissiveXrayDOM::singleton; |
2452 | 0 | } |
2453 | | |
2454 | | js::XrayJitInfo gXrayJitInfo = { |
2455 | | IsCrossCompartmentXrayCallback, |
2456 | | CompartmentHasExclusiveExpandos, |
2457 | | JSSLOT_XRAY_HOLDER, |
2458 | | XrayTraits::HOLDER_SLOT_EXPANDO, |
2459 | | JSSLOT_EXPANDO_PROTOTYPE |
2460 | | }; |
2461 | | |
2462 | | } // namespace xpc |