Conflicto con la solución RGPD de Quantcast "choice" y ember.js

¡Hola a todos,
tengo algunas nuevas noticias para ustedes y he encontrado una solución a este problema.

Así que, hoy he estado investigando de nuevo este error y creo que he encontrado más información.

Al examinar el archivo cmp2ui-fr.js de Quantcast, pude localizar dónde ocurre el error; está en esta función (solo tenemos la versión minificada):

function(t){for(var n in t){t[n].status=e;}}

Como pueden ver, esta función utiliza un bucle for..in, y la variable t es un array. Ya habíamos explicado esto anteriormente: Ember.js extiende el Array nativo de JavaScript. Resulta que una de las modificaciones es la adición de una entrada _super.

La entrada _super apunta a una función ROOT(), que parece referirse a _utils.ROOT en Ember.js (quizás esto les suene a algunos de ustedes, pero a mí no ^^). Esta función ROOT() no es extensible.

Aparentemente, esta propiedad _super se considera enumerable, lo que significa que al usar un bucle for..in en un array, la entrada _super se trata como una entrada “normal” (a diferencia, por ejemplo, de values, bind, valueOf, básicamente todas las funciones de un objeto Array).

Creo que esto es un error de Ember.js y no un comportamiento deseable.

Así que he logrado hacer que el error sea muy reproducible. Para ello, simplemente cree un array de objetos, así:

var objs = [{'key':'val'},{'key':'val'}];

Luego, cree una función estricta que itere sobre un array y establezca una nueva propiedad para cada entrada:

var tst_func = function (objs){'use strict';for(var i in objs){objs[i].newproperty = true }};

Finalmente, llame a esta función con el array de objetos:

tst_func(objs);

Debería obtener un error: Uncaught TypeError: can't define property "newproperty": Function is not extensible.

Al analizar esto, me hace pensar que el error probablemente ya está en circulación y no es específicamente de Quantcast. Básicamente, cualquiera que intente usar for..in se expone a comportamientos inconsistentes o errores críticos.

Para mí, esto no depende realmente de Discourse ni de Quantcast, sino claramente de Ember.js. Sin embargo, eso no cambia el hecho de que debemos encontrar una solución mientras no se corrija en Ember.js ^^.

La buena noticia es que creo que he encontrado una manera de solucionarlo.

Una forma de hacerlo es agregar una línea if (!objs.hasOwnProperty(i)) {continue}; en cada bucle for..in (probablemente también en forEach, for...of, etc.). Esto no modifica el extraño comportamiento de _super en Array, pero evita localmente acceder a él. Obviamente, esto significa que no funciona para cualquier script externo que no tengamos bajo control, como mi caso particular de uso de Quantcast.

Otra forma, que creo que es el camino a seguir, es modificar el prototipo de Array en JavaScript (así que básicamente sobrescribimos la sobrescritura de Ember.js ^^) para que _super no sea enumerable. Para ello, debemos ejecutar esta línea de JS DESPUÉS de que Ember.js sea llamado y evaluado:

// Hacer que _super no sea enumerable para evitar errores entre Ember.js y for..in
Object.defineProperty(Array.prototype, '_super', {'enumerable': false});

La segunda opción permite el uso directo de _super, como debería ser, y evita que aparezca en los bucles. Aunque no puedo garantizar que este comportamiento extraño no sea utilizado por alguna función interna de Ember.js o algún plugin externo.

Al examinar el archivo _ember_jquery-189e46ebcb33594b835e782fd1ce916ec750bc0cf980ebc4fb7796649161a18d.js, vi algunas líneas específicas de Discourse, como:

var ALIASES = {
    "ember-addons/ember-computed-decorators":
      "discourse-common/utils/decorators",
    "discourse/lib/raw-templates": "discourse-common/lib/raw-templates",
    "preload-store": "discourse/lib/preload-store",
    "fixtures/user_fixtures": "discourse/tests/fixtures/user-fixtures",
  };
  var ALIAS_PREPEND = {
    fixtures: "discourse/tests/",
    helpers: "discourse/tests/",
  };

¿Quizás podríamos agregar nuestra línea Object.defineProperty(Array.prototype, '_super', {'enumerable': false}); aquí?

Por ahora, he corregido el error agregando las líneas:
// Hacer que _super no sea enumerable para evitar errores entre Ember.js y for..in
Object.defineProperty(Array.prototype, ‘_super’, {‘enumerable’: false});

Antes de llamar al script choice.js de Quantcast.

Creo que alguien podría corregir el error localmente creando un archivo ‘fix_ember.js’ con estas dos líneas, sirviéndolo estáticamente desde el proxy inverso (por ejemplo, Nginx), y agregando una línea <script src="/fix_emmber.js"></script> en el pie del tema mediante personalización. Escribir el script directamente en lugar de crear un enlace no funciona debido a la extracción de scripts de Discourse (ver Custom javascript in <head> disappear).

Espero que este tema ayude a otros. Abriré un ticket en Ember.js mañana para ver si esto es un error o un comportamiento muy extraño pero deliberado.

Les mantendré informados para ver si debemos incluir una corrección en Discourse.

PS: ¡Muchas gracias a Angus Croll de Extending JavaScript Natives – JavaScript, JavaScript…, ¡su post me ayudó mucho!"}