Bug 1936278 - Prevent search mode chiclet from being dismissed when clicking in page...
[gecko.git] / dom / bindings / DOMJSProxyHandler.cpp
blob49ace4cc9a60ce8787da14be59aec837a5bded4b
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/dom/DOMJSProxyHandler.h"
8 #include "xpcpublic.h"
9 #include "xpcprivate.h"
10 #include "XPCWrapper.h"
11 #include "WrapperFactory.h"
12 #include "nsWrapperCacheInlines.h"
13 #include "mozilla/dom/BindingUtils.h"
15 #include "jsapi.h"
16 #include "js/friend/DOMProxy.h" // JS::DOMProxyShadowsResult, JS::ExpandoAndGeneration, JS::SetDOMProxyInformation
17 #include "js/PropertyAndElement.h" // JS_AlreadyHasOwnPropertyById, JS_DefineProperty, JS_DefinePropertyById, JS_DeleteProperty, JS_DeletePropertyById
18 #include "js/Object.h" // JS::GetCompartment
20 using namespace JS;
22 namespace mozilla::dom {
24 jsid s_length_id = JS::PropertyKey::Void();
26 bool DefineStaticJSVals(JSContext* cx) {
27 return AtomizeAndPinJSString(cx, s_length_id, "length");
30 const char DOMProxyHandler::family = 0;
32 JS::DOMProxyShadowsResult DOMProxyShadows(JSContext* cx,
33 JS::Handle<JSObject*> proxy,
34 JS::Handle<jsid> id) {
35 using DOMProxyShadowsResult = JS::DOMProxyShadowsResult;
37 JS::Rooted<JSObject*> expando(cx, DOMProxyHandler::GetExpandoObject(proxy));
38 JS::Value v = js::GetProxyPrivate(proxy);
39 bool isOverrideBuiltins = !v.isObject() && !v.isUndefined();
40 if (expando) {
41 bool hasOwn;
42 if (!JS_AlreadyHasOwnPropertyById(cx, expando, id, &hasOwn))
43 return DOMProxyShadowsResult::ShadowCheckFailed;
45 if (hasOwn) {
46 return isOverrideBuiltins
47 ? DOMProxyShadowsResult::ShadowsViaIndirectExpando
48 : DOMProxyShadowsResult::ShadowsViaDirectExpando;
52 if (!isOverrideBuiltins) {
53 // Our expando, if any, didn't shadow, so we're not shadowing at all.
54 return DOMProxyShadowsResult::DoesntShadow;
57 bool hasOwn;
58 if (!GetProxyHandler(proxy)->hasOwn(cx, proxy, id, &hasOwn))
59 return DOMProxyShadowsResult::ShadowCheckFailed;
61 return hasOwn ? DOMProxyShadowsResult::Shadows
62 : DOMProxyShadowsResult::DoesntShadowUnique;
65 // Store the information for the specialized ICs.
66 struct SetDOMProxyInformation {
67 SetDOMProxyInformation() {
68 JS::SetDOMProxyInformation((const void*)&DOMProxyHandler::family,
69 DOMProxyShadows,
70 &RemoteObjectProxyBase::sCrossOriginProxyFamily);
74 MOZ_RUNINIT SetDOMProxyInformation gSetDOMProxyInformation;
76 static inline void CheckExpandoObject(JSObject* proxy,
77 const JS::Value& expando) {
78 #ifdef DEBUG
79 JSObject* obj = &expando.toObject();
80 MOZ_ASSERT(!js::gc::EdgeNeedsSweepUnbarriered(&obj));
81 MOZ_ASSERT(JS::GetCompartment(proxy) == JS::GetCompartment(obj));
83 // When we create an expando object in EnsureExpandoObject below, we preserve
84 // the wrapper. The wrapper is released when the object is unlinked, but we
85 // should never call these functions after that point.
86 nsISupports* native = UnwrapDOMObject<nsISupports>(proxy);
87 nsWrapperCache* cache;
88 // QueryInterface to nsWrapperCache will not GC.
89 JS::AutoSuppressGCAnalysis suppress;
90 CallQueryInterface(native, &cache);
91 MOZ_ASSERT(cache->PreservingWrapper());
92 #endif
95 static inline void CheckExpandoAndGeneration(
96 JSObject* proxy, JS::ExpandoAndGeneration* expandoAndGeneration) {
97 #ifdef DEBUG
98 JS::Value value = expandoAndGeneration->expando;
99 if (!value.isUndefined()) CheckExpandoObject(proxy, value);
100 #endif
103 static inline void CheckDOMProxy(JSObject* proxy) {
104 #ifdef DEBUG
105 MOZ_ASSERT(IsDOMProxy(proxy), "expected a DOM proxy object");
106 MOZ_ASSERT(!js::gc::EdgeNeedsSweepUnbarriered(&proxy));
107 nsISupports* native = UnwrapDOMObject<nsISupports>(proxy);
108 nsWrapperCache* cache;
109 // QI to nsWrapperCache cannot GC for very non-obvious reasons; see
110 // https://searchfox.org/mozilla-central/rev/55da592d85c2baf8d8818010c41d9738c97013d2/js/xpconnect/src/XPCWrappedJSClass.cpp#521,545-548
111 JS::AutoSuppressGCAnalysis nogc;
112 CallQueryInterface(native, &cache);
113 MOZ_ASSERT(cache->GetWrapperPreserveColor() == proxy);
114 #endif
117 // static
118 JSObject* DOMProxyHandler::GetAndClearExpandoObject(JSObject* obj) {
119 CheckDOMProxy(obj);
121 JS::Value v = js::GetProxyPrivate(obj);
122 if (v.isUndefined()) {
123 return nullptr;
126 if (v.isObject()) {
127 js::SetProxyPrivate(obj, UndefinedValue());
128 } else {
129 auto* expandoAndGeneration =
130 static_cast<JS::ExpandoAndGeneration*>(v.toPrivate());
131 v = expandoAndGeneration->expando;
132 if (v.isUndefined()) {
133 return nullptr;
135 expandoAndGeneration->expando = UndefinedValue();
138 CheckExpandoObject(obj, v);
140 return &v.toObject();
143 // static
144 JSObject* DOMProxyHandler::EnsureExpandoObject(JSContext* cx,
145 JS::Handle<JSObject*> obj) {
146 CheckDOMProxy(obj);
148 JS::Value v = js::GetProxyPrivate(obj);
149 if (v.isObject()) {
150 CheckExpandoObject(obj, v);
151 return &v.toObject();
154 JS::ExpandoAndGeneration* expandoAndGeneration = nullptr;
155 if (!v.isUndefined()) {
156 expandoAndGeneration =
157 static_cast<JS::ExpandoAndGeneration*>(v.toPrivate());
158 CheckExpandoAndGeneration(obj, expandoAndGeneration);
159 if (expandoAndGeneration->expando.isObject()) {
160 return &expandoAndGeneration->expando.toObject();
164 JS::Rooted<JSObject*> expando(
165 cx, JS_NewObjectWithGivenProto(cx, nullptr, nullptr));
166 if (!expando) {
167 return nullptr;
170 nsISupports* native = UnwrapDOMObject<nsISupports>(obj);
171 nsWrapperCache* cache;
172 CallQueryInterface(native, &cache);
173 cache->PreserveWrapper(native);
175 if (expandoAndGeneration) {
176 expandoAndGeneration->expando.setObject(*expando);
177 return expando;
180 js::SetProxyPrivate(obj, ObjectValue(*expando));
182 return expando;
185 bool DOMProxyHandler::preventExtensions(JSContext* cx,
186 JS::Handle<JSObject*> proxy,
187 JS::ObjectOpResult& result) const {
188 // always extensible per WebIDL
189 return result.failCantPreventExtensions();
192 bool DOMProxyHandler::isExtensible(JSContext* cx, JS::Handle<JSObject*> proxy,
193 bool* extensible) const {
194 *extensible = true;
195 return true;
198 bool BaseDOMProxyHandler::getOwnPropertyDescriptor(
199 JSContext* cx, Handle<JSObject*> proxy, Handle<jsid> id,
200 MutableHandle<Maybe<PropertyDescriptor>> desc) const {
201 return getOwnPropDescriptor(cx, proxy, id, /* ignoreNamedProps = */ false,
202 desc);
205 bool DOMProxyHandler::defineProperty(JSContext* cx, JS::Handle<JSObject*> proxy,
206 JS::Handle<jsid> id,
207 Handle<PropertyDescriptor> desc,
208 JS::ObjectOpResult& result,
209 bool* done) const {
210 if (xpc::WrapperFactory::IsXrayWrapper(proxy)) {
211 return result.succeed();
214 JS::Rooted<JSObject*> expando(cx, EnsureExpandoObject(cx, proxy));
215 if (!expando) {
216 return false;
219 if (!JS_DefinePropertyById(cx, expando, id, desc, result)) {
220 return false;
222 *done = true;
223 return true;
226 bool DOMProxyHandler::set(JSContext* cx, Handle<JSObject*> proxy,
227 Handle<jsid> id, Handle<JS::Value> v,
228 Handle<JS::Value> receiver,
229 ObjectOpResult& result) const {
230 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
231 "Should not have a XrayWrapper here");
232 bool done;
233 if (!setCustom(cx, proxy, id, v, &done)) {
234 return false;
236 if (done) {
237 return result.succeed();
240 // Make sure to ignore our named properties when checking for own
241 // property descriptors for a set.
242 Rooted<Maybe<PropertyDescriptor>> ownDesc(cx);
243 if (!getOwnPropDescriptor(cx, proxy, id, /* ignoreNamedProps = */ true,
244 &ownDesc)) {
245 return false;
248 return js::SetPropertyIgnoringNamedGetter(cx, proxy, id, v, receiver, ownDesc,
249 result);
252 bool DOMProxyHandler::delete_(JSContext* cx, JS::Handle<JSObject*> proxy,
253 JS::Handle<jsid> id,
254 JS::ObjectOpResult& result) const {
255 JS::Rooted<JSObject*> expando(cx);
256 if (!xpc::WrapperFactory::IsXrayWrapper(proxy) &&
257 (expando = GetExpandoObject(proxy))) {
258 return JS_DeletePropertyById(cx, expando, id, result);
261 return result.succeed();
264 bool BaseDOMProxyHandler::ownPropertyKeys(
265 JSContext* cx, JS::Handle<JSObject*> proxy,
266 JS::MutableHandleVector<jsid> props) const {
267 return ownPropNames(cx, proxy,
268 JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, props);
271 bool BaseDOMProxyHandler::getPrototypeIfOrdinary(
272 JSContext* cx, JS::Handle<JSObject*> proxy, bool* isOrdinary,
273 JS::MutableHandle<JSObject*> proto) const {
274 *isOrdinary = true;
275 proto.set(GetStaticPrototype(proxy));
276 return true;
279 bool BaseDOMProxyHandler::getOwnEnumerablePropertyKeys(
280 JSContext* cx, JS::Handle<JSObject*> proxy,
281 JS::MutableHandleVector<jsid> props) const {
282 return ownPropNames(cx, proxy, JSITER_OWNONLY, props);
285 bool DOMProxyHandler::setCustom(JSContext* cx, JS::Handle<JSObject*> proxy,
286 JS::Handle<jsid> id, JS::Handle<JS::Value> v,
287 bool* done) const {
288 *done = false;
289 return true;
292 // static
293 JSObject* DOMProxyHandler::GetExpandoObject(JSObject* obj) {
294 CheckDOMProxy(obj);
296 JS::Value v = js::GetProxyPrivate(obj);
297 if (v.isObject()) {
298 CheckExpandoObject(obj, v);
299 return &v.toObject();
302 if (v.isUndefined()) {
303 return nullptr;
306 auto* expandoAndGeneration =
307 static_cast<JS::ExpandoAndGeneration*>(v.toPrivate());
308 CheckExpandoAndGeneration(obj, expandoAndGeneration);
310 v = expandoAndGeneration->expando;
311 return v.isUndefined() ? nullptr : &v.toObject();
314 void ShadowingDOMProxyHandler::trace(JSTracer* trc, JSObject* proxy) const {
315 DOMProxyHandler::trace(trc, proxy);
317 MOZ_ASSERT(IsDOMProxy(proxy), "expected a DOM proxy object");
318 JS::Value v = js::GetProxyPrivate(proxy);
319 MOZ_ASSERT(!v.isObject(), "Should not have expando object directly!");
321 // The proxy's private slot is set when we allocate the proxy,
322 // so it cannot be |undefined|.
323 MOZ_ASSERT(!v.isUndefined());
325 auto* expandoAndGeneration =
326 static_cast<JS::ExpandoAndGeneration*>(v.toPrivate());
327 JS::TraceEdge(trc, &expandoAndGeneration->expando,
328 "Shadowing DOM proxy expando");
331 } // namespace mozilla::dom