diff --git a/mozilla/toolkit/components/places/src/utils.js b/mozilla/toolkit/components/places/src/utils.js index 980fb347dcb..7a3b28ca61e 100644 --- a/mozilla/toolkit/components/places/src/utils.js +++ b/mozilla/toolkit/components/places/src/utils.js @@ -528,12 +528,8 @@ var PlacesUtils = { case this.TYPE_X_MOZ_PLACE: case this.TYPE_X_MOZ_PLACE_SEPARATOR: case this.TYPE_X_MOZ_PLACE_CONTAINER: - try { - var JSON = Cc["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON); - nodes = JSON.decode("[" + blob + "]"); - } catch(ex) { - LOG("unwrapNodes(): JSON.decode(): " + ex); - } + var JSON = Cc["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON); + nodes = JSON.decode("[" + blob + "]"); break; case this.TYPE_X_MOZ_URL: var parts = blob.split("\n"); @@ -958,22 +954,38 @@ var PlacesUtils = { * restoring from the file. */ restoreBookmarksFromFile: function PU_restoreBookmarksFromFile(aFile) { + var errorStr = null; + var ioSvc = Cc["@mozilla.org/network/io-service;1"]. getService(Ci.nsIIOService); var fileURL = ioSvc.newFileURI(aFile).QueryInterface(Ci.nsIURL); var fileExtension = fileURL.fileExtension.toLowerCase(); - if (fileExtension == "json") - this.restoreBookmarksFromJSONFile(aFile); + + if (fileExtension == "json") { + try { + this.restoreBookmarksFromJSONFile(aFile); + } catch(ex) { + errorStr = this.getString("restoreParseError"); + } + } else { + errorStr = this.getString("restoreFormatError"); + } + + if (errorStr) { const BRANDING_BUNDLE_URI = "chrome://branding/locale/brand.properties"; var brandShortName = Cc["@mozilla.org/intl/stringbundle;1"]. getService(Ci.nsIStringBundleService). createBundle(BRANDING_BUNDLE_URI). GetStringFromName("brandShortName"); - var errorStr = this.getString("restoreFormatError"); + + var wm = Cc["@mozilla.org/appshell/window-mediator;1"]. + getService(Ci.nsIWindowMediator); + var win = wm.getMostRecentWindow(null); + Cc["@mozilla.org/embedcomp/prompt-service;1"]. getService(Ci.nsIPromptService). - alert(window, brandShortName, errorStr); + alert(win, brandShortName, errorStr); } }, @@ -1017,13 +1029,7 @@ var PlacesUtils = { restoreBookmarksFromJSONString: function PU_restoreBookmarksFromJSONString(aString, aReplace) { // convert string to JSON - var nodes = null; - try { - nodes = this.unwrapNodes(aString, this.TYPE_X_MOZ_PLACE_CONTAINER); - } catch (ex) { - LOG("restoreBookmarksFromJSONString(): " + ex); - return; - } + var nodes = this.unwrapNodes(aString, this.TYPE_X_MOZ_PLACE_CONTAINER); if (nodes.length == 0 || !nodes[0].children || nodes[0].children.length == 0) @@ -1276,14 +1282,15 @@ var PlacesUtils = { } function writeComplexNode(aStream, aNode, aSourceNode) { + var escJSONStringRegExp = /(["\\])/g; // write prefix var properties = []; for (let [name, value] in Iterator(aNode)) { if (name == "annos") value = self.toJSONString(value); else if (typeof value == "string") - value = "\"" + value + "\""; - properties.push("\"" + name + "\":" + value); + value = "\"" + value.replace(escJSONStringRegExp, '\\$1') + "\""; + properties.push("\"" + name.replace(escJSONStringRegExp, '\\$1') + "\":" + value); } var jStr = "{" + properties.join(",") + ",\"children\":["; aStream.write(jStr, jStr.length); diff --git a/mozilla/toolkit/components/places/tests/bookmarks/test_424958-json-quoted-folders.js b/mozilla/toolkit/components/places/tests/bookmarks/test_424958-json-quoted-folders.js new file mode 100644 index 00000000000..dc5c5c8ef3d --- /dev/null +++ b/mozilla/toolkit/components/places/tests/bookmarks/test_424958-json-quoted-folders.js @@ -0,0 +1,131 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et: */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Bug 384370 code. + * + * The Initial Developer of the Original Code is Mozilla Corp. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Dietrich Ayala + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +Components.utils.import("resource://gre/modules/utils.js"); +var tests = []; + +/* + +Backup/restore tests example: + +var myTest = { + populate: function () { ... add bookmarks ... }, + validate: function () { ... query for your bookmarks ... } +} + +this.push(myTest); + +*/ + +var quotesTest = { + _folderTitle: '"quoted folder"', + _folderId: null, + + populate: function () { + this._folderId = + PlacesUtils.bookmarks.createFolder(PlacesUtils.toolbarFolderId, + this._folderTitle, + PlacesUtils.bookmarks.DEFAULT_INDEX); + }, + + clean: function () { + PlacesUtils.bookmarks.removeFolder(this._folderId); + }, + + validate: function () { + var query = PlacesUtils.history.getNewQuery(); + query.setFolders([PlacesUtils.bookmarks.toolbarFolder], 1); + var result = PlacesUtils.history.executeQuery(query, PlacesUtils.history.getNewQueryOptions()); + + var toolbar = result.root; + toolbar.containerOpen = true; + + // test for our quoted folder + do_check_true(toolbar.childCount, 1); + var folderNode = toolbar.getChild(0); + do_check_eq(folderNode.type, folderNode.RESULT_TYPE_FOLDER); + do_check_eq(folderNode.title, this._folderTitle); + + // clean up + toolbar.containerOpen = false; + } +} +tests.push(quotesTest); + +function run_test() { + do_check_eq(typeof PlacesUtils, "object"); + + // make json file + var jsonFile = dirSvc.get("ProfD", Ci.nsILocalFile); + jsonFile.append("bookmarks.json"); + if (jsonFile.exists()) + jsonFile.remove(false); + jsonFile.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, 0600); + if (!jsonFile.exists()) + do_throw("couldn't create file: bookmarks.exported.json"); + + // populate db + tests.forEach(function(aTest) { + aTest.populate(); + // sanity + aTest.validate(); + }); + + // export json to file + try { + PlacesUtils.backupBookmarksToFile(jsonFile); + } catch(ex) { do_throw("couldn't export to file: " + ex); } + + // clean + tests.forEach(function(aTest) { + aTest.clean(); + }); + + // restore json file + try { + PlacesUtils.restoreBookmarksFromFile(jsonFile); + } catch(ex) { do_throw("couldn't import the exported file: " + ex); } + + // validate + tests.forEach(function(aTest) { + aTest.validate(); + }); + + // clean up + jsonFile.remove(false); +} diff --git a/mozilla/toolkit/locales/en-US/chrome/places/places.properties b/mozilla/toolkit/locales/en-US/chrome/places/places.properties index d1400a9c97a..8910e765125 100644 --- a/mozilla/toolkit/locales/en-US/chrome/places/places.properties +++ b/mozilla/toolkit/locales/en-US/chrome/places/places.properties @@ -23,3 +23,4 @@ bookmarksBackupTitle=Bookmarks backup filename bookmarksArchiveFilename=bookmarks-%S.json restoreFormatError=Unsupported file type. +restoreParseError=Unable to process the backup file.