summaryrefslogtreecommitdiffstats
path: root/application/basilisk/components
diff options
context:
space:
mode:
authorAscrod <32915892+Ascrod@users.noreply.github.com>2019-04-18 20:35:10 -0400
committerAscrod <32915892+Ascrod@users.noreply.github.com>2019-04-18 20:35:10 -0400
commitaf7e140d4ed8f5bc9a69da2f0338ad3cb1319dec (patch)
tree4aac6c4383fb9e279fccb13c65a4e44595fd4cf6 /application/basilisk/components
parent40fc72376411587e7bf9985fb9545eca1c9aaa8e (diff)
parent51722cd4fecb5c8c79a302f2771cad71535df5ea (diff)
downloadUXP-af7e140d4ed8f5bc9a69da2f0338ad3cb1319dec.tar
UXP-af7e140d4ed8f5bc9a69da2f0338ad3cb1319dec.tar.gz
UXP-af7e140d4ed8f5bc9a69da2f0338ad3cb1319dec.tar.lz
UXP-af7e140d4ed8f5bc9a69da2f0338ad3cb1319dec.tar.xz
UXP-af7e140d4ed8f5bc9a69da2f0338ad3cb1319dec.zip
Merge branch 'master' into default-pref
Diffstat (limited to 'application/basilisk/components')
-rw-r--r--application/basilisk/components/about/AboutRedirector.cpp10
-rw-r--r--application/basilisk/components/build/nsModule.cpp4
-rw-r--r--application/basilisk/components/customizableui/CustomizableUI.jsm1
-rw-r--r--application/basilisk/components/customizableui/CustomizableWidgets.jsm138
-rw-r--r--application/basilisk/components/customizableui/content/panelUI.inc.xul110
-rw-r--r--application/basilisk/components/customizableui/moz.build5
-rw-r--r--application/basilisk/components/moz.build7
-rw-r--r--application/basilisk/components/nsBrowserGlue.js119
-rw-r--r--application/basilisk/components/places/PlacesUIUtils.jsm6
-rw-r--r--application/basilisk/components/places/moz.build2
-rw-r--r--application/basilisk/components/preferences/in-content/jar.mn4
-rw-r--r--application/basilisk/components/preferences/in-content/preferences.js2
-rw-r--r--application/basilisk/components/preferences/in-content/preferences.xul8
-rw-r--r--application/basilisk/components/preferences/in-content/sync.js442
-rw-r--r--application/basilisk/components/preferences/in-content/sync.xul327
-rw-r--r--application/basilisk/components/sync/aboutSyncTabs-bindings.xml46
-rw-r--r--application/basilisk/components/sync/aboutSyncTabs.css11
-rw-r--r--application/basilisk/components/sync/aboutSyncTabs.js307
-rw-r--r--application/basilisk/components/sync/aboutSyncTabs.xul68
-rw-r--r--application/basilisk/components/sync/addDevice.js157
-rw-r--r--application/basilisk/components/sync/addDevice.xul129
-rw-r--r--application/basilisk/components/sync/genericChange.js234
-rw-r--r--application/basilisk/components/sync/genericChange.xul123
-rw-r--r--application/basilisk/components/sync/jar.mn22
-rw-r--r--application/basilisk/components/sync/key.xhtml54
-rw-r--r--application/basilisk/components/sync/moz.build (renamed from application/basilisk/components/syncedtabs/jar.mn)7
-rw-r--r--application/basilisk/components/sync/notification.xml129
-rw-r--r--application/basilisk/components/sync/progress.js71
-rw-r--r--application/basilisk/components/sync/progress.xhtml55
-rw-r--r--application/basilisk/components/sync/quota.js247
-rw-r--r--application/basilisk/components/sync/quota.xul65
-rw-r--r--application/basilisk/components/sync/setup.js1071
-rw-r--r--application/basilisk/components/sync/setup.xul491
-rw-r--r--application/basilisk/components/sync/utils.js218
-rw-r--r--application/basilisk/components/syncedtabs/EventEmitter.jsm45
-rw-r--r--application/basilisk/components/syncedtabs/SyncedTabsDeckComponent.js169
-rw-r--r--application/basilisk/components/syncedtabs/SyncedTabsDeckStore.js60
-rw-r--r--application/basilisk/components/syncedtabs/SyncedTabsDeckView.js116
-rw-r--r--application/basilisk/components/syncedtabs/SyncedTabsListStore.js235
-rw-r--r--application/basilisk/components/syncedtabs/TabListComponent.js138
-rw-r--r--application/basilisk/components/syncedtabs/TabListView.js568
-rw-r--r--application/basilisk/components/syncedtabs/moz.build17
-rw-r--r--application/basilisk/components/syncedtabs/sidebar.js30
-rw-r--r--application/basilisk/components/syncedtabs/sidebar.xhtml114
-rw-r--r--application/basilisk/components/syncedtabs/util.js23
45 files changed, 3667 insertions, 2538 deletions
diff --git a/application/basilisk/components/about/AboutRedirector.cpp b/application/basilisk/components/about/AboutRedirector.cpp
index 2b8608484..d52b063e2 100644
--- a/application/basilisk/components/about/AboutRedirector.cpp
+++ b/application/basilisk/components/about/AboutRedirector.cpp
@@ -92,10 +92,16 @@ static RedirEntry kRedirMap[] = {
"welcomeback", "chrome://browser/content/aboutWelcomeBack.xhtml",
nsIAboutModule::ALLOW_SCRIPT
},
+#ifdef MOZ_SERVICES_SYNC
+ {
+ "sync-progress", "chrome://browser/content/sync/progress.xhtml",
+ nsIAboutModule::ALLOW_SCRIPT
+ },
{
"sync-tabs", "chrome://browser/content/sync/aboutSyncTabs.xul",
nsIAboutModule::ALLOW_SCRIPT
},
+#endif
{ "home", "chrome://browser/content/abouthome/aboutHome.xhtml",
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
nsIAboutModule::URI_MUST_LOAD_IN_CHILD |
@@ -123,10 +129,6 @@ static RedirEntry kRedirMap[] = {
},
#endif
{
- "accounts", "chrome://browser/content/aboutaccounts/aboutaccounts.xhtml",
- nsIAboutModule::ALLOW_SCRIPT
- },
- {
"reader", "chrome://global/content/reader/aboutReader.html",
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
nsIAboutModule::ALLOW_SCRIPT |
diff --git a/application/basilisk/components/build/nsModule.cpp b/application/basilisk/components/build/nsModule.cpp
index 4e082ab6c..3fdde8823 100644
--- a/application/basilisk/components/build/nsModule.cpp
+++ b/application/basilisk/components/build/nsModule.cpp
@@ -97,12 +97,14 @@ static const mozilla::Module::ContractIDEntry kBrowserContracts[] = {
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "searchreset", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "sessionrestore", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "welcomeback", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
+#ifdef MOZ_SERVICES_SYNC
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "sync-tabs", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
+ { NS_ABOUT_MODULE_CONTRACTID_PREFIX "sync-progress", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
+#endif
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "home", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "newtab", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "preferences", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "downloads", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
- { NS_ABOUT_MODULE_CONTRACTID_PREFIX "accounts", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
#ifdef MOZ_SERVICES_HEALTHREPORT
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "healthreport", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
#endif
diff --git a/application/basilisk/components/customizableui/CustomizableUI.jsm b/application/basilisk/components/customizableui/CustomizableUI.jsm
index be274da71..d56d63d99 100644
--- a/application/basilisk/components/customizableui/CustomizableUI.jsm
+++ b/application/basilisk/components/customizableui/CustomizableUI.jsm
@@ -197,7 +197,6 @@ var CustomizableUIInternal = {
"find-button",
"preferences-button",
"add-ons-button",
- "sync-button",
];
if (!AppConstants.MOZ_DEV_EDITION) {
diff --git a/application/basilisk/components/customizableui/CustomizableWidgets.jsm b/application/basilisk/components/customizableui/CustomizableWidgets.jsm
index 53812762d..d4a191a97 100644
--- a/application/basilisk/components/customizableui/CustomizableWidgets.jsm
+++ b/application/basilisk/components/customizableui/CustomizableWidgets.jsm
@@ -283,144 +283,6 @@ const CustomizableWidgets = [
log.debug("History view is being hidden!");
}
}, {
- id: "sync-button",
- label: "remotetabs-panelmenu.label",
- tooltiptext: "remotetabs-panelmenu.tooltiptext2",
- type: "view",
- viewId: "PanelUI-remotetabs",
- defaultArea: CustomizableUI.AREA_PANEL,
- deckIndices: {
- DECKINDEX_TABS: 0,
- DECKINDEX_TABSDISABLED: 1,
- DECKINDEX_FETCHING: 2,
- DECKINDEX_NOCLIENTS: 3,
- },
- onCreated(aNode) {
- // Add an observer to the button so we get the animation during sync.
- // (Note the observer sets many attributes, including label and
- // tooltiptext, but we only want the 'syncstatus' attribute for the
- // animation)
- let doc = aNode.ownerDocument;
- let obnode = doc.createElementNS(kNSXUL, "observes");
- obnode.setAttribute("element", "sync-status");
- obnode.setAttribute("attribute", "syncstatus");
- aNode.appendChild(obnode);
- },
- setDeckIndex(index) {
- let deck = this._tabsList.ownerDocument.getElementById("PanelUI-remotetabs-deck");
- // We call setAttribute instead of relying on the XBL property setter due
- // to things going wrong when we try and set the index before the XBL
- // binding has been created - see bug 1241851 for the gory details.
- deck.setAttribute("selectedIndex", index);
- },
-
- _showTabsPromise: Promise.resolve(),
- // Update the tab list after any existing in-flight updates are complete.
- _showTabs() {
- this._showTabsPromise = this._showTabsPromise.then(() => {
- return this.__showTabs();
- });
- },
- // Return a new promise to update the tab list.
- __showTabs() {
- let doc = this._tabsList.ownerDocument;
- return SyncedTabs.getTabClients().then(clients => {
- // The view may have been hidden while the promise was resolving.
- if (!this._tabsList) {
- return;
- }
- if (clients.length === 0 && !SyncedTabs.hasSyncedThisSession) {
- // the "fetching tabs" deck is being shown - let's leave it there.
- // When that first sync completes we'll be notified and update.
- return;
- }
-
- if (clients.length === 0) {
- this.setDeckIndex(this.deckIndices.DECKINDEX_NOCLIENTS);
- return;
- }
-
- this.setDeckIndex(this.deckIndices.DECKINDEX_TABS);
- this._clearTabList();
- SyncedTabs.sortTabClientsByLastUsed(clients, 50 /* maxTabs */);
- let fragment = doc.createDocumentFragment();
-
- for (let client of clients) {
- // add a menu separator for all clients other than the first.
- if (fragment.lastChild) {
- let separator = doc.createElementNS(kNSXUL, "menuseparator");
- fragment.appendChild(separator);
- }
- this._appendClient(client, fragment);
- }
- this._tabsList.appendChild(fragment);
- }).catch(err => {
- Cu.reportError(err);
- }).then(() => {
- // an observer for tests.
- Services.obs.notifyObservers(null, "synced-tabs-menu:test:tabs-updated", null);
- });
- },
- _clearTabList () {
- let list = this._tabsList;
- while (list.lastChild) {
- list.lastChild.remove();
- }
- },
- _showNoClientMessage() {
- this._appendMessageLabel("notabslabel");
- },
- _appendMessageLabel(messageAttr, appendTo = null) {
- if (!appendTo) {
- appendTo = this._tabsList;
- }
- let message = this._tabsList.getAttribute(messageAttr);
- let doc = this._tabsList.ownerDocument;
- let messageLabel = doc.createElementNS(kNSXUL, "label");
- messageLabel.textContent = message;
- appendTo.appendChild(messageLabel);
- return messageLabel;
- },
- _appendClient: function (client, attachFragment) {
- let doc = attachFragment.ownerDocument;
- // Create the element for the remote client.
- let clientItem = doc.createElementNS(kNSXUL, "label");
- clientItem.setAttribute("itemtype", "client");
- let window = doc.defaultView;
- clientItem.setAttribute("tooltiptext",
- window.gSyncUI.formatLastSyncDate(new Date(client.lastModified)));
- clientItem.textContent = client.name;
-
- attachFragment.appendChild(clientItem);
-
- if (client.tabs.length == 0) {
- let label = this._appendMessageLabel("notabsforclientlabel", attachFragment);
- label.setAttribute("class", "PanelUI-remotetabs-notabsforclient-label");
- } else {
- for (let tab of client.tabs) {
- let tabEnt = this._createTabElement(doc, tab);
- attachFragment.appendChild(tabEnt);
- }
- }
- },
- _createTabElement(doc, tabInfo) {
- let item = doc.createElementNS(kNSXUL, "toolbarbutton");
- let tooltipText = (tabInfo.title ? tabInfo.title + "\n" : "") + tabInfo.url;
- item.setAttribute("itemtype", "tab");
- item.setAttribute("class", "subviewbutton");
- item.setAttribute("targetURI", tabInfo.url);
- item.setAttribute("label", tabInfo.title != "" ? tabInfo.title : tabInfo.url);
- item.setAttribute("image", tabInfo.icon);
- item.setAttribute("tooltiptext", tooltipText);
- // We need to use "click" instead of "command" here so openUILink
- // respects different buttons (eg, to open in a new tab).
- item.addEventListener("click", e => {
- doc.defaultView.openUILink(tabInfo.url, e);
- CustomizableUI.hidePanelForNode(item);
- });
- return item;
- },
- }, {
id: "privatebrowsing-button",
shortcutId: "key_privatebrowsing",
defaultArea: CustomizableUI.AREA_PANEL,
diff --git a/application/basilisk/components/customizableui/content/panelUI.inc.xul b/application/basilisk/components/customizableui/content/panelUI.inc.xul
index 8ebd93327..da8077554 100644
--- a/application/basilisk/components/customizableui/content/panelUI.inc.xul
+++ b/application/basilisk/components/customizableui/content/panelUI.inc.xul
@@ -20,27 +20,6 @@
oncommand="gMenuButtonUpdateBadge.onMenuPanelCommand(event);"
wrap="true"
hidden="true"/>
- <hbox id="PanelUI-footer-fxa">
- <hbox id="PanelUI-fxa-status"
- defaultlabel="&fxaSignIn.label;"
- signedinTooltiptext="&syncSettings.label;"
- tooltiptext="&syncSettings.label;"
- errorlabel="&fxaSignInError.label;"
- unverifiedlabel="&fxaUnverified.label;"
- settingslabel="&syncSettings.label;"
- onclick="if (event.which == 1) gFxAccounts.onMenuPanelCommand();">
- <image id="PanelUI-fxa-avatar"/>
- <toolbarbutton id="PanelUI-fxa-label"
- fxabrandname="&syncBrand.fxAccount.label;"/>
- </hbox>
- <toolbarseparator/>
- <toolbarbutton id="PanelUI-fxa-icon"
- oncommand="gSyncUI.doSync();"
- closemenu="none">
- <observes element="sync-status" attribute="syncstatus"/>
- <observes element="sync-status" attribute="tooltiptext"/>
- </toolbarbutton>
- </hbox>
<hbox id="PanelUI-footer-inner">
<toolbarbutton id="PanelUI-customize" label="&appMenuCustomize.label;"
@@ -103,95 +82,6 @@
oncommand="PlacesCommandHook.showPlacesOrganizer('History'); CustomizableUI.hidePanelForNode(this);"/>
</panelview>
- <panelview id="PanelUI-remotetabs" flex="1" class="PanelUI-subView">
- <label value="&appMenuRemoteTabs.label;" class="panel-subview-header"/>
- <vbox class="panel-subview-body">
- <!-- this widget has 3 boxes in the body, but only 1 is ever visible -->
- <!-- When Sync is ready to sync -->
- <vbox id="PanelUI-remotetabs-main" observes="sync-syncnow-state">
- <vbox id="PanelUI-remotetabs-buttons">
- <toolbarbutton id="PanelUI-remotetabs-view-sidebar"
- class="subviewbutton"
- oncommand="BrowserOpenSyncTabs();"
- label="&appMenuRemoteTabs.sidebar.label;"/>
- <toolbarbutton id="PanelUI-remotetabs-syncnow"
- observes="sync-status"
- class="subviewbutton"
- oncommand="gSyncUI.doSync();"
- closemenu="none"/>
- <menuseparator id="PanelUI-remotetabs-separator"/>
- </vbox>
- <deck id="PanelUI-remotetabs-deck">
- <!-- Sync is ready to Sync and the "tabs" engine is enabled -->
- <vbox id="PanelUI-remotetabs-tabspane">
- <vbox id="PanelUI-remotetabs-tabslist"
- notabsforclientlabel="&appMenuRemoteTabs.notabs.label;"
- />
- </vbox>
- <!-- Sync is ready to Sync but the "tabs" engine isn't enabled-->
- <hbox id="PanelUI-remotetabs-tabsdisabledpane" pack="center" flex="1">
- <vbox class="PanelUI-remotetabs-instruction-box">
- <hbox pack="center">
- <image class="fxaSyncIllustration" alt=""/>
- </hbox>
- <label class="PanelUI-remotetabs-instruction-label">&appMenuRemoteTabs.tabsnotsyncing.label;</label>
- <hbox pack="center">
- <toolbarbutton class="PanelUI-remotetabs-prefs-button"
- label="&appMenuRemoteTabs.openprefs.label;"
- oncommand="gSyncUI.openSetup(null, 'synced-tabs');"/>
- </hbox>
- </vbox>
- </hbox>
- <!-- Sync is ready to Sync but we are still fetching the tabs to show -->
- <vbox id="PanelUI-remotetabs-fetching">
- <!-- Show intentionally blank panel, see bug 1239845 -->
- </vbox>
- <!-- Sync has only 1 (ie, this) device connected -->
- <hbox id="PanelUI-remotetabs-nodevicespane" pack="center" flex="1">
- <vbox class="PanelUI-remotetabs-instruction-box">
- <hbox pack="center">
- <image class="fxaSyncIllustration" alt=""/>
- </hbox>
- <label class="PanelUI-remotetabs-instruction-title">&appMenuRemoteTabs.noclients.title;</label>
- <label class="PanelUI-remotetabs-instruction-label">&appMenuRemoteTabs.noclients.subtitle;</label>
- <!-- The inner HTML for PanelUI-remotetabs-mobile-promo is built at runtime -->
- <label id="PanelUI-remotetabs-mobile-promo" fxAccountsBrand="&syncBrand.fxAccount.label;"/>
- </vbox>
- </hbox>
- </deck>
- </vbox>
- <!-- a box to ensure contained boxes are centered horizonally -->
- <hbox pack="center" flex="1">
- <!-- When Sync is not configured -->
- <vbox id="PanelUI-remotetabs-setupsync"
- flex="1"
- align="center"
- class="PanelUI-remotetabs-instruction-box"
- observes="sync-setup-state">
- <image class="fxaSyncIllustration" alt=""/>
- <label class="PanelUI-remotetabs-instruction-label">&appMenuRemoteTabs.notsignedin.label;</label>
- <toolbarbutton class="PanelUI-remotetabs-prefs-button"
- label="&appMenuRemoteTabs.signin.label;"
- oncommand="gSyncUI.openSetup(null, 'synced-tabs');"/>
- </vbox>
- <!-- When Sync needs re-authentication. This uses the exact same messaging
- as "Sync is not configured" but remains a separate box so we get
- the goodness of observing broadcasters to manage the hidden states -->
- <vbox id="PanelUI-remotetabs-reauthsync"
- flex="1"
- align="center"
- class="PanelUI-remotetabs-instruction-box"
- observes="sync-reauth-state">
- <image class="fxaSyncIllustration" alt=""/>
- <label class="PanelUI-remotetabs-instruction-label">&appMenuRemoteTabs.notsignedin.label;</label>
- <toolbarbutton class="PanelUI-remotetabs-prefs-button"
- label="&appMenuRemoteTabs.signin.label;"
- oncommand="gSyncUI.openSetup(null, 'synced-tabs');"/>
- </vbox>
- </hbox>
- </vbox>
- </panelview>
-
<panelview id="PanelUI-bookmarks" flex="1" class="PanelUI-subView">
<label value="&bookmarksMenu.label;" class="panel-subview-header"/>
<vbox class="panel-subview-body">
diff --git a/application/basilisk/components/customizableui/moz.build b/application/basilisk/components/customizableui/moz.build
index 034630dc9..5797a03b0 100644
--- a/application/basilisk/components/customizableui/moz.build
+++ b/application/basilisk/components/customizableui/moz.build
@@ -9,7 +9,6 @@ DIRS += [
]
EXTRA_JS_MODULES += [
- 'CustomizableUI.jsm',
'CustomizableWidgets.jsm',
'CustomizeMode.jsm',
'DragPositionManager.jsm',
@@ -17,5 +16,9 @@ EXTRA_JS_MODULES += [
'ScrollbarSampler.jsm',
]
+EXTRA_PP_JS_MODULES += [
+ 'CustomizableUI.jsm',
+]
+
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'cocoa'):
DEFINES['CAN_DRAW_IN_TITLEBAR'] = 1
diff --git a/application/basilisk/components/moz.build b/application/basilisk/components/moz.build
index 65e8beb76..a9c29936b 100644
--- a/application/basilisk/components/moz.build
+++ b/application/basilisk/components/moz.build
@@ -19,10 +19,12 @@ DIRS += [
'sessionstore',
'shell',
'selfsupport',
- 'syncedtabs',
'translation',
]
+if CONFIG['MOZ_SERVICES_SYNC']:
+ DIRS += ['sync']
+
DIRS += ['build']
XPIDL_SOURCES += [
@@ -35,6 +37,9 @@ XPIDL_MODULE = 'browsercompsbase'
EXTRA_COMPONENTS += [
'BrowserComponents.manifest',
'nsBrowserContentHandler.js',
+]
+
+EXTRA_PP_COMPONENTS += [
'nsBrowserGlue.js',
]
diff --git a/application/basilisk/components/nsBrowserGlue.js b/application/basilisk/components/nsBrowserGlue.js
index f2eb8fdf3..b4256523f 100644
--- a/application/basilisk/components/nsBrowserGlue.js
+++ b/application/basilisk/components/nsBrowserGlue.js
@@ -143,6 +143,7 @@ BrowserGlue.prototype = {
Services.prefs.savePrefFile(null);
},
+#ifdef MOZ_SERVICES_SYNC
_setSyncAutoconnectDelay: function BG__setSyncAutoconnectDelay() {
// Assume that a non-zero value for services.sync.autoconnectDelay should override
if (Services.prefs.prefHasUserValue("services.sync.autoconnectDelay")) {
@@ -164,6 +165,7 @@ BrowserGlue.prototype = {
Cu.import("resource://services-sync/main.js");
Weave.Service.scheduler.delayedAutoConnect(delay);
},
+#endif
// nsIObserver implementation
observe: function BG_observe(subject, topic, data) {
@@ -210,18 +212,14 @@ BrowserGlue.prototype = {
this._setPrefToSaveSession();
}
break;
+#ifdef MOZ_SERVICES_SYNC
case "weave:service:ready":
this._setSyncAutoconnectDelay();
break;
- case "fxaccounts:onverified":
- this._showSyncStartedDoorhanger();
- break;
- case "fxaccounts:device_disconnected":
- this._onDeviceDisconnected();
- break;
- case "weave:engine:clients:display-uris":
- this._onDisplaySyncURIs(subject);
- break;
+ case "weave:engine:clients:display-uri":
+ this._onDisplaySyncURI(subject);
+ break;
+#endif
case "session-save":
this._setPrefToSaveSession(true);
subject.QueryInterface(Ci.nsISupportsPRBool);
@@ -428,10 +426,10 @@ BrowserGlue.prototype = {
os.addObserver(this, "browser-lastwindow-close-requested", false);
os.addObserver(this, "browser-lastwindow-close-granted", false);
}
+#ifdef MOZ_SERVICES_SYNC
os.addObserver(this, "weave:service:ready", false);
- os.addObserver(this, "fxaccounts:onverified", false);
- os.addObserver(this, "fxaccounts:device_disconnected", false);
- os.addObserver(this, "weave:engine:clients:display-uris", false);
+ os.addObserver(this, "weave:engine:clients:display-uri", false);
+#endif
os.addObserver(this, "session-save", false);
os.addObserver(this, "places-init-complete", false);
this._isPlacesInitObserver = true;
@@ -479,10 +477,10 @@ BrowserGlue.prototype = {
os.removeObserver(this, "browser-lastwindow-close-requested");
os.removeObserver(this, "browser-lastwindow-close-granted");
}
+#ifdef MOZ_SERVICES_SYNC
os.removeObserver(this, "weave:service:ready");
- os.removeObserver(this, "fxaccounts:onverified");
- os.removeObserver(this, "fxaccounts:device_disconnected");
- os.removeObserver(this, "weave:engine:clients:display-uris");
+ os.removeObserver(this, "weave:engine:clients:display-uri");
+#endif
os.removeObserver(this, "session-save");
if (this._bookmarksBackupIdleTime) {
this._idleService.removeIdleObserver(this, this._bookmarksBackupIdleTime);
@@ -2255,90 +2253,29 @@ BrowserGlue.prototype = {
chromeWindow.openPreferences(...args);
},
+#ifdef MOZ_SERVICES_SYNC
/**
- * Called as an observer when Sync's "display URIs" notification is fired.
+ * Called as an observer when Sync's "display URI" notification is fired.
+ *
+ * We open the received URI in a background tab.
*
- * We open the received URIs in background tabs.
+ * Eventually, this will likely be replaced by a more robust tab syncing
+ * feature. This functionality is considered somewhat evil by UX because it
+ * opens a new tab automatically without any prompting. However, it is a
+ * lesser evil than sending a tab to a specific device (from e.g. Fennec)
+ * and having nothing happen on the receiving end.
*/
- _onDisplaySyncURIs: function _onDisplaySyncURIs(data) {
+ _onDisplaySyncURI: function _onDisplaySyncURI(data) {
try {
- // The payload is wrapped weirdly because of how Sync does notifications.
- const URIs = data.wrappedJSObject.object;
-
- const findWindow = () => RecentWindow.getMostRecentBrowserWindow({private: false});
-
- // win can be null, but it's ok, we'll assign it later in openTab()
- let win = findWindow();
+ let tabbrowser = RecentWindow.getMostRecentBrowserWindow({private: false}).gBrowser;
- const openTab = URI => {
- let tab;
- if (!win) {
- Services.appShell.hiddenDOMWindow.open(URI.uri);
- win = findWindow();
- tab = win.gBrowser.tabs[0];
- } else {
- tab = win.gBrowser.addTab(URI.uri);
- }
- tab.setAttribute("attention", true);
- return tab;
- };
-
- const firstTab = openTab(URIs[0]);
- URIs.slice(1).forEach(URI => openTab(URI));
-
- let title, body;
- const deviceName = Weave.Service.clientsEngine.getClientName(URIs[0].clientId);
- const bundle = Services.strings.createBundle("chrome://browser/locale/accounts.properties");
- if (URIs.length == 1) {
- // Due to bug 1305895, tabs from iOS may not have device information, so
- // we have separate strings to handle those cases. (See Also
- // unnamedTabsArrivingNotificationNoDevice.body below)
- if (deviceName) {
- title = bundle.formatStringFromName("tabArrivingNotificationWithDevice.title", [deviceName], 1);
- } else {
- title = bundle.GetStringFromName("tabArrivingNotification.title");
- }
- // Use the page URL as the body. We strip the fragment and query to
- // reduce size, and also format it the same way that the url bar would.
- body = URIs[0].uri.replace(/[?#].*$/, "");
- if (win.gURLBar) {
- body = win.gURLBar.trimValue(body);
- }
- } else {
- title = bundle.GetStringFromName("tabsArrivingNotification.title");
- const allSameDevice = URIs.every(URI => URI.clientId == URIs[0].clientId);
- const unknownDevice = allSameDevice && !deviceName;
- let tabArrivingBody;
- if (unknownDevice) {
- tabArrivingBody = "unnamedTabsArrivingNotificationNoDevice.body";
- } else if (allSameDevice) {
- tabArrivingBody = "unnamedTabsArrivingNotification2.body";
- } else {
- tabArrivingBody = "unnamedTabsArrivingNotificationMultiple2.body"
- }
-
- body = bundle.GetStringFromName(tabArrivingBody);
- body = PluralForm.get(URIs.length, body);
- body = body.replace("#1", URIs.length);
- body = body.replace("#2", deviceName);
- }
-
- const clickCallback = (subject, topic, data) => {
- if (topic == "alertclickcallback") {
- win.gBrowser.selectedTab = firstTab;
- }
- }
-
- // Specify an icon because on Windows no icon is shown at the moment
- let imageURL;
- if (AppConstants.platform == "win") {
- imageURL = "chrome://branding/content/icon64.png";
- }
- AlertsService.showAlertNotification(imageURL, title, body, true, null, clickCallback);
+ // The payload is wrapped weirdly because of how Sync does notifications.
+ tabbrowser.addTab(data.wrappedJSObject.object.uri);
} catch (ex) {
- Cu.reportError("Error displaying tab(s) received by Sync: " + ex);
+ Cu.reportError("Error displaying tab received by Sync: " + ex);
}
},
+#endif
_onDeviceDisconnected() {
let bundle = Services.strings.createBundle("chrome://browser/locale/accounts.properties");
diff --git a/application/basilisk/components/places/PlacesUIUtils.jsm b/application/basilisk/components/places/PlacesUIUtils.jsm
index 17fa276aa..035fc12c2 100644
--- a/application/basilisk/components/places/PlacesUIUtils.jsm
+++ b/application/basilisk/components/places/PlacesUIUtils.jsm
@@ -1418,9 +1418,9 @@ this.PlacesUIUtils = {
},
shouldShowTabsFromOtherComputersMenuitem: function() {
- let weaveOK = Weave.Status.checkSetup() != Weave.CLIENT_NOT_CONFIGURED &&
- Weave.Svc.Prefs.get("firstSync", "") != "notReady";
- return weaveOK;
+#ifdef MOZ_SERVICES_SYNC
+ // Weave code to enable menu item
+#endif
},
/**
diff --git a/application/basilisk/components/places/moz.build b/application/basilisk/components/places/moz.build
index 9e5a2c074..ea6d43538 100644
--- a/application/basilisk/components/places/moz.build
+++ b/application/basilisk/components/places/moz.build
@@ -6,6 +6,6 @@
JAR_MANIFESTS += ['jar.mn']
-EXTRA_JS_MODULES += [
+EXTRA_PP_JS_MODULES += [
'PlacesUIUtils.jsm',
]
diff --git a/application/basilisk/components/preferences/in-content/jar.mn b/application/basilisk/components/preferences/in-content/jar.mn
index 70544f332..21f5ccf38 100644
--- a/application/basilisk/components/preferences/in-content/jar.mn
+++ b/application/basilisk/components/preferences/in-content/jar.mn
@@ -3,7 +3,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
browser.jar:
- content/browser/preferences/in-content/preferences.js
+* content/browser/preferences/in-content/preferences.js
* content/browser/preferences/in-content/preferences.xul
content/browser/preferences/in-content/subdialogs.js
@@ -12,6 +12,8 @@ browser.jar:
content/browser/preferences/in-content/advanced.js
content/browser/preferences/in-content/applications.js
* content/browser/preferences/in-content/content.js
+#ifdef MOZ_SERVICES_SYNC
content/browser/preferences/in-content/sync.js
+#endif
* content/browser/preferences/in-content/security.js
content/browser/preferences/in-content/search.js
diff --git a/application/basilisk/components/preferences/in-content/preferences.js b/application/basilisk/components/preferences/in-content/preferences.js
index 35e10c58d..69cb180d5 100644
--- a/application/basilisk/components/preferences/in-content/preferences.js
+++ b/application/basilisk/components/preferences/in-content/preferences.js
@@ -64,7 +64,9 @@ function init_all() {
register_module("paneAdvanced", gAdvancedPane);
register_module("paneApplications", gApplicationsPane);
register_module("paneContent", gContentPane);
+#ifdef MOZ_SERVICES_SYNC
register_module("paneSync", gSyncPane);
+#endif
register_module("paneSecurity", gSecurityPane);
let categories = document.getElementById("categories");
diff --git a/application/basilisk/components/preferences/in-content/preferences.xul b/application/basilisk/components/preferences/in-content/preferences.xul
index 093516140..61639aa19 100644
--- a/application/basilisk/components/preferences/in-content/preferences.xul
+++ b/application/basilisk/components/preferences/in-content/preferences.xul
@@ -22,8 +22,10 @@
<!ENTITY % privacyDTD SYSTEM "chrome://browser/locale/preferences/privacy.dtd">
<!ENTITY % tabsDTD SYSTEM "chrome://browser/locale/preferences/tabs.dtd">
<!ENTITY % searchDTD SYSTEM "chrome://browser/locale/preferences/search.dtd">
+#ifdef MOZ_SERVICES_SYNC
<!ENTITY % syncBrandDTD SYSTEM "chrome://browser/locale/syncBrand.dtd">
<!ENTITY % syncDTD SYSTEM "chrome://browser/locale/preferences/sync.dtd">
+#endif
<!ENTITY % securityDTD SYSTEM
"chrome://browser/locale/preferences/security.dtd">
<!ENTITY % sanitizeDTD SYSTEM "chrome://browser/locale/sanitize.dtd">
@@ -40,8 +42,10 @@
%privacyDTD;
%tabsDTD;
%searchDTD;
+#ifdef MOZ_SERVICES_SYNC
%syncBrandDTD;
%syncDTD;
+#endif
%securityDTD;
%sanitizeDTD;
%mainDTD;
@@ -140,6 +144,7 @@
<label class="category-name" flex="1">&paneSecurity.title;</label>
</richlistitem>
+#ifdef MOZ_SERVICES_SYNC
<richlistitem id="category-sync"
class="category"
value="paneSync"
@@ -149,6 +154,7 @@
<image class="category-icon"/>
<label class="category-name" flex="1">&paneSync.title;</label>
</richlistitem>
+#endif
<richlistitem id="category-advanced"
class="category"
@@ -177,7 +183,9 @@
#include applications.xul
#include content.xul
#include security.xul
+#ifdef MOZ_SERVICES_SYNC
#include sync.xul
+#endif
</prefpane>
</vbox>
diff --git a/application/basilisk/components/preferences/in-content/sync.js b/application/basilisk/components/preferences/in-content/sync.js
index 9496d34b6..917b5f123 100644
--- a/application/basilisk/components/preferences/in-content/sync.js
+++ b/application/basilisk/components/preferences/in-content/sync.js
@@ -1,32 +1,16 @@
/* 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/. */
+ * 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/. */
Components.utils.import("resource://services-sync/main.js");
Components.utils.import("resource://gre/modules/Services.jsm");
-XPCOMUtils.defineLazyGetter(this, "FxAccountsCommon", function () {
- return Components.utils.import("resource://gre/modules/FxAccountsCommon.js", {});
-});
-
-XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts",
- "resource://gre/modules/FxAccounts.jsm");
-
const PAGE_NO_ACCOUNT = 0;
const PAGE_HAS_ACCOUNT = 1;
const PAGE_NEEDS_UPDATE = 2;
-const FXA_PAGE_LOGGED_OUT = 3;
-const FXA_PAGE_LOGGED_IN = 4;
-
-// Indexes into the "login status" deck.
-// We are in a successful verified state - everything should work!
-const FXA_LOGIN_VERIFIED = 0;
-// We have logged in to an unverified account.
-const FXA_LOGIN_UNVERIFIED = 1;
-// We are logged in locally, but the server rejected our credentials.
-const FXA_LOGIN_FAILED = 2;
var gSyncPane = {
+ _stringBundle: null,
prefArray: ["engine.bookmarks", "engine.passwords", "engine.prefs",
"engine.tabs", "engine.history"],
@@ -45,13 +29,11 @@ var gSyncPane = {
needsUpdate: function () {
this.page = PAGE_NEEDS_UPDATE;
let label = document.getElementById("loginError");
- label.textContent = Weave.Utils.getErrorString(Weave.Status.login);
+ label.value = Weave.Utils.getErrorString(Weave.Status.login);
label.className = "error";
},
init: function () {
- this._setupEventListeners();
-
// If the Service hasn't finished initializing, wait for it.
let xps = Components.classes["@mozilla.org/weave/service;1"]
.getService(Components.interfaces.nsISupports)
@@ -62,10 +44,6 @@ var gSyncPane = {
return;
}
- // it may take some time before we can determine what provider to use
- // and the state of that provider, so show the "please wait" page.
- this._showLoadPage(xps);
-
let onUnload = function () {
window.removeEventListener("unload", onUnload, false);
try {
@@ -85,238 +63,50 @@ var gSyncPane = {
xps.ensureLoaded();
},
- _showLoadPage: function (xps) {
- let username = Services.prefs.getCharPref("services.sync.username", "");
- if (!username) {
- this.page = FXA_PAGE_LOGGED_OUT;
- } else if (xps.fxAccountsEnabled) {
- // Use cached values while we wait for the up-to-date values
- let cachedComputerName = Services.prefs.getCharPref("services.sync.client.name", "");
- document.getElementById("fxaEmailAddress1").textContent = username;
- this._populateComputerName(cachedComputerName);
- this.page = FXA_PAGE_LOGGED_IN;
- } else { // Old Sync
- this.page = PAGE_HAS_ACCOUNT;
- }
- },
-
_init: function () {
let topics = ["weave:service:login:error",
"weave:service:login:finish",
- "weave:service:start-over:finish",
+ "weave:service:start-over",
"weave:service:setup-complete",
- "weave:service:logout:finish",
- FxAccountsCommon.ONVERIFIED_NOTIFICATION,
- FxAccountsCommon.ONLOGIN_NOTIFICATION,
- FxAccountsCommon.ON_PROFILE_CHANGE_NOTIFICATION,
- ];
+ "weave:service:logout:finish"];
+
// Add the observers now and remove them on unload
- // XXXzpao This should use Services.obs.* but Weave's Obs does nice handling
+ //XXXzpao This should use Services.obs.* but Weave's Obs does nice handling
// of `this`. Fix in a followup. (bug 583347)
topics.forEach(function (topic) {
Weave.Svc.Obs.add(topic, this.updateWeavePrefs, this);
}, this);
-
window.addEventListener("unload", function() {
topics.forEach(function (topic) {
Weave.Svc.Obs.remove(topic, this.updateWeavePrefs, this);
}, gSyncPane);
}, false);
- XPCOMUtils.defineLazyGetter(this, '_stringBundle', () => {
- return Services.strings.createBundle("chrome://browser/locale/preferences/preferences.properties");
- });
-
- XPCOMUtils.defineLazyGetter(this, '_accountsStringBundle', () => {
- return Services.strings.createBundle("chrome://browser/locale/accounts.properties");
- });
-
- let url = Services.prefs.getCharPref("identity.mobilepromo.android") + "sync-preferences";
- document.getElementById("fxaMobilePromo-android").setAttribute("href", url);
- document.getElementById("fxaMobilePromo-android-hasFxaAccount").setAttribute("href", url);
- url = Services.prefs.getCharPref("identity.mobilepromo.ios") + "sync-preferences";
- document.getElementById("fxaMobilePromo-ios").setAttribute("href", url);
- document.getElementById("fxaMobilePromo-ios-hasFxaAccount").setAttribute("href", url);
-
- document.getElementById("tosPP-small-ToS").setAttribute("href", gSyncUtils.tosURL);
- document.getElementById("tosPP-normal-ToS").setAttribute("href", gSyncUtils.tosURL);
- document.getElementById("tosPP-small-PP").setAttribute("href", gSyncUtils.privacyPolicyURL);
- document.getElementById("tosPP-normal-PP").setAttribute("href", gSyncUtils.privacyPolicyURL);
-
- fxAccounts.promiseAccountsManageURI(this._getEntryPoint()).then(url => {
- document.getElementById("verifiedManage").setAttribute("href", url);
- });
-
+ this._stringBundle =
+ Services.strings.createBundle("chrome://browser/locale/preferences/preferences.properties");
this.updateWeavePrefs();
-
- this._initProfileImageUI();
- },
-
- _toggleComputerNameControls: function(editMode) {
- let textbox = document.getElementById("fxaSyncComputerName");
- textbox.disabled = !editMode;
- document.getElementById("fxaChangeDeviceName").hidden = editMode;
- document.getElementById("fxaCancelChangeDeviceName").hidden = !editMode;
- document.getElementById("fxaSaveChangeDeviceName").hidden = !editMode;
- },
-
- _focusComputerNameTextbox: function() {
- let textbox = document.getElementById("fxaSyncComputerName");
- let valLength = textbox.value.length;
- textbox.focus();
- textbox.setSelectionRange(valLength, valLength);
- },
-
- _blurComputerNameTextbox: function() {
- document.getElementById("fxaSyncComputerName").blur();
- },
-
- _focusAfterComputerNameTextbox: function() {
- // Focus the most appropriate element that's *not* the "computer name" box.
- Services.focus.moveFocus(window,
- document.getElementById("fxaSyncComputerName"),
- Services.focus.MOVEFOCUS_FORWARD, 0);
- },
-
- _updateComputerNameValue: function(save) {
- if (save) {
- let textbox = document.getElementById("fxaSyncComputerName");
- Weave.Service.clientsEngine.localName = textbox.value;
- }
- this._populateComputerName(Weave.Service.clientsEngine.localName);
- },
-
- _setupEventListeners: function() {
- function setEventListener(aId, aEventType, aCallback)
- {
- document.getElementById(aId)
- .addEventListener(aEventType, aCallback.bind(gSyncPane));
- }
-
- setEventListener("noAccountSetup", "click", function (aEvent) {
- aEvent.stopPropagation();
- gSyncPane.openSetup(null);
- });
- setEventListener("noAccountPair", "click", function (aEvent) {
- aEvent.stopPropagation();
- gSyncPane.openSetup('pair');
- });
- setEventListener("syncChangePassword", "command",
- () => gSyncUtils.changePassword());
- setEventListener("syncResetPassphrase", "command",
- () => gSyncUtils.resetPassphrase());
- setEventListener("syncReset", "command", gSyncPane.resetSync);
- setEventListener("syncAddDeviceLabel", "click", function () {
- gSyncPane.openAddDevice();
- return false;
- });
- setEventListener("syncEnginesList", "select", function () {
- if (this.selectedCount)
- this.clearSelection();
- });
- setEventListener("syncComputerName", "change", function (e) {
- gSyncUtils.changeName(e.target);
- });
- setEventListener("fxaChangeDeviceName", "command", function () {
- this._toggleComputerNameControls(true);
- this._focusComputerNameTextbox();
- });
- setEventListener("fxaCancelChangeDeviceName", "command", function () {
- // We explicitly blur the textbox because of bug 75324, then after
- // changing the state of the buttons, force focus to whatever the focus
- // manager thinks should be next (which on the mac, depends on an OSX
- // keyboard access preference)
- this._blurComputerNameTextbox();
- this._toggleComputerNameControls(false);
- this._updateComputerNameValue(false);
- this._focusAfterComputerNameTextbox();
- });
- setEventListener("fxaSaveChangeDeviceName", "command", function () {
- // Work around bug 75324 - see above.
- this._blurComputerNameTextbox();
- this._toggleComputerNameControls(false);
- this._updateComputerNameValue(true);
- this._focusAfterComputerNameTextbox();
- });
- setEventListener("unlinkDevice", "click", function () {
- gSyncPane.startOver(true);
- return false;
- });
- setEventListener("loginErrorUpdatePass", "click", function () {
- gSyncPane.updatePass();
- return false;
- });
- setEventListener("loginErrorResetPass", "click", function () {
- gSyncPane.resetPass();
- return false;
- });
- setEventListener("loginErrorStartOver", "click", function () {
- gSyncPane.startOver(true);
- return false;
- });
- setEventListener("noFxaSignUp", "command", function () {
- gSyncPane.signUp();
- return false;
- });
- setEventListener("noFxaSignIn", "command", function () {
- gSyncPane.signIn();
- return false;
- });
- setEventListener("fxaUnlinkButton", "command", function () {
- gSyncPane.unlinkFirefoxAccount(true);
- });
- setEventListener("verifyFxaAccount", "command",
- gSyncPane.verifyFirefoxAccount);
- setEventListener("unverifiedUnlinkFxaAccount", "command", function () {
- /* no warning as account can't have previously synced */
- gSyncPane.unlinkFirefoxAccount(false);
- });
- setEventListener("rejectReSignIn", "command",
- gSyncPane.reSignIn);
- setEventListener("rejectUnlinkFxaAccount", "command", function () {
- gSyncPane.unlinkFirefoxAccount(true);
- });
- setEventListener("fxaSyncComputerName", "keypress", function (e) {
- if (e.keyCode == KeyEvent.DOM_VK_RETURN) {
- document.getElementById("fxaSaveChangeDeviceName").click();
- } else if (e.keyCode == KeyEvent.DOM_VK_ESCAPE) {
- document.getElementById("fxaCancelChangeDeviceName").click();
- }
- });
- },
-
- _initProfileImageUI: function () {
- try {
- if (Services.prefs.getBoolPref("identity.fxaccounts.profile_image.enabled")) {
- document.getElementById("fxaProfileImage").hidden = false;
- }
- } catch (e) { }
+ document.getElementById("weavePrefsDeck").setAttribute("hidden", "");
},
updateWeavePrefs: function () {
- let service = Components.classes["@mozilla.org/weave/service;1"]
- .getService(Components.interfaces.nsISupports)
- .wrappedJSObject;
if (Weave.Status.service == Weave.CLIENT_NOT_CONFIGURED ||
Weave.Svc.Prefs.get("firstSync", "") == "notReady") {
this.page = PAGE_NO_ACCOUNT;
- // else: sync was previously configured for the legacy provider, so we
- // make the "old" panels available.
} else if (Weave.Status.login == Weave.LOGIN_FAILED_INVALID_PASSPHRASE ||
Weave.Status.login == Weave.LOGIN_FAILED_LOGIN_REJECTED) {
this.needsUpdate();
} else {
this.page = PAGE_HAS_ACCOUNT;
- document.getElementById("accountName").textContent = Weave.Service.identity.account;
+ document.getElementById("accountName").value = Weave.Service.identity.account;
document.getElementById("syncComputerName").value = Weave.Service.clientsEngine.localName;
- document.getElementById("tosPP-normal").hidden = this._usingCustomServer;
+ document.getElementById("tosPP").hidden = this._usingCustomServer;
}
},
startOver: function (showDialog) {
if (showDialog) {
let flags = Services.prompt.BUTTON_POS_0 * Services.prompt.BUTTON_TITLE_IS_STRING +
- Services.prompt.BUTTON_POS_1 * Services.prompt.BUTTON_TITLE_CANCEL +
+ Services.prompt.BUTTON_POS_1 * Services.prompt.BUTTON_TITLE_CANCEL +
Services.prompt.BUTTON_POS_1_DEFAULT;
let buttonChoice =
Services.prompt.confirmEx(window,
@@ -327,8 +117,9 @@ var gSyncPane = {
null, null, null, {});
// If the user selects cancel, just bail
- if (buttonChoice == 1)
+ if (buttonChoice == 1) {
return;
+ }
}
Weave.Service.startOver();
@@ -336,33 +127,19 @@ var gSyncPane = {
},
updatePass: function () {
- if (Weave.Status.login == Weave.LOGIN_FAILED_LOGIN_REJECTED)
+ if (Weave.Status.login == Weave.LOGIN_FAILED_LOGIN_REJECTED) {
gSyncUtils.changePassword();
- else
+ } else {
gSyncUtils.updatePassphrase();
+ }
},
resetPass: function () {
- if (Weave.Status.login == Weave.LOGIN_FAILED_LOGIN_REJECTED)
+ if (Weave.Status.login == Weave.LOGIN_FAILED_LOGIN_REJECTED) {
gSyncUtils.resetPassword();
- else
+ } else {
gSyncUtils.resetPassphrase();
- },
-
- _getEntryPoint: function () {
- let params = new URLSearchParams(document.URL.split("#")[0].split("?")[1] || "");
- return params.get("entrypoint") || "preferences";
- },
-
- _openAboutAccounts: function(action) {
- let entryPoint = this._getEntryPoint();
- let params = new URLSearchParams();
- if (action) {
- params.set("action", action);
}
- params.set("entrypoint", entryPoint);
-
- this.replaceTabWithUrl("about:accounts?" + params);
},
/**
@@ -375,163 +152,16 @@ var gSyncPane = {
* "reset" -- reset sync
*/
openSetup: function (wizardType) {
- let service = Components.classes["@mozilla.org/weave/service;1"]
- .getService(Components.interfaces.nsISupports)
- .wrappedJSObject;
-
- if (service.fxAccountsEnabled) {
- this._openAboutAccounts();
+ let win = Services.wm.getMostRecentWindow("Weave:AccountSetup");
+ if (win) {
+ win.focus();
} else {
- 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);
- }
+ window.openDialog("chrome://browser/content/sync/setup.xul",
+ "weaveSetup", "centerscreen,chrome,resizable=no",
+ wizardType);
}
},
- openContentInBrowser: function(url, options) {
- let win = Services.wm.getMostRecentWindow("navigator:browser");
- if (!win) {
- // no window to use, so use _openLink to create a new one. We don't
- // always use that as it prefers to open a new window rather than use
- // an existing one.
- gSyncUtils._openLink(url);
- return;
- }
- win.switchToTabHavingURI(url, true, options);
- },
-
- // Replace the current tab with the specified URL.
- replaceTabWithUrl(url) {
- // Get the <browser> element hosting us.
- let browser = window.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIWebNavigation)
- .QueryInterface(Ci.nsIDocShell)
- .chromeEventHandler;
- // And tell it to load our URL.
- browser.loadURI(url);
- },
-
- signUp: function() {
- this._openAboutAccounts("signup");
- },
-
- signIn: function() {
- this._openAboutAccounts("signin");
- },
-
- reSignIn: function() {
- this._openAboutAccounts("reauth");
- },
-
-
- clickOrSpaceOrEnterPressed: function(event) {
- // Note: charCode is deprecated, but 'char' not yet implemented.
- // Replace charCode with char when implemented, see Bug 680830
- return ((event.type == "click" && event.button == 0) ||
- (event.type == "keypress" &&
- (event.charCode == KeyEvent.DOM_VK_SPACE || event.keyCode == KeyEvent.DOM_VK_RETURN)));
- },
-
- openChangeProfileImage: function(event) {
- if (this.clickOrSpaceOrEnterPressed(event)) {
- fxAccounts.promiseAccountsChangeProfileURI(this._getEntryPoint(), "avatar")
- .then(url => {
- this.openContentInBrowser(url, {
- replaceQueryString: true
- });
- });
- // Prevent page from scrolling on the space key.
- event.preventDefault();
- }
- },
-
- openManageFirefoxAccount: function(event) {
- if (this.clickOrSpaceOrEnterPressed(event)) {
- this.manageFirefoxAccount();
- // Prevent page from scrolling on the space key.
- event.preventDefault();
- }
- },
-
- manageFirefoxAccount: function() {
- fxAccounts.promiseAccountsManageURI(this._getEntryPoint())
- .then(url => {
- this.openContentInBrowser(url, {
- replaceQueryString: true
- });
- });
- },
-
- verifyFirefoxAccount: function() {
- let showVerifyNotification = (data) => {
- let isError = !data;
- let maybeNot = isError ? "Not" : "";
- let sb = this._accountsStringBundle;
- let title = sb.GetStringFromName("verification" + maybeNot + "SentTitle");
- let email = !isError && data ? data.email : "";
- let body = sb.formatStringFromName("verification" + maybeNot + "SentBody", [email], 1);
- new Notification(title, { body })
- }
-
- let onError = () => {
- showVerifyNotification();
- };
-
- let onSuccess = data => {
- if (data) {
- showVerifyNotification(data);
- } else {
- onError();
- }
- };
-
- fxAccounts.resendVerificationEmail()
- .then(fxAccounts.getSignedInUser, onError)
- .then(onSuccess, onError);
- },
-
- openOldSyncSupportPage: function() {
- let url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "old-sync";
- this.openContentInBrowser(url);
- },
-
- unlinkFirefoxAccount: function(confirm) {
- if (confirm) {
- // We use a string bundle shared with aboutAccounts.
- let sb = Services.strings.createBundle("chrome://browser/locale/syncSetup.properties");
- let disconnectLabel = sb.GetStringFromName("disconnect.label");
- let title = sb.GetStringFromName("disconnect.verify.title");
- let body = sb.GetStringFromName("disconnect.verify.bodyHeading") +
- "\n\n" +
- sb.GetStringFromName("disconnect.verify.bodyText");
- let ps = Services.prompt;
- let buttonFlags = (ps.BUTTON_POS_0 * ps.BUTTON_TITLE_IS_STRING) +
- (ps.BUTTON_POS_1 * ps.BUTTON_TITLE_CANCEL) +
- ps.BUTTON_POS_1_DEFAULT;
-
- let factory = Cc["@mozilla.org/prompter;1"]
- .getService(Ci.nsIPromptFactory);
- let prompt = factory.getPrompt(window, Ci.nsIPrompt);
- let bag = prompt.QueryInterface(Ci.nsIWritablePropertyBag2);
- bag.setPropertyAsBool("allowTabModal", true);
-
- let pressed = prompt.confirmEx(title, body, buttonFlags,
- disconnectLabel, null, null, null, {});
-
- if (pressed != 0) { // 0 is the "continue" button
- return;
- }
- }
- fxAccounts.signOut().then(() => {
- this.updateWeavePrefs();
- });
- },
-
openQuotaDialog: function () {
let win = Services.wm.getMostRecentWindow("Sync:ViewQuota");
if (win) {
@@ -543,27 +173,21 @@ var gSyncPane = {
},
openAddDevice: function () {
- if (!Weave.Utils.ensureMPUnlocked())
+ if (!Weave.Utils.ensureMPUnlocked()) {
return;
+ }
let win = Services.wm.getMostRecentWindow("Sync:AddDevice");
- if (win)
+ if (win) {
win.focus();
- else
+ } else {
window.openDialog("chrome://browser/content/sync/addDevice.xul",
"syncAddDevice", "centerscreen,chrome,resizable=no");
+ }
},
resetSync: function () {
this.openSetup("reset");
},
-
- _populateComputerName(value) {
- let textbox = document.getElementById("fxaSyncComputerName");
- if (!textbox.hasAttribute("placeholder")) {
- textbox.setAttribute("placeholder",
- Weave.Utils.getDefaultDeviceName());
- }
- textbox.value = value;
- },
};
+
diff --git a/application/basilisk/components/preferences/in-content/sync.xul b/application/basilisk/components/preferences/in-content/sync.xul
index cf0e81ca1..7ca075483 100644
--- a/application/basilisk/components/preferences/in-content/sync.xul
+++ b/application/basilisk/components/preferences/in-content/sync.xul
@@ -1,35 +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/.
-
-<!-- Sync panel -->
-
-<preferences id="syncEnginePrefs" hidden="true" data-category="paneSync">
- <preference id="engine.addons"
- name="services.sync.engine.addons"
- type="bool"/>
- <preference id="engine.bookmarks"
- name="services.sync.engine.bookmarks"
- type="bool"/>
- <preference id="engine.history"
- name="services.sync.engine.history"
- type="bool"/>
- <preference id="engine.tabs"
- name="services.sync.engine.tabs"
- type="bool"/>
- <preference id="engine.prefs"
- name="services.sync.engine.prefs"
- type="bool"/>
- <preference id="engine.passwords"
- name="services.sync.engine.passwords"
- type="bool"/>
-</preferences>
+<!-- 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/. -->
<script type="application/javascript"
src="chrome://browser/content/preferences/in-content/sync.js"/>
<script type="application/javascript"
src="chrome://browser/content/sync/utils.js"/>
+<preferences>
+<!-- <preference id="engine.addons" name="services.sync.engine.addons" type="bool"/> -->
+ <preference id="engine.bookmarks" name="services.sync.engine.bookmarks" type="bool"/>
+ <preference id="engine.history" name="services.sync.engine.history" type="bool"/>
+ <preference id="engine.tabs" name="services.sync.engine.tabs" type="bool"/>
+ <preference id="engine.prefs" name="services.sync.engine.prefs" type="bool"/>
+ <preference id="engine.passwords" name="services.sync.engine.passwords" type="bool"/>
+</preferences>
+
+
<hbox id="header-sync"
class="header"
hidden="true"
@@ -39,22 +26,19 @@
</hbox>
<deck id="weavePrefsDeck" data-category="paneSync" hidden="true">
- <!-- These panels are for the "legacy" sync provider -->
<vbox id="noAccount" align="center">
<spacer flex="1"/>
<description id="syncDesc">
&weaveDesc.label;
</description>
<separator/>
- <label id="noAccountSetup" class="text-link">
- &setupButton.label;
- </label>
- <vbox id="pairDevice">
- <separator/>
- <label id="noAccountPair" class="text-link">
- &pairDevice.label;
- </label>
- </vbox>
+ <label class="text-link"
+ onclick="event.stopPropagation(); gSyncPane.openSetup(null);"
+ value="&setupButton.label;"/>
+ <separator/>
+ <label class="text-link"
+ onclick="event.stopPropagation(); gSyncPane.openSetup('pair');"
+ value="&pairDevice.label;"/>
<spacer flex="3"/>
</vbox>
@@ -63,7 +47,7 @@
<!-- label is set to account name -->
<caption id="accountCaption" align="center">
<image id="accountCaptionImage"/>
- <label id="accountName"/>
+ <label id="accountName" value=""/>
</caption>
<hbox>
@@ -71,28 +55,39 @@
label="&manageAccount.label;"
accesskey="&manageAccount.accesskey;">
<menupopup>
- <menuitem id="syncViewQuota" label="&viewQuota.label;"
+ <menuitem label="&viewQuota.label;"
oncommand="gSyncPane.openQuotaDialog();"/>
- <menuseparator/>
- <menuitem id="syncChangePassword" label="&changePassword2.label;"/>
- <menuitem id="syncResetPassphrase" label="&myRecoveryKey.label;"/>
<menuseparator/>
- <menuitem id="syncReset" label="&resetSync2.label;"/>
+ <menuitem label="&changePassword2.label;"
+ oncommand="gSyncUtils.changePassword();"/>
+ <menuitem label="&myRecoveryKey.label;"
+ oncommand="gSyncUtils.resetPassphrase();"/>
+ <menuseparator/>
+ <menuitem label="&resetSync2.label;"
+ oncommand="gSyncPane.resetSync();"/>
</menupopup>
</button>
</hbox>
<hbox>
<label id="syncAddDeviceLabel"
- class="text-link">
- &pairDevice.label;
- </label>
+ class="text-link"
+ onclick="gSyncPane.openAddDevice(); return false;"
+ value="&pairDevice.label;"/>
</hbox>
<vbox>
- <label>&syncMy.label;</label>
+ <label value="&syncMy.label;" />
<richlistbox id="syncEnginesList"
- orient="vertical">
+ orient="vertical"
+ onselect="if (this.selectedCount) this.clearSelection();">
+ <!--
+ <richlistitem>
+ <checkbox label="&engine.addons.label;"
+ accesskey="&engine.addons.accesskey;"
+ preference="engine.addons"/>
+ </richlistitem>
+ -->
<richlistitem>
<checkbox label="&engine.bookmarks.label;"
accesskey="&engine.bookmarks.accesskey;"
@@ -130,228 +125,42 @@
</columns>
<rows>
<row align="center">
- <label control="syncComputerName">
- &syncDeviceName.label;
- </label>
- <textbox id="syncComputerName"/>
+ <label value="&syncDeviceName.label;"
+ accesskey="&syncDeviceName.accesskey;"
+ control="syncComputerName"/>
+ <textbox id="syncComputerName"
+ onchange="gSyncUtils.changeName(this)"/>
</row>
</rows>
</grid>
<hbox>
- <label id="unlinkDevice" class="text-link">
- &unlinkDevice.label;
- </label>
+ <label class="text-link"
+ onclick="gSyncPane.startOver(true); return false;"
+ value="&unlinkDevice.label;"/>
</hbox>
</groupbox>
- <vbox id="tosPP-normal">
- <label id="tosPP-normal-ToS" class="text-link">
- &prefs.tosLink.label;
- </label>
- <label id="tosPP-normal-PP" class="text-link">
- &prefs.ppLink.label;
- </label>
- </vbox>
- </vbox>
-
- <vbox id="needsUpdate" align="center" pack="center">
- <hbox>
- <label id="loginError"/>
- <label id="loginErrorUpdatePass" class="text-link">
- &updatePass.label;
- </label>
- <label id="loginErrorResetPass" class="text-link">
- &resetPass.label;
- </label>
+ <hbox id="tosPP" pack="center">
+ <label class="text-link"
+ onclick="event.stopPropagation();gSyncUtils.openToS();"
+ value="&prefs.tosLink.label;"/>
+ <label class="text-link"
+ onclick="event.stopPropagation();gSyncUtils.openPrivacyPolicy();"
+ value="&prefs.ppLink.label;"/>
</hbox>
- <label id="loginErrorStartOver" class="text-link">
- &unlinkDevice.label;
- </label>
</vbox>
- <!-- These panels are for the Firefox Accounts identity provider -->
- <vbox id="noFxaAccount">
- <hbox>
- <vbox id="fxaContentWrapper">
- <groupbox id="noFxaGroup">
- <vbox>
- <label id="noFxaCaption">&signedOut.caption;</label>
- <description id="noFxaDescription" flex="1">&signedOut.description;</description>
- <hbox class="fxaAccountBox">
- <vbox>
- <image class="fxaFirefoxLogo"/>
- </vbox>
- <vbox flex="1">
- <label id="signedOutAccountBoxTitle">&signedOut.accountBox.title;</label>
- <hbox class="fxaAccountBoxButtons">
- <button id="noFxaSignUp" label="&signedOut.accountBox.create;" accesskey="&signedOut.accountBox.create.accesskey;"></button>
- <button id="noFxaSignIn" label="&signedOut.accountBox.signin;" accesskey="&signedOut.accountBox.signin.accesskey;"></button>
- </hbox>
- </vbox>
- </hbox>
- </vbox>
- </groupbox>
- </vbox>
- <vbox>
- <image class="fxaSyncIllustration"/>
- </vbox>
- </hbox>
- <label class="fxaMobilePromo">
- &mobilePromo3.start;<!-- We put these comments to avoid inserting white spaces
- --><label id="fxaMobilePromo-android"
- class="androidLink text-link"><!--
- -->&mobilePromo3.androidLink;</label><!--
- -->&mobilePromo3.iOSBefore;<!--
- --><label id="fxaMobilePromo-ios"
- class="iOSLink text-link"><!--
- -->&mobilePromo3.iOSLink;</label><!--
- -->&mobilePromo3.end;
- </label>
- </vbox>
-
- <vbox id="hasFxaAccount">
+ <vbox id="needsUpdate" align="center" pack="center">
<hbox>
- <vbox id="fxaContentWrapper">
- <groupbox id="fxaGroup">
- <caption><label>&syncBrand.fxAccount.label;</label></caption>
- <deck id="fxaLoginStatus">
-
- <!-- logged in and verified and all is good -->
- <hbox id="fxaLoginVerified" class="fxaAccountBox">
- <vbox align="center" pack="center">
- <image id="fxaProfileImage" class="actionable"
- role="button"
- onclick="gSyncPane.openChangeProfileImage(event);" hidden="true"
- onkeypress="gSyncPane.openChangeProfileImage(event);"
- tooltiptext="&profilePicture.tooltip;"/>
- </vbox>
- <vbox flex="1" pack="center">
- <label id="fxaDisplayName" hidden="true"/>
- <label id="fxaEmailAddress1"/>
- <hbox class="fxaAccountBoxButtons">
- <button id="fxaUnlinkButton" label="&disconnect.label;" accesskey="&disconnect.accesskey;"/>
- <html:a id="verifiedManage" target="_blank"
- accesskey="&verifiedManage.accesskey;"
- onkeypress="gSyncPane.openManageFirefoxAccount(event);"><!--
- -->&verifiedManage.label;</html:a>
- </hbox>
- </vbox>
- </hbox>
-
- <!-- logged in to an unverified account -->
- <hbox id="fxaLoginUnverified" class="fxaAccountBox">
- <vbox>
- <image id="fxaProfileImage"/>
- </vbox>
- <vbox flex="1">
- <hbox>
- <vbox><image id="fxaLoginRejectedWarning"/></vbox>
- <description flex="1">
- &signedInUnverified.beforename.label;
- <label id="fxaEmailAddress2"/>
- &signedInUnverified.aftername.label;
- </description>
- </hbox>
- <hbox class="fxaAccountBoxButtons">
- <button id="verifyFxaAccount" accesskey="&verify.accesskey;">&verify.label;</button>
- <button id="unverifiedUnlinkFxaAccount" accesskey="&forget.accesskey;">&forget.label;</button>
- </hbox>
- </vbox>
- </hbox>
-
- <!-- logged in locally but server rejected credentials -->
- <hbox id="fxaLoginRejected" class="fxaAccountBox">
- <vbox>
- <image id="fxaProfileImage"/>
- </vbox>
- <vbox flex="1">
- <hbox>
- <vbox><image id="fxaLoginRejectedWarning"/></vbox>
- <description flex="1">
- &signedInLoginFailure.beforename.label;
- <label id="fxaEmailAddress3"/>
- &signedInLoginFailure.aftername.label;
- </description>
- </hbox>
- <hbox class="fxaAccountBoxButtons">
- <button id="rejectReSignIn" accessky="&signIn.accesskey;">&signIn.label;</button>
- <button id="rejectUnlinkFxaAccount" accesskey="&forget.accesskey;">&forget.label;</button>
- </hbox>
- </vbox>
- </hbox>
- </deck>
- </groupbox>
- <groupbox id="syncOptions">
- <caption><label>&signedIn.engines.label;</label></caption>
- <hbox id="fxaSyncEngines">
- <vbox align="start" flex="1">
- <checkbox label="&engine.tabs.label;"
- accesskey="&engine.tabs.accesskey;"
- preference="engine.tabs"/>
- <checkbox label="&engine.bookmarks.label;"
- accesskey="&engine.bookmarks.accesskey;"
- preference="engine.bookmarks"/>
- <checkbox label="&engine.passwords.label;"
- accesskey="&engine.passwords.accesskey;"
- preference="engine.passwords"/>
- </vbox>
- <vbox align="start" flex="1">
- <checkbox label="&engine.history.label;"
- accesskey="&engine.history.accesskey;"
- preference="engine.history"/>
- <checkbox label="&engine.addons.label;"
- accesskey="&engine.addons.accesskey;"
- preference="engine.addons"/>
- <checkbox label="&engine.prefs.label;"
- accesskey="&engine.prefs.accesskey;"
- preference="engine.prefs"/>
- </vbox>
- <spacer/>
- </hbox>
- </groupbox>
- </vbox>
- <vbox>
- <image class="fxaSyncIllustration"/>
- </vbox>
+ <label id="loginError" value=""/>
+ <label class="text-link"
+ onclick="gSyncPane.updatePass(); return false;"
+ value="&updatePass.label;"/>
+ <label class="text-link"
+ onclick="gSyncPane.resetPass(); return false;"
+ value="&resetPass.label;"/>
</hbox>
- <groupbox>
- <caption>
- <label control="fxaSyncComputerName">
- &fxaSyncDeviceName.label;
- </label>
- </caption>
- <hbox id="fxaDeviceName">
- <textbox id="fxaSyncComputerName" disabled="true"/>
- <hbox>
- <button id="fxaChangeDeviceName"
- label="&changeSyncDeviceName.label;"
- accesskey="&changeSyncDeviceName.accesskey;"/>
- <button id="fxaCancelChangeDeviceName"
- label="&cancelChangeSyncDeviceName.label;"
- accesskey="&cancelChangeSyncDeviceName.accesskey;"
- hidden="true"/>
- <button id="fxaSaveChangeDeviceName"
- label="&saveChangeSyncDeviceName.label;"
- accesskey="&saveChangeSyncDeviceName.accesskey;"
- hidden="true"/>
- </hbox>
- </hbox>
- </groupbox>
- <label class="fxaMobilePromo">
- &mobilePromo3.start;<!-- We put these comments to avoid inserting white spaces
- --><label class="androidLink text-link" id="fxaMobilePromo-android-hasFxaAccount"><!--
- -->&mobilePromo3.androidLink;</label><!--
- -->&mobilePromo3.iOSBefore;<!--
- --><label class="iOSLink text-link" id="fxaMobilePromo-ios-hasFxaAccount"><!--
- -->&mobilePromo3.iOSLink;</label><!--
- -->&mobilePromo3.end;
- </label>
- <vbox id="tosPP-small" align="start">
- <label id="tosPP-small-ToS" class="text-link">
- &prefs.tosLink.label;
- </label>
- <label id="tosPP-small-PP" class="text-link">
- &fxaPrivacyNotice.link.label;
- </label>
- </vbox>
+ <label class="text-link"
+ onclick="gSyncPane.startOver(true); return false;"
+ value="&unlinkDevice.label;"/>
</vbox>
</deck>
diff --git a/application/basilisk/components/sync/aboutSyncTabs-bindings.xml b/application/basilisk/components/sync/aboutSyncTabs-bindings.xml
new file mode 100644
index 000000000..e6108209a
--- /dev/null
+++ b/application/basilisk/components/sync/aboutSyncTabs-bindings.xml
@@ -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>
diff --git a/application/basilisk/components/sync/aboutSyncTabs.css b/application/basilisk/components/sync/aboutSyncTabs.css
new file mode 100644
index 000000000..5a353175b
--- /dev/null
+++ b/application/basilisk/components/sync/aboutSyncTabs.css
@@ -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);
+}
diff --git a/application/basilisk/components/sync/aboutSyncTabs.js b/application/basilisk/components/sync/aboutSyncTabs.js
new file mode 100644
index 000000000..4808c052f
--- /dev/null
+++ b/application/basilisk/components/sync/aboutSyncTabs.js
@@ -0,0 +1,307 @@
+/* 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
+ lastFetch = Services.prefs.getIntPref("services.sync.lastTabFetch", 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);
+ }
+ }
+}
+
diff --git a/application/basilisk/components/sync/aboutSyncTabs.xul b/application/basilisk/components/sync/aboutSyncTabs.xul
new file mode 100644
index 000000000..a4aa0032f
--- /dev/null
+++ b/application/basilisk/components/sync/aboutSyncTabs.xul
@@ -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>
+
diff --git a/application/basilisk/components/sync/addDevice.js b/application/basilisk/components/sync/addDevice.js
new file mode 100644
index 000000000..0390d4397
--- /dev/null
+++ b/application/basilisk/components/sync/addDevice.js
@@ -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);
+ });
+});
diff --git a/application/basilisk/components/sync/addDevice.xul b/application/basilisk/components/sync/addDevice.xul
new file mode 100644
index 000000000..f2371aad0
--- /dev/null
+++ b/application/basilisk/components/sync/addDevice.xul
@@ -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>
diff --git a/application/basilisk/components/sync/genericChange.js b/application/basilisk/components/sync/genericChange.js
new file mode 100644
index 000000000..df6639178
--- /dev/null
+++ b/application/basilisk/components/sync/genericChange.js
@@ -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);
+ }
+};
diff --git a/application/basilisk/components/sync/genericChange.xul b/application/basilisk/components/sync/genericChange.xul
new file mode 100644
index 000000000..3c0b2cd6c
--- /dev/null
+++ b/application/basilisk/components/sync/genericChange.xul
@@ -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>
diff --git a/application/basilisk/components/sync/jar.mn b/application/basilisk/components/sync/jar.mn
new file mode 100644
index 000000000..3782038cd
--- /dev/null
+++ b/application/basilisk/components/sync/jar.mn
@@ -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 \ No newline at end of file
diff --git a/application/basilisk/components/sync/key.xhtml b/application/basilisk/components/sync/key.xhtml
new file mode 100644
index 000000000..92abf0ee6
--- /dev/null
+++ b/application/basilisk/components/sync/key.xhtml
@@ -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>
diff --git a/application/basilisk/components/syncedtabs/jar.mn b/application/basilisk/components/sync/moz.build
index ba2b105a1..2d64d506c 100644
--- a/application/basilisk/components/syncedtabs/jar.mn
+++ b/application/basilisk/components/sync/moz.build
@@ -1,7 +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/.
-browser.jar:
- content/browser/syncedtabs/sidebar.xhtml
- content/browser/syncedtabs/sidebar.js
+JAR_MANIFESTS += ['jar.mn']
+
diff --git a/application/basilisk/components/sync/notification.xml b/application/basilisk/components/sync/notification.xml
new file mode 100644
index 000000000..8ac881e08
--- /dev/null
+++ b/application/basilisk/components/sync/notification.xml
@@ -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>
diff --git a/application/basilisk/components/sync/progress.js b/application/basilisk/components/sync/progress.js
new file mode 100644
index 000000000..101160fa8
--- /dev/null
+++ b/application/basilisk/components/sync/progress.js
@@ -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();
+}
diff --git a/application/basilisk/components/sync/progress.xhtml b/application/basilisk/components/sync/progress.xhtml
new file mode 100644
index 000000000..d403cb20d
--- /dev/null
+++ b/application/basilisk/components/sync/progress.xhtml
@@ -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>
diff --git a/application/basilisk/components/sync/quota.js b/application/basilisk/components/sync/quota.js
new file mode 100644
index 000000000..b416a44cc
--- /dev/null
+++ b/application/basilisk/components/sync/quota.js
@@ -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) {}
+
+};
diff --git a/application/basilisk/components/sync/quota.xul b/application/basilisk/components/sync/quota.xul
new file mode 100644
index 000000000..99e6ed78b
--- /dev/null
+++ b/application/basilisk/components/sync/quota.xul
@@ -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>
diff --git a/application/basilisk/components/sync/setup.js b/application/basilisk/components/sync/setup.js
new file mode 100644
index 000000000..e8d67a5f6
--- /dev/null
+++ b/application/basilisk/components/sync/setup.js
@@ -0,0 +1,1071 @@
+/* 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 Cr = Components.results;
+var Cu = Components.utils;
+
+// page consts
+
+const PAIR_PAGE = 0;
+const INTRO_PAGE = 1;
+const NEW_ACCOUNT_START_PAGE = 2;
+const EXISTING_ACCOUNT_CONNECT_PAGE = 3;
+const EXISTING_ACCOUNT_LOGIN_PAGE = 4;
+const OPTIONS_PAGE = 5;
+const OPTIONS_CONFIRM_PAGE = 6;
+
+// Broader than we'd like, but after this changed from api-secure.recaptcha.net
+// we had no choice. At least we only do this for the duration of setup.
+// See discussion in Bugs 508112 and 653307.
+const RECAPTCHA_DOMAIN = "https://www.google.com";
+
+const PIN_PART_LENGTH = 4;
+
+Cu.import("resource://services-sync/main.js");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/PlacesUtils.jsm");
+Cu.import("resource://gre/modules/PluralForm.jsm");
+
+
+function setVisibility(element, visible) {
+ element.style.visibility = visible ? "visible" : "hidden";
+}
+
+var gSyncSetup = {
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
+ Ci.nsIWebProgressListener,
+ Ci.nsISupportsWeakReference]),
+
+ captchaBrowser: null,
+ wizard: null,
+ _disabledSites: [],
+
+ status: {
+ password: false,
+ email: false,
+ server: false
+ },
+
+ get _remoteSites() [Weave.Service.serverURL, RECAPTCHA_DOMAIN],
+
+ get _usingMainServers() {
+ if (this._settingUpNew)
+ return document.getElementById("server").selectedIndex == 0;
+ return document.getElementById("existingServer").selectedIndex == 0;
+ },
+
+ init: function () {
+ let obs = [
+ ["weave:service:change-passphrase", "onResetPassphrase"],
+ ["weave:service:login:start", "onLoginStart"],
+ ["weave:service:login:error", "onLoginEnd"],
+ ["weave:service:login:finish", "onLoginEnd"]];
+
+ // Add the observers now and remove them on unload
+ let self = this;
+ let addRem = function(add) {
+ obs.forEach(function([topic, func]) {
+ //XXXzpao This should use Services.obs.* but Weave's Obs does nice handling
+ // of `this`. Fix in a followup. (bug 583347)
+ if (add)
+ Weave.Svc.Obs.add(topic, self[func], self);
+ else
+ Weave.Svc.Obs.remove(topic, self[func], self);
+ });
+ };
+ addRem(true);
+ window.addEventListener("unload", function() addRem(false), false);
+
+ window.setTimeout(function () {
+ // Force Service to be loaded so that engines are registered.
+ // See Bug 670082.
+ Weave.Service;
+ }, 0);
+
+ this.captchaBrowser = document.getElementById("captcha");
+
+ this.wizardType = null;
+ if (window.arguments && window.arguments[0]) {
+ this.wizardType = window.arguments[0];
+ }
+ switch (this.wizardType) {
+ case null:
+ this.wizard.pageIndex = INTRO_PAGE;
+ // Fall through!
+ case "pair":
+ this.captchaBrowser.addProgressListener(this);
+ Weave.Svc.Prefs.set("firstSync", "notReady");
+ break;
+ case "reset":
+ this._resettingSync = true;
+ this.wizard.pageIndex = OPTIONS_PAGE;
+ break;
+ }
+
+ this.wizard.getButton("extra1").label =
+ this._stringBundle.GetStringFromName("button.syncOptions.label");
+
+ // Remember these values because the options pages change them temporarily.
+ this._nextButtonLabel = this.wizard.getButton("next").label;
+ this._nextButtonAccesskey = this.wizard.getButton("next")
+ .getAttribute("accesskey");
+ this._backButtonLabel = this.wizard.getButton("back").label;
+ this._backButtonAccesskey = this.wizard.getButton("back")
+ .getAttribute("accesskey");
+ },
+
+ startNewAccountSetup: function () {
+ if (!Weave.Utils.ensureMPUnlocked())
+ return false;
+ this._settingUpNew = true;
+ this.wizard.pageIndex = NEW_ACCOUNT_START_PAGE;
+ },
+
+ useExistingAccount: function () {
+ if (!Weave.Utils.ensureMPUnlocked())
+ return false;
+ this._settingUpNew = false;
+ if (this.wizardType == "pair") {
+ // We're already pairing, so there's no point in pairing again.
+ // Go straight to the manual login page.
+ this.wizard.pageIndex = EXISTING_ACCOUNT_LOGIN_PAGE;
+ } else {
+ this.wizard.pageIndex = EXISTING_ACCOUNT_CONNECT_PAGE;
+ }
+ },
+
+ resetPassphrase: function resetPassphrase() {
+ // Apply the existing form fields so that
+ // Weave.Service.changePassphrase() has the necessary credentials.
+ Weave.Service.identity.account = document.getElementById("existingAccountName").value;
+ Weave.Service.identity.basicPassword = document.getElementById("existingPassword").value;
+
+ // Generate a new passphrase so that Weave.Service.login() will
+ // actually do something.
+ let passphrase = Weave.Utils.generatePassphrase();
+ Weave.Service.identity.syncKey = passphrase;
+
+ // Only open the dialog if username + password are actually correct.
+ Weave.Service.login();
+ if ([Weave.LOGIN_FAILED_INVALID_PASSPHRASE,
+ Weave.LOGIN_FAILED_NO_PASSPHRASE,
+ Weave.LOGIN_SUCCEEDED].indexOf(Weave.Status.login) == -1) {
+ return;
+ }
+
+ // Hide any errors about the passphrase, we know it's not right.
+ let feedback = document.getElementById("existingPassphraseFeedbackRow");
+ feedback.hidden = true;
+ let el = document.getElementById("existingPassphrase");
+ el.value = Weave.Utils.hyphenatePassphrase(passphrase);
+
+ // changePassphrase() will sync, make sure we set the "firstSync" pref
+ // according to the user's pref.
+ Weave.Svc.Prefs.reset("firstSync");
+ this.setupInitialSync();
+ gSyncUtils.resetPassphrase(true);
+ },
+
+ onResetPassphrase: function () {
+ document.getElementById("existingPassphrase").value =
+ Weave.Utils.hyphenatePassphrase(Weave.Service.identity.syncKey);
+ this.checkFields();
+ this.wizard.advance();
+ },
+
+ onLoginStart: function () {
+ this.toggleLoginFeedback(false);
+ },
+
+ onLoginEnd: function () {
+ this.toggleLoginFeedback(true);
+ },
+
+ sendCredentialsAfterSync: function () {
+ let send = function() {
+ Services.obs.removeObserver("weave:service:sync:finish", send);
+ Services.obs.removeObserver("weave:service:sync:error", send);
+ let credentials = {account: Weave.Service.identity.account,
+ password: Weave.Service.identity.basicPassword,
+ synckey: Weave.Service.identity.syncKey,
+ serverURL: Weave.Service.serverURL};
+ this._jpakeclient.sendAndComplete(credentials);
+ }.bind(this);
+ Services.obs.addObserver("weave:service:sync:finish", send, false);
+ Services.obs.addObserver("weave:service:sync:error", send, false);
+ },
+
+ toggleLoginFeedback: function (stop) {
+ document.getElementById("login-throbber").hidden = stop;
+ let password = document.getElementById("existingPasswordFeedbackRow");
+ let server = document.getElementById("existingServerFeedbackRow");
+ let passphrase = document.getElementById("existingPassphraseFeedbackRow");
+
+ if (!stop || (Weave.Status.login == Weave.LOGIN_SUCCEEDED)) {
+ password.hidden = server.hidden = passphrase.hidden = true;
+ return;
+ }
+
+ let feedback;
+ switch (Weave.Status.login) {
+ case Weave.LOGIN_FAILED_NETWORK_ERROR:
+ case Weave.LOGIN_FAILED_SERVER_ERROR:
+ feedback = server;
+ break;
+ case Weave.LOGIN_FAILED_LOGIN_REJECTED:
+ case Weave.LOGIN_FAILED_NO_USERNAME:
+ case Weave.LOGIN_FAILED_NO_PASSWORD:
+ feedback = password;
+ break;
+ case Weave.LOGIN_FAILED_INVALID_PASSPHRASE:
+ feedback = passphrase;
+ break;
+ }
+ this._setFeedbackMessage(feedback, false, Weave.Status.login);
+ },
+
+ setupInitialSync: function () {
+ let action = document.getElementById("mergeChoiceRadio").selectedItem.id;
+ switch (action) {
+ case "resetClient":
+ // if we're not resetting sync, we don't need to explicitly
+ // call resetClient
+ if (!this._resettingSync)
+ return;
+ // otherwise, fall through
+ case "wipeClient":
+ case "wipeRemote":
+ Weave.Svc.Prefs.set("firstSync", action);
+ break;
+ }
+ },
+
+ // fun with validation!
+ checkFields: function () {
+ this.wizard.canAdvance = this.readyToAdvance();
+ },
+
+ readyToAdvance: function () {
+ switch (this.wizard.pageIndex) {
+ case INTRO_PAGE:
+ return false;
+ case NEW_ACCOUNT_START_PAGE:
+ for (let i in this.status) {
+ if (!this.status[i])
+ return false;
+ }
+ if (this._usingMainServers)
+ return document.getElementById("tos").checked;
+
+ return true;
+ case EXISTING_ACCOUNT_LOGIN_PAGE:
+ let hasUser = document.getElementById("existingAccountName").value != "";
+ let hasPass = document.getElementById("existingPassword").value != "";
+ let hasKey = document.getElementById("existingPassphrase").value != "";
+
+ if (hasUser && hasPass && hasKey) {
+ if (this._usingMainServers)
+ return true;
+
+ if (this._validateServer(document.getElementById("existingServer"))) {
+ return true;
+ }
+ }
+ return false;
+ }
+ // Default, e.g. wizard's special page -1 etc.
+ return true;
+ },
+
+ onPINInput: function onPINInput(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);
+ },
+
+ onEmailInput: function () {
+ // Check account validity when the user stops typing for 1 second.
+ if (this._checkAccountTimer)
+ window.clearTimeout(this._checkAccountTimer);
+ this._checkAccountTimer = window.setTimeout(function () {
+ gSyncSetup.checkAccount();
+ }, 1000);
+ },
+
+ checkAccount: function() {
+ delete this._checkAccountTimer;
+ let value = Weave.Utils.normalizeAccount(
+ document.getElementById("weaveEmail").value);
+ if (!value) {
+ this.status.email = false;
+ this.checkFields();
+ return;
+ }
+
+ let re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
+ let feedback = document.getElementById("emailFeedbackRow");
+ let valid = re.test(value);
+
+ let str = "";
+ if (!valid) {
+ str = "invalidEmail.label";
+ } else {
+ let availCheck = Weave.Service.checkAccount(value);
+ valid = availCheck == "available";
+ if (!valid) {
+ if (availCheck == "notAvailable")
+ str = "usernameNotAvailable.label";
+ else
+ str = availCheck;
+ }
+ }
+
+ this._setFeedbackMessage(feedback, valid, str);
+ this.status.email = valid;
+ if (valid)
+ Weave.Service.identity.account = value;
+ this.checkFields();
+ },
+
+ onPasswordChange: function () {
+ let password = document.getElementById("weavePassword");
+ let pwconfirm = document.getElementById("weavePasswordConfirm");
+ let [valid, errorString] = gSyncUtils.validatePassword(password, pwconfirm);
+
+ let feedback = document.getElementById("passwordFeedbackRow");
+ this._setFeedback(feedback, valid, errorString);
+
+ this.status.password = valid;
+ this.checkFields();
+ },
+
+ onPageShow: function() {
+ switch (this.wizard.pageIndex) {
+ case PAIR_PAGE:
+ this.wizard.getButton("back").hidden = true;
+ this.wizard.getButton("extra1").hidden = true;
+ this.onPINInput();
+ this.pin1.focus();
+ break;
+ case INTRO_PAGE:
+ // We may not need the captcha in the Existing Account branch of the
+ // wizard. However, we want to preload it to avoid any flickering while
+ // the Create Account page is shown.
+ this.loadCaptcha();
+ this.wizard.getButton("next").hidden = true;
+ this.wizard.getButton("back").hidden = true;
+ this.wizard.getButton("extra1").hidden = true;
+ this.checkFields();
+ break;
+ case NEW_ACCOUNT_START_PAGE:
+ this.wizard.getButton("extra1").hidden = false;
+ this.wizard.getButton("next").hidden = false;
+ this.wizard.getButton("back").hidden = false;
+ this.onServerCommand();
+ this.wizard.canRewind = true;
+ this.checkFields();
+ break;
+ case EXISTING_ACCOUNT_CONNECT_PAGE:
+ Weave.Svc.Prefs.set("firstSync", "existingAccount");
+ this.wizard.getButton("next").hidden = false;
+ this.wizard.getButton("back").hidden = false;
+ this.wizard.getButton("extra1").hidden = false;
+ this.wizard.canAdvance = false;
+ this.wizard.canRewind = true;
+ this.startEasySetup();
+ break;
+ case EXISTING_ACCOUNT_LOGIN_PAGE:
+ this.wizard.getButton("next").hidden = false;
+ this.wizard.getButton("back").hidden = false;
+ this.wizard.getButton("extra1").hidden = false;
+ this.wizard.canRewind = true;
+ this.checkFields();
+ break;
+ case OPTIONS_PAGE:
+ this.wizard.canRewind = false;
+ this.wizard.canAdvance = true;
+ if (!this._resettingSync) {
+ this.wizard.getButton("next").label =
+ this._stringBundle.GetStringFromName("button.syncOptionsDone.label");
+ this.wizard.getButton("next").removeAttribute("accesskey");
+ }
+ this.wizard.getButton("next").hidden = false;
+ this.wizard.getButton("back").hidden = true;
+ this.wizard.getButton("cancel").hidden = !this._resettingSync;
+ this.wizard.getButton("extra1").hidden = true;
+ document.getElementById("syncComputerName").value = Weave.Service.clientsEngine.localName;
+ document.getElementById("syncOptions").collapsed = this._resettingSync;
+ document.getElementById("mergeOptions").collapsed = this._settingUpNew;
+ break;
+ case OPTIONS_CONFIRM_PAGE:
+ this.wizard.canRewind = true;
+ this.wizard.canAdvance = true;
+ this.wizard.getButton("back").label =
+ this._stringBundle.GetStringFromName("button.syncOptionsCancel.label");
+ this.wizard.getButton("back").removeAttribute("accesskey");
+ this.wizard.getButton("back").hidden = this._resettingSync;
+ this.wizard.getButton("next").hidden = false;
+ this.wizard.getButton("finish").hidden = true;
+ break;
+ }
+ },
+
+ onWizardAdvance: function () {
+ // Check pageIndex so we don't prompt before the Sync setup wizard appears.
+ // This is a fallback in case the Master Password gets locked mid-wizard.
+ if ((this.wizard.pageIndex >= 0) &&
+ !Weave.Utils.ensureMPUnlocked()) {
+ return false;
+ }
+
+ switch (this.wizard.pageIndex) {
+ case PAIR_PAGE:
+ this.startPairing();
+ return false;
+ case NEW_ACCOUNT_START_PAGE:
+ // If the user selects Next (e.g. by hitting enter) when we haven't
+ // executed the delayed checks yet, execute them immediately.
+ if (this._checkAccountTimer) {
+ this.checkAccount();
+ }
+ if (this._checkServerTimer) {
+ this.checkServer();
+ }
+ if (!this.wizard.canAdvance) {
+ return false;
+ }
+
+ let doc = this.captchaBrowser.contentDocument;
+ let getField = function getField(field) {
+ let node = doc.getElementById("recaptcha_" + field + "_field");
+ return node && node.value;
+ };
+
+ // Display throbber
+ let feedback = document.getElementById("captchaFeedback");
+ let image = feedback.firstChild;
+ let label = image.nextSibling;
+ image.setAttribute("status", "active");
+ label.value = this._stringBundle.GetStringFromName("verifying.label");
+ setVisibility(feedback, true);
+
+ let password = document.getElementById("weavePassword").value;
+ let email = Weave.Utils.normalizeAccount(
+ document.getElementById("weaveEmail").value);
+ let challenge = getField("challenge");
+ let response = getField("response");
+
+ let error = Weave.Service.createAccount(email, password,
+ challenge, response);
+
+ if (error == null) {
+ Weave.Service.identity.account = email;
+ Weave.Service.identity.basicPassword = password;
+ Weave.Service.identity.syncKey = Weave.Utils.generatePassphrase();
+ this._handleNoScript(false);
+ Weave.Svc.Prefs.set("firstSync", "newAccount");
+ this.wizardFinish();
+ return false;
+ }
+
+ image.setAttribute("status", "error");
+ label.value = Weave.Utils.getErrorString(error);
+ return false;
+ case EXISTING_ACCOUNT_LOGIN_PAGE:
+ Weave.Service.identity.account = Weave.Utils.normalizeAccount(
+ document.getElementById("existingAccountName").value);
+ Weave.Service.identity.basicPassword =
+ document.getElementById("existingPassword").value;
+ let pp = document.getElementById("existingPassphrase").value;
+ Weave.Service.identity.syncKey = Weave.Utils.normalizePassphrase(pp);
+ if (Weave.Service.login()) {
+ this.wizardFinish();
+ }
+ return false;
+ case OPTIONS_PAGE:
+ let desc = document.getElementById("mergeChoiceRadio").selectedIndex;
+ // No confirmation needed on new account setup or merge option
+ // with existing account.
+ if (this._settingUpNew || (!this._resettingSync && desc == 0))
+ return this.returnFromOptions();
+ return this._handleChoice();
+ case OPTIONS_CONFIRM_PAGE:
+ if (this._resettingSync) {
+ this.wizardFinish();
+ return false;
+ }
+ return this.returnFromOptions();
+ }
+ return true;
+ },
+
+ onWizardBack: function () {
+ switch (this.wizard.pageIndex) {
+ case NEW_ACCOUNT_START_PAGE:
+ case EXISTING_ACCOUNT_LOGIN_PAGE:
+ this.wizard.pageIndex = INTRO_PAGE;
+ return false;
+ case EXISTING_ACCOUNT_CONNECT_PAGE:
+ this.abortEasySetup();
+ this.wizard.pageIndex = INTRO_PAGE;
+ return false;
+ case EXISTING_ACCOUNT_LOGIN_PAGE:
+ // If we were already pairing on entry, we went straight to the manual
+ // login page. If subsequently we go back, return to the page that lets
+ // us choose whether we already have an account.
+ if (this.wizardType == "pair") {
+ this.wizard.pageIndex = INTRO_PAGE;
+ return false;
+ }
+ return true;
+ case OPTIONS_CONFIRM_PAGE:
+ // Backing up from the confirmation page = resetting first sync to merge.
+ document.getElementById("mergeChoiceRadio").selectedIndex = 0;
+ return this.returnFromOptions();
+ }
+ return true;
+ },
+
+ wizardFinish: function () {
+ this.setupInitialSync();
+
+ if (this.wizardType == "pair") {
+ this.completePairing();
+ }
+
+ if (!this._resettingSync) {
+ function isChecked(element) {
+ return document.getElementById(element).hasAttribute("checked");
+ }
+
+ let prefs = ["engine.bookmarks", "engine.passwords", "engine.history",
+ "engine.tabs", "engine.prefs", "engine.addons"];
+ for (let i = 0;i < prefs.length;i++) {
+ Weave.Svc.Prefs.set(prefs[i], isChecked(prefs[i]));
+ }
+
+ // XXX: Addons syncing is currently not operational;
+ // Make doubly-sure to always disable addons syncing pref
+ Weave.Svc.Prefs.set("engine.addons", false);
+
+ this._handleNoScript(false);
+ if (Weave.Svc.Prefs.get("firstSync", "") == "notReady")
+ Weave.Svc.Prefs.reset("firstSync");
+
+ Weave.Service.persistLogin();
+ Weave.Svc.Obs.notify("weave:service:setup-complete");
+
+ gSyncUtils.openFirstSyncProgressPage();
+ }
+ Weave.Utils.nextTick(Weave.Service.sync, Weave.Service);
+ window.close();
+ },
+
+ onWizardCancel: function () {
+ if (this._resettingSync)
+ return;
+
+ this.abortEasySetup();
+ this._handleNoScript(false);
+ Weave.Service.startOver();
+ },
+
+ onSyncOptions: function () {
+ this._beforeOptionsPage = this.wizard.pageIndex;
+ this.wizard.pageIndex = OPTIONS_PAGE;
+ },
+
+ returnFromOptions: function() {
+ this.wizard.getButton("next").label = this._nextButtonLabel;
+ this.wizard.getButton("next").setAttribute("accesskey",
+ this._nextButtonAccesskey);
+ this.wizard.getButton("back").label = this._backButtonLabel;
+ this.wizard.getButton("back").setAttribute("accesskey",
+ this._backButtonAccesskey);
+ this.wizard.getButton("cancel").hidden = false;
+ this.wizard.getButton("extra1").hidden = false;
+ this.wizard.pageIndex = this._beforeOptionsPage;
+ return false;
+ },
+
+ startPairing: function startPairing() {
+ this.pairDeviceErrorRow.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() {
+ self.wizard.pageIndex = INTRO_PAGE;
+ },
+ onComplete: function onComplete() {
+ // This method will never be called since SendCredentialsController
+ // will take over after the wizard completes.
+ },
+ onAbort: function onAbort(error) {
+ delete self._jpakeclient;
+
+ // Aborted by user, ignore. The window is almost certainly going to close
+ // or is already closed.
+ if (error == JPAKE_ERROR_USERABORT) {
+ return;
+ }
+
+ self.pairDeviceErrorRow.hidden = false;
+ self.pairDeviceThrobber.hidden = true;
+ self.pin1.value = self.pin2.value = self.pin3.value = "";
+ self.pin1.disabled = self.pin2.disabled = self.pin3.disabled = false;
+ if (self.wizard.pageIndex == PAIR_PAGE) {
+ self.pin1.focus();
+ }
+ }
+ });
+ this.pairDeviceThrobber.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 = true;
+ jpakeclient.pairWithPIN(pin, expectDelay);
+ },
+
+ completePairing: function completePairing() {
+ if (!this._jpakeclient) {
+ // The channel was aborted while we were setting up the account
+ // locally. XXX TODO should we do anything here, e.g. tell
+ // the user on the last wizard page that it's ok, they just
+ // have to pair again?
+ return;
+ }
+ let controller = new Weave.SendCredentialsController(this._jpakeclient,
+ Weave.Service);
+ this._jpakeclient.controller = controller;
+ },
+
+ startEasySetup: function () {
+ // Don't do anything if we have a client already (e.g. we went to
+ // Sync Options and just came back).
+ if (this._jpakeclient)
+ return;
+
+ // When onAbort is called, Weave may already be gone
+ const JPAKE_ERROR_USERABORT = Weave.JPAKE_ERROR_USERABORT;
+
+ let self = this;
+ this._jpakeclient = new Weave.JPAKEClient({
+ displayPIN: function displayPIN(pin) {
+ document.getElementById("easySetupPIN1").value = pin.slice(0, 4);
+ document.getElementById("easySetupPIN2").value = pin.slice(4, 8);
+ document.getElementById("easySetupPIN3").value = pin.slice(8);
+ },
+
+ onPairingStart: function onPairingStart() {},
+
+ onComplete: function onComplete(credentials) {
+ Weave.Service.identity.account = credentials.account;
+ Weave.Service.identity.basicPassword = credentials.password;
+ Weave.Service.identity.syncKey = credentials.synckey;
+ Weave.Service.serverURL = credentials.serverURL;
+ gSyncSetup.wizardFinish();
+ },
+
+ onAbort: function onAbort(error) {
+ delete self._jpakeclient;
+
+ // Ignore if wizard is aborted.
+ if (error == JPAKE_ERROR_USERABORT)
+ return;
+
+ // Automatically go to manual setup if we couldn't acquire a channel.
+ if (error == Weave.JPAKE_ERROR_CHANNEL) {
+ self.wizard.pageIndex = EXISTING_ACCOUNT_LOGIN_PAGE;
+ return;
+ }
+
+ // Restart on all other errors.
+ self.startEasySetup();
+ }
+ });
+ this._jpakeclient.receiveNoPIN();
+ },
+
+ abortEasySetup: function () {
+ document.getElementById("easySetupPIN1").value = "";
+ document.getElementById("easySetupPIN2").value = "";
+ document.getElementById("easySetupPIN3").value = "";
+ if (!this._jpakeclient)
+ return;
+
+ this._jpakeclient.abort();
+ delete this._jpakeclient;
+ },
+
+ manualSetup: function () {
+ this.abortEasySetup();
+ this.wizard.pageIndex = EXISTING_ACCOUNT_LOGIN_PAGE;
+ },
+
+ // _handleNoScript is needed because it blocks the captcha. So we temporarily
+ // allow the necessary sites so that we can verify the user is in fact a human.
+ // This was done with the help of Giorgio (NoScript author). See bug 508112.
+ _handleNoScript: function (addExceptions) {
+ // if NoScript isn't installed, or is disabled, bail out.
+ let ns = Cc["@maone.net/noscript-service;1"];
+ if (ns == null)
+ return;
+
+ ns = ns.getService().wrappedJSObject;
+ if (addExceptions) {
+ this._remoteSites.forEach(function(site) {
+ site = ns.getSite(site);
+ if (!ns.isJSEnabled(site)) {
+ this._disabledSites.push(site); // save status
+ ns.setJSEnabled(site, true); // allow site
+ }
+ }, this);
+ }
+ else {
+ this._disabledSites.forEach(function(site) {
+ ns.setJSEnabled(site, false);
+ });
+ this._disabledSites = [];
+ }
+ },
+
+ onExistingServerCommand: function () {
+ let control = document.getElementById("existingServer");
+ if (control.selectedIndex == 0) {
+ control.removeAttribute("editable");
+ Weave.Svc.Prefs.reset("serverURL");
+ } else {
+ control.setAttribute("editable", "true");
+ // Force a style flush to ensure that the binding is attached.
+ control.clientTop;
+ control.value = "";
+ control.inputField.focus();
+ }
+ document.getElementById("existingServerFeedbackRow").hidden = true;
+ this.checkFields();
+ },
+
+ onExistingServerInput: function () {
+ // Check custom server validity when the user stops typing for 1 second.
+ if (this._existingServerTimer)
+ window.clearTimeout(this._existingServerTimer);
+ this._existingServerTimer = window.setTimeout(function () {
+ gSyncSetup.checkFields();
+ }, 1000);
+ },
+
+ onServerCommand: function () {
+ setVisibility(document.getElementById("TOSRow"), this._usingMainServers);
+ let control = document.getElementById("server");
+ if (!this._usingMainServers) {
+ control.setAttribute("editable", "true");
+ // Force a style flush to ensure that the binding is attached.
+ control.clientTop;
+ control.value = "";
+ control.inputField.focus();
+ // checkServer() will call checkAccount() and checkFields().
+ this.checkServer();
+ return;
+ }
+ control.removeAttribute("editable");
+ Weave.Svc.Prefs.reset("serverURL");
+ if (this._settingUpNew) {
+ this.loadCaptcha();
+ }
+ this.checkAccount();
+ this.status.server = true;
+ document.getElementById("serverFeedbackRow").hidden = true;
+ this.checkFields();
+ },
+
+ onServerInput: function () {
+ // Check custom server validity when the user stops typing for 1 second.
+ if (this._checkServerTimer)
+ window.clearTimeout(this._checkServerTimer);
+ this._checkServerTimer = window.setTimeout(function () {
+ gSyncSetup.checkServer();
+ }, 1000);
+ },
+
+ checkServer: function () {
+ delete this._checkServerTimer;
+ let el = document.getElementById("server");
+ let valid = false;
+ let feedback = document.getElementById("serverFeedbackRow");
+ let str = "";
+ if (el.value) {
+ valid = this._validateServer(el);
+ let str = valid ? "" : "serverInvalid.label";
+ this._setFeedbackMessage(feedback, valid, str);
+ }
+ else
+ this._setFeedbackMessage(feedback, true);
+
+ // Recheck account against the new server.
+ if (valid)
+ this.checkAccount();
+
+ this.status.server = valid;
+ this.checkFields();
+ },
+
+ _validateServer: function (element) {
+ let valid = false;
+ let val = element.value;
+ if (!val)
+ return false;
+
+ let uri = Weave.Utils.makeURI(val);
+
+ if (!uri)
+ uri = Weave.Utils.makeURI("https://" + val);
+
+ if (uri && this._settingUpNew) {
+ function isValid(uri) {
+ Weave.Service.serverURL = uri.spec;
+ let check = Weave.Service.checkAccount("a");
+ return (check == "available" || check == "notAvailable");
+ }
+
+ if (uri.schemeIs("http")) {
+ uri.scheme = "https";
+ if (isValid(uri))
+ valid = true;
+ else
+ // setting the scheme back to http
+ uri.scheme = "http";
+ }
+ if (!valid)
+ valid = isValid(uri);
+
+ if (valid) {
+ this.loadCaptcha();
+ }
+ }
+ else if (uri) {
+ valid = true;
+ Weave.Service.serverURL = uri.spec;
+ }
+
+ if (valid)
+ element.value = Weave.Service.serverURL;
+ else
+ Weave.Svc.Prefs.reset("serverURL");
+
+ return valid;
+ },
+
+ _handleChoice: function () {
+ let desc = document.getElementById("mergeChoiceRadio").selectedIndex;
+ document.getElementById("chosenActionDeck").selectedIndex = desc;
+ switch (desc) {
+ case 1:
+ if (this._case1Setup)
+ break;
+
+ let places_db = PlacesUtils.history
+ .QueryInterface(Ci.nsPIPlacesDatabase)
+ .DBConnection;
+ if (Weave.Service.engineManager.get("history").enabled) {
+ let daysOfHistory = 0;
+ let stm = places_db.createStatement(
+ "SELECT ROUND(( " +
+ "strftime('%s','now','localtime','utc') - " +
+ "( " +
+ "SELECT visit_date FROM moz_historyvisits " +
+ "ORDER BY visit_date ASC LIMIT 1 " +
+ ")/1000000 " +
+ ")/86400) AS daysOfHistory ");
+
+ if (stm.step())
+ daysOfHistory = stm.getInt32(0);
+ // Support %S for historical reasons (see bug 600141)
+ document.getElementById("historyCount").value =
+ PluralForm.get(daysOfHistory,
+ this._stringBundle.GetStringFromName("historyDaysCount.label"))
+ .replace("%S", daysOfHistory)
+ .replace("#1", daysOfHistory);
+ } else {
+ document.getElementById("historyCount").hidden = true;
+ }
+
+ if (Weave.Service.engineManager.get("bookmarks").enabled) {
+ let bookmarks = 0;
+ let stm = places_db.createStatement(
+ "SELECT count(*) AS bookmarks " +
+ "FROM moz_bookmarks b " +
+ "LEFT JOIN moz_bookmarks t ON " +
+ "b.parent = t.id WHERE b.type = 1 AND t.parent <> :tag");
+ stm.params.tag = PlacesUtils.tagsFolderId;
+ if (stm.executeStep())
+ bookmarks = stm.row.bookmarks;
+ // Support %S for historical reasons (see bug 600141)
+ document.getElementById("bookmarkCount").value =
+ PluralForm.get(bookmarks,
+ this._stringBundle.GetStringFromName("bookmarksCount.label"))
+ .replace("%S", bookmarks)
+ .replace("#1", bookmarks);
+ } else {
+ document.getElementById("bookmarkCount").hidden = true;
+ }
+
+ if (Weave.Service.engineManager.get("passwords").enabled) {
+ let logins = Services.logins.getAllLogins({});
+ // Support %S for historical reasons (see bug 600141)
+ document.getElementById("passwordCount").value =
+ PluralForm.get(logins.length,
+ this._stringBundle.GetStringFromName("passwordsCount.label"))
+ .replace("%S", logins.length)
+ .replace("#1", logins.length);
+ } else {
+ document.getElementById("passwordCount").hidden = true;
+ }
+
+ if (!Weave.Service.engineManager.get("prefs").enabled) {
+ document.getElementById("prefsWipe").hidden = true;
+ }
+
+ let addonsEngine = Weave.Service.engineManager.get("addons");
+ if (addonsEngine.enabled) {
+ let ids = addonsEngine._store.getAllIDs();
+ let blessedcount = 0;
+ for each (let i in ids) {
+ if (i) {
+ blessedcount++;
+ }
+ }
+ // bug 600141 does not apply, as this does not have to support existing strings
+ document.getElementById("addonCount").value =
+ PluralForm.get(blessedcount,
+ this._stringBundle.GetStringFromName("addonsCount.label"))
+ .replace("#1", blessedcount);
+ } else {
+ document.getElementById("addonCount").hidden = true;
+ }
+
+ this._case1Setup = true;
+ break;
+ case 2:
+ if (this._case2Setup)
+ break;
+ let count = 0;
+ function appendNode(label) {
+ let box = document.getElementById("clientList");
+ let node = document.createElement("label");
+ node.setAttribute("value", label);
+ node.setAttribute("class", "data indent");
+ box.appendChild(node);
+ }
+
+ for each (let name in Weave.Service.clientsEngine.stats.names) {
+ // Don't list the current client
+ if (name == Weave.Service.clientsEngine.localName)
+ continue;
+
+ // Only show the first several client names
+ if (++count <= 5)
+ appendNode(name);
+ }
+ if (count > 5) {
+ // Support %S for historical reasons (see bug 600141)
+ let label =
+ PluralForm.get(count - 5,
+ this._stringBundle.GetStringFromName("additionalClientCount.label"))
+ .replace("%S", count - 5)
+ .replace("#1", count - 5);
+ appendNode(label);
+ }
+ this._case2Setup = true;
+ break;
+ }
+
+ return true;
+ },
+
+ // sets class and string on a feedback element
+ // if no property string is passed in, we clear label/style
+ _setFeedback: function (element, success, string) {
+ element.hidden = success || !string;
+ let classname = success ? "success" : "error";
+ let image = element.getElementsByAttribute("class", "statusIcon")[0];
+ image.setAttribute("status", classname);
+ let label = element.getElementsByAttribute("class", "status")[0];
+ label.value = string;
+ },
+
+ // shim
+ _setFeedbackMessage: function (element, success, string) {
+ let str = "";
+ if (string) {
+ try {
+ str = this._stringBundle.GetStringFromName(string);
+ } catch(e) {}
+
+ if (!str)
+ str = Weave.Utils.getErrorString(string);
+ }
+ this._setFeedback(element, success, str);
+ },
+
+ loadCaptcha: function loadCaptcha() {
+ let captchaURI = Weave.Service.miscAPI + "captcha_html";
+ // First check for NoScript and whitelist the right sites.
+ this._handleNoScript(true);
+ if (this.captchaBrowser.currentURI.spec != captchaURI) {
+ this.captchaBrowser.loadURI(captchaURI);
+ }
+ },
+
+ onStateChange: function(webProgress, request, stateFlags, status) {
+ // We're only looking for the end of the frame load
+ if ((stateFlags & Ci.nsIWebProgressListener.STATE_STOP) == 0)
+ return;
+ if ((stateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK) == 0)
+ return;
+ if ((stateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW) == 0)
+ return;
+
+ // If we didn't find a captcha, assume it's not needed and don't show it.
+ let responseStatus = request.QueryInterface(Ci.nsIHttpChannel).responseStatus;
+ setVisibility(this.captchaBrowser, responseStatus != 404);
+ //XXX TODO we should really log any responseStatus other than 200
+ },
+ onProgressChange: function() {},
+ onStatusChange: function() {},
+ onSecurityChange: function() {},
+ onLocationChange: function () {}
+};
+
+// Define lazy getters for various XUL elements.
+//
+// onWizardAdvance() and onPageShow() are run before init(), so we'll even
+// define things that will almost certainly be used (like 'wizard') as a lazy
+// getter here.
+["wizard",
+ "pin1",
+ "pin2",
+ "pin3",
+ "pairDeviceErrorRow",
+ "pairDeviceThrobber"].forEach(function (id) {
+ XPCOMUtils.defineLazyGetter(gSyncSetup, id, function() {
+ return document.getElementById(id);
+ });
+});
+XPCOMUtils.defineLazyGetter(gSyncSetup, "nextFocusEl", function () {
+ return {pin1: this.pin2,
+ pin2: this.pin3,
+ pin3: this.wizard.getButton("next")};
+});
+XPCOMUtils.defineLazyGetter(gSyncSetup, "_stringBundle", function() {
+ return Services.strings.createBundle("chrome://browser/locale/syncSetup.properties");
+});
diff --git a/application/basilisk/components/sync/setup.xul b/application/basilisk/components/sync/setup.xul
new file mode 100644
index 000000000..cf2cc77e4
--- /dev/null
+++ b/application/basilisk/components/sync/setup.xul
@@ -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>
+
diff --git a/application/basilisk/components/sync/utils.js b/application/basilisk/components/sync/utils.js
new file mode 100644
index 000000000..d41ecf18a
--- /dev/null
+++ b/application/basilisk/components/sync/utils.js
@@ -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];
+ }
+};
diff --git a/application/basilisk/components/syncedtabs/EventEmitter.jsm b/application/basilisk/components/syncedtabs/EventEmitter.jsm
deleted file mode 100644
index ec3225f0f..000000000
--- a/application/basilisk/components/syncedtabs/EventEmitter.jsm
+++ /dev/null
@@ -1,45 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-
-this.EXPORTED_SYMBOLS = [
- "EventEmitter"
-];
-
-// Simple event emitter abstraction for storage objects to use.
-function EventEmitter () {
- this._events = new Map();
-}
-
-EventEmitter.prototype = {
- on(event, listener) {
- if (this._events.has(event)) {
- this._events.get(event).add(listener);
- } else {
- this._events.set(event, new Set([listener]));
- }
- },
- off(event, listener) {
- if (!this._events.has(event)) {
- return;
- }
- this._events.get(event).delete(listener);
- },
- emit(event, ...args) {
- if (!this._events.has(event)) {
- return;
- }
- for (let listener of this._events.get(event).values()) {
- try {
- listener.apply(this, args);
- } catch (e) {
- Cu.reportError(e);
- }
- }
- },
-};
-
diff --git a/application/basilisk/components/syncedtabs/SyncedTabsDeckComponent.js b/application/basilisk/components/syncedtabs/SyncedTabsDeckComponent.js
deleted file mode 100644
index c35277795..000000000
--- a/application/basilisk/components/syncedtabs/SyncedTabsDeckComponent.js
+++ /dev/null
@@ -1,169 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-
-Cu.import("resource:///modules/syncedtabs/SyncedTabsDeckStore.js");
-Cu.import("resource:///modules/syncedtabs/SyncedTabsDeckView.js");
-Cu.import("resource:///modules/syncedtabs/SyncedTabsListStore.js");
-Cu.import("resource:///modules/syncedtabs/TabListComponent.js");
-Cu.import("resource:///modules/syncedtabs/TabListView.js");
-let { getChromeWindow } = Cu.import("resource:///modules/syncedtabs/util.js", {});
-
-XPCOMUtils.defineLazyGetter(this, "FxAccountsCommon", function () {
- return Components.utils.import("resource://gre/modules/FxAccountsCommon.js", {});
-});
-
-let log = Cu.import("resource://gre/modules/Log.jsm", {})
- .Log.repository.getLogger("Sync.RemoteTabs");
-
-this.EXPORTED_SYMBOLS = [
- "SyncedTabsDeckComponent"
-];
-
-/* SyncedTabsDeckComponent
- * This component instantiates views and storage objects as well as defines
- * behaviors that will be passed down to the views. This helps keep the views
- * isolated and easier to test.
- */
-
-function SyncedTabsDeckComponent({
- window, SyncedTabs, fxAccounts, deckStore, listStore, listComponent, DeckView, getChromeWindowMock,
-}) {
- this._window = window;
- this._SyncedTabs = SyncedTabs;
- this._fxAccounts = fxAccounts;
- this._DeckView = DeckView || SyncedTabsDeckView;
- // used to stub during tests
- this._getChromeWindow = getChromeWindowMock || getChromeWindow;
-
- this._deckStore = deckStore || new SyncedTabsDeckStore();
- this._syncedTabsListStore = listStore || new SyncedTabsListStore(SyncedTabs);
- this.tabListComponent = listComponent || new TabListComponent({
- window: this._window,
- store: this._syncedTabsListStore,
- View: TabListView,
- SyncedTabs: SyncedTabs,
- clipboardHelper: Cc["@mozilla.org/widget/clipboardhelper;1"]
- .getService(Ci.nsIClipboardHelper),
- getChromeWindow: this._getChromeWindow,
- });
-}
-
-SyncedTabsDeckComponent.prototype = {
- PANELS: {
- TABS_CONTAINER: "tabs-container",
- TABS_FETCHING: "tabs-fetching",
- NOT_AUTHED_INFO: "notAuthedInfo",
- SINGLE_DEVICE_INFO: "singleDeviceInfo",
- TABS_DISABLED: "tabs-disabled",
- },
-
- get container() {
- return this._deckView ? this._deckView.container : null;
- },
-
- init() {
- Services.obs.addObserver(this, this._SyncedTabs.TOPIC_TABS_CHANGED, false);
- Services.obs.addObserver(this, FxAccountsCommon.ONLOGIN_NOTIFICATION, false);
-
- // Go ahead and trigger sync
- this._SyncedTabs.syncTabs()
- .catch(Cu.reportError);
-
- this._deckView = new this._DeckView(this._window, this.tabListComponent, {
- onAndroidClick: event => this.openAndroidLink(event),
- oniOSClick: event => this.openiOSLink(event),
- onSyncPrefClick: event => this.openSyncPrefs(event)
- });
-
- this._deckStore.on("change", state => this._deckView.render(state));
- // Trigger the initial rendering of the deck view
- // Object.values only in nightly
- this._deckStore.setPanels(Object.keys(this.PANELS).map(k => this.PANELS[k]));
- // Set the initial panel to display
- this.updatePanel();
- },
-
- uninit() {
- Services.obs.removeObserver(this, this._SyncedTabs.TOPIC_TABS_CHANGED);
- Services.obs.removeObserver(this, FxAccountsCommon.ONLOGIN_NOTIFICATION);
- this._deckView.destroy();
- },
-
- observe(subject, topic, data) {
- switch (topic) {
- case this._SyncedTabs.TOPIC_TABS_CHANGED:
- this._syncedTabsListStore.getData();
- this.updatePanel();
- break;
- case FxAccountsCommon.ONLOGIN_NOTIFICATION:
- this.updatePanel();
- break;
- default:
- break;
- }
- },
-
- // There's no good way to mock fxAccounts in browser tests where it's already
- // been instantiated, so we have this method for stubbing.
- _accountStatus() {
- return this._fxAccounts.accountStatus();
- },
-
- getPanelStatus() {
- return this._accountStatus().then(exists => {
- if (!exists) {
- return this.PANELS.NOT_AUTHED_INFO;
- }
- if (!this._SyncedTabs.isConfiguredToSyncTabs) {
- return this.PANELS.TABS_DISABLED;
- }
- if (!this._SyncedTabs.hasSyncedThisSession) {
- return this.PANELS.TABS_FETCHING;
- }
- return this._SyncedTabs.getTabClients().then(clients => {
- if (clients.length) {
- return this.PANELS.TABS_CONTAINER;
- }
- return this.PANELS.SINGLE_DEVICE_INFO;
- });
- })
- .catch(err => {
- Cu.reportError(err);
- return this.PANELS.NOT_AUTHED_INFO;
- });
- },
-
- updatePanel() {
- // return promise for tests
- return this.getPanelStatus()
- .then(panelId => this._deckStore.selectPanel(panelId))
- .catch(Cu.reportError);
- },
-
- openAndroidLink(event) {
- let href = Services.prefs.getCharPref("identity.mobilepromo.android") + "synced-tabs-sidebar";
- this._openUrl(href, event);
- },
-
- openiOSLink(event) {
- let href = Services.prefs.getCharPref("identity.mobilepromo.ios") + "synced-tabs-sidebar";
- this._openUrl(href, event);
- },
-
- _openUrl(url, event) {
- this._window.openUILink(url, event);
- },
-
- openSyncPrefs() {
- this._getChromeWindow(this._window).gSyncUI.openSetup(null, "tabs-sidebar");
- }
-};
-
diff --git a/application/basilisk/components/syncedtabs/SyncedTabsDeckStore.js b/application/basilisk/components/syncedtabs/SyncedTabsDeckStore.js
deleted file mode 100644
index ede6914c8..000000000
--- a/application/basilisk/components/syncedtabs/SyncedTabsDeckStore.js
+++ /dev/null
@@ -1,60 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-
-let { EventEmitter } = Cu.import("resource:///modules/syncedtabs/EventEmitter.jsm", {});
-
-this.EXPORTED_SYMBOLS = [
- "SyncedTabsDeckStore"
-];
-
-/**
- * SyncedTabsDeckStore
- *
- * This store keeps track of the deck view state, including the panels and which
- * one is selected. The view listens for change events on the store, which are
- * triggered whenever the state changes. If it's a small change, the state
- * will have `isUpdatable` set to true so the view can skip rerendering the whole
- * DOM.
- */
-function SyncedTabsDeckStore() {
- EventEmitter.call(this);
- this._panels = [];
-}
-
-Object.assign(SyncedTabsDeckStore.prototype, EventEmitter.prototype, {
- _change(isUpdatable = false) {
- let panels = this._panels.map(panel => {
- return {id: panel, selected: panel === this._selectedPanel};
- });
- this.emit("change", {panels, isUpdatable: isUpdatable});
- },
-
- /**
- * Sets the selected panelId and triggers a change event.
- * @param {String} panelId - ID of the panel to select.
- */
- selectPanel(panelId) {
- if (this._panels.indexOf(panelId) === -1 || this._selectedPanel === panelId) {
- return;
- }
- this._selectedPanel = panelId;
- this._change(true);
- },
-
- /**
- * Update the set of panels in the deck and trigger a change event.
- * @param {Array} panels - an array of IDs for each panel in the deck.
- */
- setPanels(panels) {
- if (panels === this._panels) {
- return;
- }
- this._panels = panels || [];
- this._change();
- }
-});
diff --git a/application/basilisk/components/syncedtabs/SyncedTabsDeckView.js b/application/basilisk/components/syncedtabs/SyncedTabsDeckView.js
deleted file mode 100644
index e9efff323..000000000
--- a/application/basilisk/components/syncedtabs/SyncedTabsDeckView.js
+++ /dev/null
@@ -1,116 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-
-let { getChromeWindow } = Cu.import("resource:///modules/syncedtabs/util.js", {});
-
-let log = Cu.import("resource://gre/modules/Log.jsm", {})
- .Log.repository.getLogger("Sync.RemoteTabs");
-
-this.EXPORTED_SYMBOLS = [
- "SyncedTabsDeckView"
-];
-
-/**
- * SyncedTabsDeckView
- *
- * Instances of SyncedTabsDeckView render DOM nodes from a given state.
- * No state is kept internaly and the DOM will completely
- * rerender unless the state flags `isUpdatable`, which helps
- * make small changes without the overhead of a full rerender.
- */
-const SyncedTabsDeckView = function (window, tabListComponent, props) {
- this.props = props;
-
- this._window = window;
- this._doc = window.document;
-
- this._tabListComponent = tabListComponent;
- this._deckTemplate = this._doc.getElementById("deck-template");
- this.container = this._doc.createElement("div");
-};
-
-SyncedTabsDeckView.prototype = {
- render(state) {
- if (state.isUpdatable) {
- this.update(state);
- } else {
- this.create(state);
- }
- },
-
- create(state) {
- let deck = this._doc.importNode(this._deckTemplate.content, true).firstElementChild;
- this._clearChilden();
-
- let tabListWrapper = this._doc.createElement("div");
- tabListWrapper.className = "tabs-container sync-state";
- this._tabListComponent.init();
- tabListWrapper.appendChild(this._tabListComponent.container);
- deck.appendChild(tabListWrapper);
- this.container.appendChild(deck);
-
- this._generateDevicePromo();
-
- this._attachListeners();
- this.update(state);
- },
-
- _getBrowserBundle() {
- return getChromeWindow(this._window).document.getElementById("bundle_browser");
- },
-
- _generateDevicePromo() {
- let bundle = this._getBrowserBundle();
- let formatArgs = ["android", "ios"].map(os => {
- let link = this._doc.createElement("a");
- link.textContent = bundle.getString(`appMenuRemoteTabs.mobilePromo.${os}`);
- link.className = `${os}-link text-link`;
- link.setAttribute("href", "#");
- return link.outerHTML;
- });
- // Put it all together...
- let contents = bundle.getFormattedString("appMenuRemoteTabs.mobilePromo.text2", formatArgs);
- this.container.querySelector(".device-promo").innerHTML = contents;
- },
-
- destroy() {
- this._tabListComponent.uninit();
- this.container.remove();
- },
-
- update(state) {
- // Note that we may also want to update elements that are outside of the
- // deck, so use the document to find the class names rather than our
- // container.
- for (let panel of state.panels) {
- if (panel.selected) {
- Array.prototype.map.call(this._doc.getElementsByClassName(panel.id),
- item => item.classList.add("selected"));
- } else {
- Array.prototype.map.call(this._doc.getElementsByClassName(panel.id),
- item => item.classList.remove("selected"));
- }
- }
- },
-
- _clearChilden() {
- while (this.container.firstChild) {
- this.container.removeChild(this.container.firstChild);
- }
- },
-
- _attachListeners() {
- this.container.querySelector(".android-link").addEventListener("click", this.props.onAndroidClick);
- this.container.querySelector(".ios-link").addEventListener("click", this.props.oniOSClick);
- let syncPrefLinks = this.container.querySelectorAll(".sync-prefs");
- for (let link of syncPrefLinks) {
- link.addEventListener("click", this.props.onSyncPrefClick);
- }
- },
-};
-
diff --git a/application/basilisk/components/syncedtabs/SyncedTabsListStore.js b/application/basilisk/components/syncedtabs/SyncedTabsListStore.js
deleted file mode 100644
index 8f03d9a89..000000000
--- a/application/basilisk/components/syncedtabs/SyncedTabsListStore.js
+++ /dev/null
@@ -1,235 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-
-let { EventEmitter } = Cu.import("resource:///modules/syncedtabs/EventEmitter.jsm", {});
-
-this.EXPORTED_SYMBOLS = [
- "SyncedTabsListStore"
-];
-
-/**
- * SyncedTabsListStore
- *
- * Instances of this store encapsulate all of the state associated with a synced tabs list view.
- * The state includes the clients, their tabs, the row that is currently selected,
- * and the filtered query.
- */
-function SyncedTabsListStore(SyncedTabs) {
- EventEmitter.call(this);
- this._SyncedTabs = SyncedTabs;
- this.data = [];
- this._closedClients = {};
- this._selectedRow = [-1, -1];
- this.filter = "";
- this.inputFocused = false;
-}
-
-Object.assign(SyncedTabsListStore.prototype, EventEmitter.prototype, {
- // This internal method triggers the "change" event that views
- // listen for. It denormalizes the state so that it's easier for
- // the view to deal with. updateType hints to the view what
- // actually needs to be rerendered or just updated, and can be
- // empty (to (re)render everything), "searchbox" (to rerender just the tab list),
- // or "all" (to skip rendering and just update all attributes of existing nodes).
- _change(updateType) {
- let selectedParent = this._selectedRow[0];
- let selectedChild = this._selectedRow[1];
- let rowSelected = false;
- // clone the data so that consumers can't mutate internal storage
- let data = Cu.cloneInto(this.data, {});
- let tabCount = 0;
-
- data.forEach((client, index) => {
- client.closed = !!this._closedClients[client.id];
-
- if (rowSelected || selectedParent < 0) {
- return;
- }
- if (this.filter) {
- if (selectedParent < tabCount + client.tabs.length) {
- client.tabs[selectedParent - tabCount].selected = true;
- client.tabs[selectedParent - tabCount].focused = !this.inputFocused;
- rowSelected = true;
- } else {
- tabCount += client.tabs.length;
- }
- return;
- }
- if (selectedParent === index && selectedChild === -1) {
- client.selected = true;
- client.focused = !this.inputFocused;
- rowSelected = true;
- } else if (selectedParent === index) {
- client.tabs[selectedChild].selected = true;
- client.tabs[selectedChild].focused = !this.inputFocused;
- rowSelected = true;
- }
- });
-
- // If this were React the view would be smart enough
- // to not re-render the whole list unless necessary. But it's
- // not, so updateType is a hint to the view of what actually
- // needs to be rerendered.
- this.emit("change", {
- clients: data,
- canUpdateAll: updateType === "all",
- canUpdateInput: updateType === "searchbox",
- filter: this.filter,
- inputFocused: this.inputFocused
- });
- },
-
- /**
- * Moves the row selection from a child to its parent,
- * which occurs when the parent of a selected row closes.
- */
- _selectParentRow() {
- this._selectedRow[1] = -1;
- },
-
- _toggleBranch(id, closed) {
- this._closedClients[id] = closed;
- if (this._closedClients[id]) {
- this._selectParentRow();
- }
- this._change("all");
- },
-
- _isOpen(client) {
- return !this._closedClients[client.id];
- },
-
- moveSelectionDown() {
- let branchRow = this._selectedRow[0];
- let childRow = this._selectedRow[1];
- let branch = this.data[branchRow];
-
- if (this.filter) {
- this.selectRow(branchRow + 1);
- return;
- }
-
- if (branchRow < 0) {
- this.selectRow(0, -1);
- } else if ((!branch.tabs.length || childRow >= branch.tabs.length - 1 || !this._isOpen(branch)) && branchRow < this.data.length) {
- this.selectRow(branchRow + 1, -1);
- } else if (childRow < branch.tabs.length) {
- this.selectRow(branchRow, childRow + 1);
- }
- },
-
- moveSelectionUp() {
- let branchRow = this._selectedRow[0];
- let childRow = this._selectedRow[1];
-
- if (this.filter) {
- this.selectRow(branchRow - 1);
- return;
- }
-
- if (branchRow < 0) {
- this.selectRow(0, -1);
- } else if (childRow < 0 && branchRow > 0) {
- let prevBranch = this.data[branchRow - 1];
- let newChildRow = this._isOpen(prevBranch) ? prevBranch.tabs.length - 1 : -1;
- this.selectRow(branchRow - 1, newChildRow);
- } else if (childRow >= 0) {
- this.selectRow(branchRow, childRow - 1);
- }
- },
-
- // Selects a row and makes sure the selection is within bounds
- selectRow(parent, child) {
- let maxParentRow = this.filter ? this._tabCount() : this.data.length;
- let parentRow = parent;
- if (parent <= -1) {
- parentRow = 0;
- } else if (parent >= maxParentRow) {
- return;
- }
-
- let childRow = child;
- if (parentRow === -1 || this.filter || typeof child === "undefined" || child < -1) {
- childRow = -1;
- } else if (child >= this.data[parentRow].tabs.length) {
- childRow = this.data[parentRow].tabs.length - 1;
- }
-
- if (this._selectedRow[0] === parentRow && this._selectedRow[1] === childRow) {
- return;
- }
-
- this._selectedRow = [parentRow, childRow];
- this.inputFocused = false;
- this._change("all");
- },
-
- _tabCount() {
- return this.data.reduce((prev, curr) => curr.tabs.length + prev, 0);
- },
-
- toggleBranch(id) {
- this._toggleBranch(id, !this._closedClients[id]);
- },
-
- closeBranch(id) {
- this._toggleBranch(id, true);
- },
-
- openBranch(id) {
- this._toggleBranch(id, false);
- },
-
- focusInput() {
- this.inputFocused = true;
- // A change type of "all" updates rather than rebuilds, which is what we
- // want here - only the selection/focus has changed.
- this._change("all");
- },
-
- blurInput() {
- this.inputFocused = false;
- // A change type of "all" updates rather than rebuilds, which is what we
- // want here - only the selection/focus has changed.
- this._change("all");
- },
-
- clearFilter() {
- this.filter = "";
- this._selectedRow = [-1, -1];
- return this.getData();
- },
-
- // Fetches data from the SyncedTabs module and triggers
- // and update
- getData(filter) {
- let updateType;
- let hasFilter = typeof filter !== "undefined";
- if (hasFilter) {
- this.filter = filter;
- this._selectedRow = [-1, -1];
-
- // When a filter is specified we tell the view that only the list
- // needs to be rerendered so that it doesn't disrupt the input
- // field's focus.
- updateType = "searchbox";
- }
-
- // return promise for tests
- return this._SyncedTabs.getTabClients(this.filter)
- .then(result => {
- if (!hasFilter) {
- // Only sort clients and tabs if we're rendering the whole list.
- this._SyncedTabs.sortTabClientsByLastUsed(result);
- }
- this.data = result;
- this._change(updateType);
- })
- .catch(Cu.reportError);
- }
-});
diff --git a/application/basilisk/components/syncedtabs/TabListComponent.js b/application/basilisk/components/syncedtabs/TabListComponent.js
deleted file mode 100644
index aa60e4769..000000000
--- a/application/basilisk/components/syncedtabs/TabListComponent.js
+++ /dev/null
@@ -1,138 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-
-let log = Cu.import("resource://gre/modules/Log.jsm", {})
- .Log.repository.getLogger("Sync.RemoteTabs");
-
-XPCOMUtils.defineLazyModuleGetter(this, "PlacesUIUtils",
- "resource:///modules/PlacesUIUtils.jsm");
-
-this.EXPORTED_SYMBOLS = [
- "TabListComponent"
-];
-
-/**
- * TabListComponent
- *
- * The purpose of this component is to compose the view, state, and actions.
- * It defines high level actions that act on the state and passes them to the
- * view for it to trigger during user interaction. It also subscribes the view
- * to state changes so it can rerender.
- */
-
-function TabListComponent({window, store, View, SyncedTabs, clipboardHelper,
- getChromeWindow}) {
- this._window = window;
- this._store = store;
- this._View = View;
- this._clipboardHelper = clipboardHelper;
- this._getChromeWindow = getChromeWindow;
- // used to trigger Sync from context menu
- this._SyncedTabs = SyncedTabs;
-}
-
-TabListComponent.prototype = {
- get container() {
- return this._view.container;
- },
-
- init() {
- log.debug("Initializing TabListComponent");
-
- this._view = new this._View(this._window, {
- onSelectRow: (...args) => this.onSelectRow(...args),
- onOpenTab: (...args) => this.onOpenTab(...args),
- onOpenTabs: (...args) => this.onOpenTabs(...args),
- onMoveSelectionDown: (...args) => this.onMoveSelectionDown(...args),
- onMoveSelectionUp: (...args) => this.onMoveSelectionUp(...args),
- onToggleBranch: (...args) => this.onToggleBranch(...args),
- onBookmarkTab: (...args) => this.onBookmarkTab(...args),
- onCopyTabLocation: (...args) => this.onCopyTabLocation(...args),
- onSyncRefresh: (...args) => this.onSyncRefresh(...args),
- onFilter: (...args) => this.onFilter(...args),
- onClearFilter: (...args) => this.onClearFilter(...args),
- onFilterFocus: (...args) => this.onFilterFocus(...args),
- onFilterBlur: (...args) => this.onFilterBlur(...args)
- });
-
- this._store.on("change", state => this._view.render(state));
- this._view.render({clients: []});
- // get what's already available...
- this._store.getData();
- this._store.focusInput();
- },
-
- uninit() {
- this._view.destroy();
- },
-
- onFilter(query) {
- this._store.getData(query);
- },
-
- onClearFilter() {
- this._store.clearFilter();
- },
-
- onFilterFocus() {
- this._store.focusInput();
- },
-
- onFilterBlur() {
- this._store.blurInput();
- },
-
- onSelectRow(position) {
- this._store.selectRow(position[0], position[1]);
- },
-
- onMoveSelectionDown() {
- this._store.moveSelectionDown();
- },
-
- onMoveSelectionUp() {
- this._store.moveSelectionUp();
- },
-
- onToggleBranch(id) {
- this._store.toggleBranch(id);
- },
-
- onBookmarkTab(uri, title) {
- this._window.top.PlacesCommandHook
- .bookmarkLink(this._window.top.PlacesUtils.bookmarksMenuFolderId, uri, title)
- .catch(Cu.reportError);
- },
-
- onOpenTab(url, where, params) {
- this._window.openUILinkIn(url, where, params);
- },
-
- onOpenTabs(urls, where) {
- if (!PlacesUIUtils.confirmOpenInTabs(urls.length, this._window)) {
- return;
- }
- if (where == "window") {
- this._window.openDialog(this._window.getBrowserURL(), "_blank",
- "chrome,dialog=no,all", urls.join("|"));
- } else {
- let loadInBackground = where == "tabshifted" ? true : false;
- this._getChromeWindow(this._window).gBrowser.loadTabs(urls, loadInBackground, false);
- }
- },
-
- onCopyTabLocation(url) {
- this._clipboardHelper.copyString(url);
- },
-
- onSyncRefresh() {
- this._SyncedTabs.syncTabs(true);
- }
-};
diff --git a/application/basilisk/components/syncedtabs/TabListView.js b/application/basilisk/components/syncedtabs/TabListView.js
deleted file mode 100644
index dab15101b..000000000
--- a/application/basilisk/components/syncedtabs/TabListView.js
+++ /dev/null
@@ -1,568 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-
-Cu.import("resource://gre/modules/Services.jsm");
-
-let { getChromeWindow } = Cu.import("resource:///modules/syncedtabs/util.js", {});
-
-let log = Cu.import("resource://gre/modules/Log.jsm", {})
- .Log.repository.getLogger("Sync.RemoteTabs");
-
-this.EXPORTED_SYMBOLS = [
- "TabListView"
-];
-
-function getContextMenu(window) {
- return getChromeWindow(window).document.getElementById("SyncedTabsSidebarContext");
-}
-
-function getTabsFilterContextMenu(window) {
- return getChromeWindow(window).document.getElementById("SyncedTabsSidebarTabsFilterContext");
-}
-
-/*
- * TabListView
- *
- * Given a state, this object will render the corresponding DOM.
- * It maintains no state of it's own. It listens for DOM events
- * and triggers actions that may cause the state to change and
- * ultimately the view to rerender.
- */
-function TabListView(window, props) {
- this.props = props;
-
- this._window = window;
- this._doc = this._window.document;
-
- this._tabsContainerTemplate = this._doc.getElementById("tabs-container-template");
- this._clientTemplate = this._doc.getElementById("client-template");
- this._emptyClientTemplate = this._doc.getElementById("empty-client-template");
- this._tabTemplate = this._doc.getElementById("tab-template");
- this.tabsFilter = this._doc.querySelector(".tabsFilter");
- this.clearFilter = this._doc.querySelector(".textbox-search-clear");
- this.searchBox = this._doc.querySelector(".search-box");
- this.searchIcon = this._doc.querySelector(".textbox-search-icon");
-
- this.container = this._doc.createElement("div");
-
- this._attachFixedListeners();
-
- this._setupContextMenu();
-}
-
-TabListView.prototype = {
- render(state) {
- // Don't rerender anything; just update attributes, e.g. selection
- if (state.canUpdateAll) {
- this._update(state);
- return;
- }
- // Rerender the tab list
- if (state.canUpdateInput) {
- this._updateSearchBox(state);
- this._createList(state);
- return;
- }
- // Create the world anew
- this._create(state);
- },
-
- // Create the initial DOM from templates
- _create(state) {
- let wrapper = this._doc.importNode(this._tabsContainerTemplate.content, true).firstElementChild;
- this._clearChilden();
- this.container.appendChild(wrapper);
-
- this.list = this.container.querySelector(".list");
-
- this._createList(state);
- this._updateSearchBox(state);
-
- this._attachListListeners();
- },
-
- _createList(state) {
- this._clearChilden(this.list);
- for (let client of state.clients) {
- if (state.filter) {
- this._renderFilteredClient(client);
- } else {
- this._renderClient(client);
- }
- }
- if (this.list.firstChild) {
- const firstTab = this.list.firstChild.querySelector(".item.tab:first-child .item-title");
- if (firstTab) {
- firstTab.setAttribute("tabindex", 2);
- }
- }
- },
-
- destroy() {
- this._teardownContextMenu();
- this.container.remove();
- },
-
- _update(state) {
- this._updateSearchBox(state);
- for (let client of state.clients) {
- let clientNode = this._doc.getElementById("item-" + client.id);
- if (clientNode) {
- this._updateClient(client, clientNode);
- }
-
- client.tabs.forEach((tab, index) => {
- let tabNode = this._doc.getElementById('tab-' + client.id + '-' + index);
- this._updateTab(tab, tabNode, index);
- });
- }
- },
-
- // Client rows are hidden when the list is filtered
- _renderFilteredClient(client, filter) {
- client.tabs.forEach((tab, index) => {
- let node = this._renderTab(client, tab, index);
- this.list.appendChild(node);
- });
- },
-
- _renderClient(client) {
- let itemNode = client.tabs.length ?
- this._createClient(client) :
- this._createEmptyClient(client);
-
- this._updateClient(client, itemNode);
-
- let tabsList = itemNode.querySelector(".item-tabs-list");
- client.tabs.forEach((tab, index) => {
- let node = this._renderTab(client, tab, index);
- tabsList.appendChild(node);
- });
-
- this.list.appendChild(itemNode);
- return itemNode;
- },
-
- _renderTab(client, tab, index) {
- let itemNode = this._createTab(tab);
- this._updateTab(tab, itemNode, index);
- return itemNode;
- },
-
- _createClient(item) {
- return this._doc.importNode(this._clientTemplate.content, true).firstElementChild;
- },
-
- _createEmptyClient(item) {
- return this._doc.importNode(this._emptyClientTemplate.content, true).firstElementChild;
- },
-
- _createTab(item) {
- return this._doc.importNode(this._tabTemplate.content, true).firstElementChild;
- },
-
- _clearChilden(node) {
- let parent = node || this.container;
- while (parent.firstChild) {
- parent.removeChild(parent.firstChild);
- }
- },
-
- // These listeners are attached only once, when we initialize the view
- _attachFixedListeners() {
- this.tabsFilter.addEventListener("input", this.onFilter.bind(this));
- this.tabsFilter.addEventListener("focus", this.onFilterFocus.bind(this));
- this.tabsFilter.addEventListener("blur", this.onFilterBlur.bind(this));
- this.clearFilter.addEventListener("click", this.onClearFilter.bind(this));
- this.searchIcon.addEventListener("click", this.onFilterFocus.bind(this));
- },
-
- // These listeners have to be re-created every time since we re-create the list
- _attachListListeners() {
- this.list.addEventListener("click", this.onClick.bind(this));
- this.list.addEventListener("mouseup", this.onMouseUp.bind(this));
- this.list.addEventListener("keydown", this.onKeyDown.bind(this));
- },
-
- _updateSearchBox(state) {
- if (state.filter) {
- this.searchBox.classList.add("filtered");
- } else {
- this.searchBox.classList.remove("filtered");
- }
- this.tabsFilter.value = state.filter;
- if (state.inputFocused) {
- this.searchBox.setAttribute("focused", true);
- this.tabsFilter.focus();
- } else {
- this.searchBox.removeAttribute("focused");
- }
- },
-
- /**
- * Update the element representing an item, ensuring it's in sync with the
- * underlying data.
- * @param {client} item - Item to use as a source.
- * @param {Element} itemNode - Element to update.
- */
- _updateClient(item, itemNode) {
- itemNode.setAttribute("id", "item-" + item.id);
- let lastSync = new Date(item.lastModified);
- let lastSyncTitle = getChromeWindow(this._window).gSyncUI.formatLastSyncDate(lastSync);
- itemNode.setAttribute("title", lastSyncTitle);
- if (item.closed) {
- itemNode.classList.add("closed");
- } else {
- itemNode.classList.remove("closed");
- }
- if (item.selected) {
- itemNode.classList.add("selected");
- } else {
- itemNode.classList.remove("selected");
- }
- if (item.isMobile) {
- itemNode.classList.add("device-image-mobile");
- } else {
- itemNode.classList.add("device-image-desktop");
- }
- if (item.focused) {
- itemNode.focus();
- }
- itemNode.dataset.id = item.id;
- itemNode.querySelector(".item-title").textContent = item.name;
- },
-
- /**
- * Update the element representing a tab, ensuring it's in sync with the
- * underlying data.
- * @param {tab} item - Item to use as a source.
- * @param {Element} itemNode - Element to update.
- */
- _updateTab(item, itemNode, index) {
- itemNode.setAttribute("title", `${item.title}\n${item.url}`);
- itemNode.setAttribute("id", "tab-" + item.client + '-' + index);
- if (item.selected) {
- itemNode.classList.add("selected");
- } else {
- itemNode.classList.remove("selected");
- }
- if (item.focused) {
- itemNode.focus();
- }
- itemNode.dataset.url = item.url;
-
- itemNode.querySelector(".item-title").textContent = item.title;
-
- if (item.icon) {
- let icon = itemNode.querySelector(".item-icon-container");
- icon.style.backgroundImage = "url(" + item.icon + ")";
- }
- },
-
- onMouseUp(event) {
- if (event.which == 2) { // Middle click
- this.onClick(event);
- }
- },
-
- onClick(event) {
- let itemNode = this._findParentItemNode(event.target);
- if (!itemNode) {
- return;
- }
-
- if (itemNode.classList.contains("tab")) {
- let url = itemNode.dataset.url;
- if (url) {
- this.onOpenSelected(url, event);
- }
- }
-
- // Middle click on a client
- if (itemNode.classList.contains("client")) {
- let where = getChromeWindow(this._window).whereToOpenLink(event);
- if (where != "current") {
- const tabs = itemNode.querySelector(".item-tabs-list").childNodes;
- const urls = [...tabs].map(tab => tab.dataset.url);
- this.props.onOpenTabs(urls, where);
- }
- }
-
- if (event.target.classList.contains("item-twisty-container")
- && event.which != 2) {
- this.props.onToggleBranch(itemNode.dataset.id);
- return;
- }
-
- let position = this._getSelectionPosition(itemNode);
- this.props.onSelectRow(position);
- },
-
- /**
- * Handle a keydown event on the list box.
- * @param {Event} event - Triggering event.
- */
- onKeyDown(event) {
- if (event.keyCode == this._window.KeyEvent.DOM_VK_DOWN) {
- event.preventDefault();
- this.props.onMoveSelectionDown();
- } else if (event.keyCode == this._window.KeyEvent.DOM_VK_UP) {
- event.preventDefault();
- this.props.onMoveSelectionUp();
- } else if (event.keyCode == this._window.KeyEvent.DOM_VK_RETURN) {
- let selectedNode = this.container.querySelector('.item.selected');
- if (selectedNode.dataset.url) {
- this.onOpenSelected(selectedNode.dataset.url, event);
- } else if (selectedNode) {
- this.props.onToggleBranch(selectedNode.dataset.id);
- }
- }
- },
-
- onBookmarkTab() {
- let item = this._getSelectedTabNode();
- if (item) {
- let title = item.querySelector(".item-title").textContent;
- this.props.onBookmarkTab(item.dataset.url, title);
- }
- },
-
- onCopyTabLocation() {
- let item = this._getSelectedTabNode();
- if (item) {
- this.props.onCopyTabLocation(item.dataset.url);
- }
- },
-
- onOpenSelected(url, event) {
- let where = getChromeWindow(this._window).whereToOpenLink(event);
- this.props.onOpenTab(url, where, {});
- },
-
- onOpenSelectedFromContextMenu(event) {
- let item = this._getSelectedTabNode();
- if (item) {
- let where = event.target.getAttribute("where");
- let params = {
- private: event.target.hasAttribute("private"),
- };
- this.props.onOpenTab(item.dataset.url, where, params);
- }
- },
-
- onFilter(event) {
- let query = event.target.value;
- if (query) {
- this.props.onFilter(query);
- } else {
- this.props.onClearFilter();
- }
- },
-
- onClearFilter() {
- this.props.onClearFilter();
- },
-
- onFilterFocus() {
- this.props.onFilterFocus();
- },
- onFilterBlur() {
- this.props.onFilterBlur();
- },
-
- _getSelectedTabNode() {
- let item = this.container.querySelector('.item.selected');
- if (this._isTab(item) && item.dataset.url) {
- return item;
- }
- return null;
- },
-
- // Set up the custom context menu
- _setupContextMenu() {
- Services.els.addSystemEventListener(this._window, "contextmenu", this, false);
- for (let getMenu of [getContextMenu, getTabsFilterContextMenu]) {
- let menu = getMenu(this._window);
- menu.addEventListener("popupshowing", this, true);
- menu.addEventListener("command", this, true);
- }
- },
-
- _teardownContextMenu() {
- // Tear down context menu
- Services.els.removeSystemEventListener(this._window, "contextmenu", this, false);
- for (let getMenu of [getContextMenu, getTabsFilterContextMenu]) {
- let menu = getMenu(this._window);
- menu.removeEventListener("popupshowing", this, true);
- menu.removeEventListener("command", this, true);
- }
- },
-
- handleEvent(event) {
- switch (event.type) {
- case "contextmenu":
- this.handleContextMenu(event);
- break;
-
- case "popupshowing": {
- if (event.target.getAttribute("id") == "SyncedTabsSidebarTabsFilterContext") {
- this.handleTabsFilterContextMenuShown(event);
- }
- break;
- }
-
- case "command": {
- let menu = event.target.closest("menupopup");
- switch (menu.getAttribute("id")) {
- case "SyncedTabsSidebarContext":
- this.handleContentContextMenuCommand(event);
- break;
-
- case "SyncedTabsSidebarTabsFilterContext":
- this.handleTabsFilterContextMenuCommand(event);
- break;
- }
- break;
- }
- }
- },
-
- handleTabsFilterContextMenuShown(event) {
- let document = event.target.ownerDocument;
- let focusedElement = document.commandDispatcher.focusedElement;
- if (focusedElement != this.tabsFilter) {
- this.tabsFilter.focus();
- }
- for (let item of event.target.children) {
- if (!item.hasAttribute("cmd")) {
- continue;
- }
- let command = item.getAttribute("cmd");
- let controller = document.commandDispatcher.getControllerForCommand(command);
- if (controller.isCommandEnabled(command)) {
- item.removeAttribute("disabled");
- } else {
- item.setAttribute("disabled", "true");
- }
- }
- },
-
- handleContentContextMenuCommand(event) {
- let id = event.target.getAttribute("id");
- switch (id) {
- case "syncedTabsOpenSelected":
- case "syncedTabsOpenSelectedInTab":
- case "syncedTabsOpenSelectedInWindow":
- case "syncedTabsOpenSelectedInPrivateWindow":
- this.onOpenSelectedFromContextMenu(event);
- break;
- case "syncedTabsBookmarkSelected":
- this.onBookmarkTab();
- break;
- case "syncedTabsCopySelected":
- this.onCopyTabLocation();
- break;
- case "syncedTabsRefresh":
- case "syncedTabsRefreshFilter":
- this.props.onSyncRefresh();
- break;
- }
- },
-
- handleTabsFilterContextMenuCommand(event) {
- let command = event.target.getAttribute("cmd");
- let dispatcher = getChromeWindow(this._window).document.commandDispatcher;
- let controller = dispatcher.focusedElement.controllers.getControllerForCommand(command);
- controller.doCommand(command);
- },
-
- handleContextMenu(event) {
- let menu;
-
- if (event.target == this.tabsFilter) {
- menu = getTabsFilterContextMenu(this._window);
- } else {
- let itemNode = this._findParentItemNode(event.target);
- if (itemNode) {
- let position = this._getSelectionPosition(itemNode);
- this.props.onSelectRow(position);
- }
- menu = getContextMenu(this._window);
- this.adjustContextMenu(menu);
- }
-
- menu.openPopupAtScreen(event.screenX, event.screenY, true, event);
- },
-
- adjustContextMenu(menu) {
- let item = this.container.querySelector('.item.selected');
- let showTabOptions = this._isTab(item);
-
- let el = menu.firstChild;
-
- while (el) {
- if (showTabOptions || el.getAttribute("id") === "syncedTabsRefresh") {
- el.hidden = false;
- } else {
- el.hidden = true;
- }
-
- el = el.nextSibling;
- }
- },
-
- /**
- * Find the parent item element, from a given child element.
- * @param {Element} node - Child element.
- * @return {Element} Element for the item, or null if not found.
- */
- _findParentItemNode(node) {
- while (node && node !== this.list && node !== this._doc.documentElement &&
- !node.classList.contains("item")) {
- node = node.parentNode;
- }
-
- if (node !== this.list && node !== this._doc.documentElement) {
- return node;
- }
-
- return null;
- },
-
- _findParentBranchNode(node) {
- while (node && !node.classList.contains("list") && node !== this._doc.documentElement &&
- !node.parentNode.classList.contains("list")) {
- node = node.parentNode;
- }
-
- if (node !== this.list && node !== this._doc.documentElement) {
- return node;
- }
-
- return null;
- },
-
- _getSelectionPosition(itemNode) {
- let parent = this._findParentBranchNode(itemNode);
- let parentPosition = this._indexOfNode(parent.parentNode, parent);
- let childPosition = -1;
- // if the node is not a client, find its position within the parent
- if (parent !== itemNode) {
- childPosition = this._indexOfNode(itemNode.parentNode, itemNode);
- }
- return [parentPosition, childPosition];
- },
-
- _indexOfNode(parent, child) {
- return Array.prototype.indexOf.call(parent.childNodes, child);
- },
-
- _isTab(item) {
- return item && item.classList.contains("tab");
- }
-};
diff --git a/application/basilisk/components/syncedtabs/moz.build b/application/basilisk/components/syncedtabs/moz.build
deleted file mode 100644
index a6515d6a1..000000000
--- a/application/basilisk/components/syncedtabs/moz.build
+++ /dev/null
@@ -1,17 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-JAR_MANIFESTS += ['jar.mn']
-
-EXTRA_JS_MODULES.syncedtabs += [
- 'EventEmitter.jsm',
- 'SyncedTabsDeckComponent.js',
- 'SyncedTabsDeckStore.js',
- 'SyncedTabsDeckView.js',
- 'SyncedTabsListStore.js',
- 'TabListComponent.js',
- 'TabListView.js',
- 'util.js',
-]
-
diff --git a/application/basilisk/components/syncedtabs/sidebar.js b/application/basilisk/components/syncedtabs/sidebar.js
deleted file mode 100644
index 84df95e9d..000000000
--- a/application/basilisk/components/syncedtabs/sidebar.js
+++ /dev/null
@@ -1,30 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://services-sync/SyncedTabs.jsm");
-Cu.import("resource:///modules/syncedtabs/SyncedTabsDeckComponent.js");
-
-XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts",
- "resource://gre/modules/FxAccounts.jsm");
-
-this.syncedTabsDeckComponent = new SyncedTabsDeckComponent({window, SyncedTabs, fxAccounts});
-
-let onLoaded = () => {
- syncedTabsDeckComponent.init();
- document.getElementById("template-container").appendChild(syncedTabsDeckComponent.container);
-};
-
-let onUnloaded = () => {
- removeEventListener("DOMContentLoaded", onLoaded);
- removeEventListener("unload", onUnloaded);
- syncedTabsDeckComponent.uninit();
-};
-
-addEventListener("DOMContentLoaded", onLoaded);
-addEventListener("unload", onUnloaded);
diff --git a/application/basilisk/components/syncedtabs/sidebar.xhtml b/application/basilisk/components/syncedtabs/sidebar.xhtml
deleted file mode 100644
index 3efcbea0e..000000000
--- a/application/basilisk/components/syncedtabs/sidebar.xhtml
+++ /dev/null
@@ -1,114 +0,0 @@
-<?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 PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" [
- <!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
- %browserDTD;
- <!ENTITY % globalDTD
- SYSTEM "chrome://global/locale/global.dtd">
- %globalDTD;
- <!ENTITY % syncBrandDTD
- SYSTEM "chrome://browser/locale/syncBrand.dtd">
- %syncBrandDTD;
-]>
-<html xmlns="http://www.w3.org/1999/xhtml"
- xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
- <head>
- <script src="chrome://browser/content/syncedtabs/sidebar.js" type="application/javascript;version=1.8"></script>
- <script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/>
-
- <link rel="stylesheet" type="text/css" media="all" href="chrome://browser/skin/syncedtabs/sidebar.css"/>
- <link rel="stylesheet" type="text/css" media="all" href="chrome://global/skin/"/>
- <link rel="stylesheet" type="text/css" media="all" href="chrome://global/skin/textbox.css"/>
- <link rel="stylesheet" type="text/css" media="all" href="chrome://browser/content/browser.css"/>
- <title>&syncedTabs.sidebar.label;</title>
- </head>
-
- <body dir="&locale.dir;" role="application">
- <template id="client-template">
- <div class="item client" role="option" tabindex="-1">
- <div class="item-title-container">
- <div class="item-twisty-container"></div>
- <div class="item-icon-container"></div>
- <p class="item-title"></p>
- </div>
- <div class="item-tabs-list"></div>
- </div>
- </template>
- <template id="empty-client-template">
- <div class="item empty client" role="option" tabindex="-1">
- <div class="item-title-container">
- <div class="item-twisty-container"></div>
- <div class="item-icon-container"></div>
- <p class="item-title"></p>
- </div>
- <div class="item-tabs-list">
- <div class="item empty" role="option" tabindex="-1">
- <div class="item-title-container">
- <div class="item-icon-container"></div>
- <p class="item-title">&syncedTabs.sidebar.notabs.label;</p>
- </div>
- </div>
- </div>
- </div>
- </template>
- <template id="tab-template">
- <div class="item tab" role="option" tabindex="-1">
- <div class="item-title-container">
- <div class="item-icon-container"></div>
- <p class="item-title"></p>
- </div>
- </div>
- </template>
-
- <template id="tabs-container-template">
- <div class="tabs-container">
- <div class="list" role="listbox"></div>
- </div>
- </template>
-
- <template id="deck-template">
- <div class="deck">
- <div class="tabs-fetching sync-state">
- <!-- Show intentionally blank panel, see bug 1239845 -->
- </div>
- <div class="notAuthedInfo sync-state">
- <p>&syncedTabs.sidebar.notsignedin.label;</p>
- <p><a href="#" class="sync-prefs text-link">&fxaSignIn.label;</a></p>
- </div>
- <div class="singleDeviceInfo sync-state">
- <p>&syncedTabs.sidebar.noclients.title;</p>
- <p>&syncedTabs.sidebar.noclients.subtitle;</p>
- <p class="device-promo" fxAccountsBrand="&syncBrand.fxAccount.label;"></p>
- </div>
- <div class="tabs-disabled sync-state">
- <p>&syncedTabs.sidebar.tabsnotsyncing.label;</p>
- <p><a href="#" class="sync-prefs text-link">&syncedTabs.sidebar.openprefs.label;</a></p>
- </div>
- </div>
- </template>
-
- <div class="content-container">
- <!-- the non-scrollable header -->
- <div class="content-header">
- <div class="sidebar-search-container tabs-container sync-state">
- <div class="search-box compact">
- <div class="textbox-input-box">
- <input type="text" class="tabsFilter textbox-input" tabindex="1"/>
- <div class="textbox-search-icons">
- <a class="textbox-search-clear"></a>
- <a class="textbox-search-icon"></a>
- </div>
- </div>
- </div>
- </div>
- </div>
- <!-- the scrollable content area where our templates are inserted -->
- <div id="template-container" class="content-scrollable" tabindex="-1">
- </div>
- </div>
- </body>
-</html>
diff --git a/application/basilisk/components/syncedtabs/util.js b/application/basilisk/components/syncedtabs/util.js
deleted file mode 100644
index e09a1a528..000000000
--- a/application/basilisk/components/syncedtabs/util.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-
-this.EXPORTED_SYMBOLS = [
- "getChromeWindow"
-];
-
-// Get the chrome (ie, browser) window hosting this content.
-function getChromeWindow(window) {
- return window
- .QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIWebNavigation)
- .QueryInterface(Ci.nsIDocShellTreeItem)
- .rootTreeItem
- .QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIDOMWindow)
- .wrappedJSObject;
-}