Publish Vulpes Preview 15 sources for desktop, Android and Tundra

Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
OverSu 2026-09-30 23:13:03 +02:00
commit b38dea0597
25901 changed files with 9982925 additions and 369 deletions

View file

@ -0,0 +1,107 @@
/* 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';
/* exported SyncBookmark */
/* global SyncBrowserDB */
/* global SyncDsHelper */
var SyncBookmark = (function () {
var syncDataStore;
function updateWrapper(data) {
return new Promise(resolve => {
if (data.deleted) {
SyncBrowserDB.removeBookmark(data.id, () => {
resolve();
});
} else {
SyncBrowserDB.updateRawBookmark(data, () => {
resolve();
});
}
});
}
function handleTask(task) {
return new Promise((resolve, reject) => {
var data = [];
if (task.data && task.data.fxsyncRecords) {
for (var key in task.data.fxsyncRecords) {
data.push(task.data.fxsyncRecords[key]);
}
}
switch(task.operation) {
case 'update':
case 'add':
Promise.all(data.map(updateWrapper)).then(resolve);
break;
case 'clear':
// Ignore clear operation.
resolve();
break;
case 'remove':
getBookmarkByDsId(task.id, bookmark => {
if (bookmark) {
SyncBrowserDB.removeBookmark(bookmark.id, () => {
resolve();
});
}
});
break;
case 'done':
resolve();
break;
}
});
}
function getBookmarkByDsId(dsid, callback) {
SyncBrowserDB.getBookmark({bmkUri: dsid}, bookmark => {
if (bookmark) {
callback(bookmark);
} else {
var specialType =
['query', 'microsummary', 'folder', 'livemark', 'separator'];
var isSpecialType = specialType.some(value => {
return dsid.indexOf(value + '|') === 0;
});
var tmp = dsid.split('|');
if (isSpecialType && tmp.length === 2) {
var fxsyncId = tmp[1];
SyncBrowserDB.getBookmark(fxsyncId, callback);
} else {
console.warn('trying to delete a unknown type?', dsid);
callback(null);
}
}
});
}
function start() {
if (syncDataStore) {
return Promise.resolve();
}
syncDataStore = new SyncDsHelper('bookmarks_store');
return syncDataStore.init().then(() => {
return syncDataStore.start(handleTask);
});
}
function stop() {
if (!syncDataStore) {
return Promise.reject('Uninitialized DataStore');
}
return syncDataStore.stop().then(() => {
syncDataStore = null;
return SyncBrowserDB.clearBookmarks();
});
}
return {
start: start,
stop: stop
};
})();

1958
gaia/tv_apps/browser/js/sync/db.js Executable file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,113 @@
/* 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';
/* exported SyncDsHelper */
/* global localStorage */
var SyncDsHelper = function (dataStoreName) {
if (typeof dataStoreName !== 'string') {
throw new Event('Incorrect DataStore name.');
}
this.dataStoreName = dataStoreName;
this.dataStoreRevId = dataStoreName + '::revId';
};
SyncDsHelper.prototype = {
store: null,
dataChangeHandler: null,
init() {
return this._ensureStore();
},
_ensureStore() {
if (this.store) {
return Promise.resolve(this.store);
}
return navigator.getDataStores(this.dataStoreName).then(stores => {
this.store = stores[0];
return this.store;
});
},
setSyncedRevId(revId) {
localStorage.setItem(this.dataStoreRevId, revId);
},
getSyncedRevId() {
return localStorage.getItem(this.dataStoreRevId);
},
registerStoreChangeEvent(handler) {
this._ensureStore().then(() => {
if (!this.dataChangeHandler) {
this.store.removeEventListener('change', this.dataChangeHandler);
}
this.dataChangeHandler = handler;
this.store.addEventListener('change', this.dataChangeHandler);
});
},
unregisterStoreChangeEvent() {
return this._ensureStore().then(() => {
this.store.removeEventListener('change', this.dataChangeHandler);
this.dataChangeHandler = null;
});
},
clear() {
localStorage.removeItem(this.dataStoreRevId);
return this._ensureStore().then(() => {
return this.store.clear();
});
},
start(handleTask) {
return this.init().then(() => {
this.registerStoreChangeEvent(() => {
this.dataStoreSync(handleTask);
});
return this.dataStoreSync(handleTask);
});
},
stop() {
return this.unregisterStoreChangeEvent().then(this.clear.bind(this));
},
dataStoreSync(handle) {
return this._ensureStore().then(() => {
var _revId = this.getSyncedRevId();
return new Promise((resolve, reject) => {
var runNextTask = (cursor) => {
cursor.next().then(task => {
manageTask(cursor, task);
});
};
var post = (cursor, task) => {
if (task.operation === 'done') {
this.setSyncedRevId(this.store.revisionId);
resolve();
return;
}
runNextTask(cursor);
};
var manageTask = (cursor, task) => {
if (handle) {
handle(task).then(() => post(cursor, task));
} else {
post(cursor, task);
}
};
runNextTask(this.store.sync(_revId));
});
});
}
};

