Repository: cordova-app-harness
Updated Branches:
  refs/heads/master ba6a40533 -> 18f4ce7af


Write per-app cordova_plugins.js to contain only required plugins

This also adds plugin metadata for CRX-based apps


Project: http://git-wip-us.apache.org/repos/asf/cordova-app-harness/repo
Commit: 
http://git-wip-us.apache.org/repos/asf/cordova-app-harness/commit/18f4ce7a
Tree: http://git-wip-us.apache.org/repos/asf/cordova-app-harness/tree/18f4ce7a
Diff: http://git-wip-us.apache.org/repos/asf/cordova-app-harness/diff/18f4ce7a

Branch: refs/heads/master
Commit: 18f4ce7afd0692cd286c3cf402ca4e009188dfb4
Parents: 6904274
Author: Andrew Grieve <[email protected]>
Authored: Thu May 1 11:17:21 2014 -0400
Committer: Andrew Grieve <[email protected]>
Committed: Thu May 1 11:19:09 2014 -0400

----------------------------------------------------------------------
 www/cdvah/js/CrxInstaller.js    | 76 ++++++++++++++++++++++++++++++++++++
 www/cdvah/js/Installer.js       | 39 ++++++------------
 www/cdvah/js/PluginMetadata.js  | 65 +++++++++++++++++++++++++++---
 www/cdvah/js/ResourcesLoader.js | 12 ++++++
 www/cdvah/js/ServeInstaller.js  | 75 +++++++++++++++++++++--------------
 5 files changed, 206 insertions(+), 61 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-app-harness/blob/18f4ce7a/www/cdvah/js/CrxInstaller.js
