1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #ifndef mozilla_dom_MaybeCrossOriginObject_h
8 #define mozilla_dom_MaybeCrossOriginObject_h
9 
10 /**
11  * Shared infrastructure for WindowProxy and Location objects.  These
12  * are the objects that can be accessed cross-origin in the HTML
13  * specification.
14  *
15  * This class can be inherited from by the relevant proxy handlers to
16  * help implement spec algorithms.
17  *
18  * The algorithms this class implements come from
19  * <https://html.spec.whatwg.org/multipage/browsers.html#shared-abstract-operations>,
20  * <https://html.spec.whatwg.org/multipage/window-object.html#the-windowproxy-exotic-object>,
21  * and
22  * <https://html.spec.whatwg.org/multipage/history.html#the-location-interface>.
23  *
24  * The class is templated on its base so we can directly implement the things
25  * that should have identical implementations for WindowProxy and Location.  The
26  * templating is needed because WindowProxy needs to be a wrapper and Location
27  * shouldn't be one.
28  */
29 
30 #include "js/Class.h"
31 #include "js/TypeDecls.h"
32 #include "nsStringFwd.h"
33 
34 namespace mozilla {
35 namespace dom {
36 
37 // Methods that MaybeCrossOriginObject wants that do not depend on the "Base"
38 // template parameter.  We can avoid having multiple instantiations of them by
39 // pulling them out into this helper class.
40 class MaybeCrossOriginObjectMixins {
41  public:
42   /**
43    * Implementation of
44    * <https://html.spec.whatwg.org/multipage/browsers.html#isplatformobjectsameorigin-(-o-)>.
45    * "cx" and "obj" may or may not be same-compartment and even when
46    * same-compartment may not be same-Realm.  "obj" can be a WindowProxy, a
47    * Window, or a Location.
48    */
49   static bool IsPlatformObjectSameOrigin(JSContext* cx, JSObject* obj);
50 
51  protected:
52   /**
53    * Implementation of
54    * <https://html.spec.whatwg.org/multipage/browsers.html#crossorigingetownpropertyhelper-(-o,-p-)>.
55    *
56    * "cx" and "obj" are expected to be different-Realm here, and may be
57    * different-compartment.  "obj" can be a "WindowProxy" or a "Location" or a
58    * cross-process proxy for one of those.
59    */
60   bool CrossOriginGetOwnPropertyHelper(
61       JSContext* cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
62       JS::MutableHandle<JS::PropertyDescriptor> desc) const;
63 
64   /**
65    * Implementation of
66    * <https://html.spec.whatwg.org/multipage/browsers.html#crossoriginpropertyfallback-(-p-)>.
67    *
68    * This should be called at the end of getOwnPropertyDescriptor
69    * methods in the cross-origin case.
70    *
71    * "cx" and "obj" are expected to be different-Realm here, and may
72    * be different-compartment.  "obj" can be a "WindowProxy" or a
73    * "Location" or a cross-process proxy for one of those.
74    */
75   static bool CrossOriginPropertyFallback(
76       JSContext* cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
77       JS::MutableHandle<JS::PropertyDescriptor> desc);
78 
79   /**
80    * Implementation of
81    * <https://html.spec.whatwg.org/multipage/browsers.html#crossoriginget-(-o,-p,-receiver-)>.
82    *
83    * "cx" and "obj" are expected to be different-Realm here and may be
84    * different-compartment.  "obj" can be a "WindowProxy" or a
85    * "Location" or a cross-process proxy for one of those.
86    *
87    * "receiver" will be in the compartment of "cx".  The return value will
88    * be in the compartment of "cx".
89    */
90   static bool CrossOriginGet(JSContext* cx, JS::Handle<JSObject*> obj,
91                              JS::Handle<JS::Value> receiver,
92                              JS::Handle<jsid> id,
93                              JS::MutableHandle<JS::Value> vp);
94 
95   /**
96    * Implementation of
97    * <https://html.spec.whatwg.org/multipage/browsers.html#crossoriginset-(-o,-p,-v,-receiver-)>.
98    *
99    * "cx" and "obj" are expected to be different-Realm here and may be
100    * different-compartment.  "obj" can be a "WindowProxy" or a
101    * "Location" or a cross-process proxy for one of those.
102    *
103    * "receiver" and "v" will be in the compartment of "cx".
104    */
105   static bool CrossOriginSet(JSContext* cx, JS::Handle<JSObject*> obj,
106                              JS::Handle<jsid> id, JS::Handle<JS::Value> v,
107                              JS::Handle<JS::Value> receiver,
108                              JS::ObjectOpResult& result);
109 
110   /**
111    * Utility method to ensure a holder for cross-origin properties for the
112    * current global of the JSContext.
113    *
114    * When this is called, "cx" and "obj" are _always_ different-Realm, because
115    * this is only used in cross-origin situations.  The "holder" return value is
116    * always in the Realm of "cx".
117    *
118    * "obj" is the object which has space to store the collection of holders in
119    * the given slot.
120    *
121    * "attributes" and "methods" are the cross-origin attributes and methods we
122    * care about, which should get defined on holders.
123    */
124   static bool EnsureHolder(JSContext* cx, JS::Handle<JSObject*> obj,
125                            size_t slot, JSPropertySpec* attributes,
126                            JSFunctionSpec* methods,
127                            JS::MutableHandle<JSObject*> holder);
128 
129   /**
130    * Ensures we have a holder object for the current Realm.  When this is
131    * called, "obj" is guaranteed to not be same-Realm with "cx", because this
132    * is only used for cross-origin cases.
133    *
134    * Subclasses are expected to implement this by calling our static
135    * EnsureHolder with the appropriate arguments.
136    */
137   virtual bool EnsureHolder(JSContext* cx, JS::Handle<JSObject*> proxy,
138                             JS::MutableHandle<JSObject*> holder) const = 0;
139 
140   /**
141    * Report a cross-origin denial for a property named by aId.  Always
142    * returns false, so it can be used as "return
143    * ReportCrossOriginDenial(...);".
144    */
145   static bool ReportCrossOriginDenial(JSContext* aCx, JS::Handle<jsid> aId,
146                                       const nsACString& aAccessType);
147 };
148 
149 // A proxy handler for objects that may be cross-origin objects.  Whether they
150 // actually _are_ cross-origin objects can change dynamically if document.domain
151 // is set.
152 template <typename Base>
153 class MaybeCrossOriginObject : public Base,
154                                public MaybeCrossOriginObjectMixins {
155  protected:
156   template <typename... Args>
MaybeCrossOriginObject(Args &&...aArgs)157   constexpr MaybeCrossOriginObject(Args&&... aArgs)
158       : Base(std::forward<Args>(aArgs)...) {}
159 
160   /**
161    * Implementation of [[GetPrototypeOf]] as defined in
162    * <https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-getprototypeof>
163    * and
164    * <https://html.spec.whatwg.org/multipage/history.html#location-getprototypeof>.
165    *
166    * Our prototype-storage model looks quite different from the spec's, so we
167    * need to implement some hooks that don't directly map to the spec.
168    *
169    * "proxy" is the WindowProxy or Location involved.  It may or may not be
170    * same-compartment with cx.
171    *
172    * "protop" is the prototype value (possibly null).  It is guaranteed to be
173    * same-compartment with cx after this function returns successfully.
174    */
175   bool getPrototype(JSContext* cx, JS::Handle<JSObject*> proxy,
176                     JS::MutableHandle<JSObject*> protop) const final;
177 
178   /**
179    * Hook for doing the OrdinaryGetPrototypeOf bits that [[GetPrototypeOf]] does
180    * in the spec.  Location and WindowProxy store that information somewhat
181    * differently.
182    *
183    * The prototype should come from the Realm of "cx".
184    */
185   virtual JSObject* getSameOriginPrototype(JSContext* cx) const = 0;
186 
187   /**
188    * Implementation of [[SetPrototypeOf]] as defined in
189    * <https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-setprototypeof>
190    * and
191    * <https://html.spec.whatwg.org/multipage/history.html#location-setprototypeof>.
192    *
193    * "proxy" is the WindowProxy or Location object involved.  It may or may not
194    * be same-compartment with "cx".
195    *
196    * "proto" is the new prototype object (possibly null).  It must be
197    * same-compartment with "cx".
198    */
199   bool setPrototype(JSContext* cx, JS::Handle<JSObject*> proxy,
200                     JS::Handle<JSObject*> proto,
201                     JS::ObjectOpResult& result) const final;
202 
203   /**
204    * Our non-standard getPrototypeIfOrdinary hook.
205    */
206   bool getPrototypeIfOrdinary(JSContext* cx, JS::Handle<JSObject*> proxy,
207                               bool* isOrdinary,
208                               JS::MutableHandle<JSObject*> protop) const final;
209 
210   /**
211    * Our non-standard setImmutablePrototype hook.
212    */
213   bool setImmutablePrototype(JSContext* cx, JS::Handle<JSObject*> proxy,
214                              bool* succeeded) const final;
215 
216   /**
217    * Implementation of [[IsExtensible]] as defined in
218    * <https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-isextensible>
219    * and
220    * <https://html.spec.whatwg.org/multipage/history.html#location-isextensible>.
221    */
222   bool isExtensible(JSContext* cx, JS::Handle<JSObject*> proxy,
223                     bool* extensible) const final;
224 
225   /**
226    * Implementation of [[PreventExtensions]] as defined in
227    * <https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-preventextensions>
228    * and
229    * <https://html.spec.whatwg.org/multipage/history.html#location-preventextensions>.
230    */
231   bool preventExtensions(JSContext* cx, JS::Handle<JSObject*> proxy,
232                          JS::ObjectOpResult& result) const final;
233 
234   /**
235    * Implementation of [[GetOwnProperty]] is completely delegated to subclasses.
236    *
237    * "proxy" is the WindowProxy or Location object involved.  It may or may not
238    * be same-compartment with cx.
239    */
240   bool getOwnPropertyDescriptor(
241       JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
242       JS::MutableHandle<JS::PropertyDescriptor> desc) const override = 0;
243 
244   /**
245    * Implementation of [[DefineOwnProperty]] as defined in
246    * <https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-defineownproperty>
247    * and
248    * <https://html.spec.whatwg.org/multipage/history.html#location-defineownproperty>.
249    * "proxy" is the WindowProxy or Location object involved.  It may or may not
250    * be same-compartment with cx.
251    *
252    */
253   bool defineProperty(JSContext* cx, JS::Handle<JSObject*> proxy,
254                       JS::Handle<jsid> id,
255                       JS::Handle<JS::PropertyDescriptor> desc,
256                       JS::ObjectOpResult& result) const final;
257 
258   /**
259    * Some of our base classes define _another_ virtual defineProperty, and we
260    * get overloaded-virtual warnings as a result due to us hiding it, if we
261    * don't pull it in here.
262    */
263   using Base::defineProperty;
264 
265   /**
266    * Hook for handling the same-origin case in defineProperty.
267    *
268    * "proxy" is the WindowProxy or Location object involved.  It will be
269    * same-compartment with cx.
270    *
271    * "desc" is a the descriptor being defined.  It will be same-compartment with
272    * cx.
273    */
274   virtual bool definePropertySameOrigin(JSContext* cx,
275                                         JS::Handle<JSObject*> proxy,
276                                         JS::Handle<jsid> id,
277                                         JS::Handle<JS::PropertyDescriptor> desc,
278                                         JS::ObjectOpResult& result) const = 0;
279 
280   /**
281    * Implementation of [[Get]] is completely delegated to subclasses.
282    *
283    * "proxy" is the WindowProxy or Location object involved.  It may or may not
284    * be same-compartment with "cx".
285    *
286    * "receiver" is the receiver ("this") for the get.  It will be
287    * same-compartment with "cx"
288    *
289    * "vp" is the return value.  It will be same-compartment with "cx".
290    */
291   bool get(JSContext* cx, JS::Handle<JSObject*> proxy,
292            JS::Handle<JS::Value> receiver, JS::Handle<jsid> id,
293            JS::MutableHandle<JS::Value> vp) const override = 0;
294 
295   /**
296    * Implementation of [[Set]] is completely delegated to subclasses.
297    *
298    * "proxy" is the WindowProxy or Location object involved.  It may or may not
299    * be same-compartment with "cx".
300    *
301    * "v" is the value being set.  It will be same-compartment with "cx".
302    *
303    * "receiver" is the receiver ("this") for the set.  It will be
304    * same-compartment with "cx".
305    */
306   bool set(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
307            JS::Handle<JS::Value> v, JS::Handle<JS::Value> receiver,
308            JS::ObjectOpResult& result) const override = 0;
309 
310   /**
311    * Implementation of [[Delete]] is completely delegated to subclasses.
312    *
313    * "proxy" is the WindowProxy or Location object involved.  It may or may not
314    * be same-compartment with "cx".
315    */
316   bool delete_(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
317                JS::ObjectOpResult& result) const override = 0;
318 
319   /**
320    * Spidermonkey-internal hook for enumerating objects.
321    */
322   bool enumerate(JSContext* cx, JS::Handle<JSObject*> proxy,
323                  JS::MutableHandleVector<jsid> props) const final;
324 
325   /**
326    * Spidermonkey-internal hook used for instanceof.  We need to override this
327    * because otherwise we can end up doing instanceof work in the wrong
328    * compartment.
329    */
330   bool hasInstance(JSContext* cx, JS::Handle<JSObject*> proxy,
331                    JS::MutableHandle<JS::Value> v, bool* bp) const final;
332 
333   /**
334    * Spidermonkey-internal hook used by Object.prototype.toString.  Subclasses
335    * need to implement this, because we don't know what className they want.
336    * Except in the cross-origin case, when we could maybe handle it...
337    */
338   const char* className(JSContext* cx,
339                         JS::Handle<JSObject*> proxy) const override = 0;
340 };
341 
342 }  // namespace dom
343 }  // namespace mozilla
344 
345 #endif /* mozilla_dom_MaybeCrossOriginObject_h */
346