[BASILISK] Port PM Sync Client - Part 1: Initial transfer

This commit is contained in:
Matt A. Tobin 2019-03-31 18:48:11 -04:00 • committed by Roy Tam
commit 648ed8edaf
21 changed files with 3997 additions and 0 deletions

View file

@ -0,0 +1,470 @@
# 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/.
// gSyncUI handles updating the tools menu
var gSyncUI = {
_obs: ["weave:service:sync:start",
"weave:service:sync:delayed",
"weave:service:quota:remaining",
"weave:service:setup-complete",
"weave:service:login:start",
"weave:service:login:finish",
"weave:service:logout:finish",
"weave:service:start-over",
"weave:ui:login:error",
"weave:ui:sync:error",
"weave:ui:sync:finish",
"weave:ui:clear-error",
],
_unloaded: false,
init: function SUI_init() {
// Proceed to set up the UI if Sync has already started up.
// Otherwise we'll do it when Sync is firing up.
let xps = Components.classes["@mozilla.org/weave/service;1"]
.getService(Components.interfaces.nsISupports)
.wrappedJSObject;
if (xps.ready) {
this.initUI();
return;
}
Services.obs.addObserver(this, "weave:service:ready", true);
// Remove the observer if the window is closed before the observer
// was triggered.
window.addEventListener("unload", function onUnload() {
gSyncUI._unloaded = true;
window.removeEventListener("unload", onUnload, false);
Services.obs.removeObserver(gSyncUI, "weave:service:ready");
if (Weave.Status.ready) {
gSyncUI._obs.forEach(function(topic) {
Services.obs.removeObserver(gSyncUI, topic);
});
}
}, false);
},
initUI: function SUI_initUI() {
// If this is a browser window?
if (gBrowser) {
this._obs.push("weave:notification:added");
}
this._obs.forEach(function(topic) {
Services.obs.addObserver(this, topic, true);
}, this);
if (gBrowser && Weave.Notifications.notifications.length) {
this.initNotifications();
}
this.updateUI();
},
initNotifications: function SUI_initNotifications() {
const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
let notificationbox = document.createElementNS(XULNS, "notificationbox");
notificationbox.id = "sync-notifications";
notificationbox.setAttribute("flex", "1");
let bottombox = document.getElementById("browser-bottombox");
bottombox.insertBefore(notificationbox, bottombox.firstChild);
// Force a style flush to ensure that our binding is attached.
notificationbox.clientTop;
// notificationbox will listen to observers from now on.
Services.obs.removeObserver(this, "weave:notification:added");
},
_wasDelayed: false,
_needsSetup: function SUI__needsSetup() {
let firstSync = "";
try {
firstSync = Services.prefs.getCharPref("services.sync.firstSync");
} catch (e) { }
return Weave.Status.checkSetup() == Weave.CLIENT_NOT_CONFIGURED ||
firstSync == "notReady";
},
updateUI: function SUI_updateUI() {
let needsSetup = this._needsSetup();
document.getElementById("sync-setup-state").hidden = !needsSetup;
document.getElementById("sync-syncnow-state").hidden = needsSetup;
if (!gBrowser)
return;
let button = document.getElementById("sync-button");
if (!button)
return;
button.removeAttribute("status");
this._updateLastSyncTime();
if (needsSetup)
button.removeAttribute("tooltiptext");
},
// Functions called by observers
onActivityStart: function SUI_onActivityStart() {
if (!gBrowser)
return;
let button = document.getElementById("sync-button");
if (!button)
return;
button.setAttribute("status", "active");
},
onSyncDelay: function SUI_onSyncDelay() {
// basically, we want to just inform users that stuff is going to take a while
let title = this._stringBundle.GetStringFromName("error.sync.no_node_found.title");
let description = this._stringBundle.GetStringFromName("error.sync.no_node_found");
let buttons = [new Weave.NotificationButton(
this._stringBundle.GetStringFromName("error.sync.serverStatusButton.label"),
this._stringBundle.GetStringFromName("error.sync.serverStatusButton.accesskey"),
function() { gSyncUI.openServerStatus(); return true; }
)];
let notification = new Weave.Notification(
title, description, null, Weave.Notifications.PRIORITY_INFO, buttons);
Weave.Notifications.replaceTitle(notification);
this._wasDelayed = true;
},
onLoginFinish: function SUI_onLoginFinish() {
// Clear out any login failure notifications
let title = this._stringBundle.GetStringFromName("error.login.title");
this.clearError(title);
},
onSetupComplete: function SUI_onSetupComplete() {
this.onLoginFinish();
},
onLoginError: function SUI_onLoginError() {
// if login fails, any other notifications are essentially moot
Weave.Notifications.removeAll();
// if we haven't set up the client, don't show errors
if (this._needsSetup()) {
this.updateUI();
return;
}
let title = this._stringBundle.GetStringFromName("error.login.title");
let description;
if (Weave.Status.sync == Weave.PROLONGED_SYNC_FAILURE) {
// Convert to days
let lastSync =
Services.prefs.getIntPref("services.sync.errorhandler.networkFailureReportTimeout") / 86400;
description =
this._stringBundle.formatStringFromName("error.sync.prolonged_failure", [lastSync], 1);
} else {
let reason = Weave.Utils.getErrorString(Weave.Status.login);
description =
this._stringBundle.formatStringFromName("error.sync.description", [reason], 1);
}
let buttons = [];
buttons.push(new Weave.NotificationButton(
this._stringBundle.GetStringFromName("error.login.prefs.label"),
this._stringBundle.GetStringFromName("error.login.prefs.accesskey"),
function() { gSyncUI.openPrefs(); return true; }
));
let notification = new Weave.Notification(title, description, null,
Weave.Notifications.PRIORITY_WARNING, buttons);
Weave.Notifications.replaceTitle(notification);
this.updateUI();
},
onLogout: function SUI_onLogout() {
this.updateUI();
},
onStartOver: function SUI_onStartOver() {
this.clearError();
},
onQuotaNotice: function onQuotaNotice(subject, data) {
let title = this._stringBundle.GetStringFromName("warning.sync.quota.label");
let description = this._stringBundle.GetStringFromName("warning.sync.quota.description");
let buttons = [];
buttons.push(new Weave.NotificationButton(
this._stringBundle.GetStringFromName("error.sync.viewQuotaButton.label"),
this._stringBundle.GetStringFromName("error.sync.viewQuotaButton.accesskey"),
function() { gSyncUI.openQuotaDialog(); return true; }
));
let notification = new Weave.Notification(
title, description, null, Weave.Notifications.PRIORITY_WARNING, buttons);
Weave.Notifications.replaceTitle(notification);
},
openServerStatus: function () {
let statusURL = Services.prefs.getCharPref("services.sync.statusURL");
window.openUILinkIn(statusURL, "tab");
},
// Commands
doSync: function SUI_doSync() {
setTimeout(function() Weave.Service.errorHandler.syncAndReportErrors(), 0);
},
handleToolbarButton: function SUI_handleStatusbarButton() {
if (this._needsSetup())
this.openSetup();
else
this.doSync();
},
//XXXzpao should be part of syncCommon.js - which we might want to make a module...
// To be fixed in a followup (bug 583366)
/**
* Invoke the Sync setup wizard.
*
* @param wizardType
* Indicates type of wizard to launch:
* null -- regular set up wizard
* "pair" -- pair a device first
* "reset" -- reset sync
*/
openSetup: function SUI_openSetup(wizardType) {
let win = Services.wm.getMostRecentWindow("Weave:AccountSetup");
if (win)
win.focus();
else {
window.openDialog("chrome://browser/content/sync/setup.xul",
"weaveSetup", "centerscreen,chrome,resizable=no",
wizardType);
}
},
openAddDevice: function () {
if (!Weave.Utils.ensureMPUnlocked())
return;
let win = Services.wm.getMostRecentWindow("Sync:AddDevice");
if (win)
win.focus();
else
window.openDialog("chrome://browser/content/sync/addDevice.xul",
"syncAddDevice", "centerscreen,chrome,resizable=no");
},
openQuotaDialog: function SUI_openQuotaDialog() {
let win = Services.wm.getMostRecentWindow("Sync:ViewQuota");
if (win)
win.focus();
else
Services.ww.activeWindow.openDialog(
"chrome://browser/content/sync/quota.xul", "",
"centerscreen,chrome,dialog,modal");
},
openPrefs: function SUI_openPrefs() {
openPreferences("paneSync");
},
// Helpers
_updateLastSyncTime: function SUI__updateLastSyncTime() {
if (!gBrowser)
return;
let syncButton = document.getElementById("sync-button");
if (!syncButton)
return;
let lastSync;
try {
lastSync = Services.prefs.getCharPref("services.sync.lastSync");
}
catch (e) { };
if (!lastSync || this._needsSetup()) {
syncButton.removeAttribute("tooltiptext");
return;
}
// Show the day-of-week and time (HH:MM) of last sync
let lastSyncDate = new Date(lastSync).toLocaleFormat("%a %H:%M");
let lastSyncLabel =
this._stringBundle.formatStringFromName("lastSync2.label", [lastSyncDate], 1);
syncButton.setAttribute("tooltiptext", lastSyncLabel);
},
clearError: function SUI_clearError(errorString) {
Weave.Notifications.removeAll(errorString);
this.updateUI();
},
onSyncFinish: function SUI_onSyncFinish() {
let title = this._stringBundle.GetStringFromName("error.sync.title");
// Clear out sync failures on a successful sync
this.clearError(title);
if (this._wasDelayed && Weave.Status.sync != Weave.NO_SYNC_NODE_FOUND) {
title = this._stringBundle.GetStringFromName("error.sync.no_node_found.title");
this.clearError(title);
this._wasDelayed = false;
}
},
onSyncError: function SUI_onSyncError() {
let title = this._stringBundle.GetStringFromName("error.sync.title");
if (Weave.Status.login != Weave.LOGIN_SUCCEEDED) {
this.onLoginError();
return;
}
let description;
if (Weave.Status.sync == Weave.PROLONGED_SYNC_FAILURE) {
// Convert to days
let lastSync =
Services.prefs.getIntPref("services.sync.errorhandler.networkFailureReportTimeout") / 86400;
description =
this._stringBundle.formatStringFromName("error.sync.prolonged_failure", [lastSync], 1);
} else {
let error = Weave.Utils.getErrorString(Weave.Status.sync);
description =
this._stringBundle.formatStringFromName("error.sync.description", [error], 1);
}
let priority = Weave.Notifications.PRIORITY_WARNING;
let buttons = [];
// Check if the client is outdated in some way
let outdated = Weave.Status.sync == Weave.VERSION_OUT_OF_DATE;
for (let [engine, reason] in Iterator(Weave.Status.engines))
outdated = outdated || reason == Weave.VERSION_OUT_OF_DATE;
if (outdated) {
description = this._stringBundle.GetStringFromName(
"error.sync.needUpdate.description");
buttons.push(new Weave.NotificationButton(
this._stringBundle.GetStringFromName("error.sync.needUpdate.label"),
this._stringBundle.GetStringFromName("error.sync.needUpdate.accesskey"),
function() {
window.openUILinkIn(Services.prefs.getCharPref("services.sync.outdated.url"), "tab");
return true;
}
));
}
else if (Weave.Status.sync == Weave.OVER_QUOTA) {
description = this._stringBundle.GetStringFromName(
"error.sync.quota.description");
buttons.push(new Weave.NotificationButton(
this._stringBundle.GetStringFromName(
"error.sync.viewQuotaButton.label"),
this._stringBundle.GetStringFromName(
"error.sync.viewQuotaButton.accesskey"),
function() { gSyncUI.openQuotaDialog(); return true; } )
);
}
else if (Weave.Status.enforceBackoff) {
priority = Weave.Notifications.PRIORITY_INFO;
buttons.push(new Weave.NotificationButton(
this._stringBundle.GetStringFromName("error.sync.serverStatusButton.label"),
this._stringBundle.GetStringFromName("error.sync.serverStatusButton.accesskey"),
function() { gSyncUI.openServerStatus(); return true; }
));
}
else {
priority = Weave.Notifications.PRIORITY_INFO;
buttons.push(new Weave.NotificationButton(
this._stringBundle.GetStringFromName("error.sync.tryAgainButton.label"),
this._stringBundle.GetStringFromName("error.sync.tryAgainButton.accesskey"),
function() { gSyncUI.doSync(); return true; }
));
}
let notification =
new Weave.Notification(title, description, null, priority, buttons);
Weave.Notifications.replaceTitle(notification);
if (this._wasDelayed && Weave.Status.sync != Weave.NO_SYNC_NODE_FOUND) {
title = this._stringBundle.GetStringFromName("error.sync.no_node_found.title");
Weave.Notifications.removeAll(title);
this._wasDelayed = false;
}
this.updateUI();
},
observe: function SUI_observe(subject, topic, data) {
if (this._unloaded) {
Cu.reportError("SyncUI observer called after unload: " + topic);
return;
}
switch (topic) {
case "weave:service:sync:start":
this.onActivityStart();
break;
case "weave:ui:sync:finish":
this.onSyncFinish();
break;
case "weave:ui:sync:error":
this.onSyncError();
break;
case "weave:service:sync:delayed":
this.onSyncDelay();
break;
case "weave:service:quota:remaining":
this.onQuotaNotice();
break;
case "weave:service:setup-complete":
this.onSetupComplete();
break;
case "weave:service:login:start":
this.onActivityStart();
break;
case "weave:service:login:finish":
this.onLoginFinish();
break;
case "weave:ui:login:error":
this.onLoginError();
break;
case "weave:service:logout:finish":
this.onLogout();
break;
case "weave:service:start-over":
this.onStartOver();
break;
case "weave:service:ready":
this.initUI();
break;
case "weave:notification:added":
this.initNotifications();
break;
case "weave:ui:clear-error":
this.clearError();
break;
}
},
QueryInterface: XPCOMUtils.generateQI([
Ci.nsIObserver,
Ci.nsISupportsWeakReference
])
};
XPCOMUtils.defineLazyGetter(gSyncUI, "_stringBundle", function() {
//XXXzpao these strings should probably be moved from /services to /browser... (bug 583381)
// but for now just make it work
return Cc["@mozilla.org/intl/stringbundle;1"].
getService(Ci.nsIStringBundleService).
createBundle("chrome://weave/locale/services/sync.properties");
});