----------------------------------------------------------------------
diff --git a/www/cdvah/js/CrxInstaller.js b/www/cdvah/js/CrxInstaller.js
index a6fdd5a..38d3bce 100644
--- a/www/cdvah/js/CrxInstaller.js
+++ b/www/cdvah/js/CrxInstaller.js
@@ -1,5 +1,66 @@
 (function(){
     'use strict';
+    // TODO: put these constants into an npm module.
+    // TODO: Move CRX support into MobileChromeApps/harness.
+    var DEFAULT_PLUGINS = [
+        'org.apache.cordova.file',
+        'org.apache.cordova.inappbrowser',
+        'org.apache.cordova.network-information',
+        'org.apache.cordova.keyboard',
+        'org.apache.cordova.statusbar',
+        'org.chromium.navigation',
+        'org.chromium.bootstrap',
+        'org.chromium.i18n',
+        'org.chromium.polyfill.CustomEvent',
+        'org.chromium.polyfill.xhr_features',
+        'org.chromium.polyfill.blob_constructor'
+    ];
+
+    var PLUGIN_MAP = {
+      'alarms': ['org.chromium.alarms'],
+      'fileSystem': ['org.chromium.fileSystem',
+                     'org.chromium.FileChooser'],
+      'gcm': ['org.chromium.gcm'],
+      'identity': ['org.chromium.identity'],
+      'idle': ['org.chromium.idle'],
+      'notifications': ['org.chromium.notifications'],
+      'payments': ['com.google.payments'],
+      'power': ['org.chromium.power'],
+      'pushMessaging': ['org.chromium.pushMessaging'],
+      'socket': ['org.chromium.socket'],
+      'storage': ['org.chromium.storage'],
+      'syncFileSystem': ['org.chromium.syncFileSystem'],
+      'unlimitedStorage': []
+    };
+    function extractPluginsFromManifest(manifest) {
+        var permissions = [],
+                plugins = [],
+                i;
+        if (manifest.permissions) {
+            for (i = 0; i < manifest.permissions.length; ++i) {
+                if (typeof manifest.permissions[i] === 'string') {
+                    var matchPatternParts = 
/<all_urls>|([^:]+:\/\/[^\/]+)(\/.*)$/.exec(manifest.permissions[i]);
+                    if (!matchPatternParts) {
+                        permissions.push(manifest.permissions[i]);
+                    }
+                } else {
+                    permissions = 
permissions.concat(Object.keys(manifest.permissions[i]));
+                }
+            }
+        }
+        for (i = 0; i < permissions.length; i++) {
+            var pluginsForPermission = PLUGIN_MAP[permissions[i]];
+            if (pluginsForPermission) {
+                for (var j = 0; j < pluginsForPermission.length; ++j) {
+                    plugins.push(pluginsForPermission[j]);
+                }
+            } else {
+                console.warn('Permission not supported: ' + permissions[i] + ' 
(skipping)');
+            }
+        }
+        return DEFAULT_PLUGINS.concat(plugins);
+    }
+
     /* global myApp */
     myApp.run(['$q', 'Installer', 'AppsService', 'ResourcesLoader', 
'urlCleanup', function($q, Installer, AppsService, ResourcesLoader, urlCleanup){
 
@@ -13,6 +74,20 @@
 
         CrxInstaller.prototype.type = 'crx';
 
+        CrxInstaller.prototype.getPluginMetadata = function() {
+            return ResourcesLoader.readJSONFileContents(this.installPath + 
'/www/manifest.json')
+            .then(function(manifestJson) {
+                var pluginIds = extractPluginsFromManifest(manifestJson);
+                var harnessPluginMetadata = 
cordova.require('cordova/plugin_list').metadata;
+                var ret = {};
+                // Make all versions match what is installed.
+                for (var i = 0; i < pluginIds.length; ++i) {
+                    ret[pluginIds[i]] = harnessPluginMetadata[pluginIds[i]] || 
'0';
+                }
+                return ret;
+            });
+        };
+
         CrxInstaller.prototype.doUpdateApp = function() {
             var installPath = this.installPath;
             var platformConfig = location.pathname.replace(/\/[^\/]*$/, 
'/crx_files/config.' + platformId + '.xml');
@@ -26,6 +101,7 @@
                 return ResourcesLoader.extractZipFile(crxFile, installPath + 
'/www');
             }).then(function() {
                 // Copy in the config.<platform>.xml file from the harness.
+                // TODO: We should be constructing this based on installed 
plugins.
                 return ResourcesLoader.downloadFromUrl(platformConfig, 
targetConfig);
             });
         };

http://git-wip-us.apache.org/repos/asf/cordova-app-harness/blob/18f4ce7a/www/cdvah/js/Installer.js
----------------------------------------------------------------------
diff --git a/www/cdvah/js/Installer.js b/www/cdvah/js/Installer.js
index 339430c..76894c2 100644
--- a/www/cdvah/js/Installer.js
+++ b/www/cdvah/js/Installer.js
@@ -31,24 +31,6 @@
             });
         }
 
-        function getAppPlugins(cordovaPluginsFile) {
-            console.log('Reading plugins from: ' + cordovaPluginsFile);
-            return ResourcesLoader.readFileContents(cordovaPluginsFile)
-            .then(function(contents) {
-                if (!contents) {
-                    throw new Error('cordova_plugins.js file is empty. 
Something has gone wrong with "cordova prepare".');
-                }
-
-                // Extract the JSON data from inside the JS file.
-                // It's between two magic comments created by Plugman.
-                var startIndex = contents.indexOf('TOP OF METADATA') + 16;
-                var endIndex = contents.indexOf('// BOTTOM OF METADATA');
-                var target = contents.substring(startIndex, endIndex);
-                var metadata = JSON.parse(target);
-                return metadata;
-            });
-        }
-
         function Installer(url, appId) {
             this.url = url;
             this.appId = appId || '';
@@ -70,21 +52,27 @@
             return this.doUpdateApp()
             .then(function() {
                 self.lastUpdated = new Date();
-                if (self.type === 'crx') {
-                    // No cordova_plugins.js to read for .crx-based apps.
-                    return $q.when({});
-                } else {
-                    return getAppPlugins(installPath + 
'/www/cordova_plugins.js');
-                }
+                return self.getPluginMetadata();
             }, null, function(status) {
                 self.updatingStatus = Math.round(status * 100);
             }).then(function(metadata) {
                 self.plugins = PluginMetadata.process(metadata);
+                var pluginIds = Object.keys(metadata);
+                var newPluginsFileData = 
PluginMetadata.createNewPluginListFile(pluginIds);
+                return ResourcesLoader.writeFileContents(installPath + 
'/www/cordova_plugins.js', newPluginsFileData);
             }).finally(function() {
                 self.updatingStatus = null;
             });
         };
 
+        Installer.prototype.doUpdateApp = function() {
+            throw new Error('Installer ' + this.type + ' failed to implement 
doUpdateApp.');
+        };
+
+        Installer.prototype.getPluginMetadata = function() {
+            throw new Error('Installer ' + this.type + ' failed to implement 
getPluginMetadata.');
+        };
+
         Installer.prototype.deleteFiles = function() {
             this.lastUpdated = null;
             if (this.installPath) {
@@ -124,9 +112,8 @@
                     UrlRemap.injectJsForUrl('^(?!' + harnessUrl + ')', 
injectString);
                     // Allow navigations back to the menu.
                     UrlRemap.setResetUrl('^' + harnessUrl);
-                    // Override cordova.js, cordova_plugins.js, and 
www/plugins to point at bundled plugins.
+                    // Override cordova.js, and www/plugins to point at 
bundled plugins.
                     
UrlRemap.aliasUri('^(?!app-harness://).*/www/cordova\\.js.*', '.+', 
'app-harness:///cordova.js', false /* redirect */, true /* 
allowFurtherRemapping */);
-                    
UrlRemap.aliasUri('^(?!app-harness://).*/www/cordova_plugins\\.js.*', '.+', 
'app-harness:///cordova_plugins.js', false /* redirect */, true /* 
allowFurtherRemapping */);
                     UrlRemap.aliasUri('^(?!app-harness://).*/www/plugins/.*', 
'^.*?/www/plugins/' , 'app-harness:///plugins/', false /* redirect */, true /* 
allowFurtherRemapping */);
 
                     // Make any references to www/ point to the app's install 
location.

http://git-wip-us.apache.org/repos/asf/cordova-app-harness/blob/18f4ce7a/www/cdvah/js/PluginMetadata.js
----------------------------------------------------------------------
diff --git a/www/cdvah/js/PluginMetadata.js b/www/cdvah/js/PluginMetadata.js
index 8a2bed4..f1ee97b 100644
--- a/www/cdvah/js/PluginMetadata.js
+++ b/www/cdvah/js/PluginMetadata.js
@@ -2,7 +2,8 @@
     'use strict';
     /* global myApp */
     myApp.factory('PluginMetadata', function() {
-        var harnessPlugins = cordova.require('cordova/plugin_list').metadata;
+        var harnessPluginList = cordova.require('cordova/plugin_list');
+        var harnessPluginMetadata = harnessPluginList.metadata;
 
         // Returns -1, (a > b), 0 (a = b), or 1 (a < b).
         function semverCompare(a, b) {
@@ -20,6 +21,20 @@
         }
 
         return {
+            extractPluginMetadata: function(pluginListFileContents) {
+                if (!pluginListFileContents) {
+                    throw new Error('cordova_plugins.js file is empty. 
Something has gone wrong with "cordova prepare".');
+                }
+
+                // Extract the JSON data from inside the JS file.
+                // It's between two magic comments created by Plugman.
+                var startIndex = pluginListFileContents.indexOf('TOP OF 
METADATA') + 16;
+                var endIndex = pluginListFileContents.indexOf('// BOTTOM OF 
METADATA');
+                var target = pluginListFileContents.substring(startIndex, 
endIndex);
+                var metadata = JSON.parse(target);
+                return metadata;
+            },
+
             // Returns an object with plugin matching data.
             process: function(childPlugins) {
                 var results = {
@@ -36,24 +51,62 @@
                 }
 
                 Object.keys(childPlugins).forEach(function(plugin) {
-                    if (!harnessPlugins[plugin]) {
+                    if (!harnessPluginMetadata[plugin]) {
                         results.missing.push({ id: plugin, version: 
childPlugins[plugin] });
                     } else {
-                        switch(semverCompare(harnessPlugins[plugin], 
childPlugins[plugin])) {
+                        switch(semverCompare(harnessPluginMetadata[plugin], 
childPlugins[plugin])) {
                             case -1: // Child older.
-                                results.older.push({ id: plugin, versions: { 
harness: harnessPlugins[plugin], child: childPlugins[plugin] } });
+                                results.older.push({ id: plugin, versions: { 
harness: harnessPluginMetadata[plugin], child: childPlugins[plugin] } });
                                 break;
                             case 1: // Child newer.
-                                results.newer.push({ id: plugin, versions: { 
harness: harnessPlugins[plugin], child: childPlugins[plugin] } });
+                                results.newer.push({ id: plugin, versions: { 
harness: harnessPluginMetadata[plugin], child: childPlugins[plugin] } });
                                 break;
                             case 0: // Match!
-                                results.matched.push({ id: plugin, version: 
harnessPlugins[plugin] });
+                                results.matched.push({ id: plugin, version: 
harnessPluginMetadata[plugin] });
                                 break;
                         }
                     }
                 });
 
                 return results;
+            },
+            // This creates the contents for the app's cordova_plugins.js file.
+            // Right now, it contains the harness's plugins with all plugins 
not listed
+            // in the target app removed.
+            // TODO: is to also add in plugin .js that is exists in the app 
but *not*
+            // in the harness. This will allow for JS-only plugins to work.
+            createNewPluginListFile: function(appPluginIds) {
+                function startsWith(a, b) {
+                    return a.lastIndexOf(b, 0) === 0;
+                }
+
+                function isPluginIdEnabled(id) {
+                    for (var i = 0; i < appPluginIds.length; ++i) {
+                        if (startsWith(id, appPluginIds[i])) {
+                            return true;
+                        }
+                    }
+                    return false;
+                }
+                var newPluginList = harnessPluginList.filter(function(entry) {
+                    return isPluginIdEnabled(entry.id);
+                });
+                var newMetadata = {};
+                for (var i = 0; i < appPluginIds.length; ++i) {
+                    var pluginId = appPluginIds[i];
+                    if (pluginId in harnessPluginMetadata) {
+                        newMetadata[pluginId] = 
harnessPluginMetadata[pluginId];
+                    }
+                }
+                var ret = 'cordova.define("cordova/plugin_list", 
function(require, exports, module) {\n' +
+                    'module.exports = ' + JSON.stringify(newPluginList, null, 
4) + ';\n' +
+                    'module.exports.metadata =\n' +
+                    '// TOP OF METADATA\n' +
+                    JSON.stringify(newMetadata, null, 4) + '\n' +
+                    '// BOTTOM OF METADATA\n' +
+                    '});\n';
+
+                return ret;
             }
         };
     });

http://git-wip-us.apache.org/repos/asf/cordova-app-harness/blob/18f4ce7a/www/cdvah/js/ResourcesLoader.js
----------------------------------------------------------------------
diff --git a/www/cdvah/js/ResourcesLoader.js b/www/cdvah/js/ResourcesLoader.js
index e862aa3..6151672 100644
--- a/www/cdvah/js/ResourcesLoader.js
+++ b/www/cdvah/js/ResourcesLoader.js
@@ -144,6 +144,18 @@
                 return writeToFile(url, contents, true /* append */);
             },
 
+            moveFile: function(fromUrl, toUrl) {
+                return resolveURL(fromUrl)
+                .then(function(fromEntry) {
+                    return ensureDirectoryExists(dirName(toUrl))
+                    .then(function(destEntry) {
+                        var deferred = $q.defer();
+                        fromEntry.moveTo(destEntry, baseName(toUrl), 
deferred.reslove, deferred.reject);
+                        return deferred;
+                    });
+                });
+            },
+
             deleteDirectory: function(url) {
                 return resolveURL(url)
                 .then(function(dirEntry) {

http://git-wip-us.apache.org/repos/asf/cordova-app-harness/blob/18f4ce7a/www/cdvah/js/ServeInstaller.js
----------------------------------------------------------------------
diff --git a/www/cdvah/js/ServeInstaller.js b/www/cdvah/js/ServeInstaller.js
index 56f41e6..2f3d521 100644
--- a/www/cdvah/js/ServeInstaller.js
+++ b/www/cdvah/js/ServeInstaller.js
@@ -4,7 +4,7 @@
     var ASSET_MANIFEST_PATH = 'installmanifest.json';
 
     /* global myApp */
-    myApp.run(['$q', 'Installer', 'AppsService', 'ResourcesLoader', 
'urlCleanup', function($q, Installer, AppsService, ResourcesLoader, urlCleanup) 
{
+    myApp.run(['$q', 'Installer', 'AppsService', 'ResourcesLoader', 
'urlCleanup', 'PluginMetadata', function($q, Installer, AppsService, 
ResourcesLoader, urlCleanup, PluginMetadata) {
         var platformId = cordova.require('cordova/platform').id;
 
         function ServeInstaller(url, appId) {
@@ -71,7 +71,6 @@
             .then(deferred.resolve, deferred.reject);
             return deferred.promise;
         }
-
         // TODO: update should be more atomic. Maybe download to a new 
directory?
         ServeInstaller.prototype.doUpdateApp = function() {
             if (this._assetManifest) {
@@ -83,8 +82,9 @@
             });
         };
 
-        ServeInstaller.prototype._doUpdateAppForReal = function() {
+        ServeInstaller.prototype._bulkDownload = function(files) {
             var installPath = this.installPath;
+            var wwwPath = this._cachedProjectJson.wwwPath;
             var deferred = $q.defer();
             var self = this;
             // Write the asset manifest to disk at most every 2 seconds.
@@ -99,12 +99,43 @@
                 }
             }, 2000);
 
-            fetchMetaServeData(this.url)
+            console.log('Number of files to fetch: ' + files.length);
+            var i = 0;
+            var totalFiles = files.length + 1; // + 1 for the updateAppMeta.
+            deferred.notify((i + 1) / totalFiles);
+            function downloadNext() {
+                if (i > 0) {
+                    self._assetManifest[files[i - 1].path] = files[i - 1].etag;
+                    assetManifestDirty = 1;
+                }
+                if (!files[i]) {
+                    assetManifestDirty = 2;
+                    deferred.resolve();
+                    return;
+                }
+                deferred.notify((i + 1) / totalFiles);
+
+                var sourceUrl = self.url + wwwPath + files[i].path;
+                var destPath = installPath + '/www' + files[i].path;
+                if (files[i].path == '/cordova_plugins.js') {
+                    destPath = installPath + '/orig-cordova_plugins.js';
+                }
+                console.log(destPath);
+                i += 1;
+                return ResourcesLoader.downloadFromUrl(sourceUrl, 
destPath).then(downloadNext);
+            }
+            downloadNext();
+        };
+
+        ServeInstaller.prototype._doUpdateAppForReal = function() {
+            var installPath = this.installPath;
+            var self = this;
+
+            return fetchMetaServeData(this.url)
             .then(function(meta) {
                 self._cachedProjectJson = meta.projectJson;
                 self._cachedConfigXml = meta.configXml;
                 self.appId = self.appId || meta.appId;
-                var wwwPath = self._cachedProjectJson.wwwPath;
                 var files = self._cachedProjectJson.wwwFileList;
                 files = files.filter(function(f) {
                     // Don't download cordova.js or plugins. We want to use 
the version bundled with the harness.
@@ -113,32 +144,18 @@
                     var haveAlready = self._assetManifest[f.path] == f.etag;
                     return (!isPlugin && !haveAlready);
                 });
-                console.log('Number of files to fetch: ' + files.length);
-                var i = 0;
-                var totalFiles = files.length + 1; // + 1 for the 
updateAppMeta.
-                deferred.notify((i + 1) / totalFiles);
-                function downloadNext() {
-                    if (i > 0) {
-                        self._assetManifest[files[i - 1].path] = files[i - 
1].etag;
-                        assetManifestDirty = 1;
-                    }
-                    if (!files[i]) {
-                        assetManifestDirty = 2;
-                        deferred.resolve();
-                        return;
-                    }
-                    deferred.notify((i + 1) / totalFiles);
-
-                    var sourceUrl = self.url + wwwPath + files[i].path;
-                    var destPath = installPath + '/www' + files[i].path;
-                    console.log(destPath);
-                    i += 1;
-                    return ResourcesLoader.downloadFromUrl(sourceUrl, 
destPath).then(downloadNext);
-                }
                 return ResourcesLoader.writeFileContents(installPath + 
'/config.xml', self._cachedConfigXml)
-                .then(downloadNext);
+                .then(function() {
+                    return self._bulkDownload(files);
+                });
+            });
+        };
+
+        ServeInstaller.prototype.getPluginMetadata = function() {
+            return ResourcesLoader.readFileContents(this.installPath + 
'/orig-cordova_plugins.js')
+            .then(function(contents) {
+                return PluginMetadata.extractPluginMetadata(contents);
             });
-            return deferred.promise;
         };
 
         function createFromUrl(url) {

Reply via email to