Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Bug 821630 - Fix retrieving blobs with get() in Settings API. r=gwagner

  • Loading branch information...
commit 05ea92a25cc5f37229fdec9680603d0f81db186f 1 parent 80664ba
@reuben reuben authored
View
48 dom/settings/SettingsManager.js
@@ -87,19 +87,9 @@ SettingsLock.prototype = {
if (DEBUG) debug("MOZSETTINGS-SET-WARNING: " + key + " is not in the database.\n");
}
- let setReq;
- if (typeof(info.settings[key]) != 'object') {
- let obj = {settingName: key, defaultValue: defaultValue, userValue: userValue};
- if (DEBUG) debug("store1: " + JSON.stringify(obj));
- setReq = store.put(obj);
- } else {
- //Workaround for cloning issues
- let defaultVal = JSON.parse(JSON.stringify(defaultValue));
- let userVal = JSON.parse(JSON.stringify(userValue));
- let obj = {settingName: key, defaultValue: defaultVal, userValue: userVal};
- if (DEBUG) debug("store2: " + JSON.stringify(obj));
- setReq = store.put(obj);
- }
+ let obj = {settingName: key, defaultValue: defaultValue, userValue: userValue};
+ if (DEBUG) debug("store1: " + JSON.stringify(obj));
+ let setReq = store.put(obj);
setReq.onsuccess = function() {
lock._isBusy = false;
@@ -119,7 +109,7 @@ SettingsLock.prototype = {
Services.DOMRequest.fireError(request, setReq.error.name)
}
};
- }
+ };
checkKeyRequest.onerror = function(event) {
if (!request.error) {
Services.DOMRequest.fireError(request, checkKeyRequest.error.name)
@@ -133,7 +123,7 @@ SettingsLock.prototype = {
getReq.onsuccess = function(event) {
if (DEBUG) debug("Request for '" + info.name + "' successful. " +
- "Record count: " + event.target.result.length);
+ "Record count: " + event.target.result.length);
if (event.target.result.length == 0) {
if (DEBUG) debug("MOZSETTINGS-GET-WARNING: " + info.name + " is not in the database.\n");
@@ -200,6 +190,31 @@ SettingsLock.prototype = {
}
},
+ _serializePreservingBinaries: function _serializePreservingBinaries(aObject) {
+ // We need to serialize settings objects, otherwise they can change between
+ // the set() call and the enqueued request being processed. We can't simply
+ // parse(stringify(obj)) because that breaks things like Blobs, Files and
+ // Dates, so we use stringify's replacer and parse's reviver parameters to
+ // preserve binaries.
+ let binaries = Object.create(null);
+ let stringified = JSON.stringify(aObject, function(key, value) {
+ let kind = ObjectWrapper.getObjectKind(value);
+ if (kind == "file" || kind == "blob" || kind == "date") {
+ let uuid = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator)
+ .generateUUID().toString();
+ binaries[uuid] = value;
+ return uuid;
+ }
+ return value;
+ });
+ return JSON.parse(stringified, function(key, value) {
+ if (value in binaries) {
+ return binaries[value];
+ }
+ return value;
+ });
+ },
+
set: function set(aSettings) {
if (!this._open) {
dump("Settings lock not open!\n");
@@ -209,7 +224,7 @@ SettingsLock.prototype = {
if (this._settingsManager.hasWritePrivileges) {
let req = Services.DOMRequest.createRequest(this._settingsManager._window);
if (DEBUG) debug("send: " + JSON.stringify(aSettings));
- let settings = JSON.parse(JSON.stringify(aSettings));
+ let settings = this._serializePreservingBinaries(aSettings);
this._requests.enqueue({request: req, intent: "set", settings: settings});
this.createTransactionAndProcess();
return req;
@@ -311,7 +326,6 @@ SettingsManager.prototype = {
switch (aMessage.name) {
case "Settings:Change:Return:OK":
- if (DEBUG) debug("Settings:Change:Return:OK");
if (this._onsettingchange || this._callbacks) {
if (DEBUG) debug('data:' + msg.key + ':' + msg.value + '\n');
View
1  dom/settings/tests/Makefile.in
@@ -16,6 +16,7 @@ MOCHITEST_FILES = \
test_settings_basics.html \
test_settings_events.html \
test_settings_onsettingchange.html \
+ test_settings_blobs.html \
$(NULL)
_CHROMEMOCHITEST_FILES = \
View
161 dom/settings/tests/test_settings_blobs.html
@@ -0,0 +1,161 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=821630
+-->
+<head>
+ <title>Test for Bug 821630 Settings API</title>
+ <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=821630">Mozilla Bug 821630</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript;version=1.8">
+
+"use strict";
+
+var comp = SpecialPowers.wrap(SpecialPowers.Components);
+comp.utils.import("resource://gre/modules/SettingsChangeNotifier.jsm");
+SpecialPowers.setBoolPref("dom.mozSettings.enabled", true);
+SpecialPowers.addPermission("settings-read", true, document);
+SpecialPowers.addPermission("settings-write", true, document);
+
+function onUnwantedSuccess() {
+ ok(false, "onUnwantedSuccess: shouldn't get here");
+}
+
+function onFailure() {
+ return function(s) {
+ if (s) {
+ ok(false, "in on Failure! - " + s);
+ } else {
+ ok(false, "in on Failure!");
+ }
+ }
+}
+
+let mozSettings = window.navigator.mozSettings;
+let req;
+
+let storedBlob = new Blob([""], {"type": "text/xml"});
+
+function checkBlob(blob) {
+ try {
+ let url = URL.createObjectURL(blob);
+ ok(true, "Valid blob");
+ } catch (e) {
+ ok(false, "Valid blob");
+ }
+}
+
+let steps = [
+ function() {
+ let lock = mozSettings.createLock();
+ req = lock.clear();
+ req.onsuccess = next;
+ req.onerror = onFailure("Deleting database");
+ },
+ function() {
+ mozSettings.addObserver("test1", function(e) {
+ checkBlob(e.settingValue);
+ mozSettings.removeObserver("test1", this);
+ next();
+ });
+ next();
+ },
+ function() {
+ // next is called by the observer above
+ let req = mozSettings.createLock().set({"test1": storedBlob});
+ req.onerror = onFailure("Saving blob");
+ },
+ function() {
+ let req = mozSettings.createLock().get("test1");
+ req.onsuccess = function(event) {
+ checkBlob(event.target.result["test1"]);
+ next();
+ };
+ req.onerror = onFailure("Getting blob");
+ },
+ function() {
+ let req = mozSettings.createLock().set({"test2": [1, 2, storedBlob, 4]});
+ req.onsuccess = next;
+ req.onerror = onFailure("Saving array");
+ },
+ function() {
+ let req = mozSettings.createLock().get("test2");
+ req.onsuccess = function(event) {
+ let val = event.target.result["test2"];
+ ok(Array.isArray(val), "Result is an array");
+ ok(val[0] == 1 && val[1] == 2 && val[3] == 4, "Primitives are preserved");
+ checkBlob(val[2]);
+ next();
+ };
+ req.onerror = onFailure("Getting array");
+ },
+ function() {
+ let req = mozSettings.createLock().set({"test3": {foo: "bar", baz: {number: 1, arr: [storedBlob]}}});
+ req.onsuccess = next();
+ req.onerror = onFailure("Saving object");
+ },
+ function() {
+ let req = mozSettings.createLock().get("test3");
+ req.onsuccess = function(event) {
+ let val = event.target.result["test3"];
+ ok(typeof(val) == "object", "Result is an object");
+ ok("foo" in val && typeof(val.foo) == "string", "String property preserved");
+ ok("baz" in val && typeof(val.baz) == "object", "Object property preserved");
+ let baz = val.baz;
+ ok("number" in baz && baz.number == 1, "Primite inside object preserved");
+ ok("arr" in baz && Array.isArray(baz.arr), "Array inside object is preserved");
+ checkBlob(baz.arr[0]);
+ next();
+ };
+ req.onerror = onFailure("Getting object");
+ },
+ function() {
+ let req = mozSettings.createLock().clear();
+ req.onsuccess = function() {
+ next();
+ };
+ req.onerror = onFailure("Deleting database");
+ },
+ function () {
+ ok(true, "all done!\n");
+ SimpleTest.finish();
+ }
+];
+
+function next() {
+ try {
+ let step = steps.shift();
+ if (step) {
+ step();
+ }
+ } catch(ex) {
+ ok(false, "Caught exception", ex);
+ }
+}
+
+function permissionTest() {
+ if (gSettingsEnabled) {
+ next();
+ } else {
+ is(mozSettings, null, "mozSettings is null when not enabled.");
+ SimpleTest.finish();
+ }
+}
+
+let gSettingsEnabled = SpecialPowers.getBoolPref("dom.mozSettings.enabled");
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(permissionTest);
+</script>
+</pre>
+</body>
+</html>
Please sign in to comment.
Something went wrong with that request. Please try again.