| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // 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.LoaderInfo; |
| import flash.display.MovieClip; |
| import flash.events.ErrorEvent; |
| import flash.events.Event; |
| import flash.events.IEventDispatcher; |
| import flash.events.IOErrorEvent; |
| import flash.events.SecurityErrorEvent; |
| import flash.events.TimerEvent; |
| import flash.system.ApplicationDomain; |
| import flash.text.TextField; |
| import flash.text.TextFieldAutoSize; |
| import flash.utils.Dictionary; |
| import flash.utils.Timer; |
| import flash.utils.getDefinitionByName; |
| |
| import mx.core.RSLItem; |
| import mx.core.RSLListLoader; |
| import mx.events.ModuleEvent; |
| import mx.events.Request; |
| import mx.events.RSLEvent; |
| import mx.managers.SystemManagerGlobals; |
| import mx.resources.IResourceManager; |
| import mx.resources.ResourceManager; |
| import mx.utils.LoaderUtil; |
| |
| use namespace mx_internal; |
| |
| [ExcludeClass] |
| |
| /** |
| * @private |
| */ |
| public class FlexModuleFactory extends MovieClip |
| implements IFlexModuleFactory |
| { |
| include "../core/Version.as"; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Class constants |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| */ |
| private static const INIT_STATE:int = 0; |
| |
| /** |
| * @private |
| */ |
| private static const RSL_START_LOAD_STATE:int = 1; |
| |
| /** |
| * @private |
| */ |
| private static const APP_LOAD_STATE:int = 2; |
| |
| /** |
| * @private |
| */ |
| private static const APP_START_STATE:int = 3; |
| |
| /** |
| * @private |
| */ |
| private static const APP_RUNNING_STATE:int = 4; |
| |
| /** |
| * @private |
| */ |
| private static const ERROR_STATE:int = 5; |
| |
| /** |
| * @private |
| */ |
| private static const RSL_LOADING_STATE:int = 6; |
| |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Constructor |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| */ |
| public function FlexModuleFactory() |
| { |
| super(); |
| |
| mixinList = info()["mixins"]; |
| |
| stop(); // Make sure to stop the playhead on the currentframe |
| |
| loaderInfo.addEventListener(Event.INIT, moduleInitHandler); |
| loaderInfo.addEventListener(Event.COMPLETE, moduleCompleteHandler); |
| |
| var docFrame:int = totalFrames == 1 ? 0 : 1; |
| |
| addEventListener(Event.ENTER_FRAME, docFrameListener); |
| |
| timer = new Timer(100); |
| timer.addEventListener(TimerEvent.TIMER, timerHandler); |
| timer.start(); |
| } |
| |
| private function docFrameListener(event:Event):void |
| { |
| if (currentFrame == 2) |
| { |
| removeEventListener(Event.ENTER_FRAME, docFrameListener); |
| if (totalFrames > 2) |
| addEventListener(Event.ENTER_FRAME, extraFrameListener); |
| |
| docFrameHandler(); |
| } |
| } |
| |
| private function extraFrameListener(event:Event):void |
| { |
| if (lastFrame == currentFrame) |
| return; |
| |
| lastFrame = currentFrame; |
| |
| if (currentFrame + 1 > totalFrames) |
| removeEventListener(Event.ENTER_FRAME, extraFrameListener); |
| |
| extraFrameHandler(); |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Variables |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var rslListLoader:RSLListLoader; |
| |
| /** |
| * @private |
| */ |
| private var mixinList:Array; |
| |
| /** |
| * @private |
| */ |
| private var state:int = INIT_STATE; |
| |
| /** |
| * @private |
| */ |
| private var appReady:Boolean = false; |
| |
| /** |
| * @private |
| */ |
| private var appInitDone:Boolean = false; |
| |
| /** |
| * @private |
| */ |
| private var appLoaded:Boolean = false; |
| |
| /** |
| * @private |
| */ |
| private var timer:Timer = null; |
| |
| /** |
| * @private |
| * Track which frame was last processed |
| */ |
| private var lastFrame:int; |
| |
| /** |
| * @private |
| */ |
| private var nextFrameTimer:Timer = null; |
| |
| /** |
| * @private |
| */ |
| private var errorMessage:String = null; |
| |
| /** |
| * @private |
| * |
| * This exists only to provide a reference to this |
| * module's resource bundles. This module is opting |
| * into referencing its own resource bundles so the |
| * ResourceManager does not need to do it. This |
| * arrangement keeps the ResourceManager from pinning |
| * the module in memory. |
| */ |
| private var resourceBundles:Array; |
| |
| /** |
| * @private |
| * Array of RSLData objects that represent the list of RSLs this |
| * module factory is loading. Each element of the Array is an |
| * Array of RSLData. |
| */ |
| private var rslDataList:Array |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Properties: IFlexModuleFactory |
| // |
| //-------------------------------------------------------------------------- |
| |
| //---------------------------------- |
| // allowDomainsInNewRSLs |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _allowDomainsInNewRSLs:Boolean = true; |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10.2 |
| * @playerversion AIR 2.6 |
| * @productversion Flex 4.5 |
| */ |
| public function get allowDomainsInNewRSLs():Boolean |
| { |
| return _allowDomainsInNewRSLs; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set allowDomainsInNewRSLs(value:Boolean):void |
| { |
| _allowDomainsInNewRSLs = value; |
| } |
| |
| //---------------------------------- |
| // allowInsecureDomainsInNewRSLs |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _allowInsecureDomainsInNewRSLs:Boolean = true; |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10.2 |
| * @playerversion AIR 2.6 |
| * @productversion Flex 4.5 |
| */ |
| public function get allowInsecureDomainsInNewRSLs():Boolean |
| { |
| return _allowInsecureDomainsInNewRSLs; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set allowInsecureDomainsInNewRSLs(value:Boolean):void |
| { |
| _allowInsecureDomainsInNewRSLs = value; |
| } |
| |
| //---------------------------------- |
| // preloadedRSLs |
| //---------------------------------- |
| |
| /** |
| * @inheritDoc |
| * |
| */ |
| public function get preloadedRSLs():Dictionary |
| { |
| // Overridden by compiler generate code. |
| return null; |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Methods: IFlexModuleFactory |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 4.5 |
| */ |
| public function addPreloadedRSL(loaderInfo:LoaderInfo, rsl:Vector.<RSLData>):void |
| { |
| preloadedRSLs[loaderInfo] = rsl; |
| if (hasEventListener(RSLEvent.RSL_ADD_PRELOADED)) |
| { |
| var rslEvent:RSLEvent = new RSLEvent(RSLEvent.RSL_ADD_PRELOADED); |
| rslEvent.loaderInfo = loaderInfo; |
| dispatchEvent(rslEvent); |
| } |
| |
| } |
| |
| /** |
| * @private |
| * This method is overridden in the autogenerated subclass. |
| * It is part of TLF's ISWFContext interface. |
| * Although this class does not declare that it implements this interface, |
| * the autogenerated subclass does. |
| */ |
| public function callInContext(fn:Function, thisArg:Object, |
| argArray:Array, returns:Boolean = true):* |
| { |
| return undefined; |
| } |
| |
| /** |
| * @private |
| * This method is overridden in the autogenerated subclass. |
| */ |
| public function create(... params):Object |
| { |
| var mainClassName:String = info()["mainClassName"]; |
| |
| if (mainClassName == null) |
| { |
| var url:String = loaderInfo.loaderURL; |
| var dot:Number = url.lastIndexOf("."); |
| var slash:Number = url.lastIndexOf("/"); |
| mainClassName = url.substring(slash + 1, dot); |
| } |
| |
| var mainClass:Class = Class(getDefinitionByName(mainClassName)); |
| |
| return mainClass? new mainClass() : null; |
| } |
| |
| /** |
| * @private |
| */ |
| public function info():Object |
| { |
| return {}; |
| } |
| |
| /** |
| * Calls Security.allowDomain() for the SWF associated with this FlexModuleFactory. |
| * plus all the SWFs assocatiated with RSLs preloaded by this FlexModuleFactory. |
| * |
| */ |
| public function allowDomain(... domains):void |
| { |
| // Overridden by compiler generated code. |
| } |
| |
| /** |
| * Calls Security.allowInsecureDomain() for the SWF associated with this FlexModuleFactory |
| * plus all the SWFs assocatiated with RSLs preloaded by this FlexModuleFactory. |
| * |
| */ |
| public function allowInsecureDomain(... domains):void |
| { |
| // Overridden by compiler generated code. |
| } |
| |
| /** |
| * @private |
| * A map of fully-qualified interface names, |
| * such as "mx.managers::IPopUpManager", |
| * to instances, |
| */ |
| private var implMap:Object = {}; |
| |
| /** |
| * @private |
| * Adds an interface-name-to-implementation-class mapping to the registry, |
| * if a class hasn't already been registered for the specified interface. |
| * The class must implement a getInstance() method which returns |
| * its singleton instance. |
| */ |
| public function registerImplementation(interfaceName:String, |
| impl:Object):void |
| { |
| var c:Object = implMap[interfaceName]; |
| if (!c) |
| implMap[interfaceName] = impl; |
| } |
| |
| /** |
| * @private |
| * Returns the singleton instance of the implementation class |
| * that was registered for the specified interface, |
| * by looking up the class in the registry |
| * and calling its getInstance() method. |
| * |
| * This method should not be called at static initialization time, |
| * because the factory class may not have called registerImplementation() yet. |
| */ |
| public function getImplementation(interfaceName:String):Object |
| { |
| var c:Object = implMap[interfaceName]; |
| return c; |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function getDefinitionByName(name:String):Object |
| { |
| const domain:ApplicationDomain = |
| info()["currentDomain"] as ApplicationDomain; |
| |
| var definition:Object; |
| if (domain.hasDefinition(name)) |
| definition = domain.getDefinition(name); |
| |
| return definition; |
| } |
| |
| /** |
| * @private |
| */ |
| private function update():void |
| { |
| switch (state) |
| { |
| case INIT_STATE: |
| { |
| if (!appInitDone) |
| return; |
| |
| getRSLInfo(); |
| if (rslListLoader.isDone()) |
| state = APP_LOAD_STATE; |
| else |
| state = RSL_START_LOAD_STATE; |
| break; |
| } |
| |
| case RSL_START_LOAD_STATE: |
| { |
| // start loading all the rsls |
| rslListLoader.load(null, |
| rslCompleteHandler, |
| rslErrorHandler, |
| rslErrorHandler, |
| rslErrorHandler); |
| state = RSL_LOADING_STATE; |
| break; |
| } |
| case RSL_LOADING_STATE: |
| { |
| if (rslListLoader.isDone()) |
| { |
| rslListLoader = null; |
| state = APP_LOAD_STATE; |
| } |
| break; |
| } |
| |
| case APP_LOAD_STATE: |
| { |
| if (appLoaded) |
| { |
| deferredNextFrame(); |
| state = APP_START_STATE; |
| } |
| break; |
| } |
| |
| case APP_START_STATE: |
| { |
| if (appReady) |
| { |
| if (mixinList && mixinList.length > 0) |
| { |
| var n:int = mixinList.length; |
| for (var i:int = 0; i < n; i++) |
| { |
| var c:Class; |
| try |
| { |
| c = Class(getDefinitionByName(mixinList[i])); |
| c["init"](this); |
| } |
| catch(e:Error) |
| { |
| } |
| } |
| } |
| |
| state = APP_RUNNING_STATE; |
| timer.removeEventListener(TimerEvent.TIMER, timerHandler); |
| // Stop the timer. |
| timer.reset(); |
| |
| dispatchEvent(new Event("ready")); |
| |
| loaderInfo.removeEventListener(Event.COMPLETE, moduleCompleteHandler); |
| } |
| break; |
| } |
| |
| case ERROR_STATE: |
| { |
| if (timer != null) |
| { |
| timer.removeEventListener(TimerEvent.TIMER, timerHandler); |
| // stop the timer |
| timer.reset(); |
| } |
| |
| var tf:TextField = new TextField(); |
| tf.text = errorMessage; |
| tf.x = 0; |
| tf.y = 0; |
| tf.autoSize = TextFieldAutoSize.LEFT; |
| addChild(tf); |
| |
| dispatchEvent(new ModuleEvent(ModuleEvent.ERROR, false, false, |
| 0, 0, errorMessage)); |
| |
| loaderInfo.removeEventListener(Event.COMPLETE, moduleCompleteHandler); |
| break; |
| } |
| } |
| } |
| |
| /** |
| * @private |
| * Get the RSL URLs and add them to the RSLListLoader. |
| */ |
| private function getRSLInfo():void |
| { |
| var rsls:Array = info()["rsls"]; |
| var cdRsls:Array = info()["cdRsls"]; |
| |
| // Put cross-domain RSL information in the RSL list. |
| var rslItemList:Array = []; |
| var n:int; |
| var i:int; |
| if (cdRsls && cdRsls.length > 0) |
| { |
| rslDataList = LoaderUtil.processRequiredRSLs(this, cdRsls); |
| |
| var normalizedURL:String = LoaderUtil.normalizeURL(this.loaderInfo); |
| var crossDomainRSLItem:Class = Class(getDefinitionByName("mx.core::CrossDomainRSLItem")); |
| n = rslDataList.length; |
| for (i = 0; i < n; i++) |
| { |
| var rslWithFailovers:Array = rslDataList[i]; |
| |
| // If crossDomainRSLItem is null, then this is a compiler error. It should not be null. |
| var cdNode:Object = new crossDomainRSLItem(rslWithFailovers, |
| normalizedURL, |
| this); |
| rslItemList.push(cdNode); |
| } |
| } |
| |
| // Append RSL information in the RSL list. |
| if (rsls != null && rsls.length > 0) |
| { |
| if (normalizedURL == null) |
| normalizedURL = LoaderUtil.normalizeURL(this.loaderInfo); |
| |
| n = rsls.length; |
| for (i = 0; i < n; i++) |
| { |
| var node:RSLItem = new RSLItem(rsls[i].url, |
| normalizedURL, |
| this); |
| rslItemList.push(node); |
| } |
| } |
| |
| rslListLoader = new RSLListLoader(rslItemList); |
| } |
| |
| /** |
| * @private |
| */ |
| public function autorun():Boolean |
| { |
| return true; |
| } |
| |
| /** |
| * @private |
| */ |
| private function displayError(msg:String):void |
| { |
| errorMessage = msg; |
| state = ERROR_STATE; |
| update(); |
| } |
| |
| /** |
| * @private |
| */ |
| private function docFrameHandler(event:Event = null):void |
| { |
| // Register singleton classes. |
| // Note: getDefinitionByName() will return null |
| // if the class can't be found. |
| |
| Singleton.registerClass("mx.managers::IBrowserManager", |
| Class(getDefinitionByName("mx.managers::BrowserManagerImpl"))); |
| |
| Singleton.registerClass("mx.managers::ICursorManager", |
| Class(getDefinitionByName("mx.managers::CursorManagerImpl"))); |
| |
| Singleton.registerClass("mx.managers::IDragManager", |
| Class(getDefinitionByName("mx.managers::DragManagerImpl"))); |
| |
| Singleton.registerClass("mx.managers::IHistoryManager", |
| Class(getDefinitionByName("mx.managers::HistoryManagerImpl"))); |
| |
| Singleton.registerClass("mx.managers::ILayoutManager", |
| Class(getDefinitionByName("mx.managers::LayoutManager"))); |
| |
| Singleton.registerClass("mx.managers::IPopUpManager", |
| Class(getDefinitionByName("mx.managers::PopUpManagerImpl"))); |
| |
| Singleton.registerClass("mx.resources::IResourceManager", |
| Class(getDefinitionByName("mx.resources::ResourceManagerImpl"))); |
| |
| Singleton.registerClass("mx.styles::IStyleManager", |
| Class(getDefinitionByName("mx.styles::StyleManagerImpl"))); |
| |
| Singleton.registerClass("mx.styles::IStyleManager2", |
| Class(getDefinitionByName("mx.styles::StyleManagerImpl"))); |
| |
| Singleton.registerClass("mx.managers::IToolTipManager2", |
| Class(getDefinitionByName("mx.managers::ToolTipManagerImpl"))); |
| |
| appReady = true; |
| |
| // The resources must be installed before update() creates components |
| // (such as DateChooswer) that might need them immediately. |
| installCompiledResourceBundles(); |
| |
| update(); |
| |
| if (currentFrame < totalFrames) |
| deferredNextFrame(); |
| } |
| |
| /** |
| * @private |
| */ |
| private function installCompiledResourceBundles():void |
| { |
| //trace("FlexModuleFactory.installCompiledResourceBundles"); |
| |
| var info:Object = this.info(); |
| |
| var applicationDomain:ApplicationDomain = |
| info["currentDomain"]; |
| |
| var compiledLocales:Array /* of String */ = |
| info["compiledLocales"]; |
| |
| var compiledResourceBundleNames:Array /* of String */ = |
| info["compiledResourceBundleNames"]; |
| |
| var resourceManager:IResourceManager = |
| ResourceManager.getInstance(); |
| |
| resourceBundles = resourceManager.installCompiledResourceBundles( |
| applicationDomain, compiledLocales, compiledResourceBundleNames, true); |
| |
| // If the localeChain wasn't specified in the FlashVars of the SWF's |
| // HTML wrapper, or in the query parameters of the SWF URL, |
| // then initialize it to the list of compiled locales, |
| // sorted according to the system's preferred locales as reported by |
| // Capabilities.languages or Capabilities.language. |
| // For example, if the applications was compiled with, say, |
| // -locale=en_US,ja_JP and Capabilities.languages reports [ "ja-JP" ], |
| // set the localeChain to [ "ja_JP" "en_US" ]. |
| if (!resourceManager.localeChain) |
| resourceManager.initializeLocaleChain(compiledLocales); |
| } |
| |
| /** |
| * @private |
| */ |
| private function deferredNextFrame():void |
| { |
| if (currentFrame + 1 <= framesLoaded) |
| { |
| nextFrame(); |
| } |
| else |
| { |
| // Next frame isn't baked yet, we'll check back... |
| nextFrameTimer = new Timer(100); |
| nextFrameTimer.addEventListener(TimerEvent.TIMER, |
| nextFrameTimerHandler); |
| nextFrameTimer.start(); |
| } |
| } |
| /** |
| * @private |
| */ |
| private function extraFrameHandler(event:Event = null):void |
| { |
| var frameList:Object = info()["frames"]; |
| |
| if (frameList && frameList[currentLabel]) |
| { |
| var c:Class; |
| try |
| { |
| c = Class(getDefinitionByName(frameList[currentLabel])); |
| c["frame"](this); |
| } |
| catch(e:Error) |
| { |
| } |
| } |
| |
| if (currentFrame < totalFrames) |
| deferredNextFrame(); |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Event handlers |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| */ |
| private function rslCompleteHandler(event:Event):void |
| { |
| if (event.target is LoaderInfo) |
| { |
| var rslIndex:int = rslListLoader.getIndex(); |
| var rsl:Vector.<RSLData> = Vector.<RSLData>(rslDataList[rslIndex]); |
| var moduleFactory:IFlexModuleFactory = this; |
| if (rsl && rsl[0].moduleFactory) |
| moduleFactory = rsl[0].moduleFactory; |
| |
| if (moduleFactory == this) |
| preloadedRSLs[event.target] = rsl; |
| else |
| moduleFactory.addPreloadedRSL(LoaderInfo(event.target), rsl); |
| } |
| |
| update(); |
| } |
| |
| /** |
| * @private |
| */ |
| private function rslErrorHandler(event:Event):void |
| { |
| var rsl:RSLItem = rslListLoader.getItem(rslListLoader.getIndex()); |
| var detailedError:String; |
| var message:String; |
| |
| if (event is ErrorEvent) |
| detailedError = ErrorEvent(event).text; |
| |
| if (!detailedError) |
| detailedError = ""; |
| |
| message = "RSL " + rsl.urlRequest.url + " failed to load. " + detailedError; |
| displayError(message); |
| } |
| |
| /** |
| * @private |
| */ |
| private function moduleInitHandler(event:Event):void |
| { |
| loaderInfo.removeEventListener(Event.INIT, moduleInitHandler); |
| appInitDone = true; |
| update(); |
| } |
| |
| /** |
| * @private |
| */ |
| private function moduleCompleteHandler(event:Event):void |
| { |
| appLoaded = true; |
| update(); |
| } |
| |
| /** |
| * @private |
| */ |
| private function timerHandler(event:TimerEvent):void |
| { |
| if (totalFrames > 2 && framesLoaded >= 2 || |
| framesLoaded == totalFrames) |
| { |
| appLoaded = true; |
| } |
| |
| update(); |
| } |
| |
| /** |
| * @private |
| */ |
| private function nextFrameTimerHandler(event:TimerEvent):void |
| { |
| if (currentFrame + 1 <= framesLoaded) |
| { |
| nextFrame(); |
| nextFrameTimer.removeEventListener(TimerEvent.TIMER, |
| nextFrameTimerHandler); |
| // stop the timer |
| nextFrameTimer.reset(); |
| } |
| } |
| } |
| |
| } |