You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by je...@apache.org on 2013/10/10 19:19:00 UTC

git commit: [CB-4774] Updated prepare flow to make platform config.xml a build output - Adds a new method to manually merge two config.xml files

Updated Branches:
  refs/heads/master 9823bdddb -> 9b5f464a4


[CB-4774] Updated prepare flow to make platform config.xml a build
output
  - Adds a new method to manually merge two config.xml files


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

Branch: refs/heads/master
Commit: 9b5f464a4852c4bd17e045613be853a7c72808ff
Parents: 9823bdd
Author: Jeffrey Heifetz <jh...@blackberry.com>
Authored: Thu Sep 12 13:16:47 2013 -0400
Committer: Jeffrey Heifetz <jh...@blackberry.com>
Committed: Thu Oct 10 13:18:03 2013 -0400

----------------------------------------------------------------------
 spec/config_parser.spec.js              | 96 ++++++++++++++++++++++++++++
 spec/metadata/android_parser.spec.js    | 30 ---------
 spec/metadata/blackberry_parser.spec.js | 36 ++---------
 spec/metadata/ios_parser.spec.js        | 46 -------------
 spec/metadata/wp7_parser.spec.js        |  4 --
 spec/metadata/wp8_parser.spec.js        |  4 --
 spec/prepare.spec.js                    | 30 ++++++++-
 src/config_parser.js                    | 63 ++++++++++++++++++
 src/metadata/android_parser.js          | 39 +----------
 src/metadata/blackberry10_parser.js     | 28 ++------
 src/metadata/ios_parser.js              | 35 ----------
 src/metadata/wp7_parser.js              |  3 -
 src/metadata/wp8_parser.js              |  5 +-
 src/prepare.js                          | 48 +++++++++-----
 14 files changed, 234 insertions(+), 233 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/9b5f464a/spec/config_parser.spec.js
----------------------------------------------------------------------
diff --git a/spec/config_parser.spec.js b/spec/config_parser.spec.js
index daa0aa8..1bd449b 100644
--- a/spec/config_parser.spec.js
+++ b/spec/config_parser.spec.js
@@ -47,6 +47,102 @@ describe('config.xml parser', function () {
             cfg = new config_parser(xml);
         });
 
