зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1098343 (part 1) - support 'sticky' preferences, meaning a user value is retained even when it matches the default. r=bsmedberg
This commit is contained in:
Родитель
d196e4dd52
Коммит
bb1c4b905b
|
@ -140,7 +140,8 @@ static nsresult pref_DoCallback(const char* changed_pref);
|
|||
|
||||
enum {
|
||||
kPrefSetDefault = 1,
|
||||
kPrefForceSet = 2
|
||||
kPrefForceSet = 2,
|
||||
kPrefStickyDefault = 4,
|
||||
};
|
||||
static nsresult pref_HashPref(const char *key, PrefValue value, PrefType type, uint32_t flags);
|
||||
|
||||
|
@ -340,7 +341,8 @@ pref_savePref(PLDHashTable *table, PLDHashEntryHdr *heh, uint32_t i, void *arg)
|
|||
(pref_ValueChanged(pref->defaultPref,
|
||||
pref->userPref,
|
||||
(PrefType) PREF_TYPE(pref)) ||
|
||||
!(pref->flags & PREF_HAS_DEFAULT))) {
|
||||
!(pref->flags & PREF_HAS_DEFAULT) ||
|
||||
pref->flags & PREF_STICKY_DEFAULT)) {
|
||||
sourcePref = &pref->userPref;
|
||||
} else {
|
||||
if (argData->saveTypes == SAVE_ALL_AND_DEFAULTS) {
|
||||
|
@ -778,6 +780,8 @@ nsresult pref_HashPref(const char *key, PrefValue value, PrefType type, uint32_t
|
|||
{
|
||||
pref_SetValue(&pref->defaultPref, &pref->flags, value, type);
|
||||
pref->flags |= PREF_HAS_DEFAULT;
|
||||
if (flags & kPrefStickyDefault)
|
||||
pref->flags |= PREF_STICKY_DEFAULT;
|
||||
if (!PREF_HAS_USER_VALUE(pref))
|
||||
valueChanged = true;
|
||||
}
|
||||
|
@ -787,9 +791,11 @@ nsresult pref_HashPref(const char *key, PrefValue value, PrefType type, uint32_t
|
|||
}
|
||||
else
|
||||
{
|
||||
/* If new value is same as the default value, then un-set the user value.
|
||||
/* If new value is same as the default value and it's not a "sticky"
|
||||
pref, then un-set the user value.
|
||||
Otherwise, set the user value only if it has changed */
|
||||
if ((pref->flags & PREF_HAS_DEFAULT) &&
|
||||
!(pref->flags & PREF_STICKY_DEFAULT) &&
|
||||
!pref_ValueChanged(pref->defaultPref, value, type) &&
|
||||
!(flags & kPrefForceSet))
|
||||
{
|
||||
|
@ -1002,7 +1008,12 @@ void PREF_ReaderCallback(void *closure,
|
|||
const char *pref,
|
||||
PrefValue value,
|
||||
PrefType type,
|
||||
bool isDefault)
|
||||
bool isDefault,
|
||||
bool isStickyDefault)
|
||||
{
|
||||
pref_HashPref(pref, value, type, isDefault ? kPrefSetDefault : kPrefForceSet);
|
||||
uint32_t flags = isDefault ? kPrefSetDefault : kPrefForceSet;
|
||||
if (isDefault && isStickyDefault) {
|
||||
flags |= kPrefStickyDefault;
|
||||
}
|
||||
pref_HashPref(pref, value, type, flags);
|
||||
}
|
||||
|
|
|
@ -61,6 +61,8 @@ typedef enum { PREF_INVALID = 0,
|
|||
PREF_LOCKED = 1, PREF_USERSET = 2, PREF_CONFIG = 4, PREF_REMOTE = 8,
|
||||
PREF_LILOCAL = 16, PREF_STRING = 32, PREF_INT = 64, PREF_BOOL = 128,
|
||||
PREF_HAS_DEFAULT = 256,
|
||||
// pref is default pref with "sticky" semantics
|
||||
PREF_STICKY_DEFAULT = 512,
|
||||
PREF_VALUETYPE_MASK = (PREF_STRING | PREF_INT | PREF_BOOL)
|
||||
} PrefType;
|
||||
|
||||
|
@ -183,7 +185,8 @@ void PREF_ReaderCallback( void *closure,
|
|||
const char *pref,
|
||||
PrefValue value,
|
||||
PrefType type,
|
||||
bool isDefault);
|
||||
bool isDefault,
|
||||
bool isStickyDefault);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ enum {
|
|||
|
||||
static const char kUserPref[] = "user_pref";
|
||||
static const char kPref[] = "pref";
|
||||
static const char kPrefSticky[] = "sticky_pref";
|
||||
static const char kTrue[] = "true";
|
||||
static const char kFalse[] = "false";
|
||||
|
||||
|
@ -129,7 +130,8 @@ pref_DoCallback(PrefParseState *ps)
|
|||
default:
|
||||
break;
|
||||
}
|
||||
(*ps->reader)(ps->closure, ps->lb, value, ps->vtype, ps->fdefault);
|
||||
(*ps->reader)(ps->closure, ps->lb, value, ps->vtype, ps->fdefault,
|
||||
ps->fstickydefault);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -188,6 +190,7 @@ PREF_ParseBuf(PrefParseState *ps, const char *buf, int bufLen)
|
|||
ps->vb = nullptr;
|
||||
ps->vtype = PREF_INVALID;
|
||||
ps->fdefault = false;
|
||||
ps->fstickydefault = false;
|
||||
}
|
||||
switch (c) {
|
||||
case '/': /* begin comment block or line? */
|
||||
|
@ -198,7 +201,9 @@ PREF_ParseBuf(PrefParseState *ps, const char *buf, int bufLen)
|
|||
break;
|
||||
case 'u': /* indicating user_pref */
|
||||
case 'p': /* indicating pref */
|
||||
ps->smatch = (c == 'u' ? kUserPref : kPref);
|
||||
case 's': /* indicating sticky_pref */
|
||||
ps->smatch = (c == 'u' ? kUserPref :
|
||||
(c == 's' ? kPrefSticky : kPref));
|
||||
ps->sindex = 1;
|
||||
ps->nextstate = PREF_PARSE_UNTIL_OPEN_PAREN;
|
||||
state = PREF_PARSE_MATCH_STRING;
|
||||
|
@ -242,7 +247,8 @@ PREF_ParseBuf(PrefParseState *ps, const char *buf, int bufLen)
|
|||
/* name parsing */
|
||||
case PREF_PARSE_UNTIL_NAME:
|
||||
if (c == '\"' || c == '\'') {
|
||||
ps->fdefault = (ps->smatch == kPref);
|
||||
ps->fdefault = (ps->smatch == kPref || ps->smatch == kPrefSticky);
|
||||
ps->fstickydefault = (ps->smatch == kPrefSticky);
|
||||
ps->quotechar = c;
|
||||
ps->nextstate = PREF_PARSE_UNTIL_COMMA; /* return here when done */
|
||||
state = PREF_PARSE_QUOTED_STRING;
|
||||
|
|
|
@ -26,12 +26,15 @@ extern "C" {
|
|||
* preference type (PREF_STRING, PREF_INT, or PREF_BOOL)
|
||||
* @param defPref
|
||||
* preference type (true: default, false: user preference)
|
||||
* @param stickyPref
|
||||
* default preference marked as a "sticky" pref
|
||||
*/
|
||||
typedef void (*PrefReader)(void *closure,
|
||||
const char *pref,
|
||||
PrefValue val,
|
||||
PrefType type,
|
||||
bool defPref);
|
||||
bool defPref,
|
||||
bool stickyPref);
|
||||
|
||||
/* structure fields are private */
|
||||
typedef struct PrefParseState {
|
||||
|
@ -52,6 +55,7 @@ typedef struct PrefParseState {
|
|||
char *vb; /* value buffer (ptr into lb) */
|
||||
PrefType vtype; /* PREF_STRING,INT,BOOL */
|
||||
bool fdefault; /* true if (default) pref */
|
||||
bool fstickydefault; /* true if (sticky) pref */
|
||||
} PrefParseState;
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
pref("testPref.unsticky.bool", true);
|
||||
sticky_pref("testPref.sticky.bool", false);
|
|
@ -0,0 +1,5 @@
|
|||
// testPrefSticky.js defined this pref as a sticky_pref(). Once a sticky
|
||||
// pref has been changed, it's written as a user_pref().
|
||||
// So this test file reflects that scenario.
|
||||
// Note the default in testPrefSticky.js is also false.
|
||||
user_pref("testPref.sticky.bool", false);
|
|
@ -0,0 +1,170 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/licenses/publicdomain/ */
|
||||
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const ps = Services.prefs;
|
||||
|
||||
// Once we fetch the profile directory the xpcshell test harness will send
|
||||
// a profile-before-change notification at shutdown. This causes the prefs
|
||||
// service to flush the prefs file - and the prefs file it uses ends up being
|
||||
// testPrefSticky*.js in the test dir. This upsets things in confusing ways :)
|
||||
// We avoid this by ensuring our "temp" prefs.js is the current prefs file.
|
||||
do_get_profile();
|
||||
do_register_cleanup(saveAndReload);
|
||||
|
||||
// A little helper to reset the service and load some pref files
|
||||
function resetAndLoad(filenames) {
|
||||
ps.resetPrefs();
|
||||
for (let filename of filenames) {
|
||||
ps.readUserPrefs(do_get_file(filename));
|
||||
}
|
||||
}
|
||||
|
||||
// A little helper that saves the current state to a file in the profile
|
||||
// dir, then resets the service and re-reads the file it just saved.
|
||||
// Used to test what gets actually written - things the pref service decided
|
||||
// not to write don't exist at all after this call.
|
||||
function saveAndReload() {
|
||||
let file = do_get_profile();
|
||||
file.append("prefs.js");
|
||||
ps.savePrefFile(file);
|
||||
|
||||
// Now reset the pref service and re-read what we saved.
|
||||
ps.resetPrefs();
|
||||
ps.readUserPrefs(file);
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
// A sticky pref should not be written if the value is unchanged.
|
||||
add_test(function notWrittenWhenUnchanged() {
|
||||
resetAndLoad(["data/testPrefSticky.js"]);
|
||||
Assert.strictEqual(ps.getBoolPref("testPref.unsticky.bool"), true);
|
||||
Assert.strictEqual(ps.getBoolPref("testPref.sticky.bool"), false);
|
||||
|
||||
// write prefs - but we haven't changed the sticky one, so it shouldn't be written.
|
||||
saveAndReload();
|
||||
// sticky should not have been written to the new file.
|
||||
try {
|
||||
ps.getBoolPref("testPref.sticky.bool");
|
||||
Assert.ok(false, "expected failure reading this pref");
|
||||
} catch (ex) {
|
||||
Assert.ok(ex, "exception reading regular pref");
|
||||
}
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
// Loading a sticky_pref then a user_pref for the same pref means it should
|
||||
// always be written.
|
||||
add_test(function writtenOnceLoadedWithoutChange() {
|
||||
// Load the same pref file *as well as* a pref file that has a user_pref for
|
||||
// our sticky with the default value. It should be re-written without us
|
||||
// touching it.
|
||||
resetAndLoad(["data/testPrefSticky.js", "data/testPrefStickyUser.js"]);
|
||||
// reset and re-read what we just wrote - it should be written.
|
||||
saveAndReload();
|
||||
Assert.strictEqual(ps.getBoolPref("testPref.sticky.bool"), false,
|
||||
"user_pref was written with default value");
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
// If a sticky pref is explicicitly changed, even to the default, it is written.
|
||||
add_test(function writtenOnceLoadedWithChangeNonDefault() {
|
||||
// Load the same pref file *as well as* a pref file that has a user_pref for
|
||||
// our sticky - then change the pref. It should be written.
|
||||
resetAndLoad(["data/testPrefSticky.js", "data/testPrefStickyUser.js"]);
|
||||
// Set a new val and check we wrote it.
|
||||
ps.setBoolPref("testPref.sticky.bool", false);
|
||||
saveAndReload();
|
||||
Assert.strictEqual(ps.getBoolPref("testPref.sticky.bool"), false,
|
||||
"user_pref was written with custom value");
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
// If a sticky pref is changed to the non-default value, it is written.
|
||||
add_test(function writtenOnceLoadedWithChangeNonDefault() {
|
||||
// Load the same pref file *as well as* a pref file that has a user_pref for
|
||||
// our sticky - then change the pref. It should be written.
|
||||
resetAndLoad(["data/testPrefSticky.js", "data/testPrefStickyUser.js"]);
|
||||
// Set a new val and check we wrote it.
|
||||
ps.setBoolPref("testPref.sticky.bool", true);
|
||||
saveAndReload();
|
||||
Assert.strictEqual(ps.getBoolPref("testPref.sticky.bool"), true,
|
||||
"user_pref was written with custom value");
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
// Test that prefHasUserValue always returns true whenever there is a sticky
|
||||
// value, even when that value matches the default. This is mainly for
|
||||
// about:config semantics - prefs with a sticky value always remain bold and
|
||||
// always offer "reset" (which fully resets and drops the sticky value as if
|
||||
// the pref had never changed.)
|
||||
add_test(function hasUserValue() {
|
||||
// sticky pref without user value.
|
||||
resetAndLoad(["data/testPrefSticky.js"]);
|
||||
Assert.strictEqual(ps.getBoolPref("testPref.sticky.bool"), false);
|
||||
Assert.ok(!ps.prefHasUserValue("testPref.sticky.bool"),
|
||||
"should not initially reflect a user value");
|
||||
|
||||
ps.setBoolPref("testPref.sticky.bool", false);
|
||||
Assert.ok(ps.prefHasUserValue("testPref.sticky.bool"),
|
||||
"should reflect a user value after set to default");
|
||||
|
||||
ps.setBoolPref("testPref.sticky.bool", true);
|
||||
Assert.ok(ps.prefHasUserValue("testPref.sticky.bool"),
|
||||
"should reflect a user value after change to non-default");
|
||||
|
||||
ps.clearUserPref("testPref.sticky.bool");
|
||||
Assert.ok(!ps.prefHasUserValue("testPref.sticky.bool"),
|
||||
"should reset to no user value");
|
||||
ps.setBoolPref("testPref.sticky.bool", false, "expected default");
|
||||
|
||||
// And make sure the pref immediately reflects a user value after load.
|
||||
resetAndLoad(["data/testPrefSticky.js", "data/testPrefStickyUser.js"]);
|
||||
Assert.strictEqual(ps.getBoolPref("testPref.sticky.bool"), false);
|
||||
Assert.ok(ps.prefHasUserValue("testPref.sticky.bool"),
|
||||
"should have a user value when loaded value is the default");
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
// Test that clearUserPref removes the "sticky" value.
|
||||
add_test(function clearUserPref() {
|
||||
// load things such that we have a sticky value which is the same as the
|
||||
// default.
|
||||
resetAndLoad(["data/testPrefSticky.js", "data/testPrefStickyUser.js"]);
|
||||
ps.clearUserPref("testPref.sticky.bool");
|
||||
|
||||
// Once we save prefs the sticky pref should no longer be written.
|
||||
saveAndReload();
|
||||
try {
|
||||
ps.getBoolPref("testPref.sticky.bool");
|
||||
Assert.ok(false, "expected failure reading this pref");
|
||||
} catch (ex) {
|
||||
Assert.ok(ex, "pref doesn't have a sticky value");
|
||||
}
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
// Test that a pref observer gets a notification fired when a sticky pref
|
||||
// has it's value changed to the same value as the default. The reason for
|
||||
// this behaviour is that later we might have other code that cares about a
|
||||
// pref being sticky (IOW, we notify due to the "state" of the pref changing
|
||||
// even if the value has not)
|
||||
add_test(function observerFires() {
|
||||
// load things so there's no sticky value.
|
||||
resetAndLoad(["data/testPrefSticky.js"]);
|
||||
|
||||
function observe(subject, topic, data) {
|
||||
Assert.equal(data, "testPref.sticky.bool");
|
||||
ps.removeObserver("testPref.sticky.bool", observe);
|
||||
run_next_test();
|
||||
}
|
||||
ps.addObserver("testPref.sticky.bool", observe, false);
|
||||
|
||||
ps.setBoolPref("testPref.sticky.bool", ps.getBoolPref("testPref.sticky.bool"));
|
||||
// and the observer will fire triggering the next text.
|
||||
});
|
|
@ -11,6 +11,8 @@ support-files =
|
|||
[test_bug506224.js]
|
||||
[test_bug577950.js]
|
||||
[test_bug790374.js]
|
||||
[test_stickyprefs.js]
|
||||
support-files = data/testPrefSticky.js data/testPrefStickyUser.js
|
||||
[test_changeType.js]
|
||||
[test_dirtyPrefs.js]
|
||||
[test_extprefs.js]
|
||||
|
|
Загрузка…
Ссылка в новой задаче