Thursday 10 June 2010

Security in Flash Part 1

This is is what I found out in a furstrated afternoon. There might be wrog things due the cache. If people know about this more, please let me know.

This might work in AS2 and AS3. But it was tested on AS2 Flash9, under Flash Player 10 re32(debug).


How Flash security works between SWFs or SWF loading external images (JPG,PNG,more types?), understandble for everyone:

System.security.allowDomain()

We have two files: master.swf and slave.swf.

master is on: 'http://www.exampleDomOne.com/master.swf';
master will load a file: http://www.exampleDomTwo.com/slave.swf";

If we place on master.swf:
System.security.allowDomain("http://www.exampleDomTwo.com");

Will allow a flash file loaded from that domain to access _root vars (but not _global?) vars from master.swf

If you place on slave.swf:
System.security.allowDomain("http://www.exampleDomOne.com");
It will make ony the variables that are this.[var name] (you can ommit this) will be accessible from the clip that was used to load the swf file. but _root or _global vars will not be available.

Converting to Bitmap or using setMask() directly on those clips when no System.security.allowDomain is set in both files will result in error. But you can mask a holder. That means that if you have a clip(mc2) inside another clip(mc1). And you load an external file on mc2, you are not allowed to mask mc2 but you are mc1. Stupid huh?

What is adobe when they are thinkging about security?

Conclusion:

If you want to load external images and you want to mask them, there is no need for crossdomain or Security stuff, just load them as usual and mask the holder. easy.

If you want to load a flash file securely in your online flash application. Don't use System.security.allowDomain();

Simple, not complicated.

Friday 7 May 2010

How NOT to make flash code.

Recently I was contacted to fix some existing website for a clothes company in UK, France and Spain.
Originally the website was developed by some guys overseas. They did an awful job, they got the website working but everytime you were clicking on a link you would get pissed off.

Why?

It would go through the next steps:
  1. after clicking, page fade off.
  2. load a new html page that contains almost the whole website again.
  3. fades in the preloader. (it shows an empty page with "0%")
  4. shows progress until finished loading
  5. fade out the percentage.
  6. fades in the logo of the company
  7. stays for 1 - 2 seconds
  8. fades out the logo and waits a second.
  9. fade in the navigation
  10. show the content that you wanted to see.

10 steps that equals aprox. 14 seconds.

I had the task to make it quicker, and after reprogramming the whole site took <4 seconds which considering that has 3000px wide images it takes a little bit of time for loading. but after that, when it is cached, it less than a second.
I removed steps: 1,2,5,6,7,8,9 ending up with:
  1. after clicking, fades out the section and fades in a preloader (at the same time).
  2. shows progress and when finishes loading
  3. fades out progress and show the content that you wanted to see (at the same time).

Why developers can make such an unusable website?

I tell you why: inexperience and things wanted to be done ASAP doesnt mix very well. It comes monster websites like this. It was bloated with useless code and images that were meant to fit a resolution almost double than 1080p HD....

Now, I am going to go through some points that is worth talking about to avoid websites like this. So if you are starting to create flash (or silverlight!) websites please put attention , this could be useful to you!



Something that bothered me during my whole refactoring was the size of the Stage, it is 1600 x 1200 or something like that. To fix the basic problem, they created a script that was resizing everything to a normal size. They placed this code in each of their FLAs:
stop();
Stage.align = "LT";
Stage.scaleMode = "noScale";
#include "mc_tween2.as"
var intervalIdResize:Number;
var durationResize:Number = 50;
home_bg._height=Stage.height;
home_bg._width=Stage.width;
if(home_bg._xscale>home_bg._yscale){home_bg._xscale=home_bg._yscale;}else{home_bg._yscale=home_bg._xscale;}
home_bg._y=0;
home_bg._x=((Stage.width/2)-(home_bg._width/2));
main_menu._height=Stage.height;
main_menu._width=Stage.width;
if(main_menu._yscale>main_menu._xscale){main_menu._yscale=main_menu._xscale;} else {main_menu._xscale=main_menu._yscale;}
main_menu._y=0;
main_menu._x=((Stage.width/2)-(main_menu._width/2));
if(_global.imagesRolling==1){
image_swap._width=Stage.width;
image_swap._height=Stage.height;
if(image_swap._xscale>image_swap._yscale){image_swap._xscale=image_swap._yscale;}else{image_swap._yscale=image_swap._xscale;}
image_swap._y= 0;
image_swap._x=((Stage.width/2)-(image_swap._width/2));
}
footer_txt._height=Stage.height;
footer_txt._width=Stage.width;
if(footer_txt._xscale>footer_txt._yscale){footer_txt._xscale=footer_txt._yscale;}else{footer_txt._yscale=footer_txt._xscale;}
footer_txt._y = 0;
footer_txt._x = ((Stage.width/2)-(image_swap._width/2));
logo1._width=Stage.width;
logo1._height=Stage.height;
if(logo1._yscale>logo1._xscale){logo1._yscale=logo1._xscale;}else{logo1._xscale=logo1._yscale;}
logo1._y= 0;
logo1._x=((Stage.width/2)-(logo1._width/2));

