/flowplayer/branches/3_2_7/src/actionscript/org/flowplayer/view/PluginLoader.as

http://flowplayer-core.googlecode.com/ · ActionScript · 349 lines · 276 code · 47 blank · 26 comment · 49 complexity · 83c03e99cb651afca3dcd1c18251fc48 MD5 · raw file

  1. /*
  2. * Copyright 2008 Flowplayer Oy
  3. *
  4. * This file is part of Flowplayer.
  5. *
  6. * Flowplayer is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * Flowplayer is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with Flowplayer. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. package org.flowplayer.view {
  20. import flash.display.AVM1Movie;
  21. import flash.system.Security;
  22. import org.flowplayer.model.ErrorCode;
  23. import org.flowplayer.model.Plugin;
  24. import org.flowplayer.controller.NetStreamControllingStreamProvider;
  25. import com.adobe.utils.StringUtil;
  26. import org.flowplayer.config.ExternalInterfaceHelper;
  27. import org.flowplayer.controller.StreamProvider;
  28. import org.flowplayer.model.Callable;
  29. import org.flowplayer.model.DisplayPluginModel;
  30. import org.flowplayer.model.FontProvider;
  31. import org.flowplayer.model.Loadable;
  32. import org.flowplayer.model.PlayerError;
  33. import org.flowplayer.model.PluginError;
  34. import org.flowplayer.model.PluginEvent;
  35. import org.flowplayer.model.PluginModel;
  36. import org.flowplayer.model.ProviderModel;
  37. import org.flowplayer.util.Log;
  38. import org.flowplayer.util.URLUtil;
  39. import flash.display.DisplayObject;
  40. import flash.display.Loader;
  41. import flash.display.LoaderInfo;
  42. import flash.events.Event;
  43. import flash.events.EventDispatcher;
  44. import flash.events.IOErrorEvent;
  45. import flash.events.ProgressEvent;
  46. import flash.net.URLRequest;
  47. import flash.system.ApplicationDomain;
  48. import flash.system.LoaderContext;
  49. import flash.system.SecurityDomain;
  50. import flash.utils.Dictionary;
  51. import flash.utils.getDefinitionByName;
  52. import flash.utils.getQualifiedClassName;
  53. /**
  54. * @author api
  55. */
  56. public class PluginLoader extends EventDispatcher {
  57. private var log:Log = new Log(this);
  58. private var _loadables:Array;
  59. private var _loadedPlugins:Dictionary;
  60. private var _loadedCount:int;
  61. private var _errorHandler:ErrorHandler;
  62. private var _swiffsToLoad:Array;
  63. private var _pluginRegistry:PluginRegistry;
  64. private var _providers:Dictionary;
  65. private var _callback:Function;
  66. private var _baseUrl:String;
  67. private var _useExternalInterface:Boolean;
  68. private var _loadErrorListener:Function;
  69. private var _loadListener:Function;
  70. private var _loadComplete:Boolean;
  71. private var _allPlugins:Array;
  72. private var _loaderContext:LoaderContext;
  73. private var _loadStartedCount:int = 0;
  74. public function PluginLoader(baseUrl:String, pluginRegistry:PluginRegistry, errorHandler:ErrorHandler, useExternalInterface:Boolean) {
  75. _baseUrl = baseUrl;
  76. _pluginRegistry = pluginRegistry;
  77. _errorHandler = errorHandler;
  78. _useExternalInterface = useExternalInterface;
  79. _loadedCount = 0;
  80. }
  81. private function constructUrl(url:String):String {
  82. if (url.indexOf("..") >= 0) return url;
  83. if (url.indexOf("/") >= 0) return url;
  84. return URLUtil.addBaseURL(_baseUrl, url);
  85. }
  86. public function loadPlugin(model:Loadable, callback:Function = null):void {
  87. _callback = callback;
  88. _loadListener = null;
  89. _loadErrorListener = null;
  90. load([model]);
  91. }
  92. public function load(plugins:Array, loadListener:Function = null, loadErrorListener:Function = null):void {
  93. log.debug("load()");
  94. _loadListener = loadListener;
  95. _loadErrorListener = loadErrorListener;
  96. Security.allowDomain("*");
  97. _providers = new Dictionary();
  98. _allPlugins = plugins.concat([]);
  99. _loadables = plugins.filter(function(plugin:*, index:int, array:Array):Boolean {
  100. return plugin.url && String(plugin.url).toLocaleLowerCase().indexOf(".swf") > 0;
  101. });
  102. _swiffsToLoad = getPluginSwiffUrls(plugins);
  103. _loadedPlugins = new Dictionary();
  104. _loadedCount = 0;
  105. _loadStartedCount = 0;
  106. _loaderContext = new LoaderContext();
  107. _loaderContext.applicationDomain = ApplicationDomain.currentDomain;
  108. if (!URLUtil.localDomain(_baseUrl)) {
  109. _loaderContext.securityDomain = SecurityDomain.currentDomain;
  110. }
  111. for (var i:Number = 0; i < _loadables.length; i++) {
  112. Loadable(_loadables[i]).onError(_loadErrorListener);
  113. }
  114. intitializeBuiltInPlugins(plugins);
  115. if (_swiffsToLoad.length == 0) {
  116. setConfigPlugins();
  117. dispatchEvent(new Event(Event.COMPLETE, true, false));
  118. return;
  119. }
  120. loadNext();
  121. }
  122. private function loadNext():Boolean {
  123. if (_loadStartedCount >= _swiffsToLoad.length) {
  124. log.debug("loadNext(): all plugins loaded");
  125. return false;
  126. }
  127. var loader:Loader = new Loader();
  128. loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loaded);
  129. var url:String = _swiffsToLoad[_loadStartedCount];
  130. loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, createIOErrorListener(url));
  131. loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, onProgress);
  132. log.debug("starting to load plugin from url " + _swiffsToLoad[_loadStartedCount]);
  133. loader.load(new URLRequest(url), _loaderContext);
  134. _loadStartedCount++;
  135. return true;
  136. }
  137. private function getPluginSwiffUrls(plugins:Array):Array {
  138. var result:Array = new Array();
  139. for (var i:Number = 0; i < plugins.length; i++) {
  140. var loadable:Loadable = Loadable(plugins[i]);
  141. if (! loadable.isBuiltIn && loadable.url && result.indexOf(loadable.url) < 0) {
  142. result.push(constructUrl(loadable.url));
  143. }
  144. }
  145. return result;
  146. }
  147. private function intitializeBuiltInPlugins(plugins:Array):void {
  148. for (var i:int = 0; i < plugins.length; i++) {
  149. var loadable:Loadable = plugins[i] as Loadable;
  150. log.debug("intitializeBuiltInPlugins() " + loadable);
  151. if (loadable.isBuiltIn) {
  152. log.info("intitializeBuiltInPlugins(), instantiating from loadable " + loadable + ", with config ", loadable.config);
  153. var instance:Object = loadable.instantiate();
  154. var model:PluginModel = createPluginModel(loadable, instance);
  155. model.isBuiltIn = true;
  156. // if (instance.hasOwnProperty("onConfig")) {
  157. // instance.onConfig(model);
  158. // }
  159. initializePlugin(model, instance);
  160. }
  161. }
  162. }
  163. private function createIOErrorListener(url:String):Function {
  164. return function(event:IOErrorEvent):void {
  165. log.error("onIoError " + url);
  166. _loadables.forEach(function(loadable:Loadable, index:int, array:Array):void {
  167. if (! loadable.loadFailed && hasSwiff(url, loadable.url)) {
  168. log.debug("onIoError: this is the swf for loadable " + loadable);
  169. loadable.loadFailed = true;
  170. loadable.dispatchError(PluginError.INIT_FAILED);
  171. incrementLoadedCountAndFireEventIfNeeded();
  172. }
  173. });
  174. };
  175. }
  176. private function onProgress(event:ProgressEvent):void {
  177. log.debug("load in progress");
  178. }
  179. public function get plugins():Dictionary {
  180. return _loadedPlugins;
  181. }
  182. private function loaded(event:Event):void {
  183. var info:LoaderInfo = event.target as LoaderInfo;
  184. log.debug("loaded class name " + getQualifiedClassName(info.content));
  185. var instanceUsed:Boolean = false;
  186. _loadables.forEach(function(loadable:Loadable, index:int, array:Array):void {
  187. if (! loadable.plugin && hasSwiff(info.url, loadable.url)) {
  188. log.debug("this is the swf for loadable " + loadable);
  189. if (loadable.type == "classLibrary") {
  190. initializeClassLibrary(loadable, info);
  191. } else {
  192. var plugin:Object = info.content is AVM1Movie ? info.loader : createPluginInstance(instanceUsed, info.content);
  193. initializePlugin(createPluginModel(loadable, plugin), plugin);
  194. //initializePlugin(loadable, instanceUsed, info);
  195. instanceUsed = true;
  196. }
  197. }
  198. });
  199. incrementLoadedCountAndFireEventIfNeeded();
  200. if (_callback != null) {
  201. _callback();
  202. }
  203. loadNext();
  204. }
  205. private function incrementLoadedCountAndFireEventIfNeeded():void {
  206. if (++_loadedCount == _swiffsToLoad.length) {
  207. log.debug("all plugin SWFs loaded. loaded total " + loadedCount + " plugins");
  208. setConfigPlugins();
  209. dispatchEvent(new Event(Event.COMPLETE, true, false));
  210. }
  211. }
  212. private function initializeClassLibrary(loadable:Loadable, info:LoaderInfo):void {
  213. log.debug("initializing class library " + info.applicationDomain);
  214. _loadedPlugins[loadable] = info.applicationDomain;
  215. _pluginRegistry.registerGenericPlugin(loadable.createPlugin(info.applicationDomain));
  216. }
  217. private function createPluginModel(loadable:Loadable, pluginInstance:Object):PluginModel {
  218. log.debug("creating model for loadable " + loadable + ", instance " + pluginInstance);
  219. _loadedPlugins[loadable] = pluginInstance;
  220. log.debug("pluginInstance " + pluginInstance);
  221. if (pluginInstance is DisplayObject) {
  222. return Loadable(loadable).createDisplayPlugin(pluginInstance as DisplayObject);
  223. } else if (pluginInstance is StreamProvider) {
  224. return Loadable(loadable).createProvider(pluginInstance);
  225. } else {
  226. return Loadable(loadable).createPlugin(pluginInstance);
  227. }
  228. }
  229. private function initializePlugin(model:PluginModel, pluginInstance:Object):void {
  230. if (pluginInstance is FontProvider) {
  231. _pluginRegistry.registerFont(FontProvider(pluginInstance).fontFamily);
  232. } else if (pluginInstance is DisplayObject) {
  233. _pluginRegistry.registerDisplayPlugin(model as DisplayPluginModel, pluginInstance as DisplayObject);
  234. } else if (pluginInstance is StreamProvider) {
  235. _providers[model.name] = pluginInstance;
  236. _pluginRegistry.registerProvider(model as ProviderModel);
  237. } else {
  238. _pluginRegistry.registerGenericPlugin(model);
  239. }
  240. if (pluginInstance is Plugin) {
  241. if (_loadListener != null) {
  242. model.onLoad(_loadListener);
  243. }
  244. model.onError(onPluginError);
  245. }
  246. if (model is Callable && _useExternalInterface) {
  247. ExternalInterfaceHelper.initializeInterface(model as Callable, pluginInstance);
  248. }
  249. }
  250. private function onPluginError(event:PluginEvent):void {
  251. log.debug("onPluginError() " + event.error);
  252. if (event.error) {
  253. _errorHandler.handleError(event.error, event.info + ", " + event.info2, true);
  254. }
  255. }
  256. private function createPluginInstance(instanceUsed:Boolean, instance:DisplayObject):Object {
  257. if (instance.hasOwnProperty("newPlugin")) return instance["newPlugin"]();
  258. if (! instanceUsed) {
  259. log.debug("using existing instance " + instance);
  260. return instance;
  261. }
  262. var className:String = getQualifiedClassName(instance);
  263. log.info("creating new " + className);
  264. var PluginClass:Class = Class(getDefinitionByName(className));
  265. return new PluginClass() as DisplayObject;
  266. }
  267. public function setConfigPlugins():void {
  268. _allPlugins.forEach(function(loadable:Loadable, index:int, array:Array):void {
  269. if (! loadable.loadFailed) {
  270. var pluginInstance:Object = plugins[loadable];
  271. // we don't have a plugin instance for all of these (dock for example)
  272. if (pluginInstance) {
  273. log.info(index + ": setting config to " + pluginInstance + ", " + loadable);
  274. if (pluginInstance is NetStreamControllingStreamProvider) {
  275. log.debug("NetStreamControllingStreamProvider(pluginInstance).config = " +loadable.plugin);
  276. NetStreamControllingStreamProvider(pluginInstance).model = ProviderModel(loadable.plugin);
  277. } else {
  278. if (pluginInstance.hasOwnProperty("onConfig")) {
  279. pluginInstance.onConfig(loadable.plugin);
  280. }
  281. }
  282. }
  283. }
  284. });
  285. }
  286. private function hasSwiff(infoUrl:String, modelUrl:String):Boolean {
  287. if (! modelUrl) return false;
  288. var slashPos:int = modelUrl.lastIndexOf("/");
  289. var swiffUrl:String = slashPos >= 0 ? modelUrl.substr(slashPos) : modelUrl;
  290. return StringUtil.endsWith(infoUrl, swiffUrl);
  291. }
  292. public function get providers():Dictionary {
  293. return _providers;
  294. }
  295. public function get loadedCount():int {
  296. return _loadedCount;
  297. }
  298. public function get loadComplete():Boolean {
  299. return _loadComplete;
  300. }
  301. }
  302. }