You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by an...@apache.org on 2016/10/20 12:11:56 UTC

cordova-windows git commit: CB-11933: Add uap prefixes for capabilities at plugin install

Repository: cordova-windows
Updated Branches:
  refs/heads/master 610b0adeb -> b64ee1e5c


CB-11933: Add uap prefixes for capabilities at plugin install

This closes #203


Project: http://git-wip-us.apache.org/repos/asf/cordova-windows/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-windows/commit/b64ee1e5
Tree: http://git-wip-us.apache.org/repos/asf/cordova-windows/tree/b64ee1e5
Diff: http://git-wip-us.apache.org/repos/asf/cordova-windows/diff/b64ee1e5

Branch: refs/heads/master
Commit: b64ee1e5cb82a10e0762be51fc532ccf8da0e56a
Parents: 610b0ad
Author: Nikita Matrosov <ma...@gmail.com>
Authored: Wed Oct 19 14:23:52 2016 +0300
Committer: Vladimir Kotikov <v-...@microsoft.com>
Committed: Thu Oct 20 15:11:27 2016 +0300

----------------------------------------------------------------------
 spec/unit/ConfigChanges.spec.js                 |  81 +++++++-----
 .../windows/package.windows10.appxmanifest      |  73 +++++++++++
 template/cordova/Api.js                         |   5 +-
 template/cordova/lib/AppxManifest.js            |   7 ++
 template/cordova/lib/ConfigChanges.js           | 125 +++++++++++++++----
 5 files changed, 237 insertions(+), 54 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-windows/blob/b64ee1e5/spec/unit/ConfigChanges.spec.js
