Commit a0a4c4f7 authored by Dmitry Smirnov's avatar Dmitry Smirnov

Merge tag 'upstream/2.4.24'

Upstream version 2.4.24

# gpg: enabled debug flags: memstat
# gpg: Signature made Thu 26 Jul 2018 01:58:50 AEST
# gpg:                using RSA key 50BC7CF939D20C272A6B065652B6BBD953968D1B
# gpg: Good signature from "Dmitry Smirnov <onlyjob@member.fsf.org>" [ultimate]
# gpg:                 aka "Dmitry Smirnov <onlyjob@cpan.org>" [ultimate]
# gpg:                 aka "Dmitry Smirnov <onlyjob@debian.org>" [ultimate]
# gpg: keydb: handles=2 locks=0 parse=0 get=2
# gpg:        build=0 update=0 insert=0 delete=0
# gpg:        reset=0 found=2 not=0 cache=0 not=0
# gpg: kid_not_found_cache: count=0 peak=0 flushes=0
# gpg: sig_cache: total=16 cached=16 good=16 bad=0
# gpg: random usage: poolsize=600 mixed=0 polls=0/0 added=0/0
#               outmix=0 getlvl1=0/0 getlvl2=0/0
# gpg: rndjent stat: collector=0x0000000000000000 calls=0 bytes=0
# gpg: secmem usage: 0/65536 bytes in 0 blocks
parents 485a4924 672c004e
This diff is collapsed.
No preview for this file type
Signature-Version: 1.0
MD5-Digest-Manifest: MVKy1cqkZKQV+Dews26NuQ==
SHA1-Digest-Manifest: i+1qPscG0YmUCixyC8tVcFjUeKI=
MD5-Digest-Manifest: Y7V+bBaEda7q4AeIl5hgpQ==
SHA1-Digest-Manifest: NyM8/NQUfmZDmjaBEwIicz1HAmY=
SHA256-Digest-Manifest: HMzIqsEjU3c4Y1vlJV/SIv0xX+8jUkAePxxJLASNoTY=
# Notes for translators
There are some helper utilities for translations.
* [ChromeExtensionI18nHelper for Sublime Text plug-in](https://github.com/Harurow/sublime_chromeextensioni18nhelper): it is designed for Google Chrome extensions, but it is also available WebExtensions-based Firefox addons.
* [web-ext-translator](https://www.npmjs.com/package/web-ext-translator): a Java-based editor for translators.
If you want to know changes in the main `en` locale, you can compare the latest code with any specific version. For example, changes from the version 2.4.8 is: [https://github.com/piroor/treestyletab/compare/2.4.8...master](https://github.com/piroor/treestyletab/compare/2.4.8...master)
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -11,9 +11,11 @@
<script type="application/javascript" src="/common/Configs.js"></script>
<script type="application/javascript" src="/common/common.js"></script>
<script type="application/javascript" src="/common/xpath.js"></script>
<script type="application/javascript" src="/common/TabIdFixer.js"></script>
<script type="application/javascript" src="/common/TabFavIconHelper.js"></script>
<script type="application/javascript" src="/common/MetricsData.js"></script>
<script type="application/javascript" src="/common/permissions.js"></script>
<script type="application/javascript" src="/common/RichConfirm.js"></script>
<script type="application/javascript" src="/common/tree/base.js"></script>
<script type="application/javascript" src="/common/tree/api-tabs.js"></script>
<script type="application/javascript" src="/common/tree/get-tabs.js"></script>
......@@ -22,6 +24,7 @@
<script type="application/javascript" src="/common/tree/cache.js"></script>
<script type="application/javascript" src="/common/tree/contextual-identities.js"></script>
<script type="application/javascript" src="/common/tree/handlers.js"></script>
<script type="application/javascript" src="/common/commands.js"></script>
<script type="application/javascript" src="/background/background.js"></script>
<script type="application/javascript" src="/background/cache.js"></script>
<script type="application/javascript" src="/background/handlers.js"></script>
......
This diff is collapsed.
......@@ -7,28 +7,28 @@
async function restoreWindowFromEffectiveWindowCache(aWindowId, aOptions = {}) {
gMetricsData.add('restoreWindowFromEffectiveWindowCache start');
log('restoreWindowFromEffectiveWindowCache start');
logForCache('restoreWindowFromEffectiveWindowCache start');
var owner = aOptions.owner || getWindowCacheOwner(aWindowId);
if (!owner) {
log('restoreWindowFromEffectiveWindowCache fail: no owner');
logForCache('restoreWindowFromEffectiveWindowCache fail: no owner');
return false;
}
cancelReservedCacheTree(aWindowId); // prevent to break cache before loading
var tabs = aOptions.tabs || await browser.tabs.query({ windowId: aWindowId });
log('restoreWindowFromEffectiveWindowCache tabs: ', tabs);
var apiTabs = aOptions.tabs || await browser.tabs.query({ windowId: aWindowId });
logForCache('restoreWindowFromEffectiveWindowCache tabs: ', apiTabs);
var [actualSignature, cache] = await Promise.all([
getWindowSignature(tabs),
getWindowSignature(apiTabs),
getWindowCache(owner, kWINDOW_STATE_CACHED_TABS)
]);
var cachedSignature = cache && cache.signature;
log(`restoreWindowFromEffectiveWindowCache: got from the owner ${owner}`, {
logForCache(`restoreWindowFromEffectiveWindowCache: got from the owner ${owner}`, {
cachedSignature, cache
});
if (cache &&
cache.tabs &&
cachedSignature &&
cachedSignature != signatureFromTabsCache(cache.tabs)) {
log(`restoreWindowFromEffectiveWindowCache: cache for ${aWindowId} is broken.`, {
logForCache(`restoreWindowFromEffectiveWindowCache: cache for ${aWindowId} is broken.`, {
signature: cachedSignature,
cache: signatureFromTabsCache(cache.tabs)
});
......@@ -46,20 +46,20 @@ async function restoreWindowFromEffectiveWindowCache(aWindowId, aOptions = {}) {
actual: actualSignature,
cached: cachedSignature
});
log(`restoreWindowFromEffectiveWindowCache: verify cache for ${aWindowId}`, {
logForCache(`restoreWindowFromEffectiveWindowCache: verify cache for ${aWindowId}`, {
cache, actualSignature, cachedSignature, signatureMatched
});
if (!cache ||
cache.version != kSIDEBAR_CONTENTS_VERSION ||
!signatureMatched) {
log(`restoreWindowFromEffectiveWindowCache: no effective cache for ${aWindowId}`);
logForCache(`restoreWindowFromEffectiveWindowCache: no effective cache for ${aWindowId}`);
clearWindowCache(owner);
gMetricsData.add('restoreWindowFromEffectiveWindowCache fail');
return false;
}
cache.offset = actualSignature.replace(cachedSignature, '').trim().split('\n').filter(aPart => !!aPart).length;
log(`restoreWindowFromEffectiveWindowCache: restore ${aWindowId} from cache`);
logForCache(`restoreWindowFromEffectiveWindowCache: restore ${aWindowId} from cache`);
var insertionPoint = aOptions.insertionPoint;
if (!insertionPoint) {
......@@ -72,7 +72,9 @@ async function restoreWindowFromEffectiveWindowCache(aWindowId, aOptions = {}) {
insertionPoint.collapse(false);
}
var restored = restoreTabsFromCache(aWindowId, {
insertionPoint, cache, tabs
insertionPoint,
cache,
tabs: apiTabs
});
if (!aOptions.insertionPoint)
insertionPoint.detach();
......@@ -107,36 +109,43 @@ function updateWindowCache(aOwner, aKey, aValue) {
return;
if (aValue === undefined) {
//return browser.sessions.removeWindowValue(aOwner, aKey);
return browser.sessions.removeTabValue(aOwner, aKey);
return browser.sessions.removeTabValue(aOwner.id, aKey);
}
else {
//return browser.sessions.setWindowValue(aOwner, aKey, aValue);
return browser.sessions.setTabValue(aOwner, aKey, aValue);
return browser.sessions.setTabValue(aOwner.id, aKey, aValue);
}
}
function clearWindowCache(aOwner) {
log('clearWindowCache for owner ', aOwner, { stack: new Error().stack });
return Promise.all([
updateWindowCache(aOwner, kWINDOW_STATE_CACHED_TABS),
updateWindowCache(aOwner, kWINDOW_STATE_CACHED_SIDEBAR),
updateWindowCache(aOwner, kWINDOW_STATE_CACHED_SIDEBAR_TABS_DIRTY),
updateWindowCache(aOwner, kWINDOW_STATE_CACHED_SIDEBAR_COLLAPSED_DIRTY)
]);
logForCache('clearWindowCache for owner ', aOwner, { stack: new Error().stack });
updateWindowCache(aOwner, kWINDOW_STATE_CACHED_TABS);
updateWindowCache(aOwner, kWINDOW_STATE_CACHED_SIDEBAR);
updateWindowCache(aOwner, kWINDOW_STATE_CACHED_SIDEBAR_TABS_DIRTY);
updateWindowCache(aOwner, kWINDOW_STATE_CACHED_SIDEBAR_COLLAPSED_DIRTY);
}
function markWindowCacheDirtyFromTab(aTab, akey) {
var container = aTab.parentNode;
return updateWindowCache(container.lastWindowCacheOwner, akey, true);
const container = aTab.parentNode;
if (container.markWindowCacheDirtyFromTabTimeout)
clearTimeout(container.markWindowCacheDirtyFromTabTimeout);
container.markWindowCacheDirtyFromTabTimeout = setTimeout(() => {
container.markWindowCacheDirtyFromTabTimeout = null;
updateWindowCache(container.lastWindowCacheOwner, akey, true);
}, 100);
}
async function getWindowCache(aOwner, aKey) {
//return browser.sessions.getWindowValue(aOwner, aKey);
return browser.sessions.getTabValue(aOwner, aKey);
return browser.sessions.getTabValue(aOwner.id, aKey);
}
function getWindowCacheOwner(aHint) {
return getLastTab(aHint).apiTab.id;
const apiTab = getLastTab(aHint).apiTab;
return {
id: apiTab.id,
windowId: apiTab.windowId
};
}
async function reserveToCacheTree(aHint) {
......@@ -149,7 +158,7 @@ async function reserveToCacheTree(aHint) {
// restoration, we must wait until we know whether there is any other
// restoring tab or not.
if (hasCreatingTab())
await waitUntilAllTabsAreaCreated();
await waitUntilAllTabsAreCreated();
if (!aHint ||
aHint instanceof Node && !aHint.parentNode)
......@@ -163,7 +172,7 @@ async function reserveToCacheTree(aHint) {
return;
var windowId = parseInt(container.dataset.windowId);
log('reserveToCacheTree for window ', windowId, { stack: new Error().stack });
logForCache('reserveToCacheTree for window ', windowId, { stack: new Error().stack });
clearWindowCache(container.lastWindowCacheOwner);
if (container.waitingToCacheTree)
......@@ -183,7 +192,7 @@ function cancelReservedCacheTree(aWindowId) {
async function cacheTree(aWindowId) {
if (hasCreatingTab())
await waitUntilAllTabsAreaCreated();
await waitUntilAllTabsAreCreated();
var container = getTabsContainer(aWindowId);
if (!container ||
!configs.useCachedTree)
......@@ -191,11 +200,11 @@ async function cacheTree(aWindowId) {
var signature = await getWindowSignature(aWindowId);
if (container.allTabsRestored)
return;
//log('save cache for ', aWindowId);
//logForCache('save cache for ', aWindowId);
container.lastWindowCacheOwner = getWindowCacheOwner(aWindowId);
if (!container.lastWindowCacheOwner)
return;
log('cacheTree for window ', aWindowId, { stack: new Error().stack });
logForCache('cacheTree for window ', aWindowId, { stack: new Error().stack });
updateWindowCache(container.lastWindowCacheOwner, kWINDOW_STATE_CACHED_TABS, {
version: kBACKGROUND_CONTENTS_VERSION,
tabs: container.outerHTML,
......
......@@ -41,91 +41,58 @@ async function refreshContextMenuItems() {
normalItemAppeared = true;
}
let type = isSeparator ? 'separator' : 'normal';
let title = isSeparator ? null : browser.i18n.getMessage(`context.${id}.label`);
let title = isSeparator ? null : browser.i18n.getMessage(`context_${id}_label`);
browser.contextMenus.create({
id, type, title,
contexts: ['page', 'tab']
id, type,
// Access key is not supported by WE API.
// See also: https://bugzilla.mozilla.org/show_bug.cgi?id=1320462
title: title && title.replace(/\(&[a-z]\)|&([a-z])/i, '$1'),
contexts: ['tab']
});
tabContextMenu.onExternalMessage({
type: kTSTAPI_CONTEXT_MENU_CREATE,
params: {
id, type, title,
contexts: ['page', 'tab']
contexts: ['tab']
}
}, browser.runtime);
}
}
var contextMenuClickListener = (aInfo, aTab) => {
log('context menu item clicked: ', aInfo, aTab);
var contextMenuClickListener = (aInfo, aAPITab) => {
log('context menu item clicked: ', aInfo, aAPITab);
var contextTab = getTabById(aTab.id);
var contextTab = getTabById(aAPITab);
var container = contextTab.parentNode;
switch (aInfo.menuItemId) {
case 'reloadTree': {
let tabs = [contextTab].concat(getDescendantTabs(contextTab));
for (let tab of tabs) {
browser.tabs.reload(tab.apiTab.id)
.catch(handleMissingTabError);
}
}; break;
case 'reloadDescendants': {
let tabs = getDescendantTabs(contextTab);
for (let tab of tabs) {
browser.tabs.reload(tab.apiTab.id)
.catch(handleMissingTabError);
}
}; break;
case 'reloadTree':
Commands.reloadTree(contextTab);
break;
case 'reloadDescendants':
Commands.reloadDescendants(contextTab);
break;
case 'closeTree': {
let tabs = [contextTab].concat(getDescendantTabs(contextTab));
tabs.reverse(); // close bottom to top!
for (let tab of tabs) {
removeTabInternally(tab);
}
}; break;
case 'closeDescendants': {
let tabs = getDescendantTabs(contextTab);
tabs.reverse(); // close bottom to top!
for (let tab of tabs) {
removeTabInternally(tab);
}
}; break;
case 'closeOthers': {
let exceptionTabs = [contextTab].concat(getDescendantTabs(contextTab));
let tabs = getNormalTabs(container); // except pinned or hidden tabs
tabs.reverse(); // close bottom to top!
for (let tab of tabs) {
if (exceptionTabs.indexOf(tab) < 0)
removeTabInternally(tab);
}
}; break;
case 'closeTree':
Commands.closeTree(contextTab);
break;
case 'closeDescendants':
Commands.closeDescendants(contextTab);
break;
case 'closeOthers':
Commands.closeOthers(contextTab);
break;
case 'collapseAll': {
let tabs = getNormalTabs(container);
for (let tab of tabs) {
if (hasChildTabs(tab) && !isSubtreeCollapsed(tab))
collapseExpandSubtree(tab, {
collapsed: true,
broadcast: true
});
}
}; break;
case 'expandAll': {
let tabs = getNormalTabs(container);
for (let tab of tabs) {
if (hasChildTabs(tab) && isSubtreeCollapsed(tab))
collapseExpandSubtree(tab, {
collapsed: false,
broadcast: true
});
}
}; break;
case 'collapseAll':
Commands.collapseAll(contextTab);
break;
case 'expandAll':
Commands.expandAll(contextTab);
break;
case 'bookmarkTree': {
bookmarkTree(contextTab);
}; break;
case 'bookmarkTree':
Commands.bookmarkTree(contextTab);
break;
default:
break;
......
This diff is collapsed.
......@@ -6,7 +6,7 @@
'use strict';
const kLEGACY_CONFIGS_MIGRATION_VERSION = 3;
const kFEATURES_VERSION = 2;
const kFEATURES_VERSION = 3;
function migrateLegacyConfigs() {
var values = configs.importedConfigsFromLegacy;
......@@ -99,8 +99,8 @@ function migrateLegacyConfigs() {
if (migrated)
notify({
title: browser.i18n.getMessage('migration.configs.notification.title'),
message: browser.i18n.getMessage('migration.configs.notification.message'),
title: browser.i18n.getMessage('migration_configs_notification_title'),
message: browser.i18n.getMessage('migration_configs_notification_message'),
icon: kNOTIFICATION_DEFAULT_ICON,
timeout: -1
});
......@@ -109,8 +109,8 @@ function migrateLegacyConfigs() {
catch(e) {
log('failed to migrate tree: ', String(e), e.stack);
notify({
title: browser.i18n.getMessage('migration.configsFailed.notification.title'),
message: `${browser.i18n.getMessage('migration.configsFailed.notification.message')}\n${String(e)}`,
title: browser.i18n.getMessage('migration_configsFailed_notification_title'),
message: `${browser.i18n.getMessage('migration_configsFailed_notification_message')}\n${String(e)}`,
icon: kNOTIFICATION_DEFAULT_ICON,
timeout: -1
});
......@@ -182,7 +182,7 @@ async function migrateLegacyTreeStructure() {
// found: apply only structure case
let structure = structures[index];
let tabs = getAllTabs(apiWindow.id);
applyTreeStructureToTabs(tabs, structure);
await applyTreeStructureToTabs(tabs, structure);
restoredCountWithSession++;
......@@ -192,7 +192,7 @@ async function migrateLegacyTreeStructure() {
if (restoredCountWithSession > 0)
messages.push(
browser.i18n.getMessage(
'migration.tree.notification.message.withSession',
'migration_tree_notification_message_withSession',
restoredCountWithSession
)
);
......@@ -223,7 +223,7 @@ async function migrateLegacyTreeStructure() {
});
var restApiTabs = apiWindow.tabs.slice(1);
try {
await removeTabInternally(getTabById(apiWindow.tabs[0].id));
await removeTabInternally(getTabById(apiWindow.tabs[0]));
// apply pinned state
for (let i = 0, maxi = restApiTabs.length; i < maxi; i++) {
if (!aStructure[i].pinned)
......@@ -240,13 +240,13 @@ async function migrateLegacyTreeStructure() {
if (structures.length > 0)
messages.push(
browser.i18n.getMessage(
'migration.tree.notification.message.withoutSession',
'migration_tree_notification_message_withoutSession',
structures.length
)
);
notify({
title: browser.i18n.getMessage('migration.tree.notification.title'),
title: browser.i18n.getMessage('migration_tree_notification_title'),
message: messages.join('\n'),
icon: kNOTIFICATION_DEFAULT_ICON,
timeout: -1
......@@ -256,8 +256,8 @@ async function migrateLegacyTreeStructure() {
catch(e) {
log('failed to migrate tree: ', String(e), e.stack);
notify({
title: browser.i18n.getMessage('migration.treeFailed.notification.title'),
message: `${browser.i18n.getMessage('migration.treeFailed.notification.message')}\n${String(e)}`,
title: browser.i18n.getMessage('migration_treeFailed_notification_title'),
message: `${browser.i18n.getMessage('migration_treeFailed_notification_message')}\n${String(e)}`,
icon: kNOTIFICATION_DEFAULT_ICON,
timeout: -1
});
......@@ -272,7 +272,7 @@ async function notifyNewFeatures() {
configs.notifiedFeaturesVersion = kFEATURES_VERSION;
browser.tabs.create({
url: browser.extension.getURL('resources/startup.html'),
url: kSHORTHAND_URIS.startup,
active: true
});
}
/*
license: The MIT License, Copyright (c) 2016-2017 YUKI "Piro" Hiroshi
license: The MIT License, Copyright (c) 2016-2018 YUKI "Piro" Hiroshi
original:
http://github.com/piroor/webextensions-lib-configs
*/
......@@ -8,10 +8,12 @@
function Configs(aDefaults, aOptions = { syncKeys: [] }) {
this.$default = aDefaults;
this.$logging = false;
this.$logging = aOptions.logging || false;
this.$locked = {};
this.$lastValues = {};
this.$syncKeys = aOptions.syncKeys || [];
this.$syncKeys = aOptions.localKeys ?
Object.keys(aDefaults).filter(x => !aOptions.localKeys.includes(x)) :
(aOptions.syncKeys || []);
this.$loaded = this.$load();
}
Configs.prototype = {
......@@ -29,12 +31,12 @@ Configs.prototype = {
}
},
$addObserver : function(aObserver) {
$addObserver(aObserver) {
var index = this.$observers.indexOf(aObserver);
if (index < 0)
this.$observers.push(aObserver);
},
$removeObserver : function(aObserver) {
$removeObserver(aObserver) {
var index = this.$observers.indexOf(aObserver);
if (index > -1)
this.$observers.splice(index, 1);
......@@ -46,7 +48,7 @@ Configs.prototype = {
location.protocol === 'moz-extension:';
},
$log : function(aMessage, ...aArgs) {
$log(aMessage, ...aArgs) {
if (!this.$logging)
return;
......@@ -58,7 +60,7 @@ Configs.prototype = {
console.log(aMessage, ...aArgs);
},
$load : function() {
$load() {
return this.$_promisedLoad ||
(this.$_promisedLoad = this.$tryLoad());
},
......@@ -66,51 +68,113 @@ Configs.prototype = {
$tryLoad : async function() {
this.$log('load');
this.$applyValues(this.$default);
browser.runtime.onMessage.addListener(this.$onMessage.bind(this));
var values;
let values;
try {
if (this.$shouldUseStorage) { // background mode
this.$log(`load: try load from storage on ${location.href}`);
values = await browser.storage.local.get(this.$default);
values = values || this.$default;
if (this.$syncKeys && this.$syncKeys.length) {
let syncedValues = await browser.storage.sync.get(this.$syncKeys);
this.$log(`load: loaded from sync for ${location.origin}`, syncedValues);
values = Object.assign(values, syncedValues);
}
this.$log(`load: loaded for ${location.origin}`, values);
this.$log(`load: try load from storage on ${location.href}`);
let [localValues, managedValues, lockedKeys] = await Promise.all([
(async () => {
try {
const localValues = await browser.storage.local.get(this.$default);
this.$log('load: successfully loaded local storage');
return localValues;
}
catch(e) {
this.$log('load: failed to load local storage: ', String(e));
}
return {};
})(),
(async () => {
if (!browser.storage.managed) {
this.$log('load: skip managed storage');
return null;
}
try {
const managedValues = await browser.storage.managed.get();
this.$log('load: successfully loaded managed storage');
return managedValues || null;
}
catch(e) {
this.$log('load: failed to load managed storage: ', String(e));
}
return null;
})(),
(async () => {
try {
const lockedKeys = await browser.runtime.sendMessage({
type : 'Configs:request:locked'
});
this.$log('load: successfully synchronized locked state');
return lockedKeys;
}
catch(e) {
this.$log('load: failed to synchronize locked state: ', String(e));
}
return {};
})()
]);
this.$log(`load: loaded for ${location.origin}:`, { localValues, managedValues, lockedKeys });
values = Object.assign({}, localValues || {}, managedValues || {});
this.$applyValues(values);
if (browser.storage.managed) {
this.$log('load: values are applied');
lockedKeys = Object.keys(lockedKeys || {});
if (managedValues)
lockedKeys = lockedKeys.concat(Object.keys(managedValues));
for (let key of lockedKeys) {
this.$updateLocked(key, true);
}
this.$log('load: locked state is applied');
browser.storage.onChanged.addListener(this.$onChanged.bind(this));
if (this.$syncKeys || this.$syncKeys.length > 0) {
try {
let values = await browser.storage.managed.get();
Object.keys(values).map(aKey => {
this[aKey] = values[aKey];
this.$updateLocked(aKey, true);
browser.storage.sync.get(this.$syncKeys).then(syncedValues => {
this.$log('load: successfully loaded sync storage');
if (!syncedValues)
return;
for (let key of Object.keys(syncedValues)) {
this[key] = syncedValues[key];
}
});
}
catch(e) {
this.$log('load: failed to read sync storage: ', String(e));
return null;
}
}
browser.storage.onChanged.addListener(this.$onChanged.bind(this));
}
else { // content mode
this.$log('load: initialize promise on ' + location.href);
let response = await browser.runtime.sendMessage({
type : 'Configs:request:load'
});
this.$log(`load: try load from background on ${location.href}`);
let response;
while (true) {
try {
response = await browser.runtime.sendMessage({
type : 'Configs:request:load'
});
if (response)
break;
}
catch(e) {
this.$log('load: failed to load configs from background: ', String(e));
}
this.$log('load: waiting for anyone can access to the storage... ' + location.href);
await new Promise((aResolve, aReject) => setTimeout(aResolve, 200));
}
this.$log('load: responded', response);
values = response && response.values || this.$default;
this.$applyValues(values);
this.$log('load: values are applied');
this.$locked = response && response.lockedKeys || {};
this.$log('load: locked state is applied');
}
browser.runtime.onMessage.addListener(this.$onMessage.bind(this));
return values;
}
catch(e) {
this.$log('load: failed', e, e.stack);
this.$log('load: fatal error: ', e, e.stack);
throw e;
}
},
$applyValues : function(aValues) {
$applyValues(aValues) {
Object.keys(aValues).forEach(aKey => {
if (aKey in this.$locked)
return;
......@@ -124,6 +188,8 @@ Configs.prototype = {
this.$log(`warning: ${aKey} is locked and not updated`);
return aValue;
}
if (JSON.stringify(aValue) == JSON.stringify(this.$lastValues[aKey]))
return aValue;
this.$log(`set: ${aKey} = ${aValue}`);
this.$lastValues[aKey] = aValue;
this.$notifyUpdated(aKey);
......@@ -133,19 +199,19 @@ Configs.prototype = {
});
},
$lock : function(aKey) {
$lock(aKey) {
this.$log('locking: ' + aKey);
this.$updateLocked(aKey, true);
this.$notifyUpdated(aKey);
},
$unlock : function(aKey) {
$unlock(aKey) {
this.$log('unlocking: ' + aKey);
this.$updateLocked(aKey, false);
this.$notifyUpdated(aKey);
},
$updateLocked : function(aKey, aLocked) {
$updateLocked(aKey, aLocked) {
if (aLocked) {
this.$locked[aKey] = true;
}
......@@ -154,7 +220,7 @@ Configs.prototype = {
}
},
$onMessage : function(aMessage, aSender, aRespond) {
$onMessage(aMessage, aSender, aRespond) {
if (!aMessage ||
typeof aMessage.type != 'string')
return;
......@@ -176,6 +242,7 @@ Configs.prototype = {
BACKEND_COMMANDS: [
'Configs:request:load',
'Configs:request:locked',
'Configs:update',
'Configs:request:reset'
],
......@@ -195,6 +262,11 @@ Configs.prototype = {
};
<