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