View file

@ -0,0 +1,46 @@
<?xml version="1.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/. -->
<bindings id="tabBindings"
xmlns="http://www.mozilla.org/xbl"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:xbl="http://www.mozilla.org/xbl">
<binding id="tab-listing" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
<content>
<xul:hbox flex="1">
<xul:vbox pack="start">
<xul:image class="tabIcon"
xbl:inherits="src=icon"/>
</xul:vbox>
<xul:vbox pack="start" flex="1">
<xul:label xbl:inherits="value=title,selected"
crop="end" flex="1" class="title"/>
<xul:label xbl:inherits="value=url,selected"
crop="end" flex="1" class="url"/>
</xul:vbox>
</xul:hbox>
</content>
<handlers>
<handler event="dblclick" button="0">
<![CDATA[
RemoteTabViewer.openSelected();
]]>
</handler>
</handlers>
</binding>
<binding id="client-listing" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
<content>
<xul:hbox pack="start" align="center" onfocus="event.target.blur()" onselect="return false;">
<xul:image/>
<xul:label xbl:inherits="value=clientName"
class="clientName"
crop="center" flex="1"/>
</xul:hbox>
</content>
</binding>
</bindings>

View file

@ -0,0 +1,11 @@
/* 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/. */
richlistitem[type="tab"] {
-moz-binding: url(chrome://browser/content/sync/aboutSyncTabs-bindings.xml#tab-listing);
}
richlistitem[type="client"] {
-moz-binding: url(chrome://browser/content/sync/aboutSyncTabs-bindings.xml#client-listing);
}

View file

@ -0,0 +1,313 @@
/* 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/. */
var Cu = Components.utils;
Cu.import("resource://services-common/utils.js");
Cu.import("resource://services-sync/main.js");
Cu.import("resource:///modules/PlacesUIUtils.jsm");
Cu.import("resource://gre/modules/PlacesUtils.jsm", this);
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
var RemoteTabViewer = {
_tabsList: null,
init: function () {
Services.obs.addObserver(this, "weave:service:login:finish", false);
Services.obs.addObserver(this, "weave:engine:sync:finish", false);
this._tabsList = document.getElementById("tabsList");
this.buildList(true);
},
uninit: function () {
Services.obs.removeObserver(this, "weave:service:login:finish");
Services.obs.removeObserver(this, "weave:engine:sync:finish");
},
createItem: function(attrs) {
let item = document.createElement("richlistitem");
// Copy the attributes from the argument into the item
for (let attr in attrs) {
item.setAttribute(attr, attrs[attr]);
}
if (attrs["type"] == "tab") {
item.label = attrs.title != "" ? attrs.title : attrs.url;
}
return item;
},
filterTabs: function(event) {
let val = event.target.value.toLowerCase();
let numTabs = this._tabsList.getRowCount();
let clientTabs = 0;
let currentClient = null;
for (let i = 0; i < numTabs; i++) {
let item = this._tabsList.getItemAtIndex(i);
let hide = false;
if (item.getAttribute("type") == "tab") {
if (!item.getAttribute("url").toLowerCase().includes(val) &&
!item.getAttribute("title").toLowerCase().includes(val)) {
hide = true;
} else {
clientTabs++;
}
}
else if (item.getAttribute("type") == "client") {
if (currentClient) {
if (clientTabs == 0) {
currentClient.hidden = true;
}
}
currentClient = item;
clientTabs = 0;
}
item.hidden = hide;
}
if (clientTabs == 0) {
currentClient.hidden = true;
}
},
openSelected: function() {
let items = this._tabsList.selectedItems;
let urls = [];
for (let i = 0;i < items.length;i++) {
if (items[i].getAttribute("type") == "tab") {
urls.push(items[i].getAttribute("url"));
let index = this._tabsList.getIndexOfItem(items[i]);
this._tabsList.removeItemAt(index);
}
}
if (urls.length) {
getTopWin().gBrowser.loadTabs(urls);
this._tabsList.clearSelection();
}
},
bookmarkSingleTab: function() {
let item = this._tabsList.selectedItems[0];
let uri = Weave.Utils.makeURI(item.getAttribute("url"));
let title = item.getAttribute("title");
PlacesUIUtils.showBookmarkDialog({ action: "add"
, type: "bookmark"
, uri: uri
, title: title
, hiddenRows: [ "description"
, "location"
, "loadInSidebar"
, "keyword" ]
}, window.top);
},
bookmarkSelectedTabs: function() {
let items = this._tabsList.selectedItems;
let URIs = [];
for (let i = 0;i < items.length;i++) {
if (items[i].getAttribute("type") == "tab") {
let uri = Weave.Utils.makeURI(items[i].getAttribute("url"));
if (!uri) {
continue;
}
URIs.push(uri);
}
}
if (URIs.length) {
PlacesUIUtils.showBookmarkDialog({ action: "add"
, type: "folder"
, URIList: URIs
, hiddenRows: [ "description" ]
}, window.top);
}
},
getIcon: function (iconUri, defaultIcon) {
try {
let iconURI = Weave.Utils.makeURI(iconUri);
return PlacesUtils.favicons.getFaviconLinkForIcon(iconURI).spec;
} catch (ex) {
// Do nothing.
}
// Just give the provided default icon or the system's default.
return defaultIcon || PlacesUtils.favicons.defaultFavicon.spec;
},
_waitingForBuildList: false,
_buildListRequested: false,
buildList: function (force) {
if (this._waitingForBuildList) {
this._buildListRequested = true;
return;
}
this._waitingForBuildList = true;
this._buildListRequested = false;
this._clearTabList();
if (Weave.Service.isLoggedIn && this._refetchTabs(force)) {
this._generateWeaveTabList();
} else {
//XXXzpao We should say something about not being logged in & not having data
// or tell the appropriate condition. (bug 583344)
}
function complete() {
this._waitingForBuildList = false;
if (this._buildListRequested) {
CommonUtils.nextTick(this.buildList, this);
}
}
complete();
},
_clearTabList: function () {
let list = this._tabsList;
// Clear out existing richlistitems
let count = list.getRowCount();
if (count > 0) {
for (let i = count - 1; i >= 0; i--) {
list.removeItemAt(i);
}
}
},
_generateWeaveTabList: function () {
let engine = Weave.Service.engineManager.get("tabs");
let list = this._tabsList;
let seenURLs = new Set();
let localURLs = engine.getOpenURLs();
for (let [guid, client] in Iterator(engine.getAllClients())) {
// Create the client node, but don't add it in-case we don't show any tabs
let appendClient = true;
client.tabs.forEach(function({title, urlHistory, icon}) {
let url = urlHistory[0];
if (!url || localURLs.has(url) || seenURLs.has(url)) {
return;
}
seenURLs.add(url);
if (appendClient) {
let attrs = {
type: "client",
clientName: client.clientName,
class: Weave.Service.clientsEngine.isMobile(client.id) ? "mobile" : "desktop"
};
let clientEnt = this.createItem(attrs);
list.appendChild(clientEnt);
appendClient = false;
clientEnt.disabled = true;
}
let attrs = {
type: "tab",
title: title || url,
url: url,
icon: this.getIcon(icon),
}
let tab = this.createItem(attrs);
list.appendChild(tab);
}, this);
}
},
adjustContextMenu: function(event) {
let mode = "all";
switch (this._tabsList.selectedItems.length) {
case 0:
break;
case 1:
mode = "single"
break;
default:
mode = "multiple";
break;
}
let menu = document.getElementById("tabListContext");
let el = menu.firstChild;
while (el) {
let showFor = el.getAttribute("showFor");
if (showFor) {
el.hidden = showFor != mode && showFor != "all";
}
el = el.nextSibling;
}
},
_refetchTabs: function(force) {
if (!force) {
// Don't bother refetching tabs if we already did so recently
let lastFetch = 0;
try {
lastFetch = Services.prefs.getIntPref("services.sync.lastTabFetch");
}
catch (e) {
/* Just use the default value of 0 */
}
let now = Math.floor(Date.now() / 1000);
if (now - lastFetch < 30) {
return false;
}
}
// if Clients hasn't synced yet this session, we need to sync it as well.
if (Weave.Service.clientsEngine.lastSync == 0) {
Weave.Service.clientsEngine.sync();
}
// Force a sync only for the tabs engine
let engine = Weave.Service.engineManager.get("tabs");
engine.lastModified = null;
engine.sync();
Services.prefs.setIntPref("services.sync.lastTabFetch",
Math.floor(Date.now() / 1000));
return true;
},
observe: function(subject, topic, data) {
switch (topic) {
case "weave:service:login:finish":
this.buildList(true);
break;
case "weave:engine:sync:finish":
if (subject == "tabs") {
this.buildList(false);
}
break;
}
},
handleClick: function(event) {
if (event.target.getAttribute("type") != "tab") {
return;
}
if (event.button == 1) {
let url = event.target.getAttribute("url");
openUILink(url, event);
let index = this._tabsList.getIndexOfItem(event.target);
this._tabsList.removeItemAt(index);
}
}
}

View file

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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/. -->
<?xml-stylesheet href="chrome://browser/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/aboutSyncTabs.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/sync/aboutSyncTabs.css" type="text/css"?>
<!DOCTYPE window [
<!ENTITY % aboutSyncTabsDTD SYSTEM "chrome://browser/locale/aboutSyncTabs.dtd">
%aboutSyncTabsDTD;
]>
<window id="tabs-display"
onload="RemoteTabViewer.init()"
onunload="RemoteTabViewer.uninit()"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml"
title="&tabs.otherDevices.label;">
<script type="application/javascript;version=1.8" src="chrome://browser/content/sync/aboutSyncTabs.js"/>
<script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/>
<html:head>
<html:link rel="icon" href="chrome://browser/skin/sync-16.png"/>
</html:head>
<popupset id="contextmenus">
<menupopup id="tabListContext">
<menuitem label="&tabs.context.openTab.label;"
accesskey="&tabs.context.openTab.accesskey;"
oncommand="RemoteTabViewer.openSelected()"
showFor="single"/>
<menuitem label="&tabs.context.bookmarkSingleTab.label;"
accesskey="&tabs.context.bookmarkSingleTab.accesskey;"
oncommand="RemoteTabViewer.bookmarkSingleTab(event)"
showFor="single"/>
<menuitem label="&tabs.context.openMultipleTabs.label;"
accesskey="&tabs.context.openMultipleTabs.accesskey;"
oncommand="RemoteTabViewer.openSelected()"
showFor="multiple"/>
<menuitem label="&tabs.context.bookmarkMultipleTabs.label;"
accesskey="&tabs.context.bookmarkMultipleTabs.accesskey;"
oncommand="RemoteTabViewer.bookmarkSelectedTabs()"
showFor="multiple"/>
<menuseparator/>
<menuitem label="&tabs.context.refreshList.label;"
accesskey="&tabs.context.refreshList.accesskey;"
oncommand="RemoteTabViewer.buildList()"
showFor="all"/>
</menupopup>
</popupset>
<richlistbox context="tabListContext" id="tabsList" seltype="multiple"
align="center" flex="1"
onclick="RemoteTabViewer.handleClick(event)"
oncontextmenu="RemoteTabViewer.adjustContextMenu(event)">
<hbox id="headers" align="center">
<label id="tabsListHeading"
value="&tabs.otherDevices.label;"/>
<spacer flex="1"/>
<textbox type="search"
emptytext="&tabs.searchText.label;"
oncommand="RemoteTabViewer.filterTabs(event)"/>
</hbox>
</richlistbox>
</window>

View file

@ -0,0 +1,157 @@
/* 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/. */
var Ci = Components.interfaces;
var Cc = Components.classes;
var Cu = Components.utils;
Cu.import("resource://services-sync/main.js");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
const PIN_PART_LENGTH = 4;
const ADD_DEVICE_PAGE = 0;
const SYNC_KEY_PAGE = 1;
const DEVICE_CONNECTED_PAGE = 2;
var gSyncAddDevice = {
init: function init() {
this.pin1.setAttribute("maxlength", PIN_PART_LENGTH);
this.pin2.setAttribute("maxlength", PIN_PART_LENGTH);
this.pin3.setAttribute("maxlength", PIN_PART_LENGTH);
this.nextFocusEl = {pin1: this.pin2,
pin2: this.pin3,
pin3: this.wizard.getButton("next")};
this.throbber = document.getElementById("pairDeviceThrobber");
this.errorRow = document.getElementById("errorRow");
// Kick off a sync. That way the server will have the most recent data from
// this computer and it will show up immediately on the new device.
Weave.Service.scheduler.scheduleNextSync(0);
},
onPageShow: function onPageShow() {
this.wizard.getButton("back").hidden = true;
switch (this.wizard.pageIndex) {
case ADD_DEVICE_PAGE:
this.onTextBoxInput();
this.wizard.canRewind = false;
this.wizard.getButton("next").hidden = false;
this.pin1.focus();
break;
case SYNC_KEY_PAGE:
this.wizard.canAdvance = false;
this.wizard.canRewind = true;
this.wizard.getButton("back").hidden = false;
this.wizard.getButton("next").hidden = true;
document.getElementById("weavePassphrase").value =
Weave.Utils.hyphenatePassphrase(Weave.Service.identity.syncKey);
break;
case DEVICE_CONNECTED_PAGE:
this.wizard.canAdvance = true;
this.wizard.canRewind = false;
this.wizard.getButton("cancel").hidden = true;
break;
}
},
onWizardAdvance: function onWizardAdvance() {
switch (this.wizard.pageIndex) {
case ADD_DEVICE_PAGE:
this.startTransfer();
return false;
case DEVICE_CONNECTED_PAGE:
window.close();
return false;
}
return true;
},
startTransfer: function startTransfer() {
this.errorRow.hidden = true;
// When onAbort is called, Weave may already be gone.
const JPAKE_ERROR_USERABORT = Weave.JPAKE_ERROR_USERABORT;
let self = this;
let jpakeclient = this._jpakeclient = new Weave.JPAKEClient({
onPaired: function onPaired() {
let credentials = {account: Weave.Service.identity.account,
password: Weave.Service.identity.basicPassword,
synckey: Weave.Service.identity.syncKey,
serverURL: Weave.Service.serverURL};
jpakeclient.sendAndComplete(credentials);
},
onComplete: function onComplete() {
delete self._jpakeclient;
self.wizard.pageIndex = DEVICE_CONNECTED_PAGE;
// Schedule a Sync for soonish to fetch the data uploaded by the
// device with which we just paired.
Weave.Service.scheduler.scheduleNextSync(Weave.Service.scheduler.activeInterval);
},
onAbort: function onAbort(error) {
delete self._jpakeclient;
// Aborted by user, ignore.
if (error == JPAKE_ERROR_USERABORT) {
return;
}
self.errorRow.hidden = false;
self.throbber.hidden = true;
self.pin1.value = self.pin2.value = self.pin3.value = "";
self.pin1.disabled = self.pin2.disabled = self.pin3.disabled = false;
self.pin1.focus();
}
});
this.throbber.hidden = false;
this.pin1.disabled = this.pin2.disabled = this.pin3.disabled = true;
this.wizard.canAdvance = false;
let pin = this.pin1.value + this.pin2.value + this.pin3.value;
let expectDelay = false;
jpakeclient.pairWithPIN(pin, expectDelay);
},
onWizardBack: function onWizardBack() {
if (this.wizard.pageIndex != SYNC_KEY_PAGE)
return true;
this.wizard.pageIndex = ADD_DEVICE_PAGE;
return false;
},
onWizardCancel: function onWizardCancel() {
if (this._jpakeclient) {
this._jpakeclient.abort();
delete this._jpakeclient;
}
return true;
},
onTextBoxInput: function onTextBoxInput(textbox) {
if (textbox && textbox.value.length == PIN_PART_LENGTH)
this.nextFocusEl[textbox.id].focus();
this.wizard.canAdvance = (this.pin1.value.length == PIN_PART_LENGTH
&& this.pin2.value.length == PIN_PART_LENGTH
&& this.pin3.value.length == PIN_PART_LENGTH);
},
goToSyncKeyPage: function goToSyncKeyPage() {
this.wizard.pageIndex = SYNC_KEY_PAGE;
}
};
// onWizardAdvance() and onPageShow() are run before init() so we'll set
// these up as lazy getters.
["wizard", "pin1", "pin2", "pin3"].forEach(function (id) {
XPCOMUtils.defineLazyGetter(gSyncAddDevice, id, function() {
return document.getElementById(id);
});
});

View file

@ -0,0 +1,129 @@
<?xml version="1.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/. -->
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/syncSetup.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/syncCommon.css" type="text/css"?>
<!DOCTYPE window [
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
<!ENTITY % syncBrandDTD SYSTEM "chrome://browser/locale/syncBrand.dtd">
<!ENTITY % syncSetupDTD SYSTEM "chrome://browser/locale/syncSetup.dtd">
%brandDTD;
%syncBrandDTD;
%syncSetupDTD;
]>
<wizard xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml"
id="wizard"
title="&pairDevice.title.label;"
windowtype="Sync:AddDevice"
persist="screenX screenY"
onwizardnext="return gSyncAddDevice.onWizardAdvance();"
onwizardback="return gSyncAddDevice.onWizardBack();"
onwizardcancel="gSyncAddDevice.onWizardCancel();"
onload="gSyncAddDevice.init();">
<script type="application/javascript"
src="chrome://browser/content/sync/addDevice.js"/>
<script type="application/javascript"
src="chrome://browser/content/sync/utils.js"/>
<script type="application/javascript"
src="chrome://browser/content/utilityOverlay.js"/>
<script type="application/javascript"
src="chrome://global/content/printUtils.js"/>
<wizardpage id="addDevicePage"
label="&pairDevice.title.label;"
onpageshow="gSyncAddDevice.onPageShow();">
<description>
&pairDevice.dialog.description.label;
<label class="text-link"
value="&addDevice.showMeHow.label;"
href="http://www.palemoon.org/sync/help/easy-setup.shtml"/>
</description>
<separator class="groove-thin"/>
<description>
&addDevice.dialog.enterCode.label;
</description>
<separator class="groove-thin"/>
<vbox align="center">
<textbox id="pin1"
class="pin"
oninput="gSyncAddDevice.onTextBoxInput(this);"
onfocus="this.select();"
/>
<textbox id="pin2"
class="pin"
oninput="gSyncAddDevice.onTextBoxInput(this);"
onfocus="this.select();"
/>
<textbox id="pin3"
class="pin"
oninput="gSyncAddDevice.onTextBoxInput(this);"
onfocus="this.select();"
/>
</vbox>
<separator class="groove-thin"/>
<vbox id="pairDeviceThrobber" align="center" hidden="true">
<image/>
</vbox>
<hbox id="errorRow" pack="center" hidden="true">
<image class="statusIcon" status="error"/>
<label class="status"
value="&addDevice.dialog.tryAgain.label;"/>
</hbox>
<spacer flex="3"/>
<label class="text-link"
value="&addDevice.dontHaveDevice.label;"
onclick="gSyncAddDevice.goToSyncKeyPage();"/>
</wizardpage>
<!-- Need a non-empty label here, otherwise we get a default label on Mac -->
<wizardpage id="syncKeyPage"
label=" "
onpageshow="gSyncAddDevice.onPageShow();">
<description>
&addDevice.dialog.recoveryKey.label;
</description>
<spacer/>
<groupbox>
<label value="&recoveryKeyEntry.label;"
accesskey="&recoveryKeyEntry.accesskey;"
control="weavePassphrase"/>
<textbox id="weavePassphrase"
readonly="true"/>
</groupbox>
<groupbox align="center">
<description>&recoveryKeyBackup.description;</description>
<hbox>
<button id="printSyncKeyButton"
label="&button.syncKeyBackup.print.label;"
accesskey="&button.syncKeyBackup.print.accesskey;"
oncommand="gSyncUtils.passphrasePrint('weavePassphrase');"/>
<button id="saveSyncKeyButton"
label="&button.syncKeyBackup.save.label;"
accesskey="&button.syncKeyBackup.save.accesskey;"
oncommand="gSyncUtils.passphraseSave('weavePassphrase');"/>
</hbox>
</groupbox>
</wizardpage>
<wizardpage id="deviceConnectedPage"
label="&addDevice.dialog.connected.label;"
onpageshow="gSyncAddDevice.onPageShow();">
<vbox align="center">
<image id="successPageIcon"/>
</vbox>
<separator/>
<description class="normal">
&addDevice.dialog.successful.label;
</description>
</wizardpage>
</wizard>

View file

@ -0,0 +1,234 @@
/* 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/. */
var Ci = Components.interfaces;
var Cc = Components.classes;
Components.utils.import("resource://services-sync/main.js");
Components.utils.import("resource://gre/modules/Services.jsm");
var Change = {
_dialog: null,
_dialogType: null,
_status: null,
_statusIcon: null,
_firstBox: null,
_secondBox: null,
get _passphraseBox() {
delete this._passphraseBox;
return this._passphraseBox = document.getElementById("passphraseBox");
},
get _currentPasswordInvalid() {
return Weave.Status.login == Weave.LOGIN_FAILED_LOGIN_REJECTED;
},
get _updatingPassphrase() {
return this._dialogType == "UpdatePassphrase";
},
onLoad: function Change_onLoad() {
/* Load labels */
let introText = document.getElementById("introText");
let introText2 = document.getElementById("introText2");
let warningText = document.getElementById("warningText");
// load some other elements & info from the window
this._dialog = document.getElementById("change-dialog");
this._dialogType = window.arguments[0];
this._duringSetup = window.arguments[1];
this._status = document.getElementById("status");
this._statusIcon = document.getElementById("statusIcon");
this._statusRow = document.getElementById("statusRow");
this._firstBox = document.getElementById("textBox1");
this._secondBox = document.getElementById("textBox2");
this._dialog.getButton("finish").disabled = true;
this._dialog.getButton("back").hidden = true;
this._stringBundle =
Services.strings.createBundle("chrome://browser/locale/syncGenericChange.properties");
switch (this._dialogType) {
case "UpdatePassphrase":
case "ResetPassphrase":
document.getElementById("textBox1Row").hidden = true;
document.getElementById("textBox2Row").hidden = true;
document.getElementById("passphraseLabel").value
= this._str("new.recoverykey.label");
document.getElementById("passphraseSpacer").hidden = false;
if (this._updatingPassphrase) {
document.getElementById("passphraseHelpBox").hidden = false;
document.title = this._str("new.recoverykey.title");
introText.textContent = this._str("new.recoverykey.introText");
this._dialog.getButton("finish").label
= this._str("new.recoverykey.acceptButton");
}
else {
document.getElementById("generatePassphraseButton").hidden = false;
document.getElementById("passphraseBackupButtons").hidden = false;
let pp = Weave.Service.identity.syncKey;
if (Weave.Utils.isPassphrase(pp))
pp = Weave.Utils.hyphenatePassphrase(pp);
this._passphraseBox.value = pp;
this._passphraseBox.focus();
document.title = this._str("change.recoverykey.title");
introText.textContent = this._str("change.synckey.introText2");
warningText.textContent = this._str("change.recoverykey.warningText");
this._dialog.getButton("finish").label
= this._str("change.recoverykey.acceptButton");
if (this._duringSetup) {
this._dialog.getButton("finish").disabled = false;
}
}
break;
case "ChangePassword":
document.getElementById("passphraseRow").hidden = true;
let box1label = document.getElementById("textBox1Label");
let box2label = document.getElementById("textBox2Label");
box1label.value = this._str("new.password.label");
if (this._currentPasswordInvalid) {
document.title = this._str("new.password.title");
introText.textContent = this._str("new.password.introText");
this._dialog.getButton("finish").label
= this._str("new.password.acceptButton");
document.getElementById("textBox2Row").hidden = true;
}
else {
document.title = this._str("change.password.title");
box2label.value = this._str("new.password.confirm");
introText.textContent = this._str("change.password3.introText");
warningText.textContent = this._str("change.password.warningText");
this._dialog.getButton("finish").label
= this._str("change.password.acceptButton");
}
break;
}
document.getElementById("change-page")
.setAttribute("label", document.title);
},
_clearStatus: function _clearStatus() {
this._status.value = "";
this._statusIcon.removeAttribute("status");
},
_updateStatus: function Change__updateStatus(str, state) {
this._updateStatusWithString(this._str(str), state);
},
_updateStatusWithString: function Change__updateStatusWithString(string, state) {
this._statusRow.hidden = false;
this._status.value = string;
this._statusIcon.setAttribute("status", state);
let error = state == "error";
this._dialog.getButton("cancel").disabled = !error;
this._dialog.getButton("finish").disabled = !error;
document.getElementById("printSyncKeyButton").disabled = !error;
document.getElementById("saveSyncKeyButton").disabled = !error;
if (state == "success")
window.setTimeout(window.close, 1500);
},
onDialogAccept: function() {
switch (this._dialogType) {
case "UpdatePassphrase":
case "ResetPassphrase":
return this.doChangePassphrase();
break;
case "ChangePassword":
return this.doChangePassword();
break;
}
},
doGeneratePassphrase: function () {
let passphrase = Weave.Utils.generatePassphrase();
this._passphraseBox.value = Weave.Utils.hyphenatePassphrase(passphrase);
this._dialog.getButton("finish").disabled = false;
},
doChangePassphrase: function Change_doChangePassphrase() {
let pp = Weave.Utils.normalizePassphrase(this._passphraseBox.value);
if (this._updatingPassphrase) {
Weave.Service.identity.syncKey = pp;
if (Weave.Service.login()) {
this._updateStatus("change.recoverykey.success", "success");
Weave.Service.persistLogin();
Weave.Service.scheduler.delayedAutoConnect(0);
}
else {
this._updateStatus("new.passphrase.status.incorrect", "error");
}
}
else {
this._updateStatus("change.recoverykey.label", "active");
if (Weave.Service.changePassphrase(pp))
this._updateStatus("change.recoverykey.success", "success");
else
this._updateStatus("change.recoverykey.error", "error");
}
return false;
},
doChangePassword: function Change_doChangePassword() {
if (this._currentPasswordInvalid) {
Weave.Service.identity.basicPassword = this._firstBox.value;
if (Weave.Service.login()) {
this._updateStatus("change.password.status.success", "success");
Weave.Service.persistLogin();
}
else {
this._updateStatus("new.password.status.incorrect", "error");
}
}
else {
this._updateStatus("change.password.status.active", "active");
if (Weave.Service.changePassword(this._firstBox.value))
this._updateStatus("change.password.status.success", "success");
else
this._updateStatus("change.password.status.error", "error");
}
return false;
},
validate: function (event) {
let valid = false;
let errorString = "";
if (this._dialogType == "ChangePassword") {
if (this._currentPasswordInvalid)
[valid, errorString] = gSyncUtils.validatePassword(this._firstBox);
else
[valid, errorString] = gSyncUtils.validatePassword(this._firstBox, this._secondBox);
}
else {
//Pale Moon: Enforce minimum length of 8 for allowed custom passphrase
//and don't restrict it to "out of sync" situations only. People who
//go to this page generally know what they are doing ;)
valid = this._passphraseBox.value.length >= 8;
}
if (errorString == "")
this._clearStatus();
else
this._updateStatusWithString(errorString, "error");
this._statusRow.hidden = valid;
this._dialog.getButton("finish").disabled = !valid;
},
_str: function Change__string(str) {
return this._stringBundle.GetStringFromName(str);
}
};

View file

@ -0,0 +1,123 @@
<?xml version="1.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/. -->
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/syncSetup.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/syncCommon.css" type="text/css"?>
<!DOCTYPE window [
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
<!ENTITY % syncBrandDTD SYSTEM "chrome://browser/locale/syncBrand.dtd">
<!ENTITY % syncSetupDTD SYSTEM "chrome://browser/locale/syncSetup.dtd">
%brandDTD;
%syncBrandDTD;
%syncSetupDTD;
]>
<wizard xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml"
id="change-dialog"
windowtype="Weave:ChangeSomething"
persist="screenX screenY"
onwizardnext="Change.onLoad()"
onwizardfinish="return Change.onDialogAccept();">
<script type="application/javascript"
src="chrome://browser/content/sync/genericChange.js"/>
<script type="application/javascript"
src="chrome://browser/content/sync/utils.js"/>
<script type="application/javascript"
src="chrome://global/content/printUtils.js"/>
<wizardpage id="change-page"
label="">
<description id="introText">
</description>
<separator class="thin"/>
<groupbox>
<grid>
<columns>
<column align="right"/>
<column flex="3"/>
<column flex="1"/>
</columns>
<rows>
<row id="textBox1Row" align="center">
<label id="textBox1Label" control="textBox1"/>
<textbox id="textBox1" type="password" oninput="Change.validate()"/>
<spacer/>
</row>
<row id="textBox2Row" align="center">
<label id="textBox2Label" control="textBox2"/>
<textbox id="textBox2" type="password" oninput="Change.validate()"/>
<spacer/>
</row>
</rows>
</grid>
<vbox id="passphraseRow">
<hbox flex="1">
<label id="passphraseLabel" control="passphraseBox"/>
<spacer flex="1"/>
<label id="generatePassphraseButton"
hidden="true"
value="&syncGenerateNewKey.label;"
class="text-link inline-link"
onclick="event.stopPropagation();
Change.doGeneratePassphrase();"/>
</hbox>
<textbox id="passphraseBox"
flex="1"
onfocus="this.select()"
oninput="Change.validate()"/>
</vbox>
<vbox id="feedback" pack="center">
<hbox id="statusRow" align="center">
<image id="statusIcon" class="statusIcon"/>
<label id="status" class="status" value=" "/>
</hbox>
</vbox>
</groupbox>
<separator class="thin"/>
<hbox id="passphraseBackupButtons"
hidden="true"
pack="center">
<button id="printSyncKeyButton"
label="&button.syncKeyBackup.print.label;"
accesskey="&button.syncKeyBackup.print.accesskey;"
oncommand="gSyncUtils.passphrasePrint('passphraseBox');"/>
<button id="saveSyncKeyButton"
label="&button.syncKeyBackup.save.label;"
accesskey="&button.syncKeyBackup.save.accesskey;"
oncommand="gSyncUtils.passphraseSave('passphraseBox');"/>
</hbox>
<vbox id="passphraseHelpBox"
hidden="true">
<description>
&existingRecoveryKey.description;
<label class="text-link"
href="http://www.palemoon.org/sync/help/recoverykey.shtml">
&addDevice.showMeHow.label;
</label>
</description>
</vbox>
<spacer id="passphraseSpacer"
flex="1"
hidden="true"/>
<description id="warningText" class="data">
</description>
<spacer flex="1"/>
</wizardpage>
</wizard>

View file

@ -0,0 +1,22 @@
# 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/.
browser.jar:
content/browser/sync/aboutSyncTabs.xul
content/browser/sync/aboutSyncTabs.js
content/browser/sync/aboutSyncTabs.css
content/browser/sync/aboutSyncTabs-bindings.xml
content/browser/sync/setup.xul
content/browser/sync/addDevice.js
content/browser/sync/addDevice.xul
content/browser/sync/setup.js
content/browser/sync/genericChange.xul
content/browser/sync/genericChange.js
content/browser/sync/key.xhtml
content/browser/sync/notification.xml
content/browser/sync/quota.xul
content/browser/sync/quota.js
content/browser/sync/utils.js
content/browser/sync/progress.js
content/browser/sync/progress.xhtml

View file

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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/. -->
<!DOCTYPE html [
<!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
%htmlDTD;
<!ENTITY % syncBrandDTD SYSTEM "chrome://browser/locale/syncBrand.dtd">
%syncBrandDTD;
<!ENTITY % syncKeyDTD SYSTEM "chrome://browser/locale/syncKey.dtd">
%syncKeyDTD;
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd" >
%globalDTD;
]>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>&syncKey.page.title;</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta name="robots" content="noindex"/>
<style type="text/css">
#synckey { font-size: 150% }
footer { font-size: 70% }
/* Bug 575675: Need to have an a:visited rule in a chrome document. */
a:visited { color: purple; }
</style>
</head>
<body dir="&locale.dir;">
<h1>&syncKey.page.title;</h1>
<p id="synckey" dir="ltr">SYNCKEY</p>
<p>&syncKey.page.description2;</p>
<div id="column1">
<h2>&syncKey.keepItSecret.heading;</h2>
<p>&syncKey.keepItSecret.description;</p>
</div>
<div id="column2">
<h2>&syncKey.keepItSafe.heading;</h2>
<p><em>&syncKey.keepItSafe1.description;</em>&syncKey.keepItSafe2.description;<em>&syncKey.keepItSafe3.description;</em>&syncKey.keepItSafe4a.description;</p>
</div>
<p>&syncKey.findOutMore1.label;<a href="http://www.palemoon.org/sync/">http://www.palemoon.org/sync/</a>&syncKey.findOutMore2.label;</p>
<footer>
&syncKey.footer1.label;<a id="tosLink" href="termsURL">termsURL</a>&syncKey.footer2.label;<a id="ppLink" href="privacyURL">privacyURL</a>&syncKey.footer3.label;
</footer>
</body>
</html>

View file

@ -0,0 +1,8 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
JAR_MANIFESTS += ['jar.mn']

View file

@ -0,0 +1,129 @@
<?xml version="1.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/. -->
<!DOCTYPE bindings [
<!ENTITY % notificationDTD SYSTEM "chrome://global/locale/notification.dtd">
%notificationDTD;
]>
<bindings id="notificationBindings"
xmlns="http://www.mozilla.org/xbl"
xmlns:xbl="http://www.mozilla.org/xbl"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<binding id="notificationbox" extends="chrome://global/content/bindings/notification.xml#notificationbox">
<content>
<xul:vbox xbl:inherits="hidden=notificationshidden">
<xul:spacer/>
<children includes="notification"/>
</xul:vbox>
<children/>
</content>
<implementation>
<constructor><![CDATA[
let temp = {};
Cu.import("resource://services-common/observers.js", temp);
temp.Observers.add("weave:notification:added", this.onNotificationAdded, this);
temp.Observers.add("weave:notification:removed", this.onNotificationRemoved, this);
for each (var notification in Weave.Notifications.notifications)
this._appendNotification(notification);
]]></constructor>
<destructor><![CDATA[
let temp = {};
Cu.import("resource://services-common/observers.js", temp);
temp.Observers.remove("weave:notification:added", this.onNotificationAdded, this);
temp.Observers.remove("weave:notification:removed", this.onNotificationRemoved, this);
]]></destructor>
<method name="onNotificationAdded">
<parameter name="subject"/>
<parameter name="data"/>
<body><![CDATA[
this._appendNotification(subject);
]]></body>
</method>
<method name="onNotificationRemoved">
<parameter name="subject"/>
<parameter name="data"/>
<body><![CDATA[
// If the view of the notification hasn't been removed yet, remove it.
var notifications = this.allNotifications;
for each (var notification in notifications) {
if (notification.notification == subject) {
notification.close();
break;
}
}
]]></body>
</method>
<method name="_appendNotification">
<parameter name="notification"/>
<body><![CDATA[
var node = this.appendNotification(notification.description,
notification.title,
notification.iconURL,
notification.priority,
notification.buttons);
node.notification = notification;
]]></body>
</method>
</implementation>
</binding>
<binding id="notification" extends="chrome://global/content/bindings/notification.xml#notification">
<content>
<xul:hbox class="notification-inner outset" flex="1" xbl:inherits="type">
<xul:toolbarbutton ondblclick="event.stopPropagation();"
class="messageCloseButton close-icon tabbable"
xbl:inherits="hidden=hideclose"
tooltiptext="&closeNotification.tooltip;"
oncommand="document.getBindingParent(this).close()"/>
<xul:hbox anonid="details" align="center" flex="1">
<xul:image anonid="messageImage" class="messageImage" xbl:inherits="src=image,type"/>
<xul:description anonid="messageText" class="messageText" xbl:inherits="xbl:text=label"/>
<!-- The children are the buttons defined by the notification. -->
<xul:hbox oncommand="document.getBindingParent(this)._doButtonCommand(event);">
<children/>
</xul:hbox>
</xul:hbox>
</xul:hbox>
</content>
<implementation>
<!-- Note: this used to be a field, but for some reason it kept getting
- reset to its default value for TabNotification elements.
- As a property, that doesn't happen, even though the property stores
- its value in a JS property |_notification| that is not defined
- in XBL as a field or property. Maybe this is wrong, but it works.
-->
<property name="notification"
onget="return this._notification"
onset="this._notification = val; return val;"/>
<method name="close">
<body><![CDATA[
Weave.Notifications.remove(this.notification);
// We should be able to call the base class's close method here
// to remove the notification element from the notification box,
// but we can't because of bug 373652, so instead we copied its code
// and execute it below.
var control = this.control;
if (control)
control.removeNotification(this);
else
this.hidden = true;
]]></body>
</method>
</implementation>
</binding>
</bindings>

View file

@ -0,0 +1,71 @@
/* 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/. */
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://services-sync/main.js");
var gProgressBar;
var gCounter = 0;
function onLoad(event) {
Services.obs.addObserver(onEngineSync, "weave:engine:sync:finish", false);
Services.obs.addObserver(onEngineSync, "weave:engine:sync:error", false);
Services.obs.addObserver(onServiceSync, "weave:service:sync:finish", false);
Services.obs.addObserver(onServiceSync, "weave:service:sync:error", false);
gProgressBar = document.getElementById('uploadProgressBar');
if (Services.prefs.getPrefType("services.sync.firstSync") != Ci.nsIPrefBranch.PREF_INVALID) {
gProgressBar.hidden = false;
}
else {
gProgressBar.hidden = true;
}
}
function onUnload(event) {
cleanUpObservers();
}
function cleanUpObservers() {
try {
Services.obs.removeObserver(onEngineSync, "weave:engine:sync:finish");
Services.obs.removeObserver(onEngineSync, "weave:engine:sync:error");
Services.obs.removeObserver(onServiceSync, "weave:service:sync:finish");
Services.obs.removeObserver(onServiceSync, "weave:service:sync:error");
}
catch (e) {
// may be double called by unload & exit. Ignore.
}
}
function onEngineSync(subject, topic, data) {
// The Clients engine syncs first. At this point we don't necessarily know
// yet how many engines will be enabled, so we'll ignore the Clients engine
// and evaluate how many engines are enabled when the first "real" engine
// syncs.
if (data == "clients") {
return;
}
if (!gCounter &&
Services.prefs.getPrefType("services.sync.firstSync") != Ci.nsIPrefBranch.PREF_INVALID) {
gProgressBar.max = Weave.Service.engineManager.getEnabled().length;
}
gCounter += 1;
gProgressBar.setAttribute("value", gCounter);
}
function onServiceSync(subject, topic, data) {
// To address the case where 0 engines are synced, we will fill the
// progress bar so the user knows that the sync has finished.
gProgressBar.setAttribute("value", gProgressBar.max);
cleanUpObservers();
}
function closeTab() {
window.close();
}

View file

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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/. -->
<!DOCTYPE html [
<!ENTITY % htmlDTD
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"DTD/xhtml1-strict.dtd">
%htmlDTD;
<!ENTITY % syncProgressDTD
SYSTEM "chrome://browser/locale/syncProgress.dtd">
%syncProgressDTD;
<!ENTITY % syncSetupDTD
SYSTEM "chrome://browser/locale/syncSetup.dtd">
%syncSetupDTD;
<!ENTITY % globalDTD
SYSTEM "chrome://global/locale/global.dtd">
%globalDTD;
]>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>&syncProgress.pageTitle;</title>
<link rel="stylesheet" type="text/css" media="all"
href="chrome://browser/skin/syncProgress.css"/>
<link rel="icon" type="image/png" id="favicon"
href="chrome://browser/skin/sync-16.png"/>
<script type="text/javascript;version=1.8"
src="chrome://browser/content/sync/progress.js"/>
</head>
<body onload="onLoad(event)" onunload="onUnload(event)" dir="&locale.dir;">
<title>&setup.successPage.title;</title>
<div id="floatingBox" class="main-content">
<div id="title">
<h1>&setup.successPage.title;</h1>
</div>
<div id="successLogo">
<img id="brandSyncLogo" src="chrome://browser/skin/sync-128.png" alt="&syncProgress.logoAltText;" />
</div>
<div id="loadingText">
<p id="blurb">&syncProgress.textBlurb; </p>
</div>
<div id="progressBar">
<progress id="uploadProgressBar" value="0"/>
</div>
<div id="bottomRow">
<button id="closeButton" onclick="closeTab()">&syncProgress.closeButton; </button>
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,247 @@
/* 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/. */
const Ci = Components.interfaces;
const Cc = Components.classes;
const Cr = Components.results;
const Cu = Components.utils;
Cu.import("resource://services-sync/main.js");
Cu.import("resource://gre/modules/DownloadUtils.jsm");
var gSyncQuota = {
init: function init() {
this.bundle = document.getElementById("quotaStrings");
let caption = document.getElementById("treeCaption");
caption.firstChild.nodeValue = this.bundle.getString("quota.treeCaption.label");
gUsageTreeView.init();
this.tree = document.getElementById("usageTree");
this.tree.view = gUsageTreeView;
this.loadData();
},
loadData: function loadData() {
this._usage_req = Weave.Service.getStorageInfo(Weave.INFO_COLLECTION_USAGE,
function (error, usage) {
delete gSyncQuota._usage_req;
// displayUsageData handles null values, so no need to check 'error'.
gUsageTreeView.displayUsageData(usage);
});
let usageLabel = document.getElementById("usageLabel");
let bundle = this.bundle;
this._quota_req = Weave.Service.getStorageInfo(Weave.INFO_QUOTA,
function (error, quota) {
delete gSyncQuota._quota_req;
if (error) {
usageLabel.value = bundle.getString("quota.usageError.label");
return;
}
let used = gSyncQuota.convertKB(quota[0]);
if (!quota[1]) {
// No quota on the server.
usageLabel.value = bundle.getFormattedString(
"quota.usageNoQuota.label", used);
return;
}
let percent = Math.round(100 * quota[0] / quota[1]);
let total = gSyncQuota.convertKB(quota[1]);
usageLabel.value = bundle.getFormattedString(
"quota.usagePercentage.label", [percent].concat(used).concat(total));
});
},
onCancel: function onCancel() {
if (this._usage_req) {
this._usage_req.abort();
}
if (this._quota_req) {
this._quota_req.abort();
}
return true;
},
onAccept: function onAccept() {
let engines = gUsageTreeView.getEnginesToDisable();
for each (let engine in engines) {
Weave.Service.engineManager.get(engine).enabled = false;
}
if (engines.length) {
// The 'Weave' object will disappear once the window closes.
let Service = Weave.Service;
Weave.Utils.nextTick(function() { Service.sync(); });
}
return this.onCancel();
},
convertKB: function convertKB(value) {
return DownloadUtils.convertByteUnits(value * 1024);
}
};
var gUsageTreeView = {
_ignored: {keys: true,
meta: true,
clients: true},
/*
* Internal data structures underlaying the tree.
*/
_collections: [],
_byname: {},
init: function init() {
let retrievingLabel = gSyncQuota.bundle.getString("quota.retrieving.label");
for each (let engine in Weave.Service.engineManager.getEnabled()) {
if (this._ignored[engine.name])
continue;
// Some engines use the same pref, which means they can only be turned on
// and off together. We need to combine them here as well.
let existing = this._byname[engine.prefName];
if (existing) {
existing.engines.push(engine.name);
continue;
}
let obj = {name: engine.prefName,
title: this._collectionTitle(engine),
engines: [engine.name],
enabled: true,
sizeLabel: retrievingLabel};
this._collections.push(obj);
this._byname[engine.prefName] = obj;
}
},
_collectionTitle: function _collectionTitle(engine) {
try {
return gSyncQuota.bundle.getString(
"collection." + engine.prefName + ".label");
} catch (ex) {
return engine.Name;
}
},
/*
* Process the quota information as returned by info/collection_usage.
*/
displayUsageData: function displayUsageData(data) {
for each (let coll in this._collections) {
coll.size = 0;
// If we couldn't retrieve any data, just blank out the label.
if (!data) {
coll.sizeLabel = "";
continue;
}
for each (let engineName in coll.engines)
coll.size += data[engineName] || 0;
let sizeLabel = "";
sizeLabel = gSyncQuota.bundle.getFormattedString(
"quota.sizeValueUnit.label", gSyncQuota.convertKB(coll.size));
coll.sizeLabel = sizeLabel;
}
let sizeColumn = this.treeBox.columns.getNamedColumn("size");
this.treeBox.invalidateColumn(sizeColumn);
},
/*
* Handle click events on the tree.
*/
onTreeClick: function onTreeClick(event) {
if (event.button == 2)
return;
let cell = this.treeBox.getCellAt(event.clientX, event.clientY);
if (cell.col && cell.col.id == "enabled")
this.toggle(cell.row);
},
/*
* Toggle enabled state of an engine.
*/
toggle: function toggle(row) {
// Update the tree
let collection = this._collections[row];
collection.enabled = !collection.enabled;
this.treeBox.invalidateRow(row);
},
/*
* Return a list of engines (or rather their pref names) that should be
* disabled.
*/
getEnginesToDisable: function getEnginesToDisable() {
// Tycho: return [coll.name for each (coll in this._collections) if (!coll.enabled)];
let engines = [];
for each (let coll in this._collections) {
if (!coll.enabled) {
engines.push(coll.name);
}
}
return engines;
},
// nsITreeView
get rowCount() {
return this._collections.length;
},
getRowProperties: function(index) { return ""; },
getCellProperties: function(row, col) { return ""; },
getColumnProperties: function(col) { return ""; },
isContainer: function(index) { return false; },
isContainerOpen: function(index) { return false; },
isContainerEmpty: function(index) { return false; },
isSeparator: function(index) { return false; },
isSorted: function() { return false; },
canDrop: function(index, orientation, dataTransfer) { return false; },
drop: function(row, orientation, dataTransfer) {},
getParentIndex: function(rowIndex) {},
hasNextSibling: function(rowIndex, afterIndex) { return false; },
getLevel: function(index) { return 0; },
getImageSrc: function(row, col) {},
getCellValue: function(row, col) {
return this._collections[row].enabled;
},
getCellText: function getCellText(row, col) {
let collection = this._collections[row];
switch (col.id) {
case "collection":
return collection.title;
case "size":
return collection.sizeLabel;
default:
return "";
}
},
setTree: function setTree(tree) {
this.treeBox = tree;
},
toggleOpenState: function(index) {},
cycleHeader: function(col) {},
selectionChanged: function() {},
cycleCell: function(row, col) {},
isEditable: function(row, col) { return false; },
isSelectable: function (row, col) { return false; },
setCellValue: function(row, col, value) {},
setCellText: function(row, col, value) {},
performAction: function(action) {},
performActionOnRow: function(action, row) {},
performActionOnCell: function(action, row, col) {}
};

View file

@ -0,0 +1,65 @@
<?xml version="1.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/. -->
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/syncQuota.css"?>
<!DOCTYPE dialog [
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
<!ENTITY % syncBrandDTD SYSTEM "chrome://browser/locale/syncBrand.dtd">
<!ENTITY % syncQuotaDTD SYSTEM "chrome://browser/locale/syncQuota.dtd">
%brandDTD;
%syncBrandDTD;
%syncQuotaDTD;
]>
<dialog id="quotaDialog"
windowtype="Sync:ViewQuota"
persist="screenX screenY width height"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml"
onload="gSyncQuota.init()"
buttons="accept,cancel"
title="&quota.dialogTitle.label;"
ondialogcancel="return gSyncQuota.onCancel();"
ondialogaccept="return gSyncQuota.onAccept();">
<script type="application/javascript"
src="chrome://browser/content/sync/quota.js"/>
<stringbundleset id="stringbundleset">
<stringbundle id="quotaStrings"
src="chrome://browser/locale/syncQuota.properties"/>
</stringbundleset>
<vbox flex="1">
<label id="usageLabel"
value="&quota.retrievingInfo.label;"/>
<separator/>
<tree id="usageTree"
seltype="single"
hidecolumnpicker="true"
onclick="gUsageTreeView.onTreeClick(event);"
flex="1">
<treecols>
<treecol id="enabled"
type="checkbox"
fixed="true"/>
<splitter class="tree-splitter"/>
<treecol id="collection"
label="&quota.typeColumn.label;"
flex="1"/>
<splitter class="tree-splitter"/>
<treecol id="size"
label="&quota.sizeColumn.label;"
flex="1"/>
</treecols>
<treechildren flex="1"/>
</tree>
<separator/>
<description id="treeCaption"> </description>
</vbox>
</dialog>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,491 @@
<?xml version="1.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/. -->
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/syncSetup.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/syncCommon.css" type="text/css"?>
<!DOCTYPE window [
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
<!ENTITY % syncBrandDTD SYSTEM "chrome://browser/locale/syncBrand.dtd">
<!ENTITY % syncSetupDTD SYSTEM "chrome://browser/locale/syncSetup.dtd">
%brandDTD;
%syncBrandDTD;
%syncSetupDTD;
]>
<wizard id="wizard"
title="&accountSetupTitle.label;"
windowtype="Weave:AccountSetup"
persist="screenX screenY"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml"
onwizardnext="return gSyncSetup.onWizardAdvance()"
onwizardback="return gSyncSetup.onWizardBack()"
onwizardcancel="gSyncSetup.onWizardCancel()"
onload="gSyncSetup.init()">
<script type="application/javascript"
src="chrome://browser/content/sync/setup.js"/>
<script type="application/javascript"
src="chrome://browser/content/sync/utils.js"/>
<script type="application/javascript"
src="chrome://browser/content/utilityOverlay.js"/>
<script type="application/javascript"
src="chrome://global/content/printUtils.js"/>
<wizardpage id="addDevicePage"
label="&pairDevice.title.label;"
onpageshow="gSyncSetup.onPageShow()">
<description>
&pairDevice.dialog.description.label;
<label class="text-link"
value="&addDevice.showMeHow.label;"
href="http://www.palemoon.org/sync/help/easy-setup.shtml"/>
</description>
<separator class="groove-thin"/>
<description>
&addDevice.dialog.enterCode.label;
</description>
<separator class="groove-thin"/>
<vbox align="center">
<textbox id="pin1"
class="pin"
oninput="gSyncSetup.onPINInput(this);"
onfocus="this.select();"
/>
<textbox id="pin2"
class="pin"
oninput="gSyncSetup.onPINInput(this);"
onfocus="this.select();"
/>
<textbox id="pin3"
class="pin"
oninput="gSyncSetup.onPINInput(this);"
onfocus="this.select();"
/>
</vbox>
<separator class="groove-thin"/>
<vbox id="pairDeviceThrobber" align="center" hidden="true">
<image/>
</vbox>
<hbox id="pairDeviceErrorRow" pack="center" hidden="true">
<image class="statusIcon" status="error"/>
<label class="status"
value="&addDevice.dialog.tryAgain.label;"/>
</hbox>
</wizardpage>
<wizardpage id="pickSetupType"
label="&syncBrand.fullName.label;"
onpageshow="gSyncSetup.onPageShow()">
<vbox align="center" flex="1">
<description style="padding: 0 7em;">
&setup.pickSetupType.description2;
</description>
<spacer flex="3"/>
<button id="newAccount"
class="accountChoiceButton"
label="&button.createNewAccount.label;"
oncommand="gSyncSetup.startNewAccountSetup()"
align="center"/>
<spacer flex="1"/>
</vbox>
<separator class="groove"/>
<vbox align="center" flex="1">
<spacer flex="1"/>
<button id="existingAccount"
class="accountChoiceButton"
label="&button.haveAccount.label;"
oncommand="gSyncSetup.useExistingAccount()"/>
<spacer flex="3"/>
</vbox>
</wizardpage>
<wizardpage label="&setup.newAccountDetailsPage.title.label;"
id="newAccountStart"
onextra1="gSyncSetup.onSyncOptions()"
onpageshow="gSyncSetup.onPageShow();">
<grid>
<columns>
<column/>
<column class="inputColumn" flex="1"/>
</columns>
<rows>
<row id="emailRow" align="center">
<label value="&setup.emailAddress.label;"
accesskey="&setup.emailAddress.accesskey;"
control="weaveEmail"/>
<textbox id="weaveEmail"
oninput="gSyncSetup.onEmailInput()"/>
</row>
<row id="emailFeedbackRow" align="center" hidden="true">
<spacer/>
<hbox>
<image class="statusIcon"/>
<label class="status" value=" "/>
</hbox>
</row>
<row id="passwordRow" align="center">
<label value="&setup.choosePassword.label;"
accesskey="&setup.choosePassword.accesskey;"
control="weavePassword"/>
<textbox id="weavePassword"
type="password"
onchange="gSyncSetup.onPasswordChange()"/>
</row>
<row id="confirmRow" align="center">
<label value="&setup.confirmPassword.label;"
accesskey="&setup.confirmPassword.accesskey;"
control="weavePasswordConfirm"/>
<textbox id="weavePasswordConfirm"
type="password"
onchange="gSyncSetup.onPasswordChange()"/>
</row>
<row id="passwordFeedbackRow" align="center" hidden="true">
<spacer/>
<hbox>
<image class="statusIcon"/>
<label class="status" value=" "/>
</hbox>
</row>
<row align="center">
<label control="server"
value="&server.label;"/>
<menulist id="server"
oncommand="gSyncSetup.onServerCommand()"
oninput="gSyncSetup.onServerInput()">
<menupopup>
<menuitem label="&serverType.default.label;"
value="main"/>
<menuitem label="&serverType.custom2.label;"
value="custom"/>
</menupopup>
</menulist>
</row>
<row id="serverFeedbackRow" align="center" hidden="true">
<spacer/>
<hbox>
<image class="statusIcon"/>
<label class="status" value=" "/>
</hbox>
</row>
<row id="TOSRow" align="center">
<spacer/>
<hbox align="center">
<checkbox id="tos"
accesskey="&setup.tosAgree1.accesskey;"
oncommand="this.focus(); gSyncSetup.checkFields();"/>
<description id="tosDesc"
flex="1"
onclick="document.getElementById('tos').focus();
document.getElementById('tos').click()">
&setup.tosAgree1.label;
<label class="text-link inline-link"
onclick="event.stopPropagation();gSyncUtils.openToS();">
&setup.tosLink.label;
</label>
&setup.tosAgree2.label;
<label class="text-link inline-link"
onclick="event.stopPropagation();gSyncUtils.openPrivacyPolicy();">
&setup.ppLink.label;
</label>
&setup.tosAgree3.label;
</description>
</hbox>
</row>
</rows>
</grid>
<spacer flex="1"/>
<vbox flex="1" align="center">
<browser height="150"
width="500"
id="captcha"
type="content"
disablehistory="true"/>
<spacer flex="1"/>
<hbox id="captchaFeedback">
<image class="statusIcon"/>
<label class="status" value=" "/>
</hbox>
</vbox>
</wizardpage>
<wizardpage id="addDevice"
label="&pairDevice.title.label;"
onextra1="gSyncSetup.onSyncOptions()"
onpageshow="gSyncSetup.onPageShow()">
<description>
&pairDevice.setup.description.label;
<label class="text-link"
value="&addDevice.showMeHow.label;"
href="http://www.palemoon.org/sync/help/easy-setup.shtml"/>
</description>
<label value="&addDevice.setup.enterCode.label;"
control="easySetupPIN1"/>
<spacer flex="1"/>
<vbox align="center" flex="1">
<textbox id="easySetupPIN1"
class="pin"
value=""
readonly="true"
/>
<textbox id="easySetupPIN2"
class="pin"
value=""
readonly="true"
/>
<textbox id="easySetupPIN3"
class="pin"
value=""
readonly="true"
/>
</vbox>
<spacer flex="3"/>
<label class="text-link"
value="&addDevice.dontHaveDevice.label;"
onclick="gSyncSetup.manualSetup();"/>
</wizardpage>
<wizardpage id="existingAccount"
label="&setup.signInPage.title.label;"
onextra1="gSyncSetup.onSyncOptions()"
onpageshow="gSyncSetup.onPageShow()">
<grid>
<columns>
<column/>
<column class="inputColumn" flex="1"/>
</columns>
<rows>
<row id="existingAccountRow" align="center">
<label id="existingAccountLabel"
value="&signIn.account2.label;"
accesskey="&signIn.account2.accesskey;"
control="existingAccount"/>
<textbox id="existingAccountName"
oninput="gSyncSetup.checkFields(event)"
onchange="gSyncSetup.checkFields(event)"/>
</row>
<row id="existingPasswordRow" align="center">
<label id="existingPasswordLabel"
value="&signIn.password.label;"
accesskey="&signIn.password.accesskey;"
control="existingPassword"/>
<textbox id="existingPassword"
type="password"
onkeyup="gSyncSetup.checkFields(event)"
onchange="gSyncSetup.checkFields(event)"/>
</row>
<row id="existingPasswordFeedbackRow" align="center" hidden="true">
<spacer/>
<hbox>
<image class="statusIcon"/>
<label class="status" value=" "/>
</hbox>
</row>
<row align="center">
<spacer/>
<label class="text-link"
value="&resetPassword.label;"
onclick="gSyncUtils.resetPassword(); return false;"/>
</row>
<row align="center">
<label control="existingServer"
value="&server.label;"/>
<menulist id="existingServer"
oncommand="gSyncSetup.onExistingServerCommand()"
oninput="gSyncSetup.onExistingServerInput()">
<menupopup>
<menuitem label="&serverType.default.label;"
value="main"/>
<menuitem label="&serverType.custom2.label;"
value="custom"/>
</menupopup>
</menulist>
</row>
<row id="existingServerFeedbackRow" align="center" hidden="true">
<spacer/>
<hbox>
<image class="statusIcon"/>
<vbox>
<label class="status" value=" "/>
</vbox>
</hbox>
</row>
</rows>
</grid>
<groupbox>
<label id="existingPassphraseLabel"
value="&signIn.recoveryKey.label;"
accesskey="&signIn.recoveryKey.accesskey;"
control="existingPassphrase"/>
<textbox id="existingPassphrase"
oninput="gSyncSetup.checkFields()"/>
<hbox id="login-throbber" hidden="true">
<image/>
<label value="&verifying.label;"/>
</hbox>
<vbox align="left" id="existingPassphraseFeedbackRow" hidden="true">
<hbox>
<image class="statusIcon"/>
<label class="status" value=" "/>
</hbox>
</vbox>
</groupbox>
<vbox id="passphraseHelpBox">
<description>
&existingRecoveryKey.description;
<label class="text-link"
href="http://www.palemoon.org/sync/help/recoverykey.shtml">
&addDevice.showMeHow.label;
</label>
<spacer id="passphraseHelpSpacer"/>
<label class="text-link"
onclick="gSyncSetup.resetPassphrase(); return false;">
&resetSyncKey.label;
</label>
</description>
</vbox>
</wizardpage>
<wizardpage id="syncOptionsPage"
label="&setup.optionsPage.title;"
onpageshow="gSyncSetup.onPageShow()">
<groupbox id="syncOptions">
<grid>
<columns>
<column/>
<column flex="1" style="-moz-margin-end: 2px"/>
</columns>
<rows>
<row align="center">
<label value="&syncDeviceName.label;"
accesskey="&syncDeviceName.accesskey;"
control="syncComputerName"/>
<textbox id="syncComputerName" flex="1"
onchange="gSyncUtils.changeName(this)"/>
</row>
<row>
<label value="&syncMy.label;" />
<vbox>
<checkbox label="&engine.addons.label;"
accesskey="&engine.addons.accesskey;"
id="engine.addons"
checked="false"
hidden="true"/>
<checkbox label="&engine.bookmarks.label;"
accesskey="&engine.bookmarks.accesskey;"
id="engine.bookmarks"
checked="true"/>
<checkbox label="&engine.passwords.label;"
accesskey="&engine.passwords.accesskey;"
id="engine.passwords"
checked="true"/>
<checkbox label="&engine.prefs.label;"
accesskey="&engine.prefs.accesskey;"
id="engine.prefs"
checked="true"/>
<checkbox label="&engine.history.label;"
accesskey="&engine.history.accesskey;"
id="engine.history"
checked="true"/>
<checkbox label="&engine.tabs.label;"
accesskey="&engine.tabs.accesskey;"
id="engine.tabs"
checked="true"/>
</vbox>
</row>
</rows>
</grid>
</groupbox>
<groupbox id="mergeOptions">
<radiogroup id="mergeChoiceRadio" pack="start">
<grid>
<columns>
<column/>
<column flex="1"/>
</columns>
<rows flex="1">
<row align="center">
<radio id="resetClient"
class="mergeChoiceButton"
aria-labelledby="resetClientLabel"/>
<label id="resetClientLabel" control="resetClient">
<html:strong>&choice2.merge.recommended.label;</html:strong>
&choice2a.merge.main.label;
</label>
</row>
<row align="center">
<radio id="wipeClient"
class="mergeChoiceButton"
aria-labelledby="wipeClientLabel"/>
<label id="wipeClientLabel"
control="wipeClient">
&choice2a.client.main.label;
</label>
</row>
<row align="center">
<radio id="wipeRemote"
class="mergeChoiceButton"
aria-labelledby="wipeRemoteLabel"/>
<label id="wipeRemoteLabel"
control="wipeRemote">
&choice2a.server.main.label;
</label>
</row>
</rows>
</grid>
</radiogroup>
</groupbox>
</wizardpage>
<wizardpage id="syncOptionsConfirm"
label="&setup.optionsConfirmPage.title;"
onpageshow="gSyncSetup.onPageShow()">
<deck id="chosenActionDeck">
<vbox id="chosenActionMerge" class="confirm">
<description class="normal">
&confirm.merge2.label;
</description>
</vbox>
<vbox id="chosenActionWipeClient" class="confirm">
<description class="normal">
&confirm.client3.label;
</description>
<separator class="thin"/>
<vbox id="dataList">
<label class="data indent" id="bookmarkCount"/>
<label class="data indent" id="historyCount"/>
<label class="data indent" id="passwordCount"/>
<label class="data indent" id="addonCount"/>
<label class="data indent" id="prefsWipe"
value="&engine.prefs.label;"/>
</vbox>
<separator class="thin"/>
<description class="normal">
&confirm.client2.moreinfo.label;
</description>
</vbox>
<vbox id="chosenActionWipeServer" class="confirm">
<description class="normal">
&confirm.server2.label;
</description>
<separator class="thin"/>
<vbox id="clientList">
</vbox>
</vbox>
</deck>
</wizardpage>
<!-- In terms of the wizard flow shown to the user, the 'syncOptionsConfirm'
page above is not the last wizard page. To prevent the wizard binding from
assuming that it is, we're inserting this dummy page here. This also means
that the wizard needs to always be closed manually via wizardFinish(). -->
<wizardpage>
</wizardpage>
</wizard>

View file

@ -0,0 +1,218 @@
/* 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/. */
// Equivalent to 0o600 permissions; used for saved Sync Recovery Key.
// This constant can be replaced when the equivalent values are available to
// chrome JS; see Bug 433295 and Bug 757351.
const PERMISSIONS_RWUSR = 0x180;
// Weave should always exist before before this file gets included.
var gSyncUtils = {
get bundle() {
delete this.bundle;
return this.bundle = Services.strings.createBundle("chrome://browser/locale/syncSetup.properties");
},
// opens in a new window if we're in a modal prefwindow world, in a new tab otherwise
_openLink: function (url) {
let thisDocEl = document.documentElement,
openerDocEl = window.opener && window.opener.document.documentElement;
if (thisDocEl.id == "accountSetup" && window.opener &&
openerDocEl.id == "BrowserPreferences" && !openerDocEl.instantApply)
openUILinkIn(url, "window");
else if (thisDocEl.id == "BrowserPreferences" && !thisDocEl.instantApply)
openUILinkIn(url, "window");
else if (document.documentElement.id == "change-dialog")
Services.wm.getMostRecentWindow("navigator:browser")
.openUILinkIn(url, "tab");
else
openUILinkIn(url, "tab");
},
changeName: function changeName(input) {
// Make sure to update to a modified name, e.g., empty-string -> default
Weave.Service.clientsEngine.localName = input.value;
input.value = Weave.Service.clientsEngine.localName;
},
openChange: function openChange(type, duringSetup) {
// Just re-show the dialog if it's already open
let openedDialog = Services.wm.getMostRecentWindow("Sync:" + type);
if (openedDialog != null) {
openedDialog.focus();
return;
}
// Open up the change dialog
let changeXUL = "chrome://browser/content/sync/genericChange.xul";
let changeOpt = "centerscreen,chrome,resizable=no";
Services.ww.activeWindow.openDialog(changeXUL, "", changeOpt,
type, duringSetup);
},
changePassword: function () {
if (Weave.Utils.ensureMPUnlocked())
this.openChange("ChangePassword");
},
resetPassphrase: function (duringSetup) {
if (Weave.Utils.ensureMPUnlocked())
this.openChange("ResetPassphrase", duringSetup);
},
updatePassphrase: function () {
if (Weave.Utils.ensureMPUnlocked())
this.openChange("UpdatePassphrase");
},
resetPassword: function () {
this._openLink(Weave.Service.pwResetURL);
},
openToS: function () {
this._openLink(Weave.Svc.Prefs.get("termsURL"));
},
openPrivacyPolicy: function () {
this._openLink(Weave.Svc.Prefs.get("privacyURL"));
},
openFirstSyncProgressPage: function () {
this._openLink("about:sync-progress");
},
/**
* Prepare an invisible iframe with the passphrase backup document.
* Used by both the print and saving methods.
*
* @param elid : ID of the form element containing the passphrase.
* @param callback : Function called once the iframe has loaded.
*/
_preparePPiframe: function(elid, callback) {
let pp = document.getElementById(elid).value;
// Create an invisible iframe whose contents we can print.
let iframe = document.createElement("iframe");
iframe.setAttribute("src", "chrome://browser/content/sync/key.xhtml");
iframe.collapsed = true;
document.documentElement.appendChild(iframe);
iframe.contentWindow.addEventListener("load", function() {
iframe.contentWindow.removeEventListener("load", arguments.callee, false);
// Insert the Sync Key into the page.
let el = iframe.contentDocument.getElementById("synckey");
el.firstChild.nodeValue = pp;
// Insert the TOS and Privacy Policy URLs into the page.
let termsURL = Weave.Svc.Prefs.get("termsURL");
el = iframe.contentDocument.getElementById("tosLink");
el.setAttribute("href", termsURL);
el.firstChild.nodeValue = termsURL;
let privacyURL = Weave.Svc.Prefs.get("privacyURL");
el = iframe.contentDocument.getElementById("ppLink");
el.setAttribute("href", privacyURL);
el.firstChild.nodeValue = privacyURL;
callback(iframe);
}, false);
},
/**
* Print passphrase backup document.
*
* @param elid : ID of the form element containing the passphrase.
*/
passphrasePrint: function(elid) {
this._preparePPiframe(elid, function(iframe) {
let webBrowserPrint = iframe.contentWindow
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebBrowserPrint);
let printSettings = PrintUtils.getPrintSettings();
// Display no header/footer decoration except for the date.
printSettings.headerStrLeft
= printSettings.headerStrCenter
= printSettings.headerStrRight
= printSettings.footerStrLeft
= printSettings.footerStrCenter = "";
printSettings.footerStrRight = "&D";
try {
webBrowserPrint.print(printSettings, null);
} catch (ex) {
// print()'s return codes are expressed as exceptions. Ignore.
}
});
},
/**
* Save passphrase backup document to disk as HTML file.
*
* @param elid : ID of the form element containing the passphrase.
*/
passphraseSave: function(elid) {
let dialogTitle = this.bundle.GetStringFromName("save.recoverykey.title");
let defaultSaveName = this.bundle.GetStringFromName("save.recoverykey.defaultfilename");
this._preparePPiframe(elid, function(iframe) {
let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
let fpCallback = function fpCallback_done(aResult) {
if (aResult == Ci.nsIFilePicker.returnOK ||
aResult == Ci.nsIFilePicker.returnReplace) {
let stream = Cc["@mozilla.org/network/file-output-stream;1"].
createInstance(Ci.nsIFileOutputStream);
stream.init(fp.file, -1, PERMISSIONS_RWUSR, 0);
let serializer = new XMLSerializer();
let output = serializer.serializeToString(iframe.contentDocument);
output = output.replace(/<!DOCTYPE (.|\n)*?]>/,
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" ' +
'"DTD/xhtml1-strict.dtd">');
output = Weave.Utils.encodeUTF8(output);
stream.write(output, output.length);
}
};
fp.init(window, dialogTitle, Ci.nsIFilePicker.modeSave);
fp.appendFilters(Ci.nsIFilePicker.filterHTML);
fp.defaultString = defaultSaveName;
fp.open(fpCallback);
return false;
});
},
/**
* validatePassword
*
* @param el1 : the first textbox element in the form
* @param el2 : the second textbox element, if omitted it's an update form
*
* returns [valid, errorString]
*/
validatePassword: function (el1, el2) {
let valid = false;
let val1 = el1.value;
let val2 = el2 ? el2.value : "";
let error = "";
if (!el2)
valid = val1.length >= Weave.MIN_PASS_LENGTH;
else if (val1 && val1 == Weave.Service.identity.username)
error = "change.password.pwSameAsUsername";
else if (val1 && val1 == Weave.Service.identity.account)
error = "change.password.pwSameAsEmail";
else if (val1 && val1 == Weave.Service.identity.basicPassword)
error = "change.password.pwSameAsPassword";
else if (val1 && val2) {
if (val1 == val2 && val1.length >= Weave.MIN_PASS_LENGTH)
valid = true;
else if (val1.length < Weave.MIN_PASS_LENGTH)
error = "change.password.tooShort";
else if (val1 != val2)
error = "change.password.mismatch";
}
let errorString = error ? Weave.Utils.getErrorString(error) : "";
return [valid, errorString];
}
};

View file

@ -0,0 +1,15 @@
<!-- 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/. -->
<!ENTITY % brandDTD
SYSTEM "chrome://branding/locale/brand.dtd">
%brandDTD;
<!-- These strings are used in the sync progress upload page -->
<!ENTITY syncProgress.pageTitle "Your First Sync">
<!ENTITY syncProgress.textBlurb "Your data is now being encrypted and uploaded in the background. You can close this tab and continue using &brandShortName;.">
<!ENTITY syncProgress.closeButton "Close">
<!ENTITY syncProgress.logoAltText "&brandShortName; logo">
<!ENTITY syncProgress.diffText "&brandShortName; will now automatically sync in the background. You can close this tab and continue using &brandShortName;.">