Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / Runtime.js
index 308d980..708667b 100644 (file)
@@ -51,6 +51,40 @@ function loadResource(url)
     return xhr.status < 400 ? xhr.responseText : "";
 }
 
+/**
+ * @param {string} url
+ * @return {!Promise.<string>}
+ */
+function loadResourcePromise(url)
+{
+    return new Promise(load);
+
+    /**
+     * @param {function(?)} fulfill
+     * @param {function(*)} reject
+     */
+    function load(fulfill, reject)
+    {
+        var xhr = new XMLHttpRequest();
+        xhr.open("GET", url, true);
+        xhr.onreadystatechange = onreadystatechange;
+
+        /**
+         * @param {Event} e
+         */
+        function onreadystatechange(e)
+        {
+            if (xhr.readyState !== 4)
+                return;
+
+            if ([0, 200, 304].indexOf(xhr.status) === -1)  // Testing harness file:/// results in 0.
+                reject(new Error("While loading from url " + url + " server responded with a status of " + xhr.status));
+            else
+                fulfill(e.target.response);
+        }
+        xhr.send(null);
+    }
+}
 
 /**
  * http://tools.ietf.org/html/rfc3986#section-5.2.4
@@ -85,27 +119,56 @@ function normalizePath(path)
 }
 
 /**
- * @param {string} scriptName
+ * @param {!Array.<string>} scriptNames
+ * @return {!Promise.<undefined>}
  */