View file

@ -0,0 +1,86 @@
/* 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';
/* exported SyncHistory */
/* global SyncBrowserDB */
/* global SyncDsHelper */
/*
exampleHistroyInIndexedDB = {
"uri": "http://example.com/",
"title": "Hello World",
"frecency": 1,
"iconUri": "http://example.com/favicon.ico",
"screenshot": {}
}
*/
var SyncHistory = (function () {
var syncDataStore;
function handleTask(task) {
return new Promise(resolve => {
switch(task.operation) {
case 'update':
case 'add':
var data = {};
var syncedData = task.data;
if (syncedData && syncedData.url &&
Array.isArray(syncedData.visits) && syncedData.visits.length > 0) {
data.uri = syncedData.url;
data.title = syncedData.title;
data.fxsyncId = syncedData.fxsyncId;
data.timestamp = syncedData.visits[0];
} else {
console.warn('Invalid places record:', task);
resolve();
return;
}
SyncBrowserDB.updateRawHistory(data, () => {
resolve();
});
break;
case 'clear':
// Ignore clear operation.
resolve();
break;
case 'remove':
SyncBrowserDB.removeHistory(task.id, () => {
resolve();
});
break;
case 'done':
resolve();
break;
}
});
}
function start() {
if (syncDataStore) {
return Promise.resolve();
}
syncDataStore = new SyncDsHelper('places');
return syncDataStore.init().then(() => {
return syncDataStore.start(handleTask);
});
}
function stop() {
if (!syncDataStore) {
return Promise.reject('Uninitialized DataStore');
}
return syncDataStore.stop().then(() => {
syncDataStore = null;
return SyncBrowserDB.clearHistoryDeep();
});
}
return {
start: start,
stop: stop
};
})();

View file

@ -0,0 +1,163 @@
/* 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/. */
/**
* This module acts as a bridge between the browser UI and the Sync manager
* that lives in the System application.
*
* It allows consumers to subscribe to updates done in the Sync state machine
* so they can update the UI accordingly.
*
* The simple high level picture of Sync in the browser is:
*
* settings.js toolbar.js
* | |
* | |
* \______ manager_bridge.js _______/
* |
* | IAC
* |
* smart_system/sync_manager.js
*
*/
/* global LazyLoader */
/* global SyncBookmark */
/* global SyncHistory */
'use strict';
(function(exports) {
const DEBUG = false;
function debug() {
if (DEBUG) {
console.log('[SyncManagerBridge] ' + Array.slice(arguments).concat());
}
}
var SyncManagerBridge = {
_port: null,
_requests: new Map(),
_listeners: new Map(),
addListener(listener) {
debug('addListener', listener);
if (!this._listeners.has(listener)) {
this._listeners.set(listener);
}
},
removeListener(listener) {
debug('removeListener', listener);
if (!this._listeners.has(listener)) {
return;
}
this._listeners.delete(listener);
},
connect() {
if (this._port) {
return Promise.resolve(this._port);
}
return new Promise((resolve, reject) => {
navigator.mozApps.getSelf().onsuccess = event => {
var app = event.target.result;
app.connect('gaia-sync-management').then(ports => {
if (!ports || !ports.length) {
return reject();
}
this._port = ports[0];
this._port.onmessage = this.onmessage.bind(this);
resolve(this._port);
}).catch(reject);
};
});
},
iacRequest(request) {
debug('iacRequest', JSON.stringify(request));
return new Promise(resolve => {
this.connect().then(port => {
if (request.id) {
this._requests.set(request.id, resolve);
}
port.postMessage(request);
});
});
},
onmessage(event) {
var self = this;
function notify() {
if (self._listeners.size) {
Array.from(self._listeners.keys()).forEach(listener => {
listener(message);
});
}
}
var message = event.data;
if (!message) {
return;
}
debug('message', JSON.stringify(message));
if (message.id && this._requests.has(message.id)) {
this._requests.get(message.id)(message);
this._requests.delete(message.id);
return;
}
if (message.name !== 'onsyncchange') {
return;
}
if (message.state == 'disabled' ||
message.state == 'enabled') {
LazyLoader.load([
'js/sync/ds_helper.js',
'js/sync/bookmarks.js',
'js/sync/history.js'
]).then(() => {
var method = message.state == 'enabled' ? 'start'
: 'stop';
Promise.all([
SyncBookmark[method](),
SyncHistory[method]()
]).catch(error => {
console.warn(error);
});
notify();
});
return;
}
notify();
},
getInfo() {
return this.iacRequest({
name: 'getInfo',
id: Date.now()
});
}
};
['enable',
'disable',
'sync'].forEach(requestName => {
SyncManagerBridge[requestName] = () => {
return SyncManagerBridge.iacRequest({
name: requestName
});
};
});
exports.SyncManagerBridge = SyncManagerBridge;
}(window));

