Slide 1

Slide 1 text

Introducing Break the Web: Array extra methods case Yusuke Suzuki @Constellation [email protected] 2014/10/29

Slide 2

Slide 2 text

Background: ES6 Array extensions • Upcoming ES6 adds many useful methods to Array.prototype – Array.prototype.values – Array.prototype.keys – Array.prototype.entries – Array.prototype.find – etc.

Slide 3

Slide 3 text

Problem: Break the Web 1. You already added these methods – by extending Array.prototype – Sometimes, your crafted methods don’t have the same semantics to the ES6 standards 2. Too generic name to add it – “values”, “keys” etc. • In this talk, we’ll show the actual BtW cases

Slide 4

Slide 4 text

Problem: Well-known with problem • Newly added properties will hide the variable in the with block • @@unscopables (in ES6) hides some properties in with block – (“values”, “entries” etc.) (function () { var values = []; // If object inherits Array.prototype, inner "values" variable becomes // Array.prototype. with (object) { values = ...; } })());

Slide 5

Slide 5 text

Outline • Background • Problem • Case Study • Conclusion

Slide 6

Slide 6 text

Case Study 1: MooTools • MooTools has its own Array#contains – FYI: MooTools also has its own String#contains • However, it checks the methods enumerability when extending the other object – So MooTools doesn’t consider about newly added Array methods

Slide 7

Slide 7 text

Case Study 1: MooTools • Investigating MooTools and non-enumerable object.forEachMethod = function(fn){ if (!methodsEnumerable) for (var i = 0, l = methods.length; i < l; i++){ fn.call(prototype, prototype[methods[i]], methods[i]); } for (var key in prototype) fn.call(prototype, prototype[key], key) }; Array.implement({ // ... contains: function(item, from){ return this.indexOf(item, from) != -1; }, // ... });

Slide 8

Slide 8 text

Case Study 1: MooTools • Investigating MooTools and non-enumerable Only listed standard methods are considered object.forEachMethod = function(fn){ if (!methodsEnumerable) for (var i = 0, l = methods.length; i < l; i++){ fn.call(prototype, prototype[methods[i]], methods[i]); } for (var key in prototype) fn.call(prototype, prototype[key], key) }; Array.implement({ // ... contains: function(item, from){ return this.indexOf(item, from) != -1; }, // ... });

Slide 9

Slide 9 text

Case Study 1: MooTools • Investigating MooTools and non-enumerable Only listed standard methods are considered object.forEachMethod = function(fn){ if (!methodsEnumerable) for (var i = 0, l = methods.length; i < l; i++){ fn.call(prototype, prototype[methods[i]], methods[i]); } for (var key in prototype) fn.call(prototype, prototype[key], key) }; Array.implement({ // ... contains: function(item, from){ return this.indexOf(item, from) != -1; }, // ... }); There’s no standard method definition, but it becomes non-enumerable

Slide 10

Slide 10 text

Case Study 1: MooTools • Investigating MooTools and non-enumerable Only listed standard methods are considered object.forEachMethod = function(fn){ if (!methodsEnumerable) for (var i = 0, l = methods.length; i < l; i++){ fn.call(prototype, prototype[methods[i]], methods[i]); } for (var key in prototype) fn.call(prototype, prototype[key], key) }; Array.implement({ // ... contains: function(item, from){ return this.indexOf(item, from) != -1; }, // ... }); There’s no standard method definition, but it becomes non-enumerable Array.prototype.contains = function () { ...; }; If there’s non-enumerable “contains”, it becomes non-enumerable

Slide 11

Slide 11 text

Case Study 1: MooTools • And Break the Web ...('Array', Array, [ 'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice', 'indexOf', 'lastIndexOf', 'filter', 'forEach', 'every', 'map', 'some', 'reduce', 'reduceRight' ]) ... // Now, 'contains' is not enumerated. Array.forEachMethod(function(method, name){ Elements.implement(name, method); }); element.contains(...);

Slide 12

Slide 12 text

Case Study 1: MooTools • And Break the Web There’s no “contains” ...('Array', Array, [ 'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice', 'indexOf', 'lastIndexOf', 'filter', 'forEach', 'every', 'map', 'some', 'reduce', 'reduceRight' ]) ... // Now, 'contains' is not enumerated. Array.forEachMethod(function(method, name){ Elements.implement(name, method); }); element.contains(...);

Slide 13

Slide 13 text

Case Study 1: MooTools • And Break the Web There’s no “contains” There’s no “contains” in Element ...('Array', Array, [ 'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice', 'indexOf', 'lastIndexOf', 'filter', 'forEach', 'every', 'map', 'some', 'reduce', 'reduceRight' ]) ... // Now, 'contains' is not enumerated. Array.forEachMethod(function(method, name){ Elements.implement(name, method); }); element.contains(...);

Slide 14

Slide 14 text

Case Study 1: MooTools • And Break the Web There’s no “contains” There’s no “contains” in Element ...('Array', Array, [ 'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice', 'indexOf', 'lastIndexOf', 'filter', 'forEach', 'every', 'map', 'some', 'reduce', 'reduceRight' ]) ... // Now, 'contains' is not enumerated. Array.forEachMethod(function(method, name){ Elements.implement(name, method); }); element.contains(...); There’s no element.contains!

Slide 15

Slide 15 text

Case Study 1: MooTools • This breaks the jsfiddle.net – https://bugzilla.mozilla.org/show_bug.cgi?id=107505 9 • MooTools update is introduced – https://github.com/mootools/mootools- core/commit/521471f5c0617edc12c680cdad8346c3e b95776a • es-discuss is going – https://esdiscuss.org/topic/having-a-non- enumerable-array-prototype-contains-may-not-be- web-compatible – https://esdiscuss.org/topic/array-prototype-contains- solutions

Slide 16

Slide 16 text

Case Study 2: Breaking outlook.com • Array.prototype.values breaks the existing code in outlook.com – http://windowssystemspecialist.blogspot.jp/2014/10/fixing-blank-calendar- with-chrome-38.html

Slide 17

Slide 17 text

Case Study 2: Breaking outlook.com • Exposing Array.prototype.values in V8 breaks the outlook.com in stable 38 // Old code. "values" in n n

Slide 18

Slide 18 text

Case Study 2: Breaking outlook.com • Exposing Array.prototype.values in V8 breaks the outlook.com in stable 38 // Old code. "values" in n Array.prototype n [[Prototype]] … Object.prototype [[Prototype]]

Slide 19

Slide 19 text

Case Study 2: Breaking outlook.com • Exposing Array.prototype.values in V8 breaks the outlook.com in stable 38 // Old code. "values" in n Array.prototype n [[Prototype]] … Object.prototype [[Prototype]] “values”: …

Slide 20

Slide 20 text

Case Study 2: Breaking outlook.com • Exposing Array.prototype.values in V8 breaks the outlook.com in stable 38 // Old code. "values" in n Array.prototype n [[Prototype]] … Object.prototype [[Prototype]] “values”: … Trapped Becomes always true

Slide 21

Slide 21 text

Case Study 2: Breaking outlook.com • Exposing Array.prototype.values in V8 breaks the outlook.com in stable 38 // Old code. "values" in n Array.prototype n [[Prototype]] … Object.prototype [[Prototype]] “values”: … Trapped Becomes always true // Appropriate code. n.hasOwnProperty("values”)

Slide 22

Slide 22 text

Case Study 2: Breaking outlook.com • Once Chrome stable 38 is shipped, but updated later

Slide 23

Slide 23 text

Case Study 2: Breaking outlook.com • Once Chrome stable 38 is shipped, but updated later

Slide 24

Slide 24 text

Outline • Background • Problem • Case Study • Conclusion

Slide 25

Slide 25 text

Conclusion • Take the appropriate way – e.g. If you’d like to check own property existence, you should use obj.hasOwnProperty • Don’t trust the current shape of Objects overly – ECMAScript standards will extend the prototypes • And don’t use with – Use “use strict”