Je voulais vous parler d'un bug qui m'a bien emmerdé récemment et comme j'aimerais que vous ne perdiez pas de temps avec cela, un article s'imposait.
Note de la direction : j'y connais rien en Flash (et je ne crois pas avoir trop envie de connaître en fait).
Dans la boite où je travaille, nous utilisons Flash en tant que lecteur vidéo étant donné qu'il est pour le moment très difficile de faire lire sur toutes les plateformes des vidéos au format H.264 et/ou passant par du HLS. Longue histoire qui fera office d'un autre article. (Je dis ça à chaque fois, haha).
Avec notre flasheur attitré, nous nous sommes mis en tête de créer un objet
event qui permettrait au Flash de déclencher des évènements lorsqu'il le
souhaite afin de communiquer avec l'application JavaScript et que celui-ci fasse
en conséquence. Cet objet, nous l'avons appelé App.FlashManager
et plus
précisemment App.FlashManager.Events
dans le cas des évènements. Nous
utilisons Backbone.js pour gérer ce principe d'évènement mais vous en avez
d'autres de disponible. En pratique, voilà ce que
ça donne :
// on crée un objet vide
App.FlashManager.Events = {};
// on lui ajoute les fonctionnalités des events Backbone
_.extend(App.FlashManager.Events, Backbone.Events);
En soi, pas de souci, tout se passe bien à ce moment, nous avons un objet d'évènement façon Backbone. Il pourra alors s'écouter lui-même, écouter un autre objet, mais aussi déclencher des évènements.
La base est en place, maintenant on souhaite que le Flash déclenche un
évènement. On passe par l'objet ExternalInterface
:
ExternalInterface.call("window.App.FlashManager.Events.trigger", "my-event");
// http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/external/ExternalInterface.html#call
Et là, bug.
Pour le moment, je ne sais pas vraiment. Je me pencherai plus sur le sujet dès
que j'ai un peu de temps, mais ce que je sais, c'est que
ExternalInterface.call()
ne s'attend pas à recevoir ce que Backbone.js lui
propose. Lorsque vous faites .trigger()
, Backbone.js renvoie
l'objet en cours,
ce qui provoque un énooooorme bug dans le plugin Flash de votre navigateur.
Pour contrer cela, je vous propose la solution suivante qui est de ne pas
utiliser directement l'objet Backbone.Events|View
mais plutôt de passer par
une interface, un objet proxy.
Voici le schéma :
Exemple :
// Bridge between flash and js
App.FlashManager = {};
App.FlashManager.Events = {};
// Backbone events used for Flash
App.InternalFlashManager = {};
App.InternalFlashManager.Events = {};
_.extend(App.InternalFlashManager.Events, Backbone.Events);
App.FlashManager.Events.trigger = function(name, opts) {
App.InternalFlashManager.Events.trigger(name, opts);
// Must return nothing not to loop JavaScript engine, important!
return;
};
...il existe une version ayant plus ou moins le même souci que moi disponible là : ExternalInterface#call in a deadly loop (flashtoXML).
De manière générale (hors Backbone.js), lorsque vous utilisez
ExternalInterface.call()
, faites en sorte que votre JavaScript retourne
quelque chose de simple (un objet ayant que des attributs, pas de fonctions, ou
encore un booléen ou un integer).