View file

@ -0,0 +1,507 @@
/* 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/. */
/* global Awesomescreen */
/* global Browser */
/* global BrowserDialog */
/* global ERROR_DIALOG_CLOSED_BY_USER */
/* global ERROR_INVALID_SYNC_ACCOUNT */
/* global ERROR_OFFLINE */
/* global ERROR_SYNC_APP_KILLED */
/* global ERROR_UNKNOWN */
/* global LazyLoader */
/* global Settings */
/* global SettingsListener */
/* global SyncManagerBridge */
'use strict';
(function(exports) {
const DEBUG = false;
function debug() {
if (DEBUG) {
console.log('[FirefoxSyncSettings] ' + Array.slice(arguments).concat());
}
}
// Screens
const ENABLED = 'enabled';
const DISABLED = 'disabled';
const BOOKMARKS_SETTING = 'sync.collections.bookmarks.enabled';
const HISTORY_SETTING = 'sync.collections.history.enabled';
const ELEMENTS = [{
screen: DISABLED,
selector: '#fxsync-sign-in-button',
event: 'mouseup',
listener: 'enable'
}, {
screen: ENABLED,
selector: '#fxsync-signed-in-as'
}, {
screen: ENABLED,
selector: '#fxsync-sync-now-button',
event: 'mouseup',
listener: 'sync'
}, {
screen: ENABLED,
selector: '#fxsync-sign-out-button',
event: 'mouseup',
listener: 'disable'
}, {
screen: ENABLED,
selector: '#fxsync-collection-bookmarks',
event: 'click',
listener: 'onbookmarkschecked',
init: 'onbookmarkschange'
}, {
screen: ENABLED,
selector: '#fxsync-collection-history',
event: 'click',
listener: 'onhistorychecked',
init: 'onhistorychange'
}, {
screen: ENABLED,
selector: '#fxsync-tos',
event: 'click',
listener: 'openTos'
}, {
screen: ENABLED,
selector: '#fxsync-privacy',
event: 'click',
listener: 'openPrivacy'
}];
function getElementName(str) {
str = str.toLowerCase().replace('#fxsync-', '');
return str.replace(/-([a-z])/g, g => {
return g[1].toUpperCase();
});
}
var FirefoxSyncSettings = {
init() {
this.area = document.querySelector('#fxsync-area');
this.elements = {
enabled: this.area.querySelector('#fxsync-enabled'),
disabled: this.area.querySelector('#fxsync-disabled')
};
this.listeners = new Map();
this.collections = new Map();
this.screen = null;
this.state = 'disabled';
this.showScreen(DISABLED);
LazyLoader.load('shared/js/settings_listener.js').then(() => {
[{
name: BOOKMARKS_SETTING,
onchange: 'onbookmarkschange'
}, {
name: HISTORY_SETTING,
onchange: 'onhistorychange'
}].forEach(setting => {
SettingsListener.observe(setting.name, true, enabled => {
debug(setting.name + ' changed to ' + enabled);
enabled ? this.collections.set(setting.name, enabled)
: this.collections.delete(setting.name);
this[setting.onchange]();
this.maybeEnableSyncNow();
});
});
});
SyncManagerBridge.addListener(this.onsyncchange.bind(this));
document.addEventListener('visibilitychange', () => {
debug('Visibility change', document.hidden);
if (!document.hidden) {
this.refresh();
}
});
this.refresh();
},
refresh() {
debug('Refreshing');
SyncManagerBridge.getInfo().then(this.onsyncchange.bind(this));
},
/**
* This is where most part of the Sync settings logic lives.
*
* Here we handle Sync lifecycle events coming from the SyncManager
* through the SyncMangerBridge. We react to these events by
* changing the screen depending on the state.
*
* We show two possible different state:
* 1. Logged out screen. We simply show the user what Sync is and
* allow her to start using it or to keep walking.
* 2. Logged in screen. We show to the user several different options:
* + The user owning the Sync account. If the account belongs to a
* verified user that previosly used Sync, we are fine, if not, we
* show a warning asking the user to go to other Firefox client to
* create a FxA and login into Sync.
* + A button to disable sync. Clicking it will log the user out from
* FxA and will clear the synchronized data.
* + A button to start a sync on demand. Clicking it will disable the
* button and the collections switches while the synchronization is
* being performed. Once the sync is done successfully, we enable
* all the options back and refresh the last sync time.
* We disable this button if no collection is selected.
* + Collections switches. Allow the user to select which collections
* she wants to keep synchronized. If no collection is selected, the
* synchronization will be disabled.
* + ToS and Privacy links.
*/
onsyncchange(message) {
debug(JSON.stringify(message));
if (!message || !message.state) {
throw new Error('Missing sync state');
}
switch (message.state) {
case 'disabled':
// We want to show a dialog once Sync is disabled
// but we only want to do that if it's disabled via user action
// and the user has already logged in.
// (and not because it is already disabled from a previous run).
if (this.state === 'disabling' &&
(this.previousState === 'enabled' ||
this.previousState === 'syncing')) {
navigator.mozL10n.formatValue('fxsync-disabled').then(result => {
window.alert(result);
});
}
this.showScreen(DISABLED);
this.hideEnabling();
break;
case 'enabling':
this.showEnabling();
break;
case 'enabled':
// We want to show a dialog once Sync is enabled
// but we only want to do that if its enabled via user action
// (and not because it is already enabled from a previous run).
if (this.state === 'enabling') {
navigator.mozL10n.formatValue('fxsync-enabled').then(result => {
window.alert(result);
});
}
this.hideEnabling();
this.showScreen(ENABLED);
this.showUser(message.user);
this.showSyncNow();
break;
case 'syncing':
this.showScreen(ENABLED);
// In case that the user opens the browser app while we are on a
// syncing state the previous 'enabled' state is missed and so
// we need to make sure that we show the user here as well.
this.showUser(message.user);
this.showSyncing();
break;
case 'errored':
LazyLoader.load('shared/js/sync/errors.js', () => {
const IGNORED_ERRORS = [
ERROR_DIALOG_CLOSED_BY_USER,
ERROR_SYNC_APP_KILLED
];
if (IGNORED_ERRORS.indexOf(message.error) > -1) {
Settings.hide.bind(Settings)();
return;
}
var errorIds = [ERROR_UNKNOWN, ERROR_UNKNOWN + '-explanation'];
const KNOWN_ERRORS = [
ERROR_INVALID_SYNC_ACCOUNT,
ERROR_OFFLINE
];
if (KNOWN_ERRORS.indexOf(message.error) > -1) {
errorIds[0] = message.error;
errorIds[1] = message.error + '-explanation';
}
var l10n = navigator.mozL10n;
Promise.all(
errorIds.map(l10n.formatValue.bind(l10n))
).then(result => {
window.alert(result[0] + '\n' + result[1], () => {
this.showScreen(DISABLED);
});
});
});
break;
}
this.previousState = this.state;
this.state = message.state;
},
onbookmarkschange() {
debug('onbookmarkschange');
var needsCleanup;
if (!this.elements.collectionBookmarks) {
// We may observe a setting change once we already disabled Sync
// because the Sync Manager is reverting user preference changes to
// the default ones. In that case, we need to create the object
// briefly, so we can update the DOM accordingly.
this.elements.collectionBookmarks = this.area.querySelector(
'#fxsync-collection-bookmarks'
);
needsCleanup = true;
}
this.elements.collectionBookmarks.checked =
this.collections.has(BOOKMARKS_SETTING);
if (needsCleanup) {
this.elements.collectionBookmarks = null;
}
},
onbookmarkschecked() {
debug('onbookmarkschecked');
this.oncollectionchecked(this.elements.collectionBookmarks,
BOOKMARKS_SETTING);
},
onhistorychange() {
debug('onhistorychange');
var needsCleanup;
if (!this.elements.collectionHistory) {
// We may observe a setting change once we already disabled Sync
// because the Sync Manager is reverting user preference changes to
// the default ones. In that case, we need to create the object
// briefly, so we can update the DOM accordingly.
this.elements.collectionHistory = this.area.querySelector(
'#fxsync-collection-history'
);
needsCleanup = true;
}
this.elements.collectionHistory.checked =
this.collections.has(HISTORY_SETTING);
if (needsCleanup) {
this.elements.collectionHistory = null;
}
},
onhistorychecked() {
debug('onhistorychecked');
this.oncollectionchecked(this.elements.collectionHistory,
HISTORY_SETTING);
},
oncollectionchecked(element, setting) {
if (!element || !setting) {
return;
}
var checked = element.checked;
var settingObj = {};
settingObj[setting] = checked;
if (!checked) {
this.collections.delete(setting);
navigator.mozSettings.createLock().set(settingObj);
return;
}
// Yeah, mozId sucks...
// We cannot call watch twice to change the id lifetime
// callbacks. But we still need to do different stuff
// within the onlogin callback depending on who calls
// oncollectionchecked. So we need to save state.
// Thank you mozId.
this.setting = setting;
this.settingObj = settingObj;
try {
navigator.mozId.watch({
wantIssuer: 'firefox-accounts',
onlogin: () => {
// This callback is fired every time a new account logs in,
// but we only want to enable the history setting when this
// callback is called because the refresh auth flow succeeded.
// This can happen only if Sync is already enabled.
if (this.state !== 'enabled') {
return;
}
this.collections.set(this.setting, true);
navigator.mozSettings.createLock().set(this.settingObj);
},
onlogout: () => {},
onready: () => {},
onerror: error => {
element.checked = false;
console.error(error);
}
});
} catch(e) {}
navigator.mozId.request({
oncancel: () => {
element.checked = false;
},
// We keep authenticated sessions of 5 minutes, if the user clicks on
// the history collection switch after the 5 minutes are expired, we
// will be asking her to re-enter her fxa password.
refreshAuthentication: 5 * 60
});
},
addListener(element, event, listenerName) {
if (!element || !event || !listenerName) {
return;
}
// Because we bind this to the listener, we need to save
// the reference so we can remove it afterwards.
var listener = this[listenerName].bind(this);
element.addEventListener(event, listener);
return listener;
},
removeListener(element, event, listener) {
if (!element || !event || !listener) {
return;
}
element.removeEventListener(event, listener);
},
loadElements(screen) {
ELEMENTS.forEach(element => {
var name = getElementName(element.selector);
if (!Array.isArray(element.screen)) {
element.screen = [element.screen];
}
if (!this.elements[name]) {
this.elements[name] = this.area.querySelector(element.selector);
this.listeners.set(
name,
this.addListener(this.elements[name],
element.event, element.listener)
);
if (element.init) {
this[element.init]();
}
return;
}
if (element.init) {
this[element.init]();
return;
}
if (!this.listeners.has(name)) {
return;
}
if (this.elements[name].checked) {
this.elements[name].checked = false;
}
});
},
showScreen(screen) {
if (this.screen == screen) {
return;
}
this.screen = screen;
this.loadElements(screen);
this.elements.enabled.hidden = (screen != ENABLED);
this.elements.disabled.hidden = (screen != DISABLED);
},
showUser(user) {
navigator.mozL10n.setAttributes(this.elements.signedInAs,
'fxsync-signed-in-as', {
email: user
});
},
maybeEnableSyncNow() {
if (!this.elements.syncNowButton) {
return;
}
(this.collections.size <= 0) ?
this.elements.syncNowButton.classList.add('disabled') :
this.elements.syncNowButton.classList.remove('disabled');
},
disableSyncNowAndCollections(disabled) {
['collectionBookmarks',
'collectionHistory'].forEach(name => {
if (!this.elements[name]) {
return;
}
this.elements[name].disabled = disabled;
});
disabled ? this.elements.syncNowButton.classList.add('disabled')
: this.elements.syncNowButton.classList.remove('disabled');
},
showEnabling() {
this.elements.signInButton.classList.add('disabled');
this.elements.signInButton.dataset.l10nId = 'fxsync-signing';
},
hideEnabling() {
this.elements.signInButton.classList.remove('disabled');
this.elements.signInButton.dataset.l10nId = 'fxsync-sign-in';
},
showSyncNow() {
this.elements.syncNowButton.dataset.l10nId = 'fxsync-sync-now';
this.disableSyncNowAndCollections(false);
this.maybeEnableSyncNow();
},
showSyncing() {
this.elements.syncNowButton.dataset.l10nId = 'fxsync-syncing';
this.disableSyncNowAndCollections(true);
},
enable() {
SyncManagerBridge.enable();
},
disable() {
BrowserDialog.createDialog('signout_confirm').then(() => {
SyncManagerBridge.disable();
});
},
sync() {
SyncManagerBridge.sync();
},
openTos() {
this.showWebpage('https://accounts.firefox.com/legal/terms');
},
openPrivacy() {
this.showWebpage('https://accounts.firefox.com/legal/privacy');
},
showWebpage(url) {
Awesomescreen.createAddNewTab();
Browser.navigate(url);
Settings.hide();
}
};
exports.FirefoxSyncSettings = FirefoxSyncSettings;
LazyLoader.load('js/sync/manager_bridge.js', () => {
FirefoxSyncSettings.init();
});
}(window));

