moduleinfo.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. // Copyright 2008 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /**
  15. * @fileoverview Defines the goog.module.ModuleInfo class.
  16. *
  17. */
  18. goog.provide('goog.module.ModuleInfo');
  19. goog.require('goog.Disposable');
  20. goog.require('goog.async.throwException');
  21. goog.require('goog.functions');
  22. /** @suppress {extraRequire} */
  23. goog.require('goog.module');
  24. goog.require('goog.module.BaseModule');
  25. goog.require('goog.module.ModuleLoadCallback');
  26. // TODO(johnlenz): goog.module.ModuleManager.FailureType into its own file.
  27. goog.forwardDeclare('goog.module.ModuleManager.FailureType');
  28. /**
  29. * A ModuleInfo object is used by the ModuleManager to hold information about a
  30. * module of js code that may or may not yet be loaded into the environment.
  31. *
  32. * @param {Array<string>} deps Ids of the modules that must be loaded before
  33. * this one. The ids must be in dependency order (i.e. if the ith module
  34. * depends on the jth module, then i > j).
  35. * @param {string} id The module's ID.
  36. * @constructor
  37. * @extends {goog.Disposable}
  38. * @final
  39. */
  40. goog.module.ModuleInfo = function(deps, id) {
  41. goog.Disposable.call(this);
  42. /**
  43. * A list of the ids of the modules that must be loaded before this module.
  44. * @type {Array<string>}
  45. * @private
  46. */
  47. this.deps_ = deps;
  48. /**
  49. * The module's ID.
  50. * @type {string}
  51. * @private
  52. */
  53. this.id_ = id;
  54. /**
  55. * Callbacks to execute once this module is loaded.
  56. * @type {Array<goog.module.ModuleLoadCallback>}
  57. * @private
  58. */
  59. this.onloadCallbacks_ = [];
  60. /**
  61. * Callbacks to execute if the module load errors.
  62. * @type {Array<goog.module.ModuleLoadCallback>}
  63. * @private
  64. */
  65. this.onErrorCallbacks_ = [];
  66. /**
  67. * Early callbacks to execute once this module is loaded. Called after
  68. * module initialization but before regular onload callbacks.
  69. * @type {Array<goog.module.ModuleLoadCallback>}
  70. * @private
  71. */
  72. this.earlyOnloadCallbacks_ = [];
  73. };
  74. goog.inherits(goog.module.ModuleInfo, goog.Disposable);
  75. /**
  76. * The uris that can be used to retrieve this module's code.
  77. * @type {Array<string>?}
  78. * @private
  79. */
  80. goog.module.ModuleInfo.prototype.uris_ = null;
  81. /**
  82. * The constructor to use to instantiate the module object after the module
  83. * code is loaded. This must be either goog.module.BaseModule or a subclass of
  84. * it.
  85. * @type {Function}
  86. * @private
  87. */
  88. goog.module.ModuleInfo.prototype.moduleConstructor_ = goog.module.BaseModule;
  89. /**
  90. * The module object. This will be null until the module is loaded.
  91. * @type {goog.module.BaseModule?}
  92. * @private
  93. */
  94. goog.module.ModuleInfo.prototype.module_ = null;
  95. /**
  96. * Gets the dependencies of this module.
  97. * @return {Array<string>} The ids of the modules that this module depends on.
  98. */
  99. goog.module.ModuleInfo.prototype.getDependencies = function() {
  100. return this.deps_;
  101. };
  102. /**
  103. * Gets the ID of this module.
  104. * @return {string} The ID.
  105. */
  106. goog.module.ModuleInfo.prototype.getId = function() {
  107. return this.id_;
  108. };
  109. /**
  110. * Sets the uris of this module.
  111. * @param {Array<string>} uris Uris for this module's code.
  112. */
  113. goog.module.ModuleInfo.prototype.setUris = function(uris) {
  114. this.uris_ = uris;
  115. };
  116. /**
  117. * Gets the uris of this module.
  118. * @return {Array<string>?} Uris for this module's code.
  119. */
  120. goog.module.ModuleInfo.prototype.getUris = function() {
  121. return this.uris_;
  122. };
  123. /**
  124. * Sets the constructor to use to instantiate the module object after the
  125. * module code is loaded.
  126. * @param {Function} constructor The constructor of a goog.module.BaseModule
  127. * subclass.
  128. */
  129. goog.module.ModuleInfo.prototype.setModuleConstructor = function(constructor) {
  130. if (this.moduleConstructor_ === goog.module.BaseModule) {
  131. this.moduleConstructor_ = constructor;
  132. } else {
  133. throw Error('Cannot set module constructor more than once.');
  134. }
  135. };
  136. /**
  137. * Registers a function that should be called after the module is loaded. These
  138. * early callbacks are called after {@link Module#initialize} is called but
  139. * before the other callbacks are called.
  140. * @param {Function} fn A callback function that takes a single argument which
  141. * is the module context.
  142. * @param {Object=} opt_handler Optional handler under whose scope to execute
  143. * the callback.
  144. * @return {!goog.module.ModuleLoadCallback} Reference to the callback
  145. * object.
  146. */
  147. goog.module.ModuleInfo.prototype.registerEarlyCallback = function(
  148. fn, opt_handler) {
  149. return this.registerCallback_(this.earlyOnloadCallbacks_, fn, opt_handler);
  150. };
  151. /**
  152. * Registers a function that should be called after the module is loaded.
  153. * @param {Function} fn A callback function that takes a single argument which
  154. * is the module context.
  155. * @param {Object=} opt_handler Optional handler under whose scope to execute
  156. * the callback.
  157. * @return {!goog.module.ModuleLoadCallback} Reference to the callback
  158. * object.
  159. */
  160. goog.module.ModuleInfo.prototype.registerCallback = function(fn, opt_handler) {
  161. return this.registerCallback_(this.onloadCallbacks_, fn, opt_handler);
  162. };
  163. /**
  164. * Registers a function that should be called if the module load fails.
  165. * @param {Function} fn A callback function that takes a single argument which
  166. * is the failure type.
  167. * @param {Object=} opt_handler Optional handler under whose scope to execute
  168. * the callback.
  169. * @return {!goog.module.ModuleLoadCallback} Reference to the callback
  170. * object.
  171. */
  172. goog.module.ModuleInfo.prototype.registerErrback = function(fn, opt_handler) {
  173. return this.registerCallback_(this.onErrorCallbacks_, fn, opt_handler);
  174. };
  175. /**
  176. * Registers a function that should be called after the module is loaded.
  177. * @param {Array<goog.module.ModuleLoadCallback>} callbacks The array to
  178. * add the callback to.
  179. * @param {Function} fn A callback function that takes a single argument which
  180. * is the module context.
  181. * @param {Object=} opt_handler Optional handler under whose scope to execute
  182. * the callback.
  183. * @return {!goog.module.ModuleLoadCallback} Reference to the callback
  184. * object.
  185. * @private
  186. */
  187. goog.module.ModuleInfo.prototype.registerCallback_ = function(
  188. callbacks, fn, opt_handler) {
  189. var callback = new goog.module.ModuleLoadCallback(fn, opt_handler);
  190. callbacks.push(callback);
  191. return callback;
  192. };
  193. /**
  194. * Determines whether the module has been loaded.
  195. * @return {boolean} Whether the module has been loaded.
  196. */
  197. goog.module.ModuleInfo.prototype.isLoaded = function() {
  198. return !!this.module_;
  199. };
  200. /**
  201. * Gets the module.
  202. * @return {goog.module.BaseModule?} The module if it has been loaded.
  203. * Otherwise, null.
  204. */
  205. goog.module.ModuleInfo.prototype.getModule = function() {
  206. return this.module_;
  207. };
  208. /**
  209. * Sets this module as loaded.
  210. * @param {function() : Object} contextProvider A function that provides the
  211. * module context.
  212. * @return {boolean} Whether any errors occurred while executing the onload
  213. * callbacks.
  214. */
  215. goog.module.ModuleInfo.prototype.onLoad = function(contextProvider) {
  216. // Instantiate and initialize the module object.
  217. var module = new this.moduleConstructor_;
  218. module.initialize(contextProvider());
  219. // Keep an internal reference to the module.
  220. this.module_ = module;
  221. // Fire any early callbacks that were waiting for the module to be loaded.
  222. var errors =
  223. !!this.callCallbacks_(this.earlyOnloadCallbacks_, contextProvider());
  224. // Fire any callbacks that were waiting for the module to be loaded.
  225. errors =
  226. errors || !!this.callCallbacks_(this.onloadCallbacks_, contextProvider());
  227. if (!errors) {
  228. // Clear the errbacks.
  229. this.onErrorCallbacks_.length = 0;
  230. }
  231. return errors;
  232. };
  233. /**
  234. * Calls the error callbacks for the module.
  235. * @param {goog.module.ModuleManager.FailureType} cause What caused the error.
  236. */
  237. goog.module.ModuleInfo.prototype.onError = function(cause) {
  238. var result = this.callCallbacks_(this.onErrorCallbacks_, cause);
  239. if (result) {
  240. // Throw an exception asynchronously. Do not let the exception leak
  241. // up to the caller, or it will blow up the module loading framework.
  242. window.setTimeout(
  243. goog.functions.error('Module errback failures: ' + result), 0);
  244. }
  245. this.earlyOnloadCallbacks_.length = 0;
  246. this.onloadCallbacks_.length = 0;
  247. };
  248. /**
  249. * Helper to call the callbacks after module load.
  250. * @param {Array<goog.module.ModuleLoadCallback>} callbacks The callbacks
  251. * to call and then clear.
  252. * @param {*} context The module context.
  253. * @return {Array<*>} Any errors encountered while calling the callbacks,
  254. * or null if there were no errors.
  255. * @private
  256. */
  257. goog.module.ModuleInfo.prototype.callCallbacks_ = function(callbacks, context) {
  258. // NOTE(nicksantos):
  259. // In practice, there are two error-handling scenarios:
  260. // 1) The callback does some mandatory initialization of the module.
  261. // 2) The callback is for completion of some optional UI event.
  262. // There's no good way to handle both scenarios.
  263. //
  264. // Our strategy here is to protect module manager from exceptions, so that
  265. // the failure of one module doesn't affect the loading of other modules.
  266. // Errors are thrown outside of the current stack frame, so they still
  267. // get reported but don't interrupt execution.
  268. // Call each callback in the order they were registered
  269. var errors = [];
  270. for (var i = 0; i < callbacks.length; i++) {
  271. try {
  272. callbacks[i].execute(context);
  273. } catch (e) {
  274. goog.async.throwException(e);
  275. errors.push(e);
  276. }
  277. }
  278. // Clear the list of callbacks.
  279. callbacks.length = 0;
  280. return errors.length ? errors : null;
  281. };
  282. /** @override */
  283. goog.module.ModuleInfo.prototype.disposeInternal = function() {
  284. goog.module.ModuleInfo.superClass_.disposeInternal.call(this);
  285. goog.dispose(this.module_);
  286. };