-function loadScript(scriptName)
+function loadScriptsPromise(scriptNames)
 {
-    var sourceURL = self._importScriptPathPrefix + scriptName;
-    var schemaIndex = sourceURL.indexOf("://") + 3;
-    sourceURL = sourceURL.substring(0, schemaIndex) + normalizePath(sourceURL.substring(schemaIndex));
-    if (_loadedScripts[sourceURL])
-        return;
-    _loadedScripts[sourceURL] = true;
-    var scriptSource = loadResource(sourceURL);
-    if (!scriptSource) {
-        console.error("Empty response arrived for script '" + sourceURL + "'");
-        return;
+    /** @type {!Array.<!Promise.<string>>} */
+    var promises = [];
+    /** @type {!Array.<string>} */
+    var urls = [];
+    var sources = new Array(scriptNames.length);
+    var scriptToEval = 0;
+    for (var i = 0; i < scriptNames.length; ++i) {
+        var scriptName = scriptNames[i];
+        var sourceURL = self._importScriptPathPrefix + scriptName;
+        var schemaIndex = sourceURL.indexOf("://") + 3;
+        sourceURL = sourceURL.substring(0, schemaIndex) + normalizePath(sourceURL.substring(schemaIndex));
+        if (_loadedScripts[sourceURL])
+            continue;
+        urls.push(sourceURL);
+        promises.push(loadResourcePromise(sourceURL).then(scriptSourceLoaded.bind(null, i), scriptSourceLoaded.bind(null, i, undefined)));
     }
-    var oldPrefix = self._importScriptPathPrefix;
-    self._importScriptPathPrefix += scriptName.substring(0, scriptName.lastIndexOf("/") + 1);
-    try {
+    return Promise.all(promises).then(undefined);
+
+    /**
+     * @param {number} scriptNumber
+     * @param {string=} scriptSource
+     */
+    function scriptSourceLoaded(scriptNumber, scriptSource)
+    {
+        sources[scriptNumber] = scriptSource || "";
+        // Eval scripts as fast as possible.
+        while (typeof sources[scriptToEval] !== "undefined") {
+            evaluateScript(urls[scriptToEval], sources[scriptToEval]);
+            ++scriptToEval;
+        }
+    }
+
+    /**
+     * @param {string} sourceURL
+     * @param {string=} scriptSource
+     */
+    function evaluateScript(sourceURL, scriptSource)
+    {
+        _loadedScripts[sourceURL] = true;
+        if (!scriptSource) {
+            // Do not reject, as this is normal in the hosted mode.
+            console.error("Empty response arrived for script '" + sourceURL + "'");
+            return;
+        }
         self.eval(scriptSource + "\n//# sourceURL=" + sourceURL);
-    } finally {
-        self._importScriptPathPrefix = oldPrefix;
     }
 }
 
@@ -116,8 +179,10 @@ function loadScript(scriptName)
 
 /**
  * @constructor
+ * @param {!Array.<!Runtime.ModuleDescriptor>} descriptors
+ * @param {!Array.<string>=} coreModuleNames
  */
-var Runtime = function()
+function Runtime(descriptors, coreModuleNames)
 {
     /**
      * @type {!Array.<!Runtime.Module>}
@@ -142,8 +207,10 @@ var Runtime = function()
      */
     this._descriptorsMap = {};
 
-    for (var i = 0; i < allDescriptors.length; ++i)
-        this._descriptorsMap[allDescriptors[i]["name"]] = allDescriptors[i];
+    for (var i = 0; i < descriptors.length; ++i)
+        this._registerModule(descriptors[i]);
+    if (coreModuleNames)
+        this._loadAutoStartModules(coreModuleNames).catch(Runtime._reportError);
 }
 
 /**
@@ -152,6 +219,11 @@ var Runtime = function()
 Runtime._queryParamsObject = { __proto__: null };
 
 /**
+ * @type {!Object.<string, string>}
+ */
+Runtime.cachedResources = { __proto__: null };
+
+/**
  * @return {boolean}
  */
 Runtime.isReleaseMode = function()
@@ -233,55 +305,53 @@ Runtime.startWorker = function(moduleName)
 Runtime.startApplication = function(appName)
 {
     console.timeStamp("Runtime.startApplication");
-    var experiments = Runtime._experimentsSetting();
 
     var allDescriptorsByName = {};
     for (var i = 0; Runtime.isReleaseMode() && i < allDescriptors.length; ++i) {
         var d = allDescriptors[i];
         allDescriptorsByName[d["name"]] = d;
     }
-    var moduleDescriptors = applicationDescriptor || Runtime._parseJsonURL(appName + ".json");
-    var allModules = [];
-    var coreModuleNames = [];
-    moduleDescriptors.forEach(populateModules);
+
+    var applicationPromise;
+    if (applicationDescriptor)
+        applicationPromise = Promise.resolve(applicationDescriptor);
+    else
+        applicationPromise = loadResourcePromise(appName + ".json").then(JSON.parse.bind(JSON));
+
+    applicationPromise.then(parseModuleDescriptors);
 
     /**
-     * @param {!Object} desc
+     * @param {!Array.<!Object>} configuration
      */
-    function populateModules(desc)
+    function parseModuleDescriptors(configuration)
     {
-        if (!isActive(desc))
-            return;
-        var name = desc.name;
-        var moduleJSON = allDescriptorsByName[name];
-        if (!moduleJSON) {
-            moduleJSON = Runtime._parseJsonURL(name + "/module.json");
-            moduleJSON["name"] = name;
+        var moduleJSONPromises = [];
+        var coreModuleNames = [];
+        for (var i = 0; i < configuration.length; ++i) {
+            var descriptor = configuration[i];
+            if (descriptor["type"] === "worker")
+                continue;
+            var name = descriptor["name"];
+            var moduleJSON = allDescriptorsByName[name];
+            if (moduleJSON)
+                moduleJSONPromises.push(Promise.resolve(moduleJSON));
+            else
+                moduleJSONPromises.push(loadResourcePromise(name + "/module.json").then(JSON.parse.bind(JSON)));
+            if (descriptor["type"] === "autostart")
+                coreModuleNames.push(name);
         }
-        allModules.push(moduleJSON);
-        if (desc["type"] === "autostart")
-            coreModuleNames.push(name);
-    }
 
-    /**
-     * @param {!Object} descriptor
-     * @return {boolean}
-     */
-    function isActive(descriptor)
-    {
-        var activatorExperiment = descriptor["experiment"];
-        if (activatorExperiment) {
-            var shouldBePresent = activatorExperiment.charAt(0) !== "!";
-            if (!shouldBePresent)
-                activatorExperiment = activatorExperiment.substr(1);
-            if (!!experiments[activatorExperiment] !== shouldBePresent)
-                return false;
+        Promise.all(moduleJSONPromises).then(instantiateRuntime).catch(Runtime._reportError);
+        /**
+         * @param {!Array.<!Object>} moduleDescriptors
+         */
+        function instantiateRuntime(moduleDescriptors)
+        {
+            for (var i = 0; !Runtime.isReleaseMode() && i < moduleDescriptors.length; ++i)
+                moduleDescriptors[i]["name"] = configuration[i]["name"];
+            self.runtime = new Runtime(moduleDescriptors, coreModuleNames);
         }
-        return descriptor["type"] !== "worker";
     }
-
-    Runtime._initializeApplication(allModules);
-    self.runtime.loadAutoStartModules(coreModuleNames);
 }
 
 /**
@@ -307,80 +377,104 @@ Runtime._experimentsSetting = function()
 }
 
 /**
- * @param {!Array.<!Runtime.ModuleDescriptor>} descriptors
+ * @param {!Array.<!Promise.<T, !Error>>} promises
+ * @return {!Promise.<!Array.<T>>}
+ * @template T
  */
-Runtime._initializeApplication = function(descriptors)
+Runtime._some = function(promises)
 {
-    self.runtime = new Runtime();
-    var names = [];
-    for (var i = 0; i < descriptors.length; ++i) {
-        var name = descriptors[i]["name"];
-        self.runtime._descriptorsMap[name] = descriptors[i];
-        names.push(name);
+    var all = [];
+    var wasRejected = [];
+    for (var i = 0; i < promises.length; ++i) {
+        // Workaround closure compiler bug.
+        var handlerFunction = /** @type {function()} */ (handler.bind(promises[i], i));
+        all.push(promises[i].catch(handlerFunction));
+    }
+
+    return Promise.all(all).then(filterOutFailuresResults);
+
+    /**
+     * @param {!Array.<T>} results
+     * @return {!Array.<T>}
+     * @template T
+     */
+    function filterOutFailuresResults(results)
+    {
+        var filtered = [];
+        for (var i = 0; i < results.length; ++i) {
+            if (!wasRejected[i])
+                filtered.push(results[i]);
+        }
+        return filtered;
+    }
+
+    /**
+     * @this {!Promise}
+     * @param {number} index
+     * @param {!Error} e
+     */
+    function handler(index, e)
+    {
+        wasRejected[index] = true;
+        console.error(e.stack);
     }
-    self.runtime._registerModules(names);
+}
+
+Runtime._console = console;
+Runtime._originalAssert = console.assert;
+Runtime._assert = function(value, message)
+{
+    if (value)
+        return;
+    Runtime._originalAssert.call(Runtime._console, value, message);
 }
 
 /**
- * @param {string} url
- * @return {*}
+ * @param {*} e
  */
-Runtime._parseJsonURL = function(url)
+Runtime._reportError = function(e)
 {
-    var json = loadResource(url);
-    if (!json)
-        throw new Error("Resource not found at " + url + " " + new Error().stack);
-    return JSON.parse(json);
+    if (e instanceof Error)
+        console.error(e.stack);
+    else
+        console.error(e);
 }
 
 Runtime.prototype = {
-    /**
-     * @param {!Array.<string>} configuration
-     */
-    _registerModules: function(configuration)
-    {
-        for (var i = 0; i < configuration.length; ++i)
-            this._registerModule(configuration[i]);
-    },
 
     /**
-     * @param {string} moduleName
+     * @param {!Runtime.ModuleDescriptor} descriptor
      */
-    _registerModule: function(moduleName)
+    _registerModule: function(descriptor)
     {
-        if (!this._descriptorsMap[moduleName]) {
-            var content = loadResource(moduleName + "/module.json");
-            if (!content)
-                throw new Error("Module is not defined: " + moduleName + " " + new Error().stack);
-
-            var module = /** @type {!Runtime.ModuleDescriptor} */ (self.eval("(" + content + ")"));
-            module["name"] = moduleName;
-            this._descriptorsMap[moduleName] = module;
-        }
-        var module = new Runtime.Module(this, this._descriptorsMap[moduleName]);
+        var module = new Runtime.Module(this, descriptor);
         this._modules.push(module);
-        this._modulesMap[moduleName] = module;
+        this._modulesMap[descriptor["name"]] = module;
     },
 
     /**
      * @param {string} moduleName
+     * @return {!Promise.<undefined>}
      */
-    loadModule: function(moduleName)
+    loadModulePromise: function(moduleName)
     {
-        this._modulesMap[moduleName]._load();
+        return this._modulesMap[moduleName]._loadPromise();
     },
 
     /**
      * @param {!Array.<string>} moduleNames
+     * @return {!Promise.<!Array.<*>>}
      */
-    loadAutoStartModules: function(moduleNames)
+    _loadAutoStartModules: function(moduleNames)
     {
+        var promises = [];
         for (var i = 0; i < moduleNames.length; ++i) {
             if (Runtime.isReleaseMode())
-                self.runtime._modulesMap[moduleNames[i]]._loaded = true;
+                this._modulesMap[moduleNames[i]]._loaded = true;
             else
-                self.runtime.loadModule(moduleNames[i]);
+                promises.push(this.loadModulePromise(moduleNames[i]));
         }
+        return Promise.all(promises);
     },
 
     /**
@@ -427,7 +521,7 @@ Runtime.prototype = {
 
     /**
      * @param {!Runtime.Extension} extension
-     * @param {!Array.<!Function>=} currentContextTypes
+     * @param {!Set.<!Function>=} currentContextTypes
      * @return {boolean}
      */
     isExtensionApplicableToContextTypes: function(extension, currentContextTypes)
@@ -435,13 +529,7 @@ Runtime.prototype = {
         if (!extension.descriptor().contextTypes)
             return true;
 
-        // FIXME: Remove this workaround once Set is available natively.
-        for (var i = 0; i < currentContextTypes.length; ++i)
-            currentContextTypes[i]["__applicable"] = true;
-        var result = this._checkExtensionApplicability(extension, currentContextTypes ? isContextTypeKnown : null);
-        for (var i = 0; i < currentContextTypes.length; ++i)
-            delete currentContextTypes[i]["__applicable"];
-        return result;
+        return this._checkExtensionApplicability(extension, currentContextTypes ? isContextTypeKnown : null);
 
         /**
          * @param {!Function} targetType
@@ -449,7 +537,7 @@ Runtime.prototype = {
          */
         function isContextTypeKnown(targetType)
         {
-            return !!targetType["__applicable"];
+            return currentContextTypes.has(targetType);
         }
     },
 
@@ -460,6 +548,8 @@ Runtime.prototype = {
      */
     extensions: function(type, context)
     {
+        return this._extensions.filter(filter).sort(orderComparator);
+
         /**
          * @param {!Runtime.Extension} extension
          * @return {boolean}
@@ -471,9 +561,23 @@ Runtime.prototype = {
             var activatorExperiment = extension.descriptor()["experiment"];
             if (activatorExperiment && !Runtime.experiments.isEnabled(activatorExperiment))
                 return false;
+            activatorExperiment = extension._module._descriptor["experiment"];
+            if (activatorExperiment && !Runtime.experiments.isEnabled(activatorExperiment))
+                return false;
             return !context || extension.isApplicable(context);
         }
-        return this._extensions.filter(filter);
+
+        /**
+         * @param {!Runtime.Extension} extension1
+         * @param {!Runtime.Extension} extension2
+         * @return {number}
+         */
+        function orderComparator(extension1, extension2)
+        {
+            var order1 = extension1.descriptor()["order"] || 0;
+            var order2 = extension2.descriptor()["order"] || 0;
+            return order1 - order2;
+        }
     },
 
     /**
@@ -489,77 +593,28 @@ Runtime.prototype = {
     /**
      * @param {*} type
      * @param {?Object=} context
-     * @return {!Array.<!Object>}
+     * @return {!Promise.<!Array.<!Object>>}
      */
-    instances: function(type, context)
+    instancesPromise: function(type, context)
     {
-        /**
-         * @param {!Runtime.Extension} extension
-         * @return {?Object}
-         */
-        function instantiate(extension)
-        {
-            return extension.instance();
-        }
-        return this.extensions(type, context).filter(instantiate).map(instantiate);
+        var extensions = this.extensions(type, context);
+        var promises = [];
+        for (var i = 0; i < extensions.length; ++i)
+            promises.push(extensions[i].instancePromise());
+        return Runtime._some(promises);
     },
 
     /**
      * @param {*} type
      * @param {?Object=} context
-     * @return {?Object}
+     * @return {!Promise.<!Object>}
      */
-    instance: function(type, context)
+    instancePromise: function(type, context)
     {
         var extension = this.extension(type, context);
-        return extension ? extension.instance() : null;
-    },
-
-    /**
-     * @param {string|!Function} type
-     * @param {string} nameProperty
-     * @param {string} orderProperty
-     * @return {function(string, string):number}
-     */
-    orderComparator: function(type, nameProperty, orderProperty)
-    {
-        var extensions = this.extensions(type);
-        var orderForName = {};
-        for (var i = 0; i < extensions.length; ++i) {
-            var descriptor = extensions[i].descriptor();
-            orderForName[descriptor[nameProperty]] = descriptor[orderProperty];
-        }
-
-        /**
-         * @param {string} name1
-         * @param {string} name2
-         * @return {number}
-         */
-        function result(name1, name2)
-        {
-            if (name1 in orderForName && name2 in orderForName)
-                return orderForName[name1] - orderForName[name2];
-            if (name1 in orderForName)
-                return -1;
-            if (name2 in orderForName)
-                return 1;
-            return compare(name1, name2);
-        }
-
-        /**
-         * @param {string} left
-         * @param {string} right
-         * @return {number}
-         */
-        function compare(left, right)
-        {
-            if (left > right)
-              return 1;
-            if (left < right)
-                return -1;
-            return 0;
-        }
-        return result;
+        if (!extension)
+            return Promise.reject(new Error("No such extension: " + type + " in given context."));
+        return extension.instancePromise();
     },
 
     /**
@@ -575,7 +630,7 @@ Runtime.prototype = {
             if (object)
                 this._cachedTypeClasses[typeName] = /** @type function(new:Object) */(object);
         }
-        return this._cachedTypeClasses[typeName];
+        return this._cachedTypeClasses[typeName] || null;
     }
 }
 
@@ -636,6 +691,8 @@ Runtime.Module = function(manager, descriptor)
     this._manager = manager;
     this._descriptor = descriptor;
     this._name = descriptor.name;
+    /** @type {!Object.<string, ?Object>} */
+    this._instanceMap = {};
     var extensions = /** @type {?Array.<!Runtime.ExtensionDescriptor>} */ (descriptor.extensions);
     for (var i = 0; extensions && i < extensions.length; ++i)
         this._manager._extensions.push(new Runtime.Extension(this, extensions[i]));
@@ -651,34 +708,79 @@ Runtime.Module.prototype = {
         return this._name;
     },
 
-    _load: function()
+    /**
+     * @return {!Promise.<undefined>}
+     */
+    _loadPromise: function()
     {
         if (this._loaded)
-            return;
+            return Promise.resolve();
 
-        if (this._isLoading) {
-            var oldStackTraceLimit = Error.stackTraceLimit;
-            Error.stackTraceLimit = 50;
-            console.assert(false, "Module " + this._name + " is loaded from itself: " + new Error().stack);
-            Error.stackTraceLimit = oldStackTraceLimit;
-            return;
-        }
+        if (this._pendingLoadPromise)
+            return this._pendingLoadPromise;
 
-        this._isLoading = true;
         var dependencies = this._descriptor.dependencies;
+        var dependencyPromises = [];
         for (var i = 0; dependencies && i < dependencies.length; ++i)
-            this._manager.loadModule(dependencies[i]);
-        if (this._descriptor.scripts) {
-            if (Runtime.isReleaseMode()) {
-                loadScript(this._name + "_module.js");
-            } else {
-                var scripts = this._descriptor.scripts;
-                for (var i = 0; i < scripts.length; ++i)
-                    loadScript(this._name + "/" + scripts[i]);
-            }
+            dependencyPromises.push(this._manager._modulesMap[dependencies[i]]._loadPromise());
+
+        this._pendingLoadPromise = Promise.all(dependencyPromises)
+            .then(this._loadScripts.bind(this))
+            .then(markAsLoaded.bind(this));
+
+        return this._pendingLoadPromise;
+
+        /**
+         * @this {Runtime.Module}
+         */
+        function markAsLoaded()
+        {
+            delete this._pendingLoadPromise;
+            this._loaded = true;
+        }
+    },
+
+    /**
+     * @return {!Promise.<undefined>}
+     */
+    _loadScripts: function()
+    {
+        if (!this._descriptor.scripts)
+            return Promise.resolve(undefined);
+
+        if (Runtime.isReleaseMode())
+            return loadScriptsPromise([this._name + "_module.js"]);
+
+        return loadScriptsPromise(this._descriptor.scripts.map(modularizeURL, this)).catch(Runtime._reportError);
+
+        /**
+         * @param {string} scriptName
+         * @this {Runtime.Module}
+         */
+        function modularizeURL(scriptName)
+        {
+            return this._name + "/" + scriptName;
         }
-        this._isLoading = false;
-        this._loaded = true;
+    },
+
+    /**
+     * @param {string} className
+     * @return {?Object}
+     */
+    _instance: function(className)
+    {
+        if (className in this._instanceMap)
+            return this._instanceMap[className];
+
+        var constructorFunction = window.eval(className);
+        if (!(constructorFunction instanceof Function)) {
+            this._instanceMap[className] = null;
+            return null;
+        }
+
+        var instance = new constructorFunction();
+        this._instanceMap[className] = instance;
+        return instance;
     }
 }
 
@@ -738,23 +840,29 @@ Runtime.Extension.prototype = {
     },
 
     /**
-     * @return {?Object}
+     * @return {!Promise.<!Object>}
      */
-    instance: function()
+    instancePromise: function()
     {
         if (!this._className)
-            return null;
-
-        if (!this._instance) {
-            this._module._load();
+            return Promise.reject(new Error("No class name in extension"));
+        var className = this._className;
+        if (this._instance)
+            return Promise.resolve(this._instance);
 
-            var constructorFunction = window.eval(this._className);
-            if (!(constructorFunction instanceof Function))
-                return null;
+        return this._module._loadPromise().then(constructInstance.bind(this));
 
-            this._instance = new constructorFunction();
+        /**
+         * @return {!Object}
+         * @this {Runtime.Extension}
+         */
+        function constructInstance()
+        {
+            var result = this._module._instance(className);
+            if (!result)
+                return Promise.reject("Could not instantiate: " + className);
+            return result;
         }
-        return this._instance;
     }
 }
 
@@ -773,9 +881,15 @@ Runtime.ExperimentsSupport.prototype = {
     /**
      * @return {!Array.<!Runtime.Experiment>}
      */
-    allExperiments: function()
+    allConfigurableExperiments: function()
     {
-        return this._experiments.slice();
+        var result = [];
+        for (var i = 0; i < this._experiments.length; i++) {
+            var experiment = this._experiments[i];
+            if (!this._enabledTransiently[experiment.name])
+                result.push(experiment);
+        }
+        return result;
     },
 
     /**
@@ -803,7 +917,7 @@ Runtime.ExperimentsSupport.prototype = {
      */
     register: function(experimentName, experimentTitle, hidden)
     {
-        console.assert(!this._experimentNames[experimentName], "Duplicate registration of experiment " + experimentName);
+        Runtime._assert(!this._experimentNames[experimentName], "Duplicate registration of experiment " + experimentName);
         this._experimentNames[experimentName] = true;
         this._experiments.push(new Runtime.Experiment(this, experimentName, experimentTitle, !!hidden));
     },
@@ -831,8 +945,6 @@ Runtime.ExperimentsSupport.prototype = {
     setEnabled: function(experimentName, enabled)
     {
         this._checkExperiment(experimentName);
-        if (!enabled)
-            delete this._enabledTransiently[experimentName];
         var experimentsSetting = Runtime._experimentsSetting();
         experimentsSetting[experimentName] = enabled;
         this._setExperimentsSetting(experimentsSetting);
@@ -875,7 +987,7 @@ Runtime.ExperimentsSupport.prototype = {
      */
     _checkExperiment: function(experimentName)
     {
-        console.assert(this._experimentNames[experimentName], "Unknown experiment " + experimentName);
+        Runtime._assert(this._experimentNames[experimentName], "Unknown experiment " + experimentName);
     }
 }
 
@@ -936,6 +1048,7 @@ Runtime.Experiment.prototype = {
     }
 })();}
 
+
 // This must be constructed after the query parameters have been parsed.
 Runtime.experiments = new Runtime.ExperimentsSupport();