View file

@ -0,0 +1,113 @@
/* 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/. */
/* exports FirefoxSyncToolbar */
/* global LazyLoader */
/* global Settings */
/* global SyncManagerBridge */
'use strict';
/**
* This small module handles the logic of the Sync entry in the browser's
* top right toolbar menu.
*
* We show two different variations of the UI depending on the state of Sync:
*
* - When Sync is disabled, we show a 'Sign in to Sync' entry. Clicking it
* triggers the Firefox Accounts flow and signs the user into Sync if the
* FxA flow is completed successfuly.
* - When Sync is enabled, we show the 'Sign is as {{email}}' entry. Where
* {{email}} is the email used by the user to login into FxA.
*
* We also handle a third Sync state. When an error is received through the
* Sync Manager Bridge, we inform the user about them. The expected errors are:
*
* - ERROR_OFFLINE: The user is trying to log in Sync when the device is
* offline.
* - ERROR_INVALID_SYNC_ACCOUNT: The user is trying to log into Sync with a
* Firefox Account that has never been used before with Sync. On the first
* version we don't support the creation of new Sync accounts, so we ask the
* user to first login in Firefox Desktop or Android and try login again
* in the TV.
* - ERROR_TRY_LATER: The server is probably down. We inform the user about it
* and ask her to retry in a few minutes.
* - ERROR_UNKNOWN: We don't know what the h* was wrong, but we can't log the
* user in.
*
* Here we don't need to worry about keeping any kind of state or managing
* any state transistion apart from the UI changes. The Sync State Machine
* and Sync State Manager takes care of handling all that for us.
*/
(function(exports) {
const DEBUG = true;
function debug() {
if (DEBUG) {
console.log('[FirefoxSyncToolbar] ' + Array.slice(arguments).concat());
}
}
var FirefoxSyncToolbar = {
init() {
SyncManagerBridge.addListener(this.onsyncchange.bind(this));
this.syncBlock = document.getElementById('fxsync-block');
this.syncTab = this.syncBlock.querySelector('#fxsync-tab');
document.addEventListener('visibilitychange', () => {
if (!document.hidden) {
this.refresh();
}
});
this.refresh();
},
refresh() {
SyncManagerBridge.getInfo().then(this.onsyncchange.bind(this));
},
onsyncchange(message) {
debug(JSON.stringify(message));
if (!message || !message.state) {
throw new Error('Missing sync state');
}
switch (message.state) {
case 'disabled':
if (this.showListener) {
this.syncBlock.removeEventListener('mouseup', this.showListener);
this.showListener = null;
}
this.syncBlock.addEventListener('mouseup', SyncManagerBridge.enable);
this.syncTab.setAttribute('data-l10n-id', 'fxsync-sign-in-to-sync');
this.syncTab.removeAttribute('data-l10n-args');
break;
case 'enabling':
Settings.show();
this.syncTab.setAttribute('data-l10n-id', 'fxsync-signing');
break;
case 'syncing':
case 'enabled':
this.showListener = Settings.show.bind(Settings);
this.syncBlock.addEventListener('mouseup', this.showListener);
this.syncBlock.removeEventListener('mouseup',
SyncManagerBridge.enable);
navigator.mozL10n.setAttributes(this.syncTab, 'fxsync-signed-in-as', {
email: message.user
});
break;
}
}
};
// Exported only for testing purposes
exports.FirefoxSyncToolbar = FirefoxSyncToolbar;
LazyLoader.load('js/sync/manager_bridge.js', () => {
FirefoxSyncToolbar.init();
});
}(window));