function executeCallback():Void {
resizeThese();
}
intervalIdResize = setInterval(this, "executeCallback", durationResize);

function resizeThese(){
home_bg._height=Stage.height;
home_bg._width=Stage.width;
if(home_bg._xscale>home_bg._yscale){home_bg._xscale=home_bg._yscale;}else{home_bg._yscale=home_bg._xscale;}
home_bg._y= 0;
home_bg._x= ((Stage.width/2)-(home_bg._width/2));
logo1._width=Stage.width;
logo1._height=Stage.height;
if(logo1._yscale>logo1._xscale){logo1._yscale=logo1._xscale;}else{logo1._xscale=logo1._yscale;}
logo1._y= 0;
logo1._x=((Stage.width/2)-(logo1._width/2));
main_menu._height=Stage.height;
main_menu._width=Stage.width;
if(main_menu._yscale>main_menu._xscale){main_menu._yscale=main_menu._xscale;} else {main_menu._xscale=main_menu._yscale;}
main_menu._y=0;
main_menu._x=((Stage.width/2)-(main_menu._width/2));
image_swap._width=Stage.width;
image_swap._height=Stage.height;
if(image_swap._xscale>image_swap._yscale){image_swap._xscale=image_swap._yscale;}else{image_swap._yscale=image_swap._xscale;}
image_swap._y= 0;
image_swap._x=((Stage.width/2)-(image_swap._width/2));
footer_txt._height=Stage.height;
footer_txt._width=Stage.width;
if(footer_txt._xscale>footer_txt._yscale){footer_txt._xscale=footer_txt._yscale;}else{footer_txt._yscale=footer_txt._xscale;}
footer_txt._y = 0;
footer_txt._x = ((Stage.width/2)-(image_swap._width/2));
};

That code can be resumed into the next:

;

That's right... nothing . Flash resizes the stage automatically by default. If you want to align things on the sides there much better ways to do it. Let me tell you some examples.

Use a event listener that check when the Stage gets resized reposition your elements.
If you use a setInterval of 50 for that , you would be executing all the resizing code every 50ms! That will suck your CPU like a leech!
If you don't know how events and listener and you don't want to learn then create onEnterFrame at least, it would be executed many less times per second!


Flash prototypes:

Since AS2 was created prototypes are outdated, so stop using them! Instead of that create a class and extend the original object. Much cleaner.


Calling functions from a executing function argument:

If you want to call a function from inside another function, don't write a new full function with brackets and everything inside. Instead of that, create an external function that calls the other one after calculating whatever you want to calculate.

Bad:

pictureLarge.alphaTo(0, 0.65, "linear", 0.1, function() {
operationLockDown((_global.curBatch*8)+_global.curItem)
});


Better:
pictureLarge.alphaTo(0, 0.65, "linear", 0.1, operationLockDown);

In the example, both variables you are trying to send are globals so you don't need to send any variables!

If they weren't you can always use a CLASS that lets you send parameters. For example in Tweener:

var N:Number = (_global.curBatch*8)+_global.curItem;
Tweener.addTween(myMovieClip, {_x:200, time:1, onComplete:operationLockDown, onCompleteParams:N});

Please make all calculations outside function's parameters. Instead of that, send something clean like a variable containing the result.
Don't use global unless is necesary, it slows down the performance and it is spagetti coding which it's not nice when someone tries to debug it.


Don't call buttons to instances of movieclips.

We, developers, use them as buttons half of the time but buttons are buttons and adding _btn to a MovieClip makes things complicated. The reason we add _btn _mc _txt is to make life easier when debugging.

Whyyyy are you so picky about it?
Because each time of object has different properties and when accessing them from code we know what can we do without having to check what kind of object it is on Stage, it saves us lots of time and headaches.

One little example: Trying to play a "whatever_mc" that is a button, won't work: as they have 4 fixed frames and you cant move the frame wherewver you want, we have movieclips for that.


Sorry for the crappy post, I had to let it out. I cant believe I am writing this kind of stuff in 2010....