blob: 99f85a02873aa8d88ffacf63717e25f7a3630049 [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// 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();
}
}
}
}