----------------------------------------------------------------------
diff --git a/spec/unit/ConfigChanges.spec.js b/spec/unit/ConfigChanges.spec.js
index 44ad4cd..df346dd 100644
--- a/spec/unit/ConfigChanges.spec.js
+++ b/spec/unit/ConfigChanges.spec.js
@@ -61,72 +61,97 @@ describe('PlatformMunger', function () {
         });
 
         it('should additionally call parent\'s method with another munge if removing changes from windows 10 appxmanifest', function () {
-            munger.apply_file_munge('package.windows10.appxmanifest', munge, /*remove=*/true);
+            munger.apply_file_munge(WINDOWS10_MANIFEST, munge, /*remove=*/true);
             expect(BaseMunger.prototype.apply_file_munge).toHaveBeenCalledWith(WINDOWS10_MANIFEST, munge, true);
-            expect(BaseMunger.prototype.apply_file_munge).toHaveBeenCalledWith(WINDOWS10_MANIFEST, jasmine.any(Object), true);
         });
 
         it('should remove uap: capabilities added by windows prepare step', function () {
             // Generate a munge that contain non-prefixed capabilities changes
-            var baseMunge = { parents: { WINDOWS10_MANIFEST: [
+            var baseMunge = { parents: { '/Package/Capabilities': [
                 // Emulate capability that was initially added with uap prefix
                 { before: undefined, count: 1, xml: '<uap:Capability Name=\"privateNetworkClientServer\">'},
                 { before: undefined, count: 1, xml: '<Capability Name=\"enterpriseAuthentication\">'}
             ]}};
 
-            var capabilitiesMunge = { parents: { WINDOWS10_MANIFEST: [
+            var capabilitiesMunge = { parents: { '/Package/Capabilities': [
                 { before: undefined, count: 1, xml: '<uap:Capability Name=\"enterpriseAuthentication\">'}
             ]}};
-
-            munger.apply_file_munge('package.windows10.appxmanifest', baseMunge, /*remove=*/true);
+            munger.apply_file_munge(WINDOWS10_MANIFEST, baseMunge, /*remove=*/true);
             expect(BaseMunger.prototype.apply_file_munge).toHaveBeenCalledWith(WINDOWS10_MANIFEST, capabilitiesMunge, true);
         });
     });
 });
 
 describe('Capabilities within package.windows.appxmanifest', function() {
-    var testDir;
+
+    var testDir, windowsPlatform, windowsManifest, windowsManifest10, dummyPluginInfo, api;
 
     beforeEach(function() {
         testDir = path.join(__dirname, 'testDir');
         shell.mkdir('-p', testDir);
         shell.cp('-rf', windowsProject + '/*', testDir);
+        windowsPlatform = path.join(testDir, 'platforms/windows');
+        windowsManifest = path.join(windowsPlatform, WINDOWS_MANIFEST);
+        windowsManifest10 = path.join(windowsPlatform, WINDOWS10_MANIFEST);
+        dummyPluginInfo = new PluginInfo(dummyPlugin);
+        api = new Api();
+        api.root = windowsPlatform;
+        api.locations.root = windowsPlatform;
+        api.locations.www = path.join(windowsPlatform, 'www');
     });
 
     afterEach(function() {
         shell.rm('-rf', testDir);
     });
 
-    it('should be removed using overriden PlatformMunger', function(done) {
-        var windowsPlatform = path.join(testDir, 'platforms/windows');
-        var windowsManifest = path.join(windowsPlatform, WINDOWS_MANIFEST);
-        var api = new Api();
-        api.root = windowsPlatform;
-        api.locations.root = windowsPlatform;
-        api.locations.www = path.join(windowsPlatform, 'www');
-        var dummyPluginInfo = new PluginInfo(dummyPlugin);
+    function getPluginCapabilities(pluginInfo) {
+        return pluginInfo.getConfigFiles()[0].xmls;
+    }
 
-        var fail = jasmine.createSpy('fail')
-        .andCallFake(function (err) {
-            console.error(err);
-        });
+    function getManifestCapabilities(manifest) {
+        var appxmanifest = AppxManifest.get(manifest, true);
+        return appxmanifest.getCapabilities();
+    }
 
-        function getPluginCapabilities() {
-            return dummyPluginInfo.getConfigFiles()[0].xmls;
-        }
+    var fail = jasmine.createSpy('fail')
+    .andCallFake(function (err) {
+        console.error(err);
+    });
+
+    it('should be removed using overriden PlatformMunger', function(done) {
+        api.addPlugin(dummyPluginInfo)
+        .then(function() {
+            //  There is the one default capability in manifest with 'internetClient' name
+            expect(getManifestCapabilities(windowsManifest).length).toBe(getPluginCapabilities(dummyPluginInfo).length + 1);
+            api.removePlugin(dummyPluginInfo);
+        })
+        .then(function() {
+            expect(getManifestCapabilities(windowsManifest).length).toBe(1);
+        })
+        .catch(fail)
+        .finally(function() {
+            expect(fail).not.toHaveBeenCalled();
+            done();
+        });
+    });
 
-        function getManifestCapabilities() {
-            var appxmanifest = AppxManifest.get(windowsManifest, true);
-            return appxmanifest.getCapabilities();
-        }
+    it('should be added with uap prefixes when install plugin', function(done) {
         api.addPlugin(dummyPluginInfo)
         .then(function() {
             //  There is the one default capability in manifest with 'internetClient' name
-            expect(getManifestCapabilities().length).toBe(getPluginCapabilities().length + 1); 
+            var manifestCapabilities = getManifestCapabilities(windowsManifest10);
+            expect(manifestCapabilities.length).toBe(getPluginCapabilities(dummyPluginInfo).length + 1);
+
+            //  Count 'uap' prefixed capabilities
+            var uapPrefixedCapsCount = manifestCapabilities.filter(function(capability) {
+                return capability.type === 'uap:Capability';
+            }).length;
+
+            expect(uapPrefixedCapsCount).toBe(2);
             api.removePlugin(dummyPluginInfo);
         })
         .then(function() {
-            expect(getManifestCapabilities().length).toBe(1);
+            expect(getManifestCapabilities(windowsManifest10).length).toBe(1);
         })
         .catch(fail)
         .finally(function() {

http://git-wip-us.apache.org/repos/asf/cordova-windows/blob/b64ee1e5/spec/unit/fixtures/testProj/platforms/windows/package.windows10.appxmanifest
----------------------------------------------------------------------
diff --git a/spec/unit/fixtures/testProj/platforms/windows/package.windows10.appxmanifest b/spec/unit/fixtures/testProj/platforms/windows/package.windows10.appxmanifest
new file mode 100644
index 0000000..59e435a
--- /dev/null
+++ b/spec/unit/fixtures/testProj/platforms/windows/package.windows10.appxmanifest
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+       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
+  xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
+  xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
+  xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
+  IgnorableNamespaces="uap mp">
+
+  <Identity
+    Name="$guid1$"
+    Version="1.0.0.0"
+    Publisher="CN=$username$" />
+
+  <mp:PhoneIdentity PhoneProductId="$guid1$" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
+
+  <Properties>
+    <DisplayName>$projectname$</DisplayName>
+    <PublisherDisplayName>$username$</PublisherDisplayName>
+    <Logo>images\StoreLogo.png</Logo>
+  </Properties>
+
+  <Dependencies>
+    <TargetDeviceFamily Name="Windows.Universal" MinVersion="0.0.0.0" MaxVersionTested="10.0.0.0" />
+  </Dependencies>
+
+  <Resources>
+    <Resource Language="x-generate" />
+  </Resources>
+
+  <Applications>
+    <Application
+      Id="App"
+      StartPage="www/index.html">
+
+      <uap:VisualElements
+        DisplayName="$projectname$"
+        Description="CordovaApp"
+        BackgroundColor="#464646"
+        Square150x150Logo="images\Square150x150Logo.png"
+        Square44x44Logo="images\Square44x44Logo.png">
+
+        <uap:SplashScreen Image="images\splashscreen.png" />
+        <uap:DefaultTile ShortName="$projectname$"
+                         Square310x310Logo="images\Square310x310Logo.png"
+                         Square71x71Logo="images\Square71x71Logo.png"
+                         Wide310x150Logo="images\Wide310x150Logo.png" />
+
+      </uap:VisualElements>
+    </Application>
+  </Applications>
+
+  <Capabilities>
+    <Capability Name="internetClient" />
+  </Capabilities>
+
+</Package>

http://git-wip-us.apache.org/repos/asf/cordova-windows/blob/b64ee1e5/template/cordova/Api.js
----------------------------------------------------------------------
diff --git a/template/cordova/Api.js b/template/cordova/Api.js
index 50a0778..7637582 100644
--- a/template/cordova/Api.js
+++ b/template/cordova/Api.js
@@ -208,7 +208,10 @@ Api.prototype.addPlugin = function (plugin, installOptions) {
         installOptions.variables.PACKAGE_NAME = jsProject.getPackageName();
     }
 
-    return PluginManager.get(this.platform, this.locations, jsProject)
+    var platformJson = PlatformJson.load(this.root, this.platform);
+    var pluginManager = PluginManager.get(this.platform, this.locations, jsProject);
+    pluginManager.munger = new PlatformMunger(this.platform, this.locations.root, platformJson, new PluginInfoProvider());
+    return pluginManager
         .addPlugin(plugin, installOptions)
         .then(function () {
             // CB-11657 Add BOM to www files here because files added by plugin

http://git-wip-us.apache.org/repos/asf/cordova-windows/blob/b64ee1e5/template/cordova/lib/AppxManifest.js
----------------------------------------------------------------------
diff --git a/template/cordova/lib/AppxManifest.js b/template/cordova/lib/AppxManifest.js
index 6973d13..75d4eee 100644
--- a/template/cordova/lib/AppxManifest.js
+++ b/template/cordova/lib/AppxManifest.js
@@ -72,6 +72,13 @@ function AppxManifest(path, prefix) {
     this.hasPhoneIdentity = this.prefix === 'uap:' || this.prefix === 'm3:';
 }
 
+//  Static read-only property to get capabilities which need to be prefixed with uap
+Object.defineProperty(AppxManifest, 'CapsNeedUapPrefix', {
+    writable: false,
+    configurable: false,
+    value: CAPS_NEEDING_UAPNS
+});
+
 /**
  * @static
  * @constructs AppxManifest|Win10AppxManifest

http://git-wip-us.apache.org/repos/asf/cordova-windows/blob/b64ee1e5/template/cordova/lib/ConfigChanges.js
----------------------------------------------------------------------
diff --git a/template/cordova/lib/ConfigChanges.js b/template/cordova/lib/ConfigChanges.js
index 7e15606..64eba4d 100644
--- a/template/cordova/lib/ConfigChanges.js
+++ b/template/cordova/lib/ConfigChanges.js
@@ -15,7 +15,12 @@
 */
 
 var util = require('util');
+var path = require('path');
 var CommonMunger = require('cordova-common').ConfigChanges.PlatformMunger;
+var CapsNeedUapPrefix = require(path.join(__dirname, 'AppxManifest')).CapsNeedUapPrefix;
+
+var CAPS_SELECTOR = '/Package/Capabilities';
+var WINDOWS10_MANIFEST = 'package.windows10.appxmanifest';
 
 function PlatformMunger(platform, project_dir, platformJson, pluginInfoProvider) {
     CommonMunger.apply(this, arguments);
@@ -34,36 +39,111 @@ util.inherits(PlatformMunger, CommonMunger);
  *   need to be removed or added to the file
  */
 PlatformMunger.prototype.apply_file_munge = function (file, munge, remove) {
-    // Call parent class' method
-    PlatformMunger.super_.prototype.apply_file_munge.call(this, file, munge, remove);
-
-    // CB-11066 If this is a windows10 manifest and we're removing the changes
-    // then we also need to check if there are <Capability> elements were previously
-    // added and schedule removal of corresponding <uap:Capability> elements
-    if (remove && file === 'package.windows10.appxmanifest') {
-        var uapCapabilitiesMunge = generateUapCapabilities(munge);
-        // We do not check whether generated munge is empty or not before calling
-        // 'apply_file_munge' since applying empty one is just a no-op
-        PlatformMunger.super_.prototype.apply_file_munge.call(this, file, uapCapabilitiesMunge, remove);
+
+    // Create a copy to avoid modification of original munge
+    var mungeCopy = cloneObject(munge);
+    var capabilities = mungeCopy.parents[CAPS_SELECTOR];
+
+    if (capabilities) {
+        // Add 'uap' prefixes for windows 10 manifest
+        if (file === WINDOWS10_MANIFEST) {
+            capabilities = generateUapCapabilities(capabilities);
+        }
+
+        // Remove duplicates and sort capabilities when installing plugin
+        if (!remove) {
+            capabilities = getUniqueCapabilities(capabilities).sort(compareCapabilities);
+        }
+
+        // Put back capabilities into munge's copy
+        mungeCopy.parents[CAPS_SELECTOR] = capabilities;
     }
+
+    PlatformMunger.super_.prototype.apply_file_munge.call(this, file, mungeCopy, remove);
 };
 
+// Recursive function to clone an object
+function cloneObject(obj) {
+    if (obj === null || typeof obj !== 'object') {
+        return obj;
+    }
+
+    var copy = obj.constructor();
+    Object.keys(obj).forEach(function(key) {
+        copy[key] = cloneObject(obj[key]);
+    });
+
+    return copy;
+}
+
+/**
+ * Retrieve capabality name from xml field
+ * @param {Object} capability with xml field like <Capability Name="CapabilityName">
+ * @return {String} name of capability
+ */
+function getCapabilityName(capability) {
+    var reg = /Name="(\w+)"/i;
+    return capability.xml.match(reg)[1];
+}
+
+/**
+ * Remove capabilities with same names
+ * @param {Object} an array of capabilities
+ * @return {Object} an unique array of capabilities
+ */
+function getUniqueCapabilities(capabilities) {
+    return capabilities.reduce(function(uniqueCaps, currCap) {
+
+        var isRepeated = uniqueCaps.some(function(cap) {
+            return getCapabilityName(cap) === getCapabilityName(currCap);
+        });
+
+        return isRepeated ? uniqueCaps : uniqueCaps.concat([currCap]);
+    }, []);
+}
+
+/**
+ * Comparator function to pass to Array.sort
+ * @param {Object} firstCap first capability
+ * @param {Object} secondCap second capability
+ * @return {Number} either -1, 0 or 1
+ */
+function compareCapabilities(firstCap, secondCap) {
+    var firstCapName = getCapabilityName(firstCap);
+    var secondCapName = getCapabilityName(secondCap);
+
+    if (firstCapName < secondCapName) {
+        return -1;
+    }
+
+    if (firstCapName > secondCapName) {
+        return 1;
+    }
+
+    return 0;
+}
+
+
 /**
  * Generates a new munge that contains <uap:Capability> elements created based on
  * corresponding <Capability> elements from base munge. If there are no such elements
  * found in base munge, the empty munge is returned (selectors might be present under
  * the 'parents' key, but they will contain no changes).
  *
- * @param {Object} munge A munge that we need to check for <Capability> elements
- * @return {Object} A munge with 'uap'-prefixed capabilities or empty one
+ * @param {Object} capabilities A list of capabilities
+ * @return {Object} A list with 'uap'-prefixed capabilities
  */
-function generateUapCapabilities(munge) {
+function generateUapCapabilities(capabilities) {
 
     function hasCapabilityChange(change) {
         return /^\s*<Capability\s/.test(change.xml);
     }
 
     function createPrefixedCapabilityChange(change) {
+        if (CapsNeedUapPrefix.indexOf(getCapabilityName(change)) < 0) {
+            return change;
+        }
+
         return {
             xml: change.xml.replace(/Capability/, 'uap:Capability'),
             count: change.count,
@@ -71,17 +151,12 @@ function generateUapCapabilities(munge) {
         };
     }
 
-    // Iterate through all selectors in munge
-    return Object.keys(munge.parents)
-    .reduce(function (result, selector) {
-        result.parents[selector] = munge.parents[selector]
-        // For every xml change check if it adds a <Capability> element ...
-        .filter(hasCapabilityChange)
-        // ... and create a duplicate with 'uap:' prefix
-        .map(createPrefixedCapabilityChange);
-
-        return result;
-    }, { parents: {} });
+    return capabilities
+     // For every xml change check if it adds a <Capability> element ...
+    .filter(hasCapabilityChange)
+    // ... and create a duplicate with 'uap:' prefix
+    .map(createPrefixedCapabilityChange);
+
 }
 
 exports.PlatformMunger = PlatformMunger;


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cordova.apache.org
For additional commands, e-mail: commits-help@cordova.apache.org