| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // Licensed to the Apache Software Foundation (ASF) under one or more |
| // contributor license agreements. See the NOTICE file distributed with |
| // this work for additional information regarding copyright ownership. |
| // The ASF licenses this file to You under the Apache License, Version 2.0 |
| // (the "License"); you may not use this file except in compliance with |
| // the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| package mx.core |
| { |
| |
| import flash.display.Loader; |
| import flash.events.Event; |
| import flash.events.ErrorEvent; |
| import flash.events.ProgressEvent; |
| import flash.events.IOErrorEvent; |
| import flash.events.SecurityErrorEvent; |
| import flash.net.URLRequest; |
| import flash.net.URLLoader; |
| import flash.net.URLLoaderDataFormat; |
| import flash.system.LoaderContext; |
| import flash.system.ApplicationDomain; |
| import flash.system.LoaderContext; |
| import flash.system.Security; |
| import flash.system.SecurityDomain; |
| import flash.utils.ByteArray; |
| //import flash.utils.getTimer; // PERFORMANCE_INFO |
| |
| import mx.core.RSLData; |
| import mx.events.RSLEvent; |
| import mx.utils.SHA256; |
| import mx.utils.LoaderUtil; |
| |
| [ExcludeClass] |
| |
| /** |
| * @private |
| * Cross-domain RSL Item Class. |
| * |
| * The rsls are typically located on a different host than the loader. |
| * There are signed and unsigned Rsls, both have a digest to confirm the |
| * correct rsl is loaded. |
| * Signed Rsls are loaded by setting the digest of the URLRequest. |
| * Unsigned Rsls are check using actionScript to calculate a sha-256 hash of |
| * the loaded bytes and compare them to the expected digest. |
| * |
| */ |
| public class CrossDomainRSLItem extends RSLItem |
| { |
| include "../core/Version.as"; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Variables |
| // |
| //-------------------------------------------------------------------------- |
| |
| private var rsls:Array = []; // of type RSLInstanceData |
| // private var rslUrls:Array; // first url is the primary url in the url parameter, others are failovers |
| // private var policyFileUrls:Array; // optional policy files, parallel array to rslUrls |
| // private var digests:Array; // option rsl digest, parallel array to rslUrls |
| // private var isSigned:Array; // each entry is a boolean value. "true" if the rsl in the parallel array is signed |
| // private var hashTypes:Array; // type of hash used to create the digest |
| private var urlIndex:int = 0; // index into url being loaded in rslsUrls and other parallel arrays |
| |
| // this reference to the loader keeps the loader from being garbage |
| // collected before the complete event can be sent. |
| private var loadBytesLoader:Loader; |
| |
| // private var startTime:int; // PERFORMANCE_INFO |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Constructor |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Create a cross-domain RSL item to load. |
| * |
| * @param rsls Array of Objects. Each Object describes and RSL to load. |
| * The first Object in the Array is the primary RSL and the others are |
| * failover RSLs. |
| * @param rootURL provides the url used to locate relative RSL urls. |
| * @param moduleFactory The module factory that is loading the RSLs. The |
| * RSLs will be loaded into the application domain of the given module factory. |
| * If a module factory is not specified, then the RSLs will be loaded into the |
| * application domain of where the CrossDomainRSLItem class was first loaded. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function CrossDomainRSLItem(rsls:Array, |
| rootURL:String = null, |
| moduleFactory:IFlexModuleFactory = null) |
| { |
| super(rsls[0].rslURL, rootURL, moduleFactory); |
| |
| this.rsls = rsls; |
| |
| // startTime = getTimer(); // PERFORMANCE_INFO |
| } |
| |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Properties |
| // |
| //-------------------------------------------------------------------------- |
| |
| |
| /** |
| * Get the RSLData for the current RSL. This could be the primary RSL or one |
| * of the failover RSLs. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| private function get currentRSLData():RSLData |
| { |
| return RSLData(rsls[urlIndex]); |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Overridden Methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| |
| /** |
| * |
| * Load an RSL. |
| * |
| * @param progressHandler receives ProgressEvent.PROGRESS events, may be null |
| * @param completeHandler receives Event.COMPLETE events, may be null |
| * @param ioErrorHandler receives IOErrorEvent.IO_ERROR events, may be null |
| * @param securityErrorHandler receives SecurityErrorEvent.SECURITY_ERROR events, may be null |
| * @param rslErrorHandler receives RSLEvent.RSL_ERROR events, may be null |
| * |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| override public function load(progressHandler:Function, |
| completeHandler:Function, |
| ioErrorHandler:Function, |
| securityErrorHandler:Function, |
| rslErrorHandler:Function):void |
| { |
| chainedProgressHandler = progressHandler; |
| chainedCompleteHandler = completeHandler; |
| chainedIOErrorHandler = ioErrorHandler; |
| chainedSecurityErrorHandler = securityErrorHandler; |
| chainedRSLErrorHandler = rslErrorHandler; |
| |
| |
| /* |
| // Debug loading of swf files |
| |
| trace("begin load of " + url); |
| if (Security.sandboxType == Security.REMOTE) |
| { |
| trace(" in REMOTE sandbox"); |
| } |
| else if (Security.sandboxType == Security.LOCAL_TRUSTED) |
| { |
| trace(" in LOCAL_TRUSTED sandbox"); |
| } |
| else if (Security.sandboxType == Security.LOCAL_WITH_FILE) |
| { |
| trace(" in LOCAL_WITH_FILE sandbox"); |
| } |
| else if (Security.sandboxType == Security.LOCAL_WITH_NETWORK) |
| { |
| trace(" in LOCAL_WITH_NETWORK sandbox"); |
| } |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| |
| var rslData:RSLData = currentRSLData; |
| urlRequest = new URLRequest(LoaderUtil.createAbsoluteURL(rootURL, rslData.rslURL)); |
| |
| var loader:URLLoader = new URLLoader(); |
| loader.dataFormat = URLLoaderDataFormat.BINARY; |
| |
| // We needs to listen to certain events. |
| |
| loader.addEventListener( |
| ProgressEvent.PROGRESS, itemProgressHandler); |
| |
| loader.addEventListener( |
| Event.COMPLETE, itemCompleteHandler); |
| |
| loader.addEventListener( |
| IOErrorEvent.IO_ERROR, itemErrorHandler); |
| |
| loader.addEventListener( |
| SecurityErrorEvent.SECURITY_ERROR, itemErrorHandler); |
| |
| if (rslData.policyFileURL != "") |
| { |
| Security.loadPolicyFile(rslData.policyFileURL); |
| } |
| |
| if (rslData.isSigned) |
| { |
| // load a signed rsl by specifying the digest |
| urlRequest.digest = rslData.digest; |
| } |
| |
| // trace("start load of " + urlRequest.url + " at " + (getTimer() - startTime)); // PERFORMANCE_INFO |
| |
| loader.load(urlRequest); |
| } |
| |
| |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| * Complete the load of the cross-domain rsl by loading it into the current |
| * application domain. The load was started by loadCdRSL. |
| * |
| * @param - urlLoader from the complete event. |
| * |
| * @return - true if the load was completed successfully or unsuccessfully, |
| * false if the load of a failover rsl was started |
| */ |
| private function completeCdRslLoad(urlLoader:URLLoader):Boolean |
| { |
| // handle player bug #204244, complete event without data after an error |
| if (urlLoader == null || urlLoader.data == null || ByteArray(urlLoader.data).bytesAvailable == 0) |
| { |
| return true; |
| } |
| |
| // load the bytes into the current application domain. |
| loadBytesLoader = new Loader(); |
| var context:LoaderContext = new LoaderContext(); |
| var rslData:RSLData = currentRSLData; |
| |
| if (rslData.moduleFactory) |
| { |
| context.applicationDomain = rslData.moduleFactory.info()["currentDomain"]; |
| } |
| else if (moduleFactory) |
| { |
| context.applicationDomain = moduleFactory.info()["currentDomain"]; |
| } |
| else |
| { |
| context.applicationDomain = ApplicationDomain.currentDomain; |
| } |
| |
| context.securityDomain = null; |
| |
| // Set the allowCodeImport flag so we can load the RSL without a security error. |
| context.allowCodeImport = true; |
| |
| // verify the digest, if any, is correct |
| if (rslData.digest != null && rslData.verifyDigest) |
| { |
| var verifiedDigest:Boolean = false; |
| if (!rslData.isSigned) |
| { |
| // verify an unsigned rsl |
| if (rslData.hashType == SHA256.TYPE_ID) |
| { |
| // get the bytes from the rsl and calculate the hash |
| var rslDigest:String = null; |
| if (urlLoader.data != null) |
| { |
| rslDigest = SHA256.computeDigest(urlLoader.data); |
| } |
| |
| if (rslDigest == rslData.digest) |
| { |
| verifiedDigest = true; |
| } |
| } |
| } |
| else |
| { |
| // signed rsls are verified by the player |
| verifiedDigest = true; |
| } |
| |
| if (!verifiedDigest) |
| { |
| // failover to the next rsl, if one exists |
| // no failover to load, all the rsls have failed to load |
| // report an error. |
| // B Feature: externalize error message |
| var hasFailover:Boolean = this.hasFailover(); |
| var rslError:ErrorEvent = new ErrorEvent(RSLEvent.RSL_ERROR); |
| rslError.text = "Flex Error #1001: Digest mismatch with RSL " + |
| urlRequest.url + |
| ". Redeploy the matching RSL or relink your application with the matching library."; |
| itemErrorHandler(rslError); |
| |
| return !hasFailover; |
| } |
| } |
| |
| // load the rsl into memory |
| loadBytesLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadBytesCompleteHandler); |
| loadBytesLoader.loadBytes(urlLoader.data, context); |
| return true; |
| } |
| |
| |
| /** |
| * Does the current url being processed have a failover? |
| * |
| * @return true if a failover url exists, false otherwise. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function hasFailover():Boolean |
| { |
| return (rsls.length > (urlIndex + 1)); |
| } |
| |
| |
| /** |
| * Load the next url from the list of failover urls. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function loadFailover():void |
| { |
| // try to load the failover from the same node again |
| if (urlIndex < rsls.length) |
| { |
| trace("Failed to load RSL " + currentRSLData.rslURL); |
| trace("Failing over to RSL " + RSLData(rsls[urlIndex+1]).rslURL); |
| urlIndex++; // move to failover url |
| url = currentRSLData.rslURL; |
| load(chainedProgressHandler, |
| chainedCompleteHandler, |
| chainedIOErrorHandler, |
| chainedSecurityErrorHandler, |
| chainedRSLErrorHandler); |
| } |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Overridden Event Handlers |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| */ |
| override public function itemCompleteHandler(event:Event):void |
| { |
| // trace("complete load of " + url + " at " + (getTimer() - startTime)); // PERFORMANCE_INFO |
| |
| // complete loading the cross-domain rsl by calling loadBytes. |
| completeCdRslLoad(event.target as URLLoader); |
| } |
| |
| /** |
| * @private |
| */ |
| override public function itemErrorHandler(event:ErrorEvent):void |
| { |
| // trace("error loading " + url + " at " + (getTimer() - startTime)); // PERFORMANCE_INFO |
| |
| // if a failover exists, try to load it. Otherwise call super() |
| // for default error handling. |
| if (hasFailover()) |
| { |
| trace(decodeURI(event.text)); |
| loadFailover(); |
| } |
| else |
| { |
| super.itemErrorHandler(event); |
| } |
| } |
| |
| |
| /** |
| * loader.loadBytes() has a complete event. |
| * Done loading this rsl into memory. Call the completeHandler |
| * to start loading the next rsl. |
| * |
| * @private |
| */ |
| private function loadBytesCompleteHandler(event:Event):void |
| { |
| loadBytesLoader.contentLoaderInfo.removeEventListener(Event.COMPLETE, loadBytesCompleteHandler); |
| loadBytesLoader = null; |
| super.itemCompleteHandler(event); |
| } |
| |
| } |
| } |