1////////////////////////////////////////////////////////////////////////////////
2//
3//  ADOBE SYSTEMS INCORPORATED
4//  Copyright 2007 Adobe Systems Incorporated
5//  All Rights Reserved.
6//
7//  NOTICE: Adobe permits you to use, modify, and distribute this file
8//  in accordance with the terms of the license agreement accompanying it.
9//
10////////////////////////////////////////////////////////////////////////////////
11
12package mx.core
13{
14
15import flash.display.Loader;
16import flash.events.Event;
17import flash.events.ErrorEvent;
18import flash.events.ProgressEvent;
19import flash.events.IOErrorEvent;
20import flash.events.SecurityErrorEvent;
21import flash.net.URLRequest;
22import flash.net.URLLoader;
23import flash.net.URLLoaderDataFormat;
24import flash.system.LoaderContext;
25import flash.system.ApplicationDomain;
26import flash.system.LoaderContext;
27import flash.system.Security;
28import flash.system.SecurityDomain;
29import flash.utils.ByteArray;
30//import flash.utils.getTimer;        // PERFORMANCE_INFO
31
32import mx.core.RSLData;
33import mx.events.RSLEvent;
34import mx.utils.SHA256;
35import mx.utils.LoaderUtil;
36
37[ExcludeClass]
38
39/**
40 *  @private
41 *  Cross-domain RSL Item Class.
42 *
43 *  The rsls are typically located on a different host than the loader.
44 *  There are signed and unsigned Rsls, both have a digest to confirm the
45 *  correct rsl is loaded.
46 *  Signed Rsls are loaded by setting the digest of the URLRequest.
47 *  Unsigned Rsls are check using actionScript to calculate a sha-256 hash of
48 *  the loaded bytes and compare them to the expected digest.
49 *
50 */
51public class CrossDomainRSLItem extends RSLItem
52{
53    include "../core/Version.as";
54
55    //--------------------------------------------------------------------------
56    //
57    //  Variables
58    //
59    //--------------------------------------------------------------------------
60
61    private var rsls:Array = [];     // of type RSLInstanceData
62//    private var rslUrls:Array;  // first url is the primary url in the url parameter, others are failovers
63//    private var policyFileUrls:Array; // optional policy files, parallel array to rslUrls
64//    private var digests:Array;      // option rsl digest, parallel array to rslUrls
65//    private var isSigned:Array;     // each entry is a boolean value. "true" if the rsl in the parallel array is signed
66//    private var hashTypes:Array;     //  type of hash used to create the digest
67    private var urlIndex:int = 0;   // index into url being loaded in rslsUrls and other parallel arrays
68
69    // this reference to the loader keeps the loader from being garbage
70    // collected before the complete event can be sent.
71    private var loadBytesLoader:Loader;
72
73//    private var startTime:int;      // PERFORMANCE_INFO
74
75    //--------------------------------------------------------------------------
76    //
77    //  Constructor
78    //
79    //--------------------------------------------------------------------------
80
81    /**
82    *  Create a cross-domain RSL item to load.
83    *
84    *  @param rsls Array of Objects. Each Object describes and RSL to load.
85    *  The first Object in the Array is the primary RSL and the others are
86    *  failover RSLs.
87    *  @param rootURL provides the url used to locate relative RSL urls.
88    *  @param moduleFactory The module factory that is loading the RSLs. The
89    *  RSLs will be loaded into the application domain of the given module factory.
90    *  If a module factory is not specified, then the RSLs will be loaded into the
91    *  application domain of where the CrossDomainRSLItem class was first loaded.
92    *
93    *  @langversion 3.0
94    *  @playerversion Flash 9
95    *  @playerversion AIR 1.1
96    *  @productversion Flex 3
97    */
98    public function CrossDomainRSLItem(rsls:Array,
99                             rootURL:String = null,
100                             moduleFactory:IFlexModuleFactory = null)
101    {
102        super(rsls[0].rslURL, rootURL, moduleFactory);
103
104        this.rsls = rsls;
105
106        // startTime = getTimer(); // PERFORMANCE_INFO
107    }
108
109
110    //--------------------------------------------------------------------------
111    //
112    //  Properties
113    //
114    //--------------------------------------------------------------------------
115
116
117    /**
118     *  Get the RSLData for the current RSL. This could be the primary RSL or one
119     *  of the failover RSLs.
120     *
121     *  @langversion 3.0
122     *  @playerversion Flash 9
123     *  @playerversion AIR 1.1
124     *  @productversion Flex 3
125     */
126    private function get currentRSLData():RSLData
127    {
128        return RSLData(rsls[urlIndex]);
129    }
130
131    //--------------------------------------------------------------------------
132    //
133    //  Overridden Methods
134    //
135    //--------------------------------------------------------------------------
136
137
138   /**
139    *
140    * Load an RSL.
141    *
142    * @param progressHandler       receives ProgressEvent.PROGRESS events, may be null
143    * @param completeHandler       receives Event.COMPLETE events, may be null
144    * @param ioErrorHandler        receives IOErrorEvent.IO_ERROR events, may be null
145    * @param securityErrorHandler  receives SecurityErrorEvent.SECURITY_ERROR events, may be null
146    * @param rslErrorHandler       receives RSLEvent.RSL_ERROR events, may be null
147    *
148    *
149    *  @langversion 3.0
150    *  @playerversion Flash 9
151    *  @playerversion AIR 1.1
152    *  @productversion Flex 3
153    */
154    override public function load(progressHandler:Function,
155                                  completeHandler:Function,
156                                  ioErrorHandler:Function,
157                                  securityErrorHandler:Function,
158                                  rslErrorHandler:Function):void
159    {
160        chainedProgressHandler = progressHandler;
161        chainedCompleteHandler = completeHandler;
162        chainedIOErrorHandler = ioErrorHandler;
163        chainedSecurityErrorHandler = securityErrorHandler;
164        chainedRSLErrorHandler = rslErrorHandler;
165
166
167/*
168        // Debug loading of swf files
169
170        trace("begin load of " + url);
171                if (Security.sandboxType == Security.REMOTE)
172        {
173            trace(" in REMOTE sandbox");
174        }
175        else if (Security.sandboxType == Security.LOCAL_TRUSTED)
176        {
177            trace(" in LOCAL_TRUSTED sandbox");
178        }
179        else if (Security.sandboxType == Security.LOCAL_WITH_FILE)
180        {
181            trace(" in LOCAL_WITH_FILE sandbox");
182        }
183        else if (Security.sandboxType == Security.LOCAL_WITH_NETWORK)
184        {
185            trace(" in LOCAL_WITH_NETWORK sandbox");
186        }
187*
188*  @langversion 3.0
189*  @playerversion Flash 9
190*  @playerversion AIR 1.1
191*  @productversion Flex 3
192*/
193
194        var rslData:RSLData = currentRSLData;
195        urlRequest = new URLRequest(LoaderUtil.createAbsoluteURL(rootURL, rslData.rslURL));
196
197        var loader:URLLoader = new URLLoader();
198        loader.dataFormat = URLLoaderDataFormat.BINARY;
199
200        // We needs to listen to certain events.
201
202        loader.addEventListener(
203            ProgressEvent.PROGRESS, itemProgressHandler);
204
205        loader.addEventListener(
206            Event.COMPLETE, itemCompleteHandler);
207
208        loader.addEventListener(
209            IOErrorEvent.IO_ERROR, itemErrorHandler);
210
211        loader.addEventListener(
212            SecurityErrorEvent.SECURITY_ERROR, itemErrorHandler);
213
214        if (rslData.policyFileURL != "")
215        {
216            Security.loadPolicyFile(rslData.policyFileURL);
217        }
218
219        if (rslData.isSigned)
220        {
221            // load a signed rsl by specifying the digest
222            urlRequest.digest = rslData.digest;
223        }
224
225//        trace("start load of " + urlRequest.url + " at " + (getTimer() - startTime)); // PERFORMANCE_INFO
226
227        loader.load(urlRequest);
228    }
229
230
231
232    //--------------------------------------------------------------------------
233    //
234    //  Methods
235    //
236    //--------------------------------------------------------------------------
237
238    /**
239     *  @private
240     *  Complete the load of the cross-domain rsl by loading it into the current
241     *  application domain. The load was started by loadCdRSL.
242     *
243     *  @param - urlLoader from the complete event.
244     *
245     *  @return - true if the load was completed successfully or unsuccessfully,
246     *            false if the load of a failover rsl was started
247     */
248    private function completeCdRslLoad(urlLoader:URLLoader):Boolean
249    {
250        // handle player bug #204244, complete event without data after an error
251        if (urlLoader == null || urlLoader.data == null || ByteArray(urlLoader.data).bytesAvailable == 0)
252        {
253            return true;
254        }
255
256        // load the bytes into the current application domain.
257        loadBytesLoader = new Loader();
258        var context:LoaderContext = new LoaderContext();
259        var rslData:RSLData = currentRSLData;
260
261        if (rslData.moduleFactory)
262        {
263            context.applicationDomain = rslData.moduleFactory.info()["currentDomain"];
264        }
265        else if (moduleFactory)
266        {
267            context.applicationDomain = moduleFactory.info()["currentDomain"];
268        }
269        else
270        {
271            context.applicationDomain = ApplicationDomain.currentDomain;
272        }
273
274        context.securityDomain = null;
275
276        // Set the allowCodeImport flag so we can load the RSL without a security error.
277        context.allowCodeImport = true;
278
279        // verify the digest, if any, is correct
280        if (rslData.digest != null && rslData.verifyDigest)
281        {
282            var verifiedDigest:Boolean = false;
283            if (!rslData.isSigned)
284            {
285                // verify an unsigned rsl
286                if (rslData.hashType == SHA256.TYPE_ID)
287                {
288                    // get the bytes from the rsl and calculate the hash
289                    var rslDigest:String = null;
290                    if (urlLoader.data != null)
291                    {
292                        rslDigest = SHA256.computeDigest(urlLoader.data);
293                    }
294
295                    if (rslDigest == rslData.digest)
296                    {
297                        verifiedDigest = true;
298                    }
299                }
300            }
301            else
302            {
303                // signed rsls are verified by the player
304                verifiedDigest = true;
305            }
306
307            if (!verifiedDigest)
308            {
309                // failover to the next rsl, if one exists
310                // no failover to load, all the rsls have failed to load
311                // report an error.
312                 // B Feature: externalize error message
313                var hasFailover:Boolean = hasFailover();
314                var rslError:ErrorEvent = new ErrorEvent(RSLEvent.RSL_ERROR);
315                rslError.text = "Flex Error #1001: Digest mismatch with RSL " +
316                                urlRequest.url +
317                                ". Redeploy the matching RSL or relink your application with the matching library.";
318                itemErrorHandler(rslError);
319
320                return !hasFailover;
321            }
322        }
323
324        // load the rsl into memory
325        loadBytesLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadBytesCompleteHandler);
326        loadBytesLoader.loadBytes(urlLoader.data, context);
327        return true;
328    }
329
330
331    /**
332    *  Does the current url being processed have a failover?
333    *
334    * @return true if a failover url exists, false otherwise.
335    *
336    *  @langversion 3.0
337    *  @playerversion Flash 9
338    *  @playerversion AIR 1.1
339    *  @productversion Flex 3
340    */
341    public function hasFailover():Boolean
342    {
343        return (rsls.length > (urlIndex + 1));
344    }
345
346
347    /**
348    *  Load the next url from the list of failover urls.
349    *
350    *  @langversion 3.0
351    *  @playerversion Flash 9
352    *  @playerversion AIR 1.1
353    *  @productversion Flex 3
354    */
355    public function loadFailover():void
356    {
357        // try to load the failover from the same node again
358        if (urlIndex < rsls.length)
359        {
360            trace("Failed to load RSL " + currentRSLData.rslURL);
361            trace("Failing over to RSL " + RSLData(rsls[urlIndex+1]).rslURL);
362            urlIndex++;        // move to failover url
363            url = currentRSLData.rslURL;
364            load(chainedProgressHandler,
365                 chainedCompleteHandler,
366                 chainedIOErrorHandler,
367                 chainedSecurityErrorHandler,
368                 chainedRSLErrorHandler);
369        }
370    }
371
372    //--------------------------------------------------------------------------
373    //
374    //  Overridden Event Handlers
375    //
376    //--------------------------------------------------------------------------
377
378    /**
379     *  @private
380     */
381    override public function itemCompleteHandler(event:Event):void
382    {
383//        trace("complete load of " + url + " at " + (getTimer() - startTime)); // PERFORMANCE_INFO
384
385        // complete loading the cross-domain rsl by calling loadBytes.
386        completeCdRslLoad(event.target as URLLoader);
387    }
388
389    /**
390     *  @private
391     */
392    override public function itemErrorHandler(event:ErrorEvent):void
393    {
394//        trace("error loading " + url + " at " + (getTimer() - startTime)); // PERFORMANCE_INFO
395
396        // if a failover exists, try to load it. Otherwise call super()
397        // for default error handling.
398        if (hasFailover())
399        {
400            trace(decodeURI(event.text));
401            loadFailover();
402        }
403        else
404        {
405            super.itemErrorHandler(event);
406        }
407    }
408
409
410    /**
411     * loader.loadBytes() has a complete event.
412     * Done loading this rsl into memory. Call the completeHandler
413     * to start loading the next rsl.
414     *
415     *  @private
416     */
417    private function loadBytesCompleteHandler(event:Event):void
418    {
419        loadBytesLoader.contentLoaderInfo.removeEventListener(Event.COMPLETE, loadBytesCompleteHandler);
420        loadBytesLoader = null;
421        super.itemCompleteHandler(event);
422    }
423
424}
425}