Line data Source code
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 "AddonWrapper.h"
8 : #include "WrapperFactory.h"
9 : #include "XrayWrapper.h"
10 : #include "jsapi.h"
11 : #include "jsfriendapi.h"
12 : #include "nsIAddonInterposition.h"
13 : #include "xpcprivate.h"
14 : #include "mozilla/dom/BindingUtils.h"
15 : #include "nsGlobalWindow.h"
16 :
17 : #include "GeckoProfiler.h"
18 :
19 : #include "nsID.h"
20 :
21 : using namespace js;
22 : using namespace JS;
23 :
24 : namespace xpc {
25 :
26 : static inline void
27 0 : ReportASCIIErrorWithId(JSContext* cx, const char* msg, HandleId id)
28 : {
29 0 : RootedValue idv(cx);
30 0 : if (!JS_IdToValue(cx, id, &idv))
31 0 : return;
32 0 : RootedString idstr(cx, JS::ToString(cx, idv));
33 0 : if (!idstr)
34 0 : return;
35 0 : JSAutoByteString bytes;
36 0 : if (!bytes.encodeUtf8(cx, idstr))
37 0 : return;
38 0 : JS_ReportErrorUTF8(cx, msg, bytes.ptr());
39 : }
40 :
41 : bool
42 0 : InterposeProperty(JSContext* cx, HandleObject target, const nsIID* iid, HandleId id,
43 : MutableHandle<PropertyDescriptor> descriptor)
44 : {
45 : // We only want to do interpostion on DOM instances and
46 : // wrapped natives.
47 0 : RootedObject unwrapped(cx, UncheckedUnwrap(target));
48 0 : const js::Class* clasp = js::GetObjectClass(unwrapped);
49 0 : bool isCPOW = jsipc::IsWrappedCPOW(unwrapped);
50 0 : if (!mozilla::dom::IsDOMClass(clasp) &&
51 0 : !IS_WN_CLASS(clasp) &&
52 0 : !IS_PROTO_CLASS(clasp) &&
53 0 : clasp != &OuterWindowProxyClass &&
54 0 : !isCPOW) {
55 0 : return true;
56 : }
57 :
58 0 : XPCWrappedNativeScope* scope = ObjectScope(CurrentGlobalOrNull(cx));
59 0 : MOZ_ASSERT(scope->HasInterposition());
60 :
61 0 : nsCOMPtr<nsIAddonInterposition> interp = scope->GetInterposition();
62 0 : InterpositionWhitelist* wl = XPCWrappedNativeScope::GetInterpositionWhitelist(interp);
63 : // We do InterposeProperty only if the id is on the whitelist of the interpostion
64 : // or if the target is a CPOW.
65 0 : if ((!wl || !wl->has(JSID_BITS(id.get()))) && !isCPOW)
66 0 : return true;
67 :
68 0 : JSAddonId* addonId = AddonIdOfObject(target);
69 0 : RootedValue addonIdValue(cx, StringValue(StringOfAddonId(addonId)));
70 0 : RootedValue prop(cx, IdToValue(id));
71 0 : RootedValue targetValue(cx, ObjectValue(*target));
72 0 : RootedValue descriptorVal(cx);
73 0 : nsresult rv = interp->InterposeProperty(addonIdValue, targetValue,
74 0 : iid, prop, &descriptorVal);
75 0 : if (NS_FAILED(rv)) {
76 0 : xpc::Throw(cx, rv);
77 0 : return false;
78 : }
79 :
80 0 : if (!descriptorVal.isObject())
81 0 : return true;
82 :
83 : // We need to be careful parsing descriptorVal. |cx| is in the compartment
84 : // of the add-on and the descriptor is in the compartment of the
85 : // interposition. We could wrap the descriptor in the add-on's compartment
86 : // and then parse it. However, parsing the descriptor fetches properties
87 : // from it, and we would try to interpose on those property accesses. So
88 : // instead we parse in the interposition's compartment and then wrap the
89 : // descriptor.
90 :
91 : {
92 0 : JSAutoCompartment ac(cx, &descriptorVal.toObject());
93 0 : if (!JS::ObjectToCompletePropertyDescriptor(cx, target, descriptorVal, descriptor))
94 0 : return false;
95 : }
96 :
97 : // Always make the property non-configurable regardless of what the
98 : // interposition wants.
99 0 : descriptor.setAttributes(descriptor.attributes() | JSPROP_PERMANENT);
100 :
101 0 : if (!JS_WrapPropertyDescriptor(cx, descriptor))
102 0 : return false;
103 :
104 0 : return true;
105 : }
106 :
107 : bool
108 0 : InterposeCall(JSContext* cx, JS::HandleObject target, const JS::CallArgs& args, bool* done)
109 : {
110 0 : *done = false;
111 0 : XPCWrappedNativeScope* scope = ObjectScope(CurrentGlobalOrNull(cx));
112 0 : MOZ_ASSERT(scope->HasInterposition());
113 :
114 0 : nsCOMPtr<nsIAddonInterposition> interp = scope->GetInterposition();
115 :
116 0 : RootedObject unwrappedTarget(cx, UncheckedUnwrap(target));
117 0 : XPCWrappedNativeScope* targetScope = ObjectScope(unwrappedTarget);
118 0 : bool hasInterpostion = targetScope->HasCallInterposition();
119 :
120 0 : if (!hasInterpostion)
121 0 : return true;
122 :
123 : // If there is a call interpostion, we don't want to propogate the
124 : // call to Base:
125 0 : *done = true;
126 :
127 0 : JSAddonId* addonId = AddonIdOfObject(target);
128 0 : RootedValue addonIdValue(cx, StringValue(StringOfAddonId(addonId)));
129 0 : RootedValue targetValue(cx, ObjectValue(*target));
130 0 : RootedValue thisValue(cx, args.thisv());
131 0 : RootedObject argsArray(cx, ConvertArgsToArray(cx, args));
132 0 : if (!argsArray)
133 0 : return false;
134 :
135 0 : RootedValue argsVal(cx, ObjectValue(*argsArray));
136 0 : RootedValue returnVal(cx);
137 :
138 0 : nsresult rv = interp->InterposeCall(addonIdValue, targetValue,
139 0 : thisValue, argsVal, args.rval());
140 0 : if (NS_FAILED(rv)) {
141 0 : xpc::Throw(cx, rv);
142 0 : return false;
143 : }
144 :
145 0 : return true;
146 : }
147 :
148 : template<typename Base>
149 0 : bool AddonWrapper<Base>::call(JSContext* cx, JS::Handle<JSObject*> wrapper,
150 : const JS::CallArgs& args) const
151 : {
152 0 : bool done = false;
153 0 : if (!InterposeCall(cx, wrapper, args, &done))
154 0 : return false;
155 :
156 0 : return done || Base::call(cx, wrapper, args);
157 : }
158 :
159 : template<typename Base>
160 : bool
161 0 : AddonWrapper<Base>::getPropertyDescriptor(JSContext* cx, HandleObject wrapper,
162 : HandleId id, MutableHandle<PropertyDescriptor> desc) const
163 : {
164 0 : if (!InterposeProperty(cx, wrapper, nullptr, id, desc))
165 0 : return false;
166 :
167 0 : if (desc.object())
168 0 : return true;
169 :
170 0 : return Base::getPropertyDescriptor(cx, wrapper, id, desc);
171 : }
172 :
173 : template<typename Base>
174 : bool
175 0 : AddonWrapper<Base>::getOwnPropertyDescriptor(JSContext* cx, HandleObject wrapper,
176 : HandleId id, MutableHandle<PropertyDescriptor> desc) const
177 : {
178 0 : if (!InterposeProperty(cx, wrapper, nullptr, id, desc))
179 0 : return false;
180 :
181 0 : if (desc.object())
182 0 : return true;
183 :
184 0 : return Base::getOwnPropertyDescriptor(cx, wrapper, id, desc);
185 : }
186 :
187 : template<typename Base>
188 : bool
189 0 : AddonWrapper<Base>::get(JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<Value> receiver,
190 : JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp) const
191 : {
192 0 : AUTO_PROFILER_LABEL("AddonWrapper::get", OTHER);
193 :
194 0 : Rooted<PropertyDescriptor> desc(cx);
195 0 : if (!InterposeProperty(cx, wrapper, nullptr, id, &desc))
196 0 : return false;
197 :
198 0 : if (!desc.object())
199 0 : return Base::get(cx, wrapper, receiver, id, vp);
200 :
201 0 : if (desc.getter()) {
202 0 : return Call(cx, receiver, desc.getterObject(), HandleValueArray::empty(), vp);
203 : } else {
204 0 : vp.set(desc.value());
205 0 : return true;
206 : }
207 : }
208 :
209 : template<typename Base>
210 : bool
211 0 : AddonWrapper<Base>::set(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id, JS::HandleValue v,
212 : JS::HandleValue receiver, JS::ObjectOpResult& result) const
213 : {
214 0 : Rooted<PropertyDescriptor> desc(cx);
215 0 : if (!InterposeProperty(cx, wrapper, nullptr, id, &desc))
216 0 : return false;
217 :
218 0 : if (!desc.object())
219 0 : return Base::set(cx, wrapper, id, v, receiver, result);
220 :
221 0 : if (desc.setter()) {
222 0 : MOZ_ASSERT(desc.hasSetterObject());
223 0 : JS::AutoValueVector args(cx);
224 0 : if (!args.append(v))
225 0 : return false;
226 0 : RootedValue fval(cx, ObjectValue(*desc.setterObject()));
227 0 : RootedValue ignored(cx);
228 0 : if (!JS::Call(cx, receiver, fval, args, &ignored))
229 0 : return false;
230 0 : return result.succeed();
231 : }
232 :
233 0 : return result.failCantSetInterposed();
234 : }
235 :
236 : template<typename Base>
237 : bool
238 0 : AddonWrapper<Base>::defineProperty(JSContext* cx, HandleObject wrapper, HandleId id,
239 : Handle<PropertyDescriptor> desc,
240 : ObjectOpResult& result) const
241 : {
242 0 : Rooted<PropertyDescriptor> interpDesc(cx);
243 0 : if (!InterposeProperty(cx, wrapper, nullptr, id, &interpDesc))
244 0 : return false;
245 :
246 0 : if (!interpDesc.object())
247 0 : return Base::defineProperty(cx, wrapper, id, desc, result);
248 :
249 0 : ReportASCIIErrorWithId(cx, "unable to modify interposed property %s", id);
250 0 : return false;
251 : }
252 :
253 : template<typename Base>
254 : bool
255 0 : AddonWrapper<Base>::delete_(JSContext* cx, HandleObject wrapper, HandleId id,
256 : ObjectOpResult& result) const
257 : {
258 0 : Rooted<PropertyDescriptor> desc(cx);
259 0 : if (!InterposeProperty(cx, wrapper, nullptr, id, &desc))
260 0 : return false;
261 :
262 0 : if (!desc.object())
263 0 : return Base::delete_(cx, wrapper, id, result);
264 :
265 0 : ReportASCIIErrorWithId(cx, "unable to delete interposed property %s", id);
266 0 : return false;
267 : }
268 :
269 : #define AddonWrapperCC AddonWrapper<CrossCompartmentWrapper>
270 : #define AddonWrapperXrayXPCWN AddonWrapper<PermissiveXrayXPCWN>
271 : #define AddonWrapperXrayDOM AddonWrapper<PermissiveXrayDOM>
272 :
273 : template<> const AddonWrapperCC AddonWrapperCC::singleton(0);
274 : template<> const AddonWrapperXrayXPCWN AddonWrapperXrayXPCWN::singleton(0);
275 : template<> const AddonWrapperXrayDOM AddonWrapperXrayDOM::singleton(0);
276 :
277 : template class AddonWrapperCC;
278 : template class AddonWrapperXrayXPCWN;
279 : template class AddonWrapperXrayDOM;
280 :
281 : #undef AddonWrapperCC
282 : #undef AddonWrapperXrayXPCWN
283 : #undef AddonWrapperXrayDOM
284 :
285 : } // namespace xpc
|