зеркало из https://github.com/mozilla/gecko-dev.git
Backed out 82bed38ff50a (bug 900551, bug 1015518, bug 846200) due to marionette webapi test bustage on ICS Emulator
This commit is contained in:
Родитель
fbe4723bf6
Коммит
e4ee574673
|
@ -5,7 +5,7 @@
|
|||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
Cu.import('resource://gre/modules/ContactService.jsm');
|
||||
Cu.import('resource://gre/modules/SettingsRequestManager.jsm');
|
||||
Cu.import('resource://gre/modules/SettingsChangeNotifier.jsm');
|
||||
Cu.import('resource://gre/modules/DataStoreChangeNotifier.jsm');
|
||||
Cu.import('resource://gre/modules/AlarmService.jsm');
|
||||
Cu.import('resource://gre/modules/ActivitiesService.jsm');
|
||||
|
|
|
@ -30,6 +30,20 @@ function debug(aMsg) {
|
|||
//dump("-*-*- PermissionsInstaller.jsm : " + aMsg + "\n");
|
||||
}
|
||||
|
||||
// An array carring all the possible (expanded) permission names.
|
||||
let AllPossiblePermissions = [];
|
||||
for (let permName in PermissionsTable) {
|
||||
let expandedPermNames = [];
|
||||
if (PermissionsTable[permName].access) {
|
||||
expandedPermNames = expandPermissions(permName, READWRITE);
|
||||
} else {
|
||||
expandedPermNames = expandPermissions(permName);
|
||||
}
|
||||
AllPossiblePermissions = AllPossiblePermissions.concat(expandedPermNames);
|
||||
AllPossiblePermissions =
|
||||
AllPossiblePermissions.concat(["offline-app", "pin-app"]);
|
||||
}
|
||||
|
||||
this.PermissionsInstaller = {
|
||||
/**
|
||||
* Install permissisions or remove deprecated permissions upon re-install.
|
||||
|
|
|
@ -12,8 +12,7 @@ this.EXPORTED_SYMBOLS = [
|
|||
"PermissionsReverseTable",
|
||||
"expandPermissions",
|
||||
"appendAccessToPermName",
|
||||
"isExplicitInPermissionsTable",
|
||||
"AllPossiblePermissions"
|
||||
"isExplicitInPermissionsTable"
|
||||
];
|
||||
|
||||
// Permission access flags
|
||||
|
@ -145,16 +144,7 @@ this.PermissionsTable = { geolocation: {
|
|||
privileged: DENY_ACTION,
|
||||
certified: ALLOW_ACTION,
|
||||
access: ["read", "write"],
|
||||
additional: ["indexedDB-chrome-settings", "settings-api"]
|
||||
},
|
||||
// This exists purely for tests, no app
|
||||
// should ever use it. It can only be
|
||||
// handed out by SpecialPowers.
|
||||
"settings-clear": {
|
||||
app: DENY_ACTION,
|
||||
privileged: DENY_ACTION,
|
||||
certified: DENY_ACTION,
|
||||
additional: ["indexedDB-chrome-settings", "settings-api"]
|
||||
additional: ["indexedDB-chrome-settings"]
|
||||
},
|
||||
permissions: {
|
||||
app: DENY_ACTION,
|
||||
|
@ -393,13 +383,6 @@ this.PermissionsTable = { geolocation: {
|
|||
privileged: PROMPT_ACTION,
|
||||
certified: ALLOW_ACTION,
|
||||
substitute: ["firefox-accounts"]
|
||||
},
|
||||
"settings:wallpaper.image": {
|
||||
app: DENY_ACTION,
|
||||
privileged: PROMPT_ACTION,
|
||||
certified: ALLOW_ACTION,
|
||||
access: ["read", "write"],
|
||||
additional: ["settings-api"]
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -508,10 +491,7 @@ this.expandPermissions = function expandPermissions(aPermName, aAccess) {
|
|||
return expandedPermNames;
|
||||
};
|
||||
|
||||
this.PermissionsReverseTable = {};
|
||||
this.AllPossiblePermissions = [];
|
||||
|
||||
(function () {
|
||||
this.PermissionsReverseTable = (function () {
|
||||
// PermissionsTable as it is works well for direct searches, but not
|
||||
// so well for reverse ones (that is, if I get something like
|
||||
// device-storage:music-read or indexedDB-chrome-settings-read how
|
||||
|
@ -519,9 +499,8 @@ this.AllPossiblePermissions = [];
|
|||
// born. The idea is that
|
||||
// reverseTable[device-storage:music-read] should return
|
||||
// device-storage:music
|
||||
//
|
||||
// We also need a list of all the possible permissions for things like the
|
||||
// settingsmanager, so construct that while we're at it.
|
||||
let reverseTable = {};
|
||||
|
||||
for (let permName in PermissionsTable) {
|
||||
let permAliases;
|
||||
if (PermissionsTable[permName].access) {
|
||||
|
@ -530,12 +509,12 @@ this.AllPossiblePermissions = [];
|
|||
permAliases = expandPermissions(permName);
|
||||
}
|
||||
for (let i = 0; i < permAliases.length; i++) {
|
||||
PermissionsReverseTable[permAliases[i]] = permName;
|
||||
AllPossiblePermissions.push(permAliases[i]);
|
||||
reverseTable[permAliases[i]] = permName;
|
||||
}
|
||||
}
|
||||
AllPossiblePermissions =
|
||||
AllPossiblePermissions.concat(["offline-app", "pin-app"]);
|
||||
|
||||
return reverseTable;
|
||||
|
||||
})();
|
||||
|
||||
this.isExplicitInPermissionsTable = function(aPermName, aIntStatus) {
|
||||
|
|
|
@ -2109,8 +2109,8 @@ Navigator::DoNewResolve(JSContext* aCx, JS::Handle<JSObject*> aObject,
|
|||
}
|
||||
|
||||
if (name.EqualsLiteral("mozSettings")) {
|
||||
bool hasPermission = CheckPermission("settings-api-read") ||
|
||||
CheckPermission("settings-api-write");
|
||||
bool hasPermission = CheckPermission("settings-read") ||
|
||||
CheckPermission("settings-write");
|
||||
if (!hasPermission) {
|
||||
FillPropertyDescriptor(aDesc, aObject, JS::NullValue(), false);
|
||||
return true;
|
||||
|
|
|
@ -277,10 +277,6 @@ const kEventConstructors = {
|
|||
return new MozSettingsEvent(aName, aProps);
|
||||
},
|
||||
},
|
||||
MozSettingsTransactionEvent: { create: function (aName, aProps) {
|
||||
return new MozSettingsTransactionEvent(aName, aProps);
|
||||
},
|
||||
},
|
||||
MozSmsEvent: { create: function (aName, aProps) {
|
||||
return new MozSmsEvent(aName, aProps);
|
||||
},
|
||||
|
|
|
@ -15,7 +15,6 @@ var gData = [
|
|||
permission: "settings",
|
||||
access: READWRITE,
|
||||
expected: ["settings-read", "settings-write",
|
||||
"settings-api-read", "settings-api-write",
|
||||
"indexedDB-chrome-settings-read",
|
||||
"indexedDB-chrome-settings-write"]
|
||||
},
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict"
|
||||
|
||||
const DEBUG = false;
|
||||
function debug(s) {
|
||||
if (DEBUG) dump("-*- SettingsChangeNotifier: " + s + "\n");
|
||||
}
|
||||
|
||||
const Cu = Components.utils;
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["SettingsChangeNotifier"];
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const kXpcomShutdownObserverTopic = "xpcom-shutdown";
|
||||
const kMozSettingsChangedObserverTopic = "mozsettings-changed";
|
||||
const kFromSettingsChangeNotifier = "fromSettingsChangeNotifier";
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
|
||||
"@mozilla.org/parentprocessmessagemanager;1",
|
||||
"nsIMessageBroadcaster");
|
||||
|
||||
this.SettingsChangeNotifier = {
|
||||
init: function() {
|
||||
if (DEBUG) debug("init");
|
||||
this.children = [];
|
||||
this._messages = ["Settings:Changed", "Settings:RegisterForMessages", "child-process-shutdown"];
|
||||
this._messages.forEach((function(msgName) {
|
||||
ppmm.addMessageListener(msgName, this);
|
||||
}).bind(this));
|
||||
|
||||
Services.obs.addObserver(this, kXpcomShutdownObserverTopic, false);
|
||||
Services.obs.addObserver(this, kMozSettingsChangedObserverTopic, false);
|
||||
},
|
||||
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
if (DEBUG) debug("observe");
|
||||
switch (aTopic) {
|
||||
case kXpcomShutdownObserverTopic:
|
||||
this._messages.forEach((function(msgName) {
|
||||
ppmm.removeMessageListener(msgName, this);
|
||||
}).bind(this));
|
||||
Services.obs.removeObserver(this, kXpcomShutdownObserverTopic);
|
||||
Services.obs.removeObserver(this, kMozSettingsChangedObserverTopic);
|
||||
ppmm = null;
|
||||
break;
|
||||
case kMozSettingsChangedObserverTopic:
|
||||
{
|
||||
let setting = JSON.parse(aData);
|
||||
// To avoid redundantly broadcasting settings-changed events that are
|
||||
// just requested from content processes themselves, skip the observer
|
||||
// messages that are notified from the internal SettingsChangeNotifier.
|
||||
if (setting.message && setting.message === kFromSettingsChangeNotifier)
|
||||
return;
|
||||
this.broadcastMessage("Settings:Change:Return:OK",
|
||||
{ key: setting.key, value: setting.value });
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if (DEBUG) debug("Wrong observer topic: " + aTopic);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
broadcastMessage: function broadcastMessage(aMsgName, aContent) {
|
||||
if (DEBUG) debug("Broadast");
|
||||
this.children.forEach(function(msgMgr) {
|
||||
msgMgr.sendAsyncMessage(aMsgName, aContent);
|
||||
});
|
||||
},
|
||||
|
||||
receiveMessage: function(aMessage) {
|
||||
if (DEBUG) debug("receiveMessage");
|
||||
let msg = aMessage.data;
|
||||
let mm = aMessage.target;
|
||||
switch (aMessage.name) {
|
||||
case "Settings:Changed":
|
||||
if (!aMessage.target.assertPermission("settings-write")) {
|
||||
Cu.reportError("Settings message " + msg.name +
|
||||
" from a content process with no 'settings-write' privileges.");
|
||||
return null;
|
||||
}
|
||||
this.broadcastMessage("Settings:Change:Return:OK",
|
||||
{ key: msg.key, value: msg.value });
|
||||
Services.obs.notifyObservers(this, kMozSettingsChangedObserverTopic,
|
||||
JSON.stringify({
|
||||
key: msg.key,
|
||||
value: msg.value,
|
||||
message: kFromSettingsChangeNotifier
|
||||
}));
|
||||
break;
|
||||
case "Settings:RegisterForMessages":
|
||||
if (!aMessage.target.assertPermission("settings-read")) {
|
||||
Cu.reportError("Settings message " + msg.name +
|
||||
" from a content process with no 'settings-read' privileges.");
|
||||
return null;
|
||||
}
|
||||
if (DEBUG) debug("Register!");
|
||||
if (this.children.indexOf(mm) == -1) {
|
||||
this.children.push(mm);
|
||||
}
|
||||
break;
|
||||
case "child-process-shutdown":
|
||||
if (DEBUG) debug("Unregister");
|
||||
let index;
|
||||
if ((index = this.children.indexOf(mm)) != -1) {
|
||||
if (DEBUG) debug("Unregister index: " + index);
|
||||
this.children.splice(index, 1);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (DEBUG) debug("Wrong message: " + aMessage.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingsChangeNotifier.init();
|
|
@ -5,84 +5,42 @@
|
|||
"use strict";
|
||||
|
||||
const DEBUG = false;
|
||||
function debug(s) { dump("-*- SettingsManager: " + s + "\n"); }
|
||||
function debug(s) {
|
||||
if (DEBUG) dump("-*- SettingsManager: " + s + "\n");
|
||||
}
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/SettingsQueue.jsm");
|
||||
Cu.import("resource://gre/modules/SettingsDB.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(Services, "DOMRequest",
|
||||
"@mozilla.org/dom/dom-request-service;1",
|
||||
"nsIDOMRequestService");
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
|
||||
"@mozilla.org/childprocessmessagemanager;1",
|
||||
"nsIMessageSender");
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "mrm",
|
||||
"@mozilla.org/memory-reporter-manager;1",
|
||||
"nsIMemoryReporterManager");
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
|
||||
"@mozilla.org/uuid-generator;1",
|
||||
"nsIUUIDGenerator");
|
||||
|
||||
/**
|
||||
* In order to make SettingsManager work with Privileged Apps, we need the lock
|
||||
* to be OOP. However, the lock state needs to be managed on the child process,
|
||||
* while the IDB functions now happen on the parent process so we don't have to
|
||||
* expose IDB permissions at the child process level. We use the
|
||||
* DOMRequestHelper mechanism to deal with DOMRequests/promises across the
|
||||
* processes.
|
||||
*
|
||||
* However, due to the nature of the IDBTransaction lifetime, we need to relay
|
||||
* to the parent when to finalize the transaction once the child is done with the
|
||||
* lock. We keep a list of all open requests for a lock, and once the lock
|
||||
* reaches the end of its receiveMessage function with no more queued requests,
|
||||
* we consider it dead. At that point, we send a message to the parent to notify
|
||||
* it to finalize the transaction.
|
||||
*/
|
||||
|
||||
function SettingsLock(aSettingsManager) {
|
||||
if (DEBUG) debug("settings lock init");
|
||||
this._open = true;
|
||||
this._isBusy = false;
|
||||
this._requests = new Queue();
|
||||
this._settingsManager = aSettingsManager;
|
||||
this._id = uuidgen.generateUUID().toString();
|
||||
this._transaction = null;
|
||||
|
||||
let closeHelper = function() {
|
||||
if (DEBUG) debug("closing lock " + this._id);
|
||||
if (DEBUG) debug("closing lock");
|
||||
this._open = false;
|
||||
this.runOrFinalizeQueries();
|
||||
}.bind(this);
|
||||
|
||||
// DOMRequestIpcHelper.initHelper sets this._window
|
||||
this.initDOMRequestHelper(this._settingsManager._window, ["Settings:Get:OK", "Settings:Get:KO",
|
||||
"Settings:Clear:OK", "Settings:Clear:KO",
|
||||
"Settings:Set:OK", "Settings:Set:KO",
|
||||
"Settings:Finalize:OK", "Settings:Finalize:KO"]);
|
||||
this.sendMessage("Settings:CreateLock", {lockID: this._id, isInternalLock: false});
|
||||
Services.tm.currentThread.dispatch(closeHelper, Ci.nsIThread.DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
SettingsLock.prototype = {
|
||||
__proto__: DOMRequestIpcHelper.prototype,
|
||||
set onsettingstransactionsuccess(aHandler) {
|
||||
this.__DOM_IMPL__.setEventHandler("onsettingstransactionsuccess", aHandler);
|
||||
},
|
||||
|
||||
get onsettingstransactionsuccess() {
|
||||
return this.__DOM_IMPL__.getEventHandler("onsettingstransactionsuccess");
|
||||
},
|
||||
|
||||
set onsettingstransactionfailure(aHandler) {
|
||||
this.__DOM_IMPL__.setEventHandler("onsettingstransactionfailure", aHandler);
|
||||
},
|
||||
|
||||
get onsettingstransactionfailure() {
|
||||
return this.__DOM_IMPL__.getEventHandler("onsettingstransactionfailure");
|
||||
},
|
||||
|
||||
get closed() {
|
||||
return !this._open;
|
||||
},
|
||||
|
@ -91,140 +49,230 @@ SettingsLock.prototype = {
|
|||
return Cu.cloneInto(obj, this._settingsManager._window);
|
||||
},
|
||||
|
||||
sendMessage: function(aMessageName, aData) {
|
||||
cpmm.sendAsyncMessage(aMessageName,
|
||||
aData,
|
||||
undefined,
|
||||
this._settingsManager._window.document.nodePrincipal);
|
||||
},
|
||||
process: function process() {
|
||||
let lock = this;
|
||||
let store = lock._transaction.objectStore(SETTINGSSTORE_NAME);
|
||||
|
||||
runOrFinalizeQueries: function() {
|
||||
if (!this._requests || Object.keys(this._requests).length == 0) {
|
||||
this.sendMessage("Settings:Finalize", {lockID: this._id});
|
||||
while (!lock._requests.isEmpty()) {
|
||||
let info = lock._requests.dequeue();
|
||||
if (DEBUG) debug("info: " + info.intent);
|
||||
let request = info.request;
|
||||
switch (info.intent) {
|
||||
case "clear":
|
||||
let clearReq = store.clear();
|
||||
clearReq.onsuccess = function() {
|
||||
this._open = true;
|
||||
Services.DOMRequest.fireSuccess(request, 0);
|
||||
this._open = false;
|
||||
}.bind(lock);
|
||||
clearReq.onerror = function() {
|
||||
Services.DOMRequest.fireError(request, 0)
|
||||
};
|
||||
break;
|
||||
case "set":
|
||||
let keys = Object.getOwnPropertyNames(info.settings);
|
||||
if (keys.length) {
|
||||
lock._isBusy = true;
|
||||
}
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
let key = keys[i];
|
||||
let last = i === keys.length - 1;
|
||||
if (DEBUG) debug("key: " + key + ", val: " + JSON.stringify(info.settings[key]) + ", type: " + typeof(info.settings[key]));
|
||||
let checkKeyRequest = store.get(key);
|
||||
|
||||
checkKeyRequest.onsuccess = function (event) {
|
||||
let defaultValue;
|
||||
let userValue = info.settings[key];
|
||||
if (event.target.result) {
|
||||
defaultValue = event.target.result.defaultValue;
|
||||
} else {
|
||||
this.sendMessage("Settings:Run", {lockID: this._id});
|
||||
defaultValue = null;
|
||||
if (DEBUG) debug("MOZSETTINGS-SET-WARNING: " + key + " is not in the database.\n");
|
||||
}
|
||||
|
||||
let obj = {settingName: key, defaultValue: defaultValue, userValue: userValue};
|
||||
if (DEBUG) debug("store1: " + JSON.stringify(obj));
|
||||
let setReq = store.put(obj);
|
||||
|
||||
setReq.onsuccess = function() {
|
||||
cpmm.sendAsyncMessage("Settings:Changed", { key: key, value: userValue });
|
||||
if (last && !request.error) {
|
||||
lock._open = true;
|
||||
Services.DOMRequest.fireSuccess(request, 0);
|
||||
lock._open = false;
|
||||
}
|
||||
};
|
||||
|
||||
setReq.onerror = function() {
|
||||
if (!request.error) {
|
||||
Services.DOMRequest.fireError(request, setReq.error.name)
|
||||
}
|
||||
};
|
||||
|
||||
if (last) {
|
||||
lock._isBusy = false;
|
||||
if (!lock._requests.isEmpty()) {
|
||||
lock.process();
|
||||
}
|
||||
}
|
||||
};
|
||||
checkKeyRequest.onerror = function(event) {
|
||||
if (!request.error) {
|
||||
Services.DOMRequest.fireError(request, checkKeyRequest.error.name)
|
||||
}
|
||||
};
|
||||
}
|
||||
// Don't break here, instead return. Once the previous requests have
|
||||
// finished this loop will start again.
|
||||
return;
|
||||
case "get":
|
||||
let getReq = (info.name === "*") ? store.mozGetAll()
|
||||
: store.mozGetAll(info.name);
|
||||
|
||||
getReq.onsuccess = function(event) {
|
||||
if (DEBUG) debug("Request for '" + info.name + "' successful. " +
|
||||
"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");
|
||||
}
|
||||
|
||||
let results = {};
|
||||
|
||||
for (var i in event.target.result) {
|
||||
let result = event.target.result[i];
|
||||
var name = result.settingName;
|
||||
if (DEBUG) debug("VAL: " + result.userValue +", " + result.defaultValue + "\n");
|
||||
results[name] = result.userValue !== undefined ? result.userValue : result.defaultValue;
|
||||
}
|
||||
|
||||
this._open = true;
|
||||
Services.DOMRequest.fireSuccess(request, this._wrap(results));
|
||||
this._open = false;
|
||||
}.bind(lock);
|
||||
|
||||
getReq.onerror = function() {
|
||||
Services.DOMRequest.fireError(request, 0)
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
receiveMessage: function(aMessage) {
|
||||
let msg = aMessage.data;
|
||||
// SettingsRequestManager broadcasts changes to all locks in the child. If
|
||||
// our lock isn't being addressed, just return.
|
||||
if (msg.lockID != this._id) {
|
||||
return;
|
||||
}
|
||||
createTransactionAndProcess: function() {
|
||||
if (DEBUG) debug("database opened, creating transaction");
|
||||
|
||||
// Finalizing a transaction does not return a request ID since we are
|
||||
// supposed to fire callbacks.
|
||||
if (!msg.requestID) {
|
||||
let event;
|
||||
switch (aMessage.name) {
|
||||
case "Settings:Finalize:OK":
|
||||
if (DEBUG) debug("Lock finalize ok!");
|
||||
event = new this._window.MozSettingsTransactionEvent("settingstransactionsuccess", {});
|
||||
this.__DOM_IMPL__.dispatchEvent(event);
|
||||
break;
|
||||
case "Settings:Finalize:KO":
|
||||
if (DEBUG) debug("Lock finalize failed!");
|
||||
event = new this._window.MozSettingsTransactionEvent("settingstransactionfailure", {
|
||||
error: msg.errorMsg
|
||||
});
|
||||
this.__DOM_IMPL__.dispatchEvent(event);
|
||||
break;
|
||||
default:
|
||||
if (DEBUG) debug("Message type " + aMessage.name + " is missing a requestID");
|
||||
}
|
||||
return;
|
||||
}
|
||||
let manager = this._settingsManager;
|
||||
let transactionType = manager.hasWritePrivileges ? "readwrite" : "readonly";
|
||||
|
||||
this._transaction =
|
||||
manager._settingsDB._db.transaction(SETTINGSSTORE_NAME, transactionType);
|
||||
|
||||
let req = this.getRequest(msg.requestID);
|
||||
if (!req) {
|
||||
if (DEBUG) debug("Matching request not found.");
|
||||
return;
|
||||
this.process();
|
||||
},
|
||||
|
||||
maybeProcess: function() {
|
||||
if (this._transaction && !this._isBusy) {
|
||||
this.process();
|
||||
}
|
||||
this.removeRequest(msg.requestID);
|
||||
if (DEBUG) debug("receiveMessage: " + aMessage.name);
|
||||
switch (aMessage.name) {
|
||||
case "Settings:Get:OK":
|
||||
for (let i in msg.settings) {
|
||||
msg.settings[i] = this._wrap(msg.settings[i]);
|
||||
}
|
||||
this._open = true;
|
||||
Services.DOMRequest.fireSuccess(req.request, this._wrap(msg.settings));
|
||||
this._open = false;
|
||||
break;
|
||||
case "Settings:Set:OK":
|
||||
case "Settings:Clear:OK":
|
||||
this._open = true;
|
||||
Services.DOMRequest.fireSuccess(req.request, 0);
|
||||
this._open = false;
|
||||
break;
|
||||
case "Settings:Get:KO":
|
||||
case "Settings:Set:KO":
|
||||
case "Settings:Clear:KO":
|
||||
if (DEBUG) debug("error:" + msg.errorMsg);
|
||||
Services.DOMRequest.fireError(req.request, msg.errorMsg);
|
||||
break;
|
||||
default:
|
||||
if (DEBUG) debug("Wrong message: " + aMessage.name);
|
||||
}
|
||||
this.runOrFinalizeQueries();
|
||||
},
|
||||
|
||||
get: function get(aName) {
|
||||
if (DEBUG) debug("get (" + this._id + "): " + aName);
|
||||
if (!this._open) {
|
||||
dump("Settings lock not open!\n");
|
||||
throw Components.results.NS_ERROR_ABORT;
|
||||
}
|
||||
let req = this.createRequest();
|
||||
let reqID = this.getRequestId({request: req});
|
||||
this.sendMessage("Settings:Get", {requestID: reqID,
|
||||
lockID: this._id,
|
||||
name: aName});
|
||||
|
||||
if (this._settingsManager.hasReadPrivileges) {
|
||||
let req = Services.DOMRequest.createRequest(this._settingsManager._window);
|
||||
this._requests.enqueue({ request: req, intent:"get", name: aName });
|
||||
this.maybeProcess();
|
||||
return req;
|
||||
} else {
|
||||
if (DEBUG) debug("get not allowed");
|
||||
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
},
|
||||
|
||||
_serializePreservingBinaries: function _serializePreservingBinaries(aObject) {
|
||||
function needsUUID(aValue) {
|
||||
if (!aValue || !aValue.constructor) {
|
||||
return false;
|
||||
}
|
||||
return (aValue.constructor.name == "Date") || (aValue instanceof Ci.nsIDOMFile) ||
|
||||
(aValue instanceof Ci.nsIDOMBlob);
|
||||
}
|
||||
// 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 manager = this._settingsManager;
|
||||
let binaries = Object.create(null);
|
||||
let stringified = JSON.stringify(aObject, function(key, value) {
|
||||
value = manager._settingsDB.prepareValue(value);
|
||||
if (needsUUID(value)) {
|
||||
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 (DEBUG) debug("send: " + JSON.stringify(aSettings));
|
||||
if (!this._open) {
|
||||
throw "Settings lock not open";
|
||||
}
|
||||
let req = this.createRequest();
|
||||
let reqID = this.getRequestId({request: req});
|
||||
this.sendMessage("Settings:Set", {requestID: reqID,
|
||||
lockID: this._id,
|
||||
settings: aSettings});
|
||||
|
||||
if (this._settingsManager.hasWritePrivileges) {
|
||||
let req = Services.DOMRequest.createRequest(this._settingsManager._window);
|
||||
if (DEBUG) debug("send: " + JSON.stringify(aSettings));
|
||||
let settings = this._serializePreservingBinaries(aSettings);
|
||||
this._requests.enqueue({request: req, intent: "set", settings: settings});
|
||||
this.maybeProcess();
|
||||
return req;
|
||||
} else {
|
||||
if (DEBUG) debug("set not allowed");
|
||||
throw "No permission to call set";
|
||||
}
|
||||
},
|
||||
|
||||
clear: function clear() {
|
||||
if (DEBUG) if (DEBUG) debug("clear");
|
||||
if (!this._open) {
|
||||
throw "Settings lock not open";
|
||||
}
|
||||
let req = this.createRequest();
|
||||
let reqID = this.getRequestId({request: req});
|
||||
this.sendMessage("Settings:Clear", {requestID: reqID,
|
||||
lockID: this._id});
|
||||
|
||||
if (this._settingsManager.hasWritePrivileges) {
|
||||
let req = Services.DOMRequest.createRequest(this._settingsManager._window);
|
||||
this._requests.enqueue({ request: req, intent: "clear"});
|
||||
this.maybeProcess();
|
||||
return req;
|
||||
} else {
|
||||
if (DEBUG) debug("clear not allowed");
|
||||
throw "No permission to call clear";
|
||||
}
|
||||
},
|
||||
|
||||
classID: Components.ID("{60c9357c-3ae0-4222-8f55-da01428470d5}"),
|
||||
contractID: "@mozilla.org/settingsLock;1",
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
|
||||
Ci.nsIObserver,
|
||||
Ci.nsISupportsWeakReference])
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
|
||||
};
|
||||
|
||||
function SettingsManager() {
|
||||
this._settingsDB = new SettingsDB();
|
||||
this._settingsDB.init();
|
||||
}
|
||||
|
||||
SettingsManager.prototype = {
|
||||
_callbacks: null,
|
||||
_isRegistered: false,
|
||||
_perms: [],
|
||||
|
||||
_wrap: function _wrap(obj) {
|
||||
return Cu.cloneInto(obj, this._window);
|
||||
|
@ -232,7 +280,6 @@ SettingsManager.prototype = {
|
|||
|
||||
set onsettingchange(aHandler) {
|
||||
this.__DOM_IMPL__.setEventHandler("onsettingchange", aHandler);
|
||||
this.checkMessageRegistration();
|
||||
},
|
||||
|
||||
get onsettingchange() {
|
||||
|
@ -240,8 +287,12 @@ SettingsManager.prototype = {
|
|||
},
|
||||
|
||||
createLock: function() {
|
||||
if (DEBUG) debug("creating lock");
|
||||
let lock = new SettingsLock(this);
|
||||
if (DEBUG) debug("get lock!");
|
||||
var lock = new SettingsLock(this);
|
||||
this._settingsDB.ensureDB(
|
||||
function() { lock.createTransactionAndProcess(); },
|
||||
function() { dump("Cannot open Settings DB. Trying to open an old version?\n"); }
|
||||
);
|
||||
return lock;
|
||||
},
|
||||
|
||||
|
@ -273,35 +324,10 @@ SettingsManager.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
// If we have either observer callbacks or an event handler,
|
||||
// register for messages from the main thread. Otherwise, if no one
|
||||
// is listening, unregister to reduce parent load.
|
||||
checkMessageRegistration: function checkRegistration() {
|
||||
let handler = this.__DOM_IMPL__.getEventHandler("onsettingchange");
|
||||
if (!this._isRegistered) {
|
||||
if (DEBUG) debug("Registering for messages");
|
||||
cpmm.sendAsyncMessage("Settings:RegisterForMessages",
|
||||
undefined,
|
||||
undefined,
|
||||
this._window.document.nodePrincipal);
|
||||
this._isRegistered = true;
|
||||
} else {
|
||||
if ((!this._callbacks || Object.keys(this._callbacks).length == 0) &&
|
||||
!handler) {
|
||||
if (DEBUG) debug("Unregistering for messages");
|
||||
cpmm.sendAsyncMessage("Settings:UnregisterForMessages",
|
||||
undefined,
|
||||
undefined,
|
||||
this._window.document.nodePrincipal);
|
||||
this._isRegistered = false;
|
||||
this._callbacks = null;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
addObserver: function addObserver(aName, aCallback) {
|
||||
if (DEBUG) debug("addObserver " + aName);
|
||||
if (!this._callbacks) {
|
||||
cpmm.sendAsyncMessage("Settings:RegisterForMessages");
|
||||
this._callbacks = {};
|
||||
}
|
||||
if (!this._callbacks[aName]) {
|
||||
|
@ -309,35 +335,43 @@ SettingsManager.prototype = {
|
|||
} else {
|
||||
this._callbacks[aName].push(aCallback);
|
||||
}
|
||||
this.checkMessageRegistration();
|
||||
},
|
||||
|
||||
removeObserver: function removeObserver(aName, aCallback) {
|
||||
if (DEBUG) debug("deleteObserver " + aName);
|
||||
if (this._callbacks && this._callbacks[aName]) {
|
||||
let index = this._callbacks[aName].indexOf(aCallback);
|
||||
let index = this._callbacks[aName].indexOf(aCallback)
|
||||
if (index != -1) {
|
||||
this._callbacks[aName].splice(index, 1);
|
||||
if (this._callbacks[aName].length == 0) {
|
||||
delete this._callbacks[aName];
|
||||
}
|
||||
this._callbacks[aName].splice(index, 1)
|
||||
} else {
|
||||
if (DEBUG) debug("Callback not found for: " + aName);
|
||||
}
|
||||
} else {
|
||||
if (DEBUG) debug("No observers stored for " + aName);
|
||||
}
|
||||
this.checkMessageRegistration();
|
||||
},
|
||||
|
||||
init: function(aWindow) {
|
||||
if (DEBUG) debug("SettingsManager init");
|
||||
mrm.registerStrongReporter(this);
|
||||
cpmm.addMessageListener("Settings:Change:Return:OK", this);
|
||||
this._window = aWindow;
|
||||
Services.obs.addObserver(this, "inner-window-destroyed", false);
|
||||
let util = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
|
||||
this.innerWindowID = util.currentInnerWindowID;
|
||||
|
||||
let readPerm = Services.perms.testExactPermissionFromPrincipal(aWindow.document.nodePrincipal, "settings-read");
|
||||
let writePerm = Services.perms.testExactPermissionFromPrincipal(aWindow.document.nodePrincipal, "settings-write");
|
||||
this.hasReadPrivileges = readPerm == Ci.nsIPermissionManager.ALLOW_ACTION;
|
||||
this.hasWritePrivileges = writePerm == Ci.nsIPermissionManager.ALLOW_ACTION;
|
||||
|
||||
if (this.hasReadPrivileges) {
|
||||
cpmm.sendAsyncMessage("Settings:RegisterForMessages");
|
||||
}
|
||||
|
||||
if (!this.hasReadPrivileges && !this.hasWritePrivileges) {
|
||||
dump("No settings permission for: " + aWindow.document.nodePrincipal.origin + "\n");
|
||||
Cu.reportError("No settings permission for: " + aWindow.document.nodePrincipal.origin);
|
||||
}
|
||||
},
|
||||
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
|
@ -351,7 +385,7 @@ SettingsManager.prototype = {
|
|||
},
|
||||
|
||||
collectReports: function(aCallback, aData, aAnonymize) {
|
||||
for (let topic in this._callbacks) {
|
||||
for (var topic in this._callbacks) {
|
||||
let length = this._callbacks[topic].length;
|
||||
if (length == 0) {
|
||||
continue;
|
||||
|
@ -381,6 +415,7 @@ SettingsManager.prototype = {
|
|||
this._requests = null;
|
||||
this._window = null;
|
||||
this._innerWindowID = null;
|
||||
this._settingsDB.close();
|
||||
},
|
||||
|
||||
classID: Components.ID("{c40b1c70-00fb-11e2-a21f-0800200c9a66}"),
|
||||
|
@ -391,4 +426,4 @@ SettingsManager.prototype = {
|
|||
Ci.nsIMemoryReporter]),
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SettingsManager, SettingsLock]);
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SettingsManager, SettingsLock])
|
||||
|
|
|
@ -1,912 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const DEBUG = false;
|
||||
function debug(s) { dump("-*- SettingsRequestManager: " + s + "\n"); }
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["SettingsRequestManager"];
|
||||
|
||||
Cu.import("resource://gre/modules/SettingsDB.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/PermissionsTable.jsm");
|
||||
|
||||
const kXpcomShutdownObserverTopic = "xpcom-shutdown";
|
||||
const kMozSettingsChangedObserverTopic = "mozsettings-changed";
|
||||
const kSettingsReadSuffix = "-read";
|
||||
const kSettingsWriteSuffix = "-write";
|
||||
const kSettingsClearPermission = "settings-clear";
|
||||
const kAllSettingsReadPermission = "settings" + kSettingsReadSuffix;
|
||||
const kAllSettingsWritePermission = "settings" + kSettingsWriteSuffix;
|
||||
// Any application with settings permissions, be it for all settings
|
||||
// or a single one, will need to be able to access the settings API.
|
||||
// The settings-api permission allows an app to see the mozSettings
|
||||
// API in order to create locks and queue tasks. Whether these tasks
|
||||
// will be allowed depends on the exact permissions the app has.
|
||||
const kSomeSettingsReadPermission = "settings-api" + kSettingsReadSuffix;
|
||||
const kSomeSettingsWritePermission = "settings-api" + kSettingsWriteSuffix;
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
|
||||
"@mozilla.org/parentprocessmessagemanager;1",
|
||||
"nsIMessageBroadcaster");
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
|
||||
"@mozilla.org/uuid-generator;1",
|
||||
"nsIUUIDGenerator");
|
||||
|
||||
let SettingsPermissions = {
|
||||
_mmPermissions: {},
|
||||
addManager: function(aMessage) {
|
||||
if (DEBUG) debug("Adding message manager permissions");
|
||||
let mm = aMessage.target;
|
||||
// In order for mochitests to work, we have to update permissions on every
|
||||
// lock creation or observer addition. This still means we can cache
|
||||
// permissions.
|
||||
if (this._mmPermissions[mm]) {
|
||||
if (DEBUG) debug("Manager already added, updating permissions");
|
||||
}
|
||||
let perms = [];
|
||||
let principal;
|
||||
let isSystemPrincipal = false;
|
||||
if (aMessage.principal.origin == "[System Principal]") {
|
||||
isSystemPrincipal = true;
|
||||
} else {
|
||||
let uri = Services.io.newURI(aMessage.principal.origin, null, null);
|
||||
principal = Services.scriptSecurityManager.getAppCodebasePrincipal(uri,
|
||||
aMessage.principal.appId,
|
||||
aMessage.principal.isInBrowserElement);
|
||||
}
|
||||
for (let i in AllPossiblePermissions) {
|
||||
let permName = AllPossiblePermissions[i];
|
||||
// We only care about permissions starting with the word "settings"
|
||||
if (permName.indexOf("settings") != 0) {
|
||||
continue;
|
||||
}
|
||||
if (isSystemPrincipal || Services.perms.testExactPermissionFromPrincipal(principal, permName) == Ci.nsIPermissionManager.ALLOW_ACTION) {
|
||||
perms.push(permName);
|
||||
}
|
||||
}
|
||||
this._mmPermissions[mm] = perms;
|
||||
},
|
||||
removeManager: function(aMsgMgr) {
|
||||
if (DEBUG) debug("Removing message manager permissions for " + aMsgMgr);
|
||||
if (!this._mmPermissions[aMsgMgr]) {
|
||||
if (DEBUG) debug("Manager not added!");
|
||||
return;
|
||||
}
|
||||
delete this._mmPermissions[aMsgMgr];
|
||||
},
|
||||
checkPermission: function(aMsgMgr, aPerm) {
|
||||
if (!this._mmPermissions[aMsgMgr]) {
|
||||
if (DEBUG) debug("Manager not added!");
|
||||
return false;
|
||||
}
|
||||
return (this._mmPermissions[aMsgMgr].indexOf(aPerm) != -1);
|
||||
},
|
||||
hasAllReadPermission: function(aMsgMgr) {
|
||||
return this.checkPermission(aMsgMgr, kAllSettingsReadPermission);
|
||||
},
|
||||
hasAllWritePermission: function(aMsgMgr) {
|
||||
return this.checkPermission(aMsgMgr, kAllSettingsWritePermission);
|
||||
},
|
||||
hasSomeReadPermission: function(aMsgMgr) {
|
||||
return this.checkPermission(aMsgMgr, kSomeSettingsReadPermission);
|
||||
},
|
||||
hasSomeWritePermission: function(aMsgMgr) {
|
||||
return this.checkPermission(aMsgMgr, kSomeSettingsWritePermission);
|
||||
},
|
||||
hasClearPermission: function(aMsgMgr) {
|
||||
return this.checkPermission(aMsgMgr, kSettingsClearPermission);
|
||||
},
|
||||
assertSomeReadPermission: function(aMsgMgr) {
|
||||
aMsgMgr.assertPermission(kSomeSettingsReadPermission);
|
||||
},
|
||||
hasReadPermission: function(aMsgMgr, aSettingsName) {
|
||||
return this.hasAllReadPermission(aMsgMgr) || this.checkPermission(aMsgMgr, "settings:" + aSettingsName + kSettingsReadSuffix);
|
||||
},
|
||||
hasWritePermission: function(aMsgMgr, aSettingsName) {
|
||||
return this.hasAllWritePermission(aMsgMgr) || this.checkPermission(aMsgMgr, "settings:" + aSettingsName + kSettingsWriteSuffix);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function SettingsLockInfo(aDB, aMsgMgr, aLockID, aIsServiceLock) {
|
||||
return {
|
||||
// ID Shared with the object on the child side
|
||||
lockID: aLockID,
|
||||
// Is this a content lock or a settings service lock?
|
||||
isServiceLock: aIsServiceLock,
|
||||
// Tasks to be run once the lock is at the head of the queue
|
||||
tasks: [],
|
||||
// This is set to true once a transaction is ready to run, but is not at the
|
||||
// head of the lock queue.
|
||||
consumable: false,
|
||||
// Holds values that are requested to be set until the lock lifetime ends,
|
||||
// then commits them to the DB.
|
||||
queuedSets: {},
|
||||
// Internal transaction object
|
||||
_transaction: undefined,
|
||||
// Message manager that controls the lock
|
||||
_mm: aMsgMgr,
|
||||
// If true, it means a permissions check failed, so just fail everything now
|
||||
_failed: false,
|
||||
// If we're slated to run finalize, set this to make sure we don't
|
||||
// somehow run other events afterward.
|
||||
finalizing: false,
|
||||
// Lets us know if we can use this lock for a clear command
|
||||
canClear: true,
|
||||
// Lets us know if this lock has been used to clear at any point.
|
||||
hasCleared: false,
|
||||
getObjectStore: function() {
|
||||
if (DEBUG) debug("Getting transaction for " + this.lockID);
|
||||
let store;
|
||||
// Test for transaction validity via trying to get the
|
||||
// datastore. If it doesn't work, assume the transaction is
|
||||
// closed, create a new transaction and try again.
|
||||
if (this._transaction) {
|
||||
try {
|
||||
store = this._transaction.objectStore(SETTINGSSTORE_NAME);
|
||||
} catch (e) {
|
||||
if (e.name == "InvalidStateError") {
|
||||
if (DEBUG) debug("Current transaction for " + this.lockID + " closed, trying to create new one.");
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Create one transaction with a global permission. This may be
|
||||
// slightly slower on apps with full settings permissions, but
|
||||
// it means we don't have to do our own transaction order
|
||||
// bookkeeping.
|
||||
if (!SettingsPermissions.hasSomeWritePermission(this._mm)) {
|
||||
this._transaction = aDB._db.transaction(SETTINGSSTORE_NAME, "readonly");
|
||||
} else {
|
||||
this._transaction = aDB._db.transaction(SETTINGSSTORE_NAME, "readwrite");
|
||||
}
|
||||
this._transaction.oncomplete = function() {
|
||||
if (DEBUG) debug("Transaction for lock " + this.lockID + " closed");
|
||||
}.bind(this);
|
||||
this._transaction.onabort = function () {
|
||||
if (DEBUG) debug("Transaction for lock " + this.lockID + " aborted");
|
||||
this._failed = true;
|
||||
}.bind(this);
|
||||
try {
|
||||
store = this._transaction.objectStore(SETTINGSSTORE_NAME);
|
||||
} catch (e) {
|
||||
if (e.name == "InvalidStateError") {
|
||||
if (DEBUG) debug("Cannot create objectstore on transaction for " + this.lockID);
|
||||
return null;
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
return store;
|
||||
},
|
||||
get objectStore() {
|
||||
return this.getObjectStore();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let SettingsRequestManager = {
|
||||
// Access to the settings DB
|
||||
settingsDB: new SettingsDB(),
|
||||
// Remote messages to listen for from child
|
||||
messages: ["child-process-shutdown", "Settings:Get", "Settings:Set",
|
||||
"Settings:Clear", "Settings:Run", "Settings:Finalize",
|
||||
"Settings:CreateLock", "Settings:RegisterForMessages"],
|
||||
// Map of LockID to SettingsLockInfo objects
|
||||
lockInfo: {},
|
||||
// Queue of LockIDs. The LockID on the front of the queue is the only lock
|
||||
// that will have requests processed, all other locks will queue requests
|
||||
// until they hit the front of the queue.
|
||||
settingsLockQueue: [],
|
||||
children: [],
|
||||
init: function() {
|
||||
if (DEBUG) debug("init");
|
||||
this.settingsDB.init();
|
||||
this.messages.forEach((function(msgName) {
|
||||
ppmm.addMessageListener(msgName, this);
|
||||
}).bind(this));
|
||||
Services.obs.addObserver(this, kXpcomShutdownObserverTopic, false);
|
||||
},
|
||||
|
||||
_serializePreservingBinaries: function _serializePreservingBinaries(aObject) {
|
||||
function needsUUID(aValue) {
|
||||
if (!aValue || !aValue.constructor) {
|
||||
return false;
|
||||
}
|
||||
return (aValue.constructor.name == "Date") || (aValue instanceof Ci.nsIDOMFile) ||
|
||||
(aValue instanceof Ci.nsIDOMBlob);
|
||||
}
|
||||
// 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) {
|
||||
value = this.settingsDB.prepareValue(value);
|
||||
if (needsUUID(value)) {
|
||||
let uuid = uuidgen.generateUUID().toString();
|
||||
binaries[uuid] = value;
|
||||
return uuid;
|
||||
}
|
||||
return value;
|
||||
}.bind(this));
|
||||
return JSON.parse(stringified, function(key, value) {
|
||||
if (value in binaries) {
|
||||
return binaries[value];
|
||||
}
|
||||
return value;
|
||||
});
|
||||
},
|
||||
|
||||
queueTask: function(aOperation, aData) {
|
||||
if (DEBUG) debug("Queueing task: " + aOperation);
|
||||
|
||||
let defer = {};
|
||||
|
||||
if (aOperation == "set") {
|
||||
aData.settings = this._serializePreservingBinaries(aData.settings);
|
||||
}
|
||||
|
||||
this.lockInfo[aData.lockID].tasks.push({
|
||||
operation: aOperation,
|
||||
data: aData,
|
||||
defer: defer
|
||||
});
|
||||
|
||||
let promise = new Promise(function(resolve, reject) {
|
||||
defer.resolve = resolve;
|
||||
defer.reject = reject;
|
||||
});
|
||||
|
||||
return promise;
|
||||
},
|
||||
|
||||
// Due to the fact that we're skipping the database in some places
|
||||
// by keeping a local "set" value cache, resolving some calls
|
||||
// without a call to the database would mean we could potentially
|
||||
// receive promise responses out of expected order if a get is
|
||||
// called before a set. Therefore, we wrap our resolve in a null
|
||||
// get, which means it will resolves afer the rest of the calls
|
||||
// queued to the DB.
|
||||
queueTaskReturn: function(aTask, aReturnValue) {
|
||||
if (DEBUG) debug("Making task queuing transaction request.");
|
||||
let data = aTask.data;
|
||||
let lock = this.lockInfo[data.lockID];
|
||||
let store = lock.objectStore;
|
||||
if (!store) {
|
||||
if (DEBUG) debug("Rejecting task queue on lock " + aTask.data.lockID);
|
||||
return Promise.reject({task: aTask, error: "Cannot get object store"});
|
||||
}
|
||||
// Due to the fact that we're skipping the database, resolving
|
||||
// this without a call to the database would mean we could
|
||||
// potentially receive promise responses out of expected order if
|
||||
// a get is called before a set. Therefore, we wrap our resolve in
|
||||
// a null get, which means it will resolves afer the rest of the
|
||||
// calls queued to the DB.
|
||||
let getReq = store.get(0);
|
||||
|
||||
let defer = {};
|
||||
let promiseWrapper = new Promise(function(resolve, reject) {
|
||||
defer.resolve = resolve;
|
||||
defer.reject = reject;
|
||||
});
|
||||
|
||||
getReq.onsuccess = function(event) {
|
||||
return defer.resolve(aReturnValue);
|
||||
};
|
||||
getReq.onerror = function() {
|
||||
return defer.reject({task: aTask, error: getReq.error.name});
|
||||
};
|
||||
return promiseWrapper;
|
||||
},
|
||||
|
||||
taskGet: function(aTask) {
|
||||
if (DEBUG) debug("Running Get task on lock " + aTask.data.lockID);
|
||||
|
||||
// Check that we have permissions for getting the value
|
||||
let data = aTask.data;
|
||||
let lock = this.lockInfo[data.lockID];
|
||||
|
||||
if (lock._failed) {
|
||||
if (DEBUG) debug("Lock failed. All subsequent requests will fail.");
|
||||
return Promise.reject({task: aTask, error: "Lock failed, all requests now failing."});
|
||||
}
|
||||
|
||||
if (lock.hasCleared) {
|
||||
if (DEBUG) debug("Lock was used for a clear command. All subsequent requests will fail.");
|
||||
return Promise.reject({task: aTask, error: "Lock was used for a clear command. All subsequent requests will fail."});
|
||||
}
|
||||
|
||||
lock.canClear = false;
|
||||
|
||||
if (!SettingsPermissions.hasReadPermission(lock._mm, data.name)) {
|
||||
if (DEBUG) debug("get not allowed for " + data.name);
|
||||
lock._failed = true;
|
||||
return Promise.reject({task: aTask, error: "No permission to get " + data.name});
|
||||
}
|
||||
|
||||
// If the value was set during this transaction, use the cached value
|
||||
if (data.name in lock.queuedSets) {
|
||||
if (DEBUG) debug("Returning cached set value " + lock.queuedSets[data.name] + " for " + data.name);
|
||||
let local_results = {};
|
||||
local_results[data.name] = lock.queuedSets[data.name];
|
||||
return this.queueTaskReturn(aTask, {task: aTask, results: local_results});
|
||||
}
|
||||
|
||||
// Create/Get transaction and make request
|
||||
if (DEBUG) debug("Making get transaction request for " + data.name);
|
||||
let store = lock.objectStore;
|
||||
if (!store) {
|
||||
if (DEBUG) debug("Rejecting Get task on lock " + aTask.data.lockID);
|
||||
return Promise.reject({task: aTask, error: "Cannot get object store"});
|
||||
}
|
||||
|
||||
if (DEBUG) debug("Making get request for " + data.name);
|
||||
let getReq = (data.name === "*") ? store.mozGetAll() : store.mozGetAll(data.name);
|
||||
|
||||
let defer = {};
|
||||
let promiseWrapper = new Promise(function(resolve, reject) {
|
||||
defer.resolve = resolve;
|
||||
defer.reject = reject;
|
||||
});
|
||||
|
||||
getReq.onsuccess = function(event) {
|
||||
if (DEBUG) debug("Request for '" + data.name + "' successful. " +
|
||||
"Record count: " + event.target.result.length);
|
||||
|
||||
if (event.target.result.length == 0) {
|
||||
if (DEBUG) debug("MOZSETTINGS-GET-WARNING: " + data.name + " is not in the database.\n");
|
||||
}
|
||||
|
||||
let results = {};
|
||||
|
||||
for (let i in event.target.result) {
|
||||
let result = event.target.result[i];
|
||||
let name = result.settingName;
|
||||
if (DEBUG) debug(name + ": " + result.userValue +", " + result.defaultValue);
|
||||
let value = result.userValue !== undefined ? result.userValue : result.defaultValue;
|
||||
results[name] = value;
|
||||
}
|
||||
return defer.resolve({task: aTask, results: results});
|
||||
};
|
||||
getReq.onerror = function() {
|
||||
return defer.reject({task: aTask, error: getReq.error.name});
|
||||
};
|
||||
return promiseWrapper;
|
||||
},
|
||||
|
||||
taskSet: function(aTask) {
|
||||
let data = aTask.data;
|
||||
let lock = this.lockInfo[data.lockID];
|
||||
let keys = Object.getOwnPropertyNames(data.settings);
|
||||
|
||||
if (lock._failed) {
|
||||
if (DEBUG) debug("Lock failed. All subsequent requests will fail.");
|
||||
return Promise.reject({task: aTask, error: "Lock failed a permissions check, all requests now failing."});
|
||||
}
|
||||
|
||||
if (lock.hasCleared) {
|
||||
if (DEBUG) debug("Lock was used for a clear command. All subsequent requests will fail.");
|
||||
return Promise.reject({task: aTask, error: "Lock was used for a clear command. All other requests will fail."});
|
||||
}
|
||||
|
||||
lock.canClear = false;
|
||||
|
||||
// If we have no keys, resolve
|
||||
if (keys.length === 0) {
|
||||
if (DEBUG) debug("No keys to change entered!");
|
||||
return Promise.resolve({task: aTask});
|
||||
}
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
if (!SettingsPermissions.hasWritePermission(lock._mm, keys[i])) {
|
||||
if (DEBUG) debug("set not allowed on " + keys[i]);
|
||||
lock._failed = true;
|
||||
return Promise.reject({task: aTask, error: "No permission to set " + keys[i]});
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
let key = keys[i];
|
||||
if (DEBUG) debug("key: " + key + ", val: " + JSON.stringify(data.settings[key]) + ", type: " + typeof(data.settings[key]));
|
||||
lock.queuedSets[key] = data.settings[key];
|
||||
}
|
||||
|
||||
return this.queueTaskReturn(aTask, {task: aTask});
|
||||
},
|
||||
|
||||
queueConsume: function() {
|
||||
if (this.settingsLockQueue.length > 0 && this.lockInfo[this.settingsLockQueue[0]].consumable) {
|
||||
Services.tm.currentThread.dispatch(SettingsRequestManager.consumeTasks.bind(this), Ci.nsIThread.DISPATCH_NORMAL);
|
||||
}
|
||||
},
|
||||
|
||||
// Removes the current lock from the queue, and starts transactions for the
|
||||
// next lock, assuming there is one.
|
||||
removeCurrentLock: function() {
|
||||
let lock = this.settingsLockQueue.shift();
|
||||
delete this.lockInfo[lock.lockID];
|
||||
this.queueConsume();
|
||||
},
|
||||
|
||||
finalizeSets: function(aTask) {
|
||||
let data = aTask.data;
|
||||
if (DEBUG) debug("Finalizing tasks for lock " + data.lockID);
|
||||
let lock = this.lockInfo[data.lockID];
|
||||
lock.finalizing = true;
|
||||
if (lock._failed) {
|
||||
this.removeCurrentLock();
|
||||
return Promise.reject({task: aTask, error: "Lock failed a permissions check, all requests now failing."});
|
||||
}
|
||||
// If we have cleared, there is no reason to continue finalizing
|
||||
// this lock. Just resolve promise with task and move on.
|
||||
if (lock.hasCleared) {
|
||||
if (DEBUG) debug("Clear was called on lock, skipping finalize");
|
||||
this.removeCurrentLock();
|
||||
return Promise.resolve({task: aTask});
|
||||
}
|
||||
let keys = Object.getOwnPropertyNames(lock.queuedSets);
|
||||
if (keys.length === 0) {
|
||||
if (DEBUG) debug("Nothing to finalize. Exiting.");
|
||||
this.removeCurrentLock();
|
||||
return Promise.resolve({task: aTask});
|
||||
}
|
||||
|
||||
let store = lock.objectStore;
|
||||
if (!store) {
|
||||
if (DEBUG) debug("Rejecting Set task on lock " + aTask.data.lockID);
|
||||
return Promise.reject({task: aTask, error: "Cannot get object store"});
|
||||
}
|
||||
|
||||
// Due to the fact there may have multiple set operations to clear, and
|
||||
// they're all async, callbacks are gathered into promises, and the promises
|
||||
// are processed with Promises.all().
|
||||
let checkPromises = [];
|
||||
let finalValues = {};
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
let key = keys[i];
|
||||
if (DEBUG) debug("key: " + key + ", val: " + lock.queuedSets[key] + ", type: " + typeof(lock.queuedSets[key]));
|
||||
let checkDefer = {};
|
||||
let checkPromise = new Promise(function(resolve, reject) {
|
||||
checkDefer.resolve = resolve;
|
||||
checkDefer.reject = reject;
|
||||
});
|
||||
|
||||
// Get operation is used to fill in the default value, assuming there is
|
||||
// one. For the moment, if a value doesn't exist in the settings DB, we
|
||||
// allow the user to add it, and just pass back a null default value.
|
||||
let checkKeyRequest = store.get(key);
|
||||
checkKeyRequest.onsuccess = function (event) {
|
||||
let userValue = lock.queuedSets[key];
|
||||
let defaultValue;
|
||||
if (!event.target.result) {
|
||||
defaultValue = null;
|
||||
if (DEBUG) debug("MOZSETTINGS-GET-WARNING: " + key + " is not in the database.\n");
|
||||
} else {
|
||||
defaultValue = event.target.result.defaultValue;
|
||||
}
|
||||
let obj = {settingName: key, defaultValue: defaultValue, userValue: userValue};
|
||||
finalValues[key] = {defaultValue: defaultValue, userValue: userValue};
|
||||
let setReq = store.put(obj);
|
||||
setReq.onsuccess = function() {
|
||||
if (DEBUG) debug("Set successful!");
|
||||
if (DEBUG) debug("key: " + key + ", val: " + finalValues[key] + ", type: " + typeof(finalValues[key]));
|
||||
return checkDefer.resolve({task: aTask});
|
||||
};
|
||||
setReq.onerror = function() {
|
||||
return checkDefer.reject({task: aTask, error: setReq.error.name});
|
||||
};
|
||||
}.bind(this);
|
||||
checkKeyRequest.onerror = function(event) {
|
||||
return checkDefer.reject({task: aTask, error: checkKeyRequest.error.name});
|
||||
};
|
||||
checkPromises.push(checkPromise);
|
||||
}
|
||||
|
||||
let defer = {};
|
||||
let promiseWrapper = new Promise(function(resolve, reject) {
|
||||
defer.resolve = resolve;
|
||||
defer.reject = reject;
|
||||
});
|
||||
|
||||
// Once all transactions are done, or any have failed, remove the lock and
|
||||
// start processing the tasks from the next lock in the queue.
|
||||
Promise.all(checkPromises).then(function() {
|
||||
// If all commits were successful, notify observers
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
this.sendSettingsChange(keys[i], finalValues[keys[i]].userValue, lock.isServiceLock);
|
||||
}
|
||||
this.removeCurrentLock();
|
||||
defer.resolve({task: aTask});
|
||||
}.bind(this), function(ret) {
|
||||
this.removeCurrentLock();
|
||||
defer.reject({task: aTask, error: "Set transaction failure"});
|
||||
}.bind(this));
|
||||
return promiseWrapper;
|
||||
},
|
||||
|
||||
// Clear is only expected to be called via tests, and if a lock
|
||||
// calls clear, it should be the only thing the lock does. This
|
||||
// allows us to not have to deal with the possibility of query
|
||||
// integrity checking. Clear should never be called in the wild,
|
||||
// even by certified apps, which is why it has its own permission
|
||||
// (settings-clear).
|
||||
taskClear: function(aTask) {
|
||||
if (DEBUG) debug("Clearing");
|
||||
let data = aTask.data;
|
||||
let lock = this.lockInfo[data.lockID];
|
||||
|
||||
if (lock._failed) {
|
||||
if (DEBUG) debug("Lock failed, all requests now failing.");
|
||||
return Promise.reject({task: aTask, error: "Lock failed, all requests now failing."});
|
||||
}
|
||||
|
||||
if (!lock.canClear) {
|
||||
if (DEBUG) debug("Lock tried to clear after queuing other tasks. Failing.");
|
||||
lock._failed = true;
|
||||
return Promise.reject({task: aTask, error: "Cannot call clear after queuing other tasks, all requests now failing."});
|
||||
}
|
||||
|
||||
if (!SettingsPermissions.hasClearPermission(lock._mm)) {
|
||||
if (DEBUG) debug("clear not allowed");
|
||||
lock._failed = true;
|
||||
return Promise.reject({task: aTask, error: "No permission to clear DB"});
|
||||
}
|
||||
|
||||
lock.hasCleared = true;
|
||||
|
||||
let store = lock.objectStore;
|
||||
if (!store) {
|
||||
if (DEBUG) debug("Rejecting Clear task on lock " + aTask.data.lockID);
|
||||
return Promise.reject({task: aTask, error: "Cannot get object store"});
|
||||
}
|
||||
let defer = {};
|
||||
let promiseWrapper = new Promise(function(resolve, reject) {
|
||||
defer.resolve = resolve;
|
||||
defer.reject = reject;
|
||||
});
|
||||
|
||||
let clearReq = store.clear();
|
||||
clearReq.onsuccess = function() {
|
||||
return defer.resolve({task: aTask});
|
||||
};
|
||||
clearReq.onerror = function() {
|
||||
return defer.reject({task: aTask});
|
||||
};
|
||||
return promiseWrapper;
|
||||
},
|
||||
|
||||
ensureConnection : function() {
|
||||
if (DEBUG) debug("Ensuring Connection");
|
||||
let defer = {};
|
||||
let promiseWrapper = new Promise(function(resolve, reject) {
|
||||
defer.resolve = resolve;
|
||||
defer.reject = reject;
|
||||
});
|
||||
this.settingsDB.ensureDB(
|
||||
function() { defer.resolve(); },
|
||||
function(error) {
|
||||
if (DEBUG) debug("Cannot open Settings DB. Trying to open an old version?\n");
|
||||
defer.reject(error);
|
||||
}
|
||||
);
|
||||
return promiseWrapper;
|
||||
},
|
||||
|
||||
runTasks: function(aLockID) {
|
||||
if (DEBUG) debug("Running tasks for " + aLockID);
|
||||
let lock = this.lockInfo[aLockID];
|
||||
if (lock.finalizing) {
|
||||
debug("TASK TRYING TO QUEUE AFTER FINALIZE CALLED. THIS IS BAD. Lock: " + aLockID);
|
||||
return;
|
||||
}
|
||||
let currentTask = lock.tasks.shift();
|
||||
let promises = [];
|
||||
while (currentTask) {
|
||||
if (DEBUG) debug("Running Operation " + currentTask.operation);
|
||||
let p;
|
||||
switch (currentTask.operation) {
|
||||
case "get":
|
||||
p = this.taskGet(currentTask);
|
||||
break;
|
||||
case "set":
|
||||
p = this.taskSet(currentTask);
|
||||
break;
|
||||
case "clear":
|
||||
p = this.taskClear(currentTask);
|
||||
break;
|
||||
case "finalize":
|
||||
p = this.finalizeSets(currentTask);
|
||||
break;
|
||||
default:
|
||||
if (DEBUG) debug("Invalid operation: " + currentTask.operation);
|
||||
p.reject("Invalid operation: " + currentTask.operation);
|
||||
}
|
||||
p.then(function(ret) {
|
||||
ret.task.defer.resolve(ret.results);
|
||||
}.bind(currentTask), function(ret) {
|
||||
ret.task.defer.reject(ret.error);
|
||||
});
|
||||
promises.push(p);
|
||||
currentTask = lock.tasks.shift();
|
||||
}
|
||||
},
|
||||
|
||||
consumeTasks: function() {
|
||||
if (this.settingsLockQueue.length == 0) {
|
||||
if (DEBUG) debug("Nothing to run!");
|
||||
return;
|
||||
}
|
||||
|
||||
let lockID = this.settingsLockQueue[0];
|
||||
if (DEBUG) debug("Consuming tasks for " + lockID);
|
||||
let lock = this.lockInfo[lockID];
|
||||
|
||||
// If a process dies, we should clean up after it via the
|
||||
// child-process-shutdown event. But just in case we don't, we want to make
|
||||
// sure we never block on consuming.
|
||||
if (!lock) {
|
||||
if (DEBUG) debug("Lock not found");
|
||||
this.queueConsume();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!lock.consumable || lock.tasks.length === 0) {
|
||||
if (DEBUG) debug("No more tasks to run or not yet consuamble.");
|
||||
return;
|
||||
}
|
||||
|
||||
lock.consumable = false;
|
||||
this.ensureConnection().then(
|
||||
function(task) {
|
||||
this.runTasks(lockID);
|
||||
}.bind(this), function(ret) {
|
||||
dump("-*- SettingsRequestManager: SETTINGS DATABASE ERROR: Cannot make DB connection!\n");
|
||||
});
|
||||
},
|
||||
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
if (DEBUG) debug("observe");
|
||||
switch (aTopic) {
|
||||
case kXpcomShutdownObserverTopic:
|
||||
this.messages.forEach((function(msgName) {
|
||||
ppmm.removeMessageListener(msgName, this);
|
||||
}).bind(this));
|
||||
Services.obs.removeObserver(this, kXpcomShutdownObserverTopic);
|
||||
ppmm = null;
|
||||
break;
|
||||
default:
|
||||
if (DEBUG) debug("Wrong observer topic: " + aTopic);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
sendSettingsChange: function(aKey, aValue, aIsServiceLock) {
|
||||
this.broadcastMessage("Settings:Change:Return:OK",
|
||||
{ key: aKey, value: aValue });
|
||||
Services.obs.notifyObservers(this, kMozSettingsChangedObserverTopic,
|
||||
JSON.stringify({
|
||||
key: aKey,
|
||||
value: aValue,
|
||||
isInternalChange: aIsServiceLock
|
||||
}));
|
||||
},
|
||||
|
||||
broadcastMessage: function broadcastMessage(aMsgName, aContent) {
|
||||
if (DEBUG) debug("Broadcast");
|
||||
this.children.forEach(function(msgMgr) {
|
||||
if (SettingsPermissions.hasReadPermission(msgMgr, aContent.key)) {
|
||||
msgMgr.sendAsyncMessage(aMsgName, aContent);
|
||||
}
|
||||
});
|
||||
if (DEBUG) debug("Finished Broadcasting");
|
||||
},
|
||||
|
||||
addObserver: function(aMsgMgr) {
|
||||
if (DEBUG) debug("Add observer for" + aMsgMgr);
|
||||
if (this.children.indexOf(aMsgMgr) == -1) {
|
||||
this.children.push(aMsgMgr);
|
||||
}
|
||||
},
|
||||
|
||||
removeObserver: function(aMsgMgr) {
|
||||
if (DEBUG) debug("Remove observer for" + aMsgMgr);
|
||||
let index = this.children.indexOf(aMsgMgr);
|
||||
if (index != -1) {
|
||||
this.children.splice(index, 1);
|
||||
}
|
||||
},
|
||||
|
||||
removeMessageManager: function(aMsgMgr){
|
||||
if (DEBUG) debug("Removing message manager " + aMsgMgr);
|
||||
this.removeObserver(aMsgMgr);
|
||||
SettingsPermissions.removeManager(aMsgMgr);
|
||||
let closedLockIDs = [];
|
||||
let lockIDs = Object.keys(this.lockInfo);
|
||||
for (let i in lockIDs) {
|
||||
if (this.lockInfo[lockIDs[i]]._mm == aMsgMgr) {
|
||||
if (DEBUG) debug("Removing lock " + lockIDs[i] + " due to process close/crash");
|
||||
closedLockIDs.push(lockIDs[i]);
|
||||
}
|
||||
}
|
||||
for (let i in closedLockIDs) {
|
||||
let transaction = this.lockInfo[closedLockIDs[i]]._transaction;
|
||||
if (transaction) {
|
||||
transaction.abort();
|
||||
}
|
||||
delete this.lockInfo[closedLockIDs[i]];
|
||||
let index = this.settingsLockQueue.indexOf(closedLockIDs[i]);
|
||||
if (index > -1) {
|
||||
this.settingsLockQueue.splice(index, 1);
|
||||
}
|
||||
// If index is 0, the lock we just removed was at the head of
|
||||
// the queue, so possibly queue the next lock if it's
|
||||
// consumable.
|
||||
if (index == 0) {
|
||||
this.queueConsume();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
receiveMessage: function(aMessage) {
|
||||
if (DEBUG) debug("receiveMessage " + aMessage.name);
|
||||
|
||||
let msg = aMessage.data;
|
||||
let mm = aMessage.target;
|
||||
|
||||
function returnMessage(name, data) {
|
||||
try {
|
||||
mm.sendAsyncMessage(name, data);
|
||||
} catch (e) {
|
||||
if (DEBUG) debug("Return message failed, " + name);
|
||||
}
|
||||
}
|
||||
|
||||
// For all message types that expect a lockID, we check to make
|
||||
// sure that we're accessing a lock that's part of our process. If
|
||||
// not, consider it a security violation and kill the app. Killing
|
||||
// based on creating a colliding lock ID happens as part of
|
||||
// CreateLock check below.
|
||||
switch (aMessage.name) {
|
||||
case "Settings:Get":
|
||||
case "Settings:Set":
|
||||
case "Settings:Clear":
|
||||
case "Settings:Run":
|
||||
case "Settings:Finalize":
|
||||
if (!msg.lockID ||
|
||||
!this.lockInfo[msg.lockID] ||
|
||||
mm != this.lockInfo[msg.lockID]._mm) {
|
||||
Cu.reportError("Process trying to access settings lock from another process. Killing.");
|
||||
// Kill the app by checking for a non-existent permission
|
||||
aMessage.target.assertPermission("message-manager-mismatch-kill");
|
||||
return;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (aMessage.name) {
|
||||
case "child-process-shutdown":
|
||||
if (DEBUG) debug("Child process shutdown received.");
|
||||
this.removeMessageManager(mm);
|
||||
break;
|
||||
case "Settings:RegisterForMessages":
|
||||
SettingsPermissions.addManager(aMessage);
|
||||
if (!SettingsPermissions.hasSomeReadPermission(mm)) {
|
||||
Cu.reportError("Settings message " + aMessage.name +
|
||||
" from a content process with no 'settings-api-read' privileges.");
|
||||
// Kill app after reporting error
|
||||
SettingsPermissions.assertSomeReadPermission(mm);
|
||||
return;
|
||||
}
|
||||
this.addObserver(mm);
|
||||
break;
|
||||
case "Settings:UnregisterForMessages":
|
||||
this.removeObserver(mm);
|
||||
break;
|
||||
case "Settings:CreateLock":
|
||||
if (DEBUG) debug("Received CreateLock for " + msg.lockID);
|
||||
// If we try to create a lock ID that collides with one
|
||||
// already in the system, consider it a security violation and
|
||||
// kill.
|
||||
if (msg.lockID in this.settingsLockQueue) {
|
||||
Cu.reportError("Trying to queue a lock with the same ID as an already queued lock. Killing app.");
|
||||
aMessage.target.assertPermission("lock-id-duplicate-kill");
|
||||
return;
|
||||
}
|
||||
this.settingsLockQueue.push(msg.lockID);
|
||||
SettingsPermissions.addManager(aMessage);
|
||||
this.lockInfo[msg.lockID] = SettingsLockInfo(this.settingsDB, mm, msg.lockID, msg.isServiceLock);
|
||||
break;
|
||||
case "Settings:Get":
|
||||
if (DEBUG) debug("Received getRequest");
|
||||
this.queueTask("get", msg).then(function(settings) {
|
||||
returnMessage("Settings:Get:OK", {
|
||||
lockID: msg.lockID,
|
||||
requestID: msg.requestID,
|
||||
settings: settings
|
||||
});
|
||||
}, function(error) {
|
||||
if (DEBUG) debug("getRequest FAILED " + msg.name);
|
||||
returnMessage("Settings:Get:KO", {
|
||||
lockID: msg.lockID,
|
||||
requestID: msg.requestID,
|
||||
errorMsg: error
|
||||
});
|
||||
});
|
||||
break;
|
||||
case "Settings:Set":
|
||||
if (DEBUG) debug("Received Set Request");
|
||||
this.queueTask("set", msg).then(function(settings) {
|
||||
returnMessage("Settings:Set:OK", {
|
||||
lockID: msg.lockID,
|
||||
requestID: msg.requestID
|
||||
});
|
||||
}, function(error) {
|
||||
returnMessage("Settings:Set:KO", {
|
||||
lockID: msg.lockID,
|
||||
requestID: msg.requestID,
|
||||
errorMsg: error
|
||||
});
|
||||
});
|
||||
break;
|
||||
case "Settings:Clear":
|
||||
if (DEBUG) debug("Received Clear Request");
|
||||
this.queueTask("clear", msg).then(function() {
|
||||
returnMessage("Settings:Clear:OK", {
|
||||
lockID: msg.lockID,
|
||||
requestID: msg.requestID
|
||||
});
|
||||
}, function(error) {
|
||||
returnMessage("Settings:Clear:KO", {
|
||||
lockID: msg.lockID,
|
||||
requestID: msg.requestID,
|
||||
errorMsg: error
|
||||
});
|
||||
});
|
||||
break;
|
||||
case "Settings:Finalize":
|
||||
if (DEBUG) debug("Received Finalize");
|
||||
this.queueTask("finalize", msg).then(function() {
|
||||
returnMessage("Settings:Finalize:OK", {
|
||||
lockID: msg.lockID
|
||||
});
|
||||
}, function(error) {
|
||||
returnMessage("Settings:Finalize:KO", {
|
||||
lockID: msg.lockID,
|
||||
errorMsg: error
|
||||
});
|
||||
});
|
||||
// YES THIS IS SUPPOSED TO FALL THROUGH. Finalize is considered a task
|
||||
// running situation, but it also needs to queue a task.
|
||||
case "Settings:Run":
|
||||
if (DEBUG) debug("Received Run");
|
||||
this.lockInfo[msg.lockID].consumable = true;
|
||||
if (msg.lockID == this.settingsLockQueue[0]) {
|
||||
// If a lock is currently at the head of the queue, run all tasks for
|
||||
// it.
|
||||
if (DEBUG) debug("Running tasks for " + msg.lockID);
|
||||
this.queueConsume();
|
||||
} else {
|
||||
// If a lock isn't at the head of the queue, but requests to be run,
|
||||
// simply mark it as consumable, which means it will automatically run
|
||||
// once it comes to the head of the queue.
|
||||
if (DEBUG) debug("Queuing tasks for " + msg.lockID + " while waiting for " + this.settingsLockQueue[0]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (DEBUG) debug("Wrong message: " + aMessage.name);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
SettingsRequestManager.init();
|
|
@ -5,24 +5,20 @@
|
|||
"use strict"
|
||||
|
||||
/* static functions */
|
||||
const DEBUG = false;
|
||||
function debug(s) {
|
||||
dump("-*- SettingsService: " + s + "\n");
|
||||
}
|
||||
let DEBUG = 0;
|
||||
let debug;
|
||||
if (DEBUG)
|
||||
debug = function (s) { dump("-*- SettingsService: " + s + "\n"); }
|
||||
else
|
||||
debug = function (s) {}
|
||||
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/SettingsQueue.jsm");
|
||||
Cu.import("resource://gre/modules/SettingsDB.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import('resource://gre/modules/SettingsRequestManager.jsm');
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
|
||||
"@mozilla.org/uuid-generator;1",
|
||||
"nsIUUIDGenerator");
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
|
||||
"@mozilla.org/childprocessmessagemanager;1",
|
||||
"nsIMessageSender");
|
||||
|
||||
const nsIClassInfo = Ci.nsIClassInfo;
|
||||
|
||||
|
@ -30,149 +26,18 @@ const SETTINGSSERVICELOCK_CONTRACTID = "@mozilla.org/settingsServiceLock;1";
|
|||
const SETTINGSSERVICELOCK_CID = Components.ID("{d7a395a0-e292-11e1-834e-1761d57f5f99}");
|
||||
const nsISettingsServiceLock = Ci.nsISettingsServiceLock;
|
||||
|
||||
function makeSettingsServiceRequest(aCallback, aName, aValue) {
|
||||
return {
|
||||
callback: aCallback,
|
||||
name: aName,
|
||||
value: aValue
|
||||
};
|
||||
};
|
||||
|
||||
function SettingsServiceLock(aSettingsService, aTransactionCallback) {
|
||||
function SettingsServiceLock(aSettingsService, aTransactionCallback)
|
||||
{
|
||||
if (DEBUG) debug("settingsServiceLock constr!");
|
||||
this._open = true;
|
||||
this._busy = false;
|
||||
this._requests = new Queue();
|
||||
this._settingsService = aSettingsService;
|
||||
this._id = uuidgen.generateUUID().toString();
|
||||
this._transaction = null;
|
||||
this._transactionCallback = aTransactionCallback;
|
||||
this._requests = {};
|
||||
let closeHelper = function() {
|
||||
if (DEBUG) debug("closing lock " + this._id);
|
||||
this._open = false;
|
||||
this.runOrFinalizeQueries();
|
||||
}.bind(this);
|
||||
|
||||
let msgs = ["Settings:Get:OK", "Settings:Get:KO",
|
||||
"Settings:Clear:OK", "Settings:Clear:KO",
|
||||
"Settings:Set:OK", "Settings:Set:KO",
|
||||
"Settings:Finalize:OK", "Settings:Finalize:KO"];
|
||||
|
||||
for (let msg in msgs) {
|
||||
cpmm.addMessageListener(msgs[msg], this);
|
||||
}
|
||||
|
||||
cpmm.sendAsyncMessage("Settings:CreateLock", {lockID: this._id, isServiceLock: true}, undefined, Services.scriptSecurityManager.getSystemPrincipal());
|
||||
Services.tm.currentThread.dispatch(closeHelper, Ci.nsIThread.DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
SettingsServiceLock.prototype = {
|
||||
get closed() {
|
||||
return !this._open;
|
||||
},
|
||||
|
||||
runOrFinalizeQueries: function() {
|
||||
if (!this._requests || Object.keys(this._requests).length == 0) {
|
||||
cpmm.sendAsyncMessage("Settings:Finalize", {lockID: this._id}, undefined, Services.scriptSecurityManager.getSystemPrincipal());
|
||||
} else {
|
||||
cpmm.sendAsyncMessage("Settings:Run", {lockID: this._id}, undefined, Services.scriptSecurityManager.getSystemPrincipal());
|
||||
}
|
||||
},
|
||||
|
||||
receiveMessage: function(aMessage) {
|
||||
if (DEBUG) debug("receiveMessage: " + aMessage.name);
|
||||
let msg = aMessage.data;
|
||||
// SettingsRequestManager broadcasts changes to all locks in the child. If
|
||||
// our lock isn't being addressed, just return.
|
||||
if(msg.lockID != this._id) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Finalizing a transaction does not return a request ID since we are
|
||||
// supposed to fire callbacks.
|
||||
if (!msg.requestID) {
|
||||
switch (aMessage.name) {
|
||||
case "Settings:Finalize:OK":
|
||||
if (DEBUG) debug("Lock finalize ok!");
|
||||
this.callTransactionHandle();
|
||||
break;
|
||||
case "Settings:Finalize:KO":
|
||||
if (DEBUG) debug("Lock finalize failed!");
|
||||
this.callAbort();
|
||||
break;
|
||||
default:
|
||||
if (DEBUG) debug("Message type " + aMessage.name + " is missing a requestID");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let req = this._requests[msg.requestID];
|
||||
if (!req) {
|
||||
if (DEBUG) debug("Matching request not found.");
|
||||
return;
|
||||
}
|
||||
delete this._requests[msg.requestID];
|
||||
switch (aMessage.name) {
|
||||
case "Settings:Get:OK":
|
||||
this._open = true;
|
||||
let settings_names = Object.keys(msg.settings);
|
||||
if (settings_names.length > 0) {
|
||||
let name = settings_names[0];
|
||||
if (DEBUG && settings_names.length > 1) {
|
||||
debug("Warning: overloaded setting:" + name);
|
||||
}
|
||||
let result = msg.settings[name];
|
||||
this.callHandle(req.callback, name, result);
|
||||
} else {
|
||||
this.callHandle(req.callback, req.name, null);
|
||||
}
|
||||
this._open = false;
|
||||
break;
|
||||
case "Settings:Set:OK":
|
||||
this._open = true;
|
||||
// We don't pass values back from sets in SettingsManager...
|
||||
this.callHandle(req.callback, req.name, req.value);
|
||||
this._open = false;
|
||||
break;
|
||||
case "Settings:Get:KO":
|
||||
case "Settings:Set:KO":
|
||||
if (DEBUG) debug("error:" + msg.errorMsg);
|
||||
this.callError(req.callback, msg.error);
|
||||
break;
|
||||
default:
|
||||
if (DEBUG) debug("Wrong message: " + aMessage.name);
|
||||
}
|
||||
this.runOrFinalizeQueries();
|
||||
},
|
||||
|
||||
get: function get(aName, aCallback) {
|
||||
if (DEBUG) debug("get (" + this._id + "): " + aName);
|
||||
if (!this._open) {
|
||||
dump("Settings lock not open!\n");
|
||||
throw Components.results.NS_ERROR_ABORT;
|
||||
}
|
||||
let reqID = uuidgen.generateUUID().toString();
|
||||
this._requests[reqID] = makeSettingsServiceRequest(aCallback, aName);
|
||||
cpmm.sendAsyncMessage("Settings:Get", {requestID: reqID,
|
||||
lockID: this._id,
|
||||
name: aName},
|
||||
undefined,
|
||||
Services.scriptSecurityManager.getSystemPrincipal());
|
||||
},
|
||||
|
||||
set: function set(aName, aValue, aCallback) {
|
||||
if (DEBUG) debug("set: " + aName + " " + aValue);
|
||||
if (!this._open) {
|
||||
throw "Settings lock not open";
|
||||
}
|
||||
let reqID = uuidgen.generateUUID().toString();
|
||||
this._requests[reqID] = makeSettingsServiceRequest(aCallback, aName, aValue);
|
||||
let settings = {};
|
||||
settings[aName] = aValue;
|
||||
cpmm.sendAsyncMessage("Settings:Set", {requestID: reqID,
|
||||
lockID: this._id,
|
||||
settings: settings},
|
||||
undefined,
|
||||
Services.scriptSecurityManager.getSystemPrincipal());
|
||||
},
|
||||
|
||||
callHandle: function callHandle(aCallback, aName, aValue) {
|
||||
try {
|
||||
|
@ -206,6 +71,149 @@ SettingsServiceLock.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
process: function process() {
|
||||
debug("process!");
|
||||
let lock = this;
|
||||
lock._open = false;
|
||||
let store = lock._transaction.objectStore(SETTINGSSTORE_NAME);
|
||||
|
||||
while (!lock._requests.isEmpty()) {
|
||||
if (lock._isBusy) {
|
||||
return;
|
||||
}
|
||||
let info = lock._requests.dequeue();
|
||||
if (DEBUG) debug("info:" + info.intent);
|
||||
let callback = info.callback;
|
||||
let name = info.name;
|
||||
switch (info.intent) {
|
||||
case "set":
|
||||
let value = info.value;
|
||||
let message = info.message;
|
||||
if(DEBUG && typeof(value) == 'object') {
|
||||
debug("object name:" + name + ", val: " + JSON.stringify(value));
|
||||
}
|
||||
lock._isBusy = true;
|
||||
let checkKeyRequest = store.get(name);
|
||||
|
||||
checkKeyRequest.onsuccess = function (event) {
|
||||
let defaultValue;
|
||||
if (event.target.result) {
|
||||
defaultValue = event.target.result.defaultValue;
|
||||
} else {
|
||||
defaultValue = null;
|
||||
if (DEBUG) debug("MOZSETTINGS-SET-WARNING: " + name + " is not in the database.\n");
|
||||
}
|
||||
let setReq = store.put({ settingName: name, defaultValue: defaultValue, userValue: value });
|
||||
|
||||
setReq.onsuccess = function() {
|
||||
lock._isBusy = false;
|
||||
lock._open = true;
|
||||
lock.callHandle(callback, name, value);
|
||||
Services.obs.notifyObservers(lock, "mozsettings-changed", JSON.stringify({
|
||||
key: name,
|
||||
value: value,
|
||||
message: message
|
||||
}));
|
||||
lock._open = false;
|
||||
lock.process();
|
||||
};
|
||||
|
||||
setReq.onerror = function(event) {
|
||||
lock._isBusy = false;
|
||||
lock.callError(callback, event.target.errorMessage);
|
||||
lock.process();
|
||||
};
|
||||
}
|
||||
|
||||
checkKeyRequest.onerror = function(event) {
|
||||
lock._isBusy = false;
|
||||
lock.callError(callback, event.target.errorMessage);
|
||||
lock.process();
|
||||
};
|
||||
break;
|
||||
case "get":
|
||||
let getReq = store.mozGetAll(name);
|
||||
getReq.onsuccess = function(event) {
|
||||
if (DEBUG) {
|
||||
debug("Request successful. Record count:" + event.target.result.length);
|
||||
debug("result: " + JSON.stringify(event.target.result));
|
||||
}
|
||||
this._open = true;
|
||||
if (callback) {
|
||||
if (event.target.result[0]) {
|
||||
if (event.target.result.length > 1) {
|
||||
if (DEBUG) debug("Warning: overloaded setting:" + name);
|
||||
}
|
||||
let result = event.target.result[0];
|
||||
let value = result.userValue !== undefined
|
||||
? result.userValue
|
||||
: result.defaultValue;
|
||||
lock.callHandle(callback, name, value);
|
||||
} else {
|
||||
lock.callHandle(callback, name, null);
|
||||
}
|
||||
} else {
|
||||
if (DEBUG) debug("no callback defined!");
|
||||
}
|
||||
this._open = false;
|
||||
}.bind(lock);
|
||||
getReq.onerror = function error(event) {
|
||||
lock.callError(callback, event.target.errorMessage);
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
lock._open = true;
|
||||
},
|
||||
|
||||
createTransactionAndProcess: function() {
|
||||
if (this._settingsService._settingsDB._db) {
|
||||
let lock;
|
||||
while (lock = this._settingsService._locks.dequeue()) {
|
||||
if (!lock._transaction) {
|
||||
lock._transaction = lock._settingsService._settingsDB._db.transaction(SETTINGSSTORE_NAME, "readwrite");
|
||||
if (lock._transactionCallback) {
|
||||
lock._transaction.oncomplete = lock.callTransactionHandle.bind(lock);
|
||||
lock._transaction.onabort = function(event) {
|
||||
let message = '';
|
||||
if (event.target.error) {
|
||||
message = event.target.error.name + ': ' + event.target.error.message;
|
||||
}
|
||||
this.callAbort(lock._transactionCallback.handleAbort, message);
|
||||
};
|
||||
}
|
||||
}
|
||||
if (!lock._isBusy) {
|
||||
lock.process();
|
||||
} else {
|
||||
this._settingsService._locks.enqueue(lock);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!this._requests.isEmpty() && !this._isBusy) {
|
||||
this.process();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
get: function get(aName, aCallback) {
|
||||
if (DEBUG) debug("get: " + aName + ", " + aCallback);
|
||||
this._requests.enqueue({ callback: aCallback, intent:"get", name: aName });
|
||||
this.createTransactionAndProcess();
|
||||
},
|
||||
|
||||
set: function set(aName, aValue, aCallback, aMessage) {
|
||||
debug("set: " + aName + ": " + JSON.stringify(aValue));
|
||||
if (aMessage === undefined)
|
||||
aMessage = null;
|
||||
this._requests.enqueue({ callback: aCallback,
|
||||
intent: "set",
|
||||
name: aName,
|
||||
value: this._settingsService._settingsDB.prepareValue(aValue),
|
||||
message: aMessage });
|
||||
this.createTransactionAndProcess();
|
||||
},
|
||||
|
||||
classID : SETTINGSSERVICELOCK_CID,
|
||||
QueryInterface : XPCOMUtils.generateQI([nsISettingsServiceLock])
|
||||
};
|
||||
|
@ -214,18 +222,34 @@ const SETTINGSSERVICE_CID = Components.ID("{f656f0c0-f776-11e1-a21f-08002
|
|||
|
||||
function SettingsService()
|
||||
{
|
||||
if (DEBUG) debug("settingsService Constructor");
|
||||
debug("settingsService Constructor");
|
||||
this._locks = new Queue();
|
||||
this._settingsDB = new SettingsDB();
|
||||
this._settingsDB.init();
|
||||
}
|
||||
|
||||
SettingsService.prototype = {
|
||||
|
||||
nextTick: function nextTick(aCallback, thisObj) {
|
||||
if (thisObj)
|
||||
aCallback = aCallback.bind(thisObj);
|
||||
|
||||
Services.tm.currentThread.dispatch(aCallback, Ci.nsIThread.DISPATCH_NORMAL);
|
||||
},
|
||||
|
||||
createLock: function createLock(aCallback) {
|
||||
var lock = new SettingsServiceLock(this, aCallback);
|
||||
this._locks.enqueue(lock);
|
||||
this._settingsDB.ensureDB(
|
||||
function() { lock.createTransactionAndProcess(); },
|
||||
function() { dump("SettingsService failed to open DB!\n"); }
|
||||
);
|
||||
this.nextTick(function() { this._open = false; }, lock);
|
||||
return lock;
|
||||
},
|
||||
|
||||
classID : SETTINGSSERVICE_CID,
|
||||
QueryInterface : XPCOMUtils.generateQI([Ci.nsISettingsService])
|
||||
};
|
||||
}
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SettingsService, SettingsServiceLock]);
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SettingsService, SettingsServiceLock])
|
||||
|
|
|
@ -18,8 +18,9 @@ if CONFIG['MOZ_B2G']:
|
|||
MOCHITEST_CHROME_MANIFESTS += ['tests/chrome.ini']
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
'SettingsChangeNotifier.jsm',
|
||||
'SettingsDB.jsm',
|
||||
'SettingsRequestManager.jsm'
|
||||
'SettingsQueue.jsm',
|
||||
]
|
||||
|
||||
MOCHITEST_MANIFESTS += ['tests/mochitest.ini']
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
skip-if = (toolkit == 'gonk' && debug) || e10s #debug-only failure, bug 932878
|
||||
|
||||
[test_settings_basics.html]
|
||||
[test_settings_permissions.html]
|
||||
[test_settings_blobs.html]
|
||||
[test_settings_data_uris.html]
|
||||
[test_settings_events.html]
|
||||
|
|
|
@ -22,14 +22,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id={678695}
|
|||
"use strict";
|
||||
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/SettingsRequestManager.jsm");
|
||||
SpecialPowers.Cu.import("resource://gre/modules/SettingsChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
SpecialPowers.addPermission("settings-api-read", true, document);
|
||||
SpecialPowers.addPermission("settings-api-write", true, document);
|
||||
SpecialPowers.addPermission("settings-read", true, document);
|
||||
SpecialPowers.addPermission("settings-write", true, document);
|
||||
SpecialPowers.addPermission("settings-clear", true, document);
|
||||
|
||||
function onUnwantedSuccess() {
|
||||
ok(false, "onUnwantedSuccess: shouldn't get here");
|
||||
|
@ -327,11 +324,8 @@ var steps = [
|
|||
req = lock.clear();
|
||||
req.onsuccess = function () {
|
||||
ok(true, "Deleted the database");
|
||||
next();
|
||||
};
|
||||
},
|
||||
function () {
|
||||
var lock = mozSettings.createLock();
|
||||
req.onerror = onFailure;
|
||||
req2 = lock.set(wifi);
|
||||
req2.onsuccess = function () {
|
||||
ok(true, "set done");
|
||||
|
@ -340,23 +334,23 @@ var steps = [
|
|||
|
||||
ok(true, "Get all settings");
|
||||
var lock2 = mozSettings.createLock();
|
||||
req3 = lock2.get("*");
|
||||
req3.onsuccess = function () {
|
||||
is(Object.keys(req3.result).length, 1, "length 1");
|
||||
check(req3.result, wifi);
|
||||
ok(true, JSON.stringify(req3.result));
|
||||
req = lock2.get("*");
|
||||
req.onsuccess = function () {
|
||||
is(Object.keys(req.result).length, 1, "length 1");
|
||||
check(wifi, req.result);
|
||||
ok(true, JSON.stringify(req.result));
|
||||
ok(true, "Get all settings Done");
|
||||
};
|
||||
req3.onerror = onFailure;
|
||||
req.onerror = onFailure;
|
||||
|
||||
req4 = lock2.get("net3g.apn");
|
||||
req4.onsuccess = function () {
|
||||
is(Object.keys(req4.result).length, 1, "length 1");
|
||||
check(wifi, req4.result);
|
||||
req2 = lock2.get("net3g.apn");
|
||||
req2.onsuccess = function () {
|
||||
is(Object.keys(req2.result).length, 1, "length 1");
|
||||
check(wifi, req2.result);
|
||||
ok(true, "Get net3g.apn Done");
|
||||
next();
|
||||
};
|
||||
req4.onerror = onFailure;
|
||||
req2.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Change wifi1");
|
||||
|
@ -473,8 +467,8 @@ var steps = [
|
|||
req5.onerror = onFailure;
|
||||
}
|
||||
|
||||
var lock6 = mozSettings.createLock();
|
||||
req6 = lock6.clear();
|
||||
var lock5 = mozSettings.createLock();
|
||||
req6 = lock5.clear();
|
||||
req6.onsuccess = function () {
|
||||
ok(true, "Deleted the database");
|
||||
next();
|
||||
|
@ -505,20 +499,18 @@ var steps = [
|
|||
|
||||
req = lock.get("wifi.enabled");
|
||||
req.onsuccess = function() {
|
||||
check(this.result, wifiEnabled);
|
||||
check(req.result, wifiEnabled);
|
||||
ok(true, "Test2 locking result done");
|
||||
}
|
||||
req.onerror = onFailure;
|
||||
|
||||
var lock2 = mozSettings.createLock();
|
||||
req2 = lock2.clear();
|
||||
req2 = lock.clear();
|
||||
req2.onsuccess = function () {
|
||||
ok(true, "Deleted the database");
|
||||
};
|
||||
req2.onerror = onFailure;
|
||||
|
||||
var lock3 = mozSettings.createLock();
|
||||
req3 = lock3.set(wifi);
|
||||
req3 = lock.set(wifi);
|
||||
req3.onsuccess = function () {
|
||||
ok(true, "set done");
|
||||
next();
|
||||
|
@ -769,24 +761,6 @@ var steps = [
|
|||
};
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Call success callback when transaction commits");
|
||||
var lock = mozSettings.createLock();
|
||||
lock.onsettingstransactionsuccess = function () {
|
||||
next();
|
||||
};
|
||||
req = lock.set({"setting-obj": {foo: {bar: 23}}});
|
||||
req.onsuccess = function() {
|
||||
req2 = lock.get("setting-obj");
|
||||
req2.onsuccess = function(event) {
|
||||
var result = event.target.result["setting-obj"];
|
||||
ok(result, "Got valid result");
|
||||
ok(typeof result == "object", "Result is object");
|
||||
ok("foo" in result && "bar" in result.foo, "Result has properties");
|
||||
ok(result.foo.bar == 23, "Result properties are set");
|
||||
};
|
||||
};
|
||||
},
|
||||
function () {
|
||||
ok(true, "all done!\n");
|
||||
SimpleTest.finish();
|
||||
|
|
|
@ -22,14 +22,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=821630
|
|||
"use strict";
|
||||
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/SettingsRequestManager.jsm");
|
||||
SpecialPowers.Cu.import("resource://gre/modules/SettingsChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
SpecialPowers.addPermission("settings-read", true, document);
|
||||
SpecialPowers.addPermission("settings-write", true, document);
|
||||
SpecialPowers.addPermission("settings-api-read", true, document);
|
||||
SpecialPowers.addPermission("settings-api-write", true, document);
|
||||
SpecialPowers.addPermission("settings-clear", true, document);
|
||||
|
||||
function onUnwantedSuccess() {
|
||||
ok(false, "onUnwantedSuccess: shouldn't get here");
|
||||
|
|
|
@ -22,14 +22,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=806374
|
|||
"use strict";
|
||||
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/SettingsRequestManager.jsm");
|
||||
SpecialPowers.Cu.import("resource://gre/modules/SettingsChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
SpecialPowers.addPermission("settings-read", true, document);
|
||||
SpecialPowers.addPermission("settings-write", true, document);
|
||||
SpecialPowers.addPermission("settings-api-read", true, document);
|
||||
SpecialPowers.addPermission("settings-api-write", true, document);
|
||||
SpecialPowers.addPermission("settings-clear", true, document);
|
||||
|
||||
function onUnwantedSuccess() {
|
||||
ok(false, "onUnwantedSuccess: shouldn't get here");
|
||||
|
|
|
@ -32,16 +32,8 @@ e = new MozSettingsEvent("settingchanged", {settingName: "a", settingValue: true
|
|||
is(e.settingName, "a", "Name should be a.");
|
||||
is(e.settingValue, true, "Value should be true.");
|
||||
|
||||
var e = new MozSettingsTransactionEvent("settingtransactionsuccess", {});
|
||||
ok(e, "Should have settings event!");
|
||||
is(e.error, "", "error should be null");
|
||||
|
||||
var e = new MozSettingsTransactionEvent("settingtransactionfailure", {error: "Test error."});
|
||||
ok(e, "Should have settings event!");
|
||||
is(e.error, "Test error.", "error should be 'Test error.'");
|
||||
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
|
@ -32,10 +32,7 @@ function testPref() {
|
|||
|
||||
SpecialPowers.pushPermissions([
|
||||
{type: "settings-read", allow: 0, context: document},
|
||||
{type: "settings-write", allow: 0, context: document},
|
||||
{type: "settings-api-read", allow: 0, context: document},
|
||||
{type: "settings-api-write", allow: 0, context: document},
|
||||
{type: "settings-clear", allow: 0, context: document}
|
||||
{type: "settings-write", allow: 0, context: document}
|
||||
], function() {
|
||||
ise(frames[0].navigator.mozSettings, null, "navigator.mozSettings is null when the page doesn't have permissions");
|
||||
testPref();
|
||||
|
|
|
@ -22,14 +22,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=678695
|
|||
"use strict";
|
||||
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/SettingsRequestManager.jsm");
|
||||
SpecialPowers.Cu.import("resource://gre/modules/SettingsChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
SpecialPowers.addPermission("settings-write", true, document);
|
||||
SpecialPowers.addPermission("settings-read", true, document);
|
||||
SpecialPowers.addPermission("settings-api-read", true, document);
|
||||
SpecialPowers.addPermission("settings-api-write", true, document);
|
||||
SpecialPowers.addPermission("settings-clear", true, document);
|
||||
|
||||
var screenBright = {"screen.brightness": 0.7};
|
||||
|
||||
|
@ -92,12 +89,8 @@ var steps = [
|
|||
req = lock.clear();
|
||||
req.onsuccess = function () {
|
||||
ok(true, "Deleted the database");
|
||||
next();
|
||||
};
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
var lock = mozSettings.createLock();
|
||||
req2 = lock.set(screenBright);
|
||||
req2.onsuccess = function () {
|
||||
ok(true, "set done");
|
||||
|
@ -274,19 +267,16 @@ var steps = [
|
|||
req = lock.clear();
|
||||
req.onsuccess = function () {
|
||||
ok(true, "Deleted the database");
|
||||
next();
|
||||
navigator.mozSettings.onsettingchange = onComplexSettingschangeWithNext;
|
||||
};
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
var lock = mozSettings.createLock();
|
||||
navigator.mozSettings.onsettingchange = onComplexSettingschangeWithNext;
|
||||
req2 = navigator.mozSettings.createLock().set({'test.key': cset});
|
||||
req2.onsuccess = function () {
|
||||
ok(true, "set done");
|
||||
}
|
||||
req2.onerror = onFailure;
|
||||
},
|
||||
|
||||
function () {
|
||||
ok(true, "all done!\n");
|
||||
SimpleTest.finish();
|
||||
|
|
|
@ -1,203 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id={678695}
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug {678695} 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={900551}">Mozilla Bug {900551}</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
"use strict";
|
||||
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/SettingsRequestManager.jsm");
|
||||
}
|
||||
|
||||
SpecialPowers.removePermission("settings-read", document);
|
||||
SpecialPowers.removePermission("settings-write", document);
|
||||
SpecialPowers.addPermission("settings-api-read", true, document);
|
||||
SpecialPowers.addPermission("settings-api-write", true, document);
|
||||
SpecialPowers.addPermission("settings:wallpaper.image-read", true, document);
|
||||
SpecialPowers.addPermission("settings:wallpaper.image-write", true, document);
|
||||
SpecialPowers.addPermission("settings-clear", true, document);
|
||||
|
||||
function onUnwantedSuccess() {
|
||||
ok(false, "onUnwantedSuccess: shouldn't get here");
|
||||
}
|
||||
|
||||
function onFailure() {
|
||||
ok(false, "in on Failure!");
|
||||
}
|
||||
|
||||
const wifi = {"wifi.enabled": false}
|
||||
const wallpaper = {"wallpaper.image": "test-image"};
|
||||
|
||||
var combination = {
|
||||
"wifi.enabled": false,
|
||||
"wallpaper.image": "test-image"
|
||||
}
|
||||
|
||||
function equals(o1, o2) {
|
||||
var k1 = Object.keys(o1).sort();
|
||||
var k2 = Object.keys(o2).sort();
|
||||
if (k1.length != k2.length) return false;
|
||||
return k1.zip(k2, function(keyPair) {
|
||||
if(typeof o1[keyPair[0]] == typeof o2[keyPair[1]] == "object"){
|
||||
return equals(o1[keyPair[0]], o2[keyPair[1]])
|
||||
} else {
|
||||
return o1[keyPair[0]] == o2[keyPair[1]];
|
||||
}
|
||||
}).all();
|
||||
};
|
||||
|
||||
function observer1(setting) {
|
||||
is(setting.settingName, "screen.brightness", "Same settingName");
|
||||
is(setting.settingValue, "0.7", "Same settingvalue");
|
||||
};
|
||||
|
||||
function onsettingschangeWithNext(event) {
|
||||
is(event.settingName, "screen.brightness", "Same settingName");
|
||||
is(event.settingValue, "0.7", "Same settingvalue");
|
||||
next();
|
||||
};
|
||||
|
||||
function check(o1, o2) {
|
||||
is(JSON.stringify(o1), JSON.stringify(o2), "same");
|
||||
}
|
||||
|
||||
var req, req2, req3, req4, req5, req6;
|
||||
var index = 0;
|
||||
|
||||
var mozSettings = navigator.mozSettings;
|
||||
|
||||
var steps = [
|
||||
// Can't delete database here since that requires permissions we don't want
|
||||
// to give the page.
|
||||
function () {
|
||||
ok(true, "Setting wallpaper");
|
||||
var lock = mozSettings.createLock();
|
||||
req = lock.set(wallpaper);
|
||||
req.onsuccess = function () {
|
||||
ok(true, "set done");
|
||||
}
|
||||
req.onerror = onFailure;
|
||||
|
||||
var lock2 = mozSettings.createLock();
|
||||
req2 = lock2.get("wallpaper.image");
|
||||
req2.onsuccess = function () {
|
||||
is(Object.keys(req2.result).length, 1, "length 1");
|
||||
check(wallpaper, req2.result);
|
||||
ok(true, "Get wallpaper Done");
|
||||
next();
|
||||
};
|
||||
req2.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Get Wifi");
|
||||
var lock = mozSettings.createLock();
|
||||
req = lock.get("wifi.enabled");
|
||||
req.onerror = function () {
|
||||
ok(true, "get failed (expected)");
|
||||
next();
|
||||
}
|
||||
req.onsuccess = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Set Wifi");
|
||||
var lock = mozSettings.createLock();
|
||||
req = lock.set(wifi);
|
||||
req.onerror = function () {
|
||||
ok(true, "set failed (expected)");
|
||||
next();
|
||||
}
|
||||
req.onsuccess = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Set combination (1 valid 1 not valid)");
|
||||
var lock = mozSettings.createLock();
|
||||
req = lock.set(combination);
|
||||
req.onerror = function () {
|
||||
ok(true, "set failed (expected)");
|
||||
next();
|
||||
}
|
||||
req.onsuccess = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "All requests on a failed lock should fail");
|
||||
var lock = mozSettings.createLock();
|
||||
lock.onsettingstransactionfailure = function (evt) {
|
||||
ok(evt.error == "Lock failed a permissions check, all requests now failing.", "transaction failure on permissions error message correct.");
|
||||
ok(true, "transaction failed (expected) ");
|
||||
next();
|
||||
};
|
||||
lock.onsettingstransactionsuccess = onFailure;
|
||||
|
||||
req = lock.set(wifi);
|
||||
req.onerror = function () {
|
||||
ok(true, "set failed (expected)");
|
||||
}
|
||||
req.onsuccess = onFailure;
|
||||
req2 = lock.get("wallpaper.image");
|
||||
req2.onerror = function () {
|
||||
ok(true, "get failed (expected)");
|
||||
}
|
||||
req2.onsuccess = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Set combination (1 valid 1 not valid)");
|
||||
var lock = mozSettings.createLock();
|
||||
req = lock.set(combination);
|
||||
req.onerror = function () {
|
||||
ok(true, "set failed (expected)");
|
||||
next();
|
||||
}
|
||||
req.onsuccess = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Set combination (1 valid 1 not valid)");
|
||||
var lock = mozSettings.createLock();
|
||||
req = lock.set(combination);
|
||||
req.onerror = function () {
|
||||
ok(true, "set failed (expected)");
|
||||
next();
|
||||
}
|
||||
req.onsuccess = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "all done!\n");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
];
|
||||
|
||||
function next() {
|
||||
ok(true, "Begin!");
|
||||
if (index >= steps.length) {
|
||||
ok(false, "Shouldn't get here!");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
steps[index]();
|
||||
} catch(ex) {
|
||||
ok(false, "Caught exception", ex);
|
||||
}
|
||||
index += 1;
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(next);
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -4,10 +4,6 @@ const Cu = Components.utils;
|
|||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/SettingsRequestManager.jsm");
|
||||
}
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
|
@ -84,6 +80,8 @@ let tests = [
|
|||
const TEST_OBSERVER_VALUE = true;
|
||||
const TEST_OBSERVER_MESSAGE = "test.observer.message";
|
||||
|
||||
let observerCount = 2;
|
||||
|
||||
function observer(subject, topic, data) {
|
||||
|
||||
if (topic !== MOZSETTINGS_CHANGED) {
|
||||
|
@ -92,6 +90,7 @@ let tests = [
|
|||
}
|
||||
|
||||
data = JSON.parse(data);
|
||||
|
||||
function checkProp(name, type, value) {
|
||||
ok(name in data, "data." + name + " is present");
|
||||
is(typeof data[name], type, "data." + name + " is " + type);
|
||||
|
@ -100,16 +99,24 @@ let tests = [
|
|||
|
||||
checkProp("key", "string", TEST_OBSERVER_KEY);
|
||||
checkProp("value", "boolean", TEST_OBSERVER_VALUE);
|
||||
checkProp("isInternalChange", "boolean", true);
|
||||
if (observerCount === 2) {
|
||||
checkProp("message", "object", null);
|
||||
} else {
|
||||
checkProp("message", "string", TEST_OBSERVER_MESSAGE);
|
||||
}
|
||||
--observerCount;
|
||||
|
||||
if (observerCount === 0) {
|
||||
Services.obs.removeObserver(this, MOZSETTINGS_CHANGED);
|
||||
next();
|
||||
}
|
||||
}
|
||||
|
||||
Services.obs.addObserver(observer, MOZSETTINGS_CHANGED, false);
|
||||
|
||||
let lock = SettingsService.createLock();
|
||||
lock.set(TEST_OBSERVER_KEY, TEST_OBSERVER_VALUE, null);
|
||||
lock.set(TEST_OBSERVER_KEY, TEST_OBSERVER_VALUE, null, null);
|
||||
lock.set(TEST_OBSERVER_KEY, TEST_OBSERVER_VALUE, null, TEST_OBSERVER_MESSAGE);
|
||||
}
|
||||
];
|
||||
|
||||
|
|
|
@ -3314,14 +3314,16 @@ RadioInterface.prototype = {
|
|||
* Set the setting value of "time.clock.automatic-update.available".
|
||||
*/
|
||||
setClockAutoUpdateAvailable: function(value) {
|
||||
gSettingsService.createLock().set(kSettingsClockAutoUpdateAvailable, value, null);
|
||||
gSettingsService.createLock().set(kSettingsClockAutoUpdateAvailable, value, null,
|
||||
"fromInternalSetting");
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the setting value of "time.timezone.automatic-update.available".
|
||||
*/
|
||||
setTimezoneAutoUpdateAvailable: function(value) {
|
||||
gSettingsService.createLock().set(kSettingsTimezoneAutoUpdateAvailable, value, null);
|
||||
gSettingsService.createLock().set(kSettingsTimezoneAutoUpdateAvailable, value, null,
|
||||
"fromInternalSetting");
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -3497,7 +3499,7 @@ RadioInterface.prototype = {
|
|||
switch (topic) {
|
||||
case kMozSettingsChangedObserverTopic:
|
||||
let setting = JSON.parse(data);
|
||||
this.handleSettingsChange(setting.key, setting.value, setting.isInternalChange);
|
||||
this.handleSettingsChange(setting.key, setting.value, setting.message);
|
||||
break;
|
||||
case kSysClockChangeObserverTopic:
|
||||
let offset = parseInt(data, 10);
|
||||
|
@ -3578,11 +3580,11 @@ RadioInterface.prototype = {
|
|||
// ICC's mcc-mnc.
|
||||
_lastKnownHomeNetwork: null,
|
||||
|
||||
handleSettingsChange: function(aName, aResult, aIsInternalSetting) {
|
||||
handleSettingsChange: function(aName, aResult, aMessage) {
|
||||
// Don't allow any content processes to modify the setting
|
||||
// "time.clock.automatic-update.available" except for the chrome process.
|
||||
if (aName === kSettingsClockAutoUpdateAvailable &&
|
||||
!aIsInternalSetting) {
|
||||
aMessage !== "fromInternalSetting") {
|
||||
let isClockAutoUpdateAvailable = this._lastNitzMessage !== null ||
|
||||
this._sntp.isAvailable();
|
||||
if (aResult !== isClockAutoUpdateAvailable) {
|
||||
|
@ -3598,7 +3600,7 @@ RadioInterface.prototype = {
|
|||
// "time.timezone.automatic-update.available" except for the chrome
|
||||
// process.
|
||||
if (aName === kSettingsTimezoneAutoUpdateAvailable &&
|
||||
!aIsInternalSetting) {
|
||||
aMessage !== "fromInternalSetting") {
|
||||
let isTimezoneAutoUpdateAvailable = this._lastNitzMessage !== null;
|
||||
if (aResult !== isTimezoneAutoUpdateAvailable) {
|
||||
if (DEBUG) {
|
||||
|
|
|
@ -706,8 +706,6 @@ var interfaceNamesInGlobalScope =
|
|||
{name: "mozRTCSessionDescription", pref: "media.peerconnection.enabled"},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"MozSettingsEvent",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "MozSettingsTransactionEvent", permission: "settings-api-read"},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"MozSmsEvent",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
|
|
|
@ -26,16 +26,12 @@ resume_geolocationProvider(function() {
|
|||
});
|
||||
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/SettingsRequestManager.jsm");
|
||||
SpecialPowers.Cu.import("resource://gre/modules/SettingsChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
function test1() {
|
||||
//This pushPermissions call is after pushPrefEnv call and pushPrefEnv calls follow after this
|
||||
SpecialPowers.pushPermissions([{'type': 'settings-read', 'allow': true, 'context': document},
|
||||
{'type': 'settings-write', 'allow': true, 'context': document},
|
||||
{'type': 'settings-api-write', 'allow': true, 'context': document},
|
||||
{'type': 'settings-api-read', 'allow': true, 'context': document}
|
||||
], test2);
|
||||
SpecialPowers.pushPermissions([{'type': 'settings-read', 'allow': true, 'context': document}, {'type': 'settings-write', 'allow': true, 'context': document}], test2);
|
||||
}
|
||||
|
||||
function test2() {
|
||||
|
|
|
@ -26,16 +26,12 @@ resume_geolocationProvider(function() {
|
|||
});
|
||||
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/SettingsRequestManager.jsm");
|
||||
SpecialPowers.Cu.import("resource://gre/modules/SettingsChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
function test1() {
|
||||
//This pushPermissions call is after pushPrefEnv call and pushPrefEnv calls follow after this
|
||||
SpecialPowers.pushPermissions([{'type': 'settings-read', 'allow': true, 'context': document},
|
||||
{'type': 'settings-write', 'allow': true, 'context': document},
|
||||
{'type': 'settings-api-write', 'allow': true, 'context': document},
|
||||
{'type': 'settings-api-read', 'allow': true, 'context': document}
|
||||
], test2);
|
||||
SpecialPowers.pushPermissions([{'type': 'settings-read', 'allow': true, 'context': document}, {'type': 'settings-write', 'allow': true, 'context': document}], test2);
|
||||
}
|
||||
|
||||
var watchId;
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
[Constructor(DOMString type, optional MozSettingsTransactionEventInit eventInitDict),
|
||||
CheckPermissions="settings-api-read settings-api-write"]
|
||||
interface MozSettingsTransactionEvent : Event
|
||||
{
|
||||
readonly attribute DOMString? error;
|
||||
};
|
||||
|
||||
dictionary MozSettingsTransactionEventInit : EventInit
|
||||
{
|
||||
DOMString error = "";
|
||||
};
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
[JSImplementation="@mozilla.org/settingsLock;1",
|
||||
Pref="dom.mozSettings.enabled"]
|
||||
interface SettingsLock : EventTarget {
|
||||
interface SettingsLock {
|
||||
// Whether this lock is invalid
|
||||
readonly attribute boolean closed;
|
||||
|
||||
|
@ -17,8 +17,6 @@ interface SettingsLock : EventTarget {
|
|||
DOMRequest get(DOMString name);
|
||||
|
||||
DOMRequest clear();
|
||||
attribute EventHandler onsettingstransactionsuccess;
|
||||
attribute EventHandler onsettingstransactionfailure;
|
||||
};
|
||||
|
||||
dictionary SettingChange {
|
||||
|
|
|
@ -658,7 +658,6 @@ GENERATED_EVENTS_WEBIDL_FILES = [
|
|||
'MozMmsEvent.webidl',
|
||||
'MozOtaStatusEvent.webidl',
|
||||
'MozSettingsEvent.webidl',
|
||||
'MozSettingsTransactionEvent.webidl',
|
||||
'MozSmsEvent.webidl',
|
||||
'MozStkCommandEvent.webidl',
|
||||
'PageTransitionEvent.webidl',
|
||||
|
|
|
@ -3511,7 +3511,8 @@ WifiWorker.prototype = {
|
|||
handleError: function(aErrorMessage) {
|
||||
self.requestDone();
|
||||
}
|
||||
});
|
||||
},
|
||||
"fromInternalSetting");
|
||||
},
|
||||
|
||||
notifyTetheringOff: function notifyTetheringOff() {
|
||||
|
@ -3531,7 +3532,8 @@ WifiWorker.prototype = {
|
|||
handleError: function(aErrorMessage) {
|
||||
self.requestDone();
|
||||
}
|
||||
});
|
||||
},
|
||||
"fromInternalSetting");
|
||||
},
|
||||
|
||||
handleWifiEnabled: function(enabled) {
|
||||
|
@ -3607,7 +3609,7 @@ WifiWorker.prototype = {
|
|||
let setting = JSON.parse(data);
|
||||
// To avoid WifiWorker setting the wifi again, don't need to deal with
|
||||
// the "mozsettings-changed" event fired from internal setting.
|
||||
if (setting.isInternalChange) {
|
||||
if (setting.message && setting.message === "fromInternalSetting") {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче