bug1403679.js (6335B)
1 load(libdir + "asserts.js"); 2 3 const thisGlobal = this; 4 const otherGlobalSameCompartment = newGlobal({sameCompartmentAs: thisGlobal}); 5 const otherGlobalNewCompartment = newGlobal({newCompartment: true}); 6 7 const globals = [thisGlobal, otherGlobalSameCompartment, otherGlobalNewCompartment]; 8 9 function testWithOptions(fn, variants = [undefined]) { 10 for (let variant of variants) { 11 for (let global of globals) { 12 for (let options of [ 13 {}, 14 {proxy: true}, 15 {object: new FakeDOMObject()}, 16 ]) { 17 fn(options, global, variant); 18 } 19 } 20 } 21 } 22 23 function testWithGlobals(fn) { 24 for (let global of globals) { 25 fn(global); 26 } 27 } 28 29 function testBasic(options, global) { 30 let {object: source, transplant} = transplantableObject(options); 31 32 // Validate that |source| is an object and |transplant| is a function. 33 assertEq(typeof source, "object"); 34 assertEq(typeof transplant, "function"); 35 36 // |source| is created in the current global. 37 assertEq(objectGlobal(source), this); 38 39 // |source|'s prototype is %ObjectPrototype%, unless it's a FakeDOMObject. 40 let oldPrototype; 41 if (options.object) { 42 oldPrototype = FakeDOMObject.prototype; 43 } else { 44 oldPrototype = Object.prototype; 45 } 46 assertEq(Object.getPrototypeOf(source), oldPrototype); 47 48 // Properties can be created on |source|. 49 assertEq(source.foo, undefined); 50 source.foo = 1; 51 assertEq(source.foo, 1); 52 53 // Calling |transplant| transplants the object and then returns undefined. 54 assertEq(transplant(global), undefined); 55 56 // |source| was moved into the new global. If the new global is in a 57 // different compartment, |source| is a now a CCW. 58 if (global !== otherGlobalNewCompartment) { 59 assertEq(objectGlobal(source), global); 60 } else { 61 assertEq(objectGlobal(source), null); 62 assertEq(isProxy(source), true); 63 } 64 65 // The properties are copied over to the swapped object. 66 assertEq(source.foo, 1); 67 68 // The prototype was changed to %ObjectPrototype% of |global| or the 69 // FakeDOMObject.prototype. 70 let newPrototype; 71 if (options.object) { 72 newPrototype = global.FakeDOMObject.prototype; 73 } else { 74 newPrototype = global.Object.prototype; 75 } 76 assertEq(Object.getPrototypeOf(source), newPrototype); 77 } 78 testWithOptions(testBasic); 79 80 // Objects can be transplanted multiple times between globals. 81 function testTransplantMulti(options, global1, global2) { 82 let {object: source, transplant} = transplantableObject(options); 83 84 transplant(global1); 85 transplant(global2); 86 } 87 testWithOptions(testTransplantMulti, globals); 88 89 // Test the case when the source object already has a wrapper in the target global. 90 function testHasWrapperInTarget(options, global) { 91 let {object: source, transplant} = transplantableObject(options); 92 93 // Create a wrapper for |source| in the other global. 94 global.p = source; 95 assertEq(global.eval("p"), source); 96 97 if (options.proxy) { 98 // It's a proxy object either way. 99 assertEq(global.eval("isProxy(p)"), true); 100 } else { 101 if (global === otherGlobalNewCompartment) { 102 // |isProxy| returns true because |p| is a CCW. 103 assertEq(global.eval("isProxy(p)"), true); 104 } else { 105 // |isProxy| returns false because |p| is not a CCW. 106 assertEq(global.eval("isProxy(p)"), false); 107 } 108 } 109 110 // And now transplant it into that global. 111 transplant(global); 112 113 assertEq(global.eval("p"), source); 114 115 if (options.proxy) { 116 // It's a proxy object either way. 117 assertEq(global.eval("isProxy(p)"), true); 118 } else { 119 // The previous CCW was replaced with a same-compartment object. 120 assertEq(global.eval("isProxy(p)"), false); 121 } 122 } 123 testWithOptions(testHasWrapperInTarget); 124 125 // Test the case when the source object has a wrapper, but in a different compartment. 126 function testHasWrapperOtherCompartment(options, global) { 127 let thirdGlobal = newGlobal({newCompartment: true}); 128 let {object: source, transplant} = transplantableObject(options); 129 130 // Create a wrapper for |source| in the new global. 131 thirdGlobal.p = source; 132 assertEq(thirdGlobal.eval("p"), source); 133 134 // And now transplant the object. 135 transplant(global); 136 137 assertEq(thirdGlobal.eval("p"), source); 138 } 139 testWithOptions(testHasWrapperOtherCompartment); 140 141 // Ensure a transplanted object is correctly handled by (weak) collections. 142 function testCollections(options, global, AnySet) { 143 let {object, transplant} = transplantableObject(options); 144 145 let set = new AnySet(); 146 147 assertEq(set.has(object), false); 148 set.add(object); 149 assertEq(set.has(object), true); 150 151 transplant(global); 152 153 assertEq(set.has(object), true); 154 } 155 testWithOptions(testCollections, [Set, WeakSet]); 156 157 // Ensure DOM object slot is correctly transplanted. 158 function testDOMObjectSlot(global) { 159 let domObject = new FakeDOMObject(); 160 let expectedValue = domObject.x; 161 assertEq(typeof expectedValue, "number"); 162 163 let {object, transplant} = transplantableObject({object: domObject}); 164 assertEq(object, domObject); 165 166 transplant(global); 167 168 assertEq(object, domObject); 169 assertEq(domObject.x, expectedValue); 170 } 171 testWithGlobals(testDOMObjectSlot); 172 173 function testArgumentValidation() { 174 // Throws an error if too many arguments are present. 175 assertThrowsInstanceOf(() => transplantableObject(thisGlobal, {}), Error); 176 177 let {object, transplant} = transplantableObject(); 178 179 // Throws an error if called with no arguments. 180 assertThrowsInstanceOf(() => transplant(), Error); 181 182 // Throws an error if called with too many arguments. 183 assertThrowsInstanceOf(() => transplant(thisGlobal, {}), Error); 184 185 // Throws an error if the first argument isn't an object 186 assertThrowsInstanceOf(() => transplant(null), Error); 187 188 // Throws an error if the argument isn't a global object. 189 assertThrowsInstanceOf(() => transplant({}), Error); 190 191 // Throws an error if the 'object' option isn't a FakeDOMObject. 192 assertThrowsInstanceOf(() => transplant({object: null}), Error); 193 assertThrowsInstanceOf(() => transplant({object: {}}), Error); 194 } 195 testArgumentValidation();