+        describe('merge_with', function () {
+            it("should merge attributes and text of the root element without clobbering", function () {
+                var testXML = new et.ElementTree(et.XML("<widget foo='bar' id='NOTANID'>TEXT</widget>"));
+                cfg.merge_with({doc: testXML});
+                expect(cfg.doc.getroot().attrib.foo).toEqual("bar");
+                expect(cfg.doc.getroot().attrib.id).not.toEqual("NOTANID");
+                expect(cfg.doc.getroot().text).not.toEqual("TEXT");
+            });
+
+            it("should merge attributes and text of the root element with clobbering", function () {
+                var testXML = new et.ElementTree(et.XML("<widget foo='bar' id='NOTANID'>TEXT</widget>"));
+                cfg.merge_with({doc: testXML}, "foo", true);
+                expect(cfg.doc.getroot().attrib.foo).toEqual("bar");
+                expect(cfg.doc.getroot().attrib.id).toEqual("NOTANID");
+                expect(cfg.doc.getroot().text).toEqual("TEXT");
+            });
+
+            it("should not merge platform tags with the wrong platform", function () {
+                var testXML = new et.ElementTree(et.XML("<widget><platform name='bar'><testElement testAttrib='value'>testTEXT</testElement></platform></widget>")),
+                    origCfg = et.tostring(cfg.doc.getroot());
+
+                cfg.merge_with({doc: testXML}, "foo", true);
+                expect(et.tostring(cfg.doc.getroot())).toEqual(origCfg);
+            });
+
+            it("should merge platform tags with the correct platform", function () {
+                var testXML = new et.ElementTree(et.XML("<widget><platform name='bar'><testElement testAttrib='value'>testTEXT</testElement></platform></widget>")),
+                    origCfg = et.tostring(cfg.doc.getroot()),
+                    testElement;
+
+                cfg.merge_with({doc: testXML}, "bar", true);
+                expect(et.tostring(cfg.doc.getroot())).not.toEqual(origCfg);
+                testElement = cfg.doc.getroot().find("testElement");
+                expect(testElement).toBeDefined();
+                expect(testElement.attrib.testAttrib).toEqual("value");
+                expect(testElement.text).toEqual("testTEXT");
+            });
+
+            it("should merge singelton children without clobber", function () {
+                var testXML = new et.ElementTree(et.XML("<widget><author testAttrib='value' href='http://www.nowhere.com'>SUPER_AUTHOR</author></widget>")),
+                    testElements;
+
+                cfg.merge_with({doc: testXML});
+                testElements = cfg.doc.getroot().findall("author");
+                expect(testElements).toBeDefined();
+                expect(testElements.length).toEqual(1);
+                expect(testElements[0].attrib.testAttrib).toEqual("value");
+                expect(testElements[0].attrib.href).toEqual("http://cordova.io");
+                expect(testElements[0].attrib.email).toEqual("dev@callback.apache.org");
+                expect(testElements[0].text).toContain("Apache Cordova Team");
+            });
+
+            it("should clobber singelton children with clobber", function () {
+                var testXML = new et.ElementTree(et.XML("<widget><author testAttrib='value' href='http://www.nowhere.com'>SUPER_AUTHOR</author></widget>")),
+                    testElements;
+
+                cfg.merge_with({doc: testXML}, "", true);
+                testElements = cfg.doc.getroot().findall("author");
+                expect(testElements).toBeDefined();
+                expect(testElements.length).toEqual(1);
+                expect(testElements[0].attrib.testAttrib).toEqual("value");
+                expect(testElements[0].attrib.href).toEqual("http://www.nowhere.com");
+                expect(testElements[0].attrib.email).toEqual("dev@callback.apache.org");
+                expect(testElements[0].text).toEqual("SUPER_AUTHOR");
+            });
+
+            it("should append non singelton children", function () {
+                var testXML = new et.ElementTree(et.XML("<widget><preference num='1'/> <preference num='2'/></widget>")),
+                    testElements;
+
+                cfg.merge_with({doc: testXML}, "", true);
+                testElements = cfg.doc.getroot().findall("preference");
+                expect(testElements.length).toEqual(4);
+            });
+
+            it("should handle namespaced elements", function () {
+                var testXML = new et.ElementTree(et.XML("<widget><foo:bar testAttrib='value'>testText</foo:bar></widget>")),
+                    testElement;
+
+                cfg.merge_with({doc: testXML}, "foo", true);
+                testElement = cfg.doc.getroot().find("foo:bar");
+                expect(testElement).toBeDefined();
+                expect(testElement.attrib.testAttrib).toEqual("value");
+                expect(testElement.text).toEqual("testText");
+            });
+
+            it("should not append duplicate non singelton children", function () {
+                var testXML = new et.ElementTree(et.XML("<widget><preference name='fullscreen' value='true'/></widget>")),
+                    testElements;
+
+                cfg.merge_with({doc: testXML}, "", true);
+                testElements = cfg.doc.getroot().findall("preference");
+                expect(testElements.length).toEqual(2);
+            });
+        });
+
         describe('package name / id', function() {
             it('should get the (default) packagename', function() {
                 expect(cfg.packageName()).toEqual('io.cordova.hellocordova');

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/9b5f464a/spec/metadata/android_parser.spec.js
----------------------------------------------------------------------
diff --git a/spec/metadata/android_parser.spec.js b/spec/metadata/android_parser.spec.js
index 71479ae..4cdcbd1 100644
--- a/spec/metadata/android_parser.spec.js
+++ b/spec/metadata/android_parser.spec.js
@@ -183,36 +183,6 @@ describe('android project parser', function() {
                 p.update_from_config(cfg);
                 expect(root_obj.attrib['android:versionName']).toEqual('one point oh');
             });
-            it('should wipe out the android whitelist every time', function() {
-                p.update_from_config(cfg);
-                expect(cfg_access_rm).toHaveBeenCalled();
-            });
-            it('should update the whitelist', function() {
-                cfg.access.get = function() { return ['one'] };
-                p.update_from_config(cfg);
-                expect(cfg_access_add).toHaveBeenCalledWith('one');
-            });
-            it('should update preferences', function() {
-                var sample_pref = {name:'pref',value:'yes'};
-                cfg.preference.get = function() { return [sample_pref] };
-                p.update_from_config(cfg);
-                expect(cfg_pref_add).toHaveBeenCalledWith(sample_pref);
-            });
-            it('should wipe out the android preferences every time', function() {
-                p.update_from_config(cfg);
-                expect(cfg_pref_rm).toHaveBeenCalled();
-            });
-            it('should write out default preferences every time', function() {
-                var sample_pref = {name:'preftwo',value:'false'};
-                cfg.preference.get = function() { return [sample_pref] };
-                p.update_from_config(cfg);
-                expect(cfg_pref_add).toHaveBeenCalledWith({name:"useBrowserHistory",value:"true"});
-                expect(cfg_pref_add).toHaveBeenCalledWith({name:"exit-on-suspend",value:"false"});
-            });
-            it('should update the content tag', function() {
-                p.update_from_config(cfg);
-                expect(cfg_content).toHaveBeenCalledWith('index.html');
-            });
         });
         describe('www_dir method', function() {
             it('should return assets/www', function() {

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/9b5f464a/spec/metadata/blackberry_parser.spec.js
----------------------------------------------------------------------
diff --git a/spec/metadata/blackberry_parser.spec.js b/spec/metadata/blackberry_parser.spec.js
index 97d8c7d..f116967 100644
--- a/spec/metadata/blackberry_parser.spec.js
+++ b/spec/metadata/blackberry_parser.spec.js
@@ -154,33 +154,6 @@ describe('blackberry10 project parser', function() {
                 cfg.access.remove = function() { };
                 cfg.preference.get = function() { return []; };
             });
-
-            it('should write out the app name to config.xml', function() {
-                p.update_from_config(cfg);
-                expect(xml_name).toHaveBeenCalledWith('testname');
-            });
-            it('should write out the app id to bb\'s config.xml', function() {
-                p.update_from_config(cfg);
-                expect(xml_pkg).toHaveBeenCalledWith('testpkg');
-            });
-            it('should write out the app version to bb\'s config.xml', function() {
-                p.update_from_config(cfg);
-                expect(xml_version).toHaveBeenCalledWith('one point oh');
-            });
-            it('should wipe out the bb config.xml whitelist every time', function() {
-                p.update_from_config(cfg);
-                expect(xml_access_rm).toHaveBeenCalled();
-            });
-            it('should update the whitelist', function() {
-                cfg.access.getAttributes = function() { return [{origin: 'one'},{uri: "two", subdomains: "false"}]; };
-                p.update_from_config(cfg);
-                expect(xml_access_add).toHaveBeenCalledWith('one', undefined);
-                expect(xml_access_add).toHaveBeenCalledWith('two', 'false');
-            });
-            it('should update the start page (content tag)', function() {
-                p.update_from_config(cfg);
-                expect(xml_content).toHaveBeenCalledWith('index.html');
-            });
         });
         describe('www_dir method', function() {
             it('should return /www', function() {
@@ -198,14 +171,19 @@ describe('blackberry10 project parser', function() {
             });
         });
         describe('update_www method', function() {
-            beforeEach(function() {
-                p.xml.update = jasmine.createSpy('xml update');
+            var backup_cfg_parser;
+            beforeEach(function () {
+                backup_cfg_parser = {
+                    update: jasmine.createSpy("backup_cfg_parser update")
+                };
+                config_p.andReturn(backup_cfg_parser);
             });
 
             it('should rm project-level www and cp in platform agnostic www', function() {
                 p.update_www();
                 expect(rm).toHaveBeenCalled();
                 expect(cp).toHaveBeenCalled();
+                expect(backup_cfg_parser.update).toHaveBeenCalled();
             });
             it('should copy in a fresh cordova.js from stock cordova lib if no custom lib is specified', function() {
                 p.update_www();

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/9b5f464a/spec/metadata/ios_parser.spec.js
----------------------------------------------------------------------
diff --git a/spec/metadata/ios_parser.spec.js b/spec/metadata/ios_parser.spec.js
index f3e57be..39c479c 100644
--- a/spec/metadata/ios_parser.spec.js
+++ b/spec/metadata/ios_parser.spec.js
@@ -196,52 +196,6 @@ describe('ios project parser', function () {
                     expect(plist_build.mostRecentCall.args[0].CFBundleVersion).toEqual('one point oh');
                 });
             });
-            it('should wipe out the ios whitelist every time', function(done) {
-                wrapper(p.update_from_config(cfg), done, function() {
-                    expect(cfg_access_rm).toHaveBeenCalled();
-                });
-            });
-            it('should update the whitelist', function(done) {
-                cfg.access.get = function() { return ['one'] };
-                wrapper(p.update_from_config(cfg), done, function() {
-                    expect(cfg_access_add).toHaveBeenCalledWith('one');
-                });
-            });
-            it('should update preferences', function(done) {
-                var sample_pref = {name:'pref',value:'yes'};
-                cfg.preference.get = function() { return [sample_pref] };
-                wrapper(p.update_from_config(cfg), done, function() {
-                    expect(cfg_pref_add).toHaveBeenCalledWith(sample_pref);
-                });
-            });
-            it('should update the content tag / start page', function(done) {
-                wrapper(p.update_from_config(cfg), done, function() {
-                    expect(cfg_content).toHaveBeenCalledWith('index.html');
-                });
-            });
-            it('should wipe out the ios preferences every time', function(done) {
-                wrapper(p.update_from_config(cfg), done, function() {
-                    expect(cfg_pref_rm).toHaveBeenCalled();
-                });
-            });
-            it('should write out default preferences every time', function(done) {
-                var sample_pref = {name:'preftwo',value:'false'};
-                cfg.preference.get = function() { return [sample_pref] };
-                wrapper(p.update_from_config(cfg), done, function() {
-                    expect(cfg_pref_add).toHaveBeenCalledWith({name:"KeyboardDisplayRequiresUserAction",value:"true"});
-                    expect(cfg_pref_add).toHaveBeenCalledWith({name:"SuppressesIncrementalRendering",value:"false"});
-                    expect(cfg_pref_add).toHaveBeenCalledWith({name:"UIWebViewBounce",value:"true"});
-                    expect(cfg_pref_add).toHaveBeenCalledWith({name:"TopActivityIndicator",value:"gray"});
-                    expect(cfg_pref_add).toHaveBeenCalledWith({name:"EnableLocation",value:"false"});
-                    expect(cfg_pref_add).toHaveBeenCalledWith({name:"EnableViewportScale",value:"false"});
-                    expect(cfg_pref_add).toHaveBeenCalledWith({name:"AutoHideSplashScreen",value:"true"});
-                    expect(cfg_pref_add).toHaveBeenCalledWith({name:"ShowSplashScreenSpinner",value:"true"});
-                    expect(cfg_pref_add).toHaveBeenCalledWith({name:"MediaPlaybackRequiresUserAction",value:"false"});
-                    expect(cfg_pref_add).toHaveBeenCalledWith({name:"AllowInlineMediaPlayback",value:"false"});
-                    expect(cfg_pref_add).toHaveBeenCalledWith({name:"OpenAllWhitelistURLsInWebView",value:"false"});
-                    expect(cfg_pref_add).toHaveBeenCalledWith({name:"BackupWebStorage",value:"cloud"});
-                });
-            });
         });
         describe('www_dir method', function() {
             it('should return /www', function() {

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/9b5f464a/spec/metadata/wp7_parser.spec.js
----------------------------------------------------------------------
diff --git a/spec/metadata/wp7_parser.spec.js b/spec/metadata/wp7_parser.spec.js
index d7ac782..5e903ab 100644
--- a/spec/metadata/wp7_parser.spec.js
+++ b/spec/metadata/wp7_parser.spec.js
@@ -169,10 +169,6 @@ describe('wp7 project parser', function() {
                 p.update_from_config(cfg);
                 expect(find_obj.attrib.Version).toEqual('one point oh');
             });
-            it('should update the content element (start page)', function() {
-                p.update_from_config(cfg);
-                expect(cfg_content).toHaveBeenCalledWith('index.html');
-            });
         });
         describe('www_dir method', function() {
             it('should return www', function() {

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/9b5f464a/spec/metadata/wp8_parser.spec.js
----------------------------------------------------------------------
diff --git a/spec/metadata/wp8_parser.spec.js b/spec/metadata/wp8_parser.spec.js
index fc90d8a..ab930c4 100644
--- a/spec/metadata/wp8_parser.spec.js
+++ b/spec/metadata/wp8_parser.spec.js
@@ -169,10 +169,6 @@ describe('wp8 project parser', function() {
                 p.update_from_config(cfg);
                 expect(find_obj.attrib.Version).toEqual('one point oh');
             });
-            it('should update the content element (start page)', function() {
-                p.update_from_config(cfg);
-                expect(cfg_content).toHaveBeenCalledWith('index.html');
-            });
         });
         describe('www_dir method', function() {
             it('should return www', function() {

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/9b5f464a/spec/prepare.spec.js
----------------------------------------------------------------------
diff --git a/spec/prepare.spec.js b/spec/prepare.spec.js
index c392e75..829283d 100644
--- a/spec/prepare.spec.js
+++ b/spec/prepare.spec.js
@@ -34,17 +34,30 @@ var supported_platforms = Object.keys(platforms).filter(function(p) { return p !
 var supported_platforms_paths = supported_platforms.map(function(p) { return path.join(project_dir, 'platforms', p, 'www'); });
 
 describe('prepare command', function() {
-    var is_cordova, list_platforms, fire, config_parser, parsers = {}, plugman_prepare, find_plugins, plugman_get_json, load;
+    var is_cordova,
+        list_platforms,
+        fire,
+        config_parser,
+        mock_config_parser,
+        parsers = {},
+        plugman_prepare,
+        find_plugins,
+        plugman_get_json,
+        load;
     beforeEach(function() {
         is_cordova = spyOn(util, 'isCordova').andReturn(project_dir);
         list_platforms = spyOn(util, 'listPlatforms').andReturn(supported_platforms);
         fire = spyOn(hooker.prototype, 'fire').andReturn(Q());
-        config_parser = spyOn(util, 'config_parser');
+        mock_config_parser = {
+            merge_with: jasmine.createSpy("config_parser merge_with")
+        };
+        config_parser = spyOn(util, 'config_parser').andReturn(mock_config_parser);
         supported_platforms.forEach(function(p) {
             parsers[p] = jasmine.createSpy(p + ' update_project').andReturn(Q());
             spyOn(platforms[p], 'parser').andReturn({
                 update_project:parsers[p],
-                www_dir:function() { return path.join(project_dir, 'platforms', p, 'www'); }
+                www_dir:function() { return path.join(project_dir, 'platforms', p, 'www'); },
+                config_xml: function () { return path.join(project_dir, "platforms", p, "www", "config.xml");}
             });
         });
         plugman_prepare = spyOn(plugman, 'prepare').andReturn(Q());
@@ -84,6 +97,7 @@ describe('prepare command', function() {
             var before_prep;
             config_parser.andCallFake(function() {
                 expect(before_prep).toBe(true);
+                return mock_config_parser;
             });
             fire.andCallFake(function(e, opts) {
                 if (e == 'before_prepare') {
@@ -149,6 +163,16 @@ describe('prepare command', function() {
                 }).fin(done);
             });
         });
+        it('should merge the platform level config.xml with the top level config.xml', function (done) {
+            cordova.raw.prepare().then(function() {
+                supported_platforms.forEach(function(p) {
+                    expect(util.config_parser).toHaveBeenCalledWith(platforms[p].parser().config_xml());
+                    expect(mock_config_parser.merge_with).toHaveBeenCalledWith(mock_config_parser, p, true);
+                });
+            }, function(err) {
+                expect(err).toBeUndefined();
+            }).fin(done);
+        });
     });
 
 

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/9b5f464a/src/config_parser.js
----------------------------------------------------------------------
diff --git a/src/config_parser.js b/src/config_parser.js
index b5cd95f..11667d3 100644
--- a/src/config_parser.js
+++ b/src/config_parser.js
@@ -64,6 +64,69 @@ config_parser.prototype = {
     },
     update:function() {
         fs.writeFileSync(this.path, this.doc.write({indent: 4}), 'utf-8');
+    },
+    merge_with: function (cfg, platform, clobber) {
+        var BLACKLIST = ["platform"],
+            SINGLETONS = ["content", "author"];
+        mergeXml(cfg.doc.getroot(), this.doc.getroot(), platform, clobber);
+        this.update();
+
+        function mergeXml(src, dest, platform, clobber) {
+            if (BLACKLIST.indexOf(src.tag) === -1) {
+                //Handle attributes
+                Object.getOwnPropertyNames(src.attrib).forEach(function (attribute) {
+                    if (clobber || !dest.attrib[attribute]) {
+                        dest.attrib[attribute] = src.attrib[attribute];
+                    }
+                });
+                //Handle text
+                if (src.text && (clobber || !dest.text)) {
+                    dest.text = src.text;
+                }
+                //Handle platform
+                if (platform) {
+                    src.findall('platform[@name="' + platform + '"]').forEach(function (platformElement) {
+                        platformElement.getchildren().forEach(mergeChild);
+                    });
+                }
+
+                //Handle children
+                src.getchildren().forEach(mergeChild);
+
+                function mergeChild (srcChild) {
+                    var srcTag = srcChild.tag,
+                        destChild = new et.Element(srcTag),
+                        foundChild,
+                        query = srcTag + "",
+                        shouldMerge = true;
+
+                    if (BLACKLIST.indexOf(srcTag) === -1) {
+                        if (SINGLETONS.indexOf(srcTag) !== -1) {
+                            foundChild = dest.find(query);
+                            if (foundChild) {
+                                destChild = foundChild;
+                                dest.remove(0, destChild);
+                            }
+                        } else {
+                            //Check for an exact match and if you find one don't add
+                            Object.getOwnPropertyNames(srcChild.attrib).forEach(function (attribute) {
+                                query += "[@" + attribute + '="' + srcChild.attrib[attribute] + '"]';
+                            });
+                            foundChild = dest.find(query);
+                            if (foundChild) {
+                                //Don't add duplicates
+                                shouldMerge = false;
+                            }
+                        }
+
+                        if (shouldMerge) {
+                            mergeXml(srcChild, destChild, platform, clobber);
+                            dest.append(destChild);
+                        }
+                    }
+                }
+            }
+        }
     }
 };
 

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/9b5f464a/src/metadata/android_parser.js
----------------------------------------------------------------------
diff --git a/src/metadata/android_parser.js b/src/metadata/android_parser.js
index ace8275..2df37e6 100644
--- a/src/metadata/android_parser.js
+++ b/src/metadata/android_parser.js
@@ -106,7 +106,7 @@ module.exports.prototype = {
 
         // Write out AndroidManifest.xml
         fs.writeFileSync(this.manifest, manifest.write({indent: 4}), 'utf-8');
-        
+
         var orig_pkgDir = path.join(this.path, 'src', path.join.apply(null, orig_pkg.split('.')));
         var orig_java_class = fs.readdirSync(orig_pkgDir).filter(function(f) {return f.indexOf('.svn') == -1;})[0];
         var pkgDir = path.join(this.path, 'src', path.join.apply(null, pkg.split('.')));
@@ -117,43 +117,6 @@ module.exports.prototype = {
         javs_contents = javs_contents.replace(/package [\w\.]*;/, 'package ' + pkg + ';');
         events.emit('verbose', 'Wrote out Android package name to "' + pkg + '"');
         fs.writeFileSync(new_javs, javs_contents, 'utf-8');
-
-        // Update whitelist by changing res/xml/config.xml
-        var android_cfg_xml = new util.config_parser(this.android_config);
-        // clean out all existing access elements first
-        android_cfg_xml.access.remove();
-        // add only the ones specified in the app/config.xml file
-        config.access.get().forEach(function(uri) {
-            android_cfg_xml.access.add(uri);
-        });
-
-        // Update content (start page)
-        android_cfg_xml.content(config.content());
-        
-        // Update preferences
-        android_cfg_xml.preference.remove();
-        var prefs = config.preference.get();
-        // write out defaults, unless user has specifically overrode it
-        for (var p in default_prefs) if (default_prefs.hasOwnProperty(p)) {
-            var override = prefs.filter(function(pref) { return pref.name == p; });
-            var value = default_prefs[p];
-            if (override.length) {
-                // override exists
-                value = override[0].value;
-                // remove from prefs list so we dont write it out again below
-                prefs = prefs.filter(function(pref) { return pref.name != p });
-            }
-            android_cfg_xml.preference.add({
-                name:p,
-                value:value
-            });
-        }
-        prefs.forEach(function(pref) {
-            android_cfg_xml.preference.add({
-                name:pref.name,
-                value:pref.value
-            });
-        });
     },
 
     // Returns the platform-specific www directory.

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/9b5f464a/src/metadata/blackberry10_parser.js
----------------------------------------------------------------------
diff --git a/src/metadata/blackberry10_parser.js b/src/metadata/blackberry10_parser.js
index 3093233..5ad4f0b 100644
--- a/src/metadata/blackberry10_parser.js
+++ b/src/metadata/blackberry10_parser.js
@@ -51,26 +51,8 @@ module.exports.check_requirements = function(project_root) {
 
 module.exports.prototype = {
     update_from_config:function(config) {
-        var self = this;
-
         if (config instanceof config_parser) {
         } else throw new Error('update_from_config requires a config_parser object');
-
-        this.xml.name(config.name());
-        events.emit('verbose', 'Wrote out BlackBerry application name to "' + config.name() + '"');
-        this.xml.packageName(config.packageName());
-        events.emit('verbose', 'Wrote out BlackBerry package name to "' + config.packageName() + '"');
-        this.xml.version(config.version());
-        events.emit('verbose', 'Wrote out BlackBerry version to "' + config.version() + '"');
-        this.xml.access.remove();
-        config.access.getAttributes().forEach(function(attribs) {
-            self.xml.access.add(attribs.uri || attribs.origin, attribs.subdomains);
-        });
-        this.xml.preference.remove();
-        config.preference.get().forEach(function (pref) {
-            self.xml.preference.add(pref);
-        });
-        this.xml.content(config.content());
     },
 
     // Returns a promise.
@@ -103,16 +85,18 @@ module.exports.prototype = {
     },
 
     update_www:function() {
-        var projectRoot = util.isCordova(this.path);
-        var www = util.projectWww(projectRoot);
-        var platformWww = this.www_dir();
+        var projectRoot = util.isCordova(this.path),
+            www = util.projectWww(projectRoot),
+            platformWww = this.www_dir(),
+            platform_cfg_backup = new util.config_parser(this.config_path);
+
 
         // remove the stock www folder
         shell.rm('-rf', this.www_dir());
         // copy over project www assets
         shell.cp('-rf', www, this.path);
         //Re-Write config.xml
-        this.xml.update();
+        platform_cfg_backup.update();
 
         var custom_path = config.has_custom_path(projectRoot, 'blackberry10');
         var lib_path = path.join(util.libDirectory, 'blackberry10', 'cordova', require('../../platforms').blackberry10.version);

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/9b5f464a/src/metadata/ios_parser.js
----------------------------------------------------------------------
diff --git a/src/metadata/ios_parser.js b/src/metadata/ios_parser.js
index b61307c..15854e8 100644
--- a/src/metadata/ios_parser.js
+++ b/src/metadata/ios_parser.js
@@ -110,41 +110,6 @@ module.exports.prototype = {
         events.emit('verbose', 'Wrote out iOS Bundle Identifier to "' + pkg + '"');
         events.emit('verbose', 'Wrote out iOS Bundle Version to "' + version + '"');
 
-        // Update whitelist
-        var self = this;
-        this.config.access.remove();
-        config.access.get().forEach(function(uri) {
-            self.config.access.add(uri);
-        });
-
-        // Update content (start page)
-        this.config.content(config.content());
-        
-        // Update preferences
-        this.config.preference.remove();
-        var prefs = config.preference.get();
-        // write out defaults, unless user has specifically overrode it
-        for (var p in default_prefs) if (default_prefs.hasOwnProperty(p)) {
-            var override = prefs.filter(function(pref) { return pref.name == p; });
-            var value = default_prefs[p];
-            if (override.length) {
-                // override exists
-                value = override[0].value;
-                // remove from prefs list so we dont write it out again below
-                prefs = prefs.filter(function(pref) { return pref.name != p });
-            }
-            this.config.preference.add({
-                name:p,
-                value:value
-            });
-        }
-        prefs.forEach(function(pref) {
-            self.config.preference.add({
-                name:pref.name,
-                value:pref.value
-            });
-        });
-        
         if (name != this.originalName) {
             // Update product name inside pbxproj file
             var proj = new xcode.project(this.pbxproj);

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/9b5f464a/src/metadata/wp7_parser.js
----------------------------------------------------------------------
diff --git a/src/metadata/wp7_parser.js b/src/metadata/wp7_parser.js
index 91697e9..5bda771 100644
--- a/src/metadata/wp7_parser.js
+++ b/src/metadata/wp7_parser.js
@@ -136,9 +136,6 @@ module.exports.prototype = {
             fs.writeFileSync(path.join(this.wp7_proj_dir, 'App.xaml.cs'), appCS.replace(namespaceRegEx, 'namespace ' + pkg), 'utf-8');
          }
 
-         // Update content (start page) element
-         this.config.content(config.content());
-
          //Write out manifest
          fs.writeFileSync(this.manifest_path, manifest.write({indent: 4}), 'utf-8');
     },

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/9b5f464a/src/metadata/wp8_parser.js
----------------------------------------------------------------------
diff --git a/src/metadata/wp8_parser.js b/src/metadata/wp8_parser.js
index 8dc121b..4ad2355 100644
--- a/src/metadata/wp8_parser.js
+++ b/src/metadata/wp8_parser.js
@@ -88,7 +88,7 @@ module.exports.prototype = {
             manifest.find('.//PrimaryToken').attrib.TokenID = name;
             //update name of sln and csproj.
             name = name.replace(/(\.\s|\s\.|\s+|\.+)/g, '_'); //make it a ligitamate name
-            prev_name = prev_name.replace(/(\.\s|\s\.|\s+|\.+)/g, '_'); 
+            prev_name = prev_name.replace(/(\.\s|\s\.|\s+|\.+)/g, '_');
             // TODO: might return .sln.user? (generated file)
             var sln_name = fs.readdirSync(this.wp8_proj_dir).filter(function(e) { return e.match(/\.sln$/i); })[0];
             var sln_path = path.join(this.wp8_proj_dir, sln_name);
@@ -136,9 +136,6 @@ module.exports.prototype = {
             fs.writeFileSync(path.join(this.wp8_proj_dir, 'App.xaml.cs'), appCS.replace(namespaceRegEx, 'namespace ' + pkg), 'utf-8');
          }
 
-         // Update content (start page) element
-         this.config.content(config.content());
-
          //Write out manifest
          fs.writeFileSync(this.manifest_path, manifest.write({indent: 4}), 'utf-8');
     },

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/9b5f464a/src/prepare.js
----------------------------------------------------------------------
diff --git a/src/prepare.js b/src/prepare.js
index b6928b9..62dbf65 100644
--- a/src/prepare.js
+++ b/src/prepare.js
@@ -68,26 +68,44 @@ module.exports = function prepare(options) {
             var platformPath = path.join(projectRoot, 'platforms', platform);
             return lazy_load.based_on_config(projectRoot, platform)
             .then(function() {
-                var parser = new platforms[platform].parser(platformPath);
-                return parser.update_project(cfg);
-            }).then(function() {
+                var parser = new platforms[platform].parser(platformPath),
+                    defaults_xml_path = path.join(platformPath, "cordova", "defaults.xml");
+
+                //If defaults.xml is present, overwrite platform config.xml with it
+                //Otherwise save whatever is there as defaults so it can be restored
+                if (fs.existsSync(defaults_xml_path)) {
+                    shell.cp("-f", defaults_xml_path, parser.config_xml());
+                    events.emit('log', 'Generating config.xml from defaults for platform "' + platform + '"');
+                } else {
+                    shell.cp("-f", parser.config_xml(), defaults_xml_path);
+                }
+
                 // Call plugman --prepare for this platform. sets up js-modules appropriately.
                 var plugins_dir = path.join(projectRoot, 'plugins');
                 events.emit('verbose', 'Calling plugman.prepare for platform "' + platform + '"');
                 plugman.prepare(platformPath, platform, plugins_dir);
+
                 // Make sure that config changes for each existing plugin is in place
-                var plugins = cordova_util.findPlugins(plugins_dir);
-                var platform_json = plugman.config_changes.get_platform_json(plugins_dir, platform);
-                plugins && plugins.forEach(function(plugin_id) {
-                    if (platform_json.installed_plugins[plugin_id]) {
-                        events.emit('verbose', 'Ensuring plugin "' + plugin_id + '" is installed correctly...');
-                        plugman.config_changes.add_plugin_changes(platform, platformPath, plugins_dir, plugin_id, /* variables for plugin */ platform_json.installed_plugins[plugin_id], /* top level plugin? */ true, /* should increment config munge? cordova-cli never should, only plugman */ false);
-                    } else if (platform_json.dependent_plugins[plugin_id]) {
-                        events.emit('verbose', 'Ensuring plugin "' + plugin_id + '" is installed correctly...');
-                        plugman.config_changes.add_plugin_changes(platform, platformPath, plugins_dir, plugin_id, /* variables for plugin */ platform_json.dependent_plugins[plugin_id], /* top level plugin? */ false, /* should increment config munge? cordova-cli never should, only plugman */ false);
-                    }
-                    events.emit('verbose', 'Plugin "' + plugin_id + '" is good to go.');
-                });
+                var plugins = cordova_util.findPlugins(plugins_dir),
+                    platform_json = plugman.config_changes.get_platform_json(plugins_dir, platform);
+                if (plugins && Array.isArray(plugins)) {
+                    plugins.forEach(function(plugin_id) {
+                        if (platform_json.installed_plugins[plugin_id]) {
+                            events.emit('verbose', 'Ensuring plugin "' + plugin_id + '" is installed correctly...');
+                            plugman.config_changes.add_plugin_changes(platform, platformPath, plugins_dir, plugin_id, /* variables for plugin */ platform_json.installed_plugins[plugin_id], /* top level plugin? */ true, /* should increment config munge? cordova-cli never should, only plugman */ false);
+                        } else if (platform_json.dependent_plugins[plugin_id]) {
+                            events.emit('verbose', 'Ensuring plugin "' + plugin_id + '" is installed correctly...');
+                            plugman.config_changes.add_plugin_changes(platform, platformPath, plugins_dir, plugin_id, /* variables for plugin */ platform_json.dependent_plugins[plugin_id], /* top level plugin? */ false, /* should increment config munge? cordova-cli never should, only plugman */ false);
+                        }
+                        events.emit('verbose', 'Plugin "' + plugin_id + '" is good to go.');
+                    });
+                }
+
+                //Update platform config.xml based on top level config.xml
+                var platform_cfg = new cordova_util.config_parser(parser.config_xml());
+                platform_cfg.merge_with(cfg, platform, true);
+
+                return parser.update_project(cfg);
             });
         })).then(function() {
             return hooks.fire('after_prepare', options);